Netlink Multicast Kernel Group

泪湿孤枕 提交于 2019-12-21 05:41:32

问题


The task I am trying to achieve is actually quite simple (multicast the string "TEST" to a userland daemon), but the kernel module doesn't compile. It stops with the error:

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

But shouldn't it just be the multicast group I defined?

Here is the code for "clarification":

#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");

Thanks for your help.

EDIT

This is the client side code:

#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;
}

回答1:


I'd like to start off by saying I'm not a huge fan of Netlink; I think it's rather poorly designed. That said, I think I have the exact answer to the question, so here it goes.

Your core problem is that before using a Generic Netlink family, you first have to register it (this also applies to normal Netlink families). The kernel cannot handle families it doesn't know. Unless you're using an already existing family, this shapes the way you have to approach Netlink.

A Generic Netlink Family belongs to a kernel module. This means a userspace client cannot create a family. In turn, this means you can't just start the client, then have the module send the message right after it creates the family. This is because the family doesn't exist at the moment the client wanted to bind itself to it.

What you need to do is this:

  1. Create and register the family and the multicast group while you're inserting the module.
  2. Start the userspace client and have it bind itself to the family and multicast group.
  3. Have the kernel module send the message at some point (after the client's bind).
  4. The userspace client now receives the message.
  5. When the module is removed, it should unregister the family.

My version of your code follows. This is the kernel module. As you can see, I decided to send the message repeatedly on a timer that runs every two seconds. This gives you time to start the client:

    #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");

And this is the userspace client:

    #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;
    }



回答2:


This is not a direct answer to the netlink question, but an alternative solution. See comments above about netlink limitations.

UDP sockets can be used on Linux to communicate between a user mode process, such as a daemon, and a kernel mode component such as a loadable module.

Daemon code my_udp.c:

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>

static int rcv_sock;
static int snd_sock;
static struct sockaddr_in rcv_addr_in;
static struct sockaddr_in snd_addr_in;
static pthread_t rcv_thread;

static void *rcv_thread_fn(void *data);

