Netlink与RTNetlink的简单使用

Netlink与RTNetlink的简单使用

近期项目要求通过程序去配置下发网口路由,所以去了解了一下netlink机制。

一、netlink通信机制

netlink套接字是用以实现用户进程与内核进程通信的一种特殊的进程间通信(IPC) ,也是网络应用程序与内核通信的最常用的接口。

目前,用户应用程序和内核通信的常用方式如下:

  • 系统调用:常见的有 write、read、ioctl 等等,它需要应用程序主动向内核写入或读取数据,是一种同步的单工数据传输方式
  • /proc文件系统:同 系统调用 类似
  • 异步IO:可以通过编写驱动代码,使得内核在某些时刻主动向应用程序发送信号,但无法传输大量数据。
  • netlink:是一种同步或者异步的数据传输方式,使用 netlink 在应用程序和内核建立起连接后即可进行双向数据发送。
1、netlink的优点
  • 支持全双工、异步通信
  • 用户空间使用标准的socket接口即可进行通信
  • 支持多播
  • 在内核端可用于进程上下文与中断上下文
2、netlink的应用

目前 linux内核 已经使用 netlink 实现了多种功能应用,关于功能使用后面会有部分讲解。下面罗列出几个常用的功能,如下:

  • 获取或修改路由信息
  • 监听TCP协议数据报文
  • 防火墙
  • netfilter子系统
  • 内核事件向用户态通知

按照笔者的理解,netlink 很像网络编程,因为它有一定的格式要求,是通过发送 消息 来完成数据传输的。那么它就需要 消息格式协议流程。说得简单一些,就是 netlink 是一种通信方式,是内核提供的一种功能。另一方面则是因为 netlink 用就是 socket套接字 那一套编程接口,所以十分像是网络协议。

二、netlink接口与数据接口

用户态应用使用标准的 socket API有sendto(),recvfrom(); sendmsg(), recvmsg()

netlink 可以直接使用 socket套接字 的标准接口直接进行代码编写,也就是说 socketbind 等接口都可以直接使用。
而按照笔者理解,netlink 的编程与 UDP 编程类似,但在网络编程中我们是使用 IP地址 + 端口号 进行寻址的,而 netlink 则是通过 协议类型+进程ID 进行寻址的,其中 协议类型 会在调用 socket接口 的时候指定。
netlink 基本的编程步骤如下:

  1. 使用 socket 声明套接字
  2. 使用 bind 绑定 本地地址 到套接字
  3. 构造 消息
  4. 发送或接收 消息,而在 netlink 中有两套发送及接收数据的接口,分别是 sendto和recvfromsendmsg和recvmsg
1、netlink用户态常用的数据结构

struct sockaddr_nl 是netlink通信地址,跟普通socket struct sockaddr_in类似。

(1)、struct sockaddr_nl结构
struct sockaddr_nl {__kernel_sa_family_t    nl_family;  //一般为AF_NETLINKunsigned short          nl_pad;     //无需填充__u32                   nl_pid;     //与内核通信的进程的进程ID,0 则代表地址为内核__u32                   nl_groups;  //多播组号,netlink支持多播
};
(2)、struct nlmsghd 结构
/* struct nlmsghd 是netlink消息头*/
struct nlmsghdr {__u32       nlmsg_len;  /* Length of message including header */__u16       nlmsg_type; /* Message content */__u16       nlmsg_flags;    /* Additional flags */__u32       nlmsg_seq;  /* Sequence number */__u32       nlmsg_pid;  /* Sending process port ID */
};

1)nlmsg_len:整个netlink消息的长度(包含消息头);
2)nlmsg_type:消息状态,内核在include/uapi/linux/netlink.h中定义了以下4种通用的消息类型,它们分别是:

