linux 获取网络状态、路由等信息(Rtnetlink)

一、Rtnetlink

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

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

  注:netlink用法在上一篇博文中有提到 linux netlink通信机制 - zhangwju - 博客园一、什么是Netlink通信机制 Netlink套接字是用以实现用户进程与内核进程通信的一种特殊的进程间通信(IPC) ,也是网络应用程序与内核通信的最常用的接口。 Netlink 是一种特殊的 socket,它是 Linux 所特有的,类似于 BSD 中的AF_ROUTE 但又远比它的功能强大,目icon-default.png?t=N7T8http://www.cnblogs.com/wenqiang/p/6306727.html

一些rtnetlink消息在初始头后有一些可选属性,下面是该属性的结构:

 struct rtattr {
     unsigned short rta_len;    /* Length of option */
     unsigned short rta_type;   /* Type of option */
     /* Data follows */
 };

操作这些属性只可以用RTA_*这些宏来操作:

/* Macros to handle rtattributes */

/* 对齐 */
#define RTA_ALIGNTO 4
#define RTA_ALIGN(len) ( ((len)+RTA_ALIGNTO-1) & ~(RTA_ALIGNTO-1) )

/* 判断是否为合法的路由属性 */
#define RTA_OK(rta,len) ((len) >= (int)sizeof(struct rtattr) && \
             (rta)->rta_len >= sizeof(struct rtattr) && \
             (rta)->rta_len <= (len))

/* 获取下一个rtattr的首地址*/
#define RTA_NEXT(rta,attrlen)   ((attrlen) -= RTA_ALIGN((rta)->rta_len), \
                 (struct rtattr*)(((char*)(rta)) + RTA_ALIGN((rta)->rta_len)))

/* 返回加上 rtattr header的总长度 */
#define RTA_LENGTH(len) (RTA_ALIGN(sizeof(struct rtattr)) + (len))

/* 返回数据对齐的最小值 */
#define RTA_SPACE(len)  RTA_ALIGN(RTA_LENGTH(len))

/* 返回属性数据部分首地址 */
#define RTA_DATA(rta)   ((void*)(((char*)(rta)) + RTA_LENGTH(0)))

/*返回属性数据部分的长度 */
#define RTA_PAYLOAD(rta) ((int)((rta)->rta_len) - RTA_LENGTH(0))

/*****************************************************************
******************************************************************/
rtnetlink_socket = socket(AF_NETLINK, int socket_type, NETLINK_ROUTE);

int RTA_OK(struct rtattr *rta, int rtabuflen);

void *RTA_DATA(struct rtattr *rta);

unsigned int RTA_PAYLOAD(struct rtattr *rta);

struct rtattr *RTA_NEXT(struct rtattr *rta, unsigned int rtabuflen);

unsigned int RTA_LENGTH(unsigned int length);

unsigned int RTA_SPACE(unsigned int length);

Rtnetlink 由下面这些消息类型构成(新加在标准的netlink消息上)
(1)#RTM_NEWLINK, RTM_DELLINK, RTM_GETLINK
创建或者删除一个特定的网络接口,或者从一个特定的网络接口上获得信息。
这些消息含有一个ifinfomsg类型的结构,紧跟在后面的是一系列的rtattr结构。

/*****************************************************************
 *      Link layer specific messages.
 ****/

/* struct ifinfomsg
 * passes link level specific information, not dependent
 * on network protocol.
 */

 struct ifinfomsg {
    unsigned char  ifi_family; /* AF_UNSPEC */
    unsigned short ifi_type;   /* Device type */
    int            ifi_index;  /* Interface index */
    unsigned int   ifi_flags;  /* Device flags  */
    unsigned int   ifi_change; /* change mask */
};
/*
* ifi_family: 接口地址类型
* ifi_type: 设备类型
* ifi_index: 是结构唯一的索引
* ifi_flags:  设备标志,可以看netdevice 结构
* ifi_change: 保留值,通常设置为0xFFFFFFFF
*/

