responde - hacer ping a nombre de host
Cómo crear múltiples espacios de nombres de red desde una sola instancia de proceso (2)
Estoy utilizando la siguiente función C para crear múltiples espacios de nombres de red desde una única instancia de proceso :
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);
}
Después de que mi proceso crea todos los espacios de nombres y agrego una interfaz de toque a cualquiera de los espacios de nombres de una red (con el comando ip link set tap1 netns ns1
), en realidad veo esta interfaz en todos los espacios de nombres (probablemente, esto es en realidad una sola espacio de nombres que va bajo diferentes nombres).
Pero, si creo múltiples espacios de nombres utilizando múltiples procesos, entonces todo está funcionando bien.
¿Qué podría estar mal aquí? ¿Debo pasar alguna marca adicional a unshare()
para que esto funcione desde una sola instancia de proceso? ¿Existe una limitación de que una sola instancia de proceso no pueda crear múltiples espacios de nombres de red? ¿O hay un problema con la llamada a mount()
, porque /proc/self/ns/net
realidad se monta varias veces?
Actualización: Parece que la función unshare()
crea múltiples espacios de nombres de red correctamente, pero todos los puntos de montaje en /var/run/netns/
realidad hacen referencia al primer espacio de nombres de red que se montó en esa direcotry.
Update2: Parece que el mejor enfoque es separar () otro proceso y ejecutar la función create_namespace () desde allí. De todos modos, me encantaría escuchar una mejor solución que no implique fork () o al menos obtener una confirmación que demuestre que es imposible crear y administrar múltiples espacios de nombres de red desde un solo proceso.
Update3: Puedo crear múltiples espacios de nombres con unshare () usando el siguiente código:
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.
}
Pero después de que el proceso termina y ejecuto ip netns exec a ifconfig -a
y ip netns exec b ifconfig -a
parece que ambos comandos se ejecutaron repentinamente en el espacio de nombres a . Así que el problema real es almacenar las referencias a los espacios de nombres (o llamar a mount () de la manera correcta. Pero no estoy seguro, si esto es posible).
Solo tiene que enlazar mount /proc/*/ns/*
si necesita acceder a estos espacios de nombres desde otro proceso, o necesita un identificador para poder alternar entre los dos. No es necesario utilizar múltiples espacios de nombres de un solo proceso.
- Unshare crea un nuevo espacio de nombres.
- Los clones y bifurcaciones por defecto no crean ningún nuevo espacio de nombres.
- hay un espacio de nombres "actual" de cada tipo asignado a un proceso. Puede ser cambiado por unshare o setns. El conjunto de espacios de nombres (por defecto) es heredado por procesos secundarios.
Cada vez que se abre ( /proc/N/ns/net
), se crea un inodo para este archivo, y todos los open () posteriores devolverán un archivo vinculado al mismo espacio de nombres. Los detalles se pierden en las profundidades de la memoria caché del núcleo.
Además, cada proceso tiene solo una entrada de archivo /proc/self/ns/net
, y el montaje bind no crea nuevas instancias de este archivo proc. Abrir los archivos montados es exactamente lo mismo que abrir /proc/self/ns/net
archivo /proc/self/ns/net
directamente (lo que seguirá apuntando al espacio de nombres al que se apuntó cuando lo abrió por primera vez).
Parece que " /proc/*/ns
" es a medias como esta.
Por lo tanto, si solo necesita 2 espacios de nombres, puede:
- abrir
/proc/1/ns/net
- no compartir
- abrir
/proc/self/ns/net
y cambiar entre los dos.
Para más de 2 puede que tengas que clone()
. Parece que no hay manera de crear más de un archivo /proc/N/ns/net
por proceso.
Sin embargo, si no necesita cambiar entre espacios de nombres en tiempo de ejecución, o compartirlos con otros procesos, puede usar muchos espacios de nombres como este:
- Abra sockets y ejecute procesos para el espacio de nombres principal.
- no compartir
- Abra sockets y ejecute procesos para el segundo espacio de nombres (netlink, tcp, etc.)
- no compartir
- ...
- no compartir
- Abra sockets y ejecute procesos para el espacio de nombres Nth (netlink, tcp, etc.)
Los sockets abiertos mantienen la referencia a su espacio de nombres de red, por lo que no se recopilarán hasta que se cierren los sockets.
También puede usar netlink para mover interfaces entre espacios de nombres, enviando el comando netlink al espacio de nombres de origen y especificando el espacio de nombres dst ya sea por PID o por el espacio de nombres FD (más adelante no tendrá).
Debe cambiar el espacio de nombres del proceso antes de acceder a las entradas /proc
que dependen de ese espacio de nombres. Una vez que el archivo "proc" está abierto, mantiene la referencia al espacio de nombres.
Los espacios de nombres de red son, por diseño , creados con una llamada a clonar , y pueden ser modificados posteriormente por unshare . Tenga en cuenta que incluso si crea un nuevo espacio de nombres de red con no compartir , de hecho, simplemente modifica la pila de la red de su proceso en ejecución. Unshare no puede modificar la pila de la red de otros procesos, por lo que no podrá crear otro solo con Unshare .
Para funcionar, un nuevo espacio de nombres de red necesita una nueva pila de red, por lo que necesita un nuevo proceso. Eso es todo.
La buena noticia es que puede hacerse muy ligero con un clon , see :
Clone () difiere de la tradicional llamada al sistema fork () en UNIX, ya que permite que los procesos padre e hijo compartan o dupliquen recursos de forma selectiva.
Puede desviar solo en esta pila de red (y evitar el espacio de memoria, la tabla de descriptores de archivos y la tabla de manejadores de señales). Su nuevo proceso de red se puede hacer más como un hilo que como una bifurcación real.
Puede manipularlos con el código C o con el kernel de Linux y / o las herramientas LXC.
Por ejemplo, para agregar un dispositivo al nuevo espacio de nombres de red, es tan simple como:
echo $PID > /sys/class/net/ethX/new_ns_pid
Consulte esta página para obtener más información acerca de CLI disponible.
En el lado C, se puede echar un vistazo a la implementación de lxc-unshare. A pesar de su nombre usa clon , como puede ver (lxc_clone está here ). También se puede ver la implementación de LTP , donde el autor ha elegido usar la bifurcación directamente.
EDITAR : Hay un truco que puede usar para hacerlos persistentes, pero aún tendrá que bifurcarse, incluso temporalmente.
Eche un vistazo a este código de ipsource2 (he eliminado la comprobación de errores para mayor claridad):
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)
Si ejecuta este código en un proceso bifurcado, podrá crear un nuevo espacio de nombres de red a voluntad. Para eliminarlos, simplemente puede desmontar y eliminar este enlace:
umount2(netns_path, MNT_DETACH);
if (unlink(netns_path) < 0) [...]
EDIT2: Otro truco (sucio) sería simplemente ejecutar "ip netns add .." cli con el sistema .