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 }
参考资料: