内核如何管理 ip 地址
命令行操作
有时候我们需要给一个网口配置的有个 IP 地址,这时候我们有两种配置方法:
- 使用 ifconfig 配置到网口别名上。
- 使用 ip addr 命令直接配置到网口上。
两种方法最终在内核实现是一样的,存储位置也一样,并且可以相互读写配置结果。
比如将9.9.9.199/24
配置到 lo 口上,并起个别名lo:9
1 | [root@VM-0-12-centos ~]# ifconfig lo:9 9.9.9.199/24 |
ifconfig命令的配置结果,也可以通用ip link
命令来查看
1 | [root@VM-0-12-centos ~]# ip a show dev lo |
网口的别名在ip命令里被当做label
输出,放在scope字段后面
Q: ifconfig和ip命令在功能上有什么差别呢?
A: 从功能上没有什么不一样的地方,只是使用了内核的两套不同的接口,一般推荐使用ip命令来配置网口。
- ifconfig:使用
ioctl
接口配置/读取网口上的IP地址 - ip addr:使用
netlink
消息接口配置/读取网口上的IP地址
这里只要写ifconfig
和ioctl
相关的处理逻辑。ip
命令如何实现对网口IP地址的配置,另外一篇文章总结。
内核对网口 IP 地址的管理
概述
- 每个ip地址,在内核里有一个
struct in_ifaddr
的结构体相对应。这个结构体挂在在每个网口设备下,并且通过一个单向链表(ifa_next
)链接在一起。 - 同时把同 ns 下的所有的
in_ifaddr
挂在到一个 hash桶链表(net->ipv4.inet_addr_lst
)里。 - 每个ifa 地址里除了上面说的两个链的挂载点,还有保存着ip地址,掩码和label。 其中label对应着ifconfig指定的网口别名。
ifconfig命令对网口地址的管理
ifcofig代码实现
源代码: https://github.com/giftnuss/net-tools/blob/master/ifconfig.c
ifconfig 首先创建一个AF_INET
的socket,借助这个socket 通过,通过一个 ioctl 命令SIOCSIFADDR
下发给内核。
配置IP 地址时候,参数被存放到struct ifreq
结构体里,用来存放网口名字和 ip 地址.
1 | int main(int argc, char **argv) |
内核侧代码实现
内核实现是围绕着一个ifa
结构体struct in_ifaddr
展开的。
- 函数调用:内核的 ioctl系统调用函数,接收用户空间传回来的命令
SIOCSIFADDR
和对应的struct ifreq
变量, 里面存放着别名及 IP 地址。 - ifa结构体
struct in_ifaddr
:ioctl通过几层抽象及 ops 的调用,最终在devinet_ioctl
中, 创建一个struct in_ifaddr
的结构体,存放别名(ifa_label
)及IP 地址(ifa_address
)。 - 同一个网口下多个IP地址:只要地址不冲突,可以配置多个 IP 地址。 并通过 ifa 结构体下面的
ifa_next
组成一个单向链表。 - 同一个 ns(netnamespace)下,ifa 按照 hash 分散到一个 hash 桶链表里。
ifa结构体in_ifaddr
1 | 143 struct in_ifaddr { |
函数调用关系
1 | SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd, unsigned long, arg) |
packet_ops_spkt
1 | 4684 static const struct proto_ops packet_ops_spkt = { |
packet_ioctl
1 | 4285 static int packet_ioctl(struct socket *sock, unsigned int cmd, |
inet_dgram_ops
1 | 1087 const struct proto_ops inet_dgram_ops = { |
inet_ioctl
1 | 957 int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) |
devinet_ioctl
1 | 1071 int devinet_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr) |
把ifa插入到链表中
ifa
被插入到两个list 里。
ifa_list
单向链表: 同一个网口下的 IP 地址,插入到对应的ifa_list
下的单向链表中。- hash桶链表:同一个ns(netnamespace)下的所有ifa 分散到一个hash链表中。
1 | 499 static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh, |
1 | 129 static void inet_hash_insert(struct net *net, struct in_ifaddr *ifa) |
- 备注:2025更新到内核代码版本v6.14(v6.14-rc6-22-gb7f94fcf5546)