一、什么是Netlink通信机制
  1. netlink使用简单,只需要在include/linux/netlink.h中增加一个新类型的 netlink 协议定义即可,(如 #define NETLINK_TEST 20 然后,内核和用户态应用就可以立即通过 socket API 使用该 netlink 协议类型进行数据交换);
  2. netlink是一种异步通信机制,在内核与用户态应用之间传递的消息保存在socket缓存队列中,发送消息只是把消息保存在接收者的socket的接收队列,而不需要等待接收者收到消息;
  3. 使用 netlink 的内核部分可以采用模块的方式实现,使用 netlink 的应用部分和内核部分没有编译时依赖; netlink 支持多播,内核模块或应用可以把消息多播给一个netlink组,属于该neilink 组的任何内核模块或应用都能接收到该消息,内核事件向用户态的通知机制就使用了这一特性;
  4. 内核可以使用 netlink 首先发起会话;

更多linux内核视频教程文档资料免费领取后台私信【内核】自行获取.

linux 查看tcp请求(如何玩转linux内核netlink通信机制)(1)

Linux内核源码/内存调优/文件系统/进程管理/设备驱动/网络协议栈-学习视频教程-腾讯课堂

二、Netlink常用数据结构及函数1、用户态数据结构

1 struct sockaddr_nl { 2 __kernel_sa_family_t nl_family; /* AF_NETLINK (跟AF_INET对应)*/ 3 unsigned short nl_pad; /* zero */ 4 __u32 nl_pid; /* port ID (通信端口号)*/ 5 __u32 nl_groups; /* multicast groups mask */ 6 };

1 /* struct nlmsghd 是netlink消息头*/ 2 struct nlmsghdr { 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 port ID */ 8 };

  1. nlmsg_len:整个netlink消息的长度(包含消息头);
  2. nlmsg_type:消息状态,内核在include/uapi/linux/netlink.h中定义了以下4种通用的消息类型,它们分别是:

1 #define NLMSG_NOOP 0x1 /* Nothing. */ 2 #define NLMSG_ERROR 0x2 /* Error */ 3 #define NLMSG_DONE 0x3 /* End of a dump */ 4 #define NLMSG_OVERRUN 0x4 /* Data lost */ 5 6 #define NLMSG_MIN_TYPE 0x10 /* < 0x10: reserved control messages */ 7 8 /*NLMSG_NOOP:不执行任何动作,必须将该消息丢弃; 9 NLMSG_ERROR:消息发生错误; 10 NLMSG_DONE:标识分组消息的末尾; 11 NLMSG_OVERRUN:缓冲区溢出,表示某些消息已经丢失。 12 NLMSG_MIN_TYPEK:预留 */

3. nlmsg_flags:消息标记,它们用以表示消息的类型,如下

1 /* Flags values */ 2 3 #define NLM_F_REQUEST 1 /* It is request message. */ 4 #define NLM_F_MULTI 2 /* Multipart message, terminated by NLMSG_DONE */ 5 #define NLM_F_ACK 4 /* Reply with ack, with zero or error code */ 6 #define NLM_F_ECHO 8 /* Echo this request */ 7 #define NLM_F_DUMP_INTR 16 /* Dump was inconsistent due to sequence change */ 8 9 /* Modifiers to GET request */ 10 #define NLM_F_ROOT 0x100 /* specify tree root */ 11 #define NLM_F_MATCH 0x200 /* return all matching */ 12 #define NLM_F_ATOMIC 0x400 /* atomic GET */ 13 #define NLM_F_DUMP (NLM_F_ROOT|NLM_F_MATCH) 14 15 /* Modifiers to NEW request */ 16 #define NLM_F_REPLACE 0x100 /* Override existing */ 17 #define NLM_F_EXCL 0x200 /* Do not touch, if it exists */ 18 #define NLM_F_CREATE 0x400 /* Create, if it does not exist */ 19 #define NLM_F_APPEND 0x800 /* Add to end of list */

4. nlmsg_seq:消息序列号,用以将消息排队,有些类似TCP协议中的序号(不完全一样),但是netlink的这个字段是可选的,不强制使用;

5. nlmsg_pid:发送端口的ID号,对于内核来说该值就是0,对于用户进程来说就是其socket所绑定的ID号。

1 struct iovec { /* Scatter/gather array items */ 2 void *iov_base; /* Starting address */ 3 size_t iov_len; /* Number of bytes to transfer */ 4 }; 5 /* iov_base: iov_base指向数据包缓冲区,即参数buff,iov_len是buff的长度。msghdr中允许一次传递多个buff, 6 以数组的形式组织在 msg_iov中,msg_iovlen就记录数组的长度 (即有多少个buff) 7 */ 8 struct msghdr { 9 void *msg_name; /* optional address */ 10 socklen_t msg_namelen; /* size of address */ 11 struct iovec *msg_iov; /* scatter/gather array */ 12 size_t msg_iovlen; /* # elements in msg_iov */ 13 void *msg_control; /* ancillary data, see below */ 14 size_t msg_controllen; /* ancillary data buffer len */ 15 int msg_flags; /* flags on received message */ 16 }; 17 /* msg_name: 数据的目的地址,网络包指向sockaddr_in, netlink则指向sockaddr_nl; 18 msg_namelen: msg_name 所代表的地址长度 19 msg_iov: 指向的是缓冲区数组 20 msg_iovlen: 缓冲区数组长度 21 msg_control: 辅助数据,控制信息(发送任何的控制信息) 22 msg_controllen: 辅助信息长度 23 msg_flags: 消息标识 24 */

2、netlink 内核数据结构、常用宏及函数:

1 #define NETLINK_ROUTE 0 /* Routing/device hook */ 2 #define NETLINK_UNUSED 1 /* Unused number */ 3 #define NETLINK_USERSOCK 2 /* Reserved for user mode socket protocols */ 4 #define NETLINK_FIREWALL 3 /* Unused number, formerly ip_queue */ 5 #define NETLINK_SOCK_DIAG 4 /* socket monitoring */ 6 #define NETLINK_NFLOG 5 /* netfilter/iptables ULOG */ 7 #define NETLINK_XFRM 6 /* ipsec */ 8 #define NETLINK_SELINUX 7 /* SELinux event notifications */ 9 #define NETLINK_ISCSI 8 /* Open-iSCSI */ 10 #define NETLINK_AUDIT 9 /* auditing */ 11 #define NETLINK_FIB_LOOKUP 10 12 #define NETLINK_CONNECTOR 11 13 #define NETLINK_NETFILTER 12 /* netfilter subsystem */ 14 #define NETLINK_IP6_FW 13 15 #define NETLINK_DNRTMSG 14 /* DECnet routing messages */ 16 #define NETLINK_KOBJECT_UEVENT 15 /* Kernel messages to userspace */ 17 #define NETLINK_GENERIC 16 18 /* leave room for NETLINK_DM (DM Events) */ 19 #define NETLINK_SCSITRANSPORT 18 /* SCSI Transports */ 20 #define NETLINK_ECRYPTFS 19 21 #define NETLINK_RDMA 20 22 #define NETLINK_CRYPTO 21 /* Crypto layer */ 23 24 #define NETLINK_INET_DIAG NETLINK_SOCK_DIAG 25 26 #define MAX_LINKS 32

1 #define NLMSG_ALIGNTO 4U 2 /* 宏NLMSG_ALIGN(len)用于得到不小于len且字节对齐的最小数值 */ 3 #define NLMSG_ALIGN(len) ( ((len) NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) ) 4 5 /* Netlink 头部长度 */ 6 #define NLMSG_HDRLEN ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr))) 7 8 /* 计算消息数据len的真实消息长度(消息体  消息头)*/ 9 #define NLMSG_LENGTH(len) ((len) NLMSG_HDRLEN) 10 11 /* 宏NLMSG_SPACE(len)返回不小于NLMSG_LENGTH(len)且字节对齐的最小数值 */ 12 #define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len)) 13 14 /* 宏NLMSG_DATA(nlh)用于取得消息的数据部分的首地址,设置和读取消息数据部分时需要使用该宏 */ 15 #define NLMSG_DATA(nlh) ((void*)(((char*)nlh) NLMSG_LENGTH(0))) 16 17 /* 宏NLMSG_NEXT(nlh,len)用于得到下一个消息的首地址, 同时len 变为剩余消息的长度 */ 18 #define NLMSG_NEXT(nlh,len) ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \ 19 (struct nlmsghdr*)(((char*)(nlh)) NLMSG_ALIGN((nlh)->nlmsg_len))) 20 21 /* 判断消息是否 >len */ 22 #define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) && \ 23 (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \ 24 (nlh)->nlmsg_len <= (len)) 25 26 /* NLMSG_PAYLOAD(nlh,len) 用于返回payload的长度*/ 27 #define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len)))

1 static inline struct sock * 2 netlink_kernel_create(struct net *net, int unit, struct netlink_kernel_cfg *cfg) 3 /* net: net指向所在的网络命名空间, 一般默认传入的是&init_net(不需要定义); 定义在net_namespace.c(extern struct net init_net); 4 unit:netlink协议类型 5 cfg: cfg存放的是netlink内核配置参数(如下) 6 */ 7 8 /* optional Netlink kernel configuration parameters */ 9 struct netlink_kernel_cfg { 10 unsigned int groups; 11 unsigned int flags; 12 void (*input)(struct sk_buff *skb); /* input 回调函数 */ 13 struct mutex *cb_mutex; 14 void (*bind)(int group); 15 bool (*compare)(struct net *net, struct sock *sk); 16 };

1 /* 来发送单播消息 */ 2 extern int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 portid, int nonblock); 3 /* ssk: netlink socket 4 skb: skb buff 指针 5 portid: 通信的端口号 6 nonblock:表示该函数是否为非阻塞,如果为1,该函数将在没有接收缓存可利用时立即返回,而如果为0,该函数在没有接收缓存可利用 定时睡眠 7 */ 8 9 /* 用来发送多播消息 */ 10 extern int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, __u32 portid, 11 __u32 group, gfp_t allocation); 12 /* ssk: 同上(对应netlink_kernel_create 返回值)、 13 skb: 内核skb buff 14 portid: 端口id 15 group: 是所有目标多播组对应掩码的"OR"操作的合值。 16 allocation: 指定内核内存分配方式,通常GFP_ATOMIC用于中断上下文,而GFP_KERNEL用于其他场合。 17 这个参数的存在是因为该API可能需要分配一个或多个缓冲区来对多播消息进行clone 18 */

