一、Rtnetlink

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

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

  注:netlink用法在上一篇博文中有提到 http://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

一面是一个具体实例:

 /*********************************************************
* 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 = -;
int g_epollfd = -; 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 + ];
struct rtmsg *rt;
char tmp[]; 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 + ];
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 + ];
struct ifaddrmsg *ifaddr;
char tmp[]; 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 = ;
char buff[] = {};
struct sockaddr_nl addr;
struct nlmsghdr *nlh; while()
{
len = sizeof(addr);
r_size = recvfrom(fd, (void *)buff, sizeof(buff), , (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 = ;
int fd_cnt = ;
int sfd;
struct epoll_event events[EPOLL_LISTEN_MAX_CNT]; memset(events, , sizeof(events));
while()
{
/* wait epoll event */
fd_cnt = epoll_wait(g_epollfd, events, EPOLL_LISTEN_MAX_CNT, EPOLL_LISTEN_TIMEOUT);
for(i = ; 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) < ) {
perror("epoll add fd error");
return -;
} printf("epoll add fd[%d] success\n", fd);
return ;
} int init_epoll_fd()
{
int epollfd = -; epollfd = epoll_create(EPOLL_LISTEN_MAX_CNT);
if (epollfd < ) {
perror("epoll create failure!...");
return -;
}
g_epollfd = epollfd; printf("epool create fd [%d] success\n", epollfd);
return g_epollfd;
} int init_nl_sockfd()
{
int ret = ;
int nlfd = -;
struct sockaddr_nl sa; /* open a netlink fd */
nlfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (nlfd < ) {
perror("create netlink socket failure");
return -;
} memset(&sa, , 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 < ) {
perror("bind nlfd error");
close(nlfd);
return -;
} if (epoll_add_fd(nlfd)) {
close(nlfd);
return -;
}
g_nlfd = nlfd; printf("netlink create fd [%d] success\n", nlfd);
return nlfd;
} int main(int argc, char **argv)
{
if (init_epoll_fd() < ) { /* 创建epoll 监听fd */
return -;
} if (init_nl_sockfd() < ) { /* 创建netlink */
return -;
} /* 循环接收处理 */
epoll_event_handle(); return ;
}

参考资料:

http://www.man7.org/linux/man-pages/man7/rtnetlink.7.html

http://www.man7.org/linux/man-pages/man3/rtnetlink.3.html

linux 获取网络状态信息(Rtnetlink)的更多相关文章

  1. Android获取网络状态

    Android获取网络状态 学习自 https://developer.android.google.cn/reference/android/net/ConnectivityManager http ...

  2. [android] android 获取网络连接信息

    效果图:  工具类 /** * 获取网络连接信息 * * 根据NetworkInfo可以知道有很多的连接方式和信息 * * ① 当没有任何可用网络的时候,networkinfo为null 判断netw ...

  3. React Native之Fetch简单封装、获取网络状态

    1.Fetch的使用 fetch的使用非常简单,只需传入请求的url fetch('https://facebook.github.io/react-native/movies.json'); 当然是 ...

  4. [React Native]获取网络状态

    使用React Native,可以使用NetInfo API获取手机当前的各个网络状态. componentWillMount() { NetInfo.fetch().done((status)=&g ...

  5. iOS 获取网络状态

    在iOS开发者,获取网络状态比较常用 -(NSString *)getNetWorkStates{ UIApplication *app = [UIApplication sharedApplicat ...

  6. android开发获取网络状态,wifi,wap,2g,3g.工具类(一)

    android开发获取网络状态整理: package com.gzcivil.utils; import android.content.Context; import android.net.Con ...

  7. 微信小程序 --- 获取网络状态

    获取网络状态:wx.getNetworkType btnclick:function(){ wx.getNetworkType({ success:function(res){ console.log ...

  8. C#获取网络状态

    /// <summary> /// 获取网络状态 /// </summary> /// <param name="ip">目标IP地址</ ...

  9. Linux网络编程之"获取网络天气信息"

    需求分析: 1.需要Linux c 网络编程基础, 2.需要了解 http 协议 3.需要天气信息相关api(可以从阿里云上购买,很便宜的!) 4.需要cJSON解析库(因为获取到的天气信息一般是用c ...

随机推荐

  1. SpriteBuilder中CCB精灵对象的Sprite frame为什么有时候不能修改

    有时候你会发现CCB中的精灵对象(root节点)的Sprite frame是灰色的,不能修改.因为它是根对象,所以不存在被嵌入其他CCB的情况,那到底是什么原因呢? 可以发现此时的Timeline当前 ...

  2. DB 查询分析器 6.03 如何灵活、快捷地操作国产达梦数据库

    DB 查询分析器 6.03 如何灵活.快捷地操作国产达梦数据库 马根峰 (广东联合电子服务股份有限公司, 广州 510300) 摘要       本文详细地介绍了"万能数据库查询分析器&qu ...

  3. 学习C++模板,初体验

    最近,看了很多码神级人物的代码,发现其代码很炫酷,尤其对模板的使用,作为小码农,感觉已经落伍了,所以应该发奋图强,好好学习和掌握模板这个东西. 模板是什么呢?有人说一个模板就是一个创建类或函数的蓝图或 ...

  4. web报表工具FineReport常用函数的用法总结(文本函数)

    文本函数 CHAR CHAR(number):根据指定数字返回对应的字符.CHAR函数可将计算机其他类型的数字代码转换为字符. Number:用于指定字符的数字,介于1Number:用于指定字符的数字 ...

  5. web报表工具FineReport的公式编辑框的语法简介

    FINEREPORT用到公式的地方非常多,单元格(以=开头的便被解析为公式),条件显示,数据字典,报表填报属性值定义,图表标题,轴定义,页眉页脚,甚至单元格的其他属性中的鼠标悬浮提示内容都可以写公式, ...

  6. CentOS6.4下安装Nginx1.12.2

    1.安装GCC安装 nginx 需要先将官网下载的源码进行编译,编译依赖 gcc 环境,如果没有 gcc 环境,则需要安装 yum install gcc-c++ 2.安装PCRE-devel PCR ...

  7. springMVC导出word模板

    controller 调用 @RequestMapping(value = "/exportWord") public void exportWord(HttpServletRes ...

  8. linux 下使用 tc 模拟网络延迟和丢包

    1 模拟延迟传输简介 netem 与 tc: netem 是 Linux 2.6 及以上内核版本提供的一个网络模拟功能模块.该功能模块可以用来在性能良好的局域网中,模拟出复杂的互联网传输性能,诸如低带 ...

  9. Linux下转换文件从GBK到UTF-8

    iconv -f gbk -t utf- source-file -o target-file

  10. 超精简易用cocoaPods的安装和使用

    cocoaPods 安装和使用 第一步:替换ruby源 $ gem sources -l                                查看当前ruby的源 $ gem sources ...