Netlink分层模型及消息格式
通过libnl
能够很快的编写一个netlink程序框架,隐藏了socket,bind,send/recv等复杂调用,
但是数据的构造和解析还是很头疼的,尤其对于我这样的初学者来说,下面按TCP/IP分层模型来学学Netlink消息格式。
Netlink “三层” 模型
下面以netlink第一、二、三层类比TCP/IP模型中的网络层,传输层和应用层
1. Netlink消息第一层netlink protocol
,有下面这20种
2. Netlink消息第二层,以NETLINK_ROUTE
为例
- LINKS
- ADDRESSES
- ROUTES
- NEIGHBORS
- RULES
- DISCIPLINES
- CLASSES
- FILTERS
3. Netlink消息第三层,以ROUTES
为例
封装的ROUTE的属性头部及数据
netlink 数据结构可形象的表示为
<----- NLMSG_HDRLEN -----> <-------- RTM_PAYOAD(rtm) --->
<RTA_PAYLOAD(r)>
+------------------+- - -+---------------+- - -+--------------+--------+ - -+
| Netlink Header | Pad | Family Header | Pad | Attributes | rtattr | Pad|
| struct nlmsghdr | | struct rtmsg | | stuct rtattr | data | |
+------------------+- - -+---------------+- - -+--------------+--------+ - -+
^ ^ ^ ^ ^
nlh | | | |
NLMSG_DATA(nlh) --------^ | | |
RTM_RTA(rtm)-----------------------------------^ | |
RTA_DATA(rta)-------------------------------------------------^ RTA_NEXT(rta)
Pad
表示为字节对齐所填充的数据。
nlmsghdr
-
NLMSG_ALIGNTO
字节对齐的值,这里按4字节对齐,4U的意思就是 (unsigned int)4。 -
NLMSG_ALIGN(len)
按4字节对齐的长度,返回字节对齐后的值align_len,len=< align_len <=len+4。 -
NLMSG_HDRLEN
struct nlmsghdr 所占内存大小的对齐后值 -
NLMSG_LENGTH(len)
nlmsghdr长度加上len -
NLMSG_SPACE(len)
定义如上,作用参见下面的RTM_PAYLOAD(n) -
NLMSG_DATA(nlh)
从nlh首地址向后移动到data起始位置 -
NLMSG_NEXT(nlh, len)
使用了逗号表达式完成两件事,先调整剩余长度len,减去当前nlmsg的总长度;再定位到下一个nlmsg的起始位置。 -
NLMSG_OK(nlh, len)
检查nlmsg是否合法,剩余数据长度len要大于nlmsg头部长度,nlh->nlmsg_len 要大于nlmsg头部长度并且小于剩余长度len。 -
NLMSG_PAYLOAD(nlh, len)
nlmsg去掉头部的数据长度。
rtmsg
struct rtmsg {
unsigned char rtm_family;
unsigned char rtm_dst_len;
unsigned char rtm_src_len;
unsigned char rtm_tos;
unsigned char rtm_table; /* Routing table id */
unsigned char rtm_protocol; /* Routing protocol; see below */
unsigned char rtm_scope; /* See below */
unsigned char rtm_type; /* See below */
unsigned rtm_flags;
};
#define RTM_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct rtmsg))))
#define RTM_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct rtmsg))
-
RTM_RTA(r)
输入route message指针 struct rtmsg* r,返回route第一个属性首地址 -
RTM_PAYLOAD(n)
- RTM_PAYLOAD(n) - NLMSG_PAYLOAD(n,sizeof(struct rtmsg)) - ((n)->nlmsg_len - NLMSG_SPACE(sizeof(struct rtmsg))) - ((n)->nlmsg_len - NLMSG_ALIGN(NLMSG_LENGTH(sizeof(struct rtmsg)))) - ((n)->nlmsg_len - NLMSG_ALIGN((sizeof(struct rtmsg) + NLMSG_HDRLEN))) - ((n)->nlmsg_len - NLMSG_ALIGN((sizeof(struct rtmsg) + ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr))))))
去除字节对齐,,将n替换成 nlmsghdr*nlh
可以简化成如下
((nlh)->nlmsg_len - sizeof(struct rtmsg) - sizeof(struct nlmsghdr))
即rtmsg层封装的数据长度,相当于TCP数据包去掉IP报头和TCP报头长度得到TCP数据部分长度。
rtattr
-
RTA_ALIGNTO
字节对齐的值,这里按4字节对齐。 -
RTA_ALIGN(len)
输入一个长度len,返回字节对齐后的值align_len,len=< align_len <=len+RTA_ALIGNTO。 -
RTA_OK(rta,len)
输入一个struct rtattr* rta,和整个rtmsg剩余长度len,返回一个bool值,判断一个属性rta是否正确,条件有3个。 -
RTA_NEXT(rta,attrlen)
用了逗号表达式,先对attrlen减去rta属性内容的全部长度,然后返回下一个rtattr的首地址。 -
RTA_LENGTH(len)
struct rtattr 对齐后内存大小 加上 len。 -
RTA_SPACE
定义如下,目前在代码中没找到用处。 -
RTA_DATA(rta)
返回rta数据的起始位置,即rta位置向后移动一个 对齐的struct rtattr大小。 -
RTA_PAYLOAD(rta)
返回有效数据的长度。
参考
- libnl source code
- http://people.redhat.com/nhorman/papers/netlink.pdf
留下评论