2012-05-24 12 views
16

estoy usando siguiente función C para crear varios espacios de nombres de red desde una única instancia de proceso :cómo crear varias espacio de nombres de red de una instancia de proceso único

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 mi proceso crea todos los namspaces y Agrego una interfaz tap a cualquiera de los espacios de nombre de una red (con el comando ip link set tap1 netns ns1), entonces realmente veo esta interfaz en todos los espacios de nombres (presumiblemente, este es en realidad un solo espacio de nombres que tiene diferentes nombres).

Pero, si creo múltiples espacios de nombres mediante el uso de múltiples procesos, entonces todo está funcionando bien.

¿Qué podría estar mal aquí? ¿Debo pasar banderas adicionales al unshare() para que funcione desde una única instancia de proceso? ¿Existe una limitación de que una única instancia de proceso no puede crear múltiples espacios de nombres de red? ¿O hay un problema con la llamada mount(), porque /proc/self/ns/net en realidad está montado varias veces?

Actualización: Parece que unshare() función crea varios espacios de nombres de red correctamente, pero en realidad todos los puntos de montaje en /var/run/netns/ referencia al primer espacio de nombres de red que se montó en el que direcotry.

Update2: Parece que el mejor enfoque es fork() otro proceso y ejecutar la función create_namespace() desde allí. De todos modos, me encantaría escuchar una solución mejor que no involucre fork() llamada 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: soy capaz de crear varios espacios de nombres con dejar de compartir() 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 yo ejecutar ip netns exec a ifconfig -a y ip netns exec b ifconfig -a parece que ambos comandos eran de repente se ejecuta en el espacio de nombres a. Entonces, 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).

Respuesta

10

Solo tiene que vincular el montaje /proc/*/ns/* si necesita acceder a estos espacios de nombres de otro proceso, o si necesita obtener un control para poder alternar entre los dos. No es necesario usar múltiples espacios de nombres de un solo proceso.

  • unshare hace crear nuevo espacio de nombres.
  • clonar y tenedor de forma predeterminada no crear espacios de nombres nuevos.
  • 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 hacemos abierto (/proc/N/ns/net), se crea inodo para este archivo, y todos los posteriores abierta() s se archivo que se une a la mismo espacio de nombres volver. Los detalles se pierden en las profundidades del kernel dentry cache.

Además, cada proceso tiene una sola entrada de archivo /proc/self/ns/net y vinculación de montaje no crea nuevas instancias de este archivo de proceso. Al abrir los archivos montados están exactamente el mismo como apertura /proc/self/ns/net archivo directamente (que seguirá apuntando al espacio de nombre al que señaló la primera vez que lo abrió).

Parece que "/proc/*/ns" está medio cocido de esta manera.

Por lo tanto, si sólo necesita 2 espacios de nombres, puede:

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

y cambiar entre los dos.

Para obtener más de 2 puede que tenga que clone(). Parece que no hay forma de crear más de un archivo /proc/N/ns/net por proceso.

Sin embargo, si usted no necesita cambiar entre espacios de nombres en tiempo de ejecución, o para compartirlos con otros procesos, puede utilizar muchos espacios de nombres como esta:

  • sockets abiertos y los procesos se ejecutan de espacio de nombres principal.
  • unshare
  • sockets abiertos y los procesos de ejecución para el segundo espacio de nombres (enlace de red, TCP, etc)
  • unshare
  • ...
  • unshare
  • sockets abiertos y los procesos se ejecutan de espacio de nombres enésimo (netlink, tcp, etc.)

Los sockets abiertos mantienen una referencia al espacio de nombres de su 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 en el espacio de nombres de origen y especificando el espacio de nombres dst mediante PID o espacio de nombre FD (el último no lo tiene).

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, guarda referencia al espacio de nombres.

17

Network Namespaces son, by design, creado con una llamada a clon, y que puede ser modificado después de dejar de compartir . Tenga en cuenta que incluso si crea un nuevo espacio de nombres de red con unshare, de hecho solo modifica la pila de red de su proceso en ejecución. unshare no puede modificar la pila de red de otros procesos, por lo que no podrá crear otra 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.

buena noticia es que se puede hacer muy ligero, con clon, see:

Clone() difiere de la tradicional tenedor() llamada sistema en UNIX, en que permite que los procesos padre e hijo compartan selectivamente recursos duplicados.

Puede desviar solo en esta pila de red (y evitar el espacio de memoria, la tabla de descriptores de archivos y la tabla de controladores de señal). Su nuevo proceso de red se puede hacer más como un thread que un fork.

Puede manipularlos con código C o con herramientas Kernel de Linux y/o LXC.

Por ejemplo, para añadir un dispositivo al nuevo espacio de nombres de red, es tan simple como:

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

Ver this page para obtener más información sobre la CLI disponible.

En el lado C, se puede echar un vistazo a la implementación lxc-unshare. A pesar de su nombre, usa clon, como can see (lxc_clone es here). También se puede mirar LTP implementation, donde el autor ha elegido usar la horquilla directamente.

EDIT: hay un truco que puede utilizar para hacerlos persistentes, pero igual tendrá que tenedor, incluso temporalmente.

Echa un vistazo a este código de ipsource2 (He quitado 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 se ejecuta este código en un proceso en forma de horquilla, usted será capaz de crear nuevo espacio de nombres en la red será. Con el fin de eliminarlos, puede simplemente umount y elimine este aprieto:

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

Edit2: Otro (sucio) truco sería simplemente para ejecutar "netns IP Añadir .." cli con sistema.

+0

+1, pero podría explicar a qué se refiere con "Tenga en cuenta que no crea un nuevo espacio de nombres de red con unshare"? Vea la actualización # 3, porque mi entendimiento es que unshare() aún puede crear espacios de nombres de red.El clon (CLONE_NEWNET) es algo así como "Voy a crear un nuevo proceso hijo con un nuevo espacio de nombres de red", mientras que no compartir (CLONE_NEWNET) es como "No quiero compartir el espacio de nombres de red con mi proceso padre. uno nuevo.". lxc usa clone(), mientras que iproute2 usa unshare(). –

+0

Trataré de explicar. Usted crea un espacio de nombres de red para su proceso _current_ con * unshare *, pero como los espacios de nombres de red necesitan un PID para vivir, no podrá crear uno nuevo solo con * unshare * para el _same_proceso. – Coren

+0

Veo que señala, pero dejar de compartir() aún puede crear un nuevo espacio de nombres de red (esto necesita una actualización de su respuesta). Además, supongo que el espacio de nombres no necesariamente necesita un PID real donde vivir (por ejemplo, después de ejecutar el comando "ip netns add nsX" el proceso de IP finaliza, pero el espacio de nombres nsX aún permanece). Creo que esta limitación "por qué es imposible crear múltiples espacios de nombres de red a partir de un solo proceso" tiene que ver con el funcionamiento de mount(). –

Cuestiones relacionadas