int my_udp_init(void)
{
    int sendlen, receivelen;
    int received = 0;

    if ((rcv_sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
            perror("socket");
            return -1;
    }

    if ((snd_sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
            perror("socket");
            return -1;
    }

    memset(&rcv_addr_in, 0, sizeof(rcv_addr_in));
    rcv_addr_in.sin_family = AF_INET;
    rcv_addr_in.sin_addr.s_addr = inet_addr("127.0.0.1");
    rcv_addr_in.sin_port = htons(MY_IN_PORT);

    receivelen = sizeof(rcv_addr_in);
    if (bind(rcv_sock, (struct sockaddr *) &rcv_addr_in, receivelen) < 0) {
            perror("bind");
            return -1;
    }

    memset(&snd_addr_in, 0, sizeof(snd_addr_in));
    snd_addr_in.sin_family = AF_INET;
    snd_addr_in.sin_addr.s_addr = inet_addr("127.0.0.1");
    snd_addr_in.sin_port = htons(MY_OUT_PORT);

    if (pthread_create(&rcv_thread, NULL, rcv_thread_fn, (void *)"rcv_thread")) {
            return -ENOMEM;
    }

    return 0;
}

void my_udp_cleanup(void)
{
    pthread_join(rcv_thread, NULL);
    close(rcv_sock);
    close(snd_sock);
}

int my_snd_msg(const char *buf, int size)
{
    sendto(snd_sock, buf, size, 0, (struct sockaddr *)&snd_addr_in, sizeof(snd_addr_in));
    return 0;
}

int my_rcv_msg(char *buf, int size)
{
    int cnt = 0;
    if ((cnt = recv(rcv_sock, buf, size, MSG_DONTWAIT)) < 0) {
            if (errno == EAGAIN) {
                    /* This is ok in the non-blocking case. */
                    sleep(1);
                    return 0;
            } else {
                    perror("recv");
                    return -1;
            }
    }
    return cnt;
}

static void *rcv_thread_fn(void *data)
{
    char buffer[64];
    int cnt;

    while (!g_stop) {
            cnt = my_rcv_msg(buffer, 63);
            if (cnt > 0) {
                    printf("message: %s\n", buffer);
            }
    }

    pthread_exit(0);
}

Kernel module code k_udp.c:

#include <linux/module.h>
#include <linux/init.h>
#include <linux/in.h>
#include <net/sock.h>
#include <linux/skbuff.h>
#include <linux/delay.h>
#include <linux/inet.h>
#include <linux/kthread.h>

static struct work_struct rcv_worker;
static struct socket *in_socket = NULL;
static struct socket *out_socket = NULL;
static struct workqueue_struct *wq = NULL;
static struct task_struct *notify_thread = NULL;

void rcv_work_queue(struct work_struct *data)
{
        int len;
        printk(KERN_INFO "%s: *******\n", __func__);

        while ((len = skb_queue_len(&in_socket->sk->sk_receive_queue)) > 0) {
                struct sk_buff *skb = NULL;

                skb = skb_dequeue(&in_socket->sk->sk_receive_queue);
                printk("message len: %i message: %s\n", skb->len - 8, skb->data+8);
                kfree_skb(skb);
        }
}

static void cb_data(struct sock *sk, int bytes)
{
        printk(KERN_INFO "%s: *******\n", __func__);
        queue_work(wq, &rcv_worker);
}

void send_notification(char *text)
{
        struct sockaddr_in to_addr;
        struct msghdr msg;
        struct iovec iov;
        mm_segment_t oldfs;
        int len = 0;

        if (out_socket->sk == NULL) {
                printk(KERN_ERR "%s: socket skbuff is null\n", __func__);
                return;
        }

        iov.iov_base = text;
        len = strlen(text);
        iov.iov_len = len;

        memset(&to_addr, 0, sizeof(to_addr));
        to_addr.sin_family = AF_INET;
        to_addr.sin_addr.s_addr = in_aton("127.0.0.1");
        to_addr.sin_port = htons(MY_OUT_PORT);

        msg.msg_flags = 0;
        msg.msg_name = &to_addr;
        msg.msg_namelen = sizeof(struct sockaddr_in);
        msg.msg_control = NULL;
        msg.msg_controllen = 0;
        msg.msg_iov = &iov;
        msg.msg_iovlen = 1;
        msg.msg_control = NULL;

        oldfs = get_fs();
        set_fs(KERNEL_DS);
        sock_sendmsg(out_socket, &msg, len);
        set_fs(oldfs);
}

static int k_udp_notify_thread(void *data)
{
        int i = 0;
        while (!kthread_should_stop()) {
                char buf[64];

                sprintf(buf, "test from kernel%d\n", i++);
                send_notification(buf);
                msleep(1000);
        }
        return 0;
}

int k_udp_init(void)
{
        struct sockaddr_in addr_out;
        struct sockaddr_in addr_in;
        int rc = 0;
        printk("%s\n", __func__);
        if (in_socket) {
                printk(KERN_INFO "%s: socket already set up\n", __func__);
                return 0;
        }

        if (sock_create(PF_INET, SOCK_DGRAM, IPPROTO_UDP, &in_socket) < 0) {
                printk( KERN_ERR "%s: failed to create socket\n", __func__);
                return -EIO;
        }
        addr_in.sin_family = AF_INET;
        addr_in.sin_addr.s_addr = in_aton("127.0.0.1");
        addr_in.sin_port = htons( (unsigned short)MY_IN_PORT);
        rc = in_socket->ops->bind(in_socket, (struct sockaddr *)&addr_in, sizeof(addr_in));
        if (rc) {
                printk(KERN_ERR "%s: failed to bind\n", __func__);
                sock_release(in_socket);
                in_socket = NULL;
                return -EIO;
        }
        in_socket->sk->sk_data_ready = cb_data;

        if (sock_create(PF_INET, SOCK_DGRAM, IPPROTO_UDP, &out_socket) < 0) {
                printk( KERN_ERR "%s: failed to create socket\n", __func__);
                sock_release(in_socket);
                in_socket = NULL;
                return -EIO;
        }
        addr_out.sin_family = AF_INET;
        addr_out.sin_addr.s_addr = in_aton("127.0.0.1");
        addr_out.sin_port = htons( (unsigned short)MY_OUT_PORT);
        rc = out_socket->ops->connect(out_socket, (struct sockaddr *)&addr_out, sizeof(addr_out), 0);
        if (rc) {
                printk(KERN_ERR "%s: failed to connect\n", __func__);
                sock_release(in_socket);
                in_socket = NULL;
                sock_release(out_socket);
                out_socket = NULL;
                return -EIO;
        }

        notify_thread = kthread_create(k_udp_notify_thread, NULL, "k_notify_thread");
        if (notify_thread) {
                printk(KERN_INFO "%s: notify thread created\n", __func__);
                wake_up_process(notify_thread);
        } else {
                printk(KERN_ERR "%s: failed to create notify thread\n", __func__);
        }

        INIT_WORK(&rcv_worker, rcv_work_queue);
        wq = create_singlethread_workqueue("k_rcv_wq");
        if (!wq) {
                return -ENOMEM;
        }

        printk(KERN_INFO "%s: success\n", __func__);

        return 0;
}

void k_udp_cleanup(void)
{
        /* Should we check that the thread is still valid (hasn't exited)? */
        if (notify_thread) {
                kthread_stop(notify_thread);
                notify_thread = NULL;
        }

        if (in_socket) {
                sock_release(in_socket);
                in_socket = NULL;
        }

        if (out_socket) {
                sock_release(out_socket);
                out_socket = NULL;
        }
        if (wq) {
                flush_workqueue(wq);
                destroy_workqueue(wq);
                wq = NULL;
        }
}

Note: I've renamed some of the variables and function names from the code I'm using, so you may need to make changes to compile (if I missed something). Be sure to make the ports match between user/kernel components.

The above code was derived from multiple samples on the net and within the Linux kernel.



来源:https://stackoverflow.com/questions/26265453/netlink-multicast-kernel-group

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!