通过C中的Netlink从内核到用户空间的多播
我试图编写一个使用Netlink在内核和用户空间之间进行通信的简单程序.基本上这就是我想要实现的目标:
>用户空间程序开始绑定到用户定义的多播组. 这是我的代码: ======用户空间程序====== #include<stdio.h> #include<string.h> #include<stdlib.h> #include<sys/socket.h> #include<linux/netlink.h> #include<sys/types.h> #include<unistd.h> #define MYPROTO NETLINK_USERSOCK #define MYMGRP 0x21 //User defined group,consistent in both kernel prog and user prog int open_netlink() { int sock = socket(AF_NETLINK,SOCK_RAW,MYPROTO); struct sockaddr_nl addr; memset((void *)&addr,sizeof(addr)); if (sock<0) return sock; addr.nl_family = AF_NETLINK; addr.nl_pid = getpid(); addr.nl_groups = MYMGRP; if (bind(sock,(struct sockaddr *)&addr,sizeof(addr))<0) return -1; return sock; } int read_event(int sock) { struct sockaddr_nl nladdr; struct msghdr msg; struct iovec iov[2]; struct nlmsghdr nlh; char buffer[65536]; int ret; iov[0].iov_base = (void *)&nlh; iov[0].iov_len = sizeof(nlh); iov[1].iov_base = (void *)buffer; iov[1].iov_len = sizeof(buffer); msg.msg_name = (void *)&(nladdr); msg.msg_namelen = sizeof(nladdr); msg.msg_iov = iov; msg.msg_iovlen = sizeof(iov)/sizeof(iov[0]); ret=recvmsg(sock,&msg,0); if (ret<0) { return ret; } printf("Received message payload: %sn",NLMSG_DATA(&nlh)); } int main(int argc,char *argv[]) { int nls = open_netlink(); if (nls<0) { err(1,"netlink"); } while (1) read_event(nls); return 0; } ======内核模块====== #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/rtnetlink.h> #include <linux/skbuff.h> #include <linux/delay.h> #define NETLINK_USER 31 #define MYGRP 0x21 //User defined group,consistent in both kernel prog and user prog struct sock *nl_sk = NULL; static void send_to_user() { struct sk_buff *skb_out; struct nlmsghdr *nlh; int msg_size; char *msg = "Hello from kernel"; int res; printk(KERN_INFO "Entering: %sn",__FUNCTION__); msg_size = strlen(msg); skb_out = nlmsg_new(msg_size,0); if (!skb_out) { printk(KERN_ERR "Failed to allocate new skbn"); return; } nlh = nlmsg_put(skb_out,1,NLMSG_DONE,msg_size,0); //NETLINK_CB(skb_out).dst_group = 1; /* Multicast to group 1,1<<0 */ strncpy(nlmsg_data(nlh),msg,msg_size); res = nlmsg_multicast(nl_sk,skb_out,MYGRP,0); if (res < 0) { printk(KERN_INFO "Error while sending bak to user,err id: %dn",res); } } static int __init hello_init(void) { struct netlink_kernel_cfg cfg = { .groups = MYGRP,}; printk("Entering: %sn",__FUNCTION__); nl_sk = netlink_kernel_create(&init_net,NETLINK_USER,&cfg); if (!nl_sk) { printk(KERN_ALERT "Error creating socket.n"); return -10; } send_to_user(); return 0; } static void __exit hello_exit(void) { printk(KERN_INFO "exiting hello modulen"); netlink_kernel_release(nl_sk); } module_init(hello_init); module_exit(hello_exit); 由于内核模块只会在初始化期间发送一次消息,因此我首先运行监听程序然后插入模块,尽管我总是遇到这个错误: Error while sending bak to user,err id: -3 追踪到错误ID时,它反映在netlink / af_netlink.c中的这段代码中: if (info.delivery_failure) { kfree_skb(info.skb2); return -ENOBUFS; } consume_skb(info.skb2); if (info.delivered) { if (info.congested && (allocation & __GFP_WAIT)) yield(); return 0; } return -ESRCH; 我认为它不是delivery_failure,但由于某些原因仍未提供. 我指的是这个example,其中作者的程序一直在改变监听路线.虽然我想使用用户定义的多播组. 有任何想法吗?提前致谢! 解决方法
这些是我在代码中发现的两个关键问题:
>协议族和组播组都需要在内核prog和用户prog中保持一致.您的协议系列是用户空间中的NETLINK_USERSOCK(2)和内核空间中的NETLINK_USER(31). 不致命: >在这种情况下,模块不会侦听组消息,因此您不需要在netlink_kernel_create()参数中包含多播组. 此外,并非真正与netlink相关,但有用: > strlen()不包含null chara.在消息分配期间,您应该添加一个字节来弥补这一点. #define NLMSG_DATA(nlh)((void *)(((char *)nlh)NLMSG_LENGTH(0))) 这是我的代码版本: 用户空间程序: #include <stdio.h> #include <string.h> #include <stdlib.h> #include <sys/socket.h> #include <linux/netlink.h> #include <unistd.h> /* Protocol family,consistent in both kernel prog and user prog. */ #define MYPROTO NETLINK_USERSOCK /* Multicast group,consistent in both kernel prog and user prog. */ #define MYMGRP 21 int open_netlink(void) { int sock; struct sockaddr_nl addr; int group = MYMGRP; sock = socket(AF_NETLINK,MYPROTO); if (sock < 0) { printf("sock < 0.n"); return sock; } memset((void *) &addr,sizeof(addr)); addr.nl_family = AF_NETLINK; addr.nl_pid = getpid(); /* This doesn't work for some reason. See the setsockopt() below. */ /* addr.nl_groups = MYMGRP; */ if (bind(sock,(struct sockaddr *) &addr,sizeof(addr)) < 0) { printf("bind < 0.n"); return -1; } /* * 270 is SOL_NETLINK. See * http://lxr.free-electrons.com/source/include/linux/socket.h?v=4.1#L314 * and * https://stackoverflow.com/questions/17732044/ */ if (setsockopt(sock,&group,sizeof(group)) < 0) { printf("setsockopt < 0n"); return -1; } return sock; } void read_event(int sock) { struct sockaddr_nl nladdr; struct msghdr msg; struct iovec iov; char buffer[65536]; int ret; iov.iov_base = (void *) buffer; iov.iov_len = sizeof(buffer); msg.msg_name = (void *) &(nladdr); msg.msg_namelen = sizeof(nladdr); msg.msg_iov = &iov; msg.msg_iovlen = 1; printf("Ok,listening.n"); ret = recvmsg(sock,0); if (ret < 0) printf("ret < 0.n"); else printf("Received message payload: %sn",NLMSG_DATA((struct nlmsghdr *) &buffer)); } int main(int argc,char *argv[]) { int nls; nls = open_netlink(); if (nls < 0) return nls; while (1) read_event(nls); return 0; } 这是内核模块: #include <linux/module.h> #include <linux/kernel.h> #include <linux/netlink.h> #include <net/netlink.h> #include <net/net_namespace.h> /* Protocol family,consistent in both kernel prog and user prog. */ #define MYGRP 21 static struct sock *nl_sk = NULL; static void send_to_user(void) { struct sk_buff *skb; struct nlmsghdr *nlh; char *msg = "Hello from kernel"; int msg_size = strlen(msg) + 1; int res; pr_info("Creating skb.n"); skb = nlmsg_new(NLMSG_ALIGN(msg_size + 1),GFP_KERNEL); if (!skb) { pr_err("Allocation failure.n"); return; } nlh = nlmsg_put(skb,msg_size + 1,0); strcpy(nlmsg_data(nlh),msg); pr_info("Sending skb.n"); res = nlmsg_multicast(nl_sk,skb,GFP_KERNEL); if (res < 0) pr_info("nlmsg_multicast() error: %dn",res); else pr_info("Success.n"); } static int __init hello_init(void) { pr_info("Inserting hello module.n"); nl_sk = netlink_kernel_create(&init_net,MYPROTO,NULL); if (!nl_sk) { pr_err("Error creating socket.n"); return -10; } send_to_user(); netlink_kernel_release(nl_sk); return 0; } static void __exit hello_exit(void) { pr_info("Exiting hello module.n"); } module_init(hello_init); module_exit(hello_exit); MODULE_LICENSE("GPL"); 在内核3.13中测试过. (我可以建议人们使用libnl-3而不是原始套接字用于用户空间程序.它的多播Netlink文档是actually decent.) (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |