Netlink套接字

原帖地址:http://tcspecial.iteye.com/blog/2174781

Linux下如何实现用户态与内核态的交互呢?一种是上文讲的/proc文件,还有一种是netlink套接字机制,netlink实现了用户空间与内核空间双向通信方法。

netlink用户态API与常见的socket编程一致,只是内核态要实现自定义protocol。

 

一.内核态模块

    该模块用于接收用户态信息,并发送一字符串响应。

C代码   收藏代码
  1. #ifndef __KERNEL__  
  2. #define __KERNEL__  
  3. #endif  /* __KERNEL__ */  
  4.   
  5. #include   
  6. #include   
  7. #include   
  8. #include   
  9. #include   
  10. #include   
  11. #include   
  12. #include   // netlink   
  13. #include   
  14. #include   
  15.   
  16. #define NETLINK_TEST 31    // 自定义用户协议   
  17.   
  18. static struct sock *nlfd = NULL;  // 内核socket文件描述符  
  19. static char *payload = "Hello user,i'm from kernel!";  
  20.   
  21. // 向用户层发送消息  
  22. static int sendNLMsg(int pid,void *msg,int len)  
  23. {  
  24.     struct sk_buff *skb;  
  25.     int size,count;  
  26.     struct nlmsghdr *nlmsgh = NULL;  
  27.     char *pos = NULL;  
  28.     int retval = 0;  
  29.   
  30.     size = NLMSG_SPACE(len);               // 加消息头部长度  
  31.     skb = alloc_skb(size,GFP_KERNEL);   // 申请空间  
  32.     if(!skb)  
  33.     {  
  34.         retval = -1;  
  35.         return retval;  
  36.     }  
  37.   
  38.     nlmsgh = nlmsg_put(skb,0,0,0,len,0);  
  39.   
  40.     // 填充数据  
  41.     pos = NLMSG_DATA(nlmsgh);  
  42.     memset(pos,0,len);  
  43.     memcpy(pos,msg,len);  
  44.   
  45.     NETLINK_CB(skb).dst_group = 0;  // 单播  
  46.   
  47.     count = netlink_unicast(nlfd,skb,pid,MSG_DONTWAIT);   
  48.     printk(KERN_ALERT "pid:%d send:%d\n",pid,count);  
  49.   
  50.     return retval;  
  51. }  
  52.   
  53.   
  54. // 处理用户层传递的消息  
  55. static void handle_msg(struct sk_buff *_sk)  
  56. {  
  57.     struct sk_buff *skb;  
  58.     struct nlmsghdr *nlh = NULL;  
  59.     char str[100] = {};  
  60.     int pid;  
  61.   
  62.     printk("==>handle_msg\n");  
  63.       
  64.     skb = skb_get(_sk);   // 引用当前_sk  
  65.   
  66.     if(skb->len >= NLMSG_SPACE(0))  
  67.     {  
  68.         nlh = nlmsg_hdr(skb);         // 获得信息头部  
  69.         pid = nlh->nlmsg_pid;         // 获取用户进程pid  
  70.   
  71.         memcpy(str,NLMSG_DATA(nlh),100);  
  72.         printk(KERN_ERR "recv:%s\n",str);  
  73.   
  74.         sendNLMsg(pid,payload,strlen(payload));  // 向用户层发送数据  
  75.   
  76.         kfree_skb(skb);  
  77.     }  
  78.   
  79.     printk("<==handle_msg\n");  
  80. }  
  81.   
  82. // 建立netlink套接字  
  83. int NLCreate(void)  
  84. {  
  85.         // 消息回调函数为handle_msg,注:参数1区别以往内核版本API,设为init_net  
  86.     nlfd = netlink_kernel_create(&init_net,NETLINK_TEST,1,handle_msg,NULL,THIS_MODULE);  
  87.     if(!nlfd)  
  88.     {  
  89.         return -1;  
  90.     }  
  91.   
  92.     return 0;  
  93. }  
  94.   
  95. // 清除netlink套接字  
  96. int NLDestroy(void)  
  97. {  
  98.     if(nlfd)  
  99.     {  
  100.         sock_release(nlfd->sk_socket);  
  101.     }  
  102.   
  103.     return 0;  
  104. }  
  105.   
  106.   
  107. static int __init netlink_init(void)  
  108. {  
  109.     NLCreate();  
  110.   
  111.     return 0;  
  112. }  
  113.   
  114.   
  115. static void __exit netlink_exit(void)  
  116. {  
  117.     NLDestroy();  
  118. }  
  119.   
  120. module_init(netlink_init);  
  121. module_exit(netlink_exit);  
  122.   
  123. MODULE_LICENSE("GPL");  
  124. MODULE_AUTHOR("kettas");  
  125. MODULE_DESCRIPTION("Netlink Test Demo");  
  126. MODULE_VERSION("1.0.1");  
  127. MODULE_ALIAS("Netlink 01");  

 

 

二.用户态

该应用向内核模块发送信息,并接收来自内核响应字符串。

        

