# Mininet Internals (II) <div style="text-align: justify"> En esta segunda parte sobre el funcionamiento interno de Mininet, indagaremos sobre la topología a nivel de Kernel recreada por Mininet para levantar nuestro escenario. Por último, explicaremos las distintas formas de levantar servicios en las distintas Network namespaces, necesario para recolectar información con `telegraf`. </div> ## Is Mininet using Network Namespaces? <div style="text-align: justify"> Hemos introducido anteriormente que Mininet hace uso de Network namespaces como método para virtualizar stacks de red independientes entre sí, y así poder emular redes a un coste mínimo, pero, ¿Cómo podemos estar tan seguros de que realmente hace uso de ellas? A continuación, se indican los pasos seguidos para poder verificar si Mininet hace uso de Network namespaces, o no. Lo primero que debemos hacer es levantar el escenario para que así, mininet, cree las Network namespaces que tenga que crear. Además, también podemos levantar el controlador en la máquina controller para comprobar en el último momento que ninguna de las verificaciones realizadas han afectado al funcionamiento de nuestro escenario. ```bash # En la máquina test, para levantar la topolgía. sudo python3 scenario_basic.py # En la máquina controller, para levantar el controlador ryu-manager ryu.app.simple_switch_13 ``` Ahora que ya hemos levantado el escenario deberíamos ser capaces de poder ver si hay Network namespaces en nuestra máquina, para ello haremos uso del pack de herramientas **iproute2**. Dentro de este pack nos quedaremos con la herramienta más famosa, `ip`. La herramienta `ip` se está consolidando en las nuevas distribuciones linux como la herramienta de facto para trabajar en todo lo que a Networking se refiere en un enviroment Linux. En las últimas versiones por ejemplo de Ubuntu, el comando `ifconfig` está empezando a ser sustituido por el pack de herramientas iproute2 (a.k.a `ip`). Esta herramienta tiene muchos módulos, para más información consultar su manual: * Manual herramienta [`ip`](https://linux.die.net/man/8/ip) El módulo que necesitaremos para trabajar con Network namespaces es **netns**, podemos ver todo lo que nos puede ofrecer haciendo `ip netns help`. El comando por excelencia para listar las Network namespaces haciendo uso del módulo netns es el siguiente: ```bash sudo ip netns list ``` Sabiendo el comando para listar Network Namespaces, y habiendo levantado previamente el escenario, vamos a comprobar si de verdad hay Network Namespaces creadas en nuestra máquina: ![netns_list](https://i.imgur.com/yFSdQ1H.png) <br> Oops :joy_cat:, parece que no hay ninguna Network namespace creada, puede que, **¿Mininet no funcione como decíamos anteriormente?** Antes que nada, calmémonos, no hay que reescribir toda la documentación.. <img src="https://i.imgur.com/lBcFDBt.jpg" alt="calm" style="display: block;margin-left: auto; margin-right: auto; width: 50%;"> </div> <br> ### Not today :wink: <div style="text-align: justify"> El problema de que el comando `ip netns list` **no** nos arroje información, es que mininet no está creando el softlink requerido para que la herramienta sea capaz de listar las network namespaces, si leemos la [documentacion](http://man7.org/linux/man-pages/man8/ip-netns.8.html) podemos averiguar que `ip netns list` lee del path `/var/run/netns/` donde se encuentran todas las Network namespaces con nombre. Si has llegado hasta este punto probablemente quieras comprobar que iproute2 lee realmente de donde dice que lee. Podemos sacar una traza del sistema, es decir, recopilar todas las syscalls realizadas por un programa y hacer debug por nosotros mismos. Para ello haremos uso del comando `strace` para más información consultar su [manual](https://linux.die.net/man/1/strace). El comando que emplearemos para sacar la traza de syscalls es el siguiente: ``` sudo starce ip netns list ``` A continuación, el output obtenido (Tratar de hacer zoom en la imagen): ![systemTrace](https://i.imgur.com/pwwmuID.jpg) <br> Fijémonos bien en las cuatro últimas lineas. Si no se viera correctamente en la imagen, son estas lineas: ``` open("/var/run/netns", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = -1 ENOENT (No such file or directory) open("/var/run/netns", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = -1 ENOENT (No such file or directory) exit_group(0) = ? +++ exited with 0 +++ ``` Se va a omitir la primera parte de la traza ya que lo único que hace es, parsear los parámetros introducidos, cargar funciones de librerías dinámicas muy básicas en Linux ( Archivos `*.so`, shared objects, por ejemplo, cache, libc entre otras). Nos quedaremos con las últimas lineas de la traza donde se puede ver perfectamente como trata de hacer un `open`, en modo lectura del directorio, pero este **no existe**. Por lo que podemos afirmar que el comando de `ip netns list` si que funciona correctamente. Pero, entonces, ¿Dónde se encuentran las Network namespaces usadas por Mininet? </div> ### Where are Mininet's Network Namespaces located? :kissing: <div style="text-align: justify"> Bien para responder esta pregunta, debemos entender primero una cosa. La herramienta `ip` con su módulo **netns** actua como wrapper a la hora de trabajar con las Network namespaces. Las Namespaces (hay varios [tipos](http://man7.org/linux/man-pages/man7/network_namespaces.7.html)) tienen una vida finita, es decir, estas viven siempre y cuando estén **referenciadas**. Una namespace puede ser referenciada de **tres** maneras: * Siempre y cuando haya **un proceso corriendo** dentro de esta namespace. * Siempre que haya abierto un descriptor de archivo al fichero de la namespace. (`/proc/{pid}/ns/{tipo_namespace}`) * Siempre que haya un bind mount del fichero (`/proc/{pid}/ns/{tipo_namespace}`) de la namespace en cuestión. Si ninguna de estas condiciones se cumple, la namespace en cuestión es **eliminada**. Si se trata de una namespace del tipo `net` (a.k.a Network Namespace) aquellas interfaces que se encuentre en la namespace en desaparición volverán a la namespace por defecto. Una vez entendido este concepto, debemos recapacitar sobre la naturaleza de las Network namespaces que crea Mininet. Mininet cuando se lanza crea una red emulada, cuando se cierra esta debe desaparecer, este proceso debe ser lo más ligero y rápido posible para así ofrecer una mejor experiencia al usuario. La naturaleza de las necesidades de Mininet nos incita a pensar que la creación y destrucción de las Network namespaces vienen asociadas a la primera condición de refereciación de una namespace. Es decir, no tendría sentido hacer mounts ni softlinks que a posteriori se deberán eliminar, ya que supondría una carga de trabajo bastante significativa para emulaciones de redes grandes y un aumento del tiempo destinado a la limpieza del sistema una vez que la emulación haya terminado. Además, debemos tener en cuenta que hay una tercera condición que es bastante idónea con las necesidades de Mininet, ya que solo es necesario un proceso corriendo por cada Network namespace, y a la hora de limpiar unicamente debemos terminar con los procesos que *sostienen* las Network namepsaces. </div> #### Just a hypothesis? <div style="text-align: justify"> Bien, según el razonamiento expuesto, deberíamos ver varios procesos que son creados a la hora del levantamiento de nuestro escenario en Mininet. Estos procesos deberán tener cada uno un fichero de Network Namespace, `/proc/{pid}/ns/net`, con un **inode** distinto para aquellos procesos que corren en distintas Network namespaces. ¿Por donde empezamos a buscar? Vamos a levantar el escenario si es que no lo habíamos levantado anteriormente, listar todos procesos, y filtrar por el nombre de *mininet*. A ver que encontramos :grimacing:. ``` sudo ps aux | grep mininet ``` ![procesos_netns](https://i.imgur.com/HSacu6V.png) Vaya :hushed:! Sin haber creado ningún proceso asociado a cada nodo de nuestro escenario, ya hay un proceso corriendo una bash asociado a cada elemento del escenario en el inicio de la emulación. Qué curioso... ¿Verdad? Vamos a indagar un poco más. Si inspeccionamos el fichero `/proc/{pid}/ns/net` para cada proceso podremos ver cuales de ellos están en una Network namespace distinta en función del valor que tenga el inode. Por ejemplo, vamos a comprobar los procesos asociados a los Host1 y Host2. ![Host1](https://i.imgur.com/D5sM9kA.png) ![Host2](https://i.imgur.com/huyuxUB.png) Como se puede ver, inode distintos, ficheros distintos, **distintas Network namespaces**. Para que se vea de una forma más evidente vamos a ejecutar un comando para sacar que interfaces están asociadas a cada Network namespace. Para poder inyectar procesos dentro de una namespace haremos uso de la herramienta `nsenter`. Para más información sobre esta herramienta, consultar su [manual](http://man7.org/linux/man-pages/man1/nsenter.1.html). ``` nsenter --target <pid> --net {Comando} ``` ![Host1_intf](https://i.imgur.com/UwohklX.png) ![Host2_intf](https://i.imgur.com/r6Z18ir.png) Si nos fijamos en el comando introducido en cada Network namespace es el mismo, `ip addr show` (a.k.a `ip a s`). Con este comando podremos listar todas las direcciones asignadas a cada interfaz de la Network namespace. El resultado obtenido de la ejecución de cada comando es el esperado, en la Network namespace del **Host1** podemos ver que existe la interfaz `h1-eth0`, y en la Network namespace del **Host2** la interfaz `h2-eth0`. Con esta prueba acabamos concluimos con la existencia de las de las Network namespace de las cuales hace uso Mininet. De forma adicional podemos corroborar nuestra hipótesis cambiando la "*verbosidad*" de nuestro script, donde construimos toda la topología del escenario, [`src/scenario_basic.py`](https://github.com/GAR-Project/project/blob/master/src/scenario_basic.py), podemos cambiar el nivel de `info` por `debug`, y lanzar de nuevo el script. ```python if __name__ == '__main__': #setLogLevel('info') setLogLevel('debug') scenario_basic() ``` ![debug](https://i.imgur.com/ucOXTLm.png) Como se puede ver en la ejecución se crean `veth` (**V**irtual **Eth**ernet devices), y los distintos procesos que *sostendrán* las distintas Network Namespaces. Además, se ha podido comprobar como se hace uso del `tc` (**T**raffic **C**ontroller) para establecer los limites de ancho de banda, y de cola máxima a los enlaces del escenario. </div> ### So, It's possible to use iproute2 with Mininet? :relaxed: <div style="text-align: justify"> La respuesta rápida y sencilla en el estado actual sería que **no**. Siempre podemos hacer uso de la API de Python para correr cosas dentro de un elemento de la red o si no, en última instancia podemos abrir la CLI de Mininet, abrir una xterm y lanzar cosas a mano, o como hemos hecho antes hacer uso de la herramienta `nsenter`. Entonces, ¿No tiene solución :unamused:? Bueno a ver, casi todo tiene solución, de nosotros depende hasta que punto queremos llegar para arreglar las cosas. Veamos como podemos habilitar la Network namespace del **Host1** para que sea visible para `ip netns`. En primera instancia debemos localizar el PID de la bash que sostiene la Network Namespace del Host1. En nuestro caso es la siguiente: ```bash sudo ps aux | grep mininet | grep h1 ``` ![pid_h1](https://i.imgur.com/o4U12CD.png) Una vez conocido el PID del proceso que *sostiene* la Network Namespace del Host1, vamos a crear el directorio `/var/run/netns` en caso de que no esté creado: ```bash # Hacemos uso del parametro -p para que en caso de que exista no nos de errores. mkdir -p /var/run/netns/ ``` Por último, debemos hacer un softlink del fichero de la Network Namespace original en el directorio creado (Que recordemos que es el path de donde lee el comando `ip netns list`). ```bash sudo ln -sfT /proc/<PID>/ns/net /var/run/netns/h1 ``` ![ln](https://i.imgur.com/7n4eMLI.png) Por último, solo nos quedaría probar de nuevo el comando` ip netns list` para ver si es capaz de listar la Network namespace: <img src="https://i.imgur.com/9Sz3fjc.png" alt="funciona_n_n" style="display: block;margin-left: auto; margin-right: auto; width: 50%;"> <br> Algunos dirán que es mágica oscura.. Pero, es solo eso, crear un softlink y saber como funciona cada elemento :wink:. ![iproute2_fixed](https://i.imgur.com/X3pfkdp.png) Como se puede apreciar, el comando es completamente funcional. Se puede ver como somos capaces de listar todas las interfaces de la Network namespace del Host1. Pero como todo, siempre tiene puntos a favor y puntos en contra, al hacer el arreglo de crear un softlink y apagar la emulación con su correspondiente limpieza del sistema (Sobretodo nos importa la eliminación de los procesos que sostenían la Network namespace), nos quedamos un softlink roto apuntando a un sitio que ya no existe, o que ya no es útil. <img src="https://i.imgur.com/sQoxBQn.png" style="display: block;margin-left: auto; margin-right: auto; width: 50%;"> <br> ![broken_softlink](https://i.imgur.com/v3zv7nW.png) Con todo lo expuesto en este apartado se deja a valoración del usuario el hecho de que quiera hacer uso de la herramienta iproute2, o no hacerlo. De hacerlo se recomienda que se desarrolle un script auxiliar de limpieza que limpie aquellos softlinks que estén rotos en el directorio `/var/run/netns` a la hora de terminar la emulación. </div> ## The Big Picture <div style="text-align: justify"> Una vez que ya hemos concluido que Mininet hace uso de Network namespaces y ya sabemos como demostrarlo, vamos a inspeccionar cada una de las Network namespaces para trazar un esquema de como está implementado nuestro escenario a nivel de Kernel. Recordemos como era nuestro escenario: ![Escenario](https://i.imgur.com/kH7kAqB.png) Como se ha podido comprobar los switchs al ser elementos de la red se supondría que estarían aislados en un Network namespace, pero para nuestra sopresa no es así se encuentran en la Network namespace por defecto. ¿Por qué funciona entonces, por que no se produce un by-pass al stack de red por defecto? Esto es así por la propia naturaleza de las veth, que van directas al propio proceso de OVS (En otra guía futura se intentará abordar este tema con una mayor amplitud). <br> ![switchs](https://i.imgur.com/kItm2gA.png) <br> #### ¿Cómo se vería entonces nuestro escenario a nivel de Kernel? ![fin](https://i.imgur.com/Ex8P7zl.png) Por lo que, para correr `telegraf` únicamente en los switchs nos bastaría con lanzarlo en la Network namespace por defecto! Esto lo podremos hacer con un único proceso de `telegraf` ya que las interfaces útiles están todas en la misma Network namespace :relaxed: . </div> ## References * Manual [ip-netns](http://man7.org/linux/man-pages/man8/ip-netns.8.html) * Manual [Network namespaces](http://man7.org/linux/man-pages/man7/network_namespaces.7.html) * Manual [unshare](http://man7.org/linux/man-pages/man1/unshare.1.html) * Manual [nsenter](http://man7.org/linux/man-pages/man1/nsenter.1.html) * Manual [namespaces](http://man7.org/linux/man-pages/man7/namespaces.7.html)