/*
ifi_type代表硬件设备的类型:
    ARPHRD_ETHER                   10M以太网
    ARPHRD_PPP                     PPP拨号
    ARPHRDLOOPBACK                 环路设备

ifi_flags包含设备的一些标志:
    IFF_UP                            接口正在运行
    IFF_BROADCAST                     有效的广播地址集
    IFF_DEBUG                         内部调试标志
    IFF_LOOPBACK                      这是自环接口
    IFF_POINTOPOINT                   这是点到点的链路设备
    IFF_RUNNING                       资源已分配
    IFF_NOARP                         无arp协议,没有设置第二层目的地址
    IFF_PROMISC                       接口为杂凑(promiscuous)模式
    IFF_NOTRAILERS                    避免使用trailer
    IFF_ALLMULTI                      接收所有组播(multicast)报文
    IFF_MASTER                        主负载平衡群(bundle)
    IFF_SLAVE                         从负载平衡群(bundle)
    IFF_MULTICAST                     支持组播(multicast)
    IFF_PORTSEL                       可以通过ifmap选择介质(media)类型
    IFF_AUTOMEDIA                     自动选择介质
    IFF_DYNAMIC                       接口关闭时丢弃地址

            Routing attributes(rtattr部分属性,rta_type)

rta_type         value type         description
 ──────────────────────────────────────────────────────────
IFLA_UNSPEC      -                  未说明,未指定的数据
IFLA_ADDRESS     hardware address   L2硬件地址
IFLA_BROADCAST   hardware address   L2广播地址.
IFLA_IFNAME      asciiz string      char型设备名.
IFLA_MTU         unsigned int       MTU of the device.
IFLA_LINK        int                Link type.
IFLA_QDISC       asciiz string      Queueing discipline.
IFLA_STATS       see below          struct rtnl_link_stats的设备信息

//用来获取ifinfomsg后面的rtattr结构
#define IFLA_RTA(r)  ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg))))
*/

(2)# RTM_NEWADDR, RTM_DELADDR, RTM_GETADDR

添加,删除或者接收一个和接口相关的IP地址的信息。
在linux2.2中,一个网口是可以有多个IP地址信息的。这些消息含有一个ifaddrmsg类型的结构,紧跟在后面的是一系列的rtattr结构。

struct ifaddrmsg {
    unsigned char ifa_family;    /* Address type */
    unsigned char ifa_prefixlen; /* Prefixlength of address */
    unsigned char ifa_flags;     /* Address flags */
    unsigned char ifa_scope;     /* Address scope */
    int           ifa_index;     /* Interface index */
  };
/*
* ifa_family: 地址类型(通常为AF_INET or AF_INET6))
* ifa_prefixlen: 地址的地址掩码长度,如果改地址定义在这个family
* ifa_flags:
* ifa_scope: 地址的作用域
* ifa_index:  接口索引与接口地址关联
*/

 /*
            Attributes (rtattr部分属性,rta_type)
rta_type        value type             description
─────────────────────────────────────────────────────────────
IFA_UNSPEC      -                      unspecified.
IFA_ADDRESS     raw protocol address   接口地址 interface address
IFA_LOCAL       raw protocol address   本地地址 local address
IFA_LABEL       asciiz string          接口名称 name of the interface
IFA_BROADCAST   raw protocol address   广播 broadcast address.
IFA_ANYCAST     raw protocol address   anycast address
IFA_CACHEINFO   struct ifa_cacheinfo   Address information.

*/

(3)#RTM_NEWROUTE, RTM_DELROUTE, RTM_GETROUTE

创建,删除或者获取网络设备的路由信息;这些消息包含一个rtmsg结构,其后跟数目可选的rtattr结构。
对于RTM_GETROUTE,设置rtm_dst_len以及rtm_src_len为0表示获取指定路由表的所有条目(entries)。
其它的成员,除了rtm_table、rtm_protocol,0是通配符

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;
};

rtm_type          Route type
───────────────────────────────────────────────────────────
RTN_UNSPEC        unknown route                /*位置路由*/
RTN_UNICAST       a gateway or direct route  /* 网关或直连路由 */
RTN_LOCAL         a local interface route    /* 本地接口路由 */
RTN_BROADCAST     a local broadcast route (sent as a broadcast)  /* 本地广播式接收,发送 */
RTN_ANYCAST       a local broadcast route (sent as a unicast)    /* 本地单播路由 */
RTN_MULTICAST     a multicast route            /* 多播路由 */
RTN_BLACKHOLE     a packet dropping route    /* 丢弃 */
RTN_UNREACHABLE   an unreachable destination /* 目标不可达 */
RTN_PROHIBIT      a packet rejection route    /* 拒绝 */
RTN_THROW         continue routing lookup in another table /* 不在本表 */
RTN_NAT           a network address translation rule        /* nat */
RTN_XRESOLVE      refer to an external resolver (not implemented)

rtm_protocol      Route origin.
───────────────────────────────────────
RTPROT_UNSPEC     unknown
RTPROT_REDIRECT   by an ICMP redirect (currently unused)   /* 通过icmp转发建立路由 (目前没用)*/
RTPROT_KERNEL     by the kernel     /* 通过内核建立路由 */
RTPROT_BOOT       during boot       /* 启动时建立路由 */
RTPROT_STATIC     by the administrator  /* 管理员建立 */

rtm_scope is the distance to the destination:

RT_SCOPE_UNIVERSE   global route
RT_SCOPE_SITE       interior route in the local autonomous system
RT_SCOPE_LINK       route on this link
RT_SCOPE_HOST       route on the local host
RT_SCOPE_NOWHERE    destination doesn't exist

/* 用户可用范围 */
RT_SCOPE_UNIVERSE ~ RT_SCOPE_SITE are available to the user.

The rtm_flags have the following meanings:

RTM_F_NOTIFY     if the route changes, notify the user via rtnetlink
RTM_F_CLONED     route is cloned from another route
RTM_F_EQUALIZE   a multipath equalizer (not yet implemented)

rtm_table specifies the routing table

RT_TABLE_UNSPEC    an unspecified routing table /* 0 未指定的表 */
RT_TABLE_DEFAULT   the default table            /* 253 默认表 */
RT_TABLE_MAIN      the main table                /* 254 main 表 */
RT_TABLE_LOCAL     the local table                /* 255 local 表 */

//用户可以使用 RT_TABLE_UNSPEC 到 RT_TABLE_DEFAULT 之间的任意值

            Attributes

rta_type        value type         description
──────────────────────────────────────────────────────────────
RTA_UNSPEC      -                  ignored.
RTA_DST         protocol address   Route destination address.   /* 目的 */
RTA_SRC         protocol address   Route source address.        /* 源地址 */
RTA_IIF         int                Input interface index.        /* 输入设备 index */
RTA_OIF         int                Output interface index.
RTA_GATEWAY     protocol address   The gateway of the route        /* 网关 */
RTA_PRIORITY    int                Priority of route.            /* 优先级 */
RTA_PREFSRC
RTA_METRICS     int                Route metric                    /* 路由metric 值*/
RTA_MULTIPATH
RTA_PROTOINFO
RTA_FLOW
RTA_CACHEINFO

下面是一个具体实例:

监听网络、ip地址、路由的变化

/*********************************************************
* Filename: nl_netinfo.c
* Author: zhangwj
* Date:
* Descripte:
* Email:
* Warnning:
**********************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <linux/route.h>
#include <errno.h>

#define EPOLL_LISTEN_MAX_CNT    256
#define EPOLL_LISTEN_TIMEOUT    500

int g_nlfd = -1;
int g_epollfd = -1;

void parse_rtattr(struct rtattr **tb, int max, struct rtattr *attr, int len)
{
    for (; RTA_OK(attr, len); attr = RTA_NEXT(attr, len)) {
        if (attr->rta_type <= max) {
            tb[attr->rta_type] = attr;
        }
    }
}

void nl_netroute_handle(struct nlmsghdr *nlh)
{
    int len;
    struct rtattr *tb[RTA_MAX + 1];
    struct rtmsg *rt;
    char tmp[256];

    bzero(tb, sizeof(tb));
    rt = NLMSG_DATA(nlh);
    len = nlh->nlmsg_len - NLMSG_SPACE(sizeof(*rt));
    parse_rtattr(tb, RTA_MAX, RTM_RTA(rt), len);
    printf("%s: ", (nlh->nlmsg_type==RTM_NEWROUTE)?"NEWROUT":"DELROUT");
    if (tb[RTA_DST] != NULL) {
        inet_ntop(rt->rtm_family, RTA_DATA(tb[RTA_DST]), tmp, sizeof(tmp));
        printf("DST: %s ", tmp);
    }
    if (tb[RTA_SRC] != NULL) {
        inet_ntop(rt->rtm_family, RTA_DATA(tb[RTA_SRC]), tmp, sizeof(tmp));
        printf("SRC: %s ", tmp);
    }
    if (tb[RTA_GATEWAY] != NULL) {
        inet_ntop(rt->rtm_family, RTA_DATA(tb[RTA_GATEWAY]), tmp, sizeof(tmp));
        printf("GATEWAY: %s ", tmp);
    }
    printf("\n");
}

void nl_netifinfo_handle(struct nlmsghdr *nlh)
{
    int len;
    struct rtattr *tb[IFLA_MAX + 1];
    struct ifinfomsg *ifinfo;

    bzero(tb, sizeof(tb));
    ifinfo = NLMSG_DATA(nlh);
    len = nlh->nlmsg_len - NLMSG_SPACE(sizeof(*ifinfo));
    parse_rtattr(tb, IFLA_MAX, IFLA_RTA (ifinfo), len);

    printf("%s: %s ", (nlh->nlmsg_type==RTM_NEWLINK) ? "NEWLINK" : "DELLINK", (ifinfo->ifi_flags & IFF_UP) ? "up" : "down");
    if(tb[IFLA_IFNAME]) {
        printf("%s", RTA_DATA(tb[IFLA_IFNAME]));
    }
    printf("\n");
}

void nl_netifaddr_handle(struct nlmsghdr *nlh)
{
    int len;
    struct rtattr *tb[IFA_MAX + 1];
    struct ifaddrmsg *ifaddr;
    char tmp[256];

    bzero(tb, sizeof(tb));
    ifaddr = NLMSG_DATA(nlh);
    len =nlh->nlmsg_len - NLMSG_SPACE(sizeof(*ifaddr));
    parse_rtattr(tb, IFA_MAX, IFA_RTA (ifaddr), len);

    printf("%s ", (nlh->nlmsg_type == RTM_NEWADDR)? "NEWADDR":"DELADDR");
    if (tb[IFA_LABEL] != NULL) {
        printf("%s ", RTA_DATA(tb[IFA_LABEL]));
    }
    if (tb[IFA_ADDRESS] != NULL) {
        inet_ntop(ifaddr->ifa_family, RTA_DATA(tb[IFA_ADDRESS]), tmp, sizeof(tmp));
        printf("%s ", tmp);
    }
    printf("\n");
}

void nl_netlink_handle(int fd)
{
    int r_size;
    socklen_t len = 0;
    char buff[2048] = {0};
    struct sockaddr_nl addr;
    struct nlmsghdr *nlh;

    while(1)
    {
        len = sizeof(addr);
        r_size = recvfrom(fd, (void *)buff, sizeof(buff), 0, (struct sockaddr *)&addr, &len);
        nlh = (struct nlmsghdr *)buff;
        for(; NLMSG_OK(nlh, r_size); nlh = NLMSG_NEXT(nlh, r_size))
        {
            switch(nlh->nlmsg_type) {
            case NLMSG_DONE:
            case NLMSG_ERROR:
                break;
            case RTM_NEWLINK:     /* */
            case RTM_DELLINK:
                nl_netifinfo_handle(nlh);
                break;
            case RTM_NEWADDR:
            case RTM_DELADDR:    /* */
                nl_netifaddr_handle(nlh);
                break;
            case RTM_NEWROUTE:
            case RTM_DELROUTE:   /* */
                nl_netroute_handle(nlh);
                break;
            default:
                break;
            }
        }
    }
}

