Linux 内核版本:5.4
1. ioctl 系统调用
ioctl(input/output control)
系统调用操作的对象是一个文件,通常是用于实现特殊设备所需但标准文件系统没有提供的操作。
可以把socket
系统调用返回的套接字描述符传给ioctl
,这也是网络代码使用ioctl
的方式。例如,ifconfig
和route
命令就使用该方式。
ioctl
命令的分派如下图所示
2. Netlink 套接字
Netlink
套接字代表用户空间与内核的IP网络配置之间的首选接口,也可作为内核内部以及多个用户空间进程之间的消息传输系统。
Netlink
架构如下图
首先快速审视一下socket
系统调用的原型:
int socket(int domain, int type, int protocol)
当打开一个Netlink
套接字时,必须提供domain, type, protocol
参数。Netlink
使用PF_NETLINK
协议簇,只支持SOCK_DGRAM
类型,而且定义了几种协议,每一种都用于网络协议栈的不同组件(或一组组件)。例如,NETLINK_ROUTE
协议用于大多数网络功能,如路由和邻居协议。
值得注意的是,地址簇AF_
和协议簇PF_
可以相互替换,因为在linux/socket.h
中有如下源代码:
/* Protocol families, same as address families. */
#define PF_UNSPEC AF_UNSPEC
#define PF_UNIX AF_UNIX
#define PF_LOCAL AF_LOCAL
#define PF_INET AF_INET
#define PF_AX25 AF_AX25
#define PF_IPX AF_IPX
#define PF_APPLETALK AF_APPLETALK
#define PF_NETROM AF_NETROM
#define PF_BRIDGE AF_BRIDGE
#define PF_ATMPVC AF_ATMPVC
#define PF_X25 AF_X25
#define PF_INET6 AF_INET6
#define PF_ROSE AF_ROSE
#define PF_DECnet AF_DECnet
#define PF_NETBEUI AF_NETBEUI
#define PF_SECURITY AF_SECURITY
#define PF_KEY AF_KEY
#define PF_NETLINK AF_NETLINK
#define PF_ROUTE AF_ROUTE
#define PF_PACKET AF_PACKET
#define PF_ASH AF_ASH
#define PF_ECONET AF_ECONET
#define PF_ATMSVC AF_ATMSVC
#define PF_RDS AF_RDS
#define PF_SNA AF_SNA
#define PF_IRDA AF_IRDA
#define PF_PPPOX AF_PPPOX
#define PF_WANPIPE AF_WANPIPE
#define PF_LLC AF_LLC
#define PF_IB AF_IB
#define PF_MPLS AF_MPLS
#define PF_CAN AF_CAN
#define PF_TIPC AF_TIPC
#define PF_BLUETOOTH AF_BLUETOOTH
#define PF_IUCV AF_IUCV
#define PF_RXRPC AF_RXRPC
#define PF_ISDN AF_ISDN
#define PF_PHONET AF_PHONET
#define PF_IEEE802154 AF_IEEE802154
#define PF_CAIF AF_CAIF
#define PF_ALG AF_ALG
#define PF_NFC AF_NFC
#define PF_VSOCK AF_VSOCK
#define PF_KCM AF_KCM
#define PF_QIPCRTR AF_QIPCRTR
#define PF_SMC AF_SMC
#define PF_XDP AF_XDP
#define PF_MAX AF_MAX
3. 用户空间路由配置工具
路由的配置可以使用net-tools
软件包和iproute2
软件包,它们分别使用ioctl
和Netlink
接口。net-tools
不能够配置任何高级路由功能,例如,多路径和策略路由。通过net-tools
包中的各个实用工具显示的结果中也看不到这些高级路由功能。但iproute2
包提供的路由配置命令向后与net-tools
兼容。
基于ioctl
和基于Netlink
的路由表操作如下图
3.1 利用 net-tools 包配置路由
net-tools
包中的route
命令,是最常见的配置路由和转储路由表及路由缓存内容的方法。
route add
和route del
命令分别向内核发送ioctl
命令SIOCADDRT
和SIOCDELRT
,用来添加路由和删除路由。
内核处理程序ip_rt_ioctl
负责这两个ioctl
命令的处理,其源码位于net/ipv4/fib_frontend.c
int ip_rt_ioctl(struct net *net, unsigned int cmd, struct rtentry *rt)
{
struct fib_config cfg;
int err;
switch (cmd) {
case SIOCADDRT: /* Add a route */
case SIOCDELRT: /* Delete a route */
if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
return -EPERM;
rtnl_lock();
err = rtentry_to_fib_config(net, cmd, rt, &cfg);
if (err == 0) {
struct fib_table *tb;
if (cmd == SIOCDELRT) {
tb = fib_get_table(net, cfg.fc_table);
if (tb)
err = fib_table_delete(net, tb, &cfg,
NULL);
else
err = -ESRCH;
} else {
tb = fib_new_table(net, cfg.fc_table);
if (tb)
err = fib_table_insert(net, tb,
&cfg, NULL);
else
err = -ENOBUFS;
}
/* allocated by rtentry_to_fib_config() */
kfree(cfg.fc_mx);
}
rtnl_unlock();
return err;
}
return -EINVAL;
}
其内部结构如下图所示:
3.2 利用 iproute2 工具配置路由
利用iproute2
包不仅可以添加或删除路由,还可以修改路由、追加路由( append )和预先追加路由( prepend ),这些操作并不代表额外的功能,而只是在处理大型路由表时使管理操作更为容易。
ip route
和ip rule
命令分别被用于处理路由和策略路由规则。处理路由的内核处理程序是inet_rtm_newroute
和inet_rtm_delroute
,其位于net/ipv4/fib_frontend.c
static int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh,
struct netlink_ext_ack *extack)
{
struct net *net = sock_net(skb->sk);
struct fib_config cfg;
struct fib_table *tb;
int err;
err = rtm_to_fib_config(net, skb, nlh, &cfg, extack);
if (err < 0)
goto errout;
tb = fib_new_table(net, cfg.fc_table);
if (!tb) {
err = -ENOBUFS;
goto errout;
}
err = fib_table_insert(net, tb, &cfg, extack);
if (!err && cfg.fc_type == RTN_LOCAL)
net->ipv4.fib_has_custom_local_routes = true;
errout:
return err;
}
static int inet_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh,
struct netlink_ext_ack *extack)
{
struct net *net = sock_net(skb->sk);
struct fib_config cfg;
struct fib_table *tb;
int err;
err = rtm_to_fib_config(net, skb, nlh, &cfg, extack);
if (err < 0)
goto errout;
if (cfg.fc_nh_id && !nexthop_find_by_id(net, cfg.fc_nh_id)) {
NL_SET_ERR_MSG(extack, "Nexthop id does not exist");
err = -EINVAL;
goto errout;
}
tb = fib_get_table(net, cfg.fc_table);
if (!tb) {
NL_SET_ERR_MSG(extack, "FIB table does not exist");
err = -ESRCH;
goto errout;
}
err = fib_table_delete(net, tb, &cfg, extack);
errout:
return err;
}
其内部结构如下图所示: