C 如何从单个进程实例创建多个网络命名空间

C 如何从单个进程实例创建多个网络命名空间,c,linux,networking,network-programming,linux-namespaces,C,Linux,Networking,Network Programming,Linux Namespaces,我正在使用以下C函数从单个进程实例创建多个网络名称空间: void create_namespace(const char *ns_name) { char ns_path[100]; snprintf(ns_path, 100, "%s/%s", "/var/run/netns", ns_name); close(open(ns_path, O_RDONLY|O_CREAT|O_EXCL, 0)); unshare(CLONE_NEWNET); mou

我正在使用以下C函数从单个进程实例创建多个网络名称空间:

void create_namespace(const char *ns_name)
{
    char ns_path[100];

    snprintf(ns_path, 100, "%s/%s", "/var/run/netns", ns_name);
    close(open(ns_path, O_RDONLY|O_CREAT|O_EXCL, 0));
    unshare(CLONE_NEWNET);
    mount("/proc/self/ns/net", ns_path, "none", MS_BIND , NULL);
}
在我的过程创建所有namspaces并向任何一个网络名称空间添加一个tap接口(使用
ip link set tap1 netns ns1
命令)之后,我实际上在所有名称空间中都看到了这个接口(据推测,这实际上是一个名称不同的单一名称空间)

但是,如果我使用多个进程创建多个名称空间,那么一切都正常工作

这里可能出了什么问题?我是否必须将任何附加标志传递给
取消共享()
,才能从单个流程实例执行此操作?是否存在单个进程实例不能创建多个网络名称空间的限制?或者,
mount()
调用是否有问题,因为
/proc/self/ns/net
实际上已装载多次

更新: 似乎
unshare()
函数正确地创建了多个网络名称空间,但
/var/run/netns/
中的所有装入点实际上都引用了该目录中装入的第一个网络名称空间

更新2: 似乎最好的方法是fork()另一个进程并从那里执行create_namespace()函数。无论如何,我很高兴听到一个更好的解决方案,它不涉及fork()调用,或者至少得到一个确认,证明不可能从一个进程创建和管理多个网络名称空间

更新3: 我可以使用以下代码使用unshare()创建多个名称空间:

int  main() {
    create_namespace("a");
    system("ip tuntap add mode tap tapa");
    system("ifconfig -a");//shows lo and tapA interface
    create_namespace("b");
    system("ip tuntap add mode tap tapb");
    system("ifconfig -a");//show lo and tapB interface, but does not show tapA. So this is second namespace created.
}
但是在进程终止并且我执行
ip netns exec a ifconfig-a
ip netns exec b ifconfig-a
之后,似乎这两个命令都突然在命名空间a中执行。因此,实际的问题是存储对名称空间的引用(或以正确的方式调用mount()。但我不确定这是否可行)。

是通过调用clone创建的,可以在取消共享后对其进行修改。请注意,即使您确实使用unshare创建了一个新的网络名称空间,实际上您只是修改了正在运行的进程的网络堆栈取消共享无法修改其他进程的网络堆栈,因此只能使用取消共享创建另一个进程

为了工作,一个新的网络名称空间需要一个新的网络堆栈,因此它需要一个新的进程。就这些

好消息是,使用克隆,它可以变得非常轻巧:

Clone()不同于UNIX中传统的fork()系统调用,具体如下 它允许父进程和子进程有选择地共享或 重复资源

您只能在此网络堆栈上转移(并避免内存空间、文件描述符表和信号处理程序表)。您的新网络进程可以更像一条线程,而不是真正的fork

您可以使用C代码或Linux内核和/或LXC工具来操作它们

例如,要将设备添加到新的网络名称空间,只需执行以下操作:

echo $PID > /sys/class/net/ethX/new_ns_pid
有关可用CLI的更多信息,请参阅

在C端,我们可以看看lxc取消共享实现。尽管它的名字叫clone,但它还是像你一样使用clone(lxc_clone)。我们还可以看看作者选择直接使用fork的地方

编辑:有一个技巧可以让它们持久化,但你仍然需要分叉,即使是暂时的

查看以下代码(为了清晰起见,我已删除了错误检查):

如果在分叉进程中执行此代码,则可以随意创建新的网络名称空间。要删除它们,您只需umount并删除此绑定:

umount2(netns_path, MNT_DETACH);
if (unlink(netns_path) < 0) [...]
umount2(网络路径,MNT\u分离);
如果(取消链接(网络路径)<0)[……]

EDIT2:另一个(肮脏的)技巧是使用系统执行“ip netns add..”cli

如果需要从另一个进程访问这些名称空间,或者需要获取句柄以便能够在这两个进程之间来回切换,则只需绑定mount
/proc/*/ns/*
。不需要从单个进程使用多个名称空间

  • 取消共享会创建新的命名空间
  • 默认情况下,clone和fork不创建任何新名称空间
  • 每种类型都有一个“当前”命名空间分配给一个进程。它可以通过取消共享或设置来更改。名称空间集(默认情况下)由子进程继承
无论何时打开(
/proc/N/ns/net
),它都会为此文件创建inode, 所有后续的open()将返回绑定到 相同的名称空间。详细信息在内核dentry缓存的深处丢失

此外,每个进程只有一个
/proc/self/ns/net
文件条目,并且 绑定装载不会创建此过程文件的新实例。 打开这些装入的文件与打开完全相同
/proc/self/ns/net
文件(将一直指向 它在您第一次打开时指向的命名空间)

似乎“
/proc/*/ns
”是这样半生不熟的

因此,如果您只需要2个名称空间,您可以:

  • 打开
    /proc/1/ns/net
  • 卸下
  • 打开
    /proc/self/ns/net
并在两者之间切换

如果超过2个,您可能必须
clone()
。似乎没有办法为每个进程创建多个
/proc/N/ns/net
文件

但是,如果您不需要在运行时在名称空间之间切换,也不需要与其他进程共享名称空间,则可以使用以下许多名称空间:

  • 打开套接字并运行主命名空间的进程umount2(netns_path, MNT_DETACH); if (unlink(netns_path) < 0) [...]