void epoll_event_handle(void)
{
    int i = 0;
    int fd_cnt = 0;
    int sfd;
    struct epoll_event events[EPOLL_LISTEN_MAX_CNT];

    memset(events, 0, sizeof(events));
    while(1)
    {
        /* wait epoll event */
        fd_cnt = epoll_wait(g_epollfd, events, EPOLL_LISTEN_MAX_CNT, EPOLL_LISTEN_TIMEOUT);
        for(i = 0; i < fd_cnt; i++)
        {
            sfd = events[i].data.fd;
            if(events[i].events & EPOLLIN)
            {
                if (sfd == g_nlfd)
                {
                    nl_netlink_handle(sfd);
                }
            }
        }
    }
}

int epoll_add_fd(int fd)
{
    struct epoll_event ev;

    ev.data.fd = fd;
    ev.events = EPOLLIN | EPOLLET;

    if (epoll_ctl(g_epollfd, EPOLL_CTL_ADD, fd, &ev) < 0) {
        perror("epoll add fd error");
        return -1;
    }

    printf("epoll add fd[%d] success\n", fd);
    return 0;
}

int init_epoll_fd()
{
    int epollfd = -1;

    epollfd = epoll_create(EPOLL_LISTEN_MAX_CNT);
    if (epollfd < 0) {
        perror("epoll create failure!...");
        return -1;
    }
    g_epollfd = epollfd;

    printf("epool create fd [%d] success\n", epollfd);
    return g_epollfd;
}

int init_nl_sockfd()
{
    int ret = 0;
    int nlfd = -1;
    struct sockaddr_nl sa;

    /* open a netlink fd */
    nlfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
    if (nlfd < 0) {
        perror("create netlink socket failure");
        return -1;
    }

    memset(&sa, 0, sizeof(sa));
    sa.nl_family = AF_NETLINK;
    sa.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_IFADDR | RTMGRP_IPV6_ROUTE;

    /* bind netlink */
    ret = bind(nlfd, (struct sockaddr *)&sa, sizeof(sa));
    if (ret < 0) {
        perror("bind nlfd error");
        close(nlfd);
        return -1;
    }

    if (epoll_add_fd(nlfd)) {
        close(nlfd);
        return -1;
    }
    g_nlfd = nlfd;

    printf("netlink create fd [%d] success\n", nlfd);
    return nlfd;
}


int main(int argc, char **argv)
{
    if (init_epoll_fd() < 0) { /* 创建epoll 监听fd */
        return -1;
    }

    if (init_nl_sockfd() < 0) { /* 创建netlink */
        return -1;
    }

    /* 循环接收处理 */
    epoll_event_handle();

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值