0

I create several network namespaces with ip netns add ns1, ip netns add ns1.

When I try to start/stop network interfaces inside network namespaces with ifup and ifdown the interface state is wrong:

$ ifup lo
ifup: interface lo already configured

I believe this is because the /run/network/ is where ifup and ifdown keep the state and it is shared between default and additional network namespaces when created as above. I think so because when I create veth interfaces inside a network namespace, the corresponding entries turn up in main space in /run/network. This problem applies to lo interface as well.

I'm seeing same problem in Ubuntu-12-LTS and 2020-02-13-raspbian-buster-lite. I have a vague memory dealing with similar symptoms in CentOS-7.4 a while back just not investigating it for causes.

To demonstrate (on Raspbian) I create and ifup/ifdown a bridge inside each network namespace, and check content of /run/network/ in the default space.

Setup

$ 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

Check that br2 state does not exists

$ cat /run/network/ifstate.br2
cat: /run/network/ifstate.br2: No such file or directory

Good, the br2 has not been ifuped of ifdowned ever in any namespace. Bring up/down/up the interface on 1st namespace

$ 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

OK, and now do same up/down/up on 2nd namespace, causes error

$ ip netns exec ns2 ifup br2
ifup: interface br2 already configured

$ cat /run/network/ifstate.br2
br2

This above is the problem! Stop the interface via 2nd namespace (note it was started via 1st)

$ ip netns exec ns2 ifdown br2
$ cat /run/network/ifstate.br2

Now back to 1st namespace

$ ip netns exec ns1 ifdown br2
ifdown: interface br2 not configured
$ cat /run/network/ifstate.br2

Above is similar problem, interface wad stopped in 2nd namespace, but is reported as down in 1st.

Unfortunately this applies to lo so it's impossible to avoid it by simply having unique names inside each namespace. I just have to keep cycling interfaces with 'up/down/up' to get around this issue. I hope there is a cleaner way to do this.

My use case and goals: I create multiple identical namespaces similar to test above only wrapped into systemd instanced service units, start their interfaces (having to up/down/up to work around the issue). At various times I launch programs inside each namespace, with ip netns exec nsX prog args often also by other systemd services. Some time later I dismantle certain namespace, and I need to do this carefully. I need ifdown to do its work properly as it tidies things up. The networks are non-trivial, with several veth interfaces, bridges and associate iptables and sysctl settings, so I really want ifup and ifdown to work well and not get confused by other namespace's state. E.g. ifdown removes veth interfaces. Certainly workaround are possible, but it gets hacky/dirty quickly.

I feel like I need to mount tmpfs onto /run/network within network namespace somehow, but I don't know how. The ip netns exec bind mounts few things only for duration of the program that runs, not permanently attached to the network namespace itself, it seems. If I bind mount during setup invocation ip netns exec ns1 ifup ... it will exit and not be available later when I need to do .... ifdown.

I am open to different methods of creating network namespaces. I just need some way of tracking things so I can launch several programs in the right namespace at different points in time.

1 Answer 1

1

The problem is that ip netns add creates a persistent network namespace only, and ip netns exec ... enters a persistent network namespace while also creating a temporary mount namespace. There is another question that asks directly about this problem, so I answered it in more detail there. https://unix.stackexchange.com/a/581618/142686

My solution is to create both network and mount namespaces persistent by using unshare. Then mount tmpfs over /run/network inside the namespace. This gives the namespace a separate copy for ifup and ifdown to use.

The main implication is that instead of ip netns exec the nsenter must now be used, at least where mounts matter anyway.

Create directory for persistent mount namespaces

This needs to be done once after boot, NOT for every namespace

# 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

Create network+mount persistent namespaces

$ 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

Use it

# Bring up the loopback interface, this used to fail.
$ nsenter --net=/run/netns/${NSNAME} --mount=/run/mntns/${NSNAME} \
    ifup lo
# Good.

More testing

The important part now. Moving network interfaces into the namespace. Here using ip is fine.

# 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

Important

This approach works, but instead of using ip netns exec use nsenter --net=... --mount=...!

In order to move the interfaces between namespaces using the 'ip link' still works.

The downside is you don't get the automatic bind mounting of /etc/netns/NETNS_NAME/* that is provided by ip netns exec NETNS_NAME cmd, which is not hard to add manually.

# 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;'

The upside is you get consistent mounts done once, and not on every ip netns exec which can vary if the content of /etc/netns/NETNS_NAME/ changes between invocations.

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.