Tutorial del simulador de redes ns-3. Capítulo 5

Tutorial del simulador de redes ns-3. Capítulo 5
capítulos 1, 2
capítulo 3
capítulo 4

5 Configuración
5.1 Uso del módulo de registro
5.1.1 Introducción al registro
5.1.2 Habilitación del registro
5.1.3 Añadiendo registro a tu código
5.2 Uso de argumentos de línea de comandos
5.2.1 Sobrescritura de valores predeterminados de atributos
5.2.2 Captura de tus propios comandos
5.3 Uso del sistema de trazado
5.3.1 Trazado ASCII
Análisis de trazas ASCII
5.3.2 Trazado PCAP

Capítulo 5

Configuración

5.1 Uso del módulo de registro

Ya hemos revisado brevemente el módulo de registro de ns-3 al ver el script first.cc. En este capítulo veremos más de cerca las posibles aplicaciones del subsistema de registro.

5.1.1 Introducción al registro

Muchos sistemas grandes soportan algún tipo de herramienta de registro de mensajes, y ns-3 no es una excepción. En algunos casos, sólo se registran los mensajes de error en la 'consola del operador' (que generalmente es stderr en sistemas basados en Unix). En otros sistemas, se pueden mostrar mensajes de advertencia, así como información más detallada. En algunos casos, las herramientas de registro se usan para imprimir mensajes de depuración que pueden rápidamente 'emboscar' la salida.

El enfoque utilizado en ns-3 asume que todos estos niveles de información son útiles, y proporcionamos un enfoque selectivo y multinivel para el registro de mensajes. El registro puede ser deshabilitado completamente, habilitado para componentes individuales o a escala global. Para esto están disponibles niveles de información configurables. El módulo de registro de ns-3 proporciona una forma relativamente sencilla de obtener información útil de tu simulación.

Debes entender que proporcionamos un mecanismo de propósito general — trazado — para extraer datos de tus modelos, que debe ser el preferido para la salida al modelar (para obtener más información sobre nuestro sistema de trazado, consulta la sección del tutorial 5.3). El registro debe ser el método preferido para obtener información de depuración, advertencias, mensajes de error o para una salida rápida de mensajes desde tus scripts o modelos en cualquier momento.

Actualmente en el sistema se han definido siete niveles (tipos) de mensajes de registro en orden ascendente de información.

  • LOG_ERROR — registro de mensajes de error (macro relacionada: NS_LOG_ERROR);
  • LOG_WARN — registro de mensajes de advertencia (macro relacionado: NS_LOG_WARN);
  • LOG_DEBUG — registro de mensajes de depuración relativamente raros (macro relacionado: NS_LOG_DEBUG);
  • LOG_INFO — registro de mensajes informativos sobre el progreso del programa (macro relacionado: NS_LOG_INFO);
  • LOG_FUNCTION — registro de mensajes que describen cada función llamada (dos macros relacionadas: NS_LOG_FUNCTION, utilizada para funciones miembro, y NS_LOG_FUNCTION_NOARGS, utilizada para funciones estáticas);
  • LOG_LOGIC — registro de mensajes que describen el flujo lógico dentro de la función (macro relacionado: NS_LOG_LOGIC);
  • LOG_ALL — registro de todo lo mencionado anteriormente (no hay macro relacionada).
    Para cada tipo (LOG_TYPE), también hay un tipo LOG_LEVEL_TYPE, que, si se utiliza, permite registrar, además de su propio nivel, todos los niveles por encima de él. (Como consecuencia, LOG_ERROR y LOG_LEVEL_ERROR, así como LOG_ALL y LOG_LEVEL_ALL son funcionalmente equivalentes). Por ejemplo, habilitar LOG_INFO permitirá solo los mensajes proporcionados por el macro NS_LOG_INFO, al habilitar LOG_LEVEL_INFO también se incluirán los mensajes proporcionados por los macros NS_LOG_DEBUG, NS_LOG_WARN y NS_LOG_ERROR.

También proporcionamos un macro de registro incondicional, que se muestra siempre, independientemente del nivel de registro o del componente seleccionador.

  • NS_LOG_UNCOND — registro incondicional del mensaje relacionado (sin nivel de registro relacionado).

Cada nivel puede ser solicitado por separado o de manera acumulativa. El registro se puede configurar mediante la variable de entorno sh NS_LOG o mediante la llamada de la función del sistema de registro. Como se mostró anteriormente, el sistema de registro tiene documentación Doxygen y ahora es el momento de revisarla, si aún no lo has hecho.

Ahora que has leído la documentación en detalle, vamos a utilizar este conocimiento para obtener algo de información interesante del ejemplo de script scratch/myfirst.cc, que ya has compilaron.

5.1.2 Habilitación del registro

Vamos a utilizar la variable de entorno NS_LOG para ejecutar algunos registros más, pero primero, solo para orientarte, ejecuta el último script como lo hiciste antes,

$ ./waf --run scratch/myfirst

Deberías ver la salida familiar del primer programa de ejemplo de ns-3.

$ Waf: Entrando en el directorio `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build`
Waf: Saliendo del directorio `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build` 'build'
finalizado con éxito (0.413s)
Se enviaron 1024 bytes a 10.1.1.2
Se recibieron 1024 bytes de 10.1.1.1
Se recibieron 1024 bytes de 10.1.1.2

Resulta que los mensajes 'enviados' y 'recibidos' que ves arriba, en realidad son mensajes registrados de UdpEchoClientApplication y UdpEchoServerApplication. Por ejemplo, podemos pedir a la aplicación cliente que imprima información adicional configurando su nivel de registro mediante la variable de entorno NS_LOG.

A partir de este momento, voy a asumir que estás utilizando un shell similar a sh que usa la sintaxis 'VARIABLE = value'. Si usas un shell similar a csh, tendrás que convertir mis ejemplos a la sintaxis que esos shells requieren, es decir, 'setenv variable value'.

En este momento, la aplicación UDP echo client responde a la siguiente línea de código en scratch/myfirst.cc,

LogComponentEnable("UdpEchoClientApplication", LOG_LEVEL_INFO);

Esto activa el nivel de registro LOG_LEVEL_INFO. Cuando pasamos la bandera de nivel de registro, en realidad estamos habilitando ese nivel y todos los niveles inferiores. En este caso, hemos habilitado NS_LOG_INFO, NS_LOG_DEBUG, NS_LOG_WARN y NS_LOG_ERROR. Podemos aumentar el nivel de registro y obtener más información, sin cambios en el script y sin recompilación, configurando la variable de entorno NS_LOG de la siguiente manera:

$ export NS_LOG=UdpEchoClientApplication=level_all

Así que estamos estableciendo el siguiente valor para la variable NS_LOG en el shell sh:

UdpEchoClientApplication=level_all

El lado izquierdo de la asignación es el nombre del componente que estamos registrando, que queremos configurar, y el lado derecho es la bandera que queremos aplicar para esto. En este caso, vamos a habilitar todos los niveles de depuración en la aplicación. Si ejecutas el script con NS_LOG configurado de esta manera, el sistema de registro de ns-3 tomará los cambios y deberías ver la siguiente salida:

Waf: Entrando en el directorio `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build`
Waf: Saliendo del directorio `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build`
'build' finalizado con éxito (0.404s)
UdpEchoClientApplication:UdpEchoClient()
UdpEchoClientApplication:SetDataSize(1024)
UdpEchoClientApplication:StartApplication()
UdpEchoClientApplication:ScheduleTransmit()
UdpEchoClientApplication:Send()
Se enviaron 1024 bytes a 10.1.1.2
Se recibieron 1024 bytes de 10.1.1.1
UdpEchoClientApplication:HandleRead(0x6241e0, 0x624a20)
Se recibieron 1024 bytes de 10.1.1.2
UdpEchoClientApplication:StopApplication()
UdpEchoClientApplication:DoDispose()
UdpEchoClientApplication:~UdpEchoClient()

La información de depuración adicional proporcionada por la aplicación ahora corresponde al nivel NS_LOG_FUNCTION. Muestra cada caso de llamada a funciones durante la ejecución del script. En general, en las funciones-métodos, se prefiere utilizar (como mínimo)NS_LOG_FUNCTION (this). Utilice NS_LOG_FUNCTION_NOARGS ()
solo en funciones estáticas. Sin embargo, tenga en cuenta que en el sistema ns-3 no hay requisitos para mantener ninguna funcionalidad de registro. La decisión sobre cuánta información se registra queda a discreción del desarrollador del modelo. En el caso de las aplicaciones de eco, hay una gran cantidad de datos de salida disponibles para el registro.

Ahora puede ver el registro de llamadas a funciones que se han realizado desde la aplicación. Si observa detenidamente, notará un dos puntos entre la línea UdpEchoClientApplication y el nombre del método, en el lugar donde podría haber esperado ver el operador de alcance de C++ (::). Esto se ha hecho deliberadamente.

De hecho, no es el nombre de la clase, sino el nombre del componente de registro. Cuando hay una correspondencia entre el archivo fuente y la clase, generalmente este es el nombre de la clase, pero debe entender que en realidad no es el nombre de la clase, y hay un solo dos puntos en lugar de dos. Esta es una forma de ayudarle a conceptualizar la separación del nombre del componente de registro del nombre de la clase.

Sin embargo, en algunos casos puede ser difícil determinar qué método está generando realmente el mensaje de registro. Si observa el texto anterior, podría preguntarse de dónde proviene la línea "Received 1024 bytes from 10.1.1.2". Puede resolver este problema configurando el nivel prefix_func en la variable de entorno NS_LOG. Intente hacer lo siguiente,

$ export 'NS_LOG=UdpEchoClientApplication=level_all|prefix_func'

Tenga en cuenta que las comillas son necesarias, ya que la barra vertical que usamos para indicar la operación OR también es un conector de tuberías de Unix. Ahora, si ejecuta el script, verá que el sistema de registro garantiza que cada mensaje de este registro tenga un prefijo con el nombre del componente.

Waf: Entrando en el directorio `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build`
Waf: Saliendo del directorio `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build`
'build' finalizado exitosamente (0.417s)
UdpEchoClientApplication:UdpEchoClient()
UdpEchoClientApplication:SetDataSize(1024)
UdpEchoClientApplication:StartApplication()
UdpEchoClientApplication:ScheduleTransmit()
UdpEchoClientApplication:Send()
UdpEchoClientApplication:Send(): Enviado 1024 bytes a 10.1.1.2
Recibidos 1024 bytes de 10.1.1.1
UdpEchoClientApplication:HandleRead(0x6241e0, 0x624a20)
UdpEchoClientApplication:HandleRead(): Recibido 1024 bytes de 10.1.1.2
UdpEchoClientApplication:StopApplication()
UdpEchoClientApplication:DoDispose()
UdpEchoClientApplication:~UdpEchoClient()

Ahora puedes ver que todos los mensajes que llegan de la aplicación del cliente UDP Echo están identificados como tales. El mensaje «Received 1024 bytes from 10.1.1.2» ahora está claramente definido como procedente de la aplicación del cliente Echo. El mensaje restante debe proceder de la aplicación del servidor UDP Echo. Podemos incluir este componente introduciendo una lista de componentes, separada por dos puntos, en la variable de entorno NS_LOG.

$ export 'NS_LOG=UdpEchoClientApplication=level_all|prefix_func:
               UdpEchoServerApplication=level_all|prefix_func'

Advertencia: en el ejemplo de texto anterior, necesitarás eliminar el carácter de nueva línea después del dos puntos (:), se utilizó para formatear el documento. Ahora, si ejecutas el script, verás todos los mensajes de registro de las aplicaciones Echo del cliente y servidor. Puede que veas que esto puede ser muy útil durante la depuración.

Waf: Entrando en el directorio `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build`
Waf: Saliendo del directorio `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build`
'build' finalizado exitosamente (0.406s)
UdpEchoServerApplication:UdpEchoServer()
UdpEchoClientApplication:UdpEchoClient()
UdpEchoClientApplication:SetDataSize(1024)
UdpEchoServerApplication:StartApplication()
UdpEchoClientApplication:StartApplication()
UdpEchoClientApplication:ScheduleTransmit()
UdpEchoClientApplication:Send()
UdpEchoClientApplication:Send(): Enviado 1024 bytes a 10.1.1.2
UdpEchoServerApplication:HandleRead(): Recibido 1024 bytes de 10.1.1.1
UdpEchoServerApplication:HandleRead(): Replicando paquete
UdpEchoClientApplication:HandleRead(0x624920, 0x625160)
UdpEchoClientApplication:HandleRead(): Recibido 1024 bytes de 10.1.1.2
UdpEchoServerApplication:StopApplication()
UdpEchoClientApplication:StopApplication()
UdpEchoClientApplication:DoDispose()
UdpEchoServerApplication:DoDispose()
UdpEchoClientApplication:~UdpEchoClient()
UdpEchoServerApplication:~UdpEchoServer()

También puede ser útil a veces poder ver el tiempo de simulación en el que se creó un mensaje de registro. Puedes hacer esto añadiendo o un bit prefix_time:

$ export 'NS_LOG=UdpEchoClientApplication=level_all|prefix_func|prefix_time: UdpEchoServerApplication=level_all|prefix_func|prefix_time'

De nuevo, tendrás que eliminar arriba el carácter de nueva línea. Si ahora ejecutas el script, deberías ver la siguiente salida:

Waf: Entrando en el directorio `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build`
Waf: Saliendo del directorio `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build`
'build' finalizado con éxito (0.418s)
0s UdpEchoServerApplication:UdpEchoServer()
0s UdpEchoClientApplication:UdpEchoClient()
0s UdpEchoClientApplication:SetDataSize(1024)
1s UdpEchoServerApplication:StartApplication()
2s UdpEchoClientApplication:StartApplication()
2s UdpEchoClientApplication:ScheduleTransmit()
2s UdpEchoClientApplication:Send()
2s UdpEchoClientApplication:Send(): Enviado 1024 bytes a 10.1.1.2
2.00369s UdpEchoServerApplication:HandleRead(): Recibido 1024 bytes de 10.1.1.1
2.00369s UdpEchoServerApplication:HandleRead(): Reenviando paquete
2.00737s UdpEchoClientApplication:HandleRead(0x624290, 0x624ad0)
2.00737s UdpEchoClientApplication:HandleRead(): Recibidos 1024 bytes de 10.1.1.2
10s UdpEchoServerApplication:StopApplication()
10s UdpEchoClientApplication:StopApplication()
UdpEchoClientApplication:DoDispose()
UdpEchoServerApplication:DoDispose()
UdpEchoClientApplication:~UdpEchoClient()
UdpEchoServerApplication:~UdpEchoServer()

Tenga en cuenta que el constructor para UdpEchoServer fue llamado durante la simulación a 0 segundos. Esto en realidad ocurre antes del inicio de la simulación, pero este tiempo se muestra como cero segundos. Lo mismo es cierto para el mensaje del constructor UdpEchoClient.

Waf: Entrando en el directorio `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build`
Waf: Saliendo del directorio `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build`
'build' finalizado con éxito (0.418s)
0s UdpEchoServerApplication:UdpEchoServer()
0s UdpEchoClientApplication:UdpEchoClient()
0s UdpEchoClientApplication:SetDataSize(1024)
1s UdpEchoServerApplication:StartApplication()
2s UdpEchoClientApplication:StartApplication()
2s UdpEchoClientApplication:ScheduleTransmit()
2s UdpEchoClientApplication:Send()
2s UdpEchoClientApplication:Send(): Enviado 1024 bytes a 10.1.1.2
2.00369s UdpEchoServerApplication:HandleRead(): Recibido 1024 bytes de 10.1.1.1
2.00369s UdpEchoServerApplication:HandleRead(): Reenviando paquete
2.00737s UdpEchoClientApplication:HandleRead(0x624290, 0x624ad0)
2.00737s UdpEchoClientApplication:HandleRead(): Recibidos 1024 bytes de 10.1.1.2
10s UdpEchoServerApplication:StopApplication()
10s UdpEchoClientApplication:StopApplication()
UdpEchoClientApplication:DoDispose()
UdpEchoServerApplication:DoDispose()
UdpEchoClientApplication:~UdpEchoClient()
UdpEchoServerApplication:~UdpEchoServer()

Recordemos que el script scratch/first.cc inició la aplicación del servidor de ecos un segundo antes de que comenzara la simulación. Ahora puede ver que el método StartApplication del servidor se invoca efectivamente en el primer segundo. También puede notar que el cliente de eco se inicia en el segundo segundo de la simulación, como solicitamos en el script.

Ahora puede seguir el progreso de la simulación al llamar a ScheduleTransmit en el cliente, que llama a Send, desencadenando el callback HandleRead en la aplicación del servidor de ecos. Tenga en cuenta que el tiempo transcurrido para enviar un paquete a través de un enlace de dos puntos es de 3.69 milisegundos. Es evidente que el servidor de ecos registra un mensaje indicando que ha respondido al paquete, y luego, después de la latencia del canal, verá que el cliente de eco recibe el paquete de eco en su método HandleRead.

En esta simulación, ocurre mucho que no es visible para usted. Pero puede rastrear fácilmente todo el proceso habilitando todos los componentes de registro en el sistema. Intente establecer el siguiente valor en la variable NS_LOG,

$ export 'NS_LOG=*=level_all|prefix_func|prefix_time'

El asterisco anterior es un comodín para el componente de registro. Esto activará todos los registros en todos los componentes utilizados en la simulación. No reproduciré la salida aquí (en el momento de escribir este artículo produce 1265 líneas de salida para un solo paquete de eco), pero puede redirigir esta información a un archivo y revisarla en su editor favorito,

$ ./waf --run scratch/myfirst > log.out 2>&1

Personalmente, utilizo esta versión extremadamente detallada de registro cuando tengo un problema y no tengo idea de dónde falló algo. Puedo seguir el progreso del código bastante fácilmente sin establecer puntos de ruptura y depurar línea por línea. Puedo simplemente editar la salida en mi editor favorito y buscar lo que espero, y ver lo que está ocurriendo que no esperaba. Cuando tengo una idea general de lo que está mal, paso al depurador para investigar el problema con más detalle. Este tipo de salida puede ser especialmente útil cuando su script hace algo totalmente inesperado. Si solo se basa en el depurador, puede perder por completo un giro inesperado. El registro hace que esos giros sean evidentes.

5.1.3 Añadiendo registro a tu código

Puedes agregar nuevas entradas a tus simulaciones haciendo llamadas al componente log desde varios macros. Vamos a hacer esto en el script myfirst.cc, que tenemos en el directorio "limpio". Recordemos que definimos en este script el componente de registro:

NS_LOG_COMPONENT_DEFINE ("FirstScriptExample");

Debes saber que puedes habilitar el registro de todos los mensajes de este componente configurando la variable de entorno NS_LOG a diferentes niveles. Continuemos y agreguemos algunas entradas al script. El macro utilizado para agregar mensajes de nivel informativo al registro es NS_LOG_INFO. Agreguemos un mensaje (justo antes de que comencemos a crear los nodos) que te diga que el script está en la etapa de creación de topología ("Creating Topology"). Esto se hace en el siguiente fragmento de código,
Abre scratch/myfirst.cc en tu editor favorito y agrega la línea,
NS_LOG_INFO ("Creating Topology");
justo antes de las líneas,

NodeContainer nodes;
nodes.Create(2);

Ahora compila el script usando , por lo que en vez de lo anterior, el siguiente comando funcionará:, y limpia la variable NS_LOG para desactivar el flujo de registro que habilitamos anteriormente:

$ ./waf
$ export NS_LOG=
Ahora, si ejecutas el script,
$ ./waf --run scratch/myfirst

no verás el nuevo mensaje, ya que el componente de registro asociado (FirstScriptExample) no fue habilitado. Para ver tu mensaje, necesitas habilitar el componente de registro FirstScriptExample con un nivel no inferior a NS_LOG_INFO. Si solo quieres ver este nivel específico de registro, puedes habilitarlo así,

$ export NS_LOG=FirstScriptExample=info

Si ejecuta el script ahora, verá un nuevo mensaje 'Creando Topología'.

Waf: Entrando en el directorio `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Saliendo del directorio `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' finalizado con éxito (0.404s)
Creando Topología
Enviado 1024 bytes a 10.1.1.2
Recibido 1024 bytes de 10.1.1.1
Recibido 1024 bytes de 10.1.1.2

5.2 Uso de argumentos de línea de comandos

5.2.1 Sobrescritura de valores predeterminados de atributos

Otra forma de cambiar el comportamiento de los scripts de ns-3 sin editar y compilar es utilizando argumentos de línea de comandos. Proporcionamos un mecanismo para analizar argumentos de línea de comandos y establecer automáticamente variables locales y globales según los resultados.

El primer paso en el uso del sistema de argumentos de línea de comandos es declarar el analizador de línea de comandos. Esto es bastante sencillo (en su programa principal), como en el siguiente código,

int
main (int argc, char *argv[])
{
...
CommandLine cmd;
cmd.Parse (argc, argv);
...
}

Este simple fragmento de dos líneas es de hecho muy útil por sí mismo. Abre la puerta a la variable global de ns-3 y al sistema de atributos. Vamos a añadir dos líneas de código al inicio de la función principal del script. scratch/myfirst.ccLuego, compilamos el script y lo ejecutamos; al ejecutar, solicitamos ayuda de la siguiente manera,

$ ./waf --run "scratch/myfirst --PrintHelp"

Este comando pedirá Waf ejecutar el script scratch/myfirst y pasarle un argumento de línea de comandos --PrintHelp. Las comillas son necesarias para mostrar para qué programa está destinado el argumento. El analizador de línea de comandos detectará el argumento --PrintHelp y devolerá una respuesta,

Waf: Entrando en el directorio `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Saliendo del directorio `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' finalizado con éxito (0.413s)
TcpL4Protocol:TcpStateMachine()
CommandLine:HandleArgument(): Manejar arg nombre=PrintHelp valor=
--PrintHelp: Imprimir este mensaje de ayuda.
--PrintGroups: Imprimir la lista de grupos.
--PrintTypeIds: Imprimir todos los TypeIds.
--PrintGroup=[grupo]: Imprimir todos los TypeIds del grupo.
--PrintAttributes=[typeid]: Imprimir todos los atributos de typeid.
--PrintGlobals: Imprimir la lista de globales.

Ahora consideremos la opción --PrintAttributes. Ya hemos mencionado el sistema de atributos de ns-3, al estudiar el script first.cc. Vimos las siguientes líneas de código,

PointToPointHelper pointToPoint;
pointToPoint.SetDeviceAttribute ("DataRate", StringValue ("5Mbps"));
pointToPoint.SetChannelAttribute ("Delay", StringValue ("2ms"));

y mencionamos que DataRate de hecho es un atributo PointToPointNetDevice. Vamos a aplicar el analizador de argumentos de línea de comandos para ver los atributos PointToPointNetDevice. La lista de ayuda indica que debemos proporcionar TypeId. Este es el nombre de la clase a la que pertenecen los atributos de interés. En nuestro caso, será ns3::PointToPointNetDevice. Continuemos avanzando, introduzca,

$ .\/waf --run "scratch\/myfirst --PrintAttributes=ns3::PointToPointNetDevice"

El sistema imprimirá todos los atributos de este tipo de dispositivo de red. Verá que entre los atributos de la lista están,

--ns3::PointToPointNetDevice::DataRate=[32768bps]:
La velocidad de datos predeterminada para enlaces punto a punto

Este es el valor predeterminado que se utilizará en el sistema al crear un objeto PointToPointNetDevice. Vamos a redefinir este valor predeterminado usando el parámetro Atributo en PointToPointHelper anterior. Vamos a utilizar los valores predeterminados para dispositivos y canales punto a punto. Para ello, eliminaremos las llamadas SetDeviceAttribute y SetChannelAttribute de myfirst.cc, que tenemos en el directorio limpio.

Su script ahora debería simplemente declarar PointToPointHelper y no realizar ninguna operación de configuración, como se muestra en el siguiente ejemplo,

...
NodeContainer nodes;
nodes.Create (2);
PointToPointHelper pointToPoint;
NetDeviceContainer devices;
devices = pointToPoint.Install (nodes);
...

Siga adelante y cree un nuevo script con Waf (./waf) y regresemos y activemos algún registro de la aplicación del servidor de eco UDP y activemos el prefijo de tiempo.

$ export 'NS_LOG=UdpEchoServerApplication=level_all|prefix_time'

