gpt4 book ai didi

c - Netlink 组播内核组

转载 作者:行者123 更新时间:2023-12-04 09:47:37 31 4
gpt4 key购买 nike

我试图实现的任务实际上非常简单(将字符串“TEST”多播到用户级守护程序),但内核模块无法编译。它因错误而停止:

passing argument 4 of ‘genlmsg_multicast_allns’ makes integer from pointer without a cast [enabled by default]

但不应该只是我定义的多播组吗?

这是“澄清”的代码:
#include <linux/module.h>
#include <net/sock.h>
#include <linux/netlink.h>
#include <linux/skbuff.h>
#include <linux/string.h>
#include <net/netlink.h>
#include <net/genetlink.h>

struct sock *nl_sk = NULL;

static void daemon(void){
struct sk_buff *skb;
void* msg_head;
unsigned char *msg;

struct genl_family my_genl_family = {
.id = GENL_ID_GENERATE,
.hdrsize = 0,
.name = "family_name",
.version = 1,
.maxattr = 5
};

struct genl_multicast_group my_mc_group = {
.name = "mc_group",
};


msg = "TEST";
skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);

msg_head = genlmsg_put(skb, 0, 0, &my_genl_family, 0, 21);

nla_put(skb, 0, sizeof(msg), msg);

genlmsg_end(skb, msg_head);

genlmsg_multicast_allns( &my_genl_family, skb, 0, my_mc_group, GFP_KERNEL);

}

static int __init hello_init(void)
{
printk("Entering: %s\n", __FUNCTION__);

printk(KERN_INFO "Calling main function with sockets\n");

struct netlink_kernel_cfg cfg = {
.groups = 1,
.flags = NL_CFG_F_NONROOT_RECV,
};

nl_sk = netlink_kernel_create(&init_net, NETLINK_GENERIC, &cfg);


daemon();

return 0;
}

static void __exit hello_exit(void)
{
printk(KERN_INFO "exiting hello module\n");
netlink_kernel_release(nl_sk);
}

module_init(hello_init);
module_exit(hello_exit);

MODULE_LICENSE("GPL");

谢谢你的帮助。

编辑

这是客户端代码:
#include <netlink/netlink.h>
#include <netlink/socket.h>
#include <netlink/msg.h>
#include <netlink/genl/genl.h>
#include <linux/genetlink.h>

/*
* This function will be called for each valid netlink message received
* in nl_recvmsgs_default()
*/
static int my_func(struct nl_msg *msg, void *arg)
{
//struct nl_msg *nlmsg = nlmsg_alloc_size(GENL_HDRLEN+nla_total_size(sizeof(msg))+36);

printf("Test\n");

return 0;
}

int main(){
struct nl_sock *sk;

int gr_id;

/* Allocate a new socket */
sk = nl_socket_alloc();

/*
* Notifications do not use sequence numbers, disable sequence number
* checking.
*/
nl_socket_disable_seq_check(sk);

/*
* Define a callback function, which will be called for each notification
* received
*/
nl_socket_modify_cb(sk, NL_CB_VALID, NL_CB_CUSTOM, my_func, NULL);
/* Connect to netlink generic protocol */
nl_connect(sk, NETLINK_GENERIC);

gr_id = genl_family_get_id("family_name");

/* Subscribe to link notifications group */
nl_socket_add_memberships(sk, gr_id, 0);

/*
* Start receiving messages. The function nl_recvmsgs_default() will block
* until one or more netlink messages (notification) are received which
* will be passed on to my_func().
*/
while (1){
nl_recvmsgs_default(sk);
}

return 0;
}

最佳答案

首先我想说我不是 Netlink 的忠实粉丝。我认为它的设计相当糟糕。也就是说,我想我有这个问题的确切答案,所以就这样吧。

您的核心问题是,在使用 Generic Netlink 系列之前,您首先必须注册它(这也适用于普通 Netlink 系列)。内核无法处理它不知道的系列。除非您使用的是已经存在的系列,否则这会影响您使用 Netlink 的方式。

A Generic Netlink Family belongs to a kernel module .这意味着用户空间客户端无法创建族。反过来,这意味着您不能只启动客户端,然后让模块在创建族后立即发送消息。这是因为在客户想要将自己绑定(bind)到它的那一刻,这个家庭并不存在。

你需要做的是:

  • 在插入模块时创建并注册系列和多播组。
  • 启动用户空间客户端并将其绑定(bind)到家庭和多播组。
  • 让内核模块在某个时间点发送消息(在客户端绑定(bind)之后)。
  • 用户空间客户端现在接收到消息。
  • 当模块被移除时,它应该注销该族。

  • 我的代码版本如下。这是内核模块。如您所见,我决定在每两秒运行一次的计时器上重复发送消息。这使您有时间启动客户端:
        #include <linux/kernel.h>
    #include <linux/module.h>
    #include <net/genetlink.h>

    static struct timer_list timer;

    /**
    * This callback runs whenever the socket receives messages.
    * We don't use it now, but Linux complains if we don't define it.
    */
    static int hello(struct sk_buff *skb, struct genl_info *info)
    {
    pr_info("Received a message in kernelspace.\n");
    return 0;
    }

    /**
    * Attributes are fields of data your messages will contain.
    * The designers of Netlink really want you to use these instead of just dumping
    * data to the packet payload... and I have really mixed feelings about it.
    */
    enum attributes {
    /*
    * The first one has to be a throwaway empty attribute; I don't know
    * why.
    * If you remove it, ATTR_HELLO (the first one) stops working, because
    * it then becomes the throwaway.
    */
    ATTR_DUMMY,
    ATTR_HELLO,
    ATTR_FOO,

    /* This must be last! */
    __ATTR_MAX,
    };

    /**
    * Here you can define some constraints for the attributes so Linux will
    * validate them for you.
    */
    static struct nla_policy policies[] = {
    [ATTR_HELLO] = { .type = NLA_STRING, },
    [ATTR_FOO] = { .type = NLA_U32, },
    };

    /**
    * Message type codes. All you need is a hello sorta function, so that's what
    * I'm defining.
    */
    enum commands {
    COMMAND_HELLO,

    /* This must be last! */
    __COMMAND_MAX,
    };

    /**
    * Actual message type definition.
    */
    struct genl_ops ops[] = {
    {
    .cmd = COMMAND_HELLO,
    .flags = 0,
    .policy = policies,
    .doit = hello,
    .dumpit = NULL,
    },
    };

    /**
    * A Generic Netlink family is a group of listeners who can and want to speak
    * your language.
    * Anyone who wants to hear your messages needs to register to the same family
    * as you.
    */
    struct genl_family family = {
    .id = GENL_ID_GENERATE,
    .hdrsize = 0,
    .name = "PotatoFamily",
    .version = 1,
    .maxattr = __ATTR_MAX,
    };

    /**
    * And more specifically, anyone who wants to hear messages you throw at
    * specific multicast groups need to register themselves to the same multicast
    * group, too.
    */
    struct genl_multicast_group groups[] = {
    { .name = "PotatoGroup" },
    };

    void send_multicast(unsigned long arg)
    {
    struct sk_buff *skb;
    void *msg_head;
    unsigned char *msg = "TEST";
    int error;

    pr_info("----- Running timer -----\n");

    pr_info("Newing message.\n");
    skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
    if (!skb) {
    pr_err("genlmsg_new() failed.\n");
    goto end;
    }

    pr_info("Putting message.\n");
    msg_head = genlmsg_put(skb, 0, 0, &family, 0, COMMAND_HELLO);
    if (!msg_head) {
    pr_err("genlmsg_put() failed.\n");
    kfree_skb(skb);
    goto end;
    }

    pr_info("Nla_putting string.\n");
    error = nla_put_string(skb, ATTR_HELLO, msg);
    if (error) {
    pr_err("nla_put_string() failed: %d\n", error);
    kfree_skb(skb);
    goto end;
    }

    pr_info("Nla_putting integer.\n");
    error = nla_put_u32(skb, ATTR_FOO, 12345);
    if (error) {
    pr_err("nla_put_u32() failed: %d\n", error);
    kfree_skb(skb);
    goto end;
    }

    pr_info("Ending message.\n");
    genlmsg_end(skb, msg_head);

    pr_info("Multicasting message.\n");
    /*
    * The family has only one group, so the group ID is just the family's
    * group offset.
    * mcgrp_offset is supposed to be private, so use this value for debug
    * purposes only.
    */
    pr_info("The group ID is %u.\n", family.mcgrp_offset);
    error = genlmsg_multicast_allns(&family, skb, 0, 0, GFP_KERNEL);
    if (error) {
    pr_err("genlmsg_multicast_allns() failed: %d\n", error);
    pr_err("(This can happen if nobody is listening. "
    "Because it's not that unexpected, "
    "you might want to just ignore this error.)\n");
    goto end;
    }

    pr_info("Success.\n");
    end:
    mod_timer(&timer, jiffies + msecs_to_jiffies(2000));
    }

    static int init_socket(void)
    {
    int error;

    pr_info("Registering family.\n");
    error = genl_register_family_with_ops_groups(&family, ops, groups);
    if (error)
    pr_err("Family registration failed: %d\n", error);

    return error;
    }

    static void initialize_timer(void)
    {
    pr_info("Starting timer.\n");

    init_timer(&timer);
    timer.function = send_multicast;
    timer.expires = 0;
    timer.data = 0;

    mod_timer(&timer, jiffies + msecs_to_jiffies(2000));
    }

    static int __init hello_init(void)
    {
    int error;

    error = init_socket();
    if (error)
    return error;

    initialize_timer();

    pr_info("Hello module registered.\n");
    return 0;
    }

    static void __exit hello_exit(void)
    {
    del_timer_sync(&timer);
    genl_unregister_family(&family);
    pr_info("Hello removed.\n");
    }

    module_init(hello_init);
    module_exit(hello_exit);

    MODULE_LICENSE("GPL");

    这是用户空间客户端:
        #include <netlink/netlink.h>
    #include <netlink/socket.h>
    #include <netlink/msg.h>
    #include <netlink/genl/genl.h>

    static struct nl_sock *sk = NULL;

    /**
    * Attributes and commands have to be the same as in kernelspace, so you might
    * want to move these enums to a .h and just #include that from both files.
    */
    enum attributes {
    ATTR_DUMMY,
    ATTR_HELLO,
    ATTR_FOO,

    /* This must be last! */
    __ATTR_MAX,
    };

    enum commands {
    COMMAND_HELLO,

    /* This must be last! */
    __COMMAND_MAX,
    };

    static int fail(int error, char *func_name)
    {
    printf("%s() failed.\n", func_name);
    return error;
    }

    static int nl_fail(int error, char *func_name)
    {
    printf("%s (%d)\n", nl_geterror(error), error);
    return fail(error, func_name);
    }

    /*
    * This function will be called for each valid netlink message received
    * in nl_recvmsgs_default()
    */
    static int cb(struct nl_msg *msg, void *arg)
    {
    struct nlmsghdr *nl_hdr;
    struct genlmsghdr *genl_hdr;
    struct nlattr *attrs[__ATTR_MAX];
    int error;

    printf("The kernel module sent a message.\n");

    nl_hdr = nlmsg_hdr(msg);
    genl_hdr = genlmsg_hdr(nl_hdr);

    if (genl_hdr->cmd != COMMAND_HELLO) {
    printf("Oops? The message type is not Hello; ignoring.\n");
    return 0;
    }

    error = genlmsg_parse(nl_hdr, 0, attrs, __ATTR_MAX - 1, NULL);
    if (error)
    return nl_fail(error, "genlmsg_parse");

    /* Remember: attrs[0] is a throwaway. */

    if (attrs[1])
    printf("ATTR_HELLO: len:%u type:%u data:%s\n",
    attrs[1]->nla_len,
    attrs[1]->nla_type,
    (char *)nla_data(attrs[1]));
    else
    printf("ATTR_HELLO: null\n");

    if (attrs[2])
    printf("ATTR_FOO: len:%u type:%u data:%u\n",
    attrs[2]->nla_len,
    attrs[2]->nla_type,
    *((__u32 *)nla_data(attrs[2])));
    else
    printf("ATTR_FOO: null\n");

    return 0;
    }

    static int do_things(void)
    {
    struct genl_family *family;
    int group;
    int error;

    /* Socket allocation yadda yadda. */
    sk = nl_socket_alloc();
    if (!sk)
    return fail(-1, "nl_socket_alloc");

    nl_socket_disable_seq_check(sk);

    error = nl_socket_modify_cb(sk, NL_CB_VALID, NL_CB_CUSTOM, cb, NULL);
    if (error)
    return nl_fail(error, "nl_socket_modify_cb");

    error = genl_connect(sk);
    if (error)
    return nl_fail(error, "genl_connect");

    /* Find the multicast group identifier and register ourselves to it. */
    group = genl_ctrl_resolve_grp(sk, "PotatoFamily", "PotatoGroup");
    if (group < 0)
    return nl_fail(group, "genl_ctrl_resolve_grp");

    printf("The group is %u.\n", group);

    error = nl_socket_add_memberships(sk, group, 0);
    if (error) {
    printf("nl_socket_add_memberships() failed: %d\n", error);
    return error;
    }

    /* Finally, receive the message. */
    nl_recvmsgs_default(sk);

    return 0;
    }

    int main(void)
    {
    int error;

    error = do_things();

    if (sk)
    nl_socket_free(sk);

    return error;
    }

    关于c - Netlink 组播内核组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26265453/

    31 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com