How to create multiple network namespace from a single process instance

半腔热情 提交于 2019-11-29 20:52:19
juris

You only have to bind mount /proc/*/ns/* if you need to access these namespaces from another process, or need to get handle to be able to switch back and forth between the two. It is not needed to use multiple namespaces from a single process.

  • unshare does create new namespace.
  • clone and fork by default do not create any new namespaces.
  • there is one "current" namespace of each kind assigned to a process. It can be changed by unshare or setns. Set of namespaces (by default) is inherited by child processes.

Whenever you do open(/proc/N/ns/net), it creates inode for this file, and all subsequent open()s will return file that is bound to the same namespace. Details are lost in the depths of kernel dentry cache.

Also, each process has only one /proc/self/ns/net file entry, and bind mount does not create new instances of this proc file. Opening those mounted files are exactly the same as opening /proc/self/ns/net file directly (which will keep pointing to the namespace it pointed to when you first opened it).

It seems that "/proc/*/ns" is half-baked like this.

So, if you only need 2 namespaces, you can:

  • open /proc/1/ns/net
  • unshare
  • open /proc/self/ns/net

and switch between the two.

For more that 2 you might have to clone(). There seems to be no way to create more than one /proc/N/ns/net file per process.

However, if you do not need to switch between namespaces at runtime, or to share them with other processes, you can use many namespaces like this:

  • open sockets and run processes for main namespace.
  • unshare
  • open sockets and run processes for 2nd namespace (netlink, tcp, etc)
  • unshare
  • ...
  • unshare
  • open sockets and run processes for Nth namespace (netlink, tcp, etc)

Open sockets keep reference to their network namespace, so they will not be collected until sockets are closed.

You can also use netlink to move interfaces between namespaces, by sending netlink command on source namespace, and specifying dst namespace either by PID or namespace FD (the later you don't have).

You need to switch process namespace before accessing /proc entries that depend on that namespace. Once "proc" file is open, it keeps reference to the namespace.

Network Namespaces are, by design, created with a call to clone, and it can be modified after by unshare. Take note that even if you do create a new network namespace with unshare, in fact you just modify network stack of your running process. unshare is unable to modify network stack of other processes, so you won't be able to create another one only with unshare.

In order to work, a new network namespace needs a new network stack, and so it needs a new process. That's all.

Good news is that it can be made very lightweight with clone, see:

Clone() differs from the traditional fork() system call in UNIX, in that it allows the parent and child processes to selectively share or duplicate resources.

You are able to divert only on this network stack (and avoid memory space, table of file descriptors and table of signal handlers). Your new network process can be made more like a thread than a real fork.

You can manipulate them with C code or with Linux Kernel and/or LXC tools.

For instance, to add a device to new network namespace, it's as simple as:

echo $PID > /sys/class/net/ethX/new_ns_pid

See this page for more info about CLI available.

On the C-side, one can take a look at lxc-unshare implementation. Despite its name it uses clone, as you can see (lxc_clone is here). One can also look at LTP implementation, where the author has chosen to use fork directly.

EDIT: There is a trick that you can use to make them persistent, but you will still need to fork, even temporarily.

Take a look at this code of ipsource2 (I have removed error checking for clarity):

snprintf(netns_path, sizeof(netns_path), "%s/%s", NETNS_RUN_DIR, name);

/* Create the base netns directory if it doesn't exist */
mkdir(NETNS_RUN_DIR, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);

/* Create the filesystem state */
fd = open(netns_path, O_RDONLY|O_CREAT|O_EXCL, 0);
[...]
close(fd);
unshare(CLONE_NEWNET);
/* Bind the netns last so I can watch for it */
mount("/proc/self/ns/net", netns_path, "none", MS_BIND, NULL)

If you execute this code in a forked process, you'll be able to create new network namespace at will. In order to delete them, you can simply umount and delete this bind:

umount2(netns_path, MNT_DETACH);
if (unlink(netns_path) < 0) [...]

EDIT2: Another (dirty) trick would be simply to execute "ip netns add .." cli with system.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!