zoukankan      html  css  js  c++  java
  • 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("
    ");  
     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("
    "); 
     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("
    ");
    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
    ", 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
    ", 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
    ", 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

  • 相关阅读:
    团队冲刺第二阶段-7
    用户体验评价
    团队冲刺第二阶段-6
    第十四周学习进度报告
    团队冲刺第二阶段-5
    团队冲刺第二阶段-4
    14周课堂测试---找水王
    进度日报14
    进度日报13
    进度日报12
  • 原文地址:https://www.cnblogs.com/wenqiang/p/6634447.html
Copyright © 2011-2022 走看看