c - Netlink 组播内核组

标签 c linux-kernel kernel-module multicast netlink

我试图实现的任务实际上非常简单(将字符串“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/

    相关文章:

    C 结构和指针

    linux - vdso32 和 vdsox32 有什么区别?

    c - 无法处理内核空指针取消引用

    c - 向静态内核模块发送参数的方式

    android - 移动应用专用内存分配

    用于生成随机数的字符设备实现

    c++ - C/C++ 中的 JSON <-> XML

    c++ - 启用 dll 的 gflags 完整堆不起作用

    C 开关/案例宏,多个案例

    linux - 如何从 IRQ 范围内的非整体内核模块进行软重启?