Linux Kernel 学习笔记9:内核与用户层通信之netlink

(本章基于:Linux-4.4.0-37)

内核与用户空间通信有很多种通信方式,netlink是其中一种,其余的还有/proc、ioctl、sockopt、共享内存等等。netlink的特点是异步全双工。


netlink使用32位端口寻址,称为pid(与进程号没有关系),其中内核的pid地址为0,。netlink主要特性如下:

1 支持全双工、异步通信(当然同步也支持)

2 用户空间可使用标准的BSD socket接口(但netlink并没有屏蔽掉协议包的构造与解析过程,推荐使用libnl等第三方库)

3 在内核空间使用专用的内核API接口

4 支持多播(因此支持“总线”式通信,可实现消息订阅)

5 在内核端可用于进程上下文与中断上下文


基本数据结构


[cpp]  view plain  copy
  1. struct msghdr {  
  2.     void         *msg_name;       /* optional address */  
  3.     socklen_t     msg_namelen;    /* size of address */  
  4.     struct iovec *msg_iov;        /* scatter/gather array */  
  5.     size_t        msg_iovlen;     /* # elements in msg_iov */  
  6.     void         *msg_control;    /* ancillary data, see below */  
  7.     size_t        msg_controllen; /* ancillary data buffer len */  
  8.     int           msg_flags;      /* flags (unused) */  
  9. };  
  10.   
  11. struct sockaddr_nl  
  12. {  
  13.     sa_family_t nl_family; /*该字段总是为AF_NETLINK */  
  14.     unsigned short nl_pad; /* 目前未用到,填充为0*/  
  15.     __u32 nl_pid; /* process pid */  
  16.     __u32 nl_groups; /* multicast groups mask */  
  17. };  
struct sockaddr_nl是netlink通信地址,和我们通常socket编程中的sockaddr_in作用一样。pid表示通信端口,groups表示组,注意这里为希望加入多播组号的掩码,也就是说最多只支持32个组。
[cpp]  view plain  copy
  1. struct nlmsghdr  
  2. {  
  3.     __u32 nlmsg_len; /* Length of message including header */  
  4.     __u16 nlmsg_type; /* Message content */  
  5.     __u16 nlmsg_flags; /* Additional flags */  
  6.     __u32 nlmsg_seq; /* Sequence number */  
  7.     __u32 nlmsg_pid; /* Sending process PID */  
  8. };  
Netlink报文的数据区由消息头和消息体构成,struct nlmsghdr即为消息头,消息体接在消息头后。


内核层操作

创建socket

[cpp]  view plain  copy
  1. static inline struct sock *  
  2. netlink_kernel_create(struct net *net, int unit, struct netlink_kernel_cfg *cfg);  
net:  一般直接填&init_net

unit:协议类型,可自定义,如#define NETLINK_TEST 25

cfg:配置结构,类型如下:

[cpp]  view plain  copy
  1. /* optional Netlink kernel configuration parameters */  
  2. struct netlink_kernel_cfg {  
  3.     unsigned int    groups;  
  4.     unsigned int    flags;  
  5.     void        (*input)(struct sk_buff *skb);  
  6.     struct mutex    *cb_mutex;  
  7.     int     (*bind)(struct net *net, int group);  
  8.     void        (*unbind)(struct net *net, int group);  
  9.     bool        (*compare)(struct net *net, struct sock *sk);  
  10. };  

groups:组编号;

input:接收回调函数,接收一个sk_buff结构,数据包含一个nlmsghdr协议头;

return:返回一个sock结构,返回NULL表示创建失败;


单播发送接口:

[cpp]  view plain  copy
  1. extern int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 portid, int nonblock);  

(1) ssk:为函数 netlink_kernel_create()返回的socket。

(2) skb:存放消息,它的data字段指向要发送的netlink消息结构,而 skb的控制块保存了消息的地址信息,宏NETLINK_CB(skb)就用于方便设置该控制块。

(3) portid:pid端口。

(4) nonblock:表示该函数是否为非阻塞,如果为1,该函数将在没有接收缓存可利用时立即返回;而如果为0,该函数在没有接收缓存可利用定时睡眠。


多播发送接口:

[cpp]  view plain  copy
  1. extern int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, __u32 portid,  
  2.                  __u32 group, gfp_t allocation);  
group:接收消息的多播组,该参数的每一个位代表一个多播组,因此如果发送给多个多播组;
allocation:内存分配类型,一般地为GFP_ATOMIC或GFP_KERNEL,GFP_ATOMIC用于原子的上下文(即不可以睡眠),而GFP_KERNEL用于非原子上下文。


释放socket