三、netlink实例

(1)用户态程序 (sendto(), recvfrom())

1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <sys/socket.h> 4 #include <string.h> 5 #include <linux/netlink.h> 6 #include <stdint.h> 7 #include <unistd.h> 8 #include <errno.h> 9 10 #define NETLINK_TEST 30 11 #define MSG_LEN 125 12 #define MAX_PLOAD 125 13 14 typedef struct _user_msg_info 15 { 16 struct nlmsghdr hdr; 17 char msg[MSG_LEN]; 18 } user_msg_info; 19 20 int main(int argc, char **argv) 21 { 22 int skfd; 23 int ret; 24 user_msg_info u_info; 25 socklen_t len; 26 struct nlmsghdr *nlh = NULL; 27 struct sockaddr_nl saddr, daddr; 28 char *umsg = "hello netlink!!"; 29 30 /* 创建NETLINK socket */ 31 skfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_TEST); 32 if(skfd == -1) 33 { 34 perror("create socket error\n"); 35 return -1; 36 } 37 38 memset(&saddr, 0, sizeof(saddr)); 39 saddr.nl_family = AF_NETLINK; //AF_NETLINK 40 saddr.nl_pid = 100; //端口号(port ID) 41 saddr.nl_groups = 0; 42 if(bind(skfd, (struct sockaddr *)&saddr, sizeof(saddr)) != 0) 43 { 44 perror("bind() error\n"); 45 close(skfd); 46 return -1; 47 } 48 49 memset(&daddr, 0, sizeof(daddr)); 50 daddr.nl_family = AF_NETLINK; 51 daddr.nl_pid = 0; // to kernel 52 daddr.nl_groups = 0; 53 54 nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PLOAD)); 55 memset(nlh, 0, sizeof(struct nlmsghdr)); 56 nlh->nlmsg_len = NLMSG_SPACE(MAX_PLOAD); 57 nlh->nlmsg_flags = 0; 58 nlh->nlmsg_type = 0; 59 nlh->nlmsg_seq = 0; 60 nlh->nlmsg_pid = saddr.nl_pid; //self port 61 62 memcpy(NLMSG_DATA(nlh), umsg, strlen(umsg)); 63 ret = sendto(skfd, nlh, nlh->nlmsg_len, 0, (struct sockaddr *)&daddr, sizeof(struct sockaddr_nl)); 64 if(!ret) 65 { 66 perror("sendto error\n"); 67 close(skfd); 68 exit(-1); 69 } 70 printf("send kernel:%s\n", umsg); 71 72 memset(&u_info, 0, sizeof(u_info)); 73 len = sizeof(struct sockaddr_nl); 74 ret = recvfrom(skfd, &u_info, sizeof(user_msg_info), 0, (struct sockaddr *)&daddr, &len); 75 if(!ret) 76 { 77 perror("recv form kernel error\n"); 78 close(skfd); 79 exit(-1); 80 } 81 82 printf("from kernel:%s\n", u_info.msg); 83 close(skfd); 84 85 free((void *)nlh); 86 return 0; 87 }

