/run/systemd/netif/links 目录大小持续增加导致 /run 目录满问题分析

问题描述

业务程序内部出现了某种异常,每分钟会创建删除一次特定名称的网络接口,运行一段时间后 /run 空间占满,观测到 /run/systemd/netif/links 目录下有巨量文件产生,且每一个都占用内存。

环境信息

内核版本:5.10
systemd 版本:219

问题触发过程示例

  1. 执行 ip 命令创建 tap 口然后删除

    ip tuntap add dev taptest mode tap
    ip tuntap del dev taptest mode tap
    
  2. 查看 /run/systemd/netif/links 目录下的内容

    ls -lh /run/systemd/netif/links
    

当网络接口大量创建时 /run/systemd/netif/links 中的文件内容就会持续增加。

初步分析

一个网络设备接口会分配一个 index,同时会在 /run/systemd/netif/links 下创建一个文件,接口 id 可以使用 ip link show 命令查看。

ip link show 命令执行示例:

 [root@localhost] # ip link show 
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: bond0: <BROADCAST,MULTICAST,MASTER> mtu 1500 qdisc noop state DOWN mode DEFAULT qlen 1000
    link/ether 6a:1c:60:57:1b:9e brd ff:ff:ff:ff:ff:ff
    link/ether 98:30:00:32:18:7b brd ff:ff:ff:ff:ff:ff
5: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT 
    link/ether 02:42:70:b8:d6:e2 brd ff:ff:ff:ff:ff:ff
7: vethfa4d604@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT 
    link/ether 2e:f2:5c:87:73:d9 brd ff:ff:ff:ff:ff:ff link-netnsid 0
9: veth550eac7@if8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT 
    link/ether 66:6c:c4:ea:fc:d2 brd ff:ff:ff:ff:ff:ff link-netnsid 1

第一个字段为网络接口的索引号。它是一个唯一标识符,用于在系统中标识不同的网络接口。

内核中网络接口索引的分配方式

测试环境观测到的情况:

按照大小从 1 开始分配,每次加一,释放后不会回收

chatgpt 中搜素到的内容:

在内核中,分配网络接口索引的行为是由内核网络子系统负责的。当内核启动时,网络子系统会扫描系统中的网络接口,并为每个接口分配一个唯一的索引号。

网络接口索引号是一个非负整数,通常从1开始递增。索引号的分配是基于接口的注册顺序和接口类型等因素进行的。例如,以太网接口(如 eth0)可能会先于无线接口(如 wlan0)获得较小的索引号。

内核使用这些索引号来标识和管理系统中的网络接口。它们在内核的网络子系统中被用作唯一标识符,用于识别和操作特定的网络接口。应用程序和工具可以使用这些索引号来引用和配置网络接口。

需要注意的是,网络接口索引号在系统重启时可能会发生变化。因此,在编写依赖于索引号的应用程序或脚本时,应该小心处理索引号的变化情况,以避免出现错误。

问题分析

  1. 是否有配置让内核在接口释放后立刻回收索引?

    经搜索,并不支持此种配置。

  2. /run/systemd/netif/links 目录的功能

    /run/systemd/netif/links 目录是 Systemd 管理网络接口的运行时状态的目录之一。在 Systemd 中,网络接口被表示为链接(links),这些链接文件以接口名称的形式存在于该目录中。

    每个接口的链接文件包含有关该接口的信息,例如接口的索引号、MAC 地址、设备类型等。这些信息可以通过读取链接文件来获取。

    此目录的功能包括:

    1. 提供接口的运行时状态信息:通过查看接口的链接文件,可以获取接口的索引号、MAC 地址、设备类型等信息。这些信息对于网络配置和故障排除非常有用。
    2. 动态更新接口状态:当系统中的网络接口发生变化(如添加、删除、重命名接口)时,Systemd 会相应地更新 /run/systemd/netif/links 目录中的链接文件。这样,其他程序可以监视该目录,以获取接口状态的实时更新。
    3. 与其他 Systemd 单元的集成:Systemd 可以根据接口的状态变化触发其他 Systemd 单元的启动、停止或重启。例如,可以配置一个 Systemd 服务单元,在某个接口启动或停止时自动执行一些操作。

    需要注意的是,/run/systemd/netif/links 目录中的链接文件是运行时状态的快照,只反映当前系统中的接口状态。它们在系统重新启动时会被清除,并根据实际情况重新生成。

  3. 是否有配置控制在接口被移除时 /run/systemd/netif/links 下的对应文件被移除?

    在 Systemd 中,没有直接的配置选项可以控制 /run/systemd/netif/links 目录中的链接在接口被移除时进行释放。这个目录中的链接文件是由 Systemd 动态管理的,并且会在接口状态发生变化时进行更新。

    当一个网络接口被移除时,Systemd 会相应地更新 /run/systemd/netif/links 目录中的链接文件,将对应的链接文件删除。这样可以确保该接口的状态信息不再存在于目录中。

    然而,需要注意的是,删除链接文件并不会立即释放接口的索引号。接口的索引号通常由内核管理,并且会在系统重启或其他接口需要分配新的索引号时才会重新使用。

    如果您需要在接口被移除时立即释放接口的索引号,一种可行的方法是通过重新启动网络服务或重启系统来实现。这样可以确保删除的接口的索引号被立即释放并可以重新分配。

对比测试

ubuntu 环境

内核版本:5.15

systemd 版本:systemd245

测试情况:

  1. 接口索引的使用也是一样的行为,删除接口不会立刻释放接口索引,会持续累增
  2. /run/systemd/netif/links 目录下并不会创建任何文件,全局搜索也搜索不到

内核代码分析

register_netdevice 的时候,内核调用 dev_new_index 分配标识,其代码如下:

static int dev_new_index(struct net *net)
{
	int ifindex = net->ifindex;

	for (;;) {
		if (++ifindex <= 0)
			ifindex = 1;
		if (!__dev_get_by_index(net, ifindex))
			return net->ifindex = ifindex;
	}
}

上述代码在当前 namespace 的 net 结构中获取 ifindex 的现值,然后对其进行递增操作,当溢出时,从 1 开始分配,分配完成一个后再检索有没有接口占用此 index,有人占用则继续循环,无人占用则修改 net 结构中的 ifindex 并返回 ifindex。此返回值会被赋值到 netdevice 结构中的 ifindex 字段上,下一次再分配就从此 ifindex 开始递增。

unregister_netdevice 的时候,netdevice 会从内核的各种表项中移除,不涉及 ifindex 相关操作。

真正的问题与解决方案

systemd-networkd 进程未在网络接口被删除的时候移除 /run/systemd/netif/links 目录中的对应文件触发了此问题。

官方已经修复了此问题,相关 patch 如下:

diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index cc9dc39..b3e2101 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -315,6 +315,8 @@ static void link_free(Link *link) {
         if (link->manager)
                 hashmap_remove(link->manager->links, INT_TO_PTR(link->ifindex));

+        (void) unlink(link->state_file);
+
         free(link->ifname);

         free(link->state_file);

相关地址如下:

https://github.com/coreos/systemd/pull/30/commits/641481fbad7e66ac8d9bda8d3454e837f452495a

https://github.com/coreos/bugs/issues/1081

可以通过更新 systemd 版本到 228 之上、合入此 patch 重新发布 systemd 组件来解决此问题。

其它解决方案

命令行方式规避方法:

rm -r /run/systemd/netif/links && systemctl restart systemd-networkd
  • 10
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值