El sistema de trazas de NS3

Una vez que hemos creado una simulación necesitamos analizar los resultados de dicha simulación. Cómo sacar los resultados de una simulación es responsabilidad del sistema de trazas de NS3. El sistema de trazas de NS3 genera, como veremos a continuación, una serie de archivos en un formato determinado que nos muestra todos los resultados de nuestra simulación. Estos formatos están definidos en los módulos existentes en NS3 y podemos añadirlos a nuestros nuevos módulos. En esta entrada vamos a centrarnos en sacar información de los módulos existentes mediante un formato determinado (pcap).

Existen dos formatos que podemos generar, archivos con extensión .tr que es heredado del simulador NS2 y archivos con extensión .pcap que es un estándar para guardar paquetes de red. Por supuesto, al ser un archivo en C++ podemos generar resultados en otros formatos, pero supondría acceder a la información directamente lo cual es más complejo e ineficiente.

No obstante, personalmente recomiendo el uso de pcap, ya que es un estándar abierto soportado por multitud de herramientas que podemos utilizar para analizar los resultados de nuestra simulación. Por ejemplo tenemos wireshark si queremos analizar el diálogo de un protocolo en nuestra simulación o scapy si queremos generar gráficas específicas mediante scripts python.

Los módulos mas relevantes de NS3 ya tienen implementados un asistente para generar trazas. Estos asistentes o Helpers nos permiten generar de forma fácil las trazas más relevantes. Vamos a generar trazas de nuestro ejemplo básico para ver cómo funciona. Tomamos nuestro basic-iot-sensors.cc y vamos a generar un archivo pcap por cada interfaz creada de nuestros tres nodos (Recordar que un nodo puede tener varias interfaces).

En principio, con habilitar la traza pcap en la capa física de wifi nos vale con lo que nos basta añadir:

wifiPhy.EnablePcapAll("resultados");

Si compilamos nuestro ejemplo de nuevo veremos que se han generado archivos pcap por cada interfaz con el formato resultados-NodeId-DeviceId.pcap. En nuestro caso tendríamos tres nodos con una única interfaz por lo que se generan los archivos resultados-0-0.pcap, resultados-1-0.pcap y resultados-2-0.pcap.
Captura wireshark

Todas las clases que heredan de la clase PcapHelperForDevice tiene este mecanismo para habilitar el pcap en todas las interfaces (Devices). Estas clases son (imagen extraída de la documentación de la clase PcapHelperForDevice):
Captura wireshark

Si se está interesado en cambios de estado de variables concretas, definidas como traceables dentro del modulo. Esto es, se pueden monitorizar sus cambios de estado, podemos definir funciones que se invoquen cuando hay un cambio en el valor de una variable. En la entrada relativa a observar el consumo de energía vemos un ejemplo de cómo observar este tipo información relativa a variables concretas (e.g. nivel de carga de una batería). Identificarás en las clases qué variables pueden ser traceadas por que son declaradas mediante una plantilla TracedValue

NS3: La clase NetDevice (Wifi)

La clase NetDevice representa una interfaz de red que va asociada a una tecnología. Es una clase abstracta de la cual heredamos para crear una interfaz de red de una tecnología asociada. Básicamente necesitamos tener en cuenta la capa MAC, la capa física y el canal.

Vamos a ver un ejemplo con la conocida tecnología WiFi. Cada módulo de tecnología de comunicación se estructura de forma distinta atendiendo al estándar, en el caso del módulo WiFi implementa muchas de las tecnologías del estándar IEEE 802.11 (802.11a, 802.11b, etc.) incluyendo los últimos estándares 802.11ac y 802.11ax así como algunas versiones especiales como 802.11p para redes vehiculares.

La clase YansWifiPhy implementa la capa física del estándar 802.11a en la cual tienes parámetros configurables como la frecuencia, la anchura del canal, el número del canal, configuraciones MIMO, etc. que son las propiedades de la clase.

Cabe destacar las variables TxGain y RxGain que expresan la ganancia en decibelios a la hora de transmitir y recibir respectivamente. Con estas variables podemos simular la antena de dicha interfaz.

Para conectar objetos de la clase YansWifiPhy necesitas un canal que simule cómo se comporta una transmisión WiFi con los parámetros que hayas configurado. La clase YansWifiChannel tiene este propósito. Realmente lo que hace este canal es configurar un ns3::PropagationLossModel y un ns3::PropagationDelayModel y aplicarlo a toda transmisión/recepción de un objeto YansWifiPhy. Como su propio nombre indica, esas clases modelan las perdidas y el retardo para la transmisión configurada. Es responsabilidad del programador configurar el canal con los modelos apropiados.

De la capa MAC definida en el estándar se encarga la clase WifiMac. Cuando creas un objeto de la clase WifiMac debes configurarle un objeto de la clase WifiPhy con el que interacciona. Puedes establecer con WifiMac parámetros para estándares específicos. Con funciones privadas, existen funciones para configurar estándares precisos, por ejemplo ns3::WifiMac::Configure80211a o Configure80211ax_2_4Ghz. En la lista de métodos privados de WifiMac tienes la lista completa.

Una vez tenemos el objeto de WifiMac y WifiPhy (por ejemplo YansWifiPhy) con su canal asociado, podemos construir el equivalente a la interfaz de rede Wifi con un NetDevice. Lo normal es utilizar un asistente ya que generalmente, tendremos que crear un NetDevice por cada nodo e instalarlo en dicho nodo. El asistente para este proceso es la clase WifiHelper. Una de las formas más comunes de usarlo es llamar al método install con un objeto WifiPhy, un objeto WifiMac y un NodeContainer. Este método creará un contenedor de objetos DeviceNet (NetDeviceContainer) asociados a cada nodo en el NodeContainer y con los correspondientes objetos instalados.

La clase que hereda de la clase NetDevice para wifi es la clase WifiNetDevice
Si echamos un vistazo a sus métodos, tenemos los métodos de configuración:

void SetMac (const Ptr< WifiMac > mac)
bool SetMtu (const uint16_t mtu)
void SetNode (const Ptr< Node > node)
void SetPhy (const Ptr< WifiPhy > phy)

Como hemos dicho anteriormente, la clase WifiHelper nos ayuda con esta configuración cuando tenemos que hacerlo con varios nodos.
Resumiendo, el esquema de recepción de un paquete con esta configuración sería, por lo tanto:
Channel -> WifiPhy -> WifiMac -> WifiNetDevice -> Node

En otra entrada analizaremos un ejemplo simplificado para ver cómo se configura un escenario simple. No obstante en el directorio examples de la instalación de NS3 hay un directorio wireless con ejemplos de Wifi y otras tecnologías inalámbricas.