Si ejecuta el script, debería ver la siguiente salida:

Waf: Entrando en el directorio `\/home\/craigdo\/repos\/ns-3-allinone\/ns-3-dev\/build'\nWaf: Saliendo del directorio `\/home\/craigdo\/repos\/ns-3-allinone\/ns-3-dev\/build'\n'build' finalizado con éxito (0.405s)\n0s UdpEchoServerApplication:UdpEchoServer()\n1s UdpEchoServerApplication:StartApplication()\nEnviado 1024 bytes a 10.1.1.2\n2.25732s Recibido 1024 bytes de 10.1.1.1\n2.25732s Reenviando paquete\nRecibido 1024 bytes de 10.1.1.2\n10s UdpEchoServerApplication:StopApplication()\nUdpEchoServerApplication:DoDispose()\nUdpEchoServerApplication:~UdpEchoServer()

Recordemos que la última vez que vimos el tiempo de simulación, el momento en que el servidor de eco recibió el paquete fue a las 2.00369 segundos.

2.00369s UdpEchoServerApplication:HandleRead(): Recibido 1024 bytes de 10.1.1.1

Ahora recibe el paquete a las 2.25732 segundos. Esto se debe a que simplemente hemos restablecido la velocidad de datos del PointToPointNetDevice de cinco megabits por segundo al valor predeterminado de 32768 bits por segundo. Si hubiéramos asignado una nueva DataRate mediante la línea de comandos, podríamos haber acelerado nuevamente nuestra simulación. Haremos esto de la siguiente manera, de acuerdo con la fórmula que implica el elemento de ayuda:

$ .\/waf --run "scratch\/myfirst --ns3::PointToPointNetDevice::DataRate=5Mbps"

Como resultado, el valor del atributo DataRate volverá a cinco megabits por segundo. ¿Te sorprende el resultado? Resulta que para restaurar el comportamiento original del script, también necesitamos establecer la demora del canal correspondiente a la velocidad de la luz. Podemos pedirle al sistema de línea de comandos que imprima los atributos del canal, de la misma manera que lo hicimos para el dispositivo de red:

$ ./waf --run "scratch/myfirst --PrintAttributes=ns3::PointToPointChannel"

Descubriremos que el atributo de la demora del canal se establece de la siguiente manera:

--ns3::PointToPointChannel::Delay=[0ns]:
Demora de transmisión a través del canal

Luego, podemos usar el sistema de línea de comandos para establecer ambos valores predeterminados,

$ ./waf --run "scratch/myfirst
--ns3::PointToPointNetDevice::DataRate=5Mbps
--ns3::PointToPointChannel::Delay=2ms"

en este caso estamos restaurando el tiempo que teníamos cuando establecimos explícitamente DataRate y Delay en el script:

Waf: Entrando en el directorio `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Saliendo del directorio `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' finalizado exitosamente (0.417s)
0s UdpEchoServerApplication:UdpEchoServer()
1s UdpEchoServerApplication:StartApplication()
Se enviaron 1024 bytes a 10.1.1.2
2.00369s Se recibieron 1024 bytes de 10.1.1.1
2.00369s Repitiendo paquete
Se recibieron 1024 bytes de 10.1.1.2
10s UdpEchoServerApplication:StopApplication()
UdpEchoServerApplication:DoDispose()
UdpEchoServerApplication:~UdpEchoServer()

Observe que el paquete es recibido nuevamente por el servidor después de 2.00369 segundos. En realidad, podríamos establecer de esta manera cualquiera de los atributos utilizados en el script. En particular, podríamos establecer valores diferentes a uno para los atributos MaxPackets UdpEchoClient.

¿Cómo usarías esto? Inténtalo. Recuerda que debes comentar la parte donde sobreescribimos el valor predeterminado del atributo y lo establecemos explícitamente MaxPackets en el script. Luego debes recompilar el script. También puedes usar la línea de comandos para obtener ayuda sobre la sintaxis para establecer un nuevo valor predeterminado del atributo. Al entender esto, podrás controlar la cantidad de paquetes mostrados en la línea de comandos. Dado que somos personas meticulosas, nuestra línea de comandos debería verse más o menos así:

$ ./waf --run "scratch/myfirst
--ns3::PointToPointNetDevice::DataRate=5Mbps
--ns3::PointToPointChannel::Delay=2ms
--ns3::UdpEchoClient::MaxPackets=2"

La pregunta natural que surge en este punto es, ¿cómo enterarse de la existencia de todos estos atributos? Nuevamente, el sistema de línea de comandos tiene una función de ayuda en este asunto. Si solicitamos ayuda en la línea de comandos, deberíamos ver:

$ ./waf --run "scratch/myfirst --PrintHelp"
myfirst [Program Arguments] [General Arguments]
General Arguments:
--PrintGlobals: Imprime la lista de globales.
--PrintGroups: Imprime la lista de grupos.
--PrintGroup=[group]: Imprime todos los TypeIds del grupo.
--PrintTypeIds: Imprime todos los TypeIds.
--PrintAttributes=[typeid]: Imprime todos los atributos de typeid.
--PrintHelp: Imprime este mensaje de ayuda.

Si eliges el argumento «PrintGroups», deberías ver una lista de todos los grupos registrados. TypeId. Los nombres de los grupos coinciden con los nombres de los módulos en el directorio fuente (aunque comenzando con letra mayúscula). Imprimir toda la información de una vez sería demasiado extenso, por lo que se ofrece un filtro adicional para imprimir información por grupos. De esta forma, centrándonos nuevamente en el módulo «punto a punto»:

./waf --run "scratch/myfirst --PrintGroup=PointToPoint"
TypeIds en el grupo PointToPoint:
ns3::PointToPointChannel
ns3::PointToPointNetDevice
ns3::PointToPointRemoteChannel
ns3::PppHeader

Aquí puedes encontrar los nombres de TypeId disponibles para buscar atributos, por ejemplo, en
--PrintAttributes = ns3::PointToPointChannel, como se mostró arriba.

Otra forma de conocer los atributos es a través de Doxygen ns-3. Hay una página que enumera todos los atributos registrados en el simulador.

5.2.2 Captura de tus propios comandos

También puedes agregar tus propios hooks a través del sistema de línea de comandos. Esto se hace de manera bastante sencilla usando el método del analizador de línea de comandos. AddValue.
Vamos a usar esta oportunidad para especificar el número de paquetes que deben mostrarse, de una manera completamente diferente. Agreguemos una variable local llamada nPackets en la función main. La estableceremos en uno, para que coincida con nuestro comportamiento predeterminado anterior. Para permitir que el analizador de línea de comandos cambie este valor, necesitamos capturar este valor en el analizador. Hacemos esto agregando una llamada AddValue. Ve y modifica el script scratch/myfirst.cc de modo que comience con el siguiente código,

int
main (int argc, char *argv[])
{
uint32_t nPackets = 1;
CommandLine cmd;
cmd.AddValue("nPackets", "Número de paquetes a imprimir", nPackets);
cmd.Parse (argc, argv);
...

Desplázate hacia abajo hasta el punto en el script donde configuramos el atributo MaxPackets y cámbialo para que esté ajustado a la variable nPackets en lugar de la constante 1, como se muestra a continuación.

echoClient.SetAttribute ("MaxPackets", UintegerValue (nPackets));

Ahora, si ejecutas el script y pasas el argumento --PrintHelp, deberías ver el nuevo argumento de usuario listado en la pantalla de ayuda. Escribe,

$ .\/waf --run "scratch\/myfirst --PrintHelp"
Waf: Entrando en el directorio `\/home\/craigdo\/repos\/ns-3-allinone\/ns-3-dev\/build'
Waf: Saliendo del directorio `\/home\/craigdo\/repos\/ns-3-allinone\/ns-3-dev\/build'
'build' finalizado con éxito (0.403s)
--PrintHelp: Imprimir este mensaje de ayuda.
--PrintGroups: Imprimir la lista de grupos.
--PrintTypeIds: Imprimir todos los TypeIds.
--PrintGroup=[grupo]: Imprimir todos los TypeIds del grupo.
--PrintAttributes=[typeid]: Imprimir todos los atributos de typeid.
--PrintGlobals: Imprimir la lista de globales.
Argumentos del usuario:
--nPackets: Número de paquetes a eco

Si desea cambiar la cantidad de paquetes transmitidos, puede hacerlo configurando el argumento - -nPackets en la línea de comandos.

$ .\/waf --run "scratch\/myfirst --nPackets=2"

Ahora deberías ver

Waf: Entrando en el directorio `\/home\/craigdo\/repos\/ns-3-allinone\/ns-3-dev\/build'
Waf: Saliendo del directorio `\/home\/craigdo\/repos\/ns-3-allinone\/ns-3-dev\/build'
'build' finalizado con éxito (0.404s)
0s UdpEchoServerApplication:UdpEchoServer()
1s UdpEchoServerApplication:StartApplication()
Se enviaron 1024 bytes a 10.1.1.2
2.25732s Se recibieron 1024 bytes de 10.1.1.1
2.25732s Eco del paquete
Se recibieron 1024 bytes de 10.1.1.2
Se enviaron 1024 bytes a 10.1.1.2
3.25732s Se recibieron 1024 bytes de 10.1.1.1
3.25732s Eco del paquete
Se recibieron 1024 bytes de 10.1.1.2
10s UdpEchoServerApplication:StopApplication()
UdpEchoServerApplication:DoDispose()
UdpEchoServerApplication:~UdpEchoServer()

Ahora has enviado dos paquetes. Bastante sencillo, ¿verdad?
Puedes ver que como usuario de ns‑3, puedes usar el sistema de argumentos de la línea de comandos para gestionar valores y atributos globales. Si eres el autor de un modelo, puedes agregar nuevos atributos a tus objetos y estarán automáticamente disponibles para que tus usuarios los configuren a través del sistema de línea de comandos. Si eres el autor de un script, puedes agregar nuevas variables a tus scripts y conectarlas sin problemas al sistema de línea de comandos.

5.3 Uso del sistema de trazado

El propósito de la modelación es generar datos de salida para un examen posterior, y el sistema de trazado de ns‑3 es el mecanismo principal para ello. Dado que ns‑3 es un programa en C++, pueden utilizarse las herramientas estándar para la generación de datos de salida de un programa en C++:

#include <iostream>
...
int main ()
{
...
std::cout << "The value of x is " << x << std::endl;
...
}

Incluso puedes usar el módulo de registro para agregar un poco de estructura a tu solución. Se conocen muchos problemas que surgen con este enfoque y, por lo tanto, para abordar estos problemas hemos proporcionado una subsistema general de trazado de eventos.

Los principales objetivos del sistema de trazado de ns‑3:

  • Para tareas básicas, el sistema de trazado debe permitir al usuario generar un trazado estándar para fuentes populares y seleccionar los objetos que generan el trazado;

  • Los usuarios intermedios deben poder ampliar el sistema de seguimiento para modificar el formato de salida generado o para insertar nuevas fuentes de seguimiento, sin necesidad de modificar el núcleo del simulador;

  • Los usuarios avanzados pueden modificar el núcleo del simulador para agregar nuevas fuentes y receptores de seguimiento. El sistema de seguimiento ns-3 está construido sobre los principios de fuentes y receptores de seguimiento independientes, así como un mecanismo unificado para conectar fuentes a consumidores.

El sistema de seguimiento ns-3 se basa en los principios de fuentes y receptores de seguimiento independientes, así como en un mecanismo unificado para conectar fuentes a receptores. Las fuentes de seguimiento son objetos que pueden señalar eventos que ocurren en la simulación y proporcionar acceso a datos fundamentales de interés. Por ejemplo, una fuente de seguimiento puede indicar cuándo un dispositivo de red recibe un paquete y brindar acceso al contenido del paquete para los receptores de seguimiento interesados.

Las fuentes de seguimiento son inútiles por sí solas, a menos que estén "conectadas" a otras partes del código que realmente hagan algo útil con la información proporcionada por el receptor. Los rastreadores son consumidores de eventos y datos proporcionados por las fuentes de seguimiento. Por ejemplo, se puede crear un receptor de seguimiento que (al estar conectado a la fuente de seguimiento del ejemplo anterior) imprima partes interesantes del paquete recibido.

Una razón válida para esta clara separación es permitir a los usuarios conectar nuevos tipos de receptores a fuentes de seguimiento existentes sin necesidad de editar y recompilar el núcleo del simulador. Así, en el ejemplo anterior, el usuario puede definir un nuevo rastreador en su script y conectarlo a una fuente de seguimiento existente definida en el núcleo de la simulación simplemente editando el script del usuario.

En esta guía, revisaremos algunas fuentes y receptores predefinidos y mostraremos cómo configurarlos con el mínimo esfuerzo por parte del usuario. Consulte la guía de ns-3 o las secciones de instrucciones para obtener información sobre la configuración avanzada de rastreo, incluyendo la extensión del espacio de nombres de rastreo y la creación de nuevas fuentes de rastreo.

5.3.1 Trazado ASCII

ns-3 proporciona funcionalidad auxiliar que ofrece un sistema de rastreo de bajo nivel para ayudarle con los detalles al configurar rastreos de paquetes simples. Si activa esta función, verá la salida en archivos ASCII. Para aquellos familiarizados con la salida de ns-2, este tipo de rastreo es similar a out.tr, que se genera a partir de varios scripts.

Pasemos a la acción y añadamos algunos resultados de rastreo ASCII a nuestro script scratch/myfirst.cc. Justo antes de la llamada a Simulator::Run(), añada las siguientes líneas de código:
AsciiTraceHelper ascii;

pointToPoint.EnableAsciiAll(ascii.CreateFileStream("myfirst.tr"));

Como en muchas otras idiomáticas de ns-3, este código utiliza un objeto auxiliar para crear rastreos ASCII. La segunda línea contiene dos llamadas anidadas al método. El método "interior" CreateFileStream() utiliza la idiomática del objeto anónimo para crear un objeto de flujo de archivo en la pila (sin nombre de objeto) y lo pasa al método llamado. En el futuro, profundizaremos en este asunto, pero lo único que necesita saber en esta etapa es que está creando un objeto que representa un archivo llamado myfirst.tr y pasándolo a ns-3. Confiamos en ns-3 para el manejo del objeto creado durante toda su vida útil, durante la cual se resuelven los problemas relacionados con la poco conocida (intencionada) restricción relacionada con los constructores de copia de los objetos de flujo en C++.

La llamada externa EnableAsciiAll() informa al asistente que desea habilitar el rastreo ASCII en su simulación para todas las conexiones del dispositivo punto a punto y que desea que los receptores de rastreo (especificados) registren información sobre el movimiento de paquetes en formato ASCII.

Para aquellos familiarizados con ns-2, los eventos rastreados son equivalentes a los conocidos puntos de rastreo que registran eventos " + ", " - ", " d " y " r ".
Ahora puede compilar el script y ejecutarlo desde la línea de comandos:

$ ./waf --run scratch/myfirst

Cuántas veces has visto varios mensajes de Waf, y luego "‘build’ finished successfully" (la construcción se completó exitosamente) con algunos mensajes del programa en funcionamiento.

Durante la ejecución, el programa creará un archivo llamado myfirst.tr. Debido a las características del funcionamiento Waf, por defecto el archivo no se crea en el directorio local, sino en el directorio superior del repositorio. Si deseas cambiar la ruta donde se guardan las trazas, puedes especificarlo para Waf con el parámetro --cwd. No hicimos esto, por lo que para ver el archivo de traza ASCII myfirst.tr en tu editor favorito, necesitaremos navegar al directorio superior de nuestro repositorio.

Análisis de trazas ASCII

Hay mucha información en una forma bastante densa, pero lo primero a lo que debes prestar atención es que el archivo consiste en líneas separadas. Esto será claramente visible si amplías la ventana de visualización.

Cada línea en el archivo corresponde a un evento de traza. En este caso estamos trazando eventos en la cola de transmisión, que está presente en cada dispositivo de red punto a punto en la simulación. La cola de transmisión es la cola por la que debe pasar cada paquete para el canal punto a punto. Ten en cuenta que cada línea en el archivo de traza comienza con un solo símbolo (y tiene un espacio después de él). Este símbolo tendrá el siguiente significado:

+: ocurrió una operación de encolado en la cola del dispositivo;
-: ocurrió una operación de extracción en la cola del dispositivo;
d: el paquete fue descartado, generalmente porque la cola estaba llena;
r: el paquete fue recibido por el dispositivo de red.

Examinemos más de cerca la primera línea del archivo de traza. La descompondré en partes (con sangrías para claridad) y el número de línea a la izquierda:

0 +
1 2
2 \/NodeList\/0\/DeviceList\/0\/$ns3::PointToPointNetDevice\/TxQueue\/Enqueue
3 ns3::PppHeader (
4   Protocolo punto a punto: IP (0x0021))
6   ns3::Ipv4Header (
7     tos 0x0 ttl 64 id 0 protocolo 17 offset 0 flags [ninguno]
8     longitud: 1052 10.1.1.1 > 10.1.1.2)
9     ns3::UdpHeader (
10      longitud: 1032 49153 > 9)
11      Payload (size=1024)

La primera sección de este evento de traza extendido (línea 0) es la operación. Aquí tenemos el símbolo +, que corresponde a la operación de encolado para la transmisión. La segunda sección (línea 1) es el tiempo de simulación, expresado en segundos. Puedes recordar que pedimos UdpEchoClientApplication Iniciar el envío de paquetes en dos segundos. Aquí vemos la confirmación de que esto realmente está ocurriendo.

En la siguiente sección del ejemplo de trazado (de la línea 2) se muestra qué fuente de trazado generó este evento (se indica el espacio de nombres de trazado). Puede representar el espacio de nombres de trazado de manera similar al espacio de nombres del sistema de archivos. La raíz del espacio de nombres es NodeList. Esto corresponde a un contenedor, gestionado principalmente en el código de ns‑3. Contiene todos los nodos que se crean en el script. Al igual que el sistema de archivos puede tener directorios en la raíz, en NodeList podemos tener múltiples nodos. Así, la línea /NodeList/0 se refiere al nodo cero en NodeList, que normalmente entendemos como «nodo 0». En cada nodo hay una lista de dispositivos que han sido instalados. Esta lista se encuentra siguiente en el espacio de nombres. Puede ver que este evento de trazado proviene de DeviceList/0, que es el dispositivo cero instalado en el nodo.

La siguiente subcadena, $ ns3 :: PointToPointNetDevice, indica qué dispositivo se encuentra en la posición cero: la lista de dispositivos del nodo cero. Recordemos que la operación +, ubicada en la línea 0, significaba que se había agregado un elemento a la cola de transmisión del dispositivo. Esto se refleja en los últimos segmentos del «camino de trazado»: TxQueue/Enqueue.

Las demás secciones en el trazado deberían ser bastante intuitivas. Las líneas 3-4 indican que el paquete está encapsulado en el protocolo punto a punto. Las líneas 5-7 muestran que el paquete tiene un encabezado de versión IPv4 y se originó en la dirección IP 10.1.1.1 y está destinado a 10.1.1.2. Las líneas 8-9 muestran que este paquete tiene un encabezado UDP y, finalmente, la línea 10 muestra que la carga útil son los esperados 1024 bytes.

La siguiente línea en el archivo de trazado muestra que el mismo paquete fue extraído de la cola de transmisión en el mismo nodo.

La tercera línea en el archivo de trazado muestra que el paquete fue recibido por el dispositivo de red en el nodo con el servidor de eco. He reproducido este evento a continuación.

0 r
1 2.25732
2 /NodeList/1/DeviceList/0/$ns3::PointToPointNetDevice/MacRx
3   ns3::Ipv4Header (
4     tos 0x0 ttl 64 id 0 protocol 17 offset 0 flags [none]
5     longitud: 1052 10.1.1.1 > 10.1.1.2)
6     ns3::UdpHeader (
7       longitud: 1032 49153 > 9)
8       Carga útil (tamaño=1024)

Ten en cuenta que la operación de rastreo ahora es r, y el tiempo de simulación se ha incrementado a 2,25732 segundos. Si seguiste atentamente las instrucciones del tutorial, esto significa que dejaste la tasa de datos de los dispositivos de red y la latencia del canal en sus valores predeterminados. Este tiempo debería ser familiar, ya que ya lo has visto en la sección anterior.

La grabación del espacio de nombres de la fuente de rastreo (línea 2) se ha modificado para reflejar que este evento proviene del nodo 1 (/NodeList/1) и пакет принят источником трассировки (/MacRx). Debería ser bastante fácil rastrear el movimiento del paquete a través de la topología, revisando lo que queda en el archivo de rastreo.

5.3.2 Trazado PCAP

Los auxiliares de dispositivos ns-3 también se pueden usar para generar archivos de rastreo en formato .pcap. El acrónimo pcap (generalmente escrito en minúsculas) se refiere a la captura de paquetes y en realidad es una API que incluye la definición del formato de archivo .pcap. El programa más popular que puede leer y mostrar este formato es Wireshark (anteriormente conocido como Ethereal). Sin embargo, hay muchos analizadores de rastreo de tráfico que utilizan este formato de paquete. Recomendamos a los usuarios utilizar diversas herramientas disponibles para analizar rastreos pcap. En esta guía, nos centraremos en visualizar rastreos pcap utilizando tcpdump.

La activación de la captura del rastreo pcap se realiza con una sola línea de código.

pointToPoint.EnablePcapAll ("myfirst");

Inserta esta línea de código después del código de rastreo ASCII que acabamos de agregar en scratch/myfirst.cc. Ten en cuenta que solo proporcionamos la cadena 'myfirst', y no 'myfirst.pcap' o algo similar. Esto se debe a que el parámetro es un prefijo, no el nombre completo del archivo. Durante la simulación, el asistente creará un archivo de rastreo para cada dispositivo punto a punto. Los nombres de los archivos se construirán usando el prefijo, el número de nodo, el número de dispositivo y el sufijo '.pcap».

Para nuestro ejemplo de escenario, en última instancia veremos archivos con nombres como 'myfirst-0-0.pcap» y «myfirst-1-0.pcap', que son rastreos pcap para el nodo 0-dispositivo 0 y el nodo 1-dispositivo 0, respectivamente. Después de agregar la línea de código para activar el rastreo pcap, puedes ejecutar el script de manera habitual:

$ ./waf --run scratch/myfirst

Si miras en el directorio de nivel superior de tu distribución, deberías ver tres archivos: el archivo de rastreo ASCII myfirst.tr, que estudiamos anteriormente, los archivos myfirst-0-0.pcap y myfirst-1-0.pcap — nuevos archivos pcap que acabamos de generar.

Lectura de la salida con tcpdump

En este momento, la forma más sencilla de ver los archivos pcap es utilizando tcpdump.

$ tcpdump -nn -tt -r myfirst-0-0.pcap
leyendo desde el archivo myfirst-0-0.pcap, tipo de enlace PPP (PPP)
2.000000 IP 10.1.1.1.49153 > 10.1.1.2.9: UDP, longitud 1024
2.514648 IP 10.1.1.2.9 > 10.1.1.1.49153: UDP, longitud 1024
tcpdump -nn -tt -r myfirst-1-0.pcap
leyendo desde el archivo myfirst-1-0.pcap, tipo de enlace PPP (PPP)
2.257324 IP 10.1.1.1.49153 > 10.1.1.2.9: UDP, longitud 1024
2.257324 IP 10.1.1.2.9 > 10.1.1.1.49153: UDP, longitud 1024

En el volcado myfirst-0-0.pcap (en el dispositivo cliente) puedes ver que el paquete de eco se envía después de 2 segundos de simulación. Si miras el segundo volcado (myfirst-1-0.pcap), verás que el paquete es recibido en el momento 2,257324 segundos. Verás en el segundo volcado que el paquete se devuelve en el momento 2.257324 segundos, y finalmente que el paquete fue recibido por el cliente en el primer volcado en el momento 2.514648 segundos.

Lectura de la salida con Wireshark

Si no estás familiarizado con Wireshark, hay un sitio web desde el cual puedes descargar programas y documentación: http://www.wireshark.org/. Wireshark — es una interfaz gráfica de usuario que se puede utilizar para visualizar estos archivos de trazado. Si tienes Wireshark, puedes abrir cualquiera de los archivos de trazado y mostrar el contenido como si hubieras capturado paquetes usando un analizador de paquetes.

Fuente: habr.com

Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS 🔥 Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS | ProHoster