#define NLMSG_NOOP      0x1 /* Nothing.     */
#define NLMSG_ERROR     0x2 /* Error        */
#define NLMSG_DONE      0x3 /* End of a dump    */
#define NLMSG_OVERRUN       0x4 /* Data lost        */#define NLMSG_MIN_TYPE      0x10    /* < 0x10: reserved control messages *//*NLMSG_NOOP:不执行任何动作,必须将该消息丢弃;
NLMSG_ERROR:消息发生错误;
NLMSG_DONE:标识分组消息的末尾;
NLMSG_OVERRUN:缓冲区溢出,表示某些消息已经丢失。
NLMSG_MIN_TYPEK:预留 */

3)nlmsg_flags:消息标记,它们用以表示消息的类型,如下:

/* Flags values */#define NLM_F_REQUEST		0x01	/* It is request message. 	*/
#define NLM_F_MULTI		0x02	/* Multipart message, terminated by NLMSG_DONE */
#define NLM_F_ACK		0x04	/* Reply with ack, with zero or error code */
#define NLM_F_ECHO		0x08	/* Echo this request 		*/
#define NLM_F_DUMP_INTR		0x10	/* Dump was inconsistent due to sequence change */
#define NLM_F_DUMP_FILTERED	0x20	/* Dump was filtered as requested *//* Modifiers to GET request */
#define NLM_F_ROOT	0x100	/* specify tree	root	*/
#define NLM_F_MATCH	0x200	/* return all matching	*/
#define NLM_F_ATOMIC	0x400	/* atomic GET		*/
#define NLM_F_DUMP	(NLM_F_ROOT|NLM_F_MATCH)/* Modifiers to NEW request */
#define NLM_F_REPLACE	0x100	/* Override existing		*/
#define NLM_F_EXCL	0x200	/* Do not touch, if it exists	*/
#define NLM_F_CREATE	0x400	/* Create, if it does not exist	*/
#define NLM_F_APPEND	0x800	/* Add to end of list		*//* Modifiers to DELETE request */
#define NLM_F_NONREC	0x100	/* Do not delete recursively	*//* Flags for ACK message */
#define NLM_F_CAPPED	0x100	/* request was capped */
#define NLM_F_ACK_TLVS	0x200	/* extended ACK TVLs were included */

4)nlmsg_seq:消息序列号,用以将消息排队,有些类似TCP协议中的序号(不完全一样),但是netlink的这个字段是可选的,不强制使用;
5)nlmsg_pid:发送端口的ID号,对于内核来说该值就是0,对于用户进程来说就是其socket所绑定的ID号。

(3)、struct msghdr 结构体
struct iovec {                    /* Scatter/gather array items */void  *iov_base;              /* Starting address */size_t iov_len;               /* Number of bytes to transfer */};/* iov_base: iov_base指向数据包缓冲区,即参数buff,iov_len是buff的长度。msghdr中允许一次传递多个buff,以数组的形式组织在 msg_iov中,msg_iovlen就记录数组的长度 (即有多少个buff)*/struct msghdr {void         *msg_name;       /* optional address */socklen_t     msg_namelen;    /* size of address */struct iovec *msg_iov;        /* scatter/gather array */size_t        msg_iovlen;     /* # elements in msg_iov */void         *msg_control;    /* ancillary data, see below */size_t        msg_controllen; /* ancillary data buffer len */int           msg_flags;      /* flags on received message */};/* msg_name: 数据的目的地址,网络包指向sockaddr_in, netlink则指向sockaddr_nl;msg_namelen: msg_name 所代表的地址长度msg_iov: 指向的是缓冲区数组msg_iovlen: 缓冲区数组长度msg_control: 辅助数据,控制信息(发送任何的控制信息)msg_controllen: 辅助信息长度msg_flags: 消息标识*/

参考:linux netlink通信机制 - zhangwju - 博客园 (cnblogs.com)

三、RTnetlink接口与数据接口

