Para la instalación del escenario se ha creado un imagen Vagrant a la cual se le aprovisionan todo todos los shellscripts necesarios para la instalación/configuración del escenario. Se ha elegido para tener todos las mismas especificaciones, de esta manera, en caso de encontrar errores podremos sacar de una forma más sencilla la trazabilidad de la causa de estos. De no querer hacer uso del metodo de instalación de Vagrant se puede seguir el metodo de instalación nativo.
Hacemos un clone del repositorio:
git clone https://github.com/GAR-Project/project
cd project
Levantamos la máquina virtual:
vagrant up
Nos conectamos a las VM:
vagrant ssh test
vagrant ssh controller
Ya deberíamos tener toda las máquinas configuradas con todas las herramientas necesarias para levantar nuestro escenario de Red con Mininet en la VM test, y Ryu en la VM controller.
Si direra problemas la conexión por ssh a la máquina comprobar que las keys que están en el path .vagrant/machines/test/virtualbox/
tienen como owner el user, y los permisos de solo lectura para el owner de la key.
cd .vagrant/machines/test/virtualbox/
chmod 400 private_key
# También podemos hacerlo así (u,g,o -> user, group, others)
chmod u=r,go= private_key
En vez de usar el gestor de vagrant para realizar la conexión ssh, podemos en una última instancia hacerla nosotros mismos, indicando el path a la private key. Por ejemplo:
ssh -i .vagrant/machines/test/virtualbox/private_key vagrant@10.0.123.2
Al ser un instalación nativa se entiende que el user tiene la máquina virtual ya pre-configurada. Lo ideal sería disponer de dos VM para separa el controlador de la VM donde se emulará la topología de red. Intentar hacer uso de una distribucción Ubuntu 16.04.
Hacemos un clone del repositorio:
git clone https://github.com/GAR-Project/project
cd project
Lanzamos los shellscript de aprovisionamiento, cada uno en su máquina:
# Para instalar Mininet y sus dependencias. Además de telegraf
sudo ./util/install_mininet.sh
sudo ./util/install_telegraf.sh
# Para instalar Ryu y el sistema de Monitorización (Grafana e InfluxDB)
sudo ./util/install_ryu.sh
sudo ./util/install_grafana_influxdb.sh
Nuestro escenario de red viene descrito en el script src/scenario_basic.py
. Mininet hace uso de una API de Python para dar la posibilidad a sus usuarios de automatizar procesos de una manera sencilla, o desarrollar ciertos modulos según la conveniencia de cada cual. Por esto y por muchas cosas más, Mininet es una herramienta altamente flexible y potente para la emulación de redes (Ampliamente utilizada por la comunidad científica).
Para el escenario planteado se ha separado el controlador en una máquina virtual y el core de la red en otra máquina virtual. En Mininet identificamos al controlador, Ryu, por la IP de la máquina virutal dentro de la red privada de VirtualBox y su puero de escucha. Esta separación en dos máquinas distintas, se debe a lo siguiente:
Facilitar el trabajo en equipo, ya que la lógica con AI irá metida directamente en la VM del controlador. De esta forma la integración del trabajo en equipo será más sencilla, independizando el core de Mininet + recolección de datos (telegraf) de la lógica de detección de ataques de DDoS, visualización (Grafana + InfluxDB).
Facilitar el almacenamiento de datos hacia InfluxDB desde telegraf, ya que debido al funcionamiento interno de Mininet puede haber conflictos en la comunicación de estos datos. Más adelante se detallará el funcionamiento básico de Mininet a bajo nivel.
Al separar en dos máquinas con funcionalidades claramente diferenciadas se puede hacer debug de una manera más sencilla ya que podremos identificar de forma clara cual de estos módulos (Mininet Core, Controller) dan problemas.
Para lanzar nuestro escenario deberemos estar conectados con uan terminar a la máquina virtual test
y a la máquina virutal controller
. En primera instancia vamos a levantar el controlador, para ello desde la máquina controller
ejecutamos lo siguiente, es una aplicación que hace un forwarding básico, lo que necesitamos:
ryu-manager ryu.app.simple_switch_13
Una vez levantado el controlador vamos a levantar el core de la red, para ello desde la máquina test
vamos a lanzar el scritp mencionado anteriormente:
sudo python3 scenario_basic.py
Además se puede apreciar que desde nuestra máquina test se nos ha abierto la llamada CLI de Mininet. Es una interfaz de linea de comandos desde la cual se pueden hacer multitud de acciones. A continuación, se detallarán la más útiles.
Ya tendriamos nuestro escenario operativo. Podemos comprobar la conectividad de nuestra red haciendo ping entre los host, por ejemplo:
mininet> h1 ping h3
# Tambien podemos hacer ping entre todos con todos con la orden pingall
mininet> pingall
Resolución ARP entre emisor y receptor del ping, para así poder obtener las MACs y poder conformar los paquetes.
Además, el paquete ICMP (ping-request) va a ser redirigido al controlador para decidir que hacer con el paquete, al no tener un Flow especificado con una regla establecida en los switchs por los cuales va a trasncurrir el ping-request y el ping-reply. De esta manera el controlador cuando le llegue el paquete instanciará una serie de reglas en los switch para que el paquete ICMP sea encaminado de un host a otro.
Como se puede ver, en el stdout del controlador, se indica las ordenes que ha ido instanciando en función de los paquetes que ha procesado. Al final, para el primer paquete va a tener que asumir una demora debido a la resolución ARP y a la resolución de reglas con el controlador. Pero el resto de paquetes ya dispondrán de la MAC del destino y de las reglas instanciadas en los switch intermedios, por lo que su tiempo de demora será ínfimo.
Ya hemos puesto apunto nuestro escenario y hemos verificado que está funcionando correctamente. Ahora detallaremos los comandos más importantes de la CLI de Mininet.
Estos tres comandos se utilizan para lo mismo, salir de la CLI de Mininet y terminar la emulación. El source code de estos tres comandos no difieren mucho, EOF y quit terminan haciendo uso de exit al final, por lo que podríamos decir que son un poco repetitivos.
def do_exit( self, _line ):
"Exit"
assert self # satisfy pylint and allow override
return 'exited by user command'
def do_quit( self, line ):
"Exit"
return self.do_exit( line )
def do_EOF( self, line ):
"Exit"
output( '\n' )
return self.do_exit( line )
El comando dpctl es un comando de utilidad de administración que permite cierto control sobre el switch OpenFlow (ovs-ofctl en el switch OpenvSwitch). Con este comando es posible agregar flujos a la tabla de flujo, consultar las características y el estado de los switchs o limpiar la tabla entre otras muchas cosas. Por ejemplo, anteriormente hicimos un ping entre h1 hacia h3, si consultamos las tablas de flujo podremos comprobar como las reglas han sido instanciadas para este tipo de flujos:
Se puede apreciar como se ha instanciado una regla de ida y otra de vuelta en aquellos switch por los cuales var a fluctuar nuestro ping.
Este comando es muy extenso, y puede que para lo que vamos a realizar en esta práctica no sea del todo necesario, pero sin lugar a duda es uno de los comandos más importantes para entender el funcionamiento interno de los switchs SDN. Para más información, consultar la documentación del mismo:
Estos comandos nos arrojarán información sobre la topología emulada. El comando net nos indicará los nombres de los entes que hay en la topología a emular y sus interfaces. El comando dump además nos indicará el tipo de ente, dirección IP, puerto cuando corresponda, interfaz y el identificador de proceso (pid) del ente.
Estos dos comandos nos permitirán abrir terminales en el nodo indicado. El comando xterm nos permitirá abrir una terminal simple, y el comando gterm nos permitirá abrir una gnome-terminal. Podemos abrir varias terminales a la vez indicando todos los nodos que queremos abrir una terminal en ellos. Más adelante, cuando entremos en el funcionamiento interno de Mininet, se explicarán ciertos detalles sobre donde se están ejecutando este proceso de bash, cabría pensar que este proceso está asilado de nuestra máquina sobre la cual estamos ejecutando Mininet, pero no es así del todo.
# xterm/gterm [node1] [node2]
xterm h1 h6
Estos comandos listarán información relativa a los nodos de la topología. El comando intfs listará toda la información relativa a las interfaces de los nodos. El comando nodes listará todos los nodos de la topología. Por útlimo, el comando ports, utilizado para listar los puertos e interfaces de los switches de la topología.
Mirarlo en con el comando help, o si no preguntarme directamente, no quería que esta parte creciera demasiado. Con los comandos expuestos se entiende que se cubrirán todas las necesidades del proyecto.
Como ya de indicó anteriormente Mininet es una herramienta que utiliza para emular redes SDN. Recalco que es importante saber la diferencia entre la emulación y la simulación. Con la simulación nosotros utilizaremos un software que calcula eventos de un comportamiento esperado, y con la emulación, recreamos todo el escenario en un hardware especifico y vemos como se comporta. Para verlo de una forma más sencilla, podriamos decir que jugar a un videojuego de aviones sería una simulación. Pero por ejemplo, prácticar en una cabina de vuelo a escala 1:1 con mandos reales se podría considerar una emulación.
Una vez entendida la diferencia, pasemos al siguiente paso. ¿Mininet Emula? Si, Mininet emula el comportamiento de una red, para ello reserva recursos en tu máquina para cada elemento de la red a emular. Podría pensarse que que cada elemento de la red es una máquina virtual o un contenedor virtualizado..
Pero no es así, esa solución aunque tiene muchas ventajas ya que asilas completamente al nodo, tiene un contra muy importante que son los recursos que serían utilizados en la máquina host. Por lo que, por ejemplo, no seríamos capaz de emular un red bastante grande en un único equipo debido a la falta de recursos. La solución por la que optaron los desarrolladores de Mininet fue la de virtualizar exclusivamente lo necesario para llevar a cabo la emulación de la Red.
¿Cómo lo hicieron? Haciendo uso de las Network Namespaces.
Una network namespace consiste en una replica lógica de stack de red que por defecto tiene el kernel de Linux, rutas, tablas ARP, Iptables e interfaces de red.
Linux se inicia con un Network namespace por defecto, con su tabla de rutas, con su tabla ARP, con sus Iptables e interfaces de red. Pero también es posible crear más network namespaces no predeterminadas, crear nuevos dispositivos en esos espacios de nombres, o mover un dispositivo existente de un espacio de nombres a otro. Este es un concepto de virtualización bastante complejo que nos proporciona el kernel de linux, no voy a ir más allá
De esta manera, cada elemento de la red tiene su propia network namespace, es decir, cada elemento tiene su propio stack de red e interfaces. Por lo que a nivel de networking como se diría se pueden ver como elementos independientes.
Aunque todos ellos comparten espacio de procesos, espacio de IPCs, mismo sistema de archivos…
Por ejemplo, yo en la máquina host pongo creo un proceso con PID 20483, con el comando sleep. De estar aislados los elementos de la red, esto no podrían ver los procesos de la máquina host, sin embargo la realidad con Mininet es otra.
Esto es algo que hay que asumir cuando se trabaja con Mininet, emulación low-cost
Por eso mismo se decidió sacar al controlador de esta máquina donde iba a correr Mininet, para evitar problemas de by-pass por IPCs desde telegraf hacia la base de datos InfluxDB. Por lo que solo quedaría ver como instalar telegraf y configurar correctamente para que cumpla su funcionamiento según lo acordado.
Si hacemos uso de una terminal, sin X server por ejemplo, para reencaminar el stdout gráfico de la máquina virtual hacia afuera, la herramienta Miniedit no va a correr. Hace uso de tkinter, necesita la variable de entorno $DISPLAY
debidamente configurada.
Si hay problemas al lanzar el escenario probar hacer un clean del enviroment anterior. Normalmente si salimos con la orden quit de la CLI de mininet se debería borrar correctamente, de no ser así siempre podemos limpiar nosotros mismos:
sudo mn -c