1 /**************************************** 2 * Author: zhangwj 3 * Date: 2017-01-19 4 * Filename: netlink_test.c 5 * Descript: netlink of kernel 6 * Kernel: 3.10.0-327.22.2.el7.x86_64 7 * Warning: 8 ******************************************/ 9 10 #include <linux/init.h> 11 #include <linux/module.h> 12 #include <linux/types.h> 13 #include <net/sock.h> 14 #include <linux/netlink.h> 15 16 #define NETLINK_TEST 30 17 #define MSG_LEN 125 18 #define USER_PORT 100 19 20 MODULE_LICENSE("GPL"); 21 MODULE_AUTHOR("zhangwj"); 22 MODULE_DESCRIPTION("netlink example"); 23 24 struct sock *nlsk = NULL; 25 extern struct net init_net; 26 27 int send_usrmsg(char *pbuf, uint16_t len) 28 { 29 struct sk_buff *nl_skb; 30 struct nlmsghdr *nlh; 31 32 int ret; 33 34 /* 创建sk_buff 空间 */ 35 nl_skb = nlmsg_new(len, GFP_ATOMIC); 36 if(!nl_skb) 37 { 38 printk("netlink alloc failure\n"); 39 return -1; 40 } 41 42 /* 设置netlink消息头部 */ 43 nlh = nlmsg_put(nl_skb, 0, 0, NETLINK_TEST, len, 0); 44 if(nlh == NULL) 45 { 46 printk("nlmsg_put failaure \n"); 47 nlmsg_free(nl_skb); 48 return -1; 49 } 50 51 /* 拷贝数据发送 */ 52 memcpy(nlmsg_data(nlh), pbuf, len); 53 ret = netlink_unicast(nlsk, nl_skb, USER_PORT, MSG_DONTWAIT); 54 55 return ret; 56 } 57 58 static void netlink_rcv_msg(struct sk_buff *skb) 59 { 60 struct nlmsghdr *nlh = NULL; 61 char *umsg = NULL; 62 char *kmsg = "hello users!!!"; 63 64 if(skb->len >= nlmsg_total_size(0)) 65 { 66 nlh = nlmsg_hdr(skb); 67 umsg = NLMSG_DATA(nlh); 68 if(umsg) 69 { 70 printk("kernel recv from user: %s\n", umsg); 71 send_usrmsg(kmsg, strlen(kmsg)); 72 } 73 } 74 } 75 76 struct netlink_kernel_cfg cfg = { 77 .input = netlink_rcv_msg, /* set recv callback */ 78 }; 79 80 int test_netlink_init(void) 81 { 82 /* create netlink socket */ 83 nlsk = (struct sock *)netlink_kernel_create(&init_net, NETLINK_TEST, &cfg); 84 if(nlsk == NULL) 85 { 86 printk("netlink_kernel_create error !\n"); 87 return -1; 88 } 89 printk("test_netlink_init\n"); 90 91 return 0; 92 } 93 94 void test_netlink_exit(void) 95 { 96 if (nlsk){ 97 netlink_kernel_release(nlsk); /* release ..*/ 98 nlsk = NULL; 99 } 100 printk("test_netlink_exit!\n"); 101 } 102 103 module_init(test_netlink_init); 104 module_exit(test_netlink_exit);

1 # 2 #Desgin of Netlink 3 # 4 5 MODULE_NAME :=netlink_test 6 obj-m :=$(MODULE_NAME).o 7 8 KERNELDIR ?= /lib/modules/$(shell uname -r)/build 9 PWD := $(shell pwd) 10 11 all: 12 $(MAKE) -C $(KERNELDIR) M=$(PWD) 13 14 clean: 15 $(MAKE) -C $(KERNELDIR) M=$(PWD) clean

运行结果:

1 [root@localhost nt_2nd]# insmod netlink_test.ko 2 [root@localhost nt_2nd]# dmesg 3 [25024.276345] test_netlink_init

1 [root@localhost nt_2nd]# ./a.out 2 send kernel:hello netlink!! 3 from kernel:hello users!!! 4 [root@localhost nt_2nd]# dmesg 5 [25024.276345] test_netlink_init 6 [25117.548350] kernel recv from user: hello netlink!! 7 [root@localhost nt_2nd]#

linux 查看tcp请求(如何玩转linux内核netlink通信机制)(2)

,