无法创建嵌套网络命名空间

无法创建嵌套网络命名空间

似乎无法从网络命名空间创建网络命名空间。结果为“错误:对等网络命名空间引用无效。”。

这是一个错误还是存在某种我不知道的限制?

下面是我的错误 cmd 跟踪。

# ip netns add foo1
# ip netns exec foo1 ip netns add foo2
# ip netns
Error: Peer netns reference is invalid.
Error: Peer netns reference is invalid.
foo2
foo1
# ip netns exec foo2 /bin/bash
setting the network namespace "foo2" failed: Invalid argument

答案1

TL;DR:虽然看起来很奇怪,但这实际上并不是网络命名空间问题,但命名空间问题,这是预料之中的。

您应该创建所有新的“ip netns namespaces”(请参阅​​后面的含义),即ip netns add ...从初始(主机)“ip netns namespace”运行所有命令,而不是从已输入的“ip netns namespace”内部运行ip netns exec ...。只要您不创造然后你就可以自由地转变随意在它们之间执行包括从一个命令嵌套到另一个命令,使用ip netns exec ...

以下是一步步示例的详细解释...


ip netns专注于网络命名空间,但是为了处理所有功能,还必须与挂载命名空间混合,原因有二(至少,我知道的):

  • 绑定挂载/etc/netns/FOO/SOMESERVICE/etc/SOMESERVICE管理备用服务/守护进程配置

    此功能非常方便,可以轻松在其他网络命名空间中运行一些(与网络相关的)守护进程,但除此之外,它仍然是“主机”的一部分。您可以在 UL 查看我对这个问题的回答:使用 ip netns (iproute2) 进行命名空间管理它的使用需要和下面的特性一样的处理,就不再讲了。

  • 重新挂载/sys以在其层次结构中公开新网络命名空间的网络设备

    这是必需的功能。以下示例暴露了问题:

    来自“初始主机”:

    # ip link add dev dummy9 type dummy
    # ip -br link show dummy9 
    dummy9           DOWN           f6:f6:48:9c:12:b9 <BROADCAST,NOARP> 
    # ls -l /sys/class/net/dummy9
    lrwxrwxrwx. 1 root root 0 Apr  4 22:09 /sys/class/net/dummy9 -> ../../devices/virtual/net/dummy9
    

    用一个低级工具更改为其他(临时)网络命名空间:

    # unshare --net ip -br link show dummy9 
    Device "dummy9" does not exist.
    # unshare --net ls -l /sys/class/net/dummy9
    lrwxrwxrwx. 1 root root 0 Apr  4 22:13 /sys/class/net/dummy9 -> ../../devices/virtual/net/dummy9
    

    这就是问题所在:/sys仍然公开初始主机的接口,而不是新网络命名空间的接口。这就是网络命名空间与挂载之间的交互/sys:如果/sys从新网络命名空间挂载,它将切换到在选定的目录层次结构中公开新网络接口(例如/sys/class/net/sys/devices/virtual/net)。这仅在挂载时完成,而不是动态完成。一些高级网络设置只需在那里读取或写入即可轻松获得,因此必须提供它们,反之亦然:在新网络环境中运行的隔离进程不应该能够查看或更改初始主机的接口。

所以ip netns exec FOO ...(但不是ip netns add FOO)也可以通过以下方式解决这个问题取消共享命名空间并/sys/在其中重新挂载,以免破坏初始主机的网络命名空间。但重要的是,这个挂载命名空间本身是短暂的:当您分别运行两个ip netns exec FOO ...命令时,它们不会最终位于同一个挂载命名空间中。它们各自都有自己的命名空间,/sys其中的重新挂载指向相同的网络命名空间。

到目前为止,没有问题。当这种情况发生时,我将其称为“ip netns 命名空间”,因为现在涉及两种类型的命名空间。到目前为止,我们有:

术语1:

# ip netns add FOO
# ls -l /proc/$$/ns/{mnt,net}
lrwxrwxrwx. 1 root root 0 Apr  4 22:28 /proc/1712/ns/mnt -> mnt:[4026531840]
lrwxrwxrwx. 1 root root 0 Apr  4 22:28 /proc/1712/ns/net -> net:[4026531992]
# ip netns exec FOO bash
# ls -l /proc/$$/ns/{mnt,net}
lrwxrwxrwx. 1 root root 0 Apr  4 22:33 /proc/1864/ns/mnt -> mnt:[4026532618]
lrwxrwxrwx. 1 root root 0 Apr  4 22:33 /proc/1864/ns/net -> net:[4026532520]

期限2:

# ls -l /proc/$$/ns/{mnt,net}
lrwxrwxrwx. 1 root root 0 Apr  4 22:32 /proc/1761/ns/mnt -> mnt:[4026531840]
lrwxrwxrwx. 1 root root 0 Apr  4 22:32 /proc/1761/ns/net -> net:[4026531992]
# ip netns exec FOO bash
# ls -l /proc/$$/ns/{mnt,net}
lrwxrwxrwx. 1 root root 0 Apr  4 22:33 /proc/1866/ns/mnt -> mnt:[4026532821]
lrwxrwxrwx. 1 root root 0 Apr  4 22:33 /proc/1866/ns/net -> net:[4026532520]

请注意,在更改 ip netns 命名空间后,虽然新的网络命名空间对于期限1期限2,新的挂载命名空间彼此不同(并且与初始主机不同)。

现在会发生什么期限1你创建了一个新的 ip netns 命名空间吗?让我们看看:

术语1:

# ip netns add BAR
# ip netns ls
BAR
FOO

期限2:

# ip netns ls
Error: Peer netns reference is invalid.
Error: Peer netns reference is invalid.
BAR
FOO

这是因为,较新的命名空间 BAR 将在没有进程的情况下继续存在,就像其他命名空间一样,安装在(新创建的空文件)上/var/run/netns/BAR(再次参见上一个链接例如)。虽然挂载命名空间不同,但它们具有相同的根目录:初始主机的根目录。因此,这个新创建的空文件当然/var/run/netns/BAR可以在任何地方看到(初始、期限1的山,期限2的挂载 ns) 创建时。

唉,上面的山,已经建好了期限1的 FOO 的挂载命名空间,只能在期限1, 不开期限2也不在其他地方,因为它是不同的挂载命名空间。因此,虽然 term1(的 FOO ip netns 命名空间)中/var/run/netns/BAR有一个属于nsfs伪文件系统的伪文件:

术语1:

# stat -f -c %T /var/run/netns/BAR
nsfs

tmpfs它是(从实际/run安装位​​置)其他任何地方的空文件:

期限2:

# stat -f -c %T /var/run/netns/BAR
tmpfs

任何其他终端:

$ stat -f -c %T /var/run/netns/BAR
tmpfs

只要不退出当前的“ip netns namespace”,它仍然可以在 term1 中看到。如果从期限1一个仍然切换 ip netns 命名空间,它仍然会没问题,因为新的非共享临时挂载命名空间是前一个的副本,包括所有挂载。

如果退出,该挂载点将丢失(这意味着,如果不再有进程或文件描述符使用它,BAR 对应的网络命名空间将消失,因为它仅由该挂载点持有)。此后,任何ip netns ls命令都会在任何地方发出抱怨。您只需删除过时且现在无用的文件即可/run/netns/BAR修复它。

在阅读了这些步骤后,你需要记住的是,你不应该创造ip netns add在当前使用 进入的命名空间内,新建 命名空间ip netns exec。您应该从初始(主机)命名空间创建它们,然后可以从任何 ip netns 命名空间随意在它们之间切换。

当然,如果/var/run/netns/(即挂载点/run)在(保持模糊的)命名空间之间是不同的,那么就没有交互,并且每个ip netns调用都将与其他调用隔离,不会看到或与其他调用交互。这通常发生在哪里?在完整的容器中,挂载和网络命名空间都是分开的,并且从一开始就指向不同的资源。


更新:正如评论中所要求的,我检查了如何“修复”这个问题,但找不到任何简单的解决方案。