netlink 是 Linux 内核中用于内核与用户空间之间进行通信的一种机制。它允许用户空间程序与内核模块进行通信,用于配置网络参数、查询状态信息、接收通知等。Netlink 使用一种类似于套接字的 API 进行通信,提供了一种可靠的、高效的机制来传输大量的异步数据,比如路由表、网络接口信息、套接字选项等。Netlink 被广泛应用于 Linux 系统中的网络配置、路由管理、网络监控等方面。

RTNetlink 则是 Netlink 的一个子协议,用于处理与路由相关的信息。RTNetlink 可以通过 Netlink 机制来获取、配置和管理路由表、路由策略、邻居信息等路由相关的数据。它是 Linux 系统中管理网络路由的标准接口,通过 RTNetlink,用户空间程序可以实现动态地添加、删除、修改路由表中的路由条目,以及监听路由表的变化。

总的来说,Netlink 是 Linux 内核与用户空间进行通信的机制,而 RTNetlink 是 Netlink 的一个子协议,用于处理与路由相关的信息。通过 RTNetlink,用户空间程序可以与内核进行高效地路由管理。

1、RTNetlink用户态常用数据结构
(1)、struct sockaddr_nl结构

​ 与Netlink相同。

(2)、struct nlmsghd 结构

​ 其与Netlink主要的区别在于nlmsg_type有很多其它可选消息类型,在rtnetlink.h中包含了很多消息类型:

