一、Netlink通信机制
Netlink是linux提供的用于内核和用户态进程之间的通信方式。
但是注意虽然Netlink主要用于用户空间和内核空间的通信,但是也能用于用户空间的两个进程通信。只是进程间通信有其他很多方式,一般不用Netlink。除非需要用到Netlink的广播特性时。
那么Netlink有什么优势呢?
一般来说用户空间和内核空间的通信方式有三种:/proc、ioctl、Netlink。而前两种都是单向的,但是Netlink可以实现双工通信。
Netlink协议基于BSD socket和AF_NETLINK地址簇(address family),使用32位的端口号寻址(以前称作PID),每个Netlink协议(或称作总线,man手册中则称之为netlink family),通常与一个或一组内核服务/组件相关联,如NETLINK_ROUTE用于获取和设置路由与链路信息、NETLINK_KOBJECT_UEVENT用于内核向用户空间的udev进程发送通知等。
netlink具有以下特点:
① 支持全双工、异步通信(当然同步也支持)
② 用户空间可使用标准的BSD socket接口(但netlink并没有屏蔽掉协议包的构造与解析过程,推荐使用libnl等第三方库)
③ 在内核空间使用专用的内核API接口
④ 支持多播(因此支持“总线”式通信,可实现消息订阅)
⑤ 在内核端可用于进程上下文与中断上下文
二、用户态数据结构
1 struct msghdr 结构体 2 3 struct iovec { /* Scatter/gather arrayitems */ 4 void *iov_base; /*Starting address */ 5 size_t iov_len; /* Number of bytes to transfer*/ 6 }; 7 /* iov_base: iov_base指向数据包缓冲区,即参数buff,iov_len是buff的长度。msghdr中允许一次传递多个buff, 8 以数组的形式组织在 msg_iov中,msg_iovlen就记录数组的长度(即有多少个buff) 9 */ 10 struct msghdr { 11 void *msg_name; /* optional address */ 12 socklen_t msg_namelen; /* size of address */ 13 struct iovec *msg_iov; /* scatter/gather array */ 14 size_t msg_iovlen; /* # elements in msg_iov */ 15 void *msg_control; /* ancillary data, see below */ 16 size_t msg_controllen; /* ancillary databuffer len */ 17 int msg_flags; /* flags on received message */ 18 }; 19 /* msg_name:数据的目的地址,网络包指向sockaddr_in, netlink则指向sockaddr_nl; 20 msg_namelen: msg_name 所代表的地址长度 21 msg_iov: 指向的是缓冲区数组 22 msg_iovlen: 缓冲区数组长度 23 msg_control: 辅助数据,控制信息(发送任何的控制信息) 24 msg_controllen: 辅助信息长度 25 msg_flags: 消息标识 26 */
首先看一下几个重要的数据结构的关系:
1、struct msghdr 结构体
msghdr这个结构在socket变成中就会用到,并不算Netlink专有的,这里不在过多说明。只说明一下如何更好理解这个结构的功能。我们
知道socket消息的发送和接收函数一般有这几对:recv/send、readv/writev、recvfrom/sendto。当然还有recvmsg/sendmsg,
前面三对函数各有各的特点功能,而recvmsg/sendmsg就是要囊括前面三对的所有功能,当然还有自己特殊的用途。msghdr的前两个
成员就是为了满足recvfrom/sendto的功能,中间两个成员msg_iov和msg_iovlen则是为了满足readv/writev的功能,而最后的
msg_flags则是为了满足recv/send中flag的功能,剩下的msg_control和msg_controllen则是满足recvmsg/sendmsg特有的功能。
2、struct sockaddr_nl
struct sockaddr_nl为Netlink的地址,和我们通常socket编程中的sockaddr_in作用一样,他们的结构对比如下:
struct sockaddr_nl的详细定义和描述如下:
1 struct sockaddr_nl 2 { 3 sa_family_t nl_family; /*该字段总是为AF_NETLINK */ 4 unsigned short nl_pad; /* 目前未用到,填充为0*/ 5 __u32 nl_pid; /* process pid */ 6 __u32 nl_groups; /* multicast groups mask */ 7 };
(1) nl_pid:在Netlink规范里,PID全称是Port-ID(32bits),其主要作用是用于唯一的标识一个基于netlink的socket通道。通常
情况下nl_pid都设置为当前进程的进程号。前面我们也说过,Netlink不仅可以实现用户-内核空间的通信还可使现实用户空间两
个进程之间,或内核空间两个进程之间的通信。该属性为0时一般指内核。
(2) nl_groups:如果用户空间的进程希望加入某个多播组,则必须执行bind()系统调用。该字段指明了调用者希望加入的多播组
号的掩码(注意不是组号,后面我们会详细讲解这个字段)。如果该字段为0则表示调用者不希望加入任何多播组。对于每个隶属于
Netlink协议域的协议,最多可支持32个多播组(因为nl_groups的长度为32比特),每个多播组用一个比特来表示。
3、struct nlmsghdr
Netlink的报文由消息头和消息体构成,struct nlmsghdr即为消息头。消息头定义在文件里,由结构体nlmsghdr表示:
1 struct nlmsghdr { 2 __u32 nlmsg_len; /* Length of message including header */ 3 __u16 nlmsg_type; /* Message content */ 4 __u16 nlmsg_flags; /* Additional flags */ 5 __u32 nlmsg_seq; /* Sequence number */ 6 __u32 nlmsg_pid; /* Sending process port ID */ 7 };
消息头中各成员属性的解释及说明:
(1) nlmsg_len:整个消息的长度,按字节计算。包括了Netlink消息头本身。
(2) nlmsg_type:消息的类型,即是数据还是控制消息。目前(内核版本2.6.21)Netlink仅支持四种类型的控制消息,如下:
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 */
NLMSG_NOOP 空消息,什么也不做;
NLMSG_ERROR 指明该消息中包含一个错误;
NLMSG_DONE 如果内核通过Netlink队列返回了多个消息,那么队列的最后一条消息的类型为NLMSG_DONE,其余所有消息的nlmsg_flags属性都被设置NLM_F_MULTI位有·效。
NLMSG_OVERRUN 暂时没用到。
(3) nlmsg_flags:附加在消息上的额外说明信息,如上面提到的NLM_F_MULTI。
4、struct genlmsghdr
1 struct genlmsghdr { 2 __u8 cmd; 3 __u8 version; 4 __u16 reserved; 5 };
(1)、cmd表示消息命令,对于用户自己定义的每个子协议类型都需要定义特定的消息命令集,这里该字段表示当前消息的消息命令;
(2)、version字段表示版本控制,可以在在不破坏向后兼容性的情况下修改消息的格式,可以不使用该字段;
(3)、reserved字段保留。
5、struct nlattr
1 /* 2 * <------- NLA_HDRLEN ------> <-- NLA_ALIGN(payload)--> 3 * +---------------------+- - -+- - - - - - - - - -+- - -+ 4 * | Header | Pad | Payload | Pad | 5 * | (struct nlattr) | ing | | ing | 6 * +---------------------+- - -+- - - - - - - - - -+- - -+ 7 * <-------------- nlattr->nla_len --------------> 8 */ 9 10 struct nlattr { 11 __u16 nla_len; 12 __u16 nla_type; 13 };netlink的消息头后面跟着的是消息的有效载荷部分,它采用的是格式为“类型——长度——值”,简写TLV。
其中类型和长度使用属性头nlattr来表示。其中nla_len表示属性长度;
nla_type表示属性类型。
二、数据解析
1 //四字节 2 #define NLMSG_ALIGNTO 4U 3 4 //用于得到不小于len且字节对齐的最小数值 5 #define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) ) 6 7 // Netlink 头部长度 8 #define NLMSG_HDRLEN ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr))) 9 10 //计算消息数据len的真实消息长度(消息体 + 消息头) 11 #define NLMSG_LENGTH(len) ((len) + NLMSG_HDRLEN) 12 13 //返回不小于NLMSG_LENGTH(len)且字节对齐的最小数值 14 #define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len)) 15 16 //取得struct nlmsghdr结构体消息的数据部分的首地址,设置和读取消息数据部分时需要使用该宏 17 //获取struct genlmsghdr 结构体位置 18 #define NLMSG_DATA(nlh) ((void *)(((char *)nlh) + NLMSG_HDRLEN)) 19 20 //用于得到下一个消息的首地址, 同时len 变为剩余消息的长度 21 #define NLMSG_NEXT(nlh,len) ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \ 22 (struct nlmsghdr *)(((char *)(nlh)) + \ 23 NLMSG_ALIGN((nlh)->nlmsg_len))) 24 25 //判断消息是否 >len 26 #define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) && \ 27 (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \ 28 (nlh)->nlmsg_len <= (len)) 29 30 //用于返回payload的长度 31 #define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len))) 32 33 34 35 //用于取得struct genlmsghdr结构体后面消息的数据部分的首地址 36 //获取struct nlattr 结构体位置 37 #define GENLMSG_DATA(glh) ((void *)(NLMSG_DATA(glh) + GENL_HDRLEN)) 38 39 // 40 #define GENLMSG_PAYLOAD(glh) (NLMSG_PAYLOAD(glh, 0) - GENL_HDRLEN) 41 42 43 44 //用于取得struct nlattr结构体后面消息的数据部分的首地址 45 #define NLA_DATA(na) ((void *)((char*)(na) + NLA_HDRLEN)) 46 47 #define NLA_PAYLOAD(len) (len - NLA_HDRLEN) 48 49 #define NLA_ALIGNTO 4U 50 51 #define NLA_ALIGN(len) (((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1)) 52 53 #define NLA_HDRLEN ((int) NLA_ALIGN(sizeof(struct nlattr))) 54 55 56 #define NLA_NEXT(nla,len) ((len) -= NLA_ALIGN((nla)->nla_len), \ 57 (struct nlattr*)(((char*)(nla)) + NLA_ALIGN((nla)->nla_len))) 58 59 #define NLA_OK(nla,len) ((len) >= (int)sizeof(struct nlattr) && \ 60 (nla)->nla_len >= sizeof(struct nlattr) && \ 61 (nla)->nla_len <= (len))
1、数据结构
1 /* ======================================================================== 2 * Netlink Messages and Attributes Interface (As Seen On TV) 3 * ------------------------------------------------------------------------ 4 * Messages Interface 5 * ------------------------------------------------------------------------ 6 * 7 * Message Format: 8 * <--- nlmsg_total_size(payload) ---> 9 * <-- nlmsg_msg_size(payload) -> 10 * +----------+- - -+-------------+- - -+-------- - - 11 * | nlmsghdr | Pad | Payload | Pad | nlmsghdr 12 * +----------+- - -+-------------+- - -+-------- - - 13 * nlmsg_data(nlh)---^ ^ 14 * nlmsg_next(nlh)-----------------------+ 15 * 16 * Payload Format: 17 * <---------------------- nlmsg_len(nlh) ---------------------> 18 * <------ hdrlen ------> <- nlmsg_attrlen(nlh, hdrlen) -> 19 * +----------------------+- - -+--------------------------------+ 20 * | Family Header | Pad | Attributes | 21 * +----------------------+- - -+--------------------------------+ 22 * nlmsg_attrdata(nlh, hdrlen)---^ 23 * 24 * Data Structures: 25 * struct nlmsghdr netlink message header 26 */
1 /* 2 * <------- NLA_HDRLEN ------> <-- NLA_ALIGN(payload)--> 3 * +---------------------+- - -+- - - - - - - - - -+- - -+ 4 * | Header | Pad | Payload | Pad | 5 * | (struct nlattr) | ing | | ing | 6 * +---------------------+- - -+- - - - - - - - - -+- - -+ 7 * <-------------- nlattr->nla_len --------------> 8 */ 9 10 struct nlattr { 11 __u16 nla_len; 12 __u16 nla_type; 13 }; 14 15 /* 16 * nla_type (16 bits) 17 * +---+---+-------------------------------+ 18 * | N | O | Attribute Type | 19 * +---+---+-------------------------------+ 20 * N := Carries nested attributes 21 * O := Payload stored in network byte order 22 * 23 * Note: The N and O flag are mutually exclusive. 24 */
(1)、nlmsg_msg_size(payload)
1 /** 2 * nlmsg_msg_size :不包括填充的网络链接消息长度 3 * @payload: 消息有效负载长度 4 */ 5 static inline int nlmsg_msg_size(int payload) 6 { 7 return NLMSG_HDRLEN + payload; 8 }
(2)、nlmsg_total_size(payload)
1 /** 2 * nlmsg_total_size : 包括填充的网络链路消息长度 3 * @payload:消息有效负载长度 4 */ 5 static inline int nlmsg_total_size(int payload) 6 { 7 return NLMSG_ALIGN(nlmsg_msg_size(payload)); 8 }
(3)、nlmsg_data(nlh)
1 /** 2 * nlmsg_data : 报文有效载荷头 3 * @nlh: netlink message header 4 */ 5 static inline void *nlmsg_data(const struct nlmsghdr *nlh) 6 { 7 return (unsigned char *) nlh + NLMSG_HDRLEN; 8 } 9 10 //取得struct nlmsghdr结构体消息的数据部分的首地址,设置和读取消息数据部分时需要使用该宏 11 #define NLMSG_DATA(nlh) ((void *)(((char *)nlh) + NLMSG_HDRLEN))
(4)、nlmsg_next(nlh)
/** * nlmsg_next - next netlink message in message stream * @nlh: netlink message header * @remaining: 在消息流中剩余的字节数 * * Returns the next netlink message in the message stream and * decrements remaining by the size of the current message. */ static inline struct nlmsghdr *nlmsg_next(const struct nlmsghdr *nlh, int *remaining) { int totlen = NLMSG_ALIGN(nlh->nlmsg_len); *remaining -= totlen; return (struct nlmsghdr *) ((unsigned char *) nlh + totlen); } //用于得到下一个消息的首地址, 同时len 变为剩余消息的长度 #define NLMSG_NEXT(nlh,len) ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \ (struct nlmsghdr *)(((char *)(nlh)) + \ NLMSG_ALIGN((nlh)->nlmsg_len)))
(5)、nlmsg_len(nlh)
1 /** 2 * nlmsg_len - 消息有效负载长度 3 * @nlh: netlink message header 4 */ 5 static inline int nlmsg_len(const struct nlmsghdr *nlh) 6 { 7 return nlh->nlmsg_len - NLMSG_HDRLEN; 8 }
(6)、nlmsg_attrdata(nlh, hdrlen)
1 /** 2 * nlmsg_attrdata - head of attributes data 3 * @nlh: netlink message header 4 * @hdrlen: length of family specific header 5 */ 6 static inline struct nlattr *nlmsg_attrdata(const struct nlmsghdr *nlh, int hdrlen) 7 { 8 unsigned char *data = nlmsg_data(nlh); 9 return (struct nlattr *) (data + NLMSG_ALIGN(hdrlen)); 10 } 11 12 //用于取得struct genlmsghdr结构体后面消息的数据部分的首地址 13 #define GENLMSG_DATA(glh) ((void *)(NLMSG_DATA(glh) + GENL_HDRLEN))
(7)、nlmsg_attrlen(nlh, hdrlen)
1 /** 2 * nlmsg_attrlen - length of attributes data 3 * @nlh: netlink message header 4 * @hdrlen: length of family specific header 5 */ 6 static inline int nlmsg_attrlen(const struct nlmsghdr *nlh, int hdrlen) 7 { 8 return nlmsg_len(nlh) - NLMSG_ALIGN(hdrlen); 9 } 10 11 #define GENLMSG_PAYLOAD(glh) (NLMSG_PAYLOAD(glh, 0) - GENL_HDRLEN)
(8)、其他
1 /** 2 * nlmsg_ok - check if the netlink message fits into the remaining bytes 3 * @nlh: netlink message header 4 * @remaining: number of bytes remaining in message stream 5 */ 6 static inline int nlmsg_ok(const struct nlmsghdr *nlh, int remaining) 7 { 8 return (remaining >= (int) sizeof(struct nlmsghdr) && 9 nlh->nlmsg_len >= sizeof(struct nlmsghdr) && 10 nlh->nlmsg_len <= remaining); 11 } 12 13 /** 14 * nlmsg_next - next netlink message in message stream 15 * @nlh: netlink message header 16 * @remaining: number of bytes remaining in message stream 17 * 18 * Returns the next netlink message in the message stream and 19 * decrements remaining by the size of the current message. 20 */ 21 static inline struct nlmsghdr * 22 nlmsg_next(const struct nlmsghdr *nlh, int *remaining) 23 { 24 int totlen = NLMSG_ALIGN(nlh->nlmsg_len); 25 26 *remaining -= totlen; 27 28 return (struct nlmsghdr *) ((unsigned char *) nlh + totlen); 29 }
2、数据解析
数据分配
1 void *gxy_genlmsg_alloc(int *size) 2 { 3 unsigned char *buf; 4 int len; 5 /* 6 * attribute len 7 * attr len = (nla_hdr + pad) + (payload(user data) + pad) 8 */ 9 len = NLA_ALIGN( NLA_HDRLEN + *size); 10 /* 11 * family msg len, 12 * but actually we have NOT custom family header 13 * family msg len = family_hdr + payload(attribute) 14 */ 15 len += 0; 16 /* 17 * generic netlink msg len 18 * genlmsg len = (genlhdr + pad) + payload(family msg) 19 */ 20 len += GENL_HDRLEN; 21 /* 22 * netlink msg len 23 * nlmsg len = (nlmsghdr + pad) + (payload(genlmsg) + pad) 24 */ 25 len = GXY_NLMSG_SPACE(len); 26 27 buf = malloc(len); 28 if (!buf) 29 return NULL; 30 31 memset(buf, 0, len); 32 *size = len; 33 34 return buf; 35 }
接收数据函数:
1 /** 2 * 3 * @param sockfd generic netlink socket fd 4 * @param buf the 'buf' is including the struct nlmsghdr, 5 * struct genlmsghdr and struct nlattr 6 * @param len size of 'buf' 7 * @return >0 size of genlmsg 8 * <0 error occur 9 */ 10 int gxy_genlmsg_recv(int sockfd, unsigned char *buf, unsigned int len) 11 { 12 struct sockaddr_nl nladdr; 13 struct msghdr msg; 14 struct iovec iov; 15 16 int ret; 17 18 nladdr.nl_family = AF_NETLINK; 19 nladdr.nl_pid = getpid(); 20 nladdr.nl_groups = 0; 21 22 iov.iov_base = buf; 23 iov.iov_len = len; 24 25 msg.msg_name = (void *)&nladdr; 26 msg.msg_namelen = sizeof(nladdr); 27 28 msg.msg_iov = &iov; 29 msg.msg_iovlen = 1; 30 31 ret = recvmsg(sockfd, &msg, 0); 32 ret = ret > 0 ? ret : -1; 33 34 return ret; 35 36 }
由gxy_genlmsg_recv()接收到数据为buf,长度为ret
struct nlmsghdr *nlh = NULL;
struct genlmsghdr *glh = NULL;
struct nlattr *nla = NULL;
nlh = (struct nlmsghdr *)buf;
glh = NLMSG_DATA(nlh);
nla = GENLMSG_DATA(nlh);
实际数据 char *temp_buf
memcpy(temp_buf,NLA_DATA(nla), nla->nla_type);
发送数据
1 int gxy_genlmsg_send(int sockfd, unsigned short nlmsg_type, unsigned int nlmsg_pid, 2 unsigned char genl_cmd, unsigned char genl_version, 3 unsigned short nla_type, const void *nla_data, unsigned int nla_len) 4 { 5 struct nlmsghdr *nlh = NULL; //netlink message header 6 struct genlmsghdr *glh = NULL; //generic netlink message header 7 struct nlattr *nla = NULL; //netlink attribute header 8 9 struct sockaddr_nl nladdr; 10 unsigned char *buf = NULL; 11 int len = 0,count = 0,ret = -1; 12 13 if ((nlmsg_type == 0) || (!nla_data) || (nla_len <= 0)){ 14 return -1; 15 } 16 17 len = nla_len; 18 19 buf = gxy_genlmsg_alloc(&len); 20 if (!buf){ 21 return -1; 22 } 23 nlh = (struct nlmsghdr *)buf; 24 nlh->nlmsg_len = len; 25 nlh->nlmsg_type = nlmsg_type; 26 nlh->nlmsg_flags = NLM_F_REQUEST; 27 nlh->nlmsg_seq = 0; 28 nlh->nlmsg_pid = nlmsg_pid; 29 30 glh = (struct genlmsghdr *)NLMSG_DATA(nlh); 31 glh->cmd = genl_cmd; 32 glh->version = genl_version; 33 34 35 nla = (struct nlattr *)GENLMSG_DATA(nlh);// 36 nla->nla_type = nla_type; 37 nla->nla_len = gxy_nla_attr_size(nla_len); 38 memcpy(NLA_DATA(nla), nla_data, nla_len); 39 40 memset(&nladdr, 0, sizeof(nladdr)); 41 nladdr.nl_family = AF_NETLINK; 42 43 do { 44 45 ret = sendto(sockfd, &buf[count], len - count, 0,(struct sockaddr *)&nladdr, sizeof(nladdr)); 46 if (ret < 0){ 47 if (errno != EAGAIN){ 48 count = -1; 49 goto out; 50 } 51 } else { 52 count += ret; 53 } 54 55 }while (count < len); 56 57 out: 58 free(buf); 59 buf = NULL; 60 return count; 61 }
例如要发送的数据为
char *msg_buf = "Hello world";
则:gxy_genlmsg_send(sockfd, nlmsg_type, nlmsg_pid,genl_cmd, genl_version,nla_type, msg_buf, strlen(msg_buf) + 1);
标签:netlink,struct,Generic,Netlink,len,nla,nlmsg,nlh,NLMSG From: https://www.cnblogs.com/ink-white/p/16841824.html