![在 Ubuntu 和 Raspbian 中使用网络命名空间时 ifup、ifdown 接口状态错误](https://linux22.com/image/171507/%E5%9C%A8%20Ubuntu%20%E5%92%8C%20Raspbian%20%E4%B8%AD%E4%BD%BF%E7%94%A8%E7%BD%91%E7%BB%9C%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4%E6%97%B6%20ifup%E3%80%81ifdown%20%E6%8E%A5%E5%8F%A3%E7%8A%B6%E6%80%81%E9%94%99%E8%AF%AF.png)
我用,创建了几个网络命名空间ip netns add ns1
。ip netns add ns2
当我尝试启动/停止网络命名空间内的网络接口时,ifup
接口ifdown
状态错误:
$ ifup lo
ifup: interface lo already configured
我相信这是因为 /run/network/ 是保存状态的位置ifup
,ifdown
并且在按上述方式创建时,它在默认网络命名空间和附加网络命名空间之间共享。我这么认为是因为当我在网络命名空间内创建 veth 接口时,相应的条目会出现在 /run/network 的主空间中。这个问题lo
也适用于接口。
我在 Ubuntu-12-LTS 和 2020-02-13-raspbian-buster-lite 中看到同样的问题。我有一个模糊的记忆,不久前处理过类似的症状,CentOS-7.4
只是没有调查其原因。
为了演示(在 Raspbian 上),我在每个网络命名空间内创建并 ifup/ifdown 一个桥,并检查默认空间中 /run/network/ 的内容。
设置
$ echo 'iface br2 inet manual' > /etc/network/interfaces.d/br2
$ ip netns add ns1
$ ip netns add ns2
$ ip netns exec ns1 ip link add br2 type bridge
$ ip netns exec ns2 ip link add br2 type bridge
检查br2
状态不存在
$ cat /run/network/ifstate.br2
cat: /run/network/ifstate.br2: No such file or directory
很好,在任何命名空间中都br2
没有被ifup
ed 过。ifdown
打开/关闭/打开第一个命名空间上的接口
$ ip netns exec ns1 ifup br2
$ cat /run/network/ifstate.br2
br2
$ ip netns exec ns1 ifdown br2
$ cat /run/network/ifstate.br2
$ ip netns exec ns1 ifup br2
$ cat /run/network/ifstate.br2
br2
好的,现在在第二个命名空间上执行相同的向上/向下/向上操作,会导致错误
$ ip netns exec ns2 ifup br2
ifup: interface br2 already configured
$ cat /run/network/ifstate.br2
br2
以上就是问题所在!通过第二个命名空间停止接口(注意它是通过第一个命名空间启动的)
$ ip netns exec ns2 ifdown br2
$ cat /run/network/ifstate.br2
现在回到第一个命名空间
$ ip netns exec ns1 ifdown br2
ifdown: interface br2 not configured
$ cat /run/network/ifstate.br2
上面是类似的问题,接口 wad 在第二个命名空间中停止,但在第一个命名空间中报告为关闭。
不幸的是,这适用于lo
,因此不可能通过简单地在每个命名空间内使用唯一的名称来避免它。我只需要继续使用“上/下/上”循环界面即可解决此问题。我希望有一种更清洁的方法来做到这一点。
我的用例和目标:我创建了多个与上面的测试类似的相同名称空间,仅包装到 systemd 实例化服务单元中,启动它们的接口(必须向上/向下/向上才能解决问题)。我在不同的时间在每个命名空间内启动程序,ip netns exec nsX prog args
通常也通过其他 systemd 服务启动。一段时间后,我拆除了某些名称空间,我需要小心地执行此操作。我需要ifdown
在它整理事情的同时正确地完成它的工作。网络并不简单,具有多个veth
接口、桥接器以及关联iptables
和sysctl
设置,因此我真的希望ifup
能够ifdown
很好地工作,并且不会被其他命名空间的状态所困惑。例如ifdown
删除veth
接口。当然,解决方法是可能的,但它很快就会变得很糟糕/肮脏。
我觉得我需要以/run/network
某种方式将 tmpfs 安装到网络命名空间内,但我不知道如何。绑定ip netns exec
仅在程序运行期间安装很少的东西,看起来并没有永久附加到网络名称空间本身。如果我在安装调用期间绑定挂载,ip netns exec ns1 ifup ...
它将退出并且稍后当我需要执行操作时不可用.... ifdown
。
我对创建网络命名空间的不同方法持开放态度。我只需要某种跟踪事物的方法,以便我可以在不同的时间点在正确的命名空间中启动多个程序。
答案1
问题是ip netns add
只创建了一个持久网络命名空间,ip netns exec ...
进入持久网络命名空间的同时还创建了一个临时挂载命名空间。还有一个问题直接问到了这个问题,所以我在那里更详细地回答了它。https://unix.stackexchange.com/a/581618/142686
我的解决方案是使用 来创建持久的网络和挂载命名空间unshare
。然后挂载tmpfs
到/run/network
命名空间内部。这为命名空间提供了一个单独的副本以ifup
供ifdown
使用。
主要的含义是,至少在安装重要的地方,而不是现在必须使用ip netns exec
。nsenter
为持久挂载命名空间创建目录
这需要在启动后完成一次,而不是针对每个命名空间
# Create a dir for persistent mount namespaces
$ mkdir /run/mntns
# Bind mount it with --make-private as required by `unshare --mount=/path`
$ mount --bind --make-private /run/mntns /run/mntns
创建网络+挂载持久命名空间
$ NSNAME=testns # give it a name
# Create files for persistent mounts.
# For network namespace use same paths as used by `ip netns` for interoperability
$ touch /run/netns/${NSNAME} /run/mntns/${NSNAME}
# Create persistent mount and network namespaces and
# as part of the setup the command (inside the namespace) adds a mount.
$ unshare --net=/run/netns/${NSNAME} --mount=/run/mntns/${NSNAME} \
mount -t tmpfs tmpfs /run/network
用它
# Bring up the loopback interface, this used to fail.
$ nsenter --net=/run/netns/${NSNAME} --mount=/run/mntns/${NSNAME} \
ifup lo
# Good.
更多测试
现在重要的部分。将网络接口移至命名空间中。这里使用ip
就可以了。
# Plug a veth interface into the namespace, use the `ip netns link`
$ ip link add veth1a type veth peer name veth1b netns ${NSNAME}
# Verify it is there (although here `ip netns exec` would work just as well)
$ nsenter --net=/run/netns/${NSNAME} --mount=/run/mntns/${NSNAME} \
ip link
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: veth1b@if17: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether e6:1b:ea:e5:77:b4 brd ff:ff:ff:ff:ff:ff link-netnsid 0
重要的
这种方法有效,但不是使用ip netns exec
use nsenter --net=... --mount=...
!
为了在命名空间之间移动接口,使用“ip link”仍然有效。
缺点是您无法获得/etc/netns/NETNS_NAME/*
由 提供的自动绑定安装ip netns exec NETNS_NAME cmd
,这并不难手动添加。
# either during unshare or later as nsenter.
# The important difference is here it is done once and result persists
# unlike with `ip netns exec` which does this every time
NSNAME="${NSNAME}" \
nsenter --net=/run/netns/${NSNAME} --mount=/run/mntns/${NSNAME} \
/bin/sh -e -x -c \
'cd "/etc/netns/${NSNAME}"; for f in *; do mount --bind "/etc/netns/${NSNAME}/${f}" "/etc/${f}"; done;'
好处是您可以一次完成一致的安装,而不是每次调用之间ip netns exec
的内容发生变化时都会发生变化。/etc/netns/NETNS_NAME/