/* Types of messages */enum {RTM_BASE	= 16,
#define RTM_BASE	RTM_BASERTM_NEWLINK	= 16,
#define RTM_NEWLINK	RTM_NEWLINKRTM_DELLINK,
#define RTM_DELLINK	RTM_DELLINKRTM_GETLINK,
#define RTM_GETLINK	RTM_GETLINKRTM_SETLINK,
#define RTM_SETLINK	RTM_SETLINKRTM_NEWADDR	= 20,
#define RTM_NEWADDR	RTM_NEWADDRRTM_DELADDR,
#define RTM_DELADDR	RTM_DELADDRRTM_GETADDR,
#define RTM_GETADDR	RTM_GETADDRRTM_NEWROUTE	= 24,
#define RTM_NEWROUTE	RTM_NEWROUTERTM_DELROUTE,
#define RTM_DELROUTE	RTM_DELROUTERTM_GETROUTE,
#define RTM_GETROUTE	RTM_GETROUTERTM_NEWNEIGH	= 28,
#define RTM_NEWNEIGH	RTM_NEWNEIGHRTM_DELNEIGH,
#define RTM_DELNEIGH	RTM_DELNEIGHRTM_GETNEIGH,
#define RTM_GETNEIGH	RTM_GETNEIGHRTM_NEWRULE	= 32,
#define RTM_NEWRULE	RTM_NEWRULERTM_DELRULE,
#define RTM_DELRULE	RTM_DELRULERTM_GETRULE,
#define RTM_GETRULE	RTM_GETRULERTM_NEWQDISC	= 36,
#define RTM_NEWQDISC	RTM_NEWQDISCRTM_DELQDISC,
#define RTM_DELQDISC	RTM_DELQDISCRTM_GETQDISC,
#define RTM_GETQDISC	RTM_GETQDISCRTM_NEWTCLASS	= 40,
#define RTM_NEWTCLASS	RTM_NEWTCLASSRTM_DELTCLASS,
#define RTM_DELTCLASS	RTM_DELTCLASSRTM_GETTCLASS,
#define RTM_GETTCLASS	RTM_GETTCLASSRTM_NEWTFILTER	= 44,
#define RTM_NEWTFILTER	RTM_NEWTFILTERRTM_DELTFILTER,
#define RTM_DELTFILTER	RTM_DELTFILTERRTM_GETTFILTER,
#define RTM_GETTFILTER	RTM_GETTFILTERRTM_NEWACTION	= 48,
#define RTM_NEWACTION   RTM_NEWACTIONRTM_DELACTION,
#define RTM_DELACTION   RTM_DELACTIONRTM_GETACTION,
#define RTM_GETACTION   RTM_GETACTIONRTM_NEWPREFIX	= 52,
#define RTM_NEWPREFIX	RTM_NEWPREFIXRTM_GETMULTICAST = 58,
#define RTM_GETMULTICAST RTM_GETMULTICASTRTM_GETANYCAST	= 62,
#define RTM_GETANYCAST	RTM_GETANYCASTRTM_NEWNEIGHTBL	= 64,
#define RTM_NEWNEIGHTBL	RTM_NEWNEIGHTBLRTM_GETNEIGHTBL	= 66,
#define RTM_GETNEIGHTBL	RTM_GETNEIGHTBLRTM_SETNEIGHTBL,
#define RTM_SETNEIGHTBL	RTM_SETNEIGHTBLRTM_NEWNDUSEROPT = 68,
#define RTM_NEWNDUSEROPT RTM_NEWNDUSEROPTRTM_NEWADDRLABEL = 72,
#define RTM_NEWADDRLABEL RTM_NEWADDRLABELRTM_DELADDRLABEL,
#define RTM_DELADDRLABEL RTM_DELADDRLABELRTM_GETADDRLABEL,
#define RTM_GETADDRLABEL RTM_GETADDRLABELRTM_GETDCB = 78,
#define RTM_GETDCB RTM_GETDCBRTM_SETDCB,
#define RTM_SETDCB RTM_SETDCBRTM_NEWNETCONF = 80,
#define RTM_NEWNETCONF RTM_NEWNETCONFRTM_DELNETCONF,
#define RTM_DELNETCONF RTM_DELNETCONFRTM_GETNETCONF = 82,
#define RTM_GETNETCONF RTM_GETNETCONFRTM_NEWMDB = 84,
#define RTM_NEWMDB RTM_NEWMDBRTM_DELMDB = 85,
#define RTM_DELMDB RTM_DELMDBRTM_GETMDB = 86,
#define RTM_GETMDB RTM_GETMDBRTM_NEWNSID = 88,
#define RTM_NEWNSID RTM_NEWNSIDRTM_DELNSID = 89,
#define RTM_DELNSID RTM_DELNSIDRTM_GETNSID = 90,
#define RTM_GETNSID RTM_GETNSIDRTM_NEWSTATS = 92,
#define RTM_NEWSTATS RTM_NEWSTATSRTM_GETSTATS = 94,
#define RTM_GETSTATS RTM_GETSTATSRTM_NEWCACHEREPORT = 96,
#define RTM_NEWCACHEREPORT RTM_NEWCACHEREPORTRTM_NEWCHAIN = 100,
#define RTM_NEWCHAIN RTM_NEWCHAINRTM_DELCHAIN,
#define RTM_DELCHAIN RTM_DELCHAINRTM_GETCHAIN,
#define RTM_GETCHAIN RTM_GETCHAINRTM_NEWNEXTHOP = 104,
#define RTM_NEWNEXTHOP	RTM_NEWNEXTHOPRTM_DELNEXTHOP,
#define RTM_DELNEXTHOP	RTM_DELNEXTHOPRTM_GETNEXTHOP,
#define RTM_GETNEXTHOP	RTM_GETNEXTHOPRTM_NEWLINKPROP = 108,
#define RTM_NEWLINKPROP	RTM_NEWLINKPROPRTM_DELLINKPROP,
#define RTM_DELLINKPROP	RTM_DELLINKPROPRTM_GETLINKPROP,
#define RTM_GETLINKPROP	RTM_GETLINKPROPRTM_NEWVLAN = 112,
#define RTM_NEWNVLAN	RTM_NEWVLANRTM_DELVLAN,
#define RTM_DELVLAN	RTM_DELVLANRTM_GETVLAN,
#define RTM_GETVLAN	RTM_GETVLANRTM_NEWNEXTHOPBUCKET = 116,
#define RTM_NEWNEXTHOPBUCKET	RTM_NEWNEXTHOPBUCKETRTM_DELNEXTHOPBUCKET,
#define RTM_DELNEXTHOPBUCKET	RTM_DELNEXTHOPBUCKETRTM_GETNEXTHOPBUCKET,
#define RTM_GETNEXTHOPBUCKET	RTM_GETNEXTHOPBUCKET__RTM_MAX,
#define RTM_MAX		(((__RTM_MAX + 3) & ~3) - 1)
};
(3)、struct rtmsg结构体
struct rtmsg
{unsigned char rtm_family; // 路由项所属协议族,如AF_INET、AF_INET6;// 路由项目的地址掩码长度,特别注意是掩码长度,和属性中的RTA_DST配合表示路由项的目的网络unsigned char rtm_dst_len;unsigned char rtm_src_len; // 源地址掩码长度unsigned char rtm_tos; // 路由项的服务质量,对应IP首部的服务字段// 路由项所述路由表ID,由于只有1个字节,所以取值最大为255,因此用户态能够配置256个路由表,// 但是内核实际上并不是只能保存256个路由表,内核对此是没有限制的;unsigned char rtm_table; /* 路由表选取 */unsigned char rtm_protocol;	// 路由协议,含义见下方unsigned char rtm_scope; // 路由项的作用域,含义见下方	unsigned char rtm_type;	// 路由项类型,含义见下方unsigned rtm_flags;// 路由标记
};

