>用户空间程序开始绑定到用户定义的多播组.
>插入内核模块>内核模块向此多播组发送消息>用户空间程序接收消息这是我的代码:
======用户空间程序======
#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, 0, 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: %s\n", 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: %s\n", __FUNCTION__); msg_size = strlen(msg); 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, 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, 0, MYGRP, 0); if (res < 0) { printk(KERN_INFO "Error while sending bak to user, err id: %d\n", res); } } static int __init hello_init(void) { struct netlink_kernel_cfg cfg = { .groups = MYGRP, }; printk("Entering: %s\n", __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 module\n"); 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).
> addr.nl_groups = MYMGRP;由于某种原因不起作用.但这确实如下:setsockopt(sock,270,NETLINK_ADD_MEMBERSHIP,& group,sizeof(group)).不致命:
>在这种情况下,模块不会侦听组消息,因此您不需要在netlink_kernel_create()参数中包含多播组.
此外,并非真正与netlink相关,但有用:
> strlen()不包含null chara.在消息分配期间,您应该添加一个字节来弥补这一点.
>在这种情况下,NLMSG_DATA(& nlh)是未定义的行为.这是因为你的标题和数据都在单独的内存块中,不能保证被粘合,并且所有宏都在nlh之后访问内存块:#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, SOCK_RAW, MYPROTO); if (sock < 0) { printf("sock < 0.\n"); return sock; } memset((void *) &addr, 0, 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, 270, NETLINK_ADD_MEMBERSHIP, &group, sizeof(group)) < 0) { printf("setsockopt < 0\n"); 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, &msg, 0); if (ret < 0) printf("ret < 0.\n"); else printf("Received message payload: %s\n", 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 MYPROTO NETLINK_USERSOCK /* Multicast group, 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, 0, 1, NLMSG_DONE, msg_size + 1, 0); strcpy(nlmsg_data(nlh), msg); pr_info("Sending skb.\n"); res = nlmsg_multicast(nl_sk, skb, 0, MYGRP, GFP_KERNEL); if (res < 0) pr_info("nlmsg_multicast() error: %d\n", 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.)
精彩评论