LAB9. 6LoWPAN y simulador Cooja

Introducción y objetivos

Los routers de borde son enrutadores que pueden encontrarse en el borde de una red, encaminando el tráfico de dicha red hacia una segunda red externa. Su función, en definitiva, es conectar una red con otra.

En esta práctica, usaremos el simulador Cooja, del proyecto Contiki-NG, para construir una red de nodos que se comuniquen por 6LoWPAN, usando RPL (Routing Protocol for Low-Power and Lossy Networks) como algoritmo de encaminamiento. Los nodos simulados usan el RTOS de Contiki-NG. Veremos cómo un router de borde puede utilizarse para enrutar tráfico entre una red RPL (una red de sensores simulada) y una red IPv4 externa, siguiendo el siguiente diagrama:

El objetivo de la práctica es ofrecer una visión general sobre cómo desplegar tanto una red RPL con Contiki-NG en el simulador Cooja, así como conseguir hacerla interactuar con una segunda red externa real utilizando la herramienta tunslip.

Instalación de Cooja

Se ofrecen dos alternativas para instalar y usar el simulador Cooja:

  • Instalación mediante contenedores Docker.
  • Uso de una máquina virtual de VirtualBox proporcionada por el profesor.

Alternativa 1: instalación con Docker

A continuación, indicamos los pasos a seguir para realizar la instalación del software necesario en un sistema GNU/Linux. Los detalles de instalación para Windows y macOS están en la getting started guide de Contiki-NG.

Comenzaremos por instalar Docker si no lo tenemos instalado ya, haciendo:

sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Si nuestro usuario no pertenece al grupo Docker, lo añadimos:

sudo usermod -aG docker <your-user>

Después, será necesario reiniciar el sistema para completar la instalación y actualizar la lista de grupos del usuario.

A continuación, descargaremos la imagen Docker de Contiki-NG:

docker pull contiker/contiki-ng

Una vez descargada la imagen, clonaremos el repositorio git de Contiki-NG (por ejemplo, en nuestro directorio home):

git clone https://github.com/contiki-ng/contiki-ng.git
cd contiki-ng
git submodule update --init --recursive

A continuación, crearemos el script $HOME/.local/bin/contiker con permisos de ejecución, el cual usaremos en el futuro para lanzar el contenedor Docker. El contenido de este script será:

#!/bin/bash

export CNG_PATH=$HOME/contiki-ng
xhost +SI:localuser:$(id -un)
docker run --privileged --sysctl net.ipv6.conf.all.disable_ipv6=0 \
  --mount type=bind,source=$CNG_PATH,destination=/home/user/contiki-ng \
  -e DISPLAY=$DISPLAY -e LOCAL_UID=$(id -u $USER) -e LOCAL_GID=$(id -g $USER) \
  -v /tmp/.X11-unix:/tmp/.X11-unix -v /dev/bus/usb:/dev/bus/usb \
  -ti contiker/contiki-ng
xhost -SI:localuser:$(id -un)

En este momento, si añadimos la ruta $HOME/.local/bin a la variable PATH, podemos ejecutar el contenedor de Contiki-NG ejecutando el script contiker y, una vez dentro del contenedor, ejecutar el simulador Cooja:

$ contiker
localuser:user being added to access control list
To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.

user@e2d84745c836:~/contiki-ng$ cooja

La primera vez que lo ejecutemos tardará un poco porque se descargará una serie de archivos Java necesarios para el simulador. Es posible que se produzca un error en la resolución de nombres (DNS). En ese caso, debemos configurar el DNS de Docker:

dockerd --dns 8.8.8.8

Alternativa 2: uso de máquina virtual

Si no tenemos VirtualBox instalado en nuestro equipo, lo primero será instalarlo descargando el instalador desde la página oficial de Oracle VirtualBox.

A continuación, descargaremos la máquina virtual Debian con Contiki-NG instalado desde este enlace de Google Drive. Se trata de un archivo .ova que tendremos que importar en VirtualBox.

Una vez importado, podemos arrancar la máquina virtual (el usuario es user y la contraseña contiki). Para arrancar el simulador, bastará con abrir una terminal y ejecutar el comando cooja. El repositorio de Contiki-NG se encuentra en un directorio con el mismo nombre dentro del home del usuario user.

Código Contiki-NG

En el desarrollo de la práctica utilizaremos algunos de los ejemplos incluidos en la instalación de Contiki-NG (ubicados en el directorio contiki-ng/examples):

  • rpl-border-router/border_router.c: contiene la lógica de enrutamiento del router de borde, que será la raíz del DODAG (Destination-Oriented Directed Acyclic Graph).
  • hello-world/hello-world.c: será ejecutado por el resto de nodos de la red RPL.

Los nodos que ejecuten el código hello-world.c formarán un DAG con el router de borde configurado como raíz. Este router recibirá el prefijo de red a través de una conexión SLIP (Serial Line Interface Protocol) y lo comunicará al resto de nodos de la red RPL para que configuren sus respectivas direcciones IPv6 globales. Una vez recibido el prefijo, el router de borde se configura como raíz del DODAG y envía el prefijo al resto de nodos de la red.

Simulación en Cooja

Para crear una simulación completa en Cooja, arrancamos el simulador usando el siguiente comando:

To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.

user@e2d84745c836:~/contiki-ng$ cooja

Si estamos utilizando la máquina virtual, basta con ejecutar Cooja desde un terminal. De aquí en adelante se pueden ignorar todos los detalles relativos a Docker si hemos optado por usar la máquina virtual.

Si todo ha ido correctamente, debería aparecer la ventana principal del simulador:

A partir de ahora, sigue los pasos indicados para crear una simulación en Cooja.

Ejercicio 1

Documenta con capturas de pantalla tanto la configuración que vas a realizar a continuación como el resultado final que muestre el simulador.

Primero, selecciona la opción File -> New simulation. Selecciona UDGM como modelo de radio para simular las comunicaciones inalámbricas e introduce el nombre de la simulación. Presiona Create y se abrirá la ventana de simulación:

En el menú Motes (mote = nodo de sensor simulado), selecciona Add motes -> Create new mote type y seleccona el tipo de mota Cooja. Luego selecciona como código fuente el archivo del ejemplo para el router de borde: examples/rpl-border-router/rpl-border-router.c:

Pulsa en Compile, luego en Create y añade una única mota de este tipo.

Repite los pasos anteriores para crear 4 motas de tipo Cooja que ejecuten el ejemplo hello-world.c. Distribúyelas por la simulación, asegurándote de que no todas estén al alcance directo del router de borde pero que puedan llegar a este pasando a través de otros nodos que sí estén dentro de su rango:

A continuación, crearemos una conexión SLIP entre la red RPL simulada en Cooja y una máquina externa (ya sea un contenedor Docker o nuestra máquina virtual). Para ello, pulsa en el menú Tools -> Serial Socket (SERVER) y selecciona la mota correspondiente al router de borde (identifícala por su número o tipo):

Aparecerá un mensaje como el de la siguiente figura (observa que indica "Listen port: 60001"). Pulsa Start para activar la conexión SLIP (en este caso, iniciar el socket TCP):

Finalmente, inicia la simulación pulsando Start/Pause en la ventana principal del simulador. Revisa la ventana Network y la salida de las motas en la ventana Mote output.

Asignando el prefijo de red

Como ya se ha comentado, un router de borde actúa como enlace para conectar una red con otra. En este ejemplo, el router de borde se utiliza para establecer una ruta de datos entre la red RPL y una máquina externa (ya sea un contenedor Docker o nuestra máquina virtual). Para ello, utilizaremos la herramienta tunslip6 proporcionada por Contiki-NG en el directorio tools/serial-io, que se puede compilar de la siguiente forma:

make tunslip6

Una vez compilado, ejecutamos el binario resultante para establecer una conexión entre la red RPL y la máquina externa:

sudo ./tunslip6 -a 127.0.0.1 -p 60001 aaaa::1/64

Si la ejecución se ha realizado correctamente, aparecerá una salida similar a la siguiente:

slip connected to ``127.0.0.1:60001''
opened tun device ``/dev/tun0''
ifconfig tun0 inet `hostname` mtu 1500 up
ifconfig tun0 add aaaa::1/64
ifconfig tun0 add fe80::0:0:0:1/64
ifconfig tun0

tun0: flags=4305<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST>  mtu 1500
        inet 127.0.1.1  netmask 255.255.255.255  destination 127.0.1.1
        inet6 fe80::1  prefixlen 64  scopeid 0x20<link>
        inet6 aaaa::1  prefixlen 64  scopeid 0x0<global>
        inet6 fe80::93d:daef:6b56:52a7  prefixlen 64  scopeid 0x20<link>
        unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00  txqueuelen 500  (UNSPEC)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

...

*** Address:aaaa::1 => aaaa:0000:0000:0000
[INFO: BR        ] Waiting for prefix
[INFO: BR        ] Server IPv6 addresses:
[INFO: BR        ]   aaaa::201:1:1:1
[INFO: BR        ]   fe80::201:1:1:1

