Как создать несколько сетевых пространств имен из одного экземпляра процесса

Я использую следующую функцию C для созданияmultiple network namespaces изsingle process instance:

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);
}

После того, как мой процесс создает все пространства имен, и я добавляюtap интерфейс к любому из одного сетевого пространства имен (сip link set tap1 netns ns1 команда), тогда я на самом деле вижу этот интерфейс во всех пространствах имен (предположительно, это фактически одно пространство имен, которое идет под разными именами).

Но если я создаю несколько пространств имен, используя несколько процессов, то все работает просто отлично.

Что здесь может быть не так? Нужно ли передавать какие-либо дополнительные флагиunshare() получить это работает из одного экземпляра процесса? Есть ли ограничение, что один экземпляр процесса не может создавать несколько сетевых пространств имен? Или есть проблема сmount() позвони, потому что/proc/self/ns/net на самом деле монтируется несколько раз?

Update: Кажется, чтоunshare() Функция правильно создает несколько сетевых пространств имен, но все точки монтирования в/var/run/netns/ фактически ссылка на первое сетевое пространство имен, которое было смонтировано в этой директории.

Update2: Кажется, что лучший подход - это запустить fork () другого процесса и выполнить оттуда функцию create_namespace (). В любом случае, я был бы рад услышать лучшее решение, которое не включает вызов fork () или хотя бы получить подтверждение, которое доказало бы, что невозможно создать и управлять несколькими сетевыми пространствами имен из одного процесса.

Update3: Я могу создать несколько пространств имен с помощью 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 (). Но я не уверен, возможно ли это).

Ответы на вопрос(2)

Решение Вопроса

/proc/*/ns/* если вам нужно получить доступ к этим пространствам имен из другого процесса, или вам нужно получить дескриптор, чтобы иметь возможность переключаться между ними. Нет необходимости использовать несколько пространств имен из одного процесса.

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.

Всякий раз, когда вы открываете (/proc/N/ns/net), он создает индекс для этого файла, и все последующие open () вернут файл, связанный с то же пространство имен. Детали теряются в глубине кеша ядра дентри.

Кроме того, каждый процесс имеет только один/proc/self/ns/net запись файла и bind mount не создает новые экземпляры этого файла proc. Открытие этих смонтированных файловexactly the same как открытие /proc/self/ns/net файл (который будет продолжать указывать на пространство имен, на которое оно указывало при первом его открытии).

Кажется, что & quot;/proc/*/ns& Quot; наполовину испечен, как это.

Итак, если вам нужно только 2 пространства имен, вы можете:

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

и переключаться между двумя.

Для более чем 2 вам, возможно, придетсяclone(), Кажется, нет способа создать более одного/proc/N/ns/net файл на процесс.

Однако, если вам не нужно переключаться между пространствами имен во время выполнения или делиться ими с другими процессами, вы можете использовать множество пространств имен, например:

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)

Открытые сокеты сохраняют ссылку на свое сетевое пространство имен, поэтому они не будут собираться до тех пор, пока сокеты не будут закрыты.

Вы также можете использовать netlink для перемещения интерфейсов между пространствами имен, отправив команду netlink в исходное пространство имен и указав пространство имен dst либо по PID, либо по пространству имен FD (чем позже вы не обладаете).

Вам необходимо переключить пространство имен процесса перед доступом/proc записи, которые зависят от этого пространства имен. Однажды "процесс" файл открыт, он хранит ссылку на пространство имен.

Пространства имен сети являются,по дизайну, созданный с призывом кcloneи может быть изменено послеunshare, Обратите внимание, что даже если вы создадите новое пространство имен сети сunshareфактически вы просто модифицируете сетевой стек вашего запущенного процесса.unshare не может изменить сетевой стек других процессов, поэтому вы не сможете создать другой только сunshare.

Для работы новому сетевому пространству имен нужен новый сетевой стек, и поэтому ему нужен новый процесс. Вот и все.

Хорошей новостью является то, что его можно сделать очень легкимclone, увидеть:

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.

Вы можете переключаться только в этом сетевом стеке (и избежать пространства памяти, таблицы файловых дескрипторов и таблицы обработчиков сигналов). Ваш новый сетевой процесс можно сделать более похожим наthread чем настоящийfork.

Вы можете манипулировать ими с помощью кода C или инструментов Linux Kernel и / или LXC.

Например, чтобы добавить устройство в новое пространство имен сети, достаточно просто:

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

Увидетьэта страница для получения дополнительной информации о CLI доступны.

Со стороны C можно взглянуть на реализацию lxc-unshare. Несмотря на свое название, оно используетclone, как и тыможно увидеть (lxc_clone являетсяВот). Можно также посмотреть наРеализация LTP, где автор решил использовать форк напрямую.

EDIT: Есть хитрость, которую вы можете использовать, чтобы сделать их постоянными, но вам все равно придется раскошелиться, даже временно.

Посмотрите на этот кодipsource2 (Я убрал проверку ошибок для ясности):

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)

Если вы выполните этот код в разветвленном процессе, вы сможете по желанию создавать новое пространство имен сети. Чтобы удалить их, вы можете просто размонтировать и удалить эту привязку:

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

EDIT2: Еще один (грязный) трюк - просто выполнить «ip netns add ..». Cli сsystem.

 01 июн. 2012 г., 23:38
Я попытаюсь объяснить. Вы создаете сетевое пространство имен для вашегоcurrent процесс сunshare, но поскольку сетевым пространствам имен требуется PID для жизни, вы не сможете создать новое только сunshare дляsame процесс.
 02 июн. 2012 г., 11:40
Они сохраняют свое сетевое пространство имен, используя эту замечательную функцию ядра Linux, об удаленных, но все еще используемых файлах, так как они все еще открываются запущенным процессом.
 02 июн. 2012 г., 11:37
Если вы посмотрите наiproute2 source codeвы увидите, что они сохраняют текущий сетевой стек даже после того, как процесс завершился с помощью трюка монтирования:/* Bind the netns last so I can watch for it */ if (mount("/proc/self/ns/net", netns_path, "none", MS_BIND, NULL) < 0)
 Hans Solo01 июн. 2012 г., 19:48
+1, но не могли бы вы объяснить, что вы имеете в виду с помощью & quot; Обратите внимание, что вы не создаете новое пространство имен сети с unshare & quot ;? Смотрите обновление № 3, потому что, насколько я понимаю, unshare () все еще может создавать сетевые пространства имен. Клон (CLONE_NEWNET) - это что-то вроде "Я собираюсь создать новый дочерний процесс с новым сетевым пространством имен", а unshare (CLONE_NEWNET) - это как "Я больше не хочу делиться сетевым пространством имен с моим родительским процессом". Так что создайте новый. & Quot ;. lxc использует clone (), а iproute2 использует unshare ().
 Hans Solo01 июн. 2012 г., 23:54
Я понимаю, что вы указали, но unshare () все еще может создать новое пространство имен сети (это требует обновления вашего ответа). Кроме того, я предполагаю, что пространству имен не обязательно нужен фактический PID, в котором он будет жить (например, после выполнения команды «ip netns add nsX» процесс ip завершается, но пространство имен nsX по-прежнему остается). Я предполагаю это ограничение "почему невозможно создать несколько сетевых пространств имен из одного процесса" должен сделать что-то с тем, как работает mount ().

Ваш ответ на вопрос