1)rtm_table:路由表ID,内核有如下规划,从表ID的定义可以看出,当支持策略路由时(多路由表),用户态程序能够新建的表的ID范围为1~252。

RT_TABLE_UNSPEC	0	//未知,当操作该路由表时,内核默认会操作main表
RT_TABLE_DEFAULT	253	//default表,用于策略路由的默认路由表
RT_TABLE_MAIN	254	//main表,主路由表,不指定具体要操作的表时就是main表
RT_TABLE_LOCAL	255	//local表,该表维护了本机IP地址相关的路由,由内核自己维护,用户态无法操作

2)rtm_protocol:路由协议,表示的是路由项是如何产生的,可取的值如下,其中常见的是RTPROT_KERNEL和RTPROT_STAIC。

RTPROT_UNSPEC	//未知
RTPROT_REDIRECT	//该路由项是通过ICMP重定向消息添加的
RTPROT_KERNEL	//该路由项是由内核自己添加的,local表的路由大多数属于这一类
RTPROT_BOOTUP	//该路由项是在启动时通过boot协议添加的
RTPROT_STAIC	//该路由项是由管理员静态添加的,用户态配置的路由属于这一类

3)rtm_scope:路由项的作用域,**表示的是本机距离该路由项表示的目的地址的距离。**如下,值(0, 200)之间的数字是可以由管理员自己配置的。值越大,距离本机越近。所以叫它作用域其实是不利于理解的。

RT_SCOPE_UNIVERSE	0	//表示目的地和本机距离超过了1跳
RT_SCOPE_SITE	200	
RT_SCOPE_LINK	253	//表示目的地位于和本机直接相连的网络,如同一局域网内的其它主机地址
RT_SCOPE_HOST	254	//表示目的地是本地地址
RT_SCOPE_NOWHERE	255	//表示目的地不存在,这是个保留值

4)rtm_type:路由表项的类型,它一定程度上决定了数据包匹配该路由表项后应该采取的动作。

