Linux中与内核通信的Netlink机制.doc_第1页
Linux中与内核通信的Netlink机制.doc_第2页
Linux中与内核通信的Netlink机制.doc_第3页
Linux中与内核通信的Netlink机制.doc_第4页
Linux中与内核通信的Netlink机制.doc_第5页
已阅读5页,还剩15页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

Linux中与内核通信的Netlink机制Netlink在2.6版本的内核中变化也是很大的,在最新的2.6.37内核中,其定义已经改成下面这种形式,传递的参数已经达到6个。其中第一个参数和mutex参数都是最新添加的。Mutex也可以为空。这里主要是关于内核空间中的netlink函数的使用。extern struct sock *netlink_kernel_create(struct net *net, int unit,unsigned int groups, void (*input)(struct sk_buff *skb), struct mutex *cb_mutex, struct module *module); struct net是一个网络名字空间namespace,在不同的名字空间里面可以有自己的转发信息库,有自己的一套net_device等等。默认情况下都是使用 init_net这个全局变量,下面是内核中调用netlink_kernel_create()函数的一个示例。在内核中,audit_sock = netlink_kernel_create(&init_net, NETLINK_AUDIT, 0, audit_receive, NULL, THIS_MODULE); 模块调用函数 netlink_unicast 来发送单播消息: int netlink_unicast(struct sock *ssk, struct sk_buff *skb, u32 pid, int nonblock) 参数ssk为函数 netlink_kernel_create()返回的socket,参数skb存放消息,它的data字段指向要发送的netlink消息结构,而 skb的控制块保存了消息的地址信息,前面的宏NETLINK_CB(skb)就用于方便设置该控制块,参数pid为接收消息进程的pid,参数nonblock表示该函数是否为非阻塞,如果为1,该函数将在没有接收缓存可利用时立即返回,而如果为0,该函数在没有接收缓存可利用 定时睡眠。 netlink的内核实现在.c文件 net/core/af_netlink.c中,内核模块要想使用netlink,也必须包含头文件linux/netlink.h。内核使用 netlink需要专门的API,这完全不同于用户态应用对netlink的使用。如果用户需要增加新的netlink协议类型,必须通过修改 linux/netlink.h来实现,当然,目前的netlink实现已经包含了一个通用的协议类型NETLINK_GENERIC以方便用户使用,用户可以直接使用它而不必增加新的协议类型。前面讲到,为了增加新的netlink协议类型,用户仅需增加如下定义到linux/netlink.h就可以: 只要增加这个定义之后,用户就可以在内核的任何地方引用该协议。 在内核中,为了创建一个netlink socket用户需要调用如下函数:extern struct sock *netlink_kernel_create(struct net *net, int unit,unsigned int groups, void (*input)(struct sk_buff *skb), struct mutex *cb_mutex, struct module *module); struct net是一个网络名字空间namespace,在不同的名字空间里面可以有自己的转发信息库,有自己的一套net_device等等。默认情况下都是使用init_net这个全局变量 参数unit表示netlink协议类型,如 NETLINK_MYTEST,参数input则为内核模块定义的netlink消息处理函数,当有消息到达这个netlink socket时,该input函数指针就会被引用。函数指针input的参数skb实际上就是函数netlink_kernel_create返回的 struct sock指针,sock实际是socket的一个内核表示数据结构,用户态应用创建的socket在内核中也会有一个struct sock结构来表示。 函数input()会在发送进程执行sendmsg()时被调用,这样处理消息比较及时,但是,如果消息特别长时,这样处理将增加系统调用sendmsg()的执行时间,也就是说当用户的程序调用sendmsg ()函数时,如果input()函数处理时间过长,也就是说input()函数不执行不完,用户程序调用的sendmsg()函数就不会返回。只有当内核空间中的input()函数返回时,用户调用的sendmsg()函数才会返回。对于这种情况,可以定义一个内核线程专门负责消息接收,而函数input 的工作只是唤醒该内核线程,这样sendmsg将很快返回。(这里网上的的说明)不过在查看Linux2.6.37版本的内核时并没有发现这种处理过程,一般都是按下面的方法进行处理。这里注册的netlink协议为NETLINK_XFRM。nlsk = netlink_kernel_create(net, NETLINK_XFRM, XFRMNLGRP_MAX, xfrm_netlink_rcv, NULL, THIS_MODULE); static void xfrm_netlink_rcv(struct sk_buff *skb) mutex_lock(&xfrm_cfg_mutex); netlink_rcv_skb(skb, &xfrm_user_rcv_msg); mutex_unlock(&xfrm_cfg_mutex);在netlink_rcv_skb()函数中进行接收处理。 int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, u32 pid, u32 group, gfp_t allocation) 前面的三个参数与 netlink_unicast相同,参数group为接收消息的多播组,该参数的每一个位代表一个多播组,因此如果发送给多个多播组,就把该参数设置为多个多播组组ID的位或。参数allocation为内核内存分配类型,一般地为GFP_ATOMIC或GFP_KERNEL,GFP_ATOMIC用于原子的上下文(即不可以睡眠),而GFP_KERNEL用于非原子上下文。 NETLINK_CB(skb).pid = 0;NETLINK_CB(skb).dst_pid = 0;NETLINK_CB(skb).dst_group = 1; 字段pid表示消息发送者进程 ID,也即源地址,对于内核,它为 0, dst_pid 表示消息接收者进程 ID,也即目标地址,如果目标为组或内核,它设置为 0,否则 dst_group 表示目标组地址,如果它目标为某一进程或内核,dst_group 应当设置为 0。 下面是参考网上使用netlink写的和内核通信的两个程序,一个是用户空间,一个是内核空间。内核空间:#include #include #include #include #include #include #include #define NETLINK_TEST 25#define MAX_MSGSIZE 1024int stringlength(char *s);void sendnlmsg(char * message);int pid;int err;struct sock *nl_sk = NULL;int flag = 0;void sendnlmsg(char *message) struct sk_buff *skb_1; struct nlmsghdr *nlh; int len = NLMSG_SPACE(MAX_MSGSIZE); int slen = 0; if(!message | !nl_sk) return ; skb_1 = alloc_skb(len,GFP_KERNEL); if(!skb_1) printk(KERN_ERR my_net_link:alloc_skb_1 errorn); slen = stringlength(message); nlh = nlmsg_put(skb_1,0,0,0,MAX_MSGSIZE,0); NETLINK_CB(skb_1).pid = 0; NETLINK_CB(skb_1).dst_group = 0; messageslen= 0; memcpy(NLMSG_DATA(nlh),message,slen+1); printk(my_net_link:send message %s.n,(char *)NLMSG_DATA(nlh); netlink_unicast(nl_sk,skb_1,pid,MSG_DONTWAIT);int stringlength(char *s) int slen = 0; for(; *s; s+) slen+; return slen;void nl_data_ready(struct sk_buff *_skb) struct sk_buff *skb; struct nlmsghdr *nlh; char str100; struct completion cmpl; int i=10; skb = skb_get (_skb); if(skb-len = NLMSG_SPACE(0) nlh = nlmsg_hdr(skb); memcpy(str, NLMSG_DATA(nlh), sizeof(str); printk(Message received:%sn,str) ; pid = nlh-nlmsg_pid; while(i-) init_completion(&cmpl); wait_for_completion_timeout(&cmpl,3 * HZ); sendnlmsg(I am from kernel!); flag = 1; kfree_skb(skb); / Initialize netlinkint netlink_init(void) nl_sk = netlink_kernel_create(&init_net, NETLINK_TEST, 1, nl_data_ready, NULL, THIS_MODULE); if(!nl_sk) printk(KERN_ERR my_net_link: create netlink socket error.n); return 1; printk(my_net_link_3: create netlink socket ok.n); return 0;static void netlink_exit(void) if(nl_sk != NULL) sock_release(nl_sk-sk_socket); printk(my_net_link: self module exitedn);module_init(netlink_init);module_exit(netlink_exit);MODULE_AUTHOR(frankzfz);MODULE_LICENSE(GPL); 下面是用户空间的程序:#include #include #include #include #include #include #include #include #include #include #include #define NETLINK_TEST 25#define MAX_PAYLOAD 1024 / maximum payload sizeint main(int argc, char* argv) int state; struct sockaddr_nl src_addr, dest_addr; struct nlmsghdr *nlh = NULL; struct iovec iov; struct msghdr msg; int sock_fd, retval; int state_smg = 0; / Create a socket sock_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_TEST); if(sock_fd = -1) printf(error getting socket: %s, strerror(errno); return -1; / To prepare binding memset(&msg,0,sizeof(msg); memset(&src_addr, 0, sizeof(src_addr); src_addr.nl_family = AF_NETLINK; src_addr.nl_pid = getpid(); / self pid src_addr.nl_groups = 0; / multi cast retval = bind(sock_fd, (struct sockaddr*)&src_addr, sizeof(src_addr); if(retval nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD); nlh-nlmsg_pid = getpid(); nlh-nlmsg_flags = 0; strcpy(NLMSG_DATA(nlh),Hello you!); iov.iov_base = (void *)nlh; iov.iov_len = NLMSG_SPACE(MAX_PAYLOAD); / iov.iov_len = nlh-nlmsg_len; memset(&msg, 0, sizeof(msg); msg.msg_name = (void *)&dest_addr; msg.msg_namelen = sizeof(dest_addr); msg.msg_iov = &iov; msg.msg_iovlen = 1; printf(state_smgn); state_smg = sendmsg(sock_fd,&msg,0); if(state_smg = -1) printf(get error sendmsg = %sn,strerror(errno); memset(nlh,0,NLMSG_SPACE(MAX_PAYLOAD); printf(waiting received!n); / Read message from kernel while(1) printf(In while recvmsgn); state = recvmsg(sock_fd, &msg, 0); if(state0) printf(statelen = NLMSG_SPACE(0) 194 195 nlh = nlmsg_hdr(skb); 196 197 memcpy(str, NLMSG_DATA(nlh), sizeof(str); 198 /DbgPrint(Message received: %s/n, str); 199 200 / H stands for Hello message. 201 if(str0 = H) 202 203 pid = nlh-nlmsg_pid; 204 u_connected = 1; 205 /sendnlmsg(Hello reply.); 206 207 / E stands for Exit message 208 else if(str0 = E) 209 210 u_connected = 0; 211 pid = 0; 212 213 kfree_skb(skb); 214 215 185 void nl_data_ready(struct sk_buff *_skb)186 187 struct sk_buff *skb;188 struct nlmsghdr *nlh;189 char str100;190 191 skb = skb_get (_skb);192 193 if(skb-len = NLMSG_SPACE(0)194 195 nlh = nlmsg_hdr(skb);196 197 memcpy(str, NLMSG_DATA(nlh), sizeof(str);198 /DbgPrint(Message received: %s/n, str);199 200 / H stands for Hello message.201 if(str0 = H)202 203 pid = nlh-nlmsg_pid;204 u_connected = 1;205 /sendnlmsg(Hello reply.);206 207 / E stands for Exit message208 else if(str0 = E)209 210 u_connected = 0;211 pid = 0;212 213 kfree_skb(skb);214 215 191 skb = skb_get (_skb); 191 skb = skb_get (_skb); 获取实际数据包。该函数的参数为netlink数据包的首地址,而sk_buff为网络协议栈使用的数据结构,两者存在细微差别。195 nlh = nlmsg_hdr(skb); 195 nlh = nlmsg_hdr(skb); 获取netlink数据包中netlink header的起始地址。197 memcpy(str, NLMSG_DATA(nlh), sizeof(str); 197 memcpy(str, NLMSG_DATA(nlh), sizeof(str); 将netlink数据包的数据区拷贝到str中。NLMSG_DATA(nlh)返回数据区地址。相关宏定义参考:/wangjingfei/archive/2010/02/04/5288263.aspx213 kfree_skb(skb); 213 kfree_skb(skb); 释放接收到的消息。 1.2.3 向用户进程发送netlink消息 以下函数的参数为netfilter捕捉到的sk_buff结构的数据包,目的是将该包通过netlink发送到用户态进程。247 void send_to_user(struct sk_buff *skb) 248 249 struct iphdr *iph; 250 struct ethhdr *ehdr; 251 252 struct nlmsghdr *nlh; 253 254 struct sk_buff *nl_skb; 255 256 /DbgPrint(Send packages to user/n); 257 258 if(skb = NULL) 259 260 return ; 261 262 if(!g_nl_sk) 263 264 return ; 265 266 if(pid = 0) 267 268 return; 269 270 271 nl_skb = alloc_skb(NLMSG_SPACE(1514), GFP_ATOMIC); 272 /nl_skb = alloc_skb(NLMSG_SPACE(0), GFP_ATOMIC); 273 if(nl_skb = NULL) 274 275 / allocate failed. 276 return; 277 278 279 ehdr = eth_hdr(skb); 280 iph = ip_hdr(skb); 281 282 nlh = nlmsg_put(nl_skb, 0, 0, 0, NLMSG_SPACE(1514) - sizeof(struct nlmsghdr), 0); 283 NETLINK_CB(nl_skb).pid = 0; 284 285 /DbgPrint(Data length: %d, len=%d/n, htons(iph-tot_len) + ETH_HLEN, NLMSG_SPACE(1514); 286 287 / Copy data to nlh 288 memcpy(NLMSG_DATA(nlh), (char *)ehdr, htons(iph-tot_len) + ETH_HLEN); 289 290 netlink_unicast(g_nl_sk, nl_skb, pid, MSG_DONTWAIT); 291 247 void send_to_user(struct sk_buff *skb)248 249 struct iphdr *iph;250 struct ethhdr *ehdr;251 252 struct nlmsghdr *nlh;253 254 struct sk_buff *nl_skb;255 256 /DbgPrint(Send packages to user/n);257 258 if(skb = NULL)259 260 return ;261 262 if(!g_nl_sk)263 264 return ;265 266 if(pid = 0)267 268 return;269 270 271 nl_skb = alloc_skb(NLMSG_SPACE(1514), GFP_ATOMIC);272 /nl_skb = alloc_skb(NLMSG_SPACE(0), GFP_ATOMIC);273 if(nl_skb = NULL)274 275 / allocate failed.276 return;277 278 279 ehdr = eth_hdr(skb);280 iph = ip_hdr(skb);281 282 nlh = nlmsg_put(nl_skb, 0, 0, 0, NLMSG_SPACE(1514) - sizeof(struct nlmsghdr), 0);283 NETLINK_CB(nl_skb).pid = 0;284 285 /DbgPrint(Data length: %d, len=%d/n, htons(iph-tot_len) + ETH_HLEN, NLMSG_SPACE(1514);286 287 / Copy data to nlh288 memcpy(NLMSG_DATA(nlh), (char *)ehdr, htons(iph-tot_len) + ETH_HLEN);289 290 netlink_unicast(g_nl_sk, nl_skb, pid, MSG_DONTWAIT);291 247 void send_to_user(struct sk_buff *skb) 247 void send_to_user(struct sk_buff *skb) 参数skb为netfilter捕捉到的数据包,不是netlink数据包。这里作为netlink的数据传输。271 nl_skb = alloc_skb(NLMSG_SPACE(1514), GFP_ATOMIC); 271 nl_skb = alloc_skb(NLMSG_SPACE(1514), GFP_ATOMIC); 为发送数据包申请空间。空间数据区大小为1514,即最大ethernet数据包长度。NLMSG_SPACE(1514)返回数据区大小为1514的netlink数据包的大小。详细参考:/wangjingfei/archive/2010/02/04/5288263.aspx282 nlh = nlmsg_put(nl_skb, 0, 0, 0, NLMSG_SPACE(1514) - sizeof(struct nlmsghdr), 0); 282 nlh = nlmsg_put(nl_skb, 0, 0, 0, NLMSG_SPACE(1514) - sizeof(struct nlmsghdr), 0); 填充netlink数据包头。283 NETLINK_CB(nl_skb).pid = 0; 283 NETLINK_CB(nl_skb).pid = 0; 确定发送数据包的进程号,0表示内核进程。该处宏定义同样参考:/wangjingfei/archive/2010/02/04/5288263.aspx290 netlink_unicast(g_nl_sk, nl_skb, pid, MSG_DONTWAIT); 290 netlink_unicast(g_nl_sk, nl_skb, pid, MSG_DONTWAIT); 通过非阻塞方式发送数据包。注意:在发送完数据包之后,nl_skb指向的数据空间将被清空,下一次发送数据包必须重新调用alloc_skb分配空间,否则将会造成内核崩溃,必须重新启动。 1.2.4 释放netlink socket使用完成netlink之后,必须要调用sock_release,否则45 #define NETLINK_REALNET 26 45 #define NETLINK_REALNET 26 指定的协议编号将不再可用。代码如下:239 void destory_netlink(void) 240 241 if(g_nl_sk != NULL) 242 243 sock_release(g_nl_sk-sk_socket); 244 245 239 void destory_netlink(void)240 241 if(g_nl_sk != NULL)242 243 sock_release(g_nl_sk-sk_socket);244 245 2. 用户态程序2.1 相关数据结构28 / - For Netlink - / 29 struct sockaddr_nl nl_src_addr, nl_dest

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论