为啥内核没有收到我的通用网络链接消息?

Posted

技术标签:

【中文标题】为啥内核没有收到我的通用网络链接消息?【英文标题】:Why isn't the Kernel receveing my generic netlink messages?为什么内核没有收到我的通用网络链接消息? 【发布时间】:2020-07-01 15:18:18 【问题描述】:

我正在尝试使用通用 netlink 将嵌套属性从用户空间发送到内核,函数 nl_send_auto() 返回 52(这应该是发送到内核的字节数)但内核没有收到消息.我的方法有问题吗?这是我在用户空间写的代码:

int err = -1;
struct nl_msg *msg;
struct nlattr *attr;
struct nl_sock *sock;
int family;
int send = 0;

if ((sock = nl_socket_alloc()) == NULL)
    return err;

if ((err = genl_connect(sock)))
    return err;

if ((family = genl_ctrl_resolve(sock, FAMILY)) < 0)
    return family;

if ((msg = nlmsg_alloc()) == NULL)
    return err;

if ((genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, FAMILY, 0, 
    NLM_F_REQUEST, CREATE_STATE, 1)) == NULL)
    return err;

if (!(attr = nla_nest_start(msg, KLUA_NL_STATE)))
    nla_nest_cancel(msg, attr);
    return err;


if ((ret = nla_put_string(msg, STATE_NAME, cmd->name)) ||
    (ret = nla_put_u32(msg, MAX_ALLOC, cmd->maxalloc)) ||
    (ret = nla_put_u32(msg, CURR_ALLOC, cmd->curralloc))
    )
    return err;

nla_nest_end(msg, attr);

if ((send = nl_send_auto(ctrl->sock, msg)) < 0)
    return send;

printf("All done sended %d bytes\n", send);

nlmsg_free(msg);

这段代码打印出 52,这是发送给内核的字节;

FAMILY 宏定义为(在内核和用户空间):

#define FAMILY "family"

我的 netlink 属性是(对于内核和用户空间):

enum 
    KLUA_NL_STATE,
    STATE_NAME,
    MAX_ALLOC,
    CURR_ALLOC,
    ATTR_COUNT,
#define ATTR_MAX (ATTR_COUNT - 1)
;

我的操作枚举是:

enum 
    CREATE_STATE = 16,
;

而我的内核代码是:

struct nla_policy lunatik_policy[ATTR_MAX] = 
    [KLUA_NL_STATE] =  .type = NLA_NESTED ,
;

static int klua_create_state(struct sk_buff *buff, struct genl_info *info);

static const struct genl_ops l_ops[] = 
    
        .cmd    = CREATE_STATE,
        .doit   = klua_create_state,
#if LINUX_VERSION_CODE < KERNEL_VERSION(5,2,0)
        /*Before kernel 5.2.0, each operation has its own policy*/
        .policy = lunatik_policy
#endif
    ,
;

#define KLUA_NL_STATE_ATTRS_COUNT 3

struct genl_family lunatik_family = 
    .name    = FAMILY,
    .version = 1,
    .maxattr = ATTR_MAX,
    .netnsok = true,
    .policy  = lunatik_policy,
    .module  = THIS_MODULE,
    .ops     = l_ops,
    .n_ops   = ARRAY_SIZE(l_ops),
;

static int klua_create_state(struct sk_buff *buff, struct genl_info *info)

    pr_info("I received the message\n");
    return 0;

此代码在 dmesg 上没有打印任何内容,我想知道原因。

【问题讨论】:

【参考方案1】:

你的实际问题

在 Linux 5.2 重构期间,NLA_F_NESTED 标志的语义发生了一些变化。看来您现在需要在调用 nla_nest_start() 时始终包含它:

if (!(attr = nla_nest_start(msg, KLUA_NL_STATE)))
    ...

应该是

if (!(attr = nla_nest_start(msg, NLA_F_NESTED | KLUA_NL_STATE)))
    ...

是的,我很清楚 libnl 库显然应该为您执行此操作,并且将来可能会这样做,但不幸的是,这就是我们现在所处的位置。

还有:

enum 
    KLUA_NL_STATE,
    ...
;

属性零总是保留的。您需要将其更改为

enum 
    KLUA_NL_STATE = 1,
    ...
;

仅供参考:操作 0 也是保留的,所以很幸运您选择了 16。但以后请记住它。

句法问题

这些可能只是复制粘贴错误,但我还是将它们包括在内是为了方便其他登陆此页面的人寻找示例。

if ((genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, FAMILY, 0, 
    NLM_F_REQUEST, CREATE_STATE, 1)) == NULL)
    return err;

应该是

if ((genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, family, 0, 
    NLM_F_REQUEST, CREATE_STATE, 1)) == NULL)
    return err;

还有:

if ((ret = nla_put_string(msg, STATE_NAME, cmd->name)) ||
    (ret = nla_put_u32(msg, MAX_ALLOC, cmd->maxalloc)) ||
    (ret = nla_put_u32(msg, CURR_ALLOC, cmd->curralloc))
    )
    return err;

应该是

if ((err = nla_put_string(msg, STATE_NAME, cmd->name)) ||
    (err = nla_put_u32(msg, MAX_ALLOC, cmd->maxalloc)) ||
    (err = nla_put_u32(msg, CURR_ALLOC, cmd->curralloc))
    )
    return err;

还有:

if ((send = nl_send_auto(ctrl->sock, msg)) < 0)
    return send;

应该是

if ((send = nl_send_auto(sock, msg)) < 0)
    return send;

【讨论】:

以上是关于为啥内核没有收到我的通用网络链接消息?的主要内容,如果未能解决你的问题,请参考以下文章

Google Cloud Messaging 设备未收到通知。 (没有“注册应用程序”链接)

一个可屏蔽长短链接的网络模块

同一个网络为啥安卓手机可以上网苹果手机不能上网是啥原因?

socket收发消息原理剖析

Erlang网络通信

为啥我的电脑网络连接老是断开?