enum {RTN_UNSPEC,RTN_UNICAST,		/* Gateway or direct route	*/RTN_LOCAL,		/* Accept locally		*/RTN_BROADCAST,		/* Accept locally as broadcast,send as broadcast */RTN_ANYCAST,		/* Accept locally as broadcast,but send as unicast */RTN_MULTICAST,		/* Multicast route		*/RTN_BLACKHOLE,		/* Drop				*/RTN_UNREACHABLE,	/* Destination is unreachable   */RTN_PROHIBIT,		/* Administratively prohibited	*/RTN_THROW,		/* Not in this table		*/RTN_NAT,		/* Translate this address	*/RTN_XRESOLVE,		/* Use external resolver	*/__RTN_MAX
};
RTN_UNSPEC	//一个未初始化的值
RTN_LOCAL	//该路由项的目的地址是主机本地地址
RTN_UNICAST	//该路由是一条到直连或者非直连目的地址的单播路由,添加路由时如果不指定路由类型,那么默认取该值
RTN_MULTICAST	//该该路由项的目的地址是一个多播地址
RTN_BROADCAST	//该该路由项的目的地址是一个广播地址
RTN_ANYCAST	//IPv4中不使用
RTN_UNREACHABLE	//路由结果是不可达,会给源地址回复主机不可达ICMP报文
RTN_BLACKHOLE	//路由结果是不可达,会悄悄的丢弃数据包,不做任何回应
RTN_PROHIBIT	//路由结果为不可达,会给源地址回复"communication administratively prohibited"类型的ICMP报文
RTN_THROW	//路由结果为不可达,会给源地址回复网络不可达ICMP报文
(4)、struct rtattr结构体
struct rtattr {unsigned short	rta_len;unsigned short	rta_type;/* Data follows */
};

所包含的结构体还有很多,具体结构体的使用根据nlmsg_type消息类型进行选择的,但基本都是以rtattr结构体结尾,所以RTNetlink的基本消息构成为:

nlmsghdr + option_msg + rtattr

  • nlmsghdr 选择对应的消息类型;

  • option_msg就是根据nlmsghdr 中nlmsg_type消息类型来确定使用的消息头;

  • rtattr主要是具体的消息内容。