C代码   收藏代码
  1. #include   
  2. #include   
  3. #include   
  4. #include   
  5. #include   
  6. #include   
  7. #include   
  8. #include   
  9.   
  10.   
  11. #define NETLINK_TEST 31  
  12. #define MAX_NL_MSG_LEN 1024  
  13.   
  14. typedef struct _packet_u  
  15. {  
  16.     struct nlmsghdr hdr;  
  17.     char payload[1024];  
  18. }packet_u;  
  19.   
  20.   
  21. static int nls;  // socket文件描述符  
  22.   
  23. // 向内核发送消息  
  24. int sendtokernel(char *buf,int len,int type)  
  25. {  
  26.     struct nlmsghdr nlmsg;  
  27.     struct sockaddr_nl nldest = {};  
  28.     int size;  
  29.   
  30.     nldest.nl_family    = AF_NETLINK;  
  31.     nldest.nl_pid       = 0;  
  32.     nldest.nl_groups    = 0;  
  33.   
  34.     // 填充netlink消息头  
  35.     nlmsg.nlmsg_len = NLMSG_LENGTH(len);  
  36.     nlmsg.nlmsg_pid = getpid();  
  37.     nlmsg.nlmsg_flags = 0;  
  38.     nlmsg.nlmsg_type  = type;  
  39.   
  40.     // 填充负载  
  41.     memcpy(NLMSG_DATA(&nlmsg),buf,len);  
  42.   
  43.     // 发送  
  44.     size = sendto(nls,&nlmsg,nlmsg.nlmsg_len,0,(struct sockaddr*)&nldest,sizeof(nldest));  
  45.     return size;  
  46. }  
  47.   
  48.   
  49. // 接收内核消息  
  50. int recvfromkernel(void)  
  51. {  
  52.     int size = 0;  
  53.   
  54.         // 方法一:调用recvfrom方法接收内核数据,注此时message结构体包含有消息体  
  55.         /* 
  56.     packet_u message; 
  57.     struct sockaddr_nl nldest = {}; 
  58.     int len = sizeof(nldest); 
  59.      
  60.     memset(&message,0,sizeof(message)); 
  61.  
  62.     nldest.nl_family    = AF_NETLINK; 
  63.     nldest.nl_pid       = 0; 
  64.     nldest.nl_groups    = 0; 
  65.      
  66.     // 接收消息 
  67.     size = recvfrom(nls,  
  68.             &message,  
  69.             sizeof(message),  
  70.             0,  
  71.             (struct sockaddr*)&nldest,  
  72.             &len);   
  73.     printf("size:%d recv:%s\n",size,message.payload);   // NLMSG_DATA()与message.payload结果一致              
  74.         */  
  75.     // 方法二:调用recvmsg方法  
  76.     struct sockaddr_nl nladdr;  
  77.     struct msghdr msg;  
  78.     struct iovec iov;  
  79.     struct nlmsghdr *nlhdr;  
  80.   
  81.     nlhdr = (struct nlmsghdr *)malloc(MAX_NL_MSG_LEN);  
  82.     iov.iov_base = (void *)nlhdr;  
  83.     iov.iov_len = MAX_NL_MSG_LEN;  
  84.     msg.msg_name = (void *)&(nladdr);  
  85.     msg.msg_namelen = sizeof(nladdr);  
  86.     msg.msg_iov = &iov;  
  87.     msg.msg_iovlen = 1;  
  88.     size = recvmsg(nls, &msg, 0);  
  89.     printf("size:%d recv:%s\n",size,(char*)NLMSG_DATA(nlhdr));  
  90.   
  91.     return size;  
  92. }  
  93.   
  94. int main(int argc,char **argv)  
  95. {  
  96.     struct sockaddr_nl nlsource;  
  97.     int ret;  
  98.   
  99.     // socket  
  100.     nls = socket(PF_NETLINK,SOCK_RAW,NETLINK_TEST);  
  101.     assert(nls!=-1);  
  102.   
  103.     memset(&nlsource,0,sizeof(struct sockaddr_nl));  
  104.     nlsource.nl_family = AF_NETLINK;  
  105.     nlsource.nl_pid    = getpid();  
  106.     nlsource.nl_groups = 0;  
  107.   
  108.     // bind  
  109.     ret = bind(nls,(struct sockaddr*)&nlsource,sizeof(nlsource));  
  110.     assert(ret!=-1);  
  111.   
  112.     // send  
  113.     char *str = "Hello kernel,i'm from user";  
  114.     sendtokernel(str,strlen(str),0);  
  115.   
  116.     // recv  
  117.     recvfromkernel();  
  118.   
  119.     close(nls);  
  120.     return 0;  
  121. }  

 

三.测试运行

 

Shell代码   收藏代码
  1. [scada@linux netlink]$ sudo insmod netlink_test.ko  
  2. [scada@linux netlink]$ ./netlink_u   
  3. size:44 recv:Hello user,i'm from kernel!  
  4. [scada@linux netlink]$ dmesg  
  5. ==>handle_msg  
  6. recv:Hello kernel,i'm from user  
  7. pid:40122 send:44  
  8. <==handle_msg  

你可能感兴趣的:(Netlink套接字)