[cpp]  view plain  copy
  1. extern void netlink_kernel_release(struct sock *sk);  


用户层操作

nlmsghdr结构常见操作:

NLMSG_SPACE(len): 将len加上nlmsghdr头长度,并按4字节对齐;

NLMSG_DATA(nlh): 返回数据区首地址;


创建socke

[cpp]  view plain  copy
  1. int netlink_create_socket(void)  
  2. {  
  3.         //create a socket  
  4.         return socket(AF_NETLINK, SOCK_RAW, NETLINK_TEST);  
  5. }  

bind

[cpp]  view plain  copy
  1. int netlink_bind(int sock_fd)  
  2. {  
  3.         struct sockaddr_nl addr;  
  4.   
  5.         memset(&addr, 0, sizeof(struct sockaddr_nl));  
  6.         addr.nl_family = AF_NETLINK;  
  7.         addr.nl_pid = TEST_PID;  
  8.         addr.nl_groups = 0;  
  9.   
  10.         return bind(sock_fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_nl));  
  11. }  

发送接收:

使用sendmsg、recvmsg发送接收数据

[cpp]  view plain  copy
  1. ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);  
  2. ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);  


使用sendto、recvfrom发送接收数据

[cpp]  view plain  copy
  1. ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,  
  2.                       const struct sockaddr *dest_addr, socklen_t addrlen);  
  3. ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,  
  4.                         struct sockaddr *src_addr, socklen_t *addrlen);  


例:

说明:用户层pid设置为100,应用层发送一条信息到内核,内核回复同样的信息;

内核层:

[cpp]  view plain  copy
  1. #include <linux/init.h>  
  2. #include <linux/module.h>  
  3. #include <linux/stat.h>  
  4. #include <linux/kdev_t.h>  
  5. #include <linux/fs.h>  
  6. #include <linux/device.h>  
  7. #include <linux/cdev.h>  
  8. #include <asm/uaccess.h>  
  9.   
  10. #include <net/netlink.h>  
  11. #include <net/sock.h>  
  12.   
  13. #define NETLINK_TEST (25)  
  14.   
  15. static dev_t devId;  
  16. static struct class *cls = NULL;  
  17. struct sock *nl_sk = NULL;  
  18.   
  19. static void  
  20. hello_cleanup(void)  
  21. {  
  22.         netlink_kernel_release(nl_sk);  
  23.         device_destroy(cls, devId);  
  24.         class_destroy(cls);  
  25.         unregister_chrdev_region(devId, 1);  
  26. }  
  27.   
  28. static void  
  29. netlink_send(int pid, uint8_t *message, int len)  
  30. {  
  31.         struct sk_buff *skb_1;  
  32.         struct nlmsghdr *nlh;  
  33.   
  34.         if(!message || !nl_sk) {  
  35.                 return;  
  36.         }  
  37.   
  38.         skb_1 = alloc_skb(NLMSG_SPACE(len), GFP_KERNEL);  
  39.         if( !skb_1 ) {  
  40.                 printk(KERN_ERR "alloc_skb error!\n");  
  41.         }  
  42.   
  43.         nlh = nlmsg_put(skb_1, 0, 0, 0, len, 0);  
  44.         NETLINK_CB(skb_1).portid = 0;  
  45.         NETLINK_CB(skb_1).dst_group = 0;  
  46.         memcpy(NLMSG_DATA(nlh), message, len);  
  47.         netlink_unicast(nl_sk, skb_1, pid, MSG_DONTWAIT);  
  48. }  
  49.   
  50. static void  
  51. netlink_input(struct sk_buff *__skb)  
  52. {  
  53.         struct sk_buff *skb;  
  54.         char str[100];  
  55.         struct nlmsghdr *nlh;  
  56.   
  57.         if( !__skb ) {  
  58.                 return;  
  59.         }  
  60.   
  61.         skb = skb_get(__skb);  
  62.         if( skb->len < NLMSG_SPACE(0)) {  
  63.                 return;  
  64.         }  
  65.   
  66.         nlh = nlmsg_hdr(skb);  
  67.         memset(str, 0, sizeof(str));  
  68.         memcpy(str, NLMSG_DATA(nlh), sizeof(str));  
  69.         printk(KERN_INFO "receive message (pid:%d):%s\n", nlh->nlmsg_pid, str);  
  70.         printk(KERN_INFO "space:%d\n", NLMSG_SPACE(0));  
  71.         printk(KERN_INFO "size:%d\n", nlh->nlmsg_len);  
  72.         netlink_send(nlh->nlmsg_pid, NLMSG_DATA(nlh), nlh->nlmsg_len - NLMSG_SPACE(0));  
  73.   
  74.         return;  
  75. }  
  76.   
  77. static __init int netlink_init(void)  
  78. {  
  79.         int result;  
  80.         struct netlink_kernel_cfg nkc;  
  81.   
  82.         printk(KERN_WARNING "netlink init start!\n");  
  83.   
  84.         //动态注册设备号  
  85.         if(( result = alloc_chrdev_region(&devId, 0, 1, "stone-alloc-dev") ) != 0) {  
  86.                 printk(KERN_WARNING "register dev id error:%d\n", result);  
  87.                 goto err;  
  88.         } else {  
  89.                 printk(KERN_WARNING "register dev id success!\n");  
  90.         }  
  91.         //动态创建设备节点  
  92.         cls = class_create(THIS_MODULE, "stone-class");  
  93.         if(IS_ERR(cls)) {  
  94.                 printk(KERN_WARNING "create class error!\n");  
  95.                 goto err;  
  96.         }  
  97.         if(device_create(cls, NULL, devId, """hello%d", 0) == NULL) {  
  98.                 printk(KERN_WARNING "create device error!\n");  
  99.                 goto err;  
  100.         }  
  101.   
  102.         //初始化netlink  
  103.         nkc.groups = 0;  
  104.         nkc.flags = 0;  
  105.         nkc.input = netlink_input;  
  106.         nkc.cb_mutex = NULL;  
  107.         nkc.bind = NULL;  
  108.         nkc.unbind = NULL;  
  109.         nkc.compare = NULL;  
  110.         nl_sk = netlink_kernel_create(&init_net, NETLINK_TEST, &nkc);  
  111.         if( !nl_sk ) {  
  112.                 printk(KERN_ERR "[netlink] create netlink socket error!\n");  
  113.                 goto err;  
  114.         }  
  115.   
  116.         printk(KERN_ALERT "netlink init success!\n");  
  117.         return 0;  
  118. err:  
  119.         hello_cleanup();  
  120.         return -1;  
  121. }  
  122.   
  123. static __exit void netlink_exit(void)  
  124. {  
  125.         hello_cleanup();  
  126.         printk(KERN_WARNING "netlink exit!\n");  
  127. }  
  128.   
  129. module_init(netlink_init);  
  130. module_exit(netlink_exit);  
  131.   
  132. MODULE_LICENSE("GPL");  
  133. MODULE_AUTHOR("Stone");  

应用层1:

[cpp]  view plain  copy
  1. #include <sys/stat.h>  
  2. #include <unistd.h>  
  3. #include <stdio.h>  
  4. #include <stdlib.h>  
  5. #include <sys/socket.h>  
  6. #include <string.h>  
  7. #include <asm/types.h>  
  8. #include <linux/netlink.h>  
  9. #include <linux/socket.h>  
  10. #include <errno.h>  
  11.   
  12. #define NETLINK_TEST    (25)  
  13. #define MAX_PAYLOAD     (1024)  
  14. #define TEST_PID        (100)  
  15.   
  16. int netlink_create_socket(void)  
  17. {  
  18.         //create a socket  
  19.         return socket(AF_NETLINK, SOCK_RAW, NETLINK_TEST);  
  20. }  
  21.   
  22. int netlink_bind(int sock_fd)  
  23. {  
  24.         struct sockaddr_nl addr;  
  25.   
  26.         memset(&addr, 0, sizeof(struct sockaddr_nl));  
  27.         addr.nl_family = AF_NETLINK;  
  28.         addr.nl_pid = TEST_PID;  
  29.         addr.nl_groups = 0;  
  30.   
  31.         return bind(sock_fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_nl));  
  32. }  
  33.   
  34. int  
  35. netlink_send_message(int sock_fd, const unsigned char *message, int len,  
  36.                                         unsigned int pid, unsigned int group)  
  37. {  
  38.         struct nlmsghdr *nlh = NULL;  
  39.         struct sockaddr_nl dest_addr;  
  40.         struct iovec iov;  
  41.         struct msghdr msg;  
  42.   
  43.         if( !message ) {  
  44.                 return -1;  
  45.         }  
  46.   
  47.         //create message  
  48.         nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(len));  
  49.         if( !nlh ) {  
  50.                 perror("malloc");  
  51.                 return -2;  
  52.         }  
  53.         nlh->nlmsg_len = NLMSG_SPACE(len);  
  54.         nlh->nlmsg_pid = TEST_PID;  
  55.         nlh->nlmsg_flags = 0;  
  56.         memcpy(NLMSG_DATA(nlh), message, len);  
  57.   
  58.         iov.iov_base = (void *)nlh;  
  59.         iov.iov_len = nlh->nlmsg_len;  
  60.         memset(&dest_addr, 0, sizeof(struct sockaddr_nl));  
  61.         dest_addr.nl_family = AF_NETLINK;  
  62.         dest_addr.nl_pid = pid;  
  63.         dest_addr.nl_groups = group;  
  64.   
  65.         memset(&msg, 0, sizeof(struct msghdr));  
  66.         msg.msg_name = (void *)&dest_addr;  
  67.         msg.msg_namelen = sizeof(struct sockaddr_nl);  
  68.         msg.msg_iov = &iov;  
  69.         msg.msg_iovlen = 1;  
  70.   
  71.         //send message  
  72.         if( sendmsg(sock_fd, &msg, 0) < 0 )  
  73.         {  
  74.                 printf("send error!\n");  
  75.                 free(nlh);  
  76.                 return -3;  
  77.         }  
  78.   
  79.         free(nlh);  
  80.         return 0;  
  81. }  
  82.   
  83. int  
  84. netlink_recv_message(int sock_fd, unsigned char *message, int *len)  
  85. {  
  86.         struct nlmsghdr *nlh = NULL;  
  87.         struct sockaddr_nl source_addr;  
  88.         struct iovec iov;  
  89.         struct msghdr msg;  
  90.   
  91.         if( !message || !len ) {  
  92.                 return -1;  
  93.         }  
  94.   
  95.         //create message  
  96.         nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));  
  97.         if( !nlh ) {  
  98.                 perror("malloc");  
  99.                 return -2;  
  100.         }  
  101.         iov.iov_base = (void *)nlh;  
  102.         iov.iov_len = NLMSG_SPACE(MAX_PAYLOAD);  
  103.         memset(&source_addr, 0, sizeof(struct sockaddr_nl));  
  104.         memset(&msg, 0, sizeof(struct msghdr));  
  105.         msg.msg_name = (void *)&source_addr;  
  106.         msg.msg_namelen = sizeof(struct sockaddr_nl);  
  107.         msg.msg_iov = &iov;  
  108.         msg.msg_iovlen = 1;  
  109.   
  110.         if ( recvmsg(sock_fd, &msg, 0) < 0 ) {  
  111.                 printf("recvmsg error!\n");  
  112.                 return -3;  
  113.         }  
  114.         *len = nlh->nlmsg_len - NLMSG_SPACE(0);  
  115.         memcpy(message, (unsigned char *)NLMSG_DATA(nlh), *len);  
  116.   
  117.         free(nlh);  
  118.         return 0;  
  119. }  
  120.   
  121. int  
  122. main(int argc, char **argv)  
  123. {  
  124.         int sock_fd;  
  125.         char buf[MAX_PAYLOAD];  
  126.         int len;  
  127.   
  128.         if( argc < 2) {  
  129.                 printf("enter message!\n");  
  130.                 exit(EXIT_FAILURE);  
  131.         }  
  132.   
  133.         sock_fd = netlink_create_socket();  
  134.         if(sock_fd == -1) {  
  135.                 printf("socket error!\n");  
  136.                 return -1;  
  137.         }  
  138.   
  139.         if( netlink_bind(sock_fd) < 0 ) {  
  140.                 perror("bind");  
  141.                 close(sock_fd);  
  142.                 exit(EXIT_FAILURE);  
  143.         }  
  144.   
  145.         netlink_send_message(sock_fd, argv[1], strlen(argv[1]) + 1, 0, 0);  
  146.         if( netlink_recv_message(sock_fd, buf, &len) == 0 ) {  
  147.                 printf("recv:%s len:%d\n", buf, len);  
  148.         }  
  149.   
  150.         close(sock_fd);  
  151.         return 0;  
  152. }  

上面例子讲的是使用sendmsg、recvmsg。在应用层我们同样可以使用sendto、recvfrom发送接收数据,操作模式和UDP通信非常相似,区别是为了能成功接收数据,我们同样需要使用bind()绑定自身地址,但对于UDP这不是必须的;


应用层2:

[cpp]  view plain  copy
  1. #include <sys/stat.h>  
  2. #include <unistd.h>  
  3. #include <stdio.h>  
  4. #include <stdlib.h>  
  5. #include <sys/socket.h>  
  6. #include <string.h>  
  7. #include <asm/types.h>  
  8. #include <linux/netlink.h>  
  9. #include <linux/socket.h>  
  10. #include <errno.h>  
  11.   
  12. #define NETLINK_TEST    (25)  
  13. #define MAX_PAYLOAD     (1024)  
  14. #define TEST_PID        (100)  
  15.   
  16. int netlink_create_socket(void)  
  17. {  
  18.         //create a socket  
  19.         return socket(AF_NETLINK, SOCK_RAW, NETLINK_TEST);  
  20. }  
  21.   
  22. int netlink_bind(int sock_fd)  
  23. {  
  24.         struct sockaddr_nl addr;  
  25.   
  26.         memset(&addr, 0, sizeof(struct sockaddr_nl));  
  27.         addr.nl_family = AF_NETLINK;  
  28.         addr.nl_pid = TEST_PID;  
  29.         addr.nl_groups = 0;  
  30.   
  31.         return bind(sock_fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_nl));  
  32. }  
  33.   
  34. int  
  35. netlink_send_message(int sock_fd, const unsigned char *message, int len,  
  36.                                         unsigned int pid, unsigned int group)  
  37. {  
  38.         struct nlmsghdr *nlh = NULL;  
  39.         struct sockaddr_nl dest_addr;  
  40.   
  41.         if( !message ) {  
  42.                 return -1;  
  43.         }  
  44.   
  45.         //create message  
  46.         nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(len));  
  47.         if( !nlh ) {  
  48.                 perror("malloc");  
  49.                 return -2;  
  50.         }  
  51.         nlh->nlmsg_len = NLMSG_SPACE(len);  
  52.         nlh->nlmsg_pid = TEST_PID;  
  53.         nlh->nlmsg_flags = 0;  
  54.         memcpy(NLMSG_DATA(nlh), message, len);  
  55.   
  56.         memset(&dest_addr, 0, sizeof(struct sockaddr_nl));  
  57.         dest_addr.nl_family = AF_NETLINK;  
  58.         dest_addr.nl_pid = pid;  
  59.         dest_addr.nl_groups = group;  
  60.   
  61.         //send message  
  62.         if( sendto(sock_fd, nlh, nlh->nlmsg_len, 0, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr_nl)) != nlh->nlmsg_len ) {  
  63.                 printf("send error!\n");  
  64.                 free(nlh);  
  65.                 return -3;  
  66.         }  
  67.   
  68.         free(nlh);  
  69.         return 0;  
  70. }  
  71.   
  72. int  
  73. netlink_recv_message(int sock_fd, unsigned char *message, int *len)  
  74. {  
  75.         struct nlmsghdr *nlh = NULL;  
  76.         struct sockaddr_nl src_addr;  
  77.         socklen_t addrlen = sizeof(struct sockaddr_nl);  
  78.   
  79.         if( !message || !len ) {  
  80.                 return -1;  
  81.         }  
  82.   
  83.         //create message  
  84.         nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));  
  85.         if( !nlh ) {  
  86.                 perror("malloc");  
  87.                 return -2;  
  88.         }  
  89.         memset(&src_addr, 0, sizeof(struct sockaddr_nl));  
  90.         if( recvfrom(sock_fd, nlh, NLMSG_SPACE(MAX_PAYLOAD), 0, (struct sockaddr *)&src_addr, (socklen_t *)&addrlen) < 0 ) {  
  91.                 printf("recvmsg error!\n");  
  92.                 return -3;  
  93.         }  
  94.         *len = nlh->nlmsg_len - NLMSG_SPACE(0);  
  95.         memcpy(message, (unsigned char *)NLMSG_DATA(nlh), *len);  
  96.   
  97.         free(nlh);  
  98.         return 0;  
  99. }  
  100.   
  101. int  
  102. main(int argc, char **argv)  
  103. {  
  104.         int sock_fd;  
  105.         char buf[MAX_PAYLOAD];  
  106.         int len;  
  107.   
  108.         if( argc < 2) {  
  109.                 printf("enter message!\n");  
  110.                 exit(EXIT_FAILURE);  
  111.         }  
  112.   
  113.         sock_fd = netlink_create_socket();  
  114.         if(sock_fd == -1) {  
  115.                 printf("socket error!\n");  
  116.                 return -1;  
  117.         }  
  118.   
  119.         if( netlink_bind(sock_fd) < 0 ) {  
  120.                 perror("bind");  
  121.                 close(sock_fd);  
  122.                 exit(EXIT_FAILURE);  
  123.         }  
  124.   
  125.         netlink_send_message(sock_fd, argv[1], strlen(argv[1]) + 1, 0, 0);  
  126.         if( netlink_recv_message(sock_fd, buf, &len) == 0 ) {  
  127.                 printf("recv:%s len:%d\n", buf, len);  
  128.         }  
  129.   
  130.         close(sock_fd);  
  131.         return 0;  
  132. }  

猜你喜欢

转载自blog.csdn.net/sam0535/article/details/78646288