2、RTNetlink消息类型
(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                    避免使用trailerIFF_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地址的信息。这些消息含有一个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结构体参考上面内容。

对应的rta_type如下:

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
RTA_SESSION, /* no longer used */
RTA_MP_ALGO, /* no longer used */
RTA_TABLE,
RTA_MARK,
RTA_MFC_STATS,
RTA_VIA,
RTA_NEWDST,
RTA_PREF,
RTA_ENCAP_TYPE,
RTA_ENCAP,
RTA_EXPIRES,
RTA_PAD,
RTA_UID,
RTA_TTL_PROPAGATE,
RTA_IP_PROTO,
RTA_SPORT,
RTA_DPORT,
RTA_NH_ID,
3、RTNetlink的具体使用实例
/*********************************************************
* 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    500int 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;
}

四、RTNetlink添加路由的实例

vnet_netlink_add_ip4_route_vrf (void *dst, u8 dst_len, void *gw, u32 o_if, u32 vrf_id)
{vnet_netlink_msg_t m;struct rtmsg rtm = { 0 };u8 dflt[4] = { 0 };clib_error_t *err = 0;rtm.rtm_family = AF_INET;rtm.rtm_table = RT_TABLE_MAIN;rtm.rtm_type = RTN_UNICAST;rtm.rtm_dst_len = dst_len;//初始化nlmsghd,填充消息类型为RTM_NEWROUTEvnet_netlink_msg_init (&m, RTM_NEWROUTE,NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE,&rtm, sizeof (struct rtmsg));if (gw != NULL){//add包含网关信息的rtavnet_netlink_msg_add_rtattr (&m, RTA_GATEWAY, gw, 4);}if (o_if != ~0){//add包含出口设备的网口索引vnet_netlink_msg_add_rtattr (&m, RTA_OIF, &o_if, 4);}if (vrf_id != ~0){//add包含路由表的rtavnet_netlink_msg_add_rtattr (&m, RTA_TABLE, &vrf_id, 4);}//add包含路由地址的rtavnet_netlink_msg_add_rtattr (&m, RTA_DST, dst ? dst : dflt, 4);err = vnet_netlink_msg_send (&m, NULL);if (err)err = clib_error_return (0, "add ip4 route %U", format_clib_error, err);return err;
}

通过netlink下发其它网口配置可以参考iproute2源码。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/784881.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java中的抽象类

抽象类是指包含了抽象方法的类。在java中&#xff0c;抽象方法指的是用abstract关键字进行修饰的方法&#xff0c;抽象方法与普通的方法的最大区别就是抽象方法没有方法体&#xff0c;也就是说抽象方法是没有具体的实现的。这也就意味着在抽象类的子类中调用抽象方法时&#xf…

蓝桥杯软件测试赛项--自动化测试

目录 Lanqiao_RJCS 1.自动化测试(Selenium+python) 分值:50 1.1. Webdriver初始化

Python脚本:pve平台自动获取名字、类型、节点、备注、状态。

此脚本可自动获取pve平台的信息。有兴趣或者有需要大家可以看看。 #anthor:bbxwg #explain:pve平台自动获取名字、类型、节点、备注、状态。 #Date:2024-3-29import os import subprocess import json import re from datetime import datetime#lkh:获取虚拟机IP地址函数 def …

SiteSucker Pro mac 5.3.2激活版 网站扒站神器

SiteSucker是一个Macintosh应用程序&#xff0c;可以从互联网自动下载网站。它通过将站点的网页、图像、PDF、样式表和其他文件异步复制到本地硬盘驱动器&#xff0c;复制站点的目录结构来实现此目的。只需输入一个URL&#xff08;统一资源定位器&#xff09;&#xff0c;按回车…

JavaScript 入门指南(三)BOM 对象和 DOM 对象

BOM 对象 BOM 简介 BOM&#xff08;browser Object Model&#xff09;即浏览器对象模型BOM 由一系列对象组成&#xff0c;是访问、控制、修改浏览器的属性的方法BOM 没有统一的标准&#xff08;每种客户端都可以自定标准&#xff09;。BOM 的顶层是 window 对象 window 对象 …

习题2-5 求平方根序列前N项和

本题要求编写程序&#xff0c;计算平方根序列 的前N项之和。可包含头文件math.h&#xff0c;并调用sqrt函数求平方根。 输入格式: 输入在一行中给出一个正整数N。 输出格式: 在一行中按照“sum S”的格式输出部分和的值S&#xff0c;精确到小数点后两位。题目保证计算结果不…

1.10 类、方法、封装、继承、多态、装饰器

一、介绍类 类(class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例 实例化&#xff1a;创建一个类的实例&#xff0c;类的具体对象。 对象&#xff1a;通过类定义的数据结构实例。对象包括两个数据成员&#x…

做题目

4.1 1.目标&#xff1a;读懂文件中SVM算法&#xff0c;识别文件中的测试数据是RFID信号或者是LORA信号。 使用训练数据训练模型&#xff0c;并使用testdata测试&#xff0c;对实验结果进行分析并截图形成报告提交。 2.做法&#xff1a; ①读懂两篇博客 ②训练模型弄出来&a…

物联网实战--入门篇之(六)嵌入式-WIFI驱动(ESP8266)

目录 一、WIFI简介 二、基础网络知识 三、思路讲解 四、代码分析 4.1 状态机制 4.2 客户端连接 4.3 应用数据接收处理 4.4 数据发送 4.5 主函数调用 4.6 网络连接ID分配 五、总结 一、WIFI简介 WIFI在我们生活中太常见了&#xff0c;手机电脑都可以用WiFi连接路由器进行上…

2024品牌私域运营:「去中心化」正在成为企业决胜关键

越来越多的品牌选择以DTC模式与消费者互动和销售。通过与消费者建立紧密联系&#xff0c;不仅可提供更具成本效益的规模扩张方式&#xff0c;还能控制品牌体验、获取宝贵的第一方数据并提升盈利能力。许多企业采取的DTC私域策略以交易为中心的方法往往导致了成本上升和运营复杂…

TCP/IP:互联网通信的核心协议

引言 在当今数字化时代&#xff0c;互联网已经成为人类社会不可或缺的一部分&#xff0c;而TCP/IP协议则是支撑互联网运作的关键。无论是发送电子邮件、浏览网页&#xff0c;还是进行视频通话&#xff0c;TCP/IP协议都在背后默默地发挥着作用。本文将引领读者深入了解TCP/IP协…

Ubuntu 16.04/18.04 LTS内核降级及通过GURB指定内核

列出当前安装的所有LINUX内核 dpkg -l|grep linux-image 安装4.4.0-47内核 sudo apt-get install linux-image-4.4.0-47-generic linux-image-extra-4.4.0-47-generic 删除4.4.0-97内核 sudo apt-get purge linux-image-generic linux-image-4.4.0-97-generic linux-image-ex…

DevOps迈向标准化,平台工程让开发运维更轻松

在近一代人的时间里&#xff0c;DevOps 在软件开发和运维领域占据了主导地位。这是一套开发人员都离不开的技能和方法。Pearl Zhu 在 “The Digital Master” 一书中描述了它的重要性&#xff0c;强调 “敏捷和 DevOps 是为了利用整合、互动和创新”。在当今竞争激烈的市场中&a…

【Blockchain】区块链浏览器 | 以太坊Etherscan比特币Blockchain门罗币Monero

区块链浏览器概述 区块链浏览器是一种软件,它使用API(应用程序编程接口)和区块链节点从区块链中提取各种数据&#xff0c;然后使用数据库来排列搜索到的数据&#xff0c;并以可搜索的格式将数据呈现给用户。 用户的输入是资源管理器上的可搜索项&#xff0c;然后通过数据库上…

使用git-repo提交代码

前置环境安装 使用Git-Repo提交代码时, 需要先在本地安装好Git环境 git下载地址: https://git-scm.com/download/win 安装git-repo git-repo文档: 阿里git文档 下载地址: 下载地址 下载完git-repo以后, windows的话,将git-repo.exe放到C:\Windows\system32 文件夹下 PS: 注…

市场复盘总结 20240329

仅用于记录当天的市场情况&#xff0c;用于统计交易策略的适用情况&#xff0c;以便程序回测 短线核心&#xff1a;不参与任何级别的调整&#xff0c;采用龙空龙模式 一支股票 10%的时候可以操作&#xff0c; 90%的时间适合空仓等待 二进三&#xff1a; 进级率中 60% 最常用…

STM32系统结构及总线介绍

1、学习路径 STM32中文参考手册中的第二章存储器和总线构架 2、系统架构&#xff08;中等容量芯片stm32f103c8&#xff09; 在小容量、中容量和大容量产品中,主系统由以下部分构成: 四个驱动单元&#xff1a; CortexTM-M3内核DCode总线&#xff08;D-bus&#xff09;&#…

python的函数传入参数

函数作为参数传入 函数本身是可以作为参数&#xff0c;传入另一个函数中进行使用的 将函数传入的作用在于&#xff1a;传入计算逻辑&#xff0c;而非传入数据 def test_func(compute):resultcompute(1,2)print(type(compute))print(f"{result}")def compute(x,y):r…

霍尼韦尔大路灯怎么样?书客、雷士、霍尼韦尔护眼落地灯实测PK!

生活在快节奏的时代中&#xff0c;当代青少年都顶着很大的压力&#xff0c;熬夜学习是家常便饭&#xff0c;有时还需要借助电子产品来辅助学习&#xff0c;再加上许多家长都不太注重孩子的视力健康问题&#xff0c;孩子长时间处于一个不良的环境光下学习&#xff0c;眼睛极易疲…

PyLMKit(9):ChatTable与你的表格聊天,表格问答

功能介绍 与你的结构化数据聊天&#xff1a;支持主流数据库、表格型excel等数据&#xff01; ChatDB&#xff1a;支持数据库问答ChatTable&#xff1a;支持txt,excel,csv等pandas dataframe表格的问答 1.下载安装 pip install pylmkit -U pip install pandasql2.ChatTable实…