首先有一个前提条件:如上所述,一旦在 FOO 内部创建新的“ip netns”命名空间 BAR,并且 FOO 已存在,则对 BAR 的唯一引用将消失,从而使 BAR 也消失。还需要做更多的事情。

其实有三种方法可以保持命名空间引用

  • process:这是主要方法,大多数情况下,命名空间就是这么使用的
  • 挂载点(这是使用的方法ip netns):允许保留一个没有任何进程的命名空间,可以有一个只有网络设置的命名空间(接口、网桥、tc 规则、防火墙规则等)。
  • 打开文件描述符:很少见,在创建命名空间时使用,但很少保留,除非应用程序同时处理多个命名空间并使用文件描述符切换一些线程以便于引用。

我们可以使用第 1 种或第 3 种方法。在找到可行的方法之前,我们进行了多次失败的尝试……

正如之前所说的,不起作用:

# ip netns add FOO
# ip netns exec FOO ip netns add BAR

只需让进程暂时运行在第一的“ip netns”命名空间,因为它的临时命名空间部分,以保留对新的“ip netns”命名空间网络命名空间,然后稍后从外部(从初始命名空间)重用它。

也不起作用:

# ip netns add FOO
# ip netns exec FOO sh -c 'ip netns add BAR; sleep 999 < /var/run/netns/BAR & echo $!'
28344
# strace -e trace=readlink,mount mount --bind /proc/6295/fd/0 /var/run/netns/BAR
readlink("/proc/6295/fd/0", "/run/netns/BAR", 4095) = 14
readlink("/var/run", "/run", 4095)      = 4
mount("/run/netns/BAR", "/run/netns/BAR", 0x55c88c9cccb0, MS_BIND, NULL) = 0
+++ exited with 0 +++
# stat -f -c %T /run/netns/BAR
tmpfs

正如所见,stracemount命令遵循了符号链接,而对于这种用例来说它不应该这样做(注意:挂载仍然以某种方式链接到睡眠进程,必须终止该进程才能卸载它)。

这(输入sleep's命名空间,用于访问 BAR 的已安装网络命名空间隐藏在那里)可以工作,但依赖于任何进程的持续存在sleep才能继续使用:

# ip netns add FOO
# ip netns exec FOO sh -c 'ip netns add BAR; ip -n BAR link add dummy8 type dummy; sleep 999 & echo $!'
12916
# nsenter --target=12916 --mount ip -n -brief BAR link show
lo               DOWN           00:00:00:00:00:00 <LOOPBACK> 
dummy8           DOWN           8e:ce:b3:d1:9c:bb <BROADCAST,NOARP> 

奇怪的是(使用 mount 命名空间快捷方式/proc/pid/root/)不起作用(我真的不知道为什么):

# stat -f -c %T /proc/12916/root/var/run/netns/BAR 
tmpfs

最后什么会起作用:

# ip netns add FOO
# ip netns exec FOO sh -c 'ip netns add BAR; ip -n BAR link add dummy8 type dummy; ip netns exec BAR sh -c '\''sleep 999 & echo $!'\'
14124
# mount --bind /proc/14124/ns/net /var/run/netns/BAR
# ip -n BAR -brief link show
lo               DOWN           00:00:00:00:00:00 <LOOPBACK> 
dummy8           DOWN           3a:48:65:20:68:c1 <BROADCAST,NOARP> 

所以最后可以使用类似的东西。如果您尝试在 sleep 命令结束之前立即删除它们,可能会出现竞争条件。

# ip netns add FOO
# mount --bind /proc/$(ip netns exec FOO sh -c 'ip netns add BAR; ip netns exec BAR bash -c '\''sleep 5 </dev/null >/dev/null 2>&1 & echo $!; disown'\')/ns/net /var/run/netns/BAR

如何使用这样的构造?我不知道,因为在遇到嵌套“ip netns”问题之前没有给出原始问题。也许有更简单的解决方案,而不必尝试创建“嵌套网络命名空间”。

相关内容