I create a new process using CLONE_NEWPID and chroot to /mnt/

Outside of that, I create a new netns using the ip netns command.


ip netns add test1

Any idea how can I make the chrooted process, or its children, see this new netns?

Based on what I see on strace, I tried:

touch /mnt/var/run/netns/test1
ip netns exec test1 mount --bind /proc/self/ns/net /mnt/var/run/netns/test1

But didn't work


Note that /var/run is a symlink to /run and should be "resolved" in advance or /mnt/var/run might point back to /run with the various commands.

At first glance this simpler command (rather than OP's which can never work as explained in the last note at the end) from initial namespace should be enough to allow ip netns exec to work from the chrooted pid namespace:

mount --bind /run/netns/test1 /mnt/run/netns/test1

but it isn't. It's not related to the pid namespace, but to chroot. Actually the whole problem is the same when not switching to a new pid namespace at all.

ip netns exec doesn't manipulate only network namespaces, but also has to manipulate mount namespaces and use mount(2) system calls for reasons I explained in this SF Q/A: Cannot create nested network namespace.

Problem: chroot

/proc should be mounted within the chroot for proper operations. With at least /proc mounted from within the chroot, using strace one can see ip netns exec test1 ... will enter a new mount namespace and fail to remount /:

getuid()                                = 0
openat(AT_FDCWD, "/run/netns/test1", O_RDONLY|O_CLOEXEC) = 3
setns(3, CLONE_NEWNET)                  = 0
close(3)                                = 0
unshare(CLONE_NEWNS)                    = 0
mount("", "/", 0x5595a749373f, MS_REC|MS_SLAVE, NULL) = -1 EINVAL (Invalid argument)
write(2, "\"mount --make-rslave /\" failed: "..., 49) = 49
exit_group(-1)                          = ?
+++ exited with 255 +++

The remount fails with EINVAL: because of the chroot, / is not a mount point anymore.

Note that the network namespace part itself doesn't have a problem. So for example doing this from the chrooted side:

nsenter --net=/run/netns/test1 ip link

will succeed and display test1's network interfaces, but will for example not switch relevant entries available in /sys/ like would do ip netns exec (as described in my former link), should those be needed.

Solution: add a bind mount over itself

The fix: bind-mount the chroot target to itself to forcibly turn it into a mount point, preferably in private mode to avoid interactions (especially when systemd turned / shared) before doing the chroot.

I reproduce below a working setup with shell commands.

I'm omitting some boiler plate, like probably mounting /run (aka init's /mnt/run) from a tmpfs etc. but mounting /proc from within the chroot should really be done.

first in term1:

~# unshare --fork --pid bash
~# echo $$
~# mount --bind --make-private /mnt /mnt
~# chroot /mnt bash
/# mount -t proc proc /proc

then in an unrelated term2:

 ~# ip netns add test1
 ~# mkdir -p /mnt/run/netns
 ~# touch /mnt/run/netns/test1
 ~# mount --bind /run/netns/test1 /mnt/run/netns/test1

again in term1:

/# ip netns exec test1 ip link
1: lo: <LOOPBACK> mtu 65536 qdisc noqueue state DOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00


  • as stated before this issue has no relation to the pid namespace. Removing the command unshare --fork --pid bash will fix the same problem when using just a chroot without pid namespace.

  • a mount command should never be run from within an ip netns exec command. Remember that ip netns exec itself creates a temporary mount namespace. Whatever mount is done within will disappear when ip netns exec ends. This is also explained in my previous link. Doing this instead would still fine:

    nsenter --net=/run/netns/test1 mount --bind /proc/self/ns/net /mnt/var/run/netns/test1

    but of course not needed since it's the exact same entity as can be verified by having same inode in the nsfs virtual filesystem:

    ~# stat -f -c %T /run/netns/test1; stat -c %i /run/netns/test1
    ~# nsenter --net=/run/netns/test1 sh -c 'stat -f -L -c %T /proc/self/ns/net; stat -L -c %i /proc/self/ns/net'

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.