Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1064363
  • 博文数量: 573
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 66
  • 用 户 组: 普通用户
  • 注册时间: 2016-06-28 16:21
文章分类

全部博文(573)

文章存档

2018年(3)

2016年(48)

2015年(522)

分类: LINUX

2015-12-04 09:43:50

Linux-3.2.0.24中内核的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写的和内核通信的两个程序,一个是用户空间,一个是内核空间。
内核空间:netlink.c

  1. #include   
  2. #include   
  3. #include   
  4. #include   
  5. #include   
  6. #include   
  7. #include    
  8.   
  9. #define NETLINK_TEST 25  
  10. #define MAX_MSGSIZE 1024  
  11. int pid;  
  12. int err;  
  13. struct sock *nl_sk = NULL;  
  14. int flag = 0;  
  15.   
  16. int stringlength(const char *s)  
  17. {  
  18.     int slen = 0;  
  19.     for(; *s; s++){  
  20.         slen++;  
  21.     }  
  22.     return slen;  
  23. }  
  24.   
  25. void sendnlmsg(void)  
  26. {  
  27.     struct sk_buff *skb_1;  
  28.     struct nlmsghdr *nlh;  
  29.     int len = NLMSG_SPACE(MAX_MSGSIZE);  
  30.     int slen = 0;  
  31.     char buffer[128];  
  32.     const char *message="hello i am kernel";  
  33.     if(!message || !nl_sk){  
  34.         return ;  
  35.     }  
  36.     skb_1 = alloc_skb(len,GFP_KERNEL);  
  37.     if(!skb_1){  
  38.         printk(KERN_ERR "my_net_link:alloc_skb_1 error\n");  
  39.     }  
  40.     nlh = nlmsg_put(skb_1,0,0,0,MAX_MSGSIZE,0);  
  41.   
  42.     NETLINK_CB(skb_1).pid = 0;  
  43.     NETLINK_CB(skb_1).dst_group = 0;  
  44.   
  45.     slen = stringlength(message);  
  46.     memset(buffer,0,sizeof(buffer));  
  47.     memcpy(buffer,message,slen);  
  48.     memcpy(NLMSG_DATA(nlh),buffer,slen+1);  
  49.     printk("my_net_link:send message '%s'.\n",(char *)NLMSG_DATA(nlh));  
  50.   
  51.     netlink_unicast(nl_sk,skb_1,pid,MSG_DONTWAIT);  
  52. }  
  53.   
  54. void nl_data_ready(struct sk_buff *__skb)  
  55. {  
  56.     struct sk_buff *skb;  
  57.     struct nlmsghdr *nlh;  
  58.     char str[100];  
  59.     struct completion cmpl;  
  60.     int i=10;  
  61.     skb = skb_get (__skb);  
  62.     if(skb->len >= NLMSG_SPACE(0)){  
  63.         nlh = nlmsg_hdr(skb);  
  64.   
  65.         memcpy(str, NLMSG_DATA(nlh), sizeof(str));  
  66.         printk("Message received:%s\n",str) ;  
  67.         pid = nlh->nlmsg_pid;  
  68.         while(i--){  
  69.             //init_completion(&cmpl);  
  70.             //wait_for_completion_timeout(&cmpl,3 * HZ);  
  71.             sendnlmsg();  
  72.             break;  
  73.         }  
  74.         flag = 1;  
  75.         kfree_skb(skb);  
  76.     }  
  77.   
  78.  }  
  79.   
  80. // Initialize netlink  
  81.   
  82. /* 
  83. kernel 3.9.6中实例代码 
  84.  
  85. struct netlink_kernel_cfg { 
  86.     unsigned int    groups; 
  87.     unsigned int    flags; 
  88.     void        (*input)(struct sk_buff *skb); 
  89.     struct mutex    *cb_mutex; 
  90.     void        (*bind)(int group); 
  91. }; 
  92.  
  93. netlink_kernel_create(struct net *net, int unit, struct netlink_kernel_cfg *cfg) 
  94.  
  95. 3.2.0-24中 
  96. struct sock *netlink_kernel_create(struct net *net, 
  97.                      int unit,unsigned int groups, 
  98.                      void (*input)(struct sk_buff *skb), 
  99.                      struct mutex *cb_mutex, 
  100.                      struct module *module); 
  101. */  
  102.   
  103. static int netlink_init(void)  
  104. {  
  105.     nl_sk = netlink_kernel_create(&init_net, NETLINK_TEST, 1,nl_data_ready, NULL, THIS_MODULE);  
  106.   
  107.     if(!nl_sk){  
  108.         printk(KERN_ERR "my_net_link: create netlink socket error.\n");  
  109.         return 1;  
  110.     }  
  111.   
  112.     printk("my_net_link_3: create netlink socket ok.\n");  
  113.     return 0;  
  114. }  
  115.   
  116. static void netlink_exit(void)  
  117. {  
  118.     if(nl_sk != NULL){  
  119.         sock_release(nl_sk->sk_socket);  
  120.     }  
  121.   
  122.     printk("my_net_link: self module exited\n");  
  123. }  
  124.   
  125. module_init(netlink_init);  
  126. module_exit(netlink_exit);  
  127.   
  128. MODULE_AUTHOR("wangpengqi");  
  129. MODULE_LICENSE("GPL");  



下面是用户空间的程序:app.c


  1. #include   
  2. #include   
  3. #include   
  4. #include   
  5. #include   
  6. #include   
  7. #include   
  8. #include   
  9. #include   
  10. #include   
  11. #include   
  12.   
  13. #define NETLINK_TEST 25  
  14. #define MAX_PAYLOAD 1024 // maximum payload size  
  15.   
  16. int main(int argc, char* argv[])  
  17. {  
  18.     int state;  
  19.     struct sockaddr_nl src_addr, dest_addr;  
  20.     struct nlmsghdr *nlh = NULL;  
  21.     struct iovec iov;  
  22.     struct msghdr msg;  
  23.     int sock_fd, retval;  
  24.     int state_smg = 0;  
  25.     // Create a socket  
  26.   
  27.     sock_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_TEST);  
  28.     if(sock_fd == -1){  
  29.         printf("error getting socket: %s", strerror(errno));  
  30.         return -1;  
  31.     }  
  32.   
  33.     // To prepare binding  
  34.   
  35.     memset(&msg,0,sizeof(msg));  
  36.     memset(&src_addr, 0, sizeof(src_addr));  
  37.     src_addr.nl_family = AF_NETLINK;  
  38.     src_addr.nl_pid = getpid(); // self pid  
  39.   
  40.     src_addr.nl_groups = 0; // multi cast  
  41.   
  42.   
  43.     retval = bind(sock_fd, (struct sockaddr*)&src_addr, sizeof(src_addr));  
  44.     if(retval < 0){  
  45.         printf("bind failed: %s", strerror(errno));  
  46.         close(sock_fd);  
  47.         return -1;  
  48.     }  
  49.   
  50.     // To prepare recvmsg  
  51.   
  52.     nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));  
  53.     if(!nlh){  
  54.         printf("malloc nlmsghdr error!\n");  
  55.         close(sock_fd);  
  56.         return -1;  
  57.     }  
  58.   
  59.     memset(&dest_addr,0,sizeof(dest_addr));  
  60.     dest_addr.nl_family = AF_NETLINK;  
  61.     dest_addr.nl_pid = 0;  
  62.     dest_addr.nl_groups = 0;  
  63.   
  64.     nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);  
  65.     nlh->nlmsg_pid = getpid();  
  66.     nlh->nlmsg_flags = 0;  
  67.     strcpy(NLMSG_DATA(nlh),"Hello you!");  
  68.   
  69.     iov.iov_base = (void *)nlh;  
  70.     iov.iov_len = NLMSG_SPACE(MAX_PAYLOAD);  
  71.     // iov.iov_len = nlh->nlmsg_len;  
  72.   
  73.     memset(&msg, 0, sizeof(msg));  
  74.      
  75.     msg.msg_name = (void *)&dest_addr;  
  76.     msg.msg_namelen = sizeof(dest_addr);  
  77.     msg.msg_iov = &iov;  
  78.     msg.msg_iovlen = 1;  
  79.   
  80.     printf("state_smg\n");  
  81.     state_smg = sendmsg(sock_fd,&msg,0);  
  82.   
  83.     if(state_smg == -1){  
  84.         printf("get error sendmsg = %s\n",strerror(errno));  
  85.     }  
  86.   
  87.     memset(nlh,0,NLMSG_SPACE(MAX_PAYLOAD));  
  88.     printf("waiting received!\n");  
  89.     // Read message from kernel  
  90.     state = recvmsg(sock_fd, &msg, 0);  
  91.     if(state<0){  
  92.         printf("state<1");  
  93.     }  
  94.     printf("Received message: %s\n",(char *) NLMSG_DATA(nlh));  
  95.     close(sock_fd);  
  96.   
  97.     return 0;  
  98. }  




Makefile文件

  1. [root@control netlink]# cat Makefile   
  2. obj-m := netlink.o  
  3. KERNELBUILD := /lib/modules/`uname -r`/build  
  4. default:  
  5.     make -C $(KERNELBUILD) M=$(shell pwd) modules  
  6. clean:  
  7.     rm -rf *.o .depend .*.cmd *.ko *.mod.c .tmp_versions *.symvers .*.d *.unsigned *.order  



其中,netlink.c为内核的空间的程序。
    先运行内核代码netlink.ko,也就是在执行完makefile文件后,会生成一个netlink.ko文件,可以使用下面的命令进行安装,insmod netlink.ko,使用lsmod查看,当安装成功后,然后,执行./netlink用户空间程序,可以在另一个终端下执行dmesg命令,查看内核通信的情况。这里netlink程序向内核空间发送一个hello you!内核返回给一个I am from kernel!在这里使用了一个定时器,也就是每3秒中发送一次I am from kernel!只有内核把10个字符串全部发送完毕后,用户空间的sendmsg()才会返回,也就是在用户空间的netlink才会输出内核空间发送过来的数据,这里只有一个简单的程序,并没有什么实际的意义,因为,正如前面所说的一般情况下不会在回调函数中处理太多的东西,以免sendmsg()函数返回不及时。下面是使用dmesg命令输出的信息。
阅读(443) | 评论(0) | 转发(0) |
0

上一篇:vmalloc代码

下一篇: C语言红黑树

给主人留下些什么吧!~~