一、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. Linux下简易线程池

    线程池简介 线程池是可以用来在后台执行多个任务的线程集合. 这使主线程可以自由地异步执行其他任务.线程池通常用于服务器应用程序. 每个传入请求都将分配给线程池中的一个线程,因此可以异步处理请求,而不会 ...

  2. EBS initialization parameters - Healthcheck

    APPLIES TO: Oracle EBS Applications Performance - Version 11.5.10.2 to 12.2 [Release 11.5.10 to 12.2 ...

  3. PS 滤镜算法原理——照亮边缘

    这个算法原理很简单,对彩色图像的R,G,B 三个通道,分别求梯度,然后将梯度值作为三个通道的值. clc; clear all;Image=imread('4.jpg');Image=double(I ...

  4. hover变化图片

    <div class="icon width mar"> <div class="cpzs_tit"></div> < ...

  5. cocapods 使用及问题

    一.CocoaPods的安装 (1)使用淘宝的Ruby镜像替换官方的ruby源,在终端输入命令 $ gem sources --remove https://rubygems.org/ $ gem s ...

  6. 使用javascript中读取Xml文件做成的一个二级联动菜单

    [html] view plaincopy <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> ...

  7. Web服务器Tomcat集群与负载均衡技术

    我们曾经介绍过三种Tomcat集群方式的优缺点分析.本文将介绍Tomcat集群与负载均衡技术具体实施过程. 在进入集群系统架构探讨之前,先定义一些专门术语: 1. 集群(Cluster):是一组独立的 ...

  8. codechef Killing Monsters

    题目大意:大厨正在玩一个打怪兽的小游戏.游戏中初始时有 n 只怪兽排成一排,从左到右编号为 0 ∼ n − 1.第 i 只怪兽的初始血量为 hi,当怪兽的血量小于等于 0 时,这只怪兽就挂了. 大厨要 ...

  9. Python小游戏之 - 飞机大战美女 !

    用Python写的"飞机大战美女"小游戏 源代码如下: # coding=utf-8 import os import random import pygame # 用一个常量来存 ...

  10. Django1.10 release notes摘编

    一.重点新特性: 1.面向PostgreSQL的全文搜索支持 2.新式风格的middleware 3.用户名Unicode编码的官方支持 二.一些可以提的改变: 1.用户名最大长度增加到150 2.不 ...