背景
在现代 Linux 系统上,管理网络接口、IP 地址和路由以及相关事物的首选方式是使用软件包中的工具iproute2
。
我正在尝试设置一个有点奇特的网络配置,其中我使用虚拟以太网适配器上的环回地址空间中的地址,这些虚拟以太网适配器连接一台机器内的不同网络命名空间。为了使它工作,我需要更改环回设备的配置。环回设备自动配置,具有以下状态:
# ip addr show dev lo
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
如您所见,接口控制了整个127.0.0.0/8
块,并且块中的所有数据包都被路由到此设备上。为了使块的某些子集可路由到我的虚拟以太网设备,我需要使lo
的声明更窄,因此它类似于以下内容:
# ip addr show dev lo
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/12 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
这很容易做到,你只需启动接口,然后使用以下命令更改附加到它的 IP 地址:
ip addr del 127.0.0.1/8 dev lo
ip addr add 127.0.0.1/12 dev lo
这还将更新路由信息,因此只会127.0.0.1/12 (127.0.0.0 through 127.15.255.255)
路由到lo
。这是可取的。
问题是,每当你建立一个环回接口时,无论它是否已经配置了地址,也不管这些地址是什么,它都会总是重新绑定其默认地址。因此,每次lo
手动降低和提高,或者从头开始创建环回接口(例如在新的网络命名空间中),我都需要跳转到它并修改其配置的地址。
问题
有什么方法可以覆盖此行为吗?我完全同意使用默认为127.0.0.1/12
而不是 的系统范围环回配置127.0.0.1/8
,但我也会接受任何不需要我像直升机一样悬停在每个命名空间中的所有环回设备上,轮询它们以确保它们的地址正确的解决方法。
我已经调查过如何iproute2
提高和降低接口不幸的是,它看起来只是将 UP 标志传递给 ioctl,没有任何花哨或炫耀,这对我来说意味着这种行为可能来自内核设备配置的某个更深层次的地方,甚至可能是硬编码的。我并不反对用一种极其技术性的解决方案来解决这个愚蠢的问题,包括重新编译内核模块或类似奢侈的方法,但在我这样做之前,我很想知道是否有一种配置调整或解决方法可以实现我需要的功能。有人能解释一下这种行为从何而来,以及如何绕过它吗?
答案1
它看起来像是来自 systemd(或者其他 init 系统,如果你使用其他系统的话)。
此代码显示了如何初始化而现在还不可能改变任何这种行为。
答案2
环回设备上的地址洛每当接口启动时,内核就会自动添加,如下面的内核 5.10 的源摘录所示。
127.0.0.1
IPv4 和aka 的相关部分INADDR_LOOPBACK
在net/ipv4/devinet.c
:
case NETDEV_UP: if (!inetdev_valid_mtu(dev->mtu)) break; if (dev->flags & IFF_LOOPBACK) { struct in_ifaddr *ifa = inet_alloc_ifa(); if (ifa) { INIT_HLIST_NODE(&ifa->hash); ifa->ifa_local = ifa->ifa_address = htonl(INADDR_LOOPBACK); ifa->ifa_prefixlen = 8; ifa->ifa_mask = inet_make_mask(8); in_dev_hold(in_dev); ifa->ifa_dev = in_dev; ifa->ifa_scope = RT_SCOPE_HOST; memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); set_ifa_lifetime(ifa, INFINITY_LIFE_TIME, INFINITY_LIFE_TIME); ipv4_devconf_setall(in_dev); neigh_parms_data_state_setall(in_dev->arp_parms); inet_insert_ifa(ifa); } }
::1
IPv6 和aka 的相关部分IN6ADDR_LOOPBACK_INIT
在net/ipv6/addrconf.c
:
static void init_loopback(struct net_device *dev) { struct inet6_dev *idev; /* ::1 */ ASSERT_RTNL(); idev = ipv6_find_idev(dev); if (IS_ERR(idev)) { pr_debug("%s: add_dev failed\n", __func__); return; } add_addr(idev, &in6addr_loopback, 128, IFA_HOST); }
仍然可以添加其他地址(例如 127.0.0.1/12)并删除 IPv4 地址 127.0.0.1/8,正如 OP 已经写道的那样:
ip address add 127.0.0.1/12 dev lo scope host
ip address delete 127.0.0.1/8 dev lo
但是如果接口被关闭,然后重新启动,这个地址将被内核放回(除了添加的地址)。
另外,一些内核部分可能还保留了 127.0.0.0/8 的硬编码使用(实际上,我只能找到一个很少发生的例子netfilter 的 h323 ALG 助手,但我可能遗漏了一些东西)。
因此必须改变内核来改变这种行为。