一、Rtnetlink
Rtnetlink 允许对内核路由表进行读和更改,它用于内核与各个子系统之间(路由子系统、IP地址、链接参数等)的通信,
用户空间可以通过NET_LINK_ROUTER socket 与内核进行通信,该过程基于标准的netlink消息进行。
注:netlink用法在上一篇博文中有提到
一些rtnetlink消息在初始头后有一些可选属性,下面是该属性的结构:
1 struct rtattr {
2 unsigned short rta_len; /* Length of option */
3 unsigned short rta_type; /* Type of option */
4 /* Data follows */
5 };
操作这些属性只可以用RTA_*这些宏来造作
1 /* Macros to handle rtattributes */
2
3 /* 对齐 */
4 #define RTA_ALIGNTO 4
5 #define RTA_ALIGN(len) ( ((len)+RTA_ALIGNTO-1) & ~(RTA_ALIGNTO-1) )
6
7 /* 判断是否为合法的路由属性 */
8 #define RTA_OK(rta,len) ((len) >= (int)sizeof(struct rtattr) && \
9 (rta)->rta_len >= sizeof(struct rtattr) && \
10 (rta)->rta_len <= (len))
11
12 /* 获取下一个rtattr的首地址*/
13 #define RTA_NEXT(rta,attrlen) ((attrlen) -= RTA_ALIGN((rta)->rta_len), \
14 (struct rtattr*)(((char*)(rta)) + RTA_ALIGN((rta)->rta_len)))
15
16 /* 返回加上 rtattr header的总长度 */
17 #define RTA_LENGTH(len) (RTA_ALIGN(sizeof(struct rtattr)) + (len))
18
19 /* 返回数据对齐的最小值 */
20 #define RTA_SPACE(len) RTA_ALIGN(RTA_LENGTH(len))
21
22 /* 返回属性数据部分首地址 */
23 #define RTA_DATA(rta) ((void*)(((char*)(rta)) + RTA_LENGTH(0)))
24
25 /*返回属性数据部分的长度 */
26 #define RTA_PAYLOAD(rta) ((int)((rta)->rta_len) - RTA_LENGTH(0))
27
28 /*****************************************************************
29 ******************************************************************/
30 rtnetlink_socket = socket(AF_NETLINK, int socket_type, NETLINK_ROUTE);
31
32 int RTA_OK(struct rtattr *rta, int rtabuflen);
33
34 void *RTA_DATA(struct rtattr *rta);
35
36 unsigned int RTA_PAYLOAD(struct rtattr *rta);
37
38 struct rtattr *RTA_NEXT(struct rtattr *rta, unsigned int rtabuflen);
39
40 unsigned int RTA_LENGTH(unsigned int length);
41
42 unsigned int RTA_SPACE(unsigned int length);
Rtnetlink 由下面这些消息类型构成(新加在标准的netlink消息上)
(1)#RTM_NEWLINK, RTM_DELLINK, RTM_GETLINK
创建或者删除一个特定的网络接口,或者从一个特定的网络接口上获得信息。
这些消息含有一个ifinfomsg类型的结构,紧跟在后面的是一系列的rtattr结构。
1 /*****************************************************************
2 * Link layer specific messages.
3 ****/
4
5 /* struct ifinfomsg
6 * passes link level specific information, not dependent
7 * on network protocol.
8 */
9
10 struct ifinfomsg {
11 unsigned char ifi_family; /* AF_UNSPEC */
12 unsigned short ifi_type; /* Device type */
13 int ifi_index; /* Interface index */
14 unsigned int ifi_flags; /* Device flags */
15 unsigned int ifi_change; /* change mask */
16 };
17 /*
18 * ifi_family: 接口地址类型
19 * ifi_type: 设备类型
20 * ifi_index: 是结构唯一的索引
21 * ifi_flags: 设备标志,可以看netdevice 结构
22 * ifi_change: 保留值,通常设置为0xFFFFFFFF
23 */
24
25 /*
26 ifi_type代表硬件设备的类型:
27 ARPHRD_ETHER 10M以太网
28 ARPHRD_PPP PPP拨号
29 ARPHRDLOOPBACK 环路设备
30
31 ifi_flags包含设备的一些标志:
32 IFF_UP 接口正在运行
33 IFF_BROADCAST 有效的广播地址集
34 IFF_DEBUG 内部调试标志
35 IFF_LOOPBACK 这是自环接口
36 IFF_POINTOPOINT 这是点到点的链路设备
37 IFF_RUNNING 资源已分配
38 IFF_NOARP 无arp协议,没有设置第二层目的地址
39 IFF_PROMISC 接口为杂凑(promiscuous)模式
40 IFF_NOTRAILERS 避免使用trailer
41 IFF_ALLMULTI 接收所有组播(multicast)报文
42 IFF_MASTER 主负载平衡群(bundle)
43 IFF_SLAVE 从负载平衡群(bundle)
44 IFF_MULTICAST 支持组播(multicast)
45 IFF_PORTSEL 可以通过ifmap选择介质(media)类型
46 IFF_AUTOMEDIA 自动选择介质
47 IFF_DYNAMIC 接口关闭时丢弃地址
48
49 Routing attributes(rtattr部分属性,rta_type)
50
51 rta_type value type description
52 ──────────────────────────────────────────────────────────
53 IFLA_UNSPEC - 未说明,未指定的数据
54 IFLA_ADDRESS hardware address L2硬件地址
55 IFLA_BROADCAST hardware address L2广播地址.
56 IFLA_IFNAME asciiz string char型设备名.
57 IFLA_MTU unsigned int MTU of the device.
58 IFLA_LINK int Link type.
59 IFLA_QDISC asciiz string Queueing discipline.
60 IFLA_STATS see below struct rtnl_link_stats的设备信息
61
62 //用来获取ifinfomsg后面的rtattr结构
63 #define IFLA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg))))
64 */
(2)# RTM_NEWADDR, RTM_DELADDR, RTM_GETADDR
添加,删除或者接收一个和接口相关的IP地址的信息。
在linux2.2中,一个网口是可以有多个IP地址信息的。这些消息含有一个ifaddrmsg类型的结构,紧跟在后面的是一系列的rtattr结构。
1 struct ifaddrmsg {
2 unsigned char ifa_family; /* Address type */
3 unsigned char ifa_prefixlen; /* Prefixlength of address */
4 unsigned char ifa_flags; /* Address flags */
5 unsigned char ifa_scope; /* Address scope */
6 int ifa_index; /* Interface index */
7 };
8 /*
9 * ifa_family: 地址类型(通常为AF_INET or AF_INET6))
10 * ifa_prefixlen: 地址的地址掩码长度,如果改地址定义在这个family
11 * ifa_flags:
12 * ifa_scope: 地址的作用域
13 * ifa_index: 接口索引与接口地址关联
14 */
15
16 /*
17 Attributes (rtattr部分属性,rta_type)
18 rta_type value type description
19 ─────────────────────────────────────────────────────────────
20 IFA_UNSPEC - unspecified.
21 IFA_ADDRESS raw protocol address 接口地址 interface address
22 IFA_LOCAL raw protocol address 本地地址 local address
23 IFA_LABEL asciiz string 接口名称 name of the interface
24 IFA_BROADCAST raw protocol address 广播 broadcast address.
25 IFA_ANYCAST raw protocol address anycast address
26 IFA_CACHEINFO struct ifa_cacheinfo Address information.
27
28 */
(3)#RTM_NEWROUTE, RTM_DELROUTE, RTM_GETROUTE
创建,删除或者获取网络设备的路由信息;这些消息包含一个rtmsg结构,其后跟数目可选的rtattr结构。
对于RTM_GETROUTE,设置rtm_dst_len以及rtm_src_len为0表示获取指定路由表的所有条目(entries)。
其它的成员,除了rtm_table、rtm_protocol,0是通配符
1 struct rtmsg {
2 unsigned char rtm_family;
3 unsigned char rtm_dst_len;
4 unsigned char rtm_src_len;
5 unsigned char rtm_tos;
6
7 unsigned char rtm_table; /* Routing table id */
8 unsigned char rtm_protocol; /* Routing protocol; see below */
9 unsigned char rtm_scope; /* See below */
10 unsigned char rtm_type; /* See below */
11
12 unsigned rtm_flags;
13 };
14
15 rtm_type Route type
16 ───────────────────────────────────────────────────────────
17 RTN_UNSPEC unknown route /*位置路由*/
18 RTN_UNICAST a gateway or direct route /* 网关或直连路由 */
19 RTN_LOCAL a local interface route /* 本地接口路由 */
20 RTN_BROADCAST a local broadcast route (sent as a broadcast) /* 本地广播式接收,发送 */
21 RTN_ANYCAST a local broadcast route (sent as a unicast) /* 本地单播路由 */
22 RTN_MULTICAST a multicast route /* 多播路由 */
23 RTN_BLACKHOLE a packet dropping route /* 丢弃 */
24 RTN_UNREACHABLE an unreachable destination /* 目标不可达 */
25 RTN_PROHIBIT a packet rejection route /* 拒绝 */
26 RTN_THROW continue routing lookup in another table /* 不在本表 */
27 RTN_NAT a network address translation rule /* nat */
28 RTN_XRESOLVE refer to an external resolver (not implemented)
29
30 rtm_protocol Route origin.
31 ───────────────────────────────────────
32 RTPROT_UNSPEC unknown
33 RTPROT_REDIRECT by an ICMP redirect (currently unused) /* 通过icmp转发建立路由 (目前没用)*/
34 RTPROT_KERNEL by the kernel /* 通过内核建立路由 */
35 RTPROT_BOOT during boot /* 启动时建立路由 */
36 RTPROT_STATIC by the administrator /* 管理员建立 */
37
38 rtm_scope is the distance to the destination:
39
40 RT_SCOPE_UNIVERSE global route
41 RT_SCOPE_SITE interior route in the local autonomous system
42 RT_SCOPE_LINK route on this link
43 RT_SCOPE_HOST route on the local host
44 RT_SCOPE_NOWHERE destination doesn't exist
45
46 /* 用户可用范围 */
47 RT_SCOPE_UNIVERSE ~ RT_SCOPE_SITE are available to the user.
48
49 The rtm_flags have the following meanings:
50
51 RTM_F_NOTIFY if the route changes, notify the user via rtnetlink
52 RTM_F_CLONED route is cloned from another route
53 RTM_F_EQUALIZE a multipath equalizer (not yet implemented)
54
55 rtm_table specifies the routing table
56
57 RT_TABLE_UNSPEC an unspecified routing table /* 0 未指定的表 */
58 RT_TABLE_DEFAULT the default table /* 253 默认表 */
59 RT_TABLE_MAIN the main table /* 254 main 表 */
60 RT_TABLE_LOCAL the local table /* 255 local 表 */
61
62 //用户可以使用 RT_TABLE_UNSPEC 到 RT_TABLE_DEFAULT 之间的任意值
63
64 Attributes
65
66 rta_type value type description
67 ──────────────────────────────────────────────────────────────
68 RTA_UNSPEC - ignored.
69 RTA_DST protocol address Route destination address. /* 目的 */
70 RTA_SRC protocol address Route source address. /* 源地址 */
71 RTA_IIF int Input interface index. /* 输入设备 index */
72 RTA_OIF int Output interface index.
73 RTA_GATEWAY protocol address The gateway of the route /* 网关 */
74 RTA_PRIORITY int Priority of route. /* 优先级 */
75 RTA_PREFSRC
76 RTA_METRICS int Route metric /* 路由metric 值*/
77 RTA_MULTIPATH
78 RTA_PROTOINFO
79 RTA_FLOW
80 RTA_CACHEINFO
一面是一个具体实例:
1 /*********************************************************
2 * Filename: nl_netinfo.c
3 * Author: zhangwj
4 * Date:
5 * Descripte:
6 * Email:
7 * Warnning:
8 **********************************************************/
9 #include <stdio.h>
10 #include <string.h>
11 #include <stdlib.h>
12 #include <sys/types.h> /* See NOTES */
13 #include <sys/socket.h>
14 #include <arpa/inet.h>
15 #include <sys/epoll.h>
16 #include <linux/netlink.h>
17 #include <linux/rtnetlink.h>
18 #include <linux/route.h>
19 #include <errno.h>
20
21 #define EPOLL_LISTEN_MAX_CNT 256
22 #define EPOLL_LISTEN_TIMEOUT 500
23
24 int g_nlfd = -1;
25 int g_epollfd = -1;
26
27 void parse_rtattr(struct rtattr **tb, int max, struct rtattr *attr, int len)
28 {
29 for (; RTA_OK(attr, len); attr = RTA_NEXT(attr, len)) {
30 if (attr->rta_type <= max) {
31 tb[attr->rta_type] = attr;
32 }
33 }
34 }
35
36 void nl_netroute_handle(struct nlmsghdr *nlh)
37 {
38 int len;
39 struct rtattr *tb[RTA_MAX + 1];
40 struct rtmsg *rt;
41 char tmp[256];
42
43 bzero(tb, sizeof(tb));
44 rt = NLMSG_DATA(nlh);
45 len = nlh->nlmsg_len - NLMSG_SPACE(sizeof(*rt));
46 parse_rtattr(tb, RTA_MAX, RTM_RTA(rt), len);
47 printf("%s: ", (nlh->nlmsg_type==RTM_NEWROUTE)?"NEWROUT":"DELROUT");
48 if (tb[RTA_DST] != NULL) {
49 inet_ntop(rt->rtm_family, RTA_DATA(tb[RTA_DST]), tmp, sizeof(tmp));
50 printf("DST: %s ", tmp);
51 }
52 if (tb[RTA_SRC] != NULL) {
53 inet_ntop(rt->rtm_family, RTA_DATA(tb[RTA_SRC]), tmp, sizeof(tmp));
54 printf("SRC: %s ", tmp);
55 }
56 if (tb[RTA_GATEWAY] != NULL) {
57 inet_ntop(rt->rtm_family, RTA_DATA(tb[RTA_GATEWAY]), tmp, sizeof(tmp));
58 printf("GATEWAY: %s ", tmp);
59 }
60 printf("\n");
61 }
62
63 void nl_netifinfo_handle(struct nlmsghdr *nlh)
64 {
65 int len;
66 struct rtattr *tb[IFLA_MAX + 1];
67 struct ifinfomsg *ifinfo;
68
69 bzero(tb, sizeof(tb));
70 ifinfo = NLMSG_DATA(nlh);
71 len = nlh->nlmsg_len - NLMSG_SPACE(sizeof(*ifinfo));
72 parse_rtattr(tb, IFLA_MAX, IFLA_RTA (ifinfo), len);
73
74 printf("%s: %s ", (nlh->nlmsg_type==RTM_NEWLINK) ? "NEWLINK" : "DELLINK", (ifinfo->ifi_flags & IFF_UP) ? "up" : "down");
75 if(tb[IFLA_IFNAME]) {
76 printf("%s", RTA_DATA(tb[IFLA_IFNAME]));
77 }
78 printf("\n");
79 }
80
81 void nl_netifaddr_handle(struct nlmsghdr *nlh)
82 {
83 int len;
84 struct rtattr *tb[IFA_MAX + 1];
85 struct ifaddrmsg *ifaddr;
86 char tmp[256];
87
88 bzero(tb, sizeof(tb));
89 ifaddr = NLMSG_DATA(nlh);
90 len =nlh->nlmsg_len - NLMSG_SPACE(sizeof(*ifaddr));
91 parse_rtattr(tb, IFA_MAX, IFA_RTA (ifaddr), len);
92
93 printf("%s ", (nlh->nlmsg_type == RTM_NEWADDR)? "NEWADDR":"DELADDR");
94 if (tb[IFA_LABEL] != NULL) {
95 printf("%s ", RTA_DATA(tb[IFA_LABEL]));
96 }
97 if (tb[IFA_ADDRESS] != NULL) {
98 inet_ntop(ifaddr->ifa_family, RTA_DATA(tb[IFA_ADDRESS]), tmp, sizeof(tmp));
99 printf("%s ", tmp);
100 }
101 printf("\n");
102 }
103
104 void nl_netlink_handle(int fd)
105 {
106 int r_size;
107 socklen_t len = 0;
108 char buff[2048] = {0};
109 struct sockaddr_nl addr;
110 struct nlmsghdr *nlh;
111
112 while(1)
113 {
114 len = sizeof(addr);
115 r_size = recvfrom(fd, (void *)buff, sizeof(buff), 0, (struct sockaddr *)&addr, &len);
116 nlh = (struct nlmsghdr *)buff;
117 for(; NLMSG_OK(nlh, r_size); nlh = NLMSG_NEXT(nlh, r_size))
118 {
119 switch(nlh->nlmsg_type) {
120 case NLMSG_DONE:
121 case NLMSG_ERROR:
122 break;
123 case RTM_NEWLINK: /* */
124 case RTM_DELLINK:
125 nl_netifinfo_handle(nlh);
126 break;
127 case RTM_NEWADDR:
128 case RTM_DELADDR: /* */
129 nl_netifaddr_handle(nlh);
130 break;
131 case RTM_NEWROUTE:
132 case RTM_DELROUTE: /* */
133 nl_netroute_handle(nlh);
134 break;
135 default:
136 break;
137 }
138 }
139 }
140 }
141
142 void epoll_event_handle(void)
143 {
144 int i = 0;
145 int fd_cnt = 0;
146 int sfd;
147 struct epoll_event events[EPOLL_LISTEN_MAX_CNT];
148
149 memset(events, 0, sizeof(events));
150 while(1)
151 {
152 /* wait epoll event */
153 fd_cnt = epoll_wait(g_epollfd, events, EPOLL_LISTEN_MAX_CNT, EPOLL_LISTEN_TIMEOUT);
154 for(i = 0; i < fd_cnt; i++)
155 {
156 sfd = events[i].data.fd;
157 if(events[i].events & EPOLLIN)
158 {
159 if (sfd == g_nlfd)
160 {
161 nl_netlink_handle(sfd);
162 }
163 }
164 }
165 }
166 }
167
168 int epoll_add_fd(int fd)
169 {
170 struct epoll_event ev;
171
172 ev.data.fd = fd;
173 ev.events = EPOLLIN | EPOLLET;
174
175 if (epoll_ctl(g_epollfd, EPOLL_CTL_ADD, fd, &ev) < 0) {
176 perror("epoll add fd error");
177 return -1;
178 }
179
180 printf("epoll add fd[%d] success\n", fd);
181 return 0;
182 }
183
184 int init_epoll_fd()
185 {
186 int epollfd = -1;
187
188 epollfd = epoll_create(EPOLL_LISTEN_MAX_CNT);
189 if (epollfd < 0) {
190 perror("epoll create failure!...");
191 return -1;
192 }
193 g_epollfd = epollfd;
194
195 printf("epool create fd [%d] success\n", epollfd);
196 return g_epollfd;
197 }
198
199 int init_nl_sockfd()
200 {
201 int ret = 0;
202 int nlfd = -1;
203 struct sockaddr_nl sa;
204
205 /* open a netlink fd */
206 nlfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
207 if (nlfd < 0) {
208 perror("create netlink socket failure");
209 return -1;
210 }
211
212 memset(&sa, 0, sizeof(sa));
213 sa.nl_family = AF_NETLINK;
214 sa.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_IFADDR | RTMGRP_IPV6_ROUTE;
215
216 /* bind netlink */
217 ret = bind(nlfd, (struct sockaddr *)&sa, sizeof(sa));
218 if (ret < 0) {
219 perror("bind nlfd error");
220 close(nlfd);
221 return -1;
222 }
223
224 if (epoll_add_fd(nlfd)) {
225 close(nlfd);
226 return -1;
227 }
228 g_nlfd = nlfd;
229
230 printf("netlink create fd [%d] success\n", nlfd);
231 return nlfd;
232 }
233
234
235 int main(int argc, char **argv)
236 {
237 if (init_epoll_fd() < 0) { /* 创建epoll 监听fd */
238 return -1;
239 }
240
241 if (init_nl_sockfd() < 0) { /* 创建netlink */
242 return -1;
243 }
244
245 /* 循环接收处理 */
246 epoll_event_handle();
247
248 return 0;
249 }
参考资料:
http://www.man7.org/linux/man-pages/man7/rtnetlink.7.html
http://www.man7.org/linux/man-pages/man3/rtnetlink.3.html