代码之家  ›  专栏  ›  技术社区  ›  binW

如何使用netlinksocket与内核模块通信?

  •  42
  • binW  · 技术社区  · 15 年前

    我正在尝试编写一个linux内核模块,它使用netlink与用户进程通信。我之所以使用netlink,是因为我想与之通信的用户程序只使用套接字进行通信,我无法将其更改为添加 ioctl()

    问题是我不知道怎么做。我在谷歌上搜索过,但我找到的所有例子都是老样子的 this one 对于当前的内核版本不再有效。我也看过 this SO question 但这里的样本使用 libnl 对于socket操作,但我希望坚持使用标准的socket函数(由 sys/socket.h

    也请不要告诉我看内核代码。我已经这样做了,但这将需要很多时间,我没有很多它离开。

    更新: netlink_unicast() 不起作用。它不仅不工作,调用挂起系统,然后我必须重新启动机器。有谁能看一下,告诉我我做错了什么。这个 调用在下面的代码中被注释。对于内核到用户程序消息,应该取消注释它。

    用户程序

    #include <sys/socket.h>  
    #include <linux/netlink.h>  
    #define NETLINK_USER 31  
    #define MAX_PAYLOAD 1024  /* maximum payload size*/  
    
    struct sockaddr_nl src_addr, dest_addr;  
    struct nlmsghdr *nlh = NULL;  
    struct iovec iov;  
    int sock_fd;  
    struct msghdr msg;  
    
    void main()  
    {  
        sock_fd=socket(PF_NETLINK, SOCK_RAW, NETLINK_USER);  
        if(sock_fd<0)  
            return -1;  
    
        memset(&src_addr, 0, sizeof(src_addr));  
        src_addr.nl_family = AF_NETLINK;  
        src_addr.nl_pid = getpid();  /* self pid */  
        /* interested in group 1<<0 */  
        bind(sock_fd, (struct sockaddr*)&src_addr,  
          sizeof(src_addr));  
    
        memset(&dest_addr, 0, sizeof(dest_addr));  
        memset(&dest_addr, 0, sizeof(dest_addr));  
        dest_addr.nl_family = AF_NETLINK;  
        dest_addr.nl_pid = 0;   /* For Linux Kernel */  
        dest_addr.nl_groups = 0; /* unicast */  
    
        nlh = (struct nlmsghdr *)malloc(  
                              NLMSG_SPACE(MAX_PAYLOAD));  
        memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));  
        nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);  
        nlh->nlmsg_pid = getpid();  
        nlh->nlmsg_flags = 0;  
    
        strcpy(NLMSG_DATA(nlh), "Hello");  
    
        iov.iov_base = (void *)nlh;  
        iov.iov_len = nlh->nlmsg_len;  
        msg.msg_name = (void *)&dest_addr;  
        msg.msg_namelen = sizeof(dest_addr);  
        msg.msg_iov = &iov;  
        msg.msg_iovlen = 1;  
    
        printf("Sending message to kernel\n");  
        sendmsg(sock_fd,&msg,0);  
        printf("Waiting for message from kernel\n");  
    
        /* Read message from kernel */  
        recvmsg(sock_fd, &msg, 0);  
        printf(" Received message payload: %s\n",  
            NLMSG_DATA(nlh));  
        close(sock_fd);  
    }
    

    #include <linux/module.h>  
    #include <linux/kernel.h>  
    #include <linux/init.h>  
    #include <net/sock.h>  
    #include <linux/socket.h>  
    #include <linux/net.h>  
    #include <asm/types.h>  
    #include <linux/netlink.h>  
    #include <linux/skbuff.h>  
    
    #define NETLINK_USER 31  
    
    struct sock *nl_sk = NULL;  
    
    static void hello_nl_recv_msg(struct sk_buff *skb)  
    {
            struct nlmsghdr *nlh;  
            int pid;  
    
            printk(KERN_INFO "Entering: %s\n", __FUNCTION__);  
    
            nlh=(struct nlmsghdr*)skb->data;  
            printk(KERN_INFO "Netlink received msg payload: %s\n",
                (char*)NLMSG_DATA(nlh));  
            pid = nlh->nlmsg_pid; /*pid of sending process */  
            NETLINK_CB(skb).dst_group = 0; /* not in mcast group */  
            NETLINK_CB(skb).pid = 0;      /* from kernel */  
            //NETLINK_CB(skb).groups = 0; /* not in mcast group */  
            //NETLINK_CB(skb).dst_pid = pid;  
            printk("About to send msg bak:\n");  
            //netlink_unicast(nl_sk,skb,pid,MSG_DONTWAIT);  
    
    }  
    
    static int __init hello_init(void)  
    {  
    
            printk("Entering: %s\n",__FUNCTION__);  
            nl_sk=netlink_kernel_create(&init_net, NETLINK_USER, 0,
                   hello_nl_recv_msg, NULL, THIS_MODULE);  
            if(!nl_sk)  
            {   
                    printk(KERN_ALERT "Error creating socket.\n");  
                    return -10;  
            }  
            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);  
    
    4 回复  |  直到 6 年前
        1
  •  62
  •   Ciro Santilli OurBigBook.com    7 年前

    在阅读了内核源代码之后,我终于设法让netlink套接字为我工作。下面是一个Netlink套接字基础的例子,即打开一个Netlink套接字,读写它,然后关闭它。

    #include <linux/module.h>
    #include <net/sock.h> 
    #include <linux/netlink.h>
    #include <linux/skbuff.h> 
    #define NETLINK_USER 31
    
    struct sock *nl_sk = NULL;
    
    static void hello_nl_recv_msg(struct sk_buff *skb)
    {
    
        struct nlmsghdr *nlh;
        int pid;
        struct sk_buff *skb_out;
        int msg_size;
        char *msg = "Hello from kernel";
        int res;
    
        printk(KERN_INFO "Entering: %s\n", __FUNCTION__);
    
        msg_size = strlen(msg);
    
        nlh = (struct nlmsghdr *)skb->data;
        printk(KERN_INFO "Netlink received msg payload:%s\n", (char *)nlmsg_data(nlh));
        pid = nlh->nlmsg_pid; /*pid of sending process */
    
        skb_out = nlmsg_new(msg_size, 0);
        if (!skb_out) {
            printk(KERN_ERR "Failed to allocate new skb\n");
            return;
        }
    
        nlh = nlmsg_put(skb_out, 0, 0, NLMSG_DONE, msg_size, 0);
        NETLINK_CB(skb_out).dst_group = 0; /* not in mcast group */
        strncpy(nlmsg_data(nlh), msg, msg_size);
    
        res = nlmsg_unicast(nl_sk, skb_out, pid);
        if (res < 0)
            printk(KERN_INFO "Error while sending bak to user\n");
    }
    
    static int __init hello_init(void)
    {
    
        printk("Entering: %s\n", __FUNCTION__);
        //nl_sk = netlink_kernel_create(&init_net, NETLINK_USER, 0, hello_nl_recv_msg, NULL, THIS_MODULE);
        struct netlink_kernel_cfg cfg = {
            .input = hello_nl_recv_msg,
        };
    
        nl_sk = netlink_kernel_create(&init_net, NETLINK_USER, &cfg);
        if (!nl_sk) {
            printk(KERN_ALERT "Error creating socket.\n");
            return -10;
        }
    
        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 <linux/netlink.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/socket.h>
    #include <unistd.h>
    
    #define NETLINK_USER 31
    
    #define MAX_PAYLOAD 1024 /* maximum payload size*/
    struct sockaddr_nl src_addr, dest_addr;
    struct nlmsghdr *nlh = NULL;
    struct iovec iov;
    int sock_fd;
    struct msghdr msg;
    
    int main()
    {
        sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_USER);
        if (sock_fd < 0)
            return -1;
    
        memset(&src_addr, 0, sizeof(src_addr));
        src_addr.nl_family = AF_NETLINK;
        src_addr.nl_pid = getpid(); /* self pid */
    
        bind(sock_fd, (struct sockaddr *)&src_addr, sizeof(src_addr));
    
        memset(&dest_addr, 0, sizeof(dest_addr));
        dest_addr.nl_family = AF_NETLINK;
        dest_addr.nl_pid = 0; /* For Linux Kernel */
        dest_addr.nl_groups = 0; /* unicast */
    
        nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));
        memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));
        nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);
        nlh->nlmsg_pid = getpid();
        nlh->nlmsg_flags = 0;
    
        strcpy(NLMSG_DATA(nlh), "Hello");
    
        iov.iov_base = (void *)nlh;
        iov.iov_len = nlh->nlmsg_len;
        msg.msg_name = (void *)&dest_addr;
        msg.msg_namelen = sizeof(dest_addr);
        msg.msg_iov = &iov;
        msg.msg_iovlen = 1;
    
        printf("Sending message to kernel\n");
        sendmsg(sock_fd, &msg, 0);
        printf("Waiting for message from kernel\n");
    
        /* Read message from kernel */
        recvmsg(sock_fd, &msg, 0);
        printf("Received message payload: %s\n", NLMSG_DATA(nlh));
        close(sock_fd);
    }
    

    关于魔常数的相关线索 NETLINK_USER 31 : Can I have more than 32 netlink sockets in kernelspace?

        2
  •  8
  •   guoger    11 年前

    万一有人不知道如何编译,谷歌“如何编译和加载内核模块”

    参考 http://www.cyberciti.biz/tips/compiling-linux-kernel-module.html

    抓取内核源代码,您将根据它编译模块 http://kernel.org

    或者如果您运行的是预期的内核,只需更新您的头文件

    # apt-get install kernel-headers-$(uname -r)
    

    例如,创建一个makefile

    obj-m = hello.o
    KVERSION = $(shell uname -r)
    all:
            make -C /lib/modules/$(KVERSION)/build M=$(PWD) modules
    clean:
            make -C /lib/modules/$(KVERSION)/build M=$(PWD) clean
    

    你会得到一堆文件。ko是您将加载到内核中的一个,run

    # insmod hello.ko
    

    如果您使用lsmod检查所有加载的模块,您将找到您的模块,很可能会看到:

    hello       12575  0 
    

    gcc hello.c -o hello.o
    ./hello.o
    

    如果一切正常,您将使用binW的代码得到以下消息:

    Sending message to kernel
    Waiting for message from kernel
     Received message payload: Hello from kernel
    

    # rmmod hello
    
        3
  •  7
  •   M.A.R. yvo.engr    8 年前

    它适用于我的内核3.2。对于内核3.6&在上面,它需要一点改变 netlink_kernel_create 功能。

     struct netlink_kernel_cfg cfg = {
                    .groups = 1,
                    .input = hello_nl_recv_msg,
            };
            printk("Entering: %s\n", __FUNCTION__);
            nl_sk = netlink_kernel_create(&init_net, NETLINK_USER, &cfg);
    
        4
  •  0
  •   Mostafa Dsg    10 年前

    您需要在客户端代码中包含以下头文件:

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    
    推荐文章