linux 获取网络状态信息(Rtnetlink)

一、Rtnetlink

  Rtnetlink 允许对内核路由表进行读和更改,它用于内核与各个子系统之间(路由子系统、IP地址、链接参数等)的通信,

用户空间可以通过NET_LINK_ROUTER socket 与内核进行通信,该过程基于标准的netlink消息进行。

  注:netlink用法在上一篇博文中有提到 http://www.cnblogs.com/wenqiang/p/6306727.html

一些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

posted @ 2017-03-28 14:28  zhangwju  阅读(23565)  评论(2编辑  收藏  举报