La herramienta tunslip6 ha creado una interfaz puente tun0 con IPv4 127.0.1.1 y ha enviado, a través de la conexión serie, un mensaje de configuración al router de borde indicándole el prefijo IPv6 deseado para todos los nodos de la red RPL (aaaa::/64). Las dos últimas líneas de la salida anterior indican cuáles son las direcciones IPv6 del router de borde tras recibir el prefijo.

Vuelve al simulador Cooja y observa el mensaje que ha aparecido en la ventana Serial Socket (SERVER) (apartado status).

Verificación de resultados

Es posible verificar la dirección IPv6 del router de borde realizando un ping desde tu contenedor o máquina virtual:

user@e2d84745c836:~/contiki-ng$ ping6 aaaa::201:1:1:1
PING aaaa::201:1:1:1(aaaa::201:1:1:1) 56 data bytes
64 bytes from aaaa::201:1:1:1: icmp_seq=1 ttl=64 time=17.7 ms
64 bytes from aaaa::201:1:1:1: icmp_seq=2 ttl=64 time=44.8 ms
64 bytes from aaaa::201:1:1:1: icmp_seq=3 ttl=64 time=10.4 ms
^C

Así como la IPv6 de cualquier otro nodo de la red. Por ejemplo, para el nodo 5:

user@e2d84745c836:~/contiki-ng$ ping6 aaaa::205:5:5:5
PING aaaa::205:5:5:5(aaaa::205:5:5:5) 56 data bytes
64 bytes from aaaa::205:5:5:5: icmp_seq=1 ttl=61 time=6.56 ms
64 bytes from aaaa::205:5:5:5: icmp_seq=2 ttl=61 time=17.2 ms
64 bytes from aaaa::205:5:5:5: icmp_seq=3 ttl=61 time=6.38 ms
^C

La dirección IPv6 de cada nodo puede obtenerse filtrando en la ventana Mote output según el ID del nodo (mota).

Ejercicio 2

Mientras haces ping a uno de los nodos, vuelve al simulador Cooja y explica lo que ocurre en la ventana Network. Ajusta la velocidad de simulación a 1X para poder seguir la transmisión en tiempo real.

Captura de paquetes para análisis

En el simulador Cooja, desde el menú Tools podemos abrir la ventana de Radio Messages. Esta ventana permite capturar todos los paquetes de la simulación y generar un archivo .pcap para su posterior análisis con Wireshark. Para ello, en el menú Analyzer de la ventana Radio Messages seleccionamos la opción 6LoWPAN Analyzer with PCAP.

A continuación, reiniciamos la simulación anterior pulsando el botón Reload en la ventana principal de Cooja. Esto interrumpirá la comunicación con la máquina externa, por lo que será necesario volver a ejecutar tunslip6.

Ahora estamos listos para volver a simular la red capturando todos los paquetes enviados entre los nodos. Dejamos que la simulación se ejecute durante un tiempo y luego la paramos. Cooja habrá generado un archivo radiolog-<n>.pcap, donde <n> será un número aleatorio, en el directorio desde el que se haya lanzado Cooja.

En Wireshark podemos filtrar los paquetes relacionados con el protocolo RPL y buscar los mensajes Destination Advertisement Object (DAO) que los nodos envían hacia la raíz indicando el nodo que han elegido como padre. Por ejemplo, en la siguiente captura podemos observar que el nodo 3 envía su DAO indicando que escoge al nodo 4 como padre:

Esto es razonable para la topología escogida en la simulación en la que el nodo 3 no tiene al nodo 1 dentro del alcance de radio, pero sí al nodo 4, que está a un salto del nodo raíz:

Ejercicio 3

Crea una red RPL con un router de borde y 10 motas de tipo Cooja. Conéctala a tu red local mediante tunslip6. Asegúrate de que no todos los nodos estén al alcance del router de borde (implementa 3 niveles). Comprueba la conectividad con todas las motas y documenta el proceso.

Ejercicio 4

Con una ejecución de ping activa sobre una mota al alcance directo del router de borde, cambia la posición de dicha mota para que necesite al menos un salto intermedio para llegar a la raíz. Registra el tiempo que tarda RPL en volver a converger el DODAG. Documenta el proceso y tus observaciones.

Ejercicio 5

Captura los mensajes enviados por los nodos de la nueva red en un archivo .pcap. Analiza y reporta el tráfico RPL generado durante el proceso de construcción del DAG. A partir de esta información, deduce la topología de la red, identificando el padre preferente de cada nodo.