Tutorial do simulador de rede ns-3. Capítulo 5

Tutorial do simulador de rede ns-3. Capítulo 5
Capítulo 1,2
capítulo 3
capítulo 4

5 configurações
5.1 Usando o módulo de registro
5.1.1 Visão geral do registro
5.1.2 Habilitar registro
5.1.3 Adicionando log ao seu código
5.2 Usando argumentos de linha de comando
5.2.1 Substituindo valores de atributos padrão
5.2.2 Capturando seus próprios comandos
5.3 Usando o sistema de rastreamento
5.3.1 Rastreamento ASCII
Analisando rastreamentos ASCII
5.3.2 Rastreamento PCAP

Capítulo 5

Fixação

5.1 Usando o módulo de registro

Já analisamos brevemente o módulo de registro do ns-3 observando o script primeiro.cc. Neste capítulo, examinaremos mais de perto os possíveis usos do subsistema de registro.

5.1.1 Visão geral do registro

Muitos sistemas grandes suportam algum tipo de recurso de registro de mensagens, e o ns-3 não é exceção. Em alguns casos, apenas mensagens de erro são gravadas no "console do operador" (que geralmente é stderr em sistemas baseados em Unix). Em outros sistemas, podem ser exibidas mensagens de aviso, bem como informações mais detalhadas. Em alguns casos, as ferramentas de registro são usadas para gerar mensagens de depuração que podem desfocar rapidamente a saída.

O subHRD usado no ns-3 assume que todos esses níveis de conteúdo de informação são úteis e fornecemos uma abordagem seletiva e em camadas para o registro de mensagens. O registro em log pode ser desabilitado completamente, habilitado por componente ou globalmente. Para tanto, são utilizados níveis ajustáveis ​​de conteúdo de informação. O módulo de registro do ns-3 fornece uma maneira relativamente simples de obter informações úteis da sua simulação.

Você deve entender que fornecemos um mecanismo de uso geral - rastreamento - para extrair dados de seus modelos, que deve ser a saída preferida para simulações (para obter mais informações sobre nosso sistema de rastreamento, consulte a seção 5.3 do tutorial). O registro em log deve ser o método preferido para obter informações de depuração, avisos, mensagens de erro ou para gerar rapidamente mensagens de seus scripts ou modelos a qualquer momento.

Atualmente, o sistema define sete níveis (tipos) de mensagens de log em ordem crescente de conteúdo de informação.

  • LOG_ERROR - registrando mensagens de erro (macro relacionada: NS_LOG_ERROR);
  • LOG_WARN - Registra mensagens de aviso (macro relacionada: NS_LOG_WARN);
  • LOG_DEBUG - Registra mensagens de depuração especiais relativamente raras (macro relacionada: NS_LOG_DEBUG);
  • LOG_INFO - registro de mensagens informativas sobre o andamento do programa (macro relacionada: NS_LOG_INFO);
  • LOG_FUNCTION - Registra mensagens descrevendo cada função chamada (duas macros relacionadas: NS_LOG_FUNCTION, usada para funções membro, e NS_LOG_FUNCTION_NOARGS, usada para funções estáticas);
  • LOG_LOGIC - mensagens de log que descrevem o fluxo lógico dentro de uma função (macro relacionada: NS_LOG_LOGIC);
  • LOG_ALL - Registra tudo mencionado acima (sem macro associada).
    Para cada tipo (LOG_TYPE) existe também um LOG_LEVEL_TYPE que, se utilizado, permite que todos os níveis acima dele sejam registrados além do seu próprio nível. (Como consequência, LOG_ERROR e LOG_LEVEL_ERROR, e LOG_ALL e LOG_LEVEL_ALL são funcionalmente equivalentes.) Por exemplo, ativar LOG_INFO permitirá apenas mensagens fornecidas pela macro NS_LOG_INFO, enquanto ativar LOG_LEVEL_INFO também incluirá mensagens fornecidas pelas macros NS_LOG_DEBUG, NS_LOG_WARN e NS_LOG_ERROR.

Também fornecemos uma macro de registro incondicional que é sempre exibida, independentemente do nível de registro ou do componente de seleção.

  • NS_LOG_UNCOND - Registro incondicional da mensagem associada (sem nível de registro associado).

Cada nível pode ser consultado individualmente ou cumulativamente. O log pode ser configurado usando a variável de ambiente sh NS_LOG ou registrando uma chamada de função do sistema. Conforme mostrado anteriormente, o sistema de registro possui documentação do Doxygen e agora é um bom momento para revisá-lo, caso ainda não o tenha feito.

Agora que você leu a documentação detalhadamente, vamos usar esse conhecimento para obter algumas informações interessantes do script de exemplo scratch / myfirst.ccque você já compilou.

5.1.2 Habilitar registro

Vamos usar a variável de ambiente NS_LOG para executar mais alguns logs, mas primeiro, apenas para se orientar, execute o último script como você fez anteriormente,

$ ./waf --run scratch/myfirst

Você deverá ver a saída familiar do primeiro programa de exemplo do ns-3

$ Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build' 'build'
finished successfully (0.413s)
Sent 1024 bytes to 10.1.1.2
Received 1024 bytes from 10.1.1.1
Received 1024 bytes from 10.1.1.2

Acontece que as mensagens "enviadas" e "recebidas" que você vê acima são, na verdade, mensagens registradas de Aplicativo UdpEchoClient и Aplicativo UdpEchoServer. Por exemplo, podemos solicitar ao aplicativo cliente que imprima informações adicionais definindo seu nível de log por meio da variável de ambiente NS_LOG.

De agora em diante, assumirei que você está usando um shell do tipo sh que usa a sintaxe "VARIABLE=value". Se você estiver usando um shell semelhante ao csh, terá que converter meus exemplos para a sintaxe "valor da variável setenv" exigida por esses shells.

Atualmente, o aplicativo cliente de eco UDP responde à seguinte linha de código em scratch / myfirst.cc,

LogComponentEnable("UdpEchoClientApplication", LOG_LEVEL_INFO);

Ele habilita o nível de log LOG_LEVEL_INFO. Quando passamos um sinalizador de nível de registro, na verdade habilitamos esse nível e todos os níveis inferiores. Neste caso, habilitamos NS_LOG_INFO, NS_LOG_DEBUG, NS_LOG_WARN e NS_LOG_ERROR. Podemos aumentar o nível de registro e obter mais informações, sem alterações de script e recompilação, definindo a variável de ambiente NS_LOG da seguinte forma:

$ export NS_LOG=UdpEchoClientApplication=level_all

Portanto, definimos a variável sh shell NS_LOG com o seguinte valor,

UdpEchoClientApplication=level_all

O lado esquerdo da atribuição é o nome do componente registrado que queremos configurar, e o lado direito é o sinalizador que queremos aplicar a ele. Neste caso, vamos habilitar todos os níveis de depuração na aplicação. Se você executar o script com NS_LOG definido desta forma, o sistema de log do ns-3 aceitará as alterações e você deverá ver a seguinte saída:

Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' finished successfully (0.404s)
UdpEchoClientApplication:UdpEchoClient()
UdpEchoClientApplication:SetDataSize(1024)
UdpEchoClientApplication:StartApplication()
UdpEchoClientApplication:ScheduleTransmit()
UdpEchoClientApplication:Send()
Sent 1024 bytes to 10.1.1.2
Received 1024 bytes from 10.1.1.1
UdpEchoClientApplication:HandleRead(0x6241e0, 0x624a20)
Received 1024 bytes from 10.1.1.2
UdpEchoClientApplication:StopApplication()
UdpEchoClientApplication:DoDispose()
UdpEchoClientApplication:~UdpEchoClient()

Informações adicionais de depuração fornecidas pelo aplicativo estão agora no nível NS_LOG_FUNCTION. Mostra todas as instâncias de uma chamada de função durante a execução do script. Como regra geral, em funções de método é preferível usar (no mínimo)NS_LOG_FUNCTION (this)... Usar NS_LOG_FUNCTION_NOARGS ()
apenas em funções estáticas. Entretanto, observe que o sistema ns-3 não é obrigado a suportar nenhuma funcionalidade de registro. A decisão sobre quanta informação é registrada é deixada para o desenvolvedor individual do modelo. No caso de aplicações de eco, uma grande quantidade de saída de registro está disponível.

Agora você pode visualizar um log de chamadas de função feitas pelo aplicativo. Se você olhar de perto, notará dois pontos entre a linha Aplicativo UdpEchoClient e o nome do método, onde você pode esperar ver o operador de escopo C++ (: :). Isso é intencional.

Na verdade, este não é o nome da classe, mas o nome do componente de registro. Quando há uma correspondência entre um arquivo de origem e uma classe, geralmente é o nome da classe, mas você deve perceber que na verdade não é o nome da classe e há dois pontos únicos em vez de dois pontos duplos. Esta é uma maneira de ajudá-lo a separar conceitualmente o nome do bean de registro do nome da classe de uma maneira relativamente sutil.

Entretanto, em alguns casos pode ser difícil determinar qual método está realmente gerando a mensagem de log. Se você olhar o texto acima, pode estar se perguntando onde está a linha "Received 1024 bytes from 10.1.1.2" Você pode resolver este problema definindo o nível prefix_func para a variável de ambiente NS_LOG. Experimente o seguinte:

$ export 'NS_LOG=UdpEchoClientApplication=level_all|prefix_func'

Observe que as aspas são necessárias porque a barra vertical que usamos para representar a operação OR também é um conector de pipe Unix. Agora, se você executar o script, verá que o sistema de log garante que cada mensagem em um determinado log seja prefixada com o nome do componente.

Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' finished successfully (0.417s)
UdpEchoClientApplication:UdpEchoClient()
UdpEchoClientApplication:SetDataSize(1024)
UdpEchoClientApplication:StartApplication()
UdpEchoClientApplication:ScheduleTransmit()
UdpEchoClientApplication:Send()
UdpEchoClientApplication:Send(): Sent 1024 bytes to 10.1.1.2
Received 1024 bytes from 10.1.1.1
UdpEchoClientApplication:HandleRead(0x6241e0, 0x624a20)
UdpEchoClientApplication:HandleRead(): Received 1024 bytes from 10.1.1.2
UdpEchoClientApplication:StopApplication()
UdpEchoClientApplication:DoDispose()
UdpEchoClientApplication:~UdpEchoClient()

Agora você pode ver que todas as mensagens provenientes do aplicativo cliente de eco UDP são identificadas como tal. Mensagem "Received 1024 bytes from 10.1.1.2"agora está claramente identificado como proveniente do aplicativo cliente echo. A mensagem restante deve vir do aplicativo do servidor de eco UDP. Podemos habilitar este componente inserindo uma lista de componentes separados por dois pontos na variável de ambiente NS_LOG.

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

Atenção: No texto de exemplo acima, você precisará remover o caractere de nova linha após os dois pontos (:), ele é usado para formatar o documento. Agora, se você executar o script, verá todas as mensagens de log dos aplicativos de eco do cliente e do servidor. Você pode ver que isso pode ser muito útil durante a depuração.

Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' finished successfully (0.406s)
UdpEchoServerApplication:UdpEchoServer()
UdpEchoClientApplication:UdpEchoClient()
UdpEchoClientApplication:SetDataSize(1024)
UdpEchoServerApplication:StartApplication()
UdpEchoClientApplication:StartApplication()
UdpEchoClientApplication:ScheduleTransmit()
UdpEchoClientApplication:Send()
UdpEchoClientApplication:Send(): Sent 1024 bytes to 10.1.1.2
UdpEchoServerApplication:HandleRead(): Received 1024 bytes from 10.1.1.1
UdpEchoServerApplication:HandleRead(): Echoing packet
UdpEchoClientApplication:HandleRead(0x624920, 0x625160)
UdpEchoClientApplication:HandleRead(): Received 1024 bytes from 10.1.1.2
UdpEchoServerApplication:StopApplication()
UdpEchoClientApplication:StopApplication()
UdpEchoClientApplication:DoDispose()
UdpEchoServerApplication:DoDispose()
UdpEchoClientApplication:~UdpEchoClient()
UdpEchoServerApplication:~UdpEchoServer()

Às vezes também é útil poder ver o tempo de simulação em que a mensagem de log foi gerada. Você pode fazer isso adicionando o bit OR prefix_time:

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

Novamente, você terá que remover o caractere de nova linha acima. Se você executar o script agora, deverá ver a seguinte saída:

Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' finished successfully (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(): Sent 1024 bytes to 10.1.1.2
2.00369s UdpEchoServerApplication:HandleRead(): Received 1024 bytes from 10.1.1.1
2.00369s UdpEchoServerApplication:HandleRead(): Echoing packet
2.00737s UdpEchoClientApplication:HandleRead(0x624290, 0x624ad0)
2.00737s UdpEchoClientApplication:HandleRead(): Received 1024 bytes from 10.1.1.2
10s UdpEchoServerApplication:StopApplication()
10s UdpEchoClientApplication:StopApplication()
UdpEchoClientApplication:DoDispose()
UdpEchoServerApplication:DoDispose()
UdpEchoClientApplication:~UdpEchoClient()
UdpEchoServerApplication:~UdpEchoServer()

Observe que o construtor para UdpEchoServer foi chamado durante a simulação 0 segundos. Na verdade, isso acontece antes do início da simulação, mas o tempo é mostrado como zero segundos. O mesmo é verdade para a mensagem do construtor UdpEchoClientName.

Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' finished successfully (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(): Sent 1024 bytes to 10.1.1.2
2.00369s UdpEchoServerApplication:HandleRead(): Received 1024 bytes from 10.1.1.1
2.00369s UdpEchoServerApplication:HandleRead(): Echoing packet
2.00737s UdpEchoClientApplication:HandleRead(0x624290, 0x624ad0)
2.00737s UdpEchoClientApplication:HandleRead(): Received 1024 bytes from 10.1.1.2
10s UdpEchoServerApplication:StopApplication()
10s UdpEchoClientApplication:StopApplication()
UdpEchoClientApplication:DoDispose()
UdpEchoServerApplication:DoDispose()
UdpEchoClientApplication:~UdpEchoClient()
UdpEchoServerApplication:~UdpEchoServer()

Lembre-se que o roteiro scratch / first.cc iniciou o aplicativo do servidor de eco um segundo antes do início da simulação. Agora você pode ver que o método Iniciar aplicativo o servidor é realmente chamado no primeiro segundo. Você também pode notar que o echo client inicia no segundo segundo da simulação, conforme pedimos no script.

Agora você pode acompanhar o progresso da simulação de plantão Agendar transmissão no cliente que chama o retorno de chamada HandleRead Enviar no aplicativo do servidor de eco. Observe que o tempo decorrido para enviar um pacote por um link ponto a ponto é de 3,69 milissegundos. Você pode ver que o servidor de eco registra uma mensagem informando que respondeu ao pacote e, depois de um atraso no canal, você vê que o cliente de eco recebe o pacote de eco em seu método HandleRead.

Nesta simulação muita coisa acontece sem você perceber. Mas você pode acompanhar todo o processo facilmente habilitando todos os componentes de registro no sistema. Tente definir a variável NS_LOG com o seguinte valor,

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

O asterisco acima é um caractere curinga para o componente de log. Isso incluirá todas as entradas em todos os componentes usados ​​na simulação. Não reproduzirei a saída aqui (no momento em que este artigo foi escrito, ela produzia 1265 linhas de saída para um único pacote de eco), mas você pode redirecionar essas informações para um arquivo e visualizá-las em seu editor favorito.

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

Eu pessoalmente uso essa versão extremamente detalhada de registro quando tenho um problema e não tenho ideia de onde as coisas deram errado. Posso acompanhar a execução do código facilmente, sem definir pontos de interrupção e percorrer o código no depurador. Posso simplesmente editar a saída no meu editor favorito e procurar o que espero e ver acontecer algo que não esperava. Depois de ter uma ideia geral do que está errado, vou para o depurador para detalhar o problema. Esse tipo de saída pode ser especialmente útil quando o seu script faz algo completamente inesperado. Se você usar apenas o depurador, poderá perder completamente uma reviravolta. O registro torna essas reviravoltas perceptíveis.

5.1.3 Adicionando log ao seu código

Você pode adicionar novas entradas às suas simulações fazendo chamadas para o componente de log a partir de múltiplas macros. Vamos fazer isso em um script meuprimeiro.cc, que temos no diretório “limpo”. Lembre-se de que definimos um componente de registro neste cenário:

NS_LOG_COMPONENT_DEFINE ("FirstScriptExample");

Você está ciente de que pode ativar o log de todas as mensagens deste componente configurando a variável de ambiente NS_LOG em diferentes níveis. Vamos prosseguir e adicionar algumas entradas ao script. A macro usada para adicionar mensagens de nível de informação ao log é NS_LOG_INFO. Vamos adicionar uma mensagem (pouco antes de começarmos a criar nós) informando que o script está na fase "Criando Topologia". Isso é feito no seguinte trecho de código,
Abrir scratch / myfirst.cc no seu editor favorito e adicione a linha,
NS_LOG_INFO ("Creating Topology");
logo antes das linhas,

NodeContainer nodes;
nodes.Create (2);

Agora compile o script usando WAFe limpe a variável NS_LOG para desabilitar o fluxo de log que habilitamos anteriormente:

$ ./waf
$ export NS_LOG=
Теперь, если вы запустите скрипт,
$ ./waf --run scratch/myfirst

Você não verá a nova mensagem porque o componente de registro associado (FirstScriptExample) não foi habilitado. Para ver sua mensagem, você precisa ativar o componente de registro Exemplo de FirstScript com um nível não inferior a NS_LOG_INFO. Se você quiser apenas ver esse nível de registro específico, poderá ativá-lo assim,

$ export NS_LOG=FirstScriptExample=info

Se você executar o script agora, verá uma nova mensagem “Criando Topologia”,

Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' finished successfully (0.404s)
Creating Topology
Sent 1024 bytes to 10.1.1.2
Received 1024 bytes from 10.1.1.1
Received 1024 bytes from 10.1.1.2

5.2 Usando argumentos de linha de comando

5.2.1 Substituindo valores de atributos padrão

Outra maneira de alterar o comportamento dos scripts do ns-3 sem editar ou compilar é usar argumentos de linha de comando. Fornecemos um mecanismo para analisar argumentos de linha de comando e definir automaticamente variáveis ​​locais e globais com base nos resultados.

A primeira etapa no uso do sistema de argumentos de linha de comando é declarar um analisador de linha de comando. Isso é muito fácil de fazer (no seu programa principal), como no código a seguir,

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

Este trecho simples de duas linhas é, na verdade, muito útil por si só. Ele abre a porta para o sistema global de variáveis ​​e atributos do ns-3. Vamos adicionar duas linhas de código ao início da função do script principal scratch / myfirst.cc. Seguindo em frente, compilamos o script e executamos, ao executar fazemos um pedido de ajuda da seguinte forma,

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

Este comando irá perguntar Waf executar script arranhar / meu primeiro e passe um argumento de linha de comando —ImprimirAjuda. As aspas são obrigatórias para indicar a qual programa o argumento se destina. O analisador de linha de comando detectará o argumento —ImprimirAjuda e exibirá a resposta,

Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' finished successfully (0.413s)
TcpL4Protocol:TcpStateMachine()
CommandLine:HandleArgument(): Handle arg name=PrintHelp value=
--PrintHelp: Print this help message.
--PrintGroups: Print the list of groups.
--PrintTypeIds: Print all TypeIds.
--PrintGroup=[group]: Print all TypeIds of group.
--PrintAttributes=[typeid]: Print all attributes of typeid.
--PrintGlobals: Print the list of globals.

Agora vamos dar uma olhada na opção —PrintAttributes. Já mencionamos o sistema de atributos do ns-3 ao estudar o script first.cc. Vimos as seguintes linhas de código,

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

e eles disseram isso Taxa de dados na verdade é um atributo Dispositivo ponto a ponto Net. Vamos usar o analisador de argumentos de linha de comando para visualizar os atributos Dispositivo ponto a ponto Net. A lista de ajuda diz o que devemos fornecer ID do tipo. Este é o nome da classe à qual pertencem os atributos de interesse. No nosso caso será ns3 :: PointToPointNetDevice. Vamos seguir em frente, entre,

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

O sistema imprimirá todos os atributos deste tipo de dispositivo de rede. Você verá que entre os atributos da lista estão,

--ns3::PointToPointNetDevice::DataRate=[32768bps]:
The default data rate for point to point links

Este é o valor padrão que será utilizado pelo sistema ao criar o objeto Dispositivo ponto a ponto Net. Substituiremos esse valor padrão usando o parâmetro Atributo в PointToPoint Helper mais alto. Vamos usar os valores padrão para dispositivos e canais ponto a ponto. Para fazer isso, excluiremos chamadas SetDeviceAttribute и SetChannelAttribute de meuprimeiro.cc, que temos em um diretório limpo.

Seu script agora deve simplesmente declarar PointToPoint Helper e não execute nenhuma operação de instalação conforme mostrado no exemplo abaixo,

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

Vá em frente e crie um novo script com Waf (./waff) e vamos voltar e incluir alguma entrada do aplicativo do servidor de eco UDP e incluir o prefixo de hora.

$ export 'NS_LOG=UdpEchoServerApplication=level_all|prefix_time'

Se você executar o script, deverá ver a seguinte saída:

Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' finished successfully (0.405s)
0s UdpEchoServerApplication:UdpEchoServer()
1s UdpEchoServerApplication:StartApplication()
Sent 1024 bytes to 10.1.1.2
2.25732s Received 1024 bytes from 10.1.1.1
2.25732s Echoing packet
Received 1024 bytes from 10.1.1.2
10s UdpEchoServerApplication:StopApplication()
UdpEchoServerApplication:DoDispose()
UdpEchoServerApplication:~UdpEchoServer()

Lembre-se que da última vez que olhamos o tempo de simulação, no momento em que o pacote foi recebido pelo servidor de eco, foi de 2,00369 segundos.

2.00369s UdpEchoServerApplication:HandleRead(): Received 1024 bytes from 10.1.1.1

Agora ele recebe o pacote em 2.25732 segundos. Isso ocorre porque simplesmente redefinimos a taxa de dados PointToPointNetDevice de cinco megabits por segundo para o valor padrão, que é 32768 bits por segundo. Se substituíssemos um novo DataRate usando a linha de comando, poderíamos acelerar nossa simulação novamente. Faremos isso da seguinte forma, de acordo com a fórmula implícita no elemento help:

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

Isso retornará o atributo DataRate ao seu valor padrão de cinco megabits por segundo. Você está surpreso com o resultado? Acontece que para retornar o comportamento original do script, também precisamos definir o atraso do canal para corresponder à velocidade da luz. Podemos pedir ao sistema de linha de comando para imprimir os atributos do canal, assim como fizemos para o dispositivo de rede:

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

Descobriremos que o atributo de atraso do canal é definido da seguinte forma:

--ns3::PointToPointChannel::Delay=[0ns]:
Transmission delay through the channel

Podemos então, através do sistema de linha de comando, definir esses dois valores padrão.

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

neste caso, restauramos o tempo que tínhamos quando definimos explicitamente DataRate e Delay no script:

Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' finished successfully (0.417s)
0s UdpEchoServerApplication:UdpEchoServer()
1s UdpEchoServerApplication:StartApplication()
Sent 1024 bytes to 10.1.1.2
2.00369s Received 1024 bytes from 10.1.1.1
2.00369s Echoing packet
Received 1024 bytes from 10.1.1.2
10s UdpEchoServerApplication:StopApplication()
UdpEchoServerApplication:DoDispose()
UdpEchoServerApplication:~UdpEchoServer()

Observe que o pacote é recebido novamente pelo servidor após 2,00369 segundos. Na verdade, poderíamos definir qualquer um dos atributos usados ​​no script dessa maneira. Em particular, poderíamos definir os atributos MaxPackets como valores diferentes de um UdpEchoClientName.

Como você usaria isso? De uma chance. Lembre-se de que você deve comentar o local onde substituímos o valor do atributo padrão e definimos explicitamente MaxPackets no roteiro. Então você deve reconstruir o script. Você também pode usar a linha de comando para obter ajuda de sintaxe para definir um novo valor de atributo padrão. Depois de entender isso, você poderá controlar o número de pacotes exibidos na linha de comando. Como somos pessoas estudiosas, nossa linha de comando deve ser mais ou menos assim:

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

A questão natural que surge neste momento é como saber da existência de todos esses atributos. Novamente, o sistema de linha de comando possui uma função de ajuda para esse assunto. Se pedirmos ajuda à linha de comando, veremos:

$ ./waf --run "scratch/myfirst --PrintHelp"
myfirst [Program Arguments] [General Arguments]
General Arguments:
--PrintGlobals: Print the list of globals.
--PrintGroups: Print the list of groups.
--PrintGroup=[group]: Print all TypeIds of group.
--PrintTypeIds: Print all TypeIds.
--PrintAttributes=[typeid]: Print all attributes of typeid.
--PrintHelp: Print this help message.

Se você selecionar o argumento "PrintGroups", deverá ver uma lista de todos os grupos registrados ID do tipo. Os nomes dos grupos são consistentes com os nomes dos módulos no diretório de origem (embora em letras maiúsculas). Imprimir todas as informações de uma vez seria muito volumoso, portanto, um filtro adicional está disponível para imprimir as informações por grupo. Então, novamente focando no módulo ponto a ponto:

./waf --run "scratch/myfirst --PrintGroup=PointToPoint"
TypeIds in group PointToPoint:
ns3::PointToPointChannel
ns3::PointToPointNetDevice
ns3::PointToPointRemoteChannel
ns3::PppHeader

Aqui você pode encontrar nomes TypeId disponíveis para pesquisas de atributos, por exemplo em
--PrintAttributes = ns3 :: PointToPointChannelcomo mostrado acima.

Outra forma de aprender sobre atributos é através do Doxygen ns‑3. Existe uma página que lista todos os atributos cadastrados no simulador.

5.2.2 Capturando seus próprios comandos

Você também pode adicionar seus próprios ganchos através do sistema de linha de comando. Isso é feito simplesmente usando o método analisador de linha de comando AddValue.
Vamos usar este recurso para especificar o número de pacotes a serem exibidos de uma forma completamente diferente. Vamos adicionar uma variável local chamada nPacotes em uma função principal. Iremos defini-lo como um para corresponder ao nosso comportamento padrão anterior. Para permitir que o analisador de linha de comando altere esse valor, precisamos capturar esse valor no analisador. Fazemos isso adicionando uma chamada AddValue. Vá e mude o script scratch / myfirst.cc então, para começar com o código a seguir,

int
main (int argc, char *argv[])
{
uint32_t nPackets = 1;
CommandLine cmd;
cmd.AddValue("nPackets", "Number of packets to echo", nPackets);
cmd.Parse (argc, argv);
...

Role para baixo até o ponto no script onde definimos o atributo MaxPackets e altere-o para que seja definido como a variável nPackets em vez da constante 1, conforme mostrado abaixo.

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

Agora, se você executar o script e fornecer o argumento -PrintHelp, deverá ver o novo argumento do usuário. listados na tela de ajuda. Digitar,

$ ./waf --run "scratch/myfirst --PrintHelp"
Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' finished successfully (0.403s)
--PrintHelp: Print this help message.
--PrintGroups: Print the list of groups.
--PrintTypeIds: Print all TypeIds.
--PrintGroup=[group]: Print all TypeIds of group.
--PrintAttributes=[typeid]: Print all attributes of typeid.
--PrintGlobals: Print the list of globals.
User Arguments:
--nPackets: Number of packets to echo

Se quiser alterar o número de pacotes transmitidos, você pode fazer isso definindo o argumento da linha de comando - -nPackets.

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

Agora você deve ver

Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' finished successfully (0.404s)
0s UdpEchoServerApplication:UdpEchoServer()
1s UdpEchoServerApplication:StartApplication()
Sent 1024 bytes to 10.1.1.2
2.25732s Received 1024 bytes from 10.1.1.1
2.25732s Echoing packet
Received 1024 bytes from 10.1.1.2
Sent 1024 bytes to 10.1.1.2
3.25732s Received 1024 bytes from 10.1.1.1
3.25732s Echoing packet
Received 1024 bytes from 10.1.1.2
10s UdpEchoServerApplication:StopApplication()
UdpEchoServerApplication:DoDispose()
UdpEchoServerApplication:~UdpEchoServer()

Você já enviou dois pacotes. Muito simples, não é?
Você pode ver que, como usuário do ns-3, você pode usar o sistema de argumentos de linha de comando para manipular valores e atributos globais. Se você for o autor do modelo, poderá adicionar novos atributos aos seus objetos e eles ficarão automaticamente disponíveis para configuração pelos seus usuários através do sistema de linha de comando. Se você for um autor de script, poderá adicionar novas variáveis ​​aos seus scripts e conectá-las perfeitamente ao seu sistema de linha de comando.

5.3 Usando o sistema de rastreamento

O objetivo da modelagem é gerar resultados para estudos adicionais, e o sistema de rastreamento ns-3 é o principal mecanismo para isso. Como o ns-3 é um programa C++, meios padrão de geração de saída de um programa C++ podem ser usados:

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

Você pode até usar um módulo de registro para adicionar um pouco de estrutura à sua solução. Existem muitos problemas conhecidos causados ​​por esta abordagem e, portanto, fornecemos um subsistema geral de rastreamento de eventos para resolver esses problemas.

Os principais objetivos do sistema de rastreamento ns-3 são:

  • Para tarefas básicas, o sistema de rastreamento deve permitir ao usuário gerar um rastreamento padrão para fontes populares e selecionar objetos que geram o rastreamento;

  • Os usuários intermediários devem ser capazes de estender o sistema de rastreamento para alterar o formato de saída gerado ou inserir novas fontes de rastreamento, sem modificar o núcleo do simulador;

  • Usuários avançados podem modificar o núcleo do simulador para adicionar novas fontes e coletores de rastreamento. O sistema de rastreamento ns-3 é baseado nos princípios de fontes e receptores de rastreamento independentes, bem como em um mecanismo unificado para conectar fontes a consumidores.

O sistema de rastreamento ns-3 é construído sobre os princípios de fontes e receptores de rastreamento independentes, bem como um mecanismo unificado para conectar fontes a receptores. Fontes de rastreamento são objetos que podem sinalizar eventos que ocorrem na simulação e fornecer acesso aos dados subjacentes de interesse. Por exemplo, uma fonte de rastreamento pode indicar quando um dispositivo de rede recebeu um pacote e disponibilizar o conteúdo do pacote para receptores de rastreamento interessados.

As fontes de rastreamento por si só são inúteis, a menos que sejam "acopladas" a outras partes do código que realmente façam algo útil com as informações fornecidas pelo coletor. Rastreadores são consumidores de eventos e dados fornecidos por fontes de rastreamento. Por exemplo, você pode criar um coletor de rastreamento que (quando conectado à fonte de rastreamento do exemplo anterior) imprimirá as partes de interesse no pacote recebido.

A justificativa para essa separação explícita é permitir que os usuários conectem novos tipos de coletores a fontes de rastreamento existentes sem precisar editar e recompilar o núcleo do simulador. Portanto, no exemplo acima, o usuário pode definir um novo rastreador em seu script e conectá-lo a uma fonte de rastreio existente definida no núcleo da simulação apenas editando o script do usuário.

Neste tutorial, examinaremos algumas das fontes e coletores predefinidos e mostraremos como eles podem ser configurados com o mínimo de esforço por parte do usuário. Consulte o Manual do ns-3 ou as seções de instruções para obter informações sobre configuração avançada de rastreamento, incluindo expansão do namespace de rastreamento e criação de novas fontes de rastreamento.

5.3.1 Rastreamento ASCII

O ns-3 fornece funcionalidade auxiliar que fornece um sistema de rastreamento de baixo nível para ajudá-lo com os detalhes ao configurar rastreamentos de pacotes simples. Se você ativar esse recurso, verá a saída em arquivos ASCII. Para aqueles familiarizados com a saída do ns-2, este tipo de rastreamento é semelhante ao fora.tr, que é gerado por muitos scripts.

Vamos direto ao assunto e adicionar alguns resultados de rastreamento ASCII ao nosso script scratch/myfirst.cc. Logo antes da ligação Simulator :: Run (), adicione as seguintes linhas de código:
AsciiTraceHelper ascii;

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

Como muitos outros idiomas do ns-3, este código usa um objeto auxiliar para criar traços ASCII. A segunda linha contém duas chamadas de método aninhadas. Método "dentro" CriarFileStream() usa o idioma do objeto anônimo para criar um objeto de fluxo de arquivo na pilha (sem um nome de objeto) e o passa para o método chamado. Iremos nos aprofundar nisso no futuro, mas tudo o que você precisa saber neste momento é que você está criando um objeto que representa um arquivo chamado meuprimeiro.tr e transfira-o para o ns-3. Confiamos ao ns-3 para cuidar do objeto criado durante toda a sua vida útil, durante o qual ele resolve problemas causados ​​por uma limitação pouco conhecida (intencional) associada aos construtores de cópia de objetos de fluxo C++.

Chamada externa EnableAsciiAll () informa ao assistente que você deseja incluir o rastreamento ASCII em sua simulação para todas as conexões de dispositivos ponto a ponto e que deseja que os receptores de rastreamento (especificados) registrem informações de movimentação de pacotes no formato ASCII.

Para aqueles familiarizados com o ns-2, os eventos rastreados são equivalentes aos tracepoints conhecidos que registram os eventos "+", "-", "d" e "r".
Agora você pode criar o script e executá-lo na linha de comando:

$ ./waf --run scratch/myfirst

Como muitas vezes antes, você verá várias mensagens do Waf e, em seguida, “'construção' finalizada com sucesso” com algumas mensagens do programa em execução.

Ao ser executado, o programa criará um arquivo chamado meuprimeiro.tr. Pela natureza do trabalho Waf, por padrão o arquivo não é criado no diretório local, mas no diretório de nível superior do repositório. Se você deseja alterar o caminho onde os rastreamentos são salvos, você pode usar o parâmetro Waf para especificá-lo --cwd. Não fizemos isso, então para ver o arquivo de rastreamento ASCII myfirst.tr em seu editor favorito, precisaremos navegar até o diretório de nível superior do nosso repositório.

Analisando rastreamentos ASCII

Há muitas informações em um formato bastante denso, mas a primeira coisa que você precisa notar é que o arquivo consiste em linhas individuais. Isso ficará claramente visível se você expandir mais a janela de visualização.

Cada linha no arquivo corresponde a um evento de rastreamento. Neste caso, rastreamos eventos na fila de transmissão presente em cada dispositivo da rede ponto a ponto na simulação. A fila de transmissão é a fila pela qual cada pacote deve passar para um link ponto a ponto. Observe que cada linha no arquivo de rastreamento começa com um único caractere (e possui um espaço após ele). Este símbolo terá o seguinte significado:

+: ocorreu uma operação de enfileiramento na fila do dispositivo;
-: ocorreu uma operação de recuperação de elemento na fila do dispositivo;
d: o pacote foi descartado, geralmente porque a fila estava cheia;
r: O pacote foi recebido por um dispositivo de rede.

Vamos dar uma olhada mais de perto na primeira linha do arquivo de rastreamento. Vou dividi-lo em partes (com recuos para maior clareza) e o número da linha à esquerda:

0 +
1 2
2 /NodeList/0/DeviceList/0/$ns3::PointToPointNetDevice/TxQueue/Enqueue
3 ns3::PppHeader (
4   Point-to-Point Protocol: IP (0x0021))
6   ns3::Ipv4Header (
7     tos 0x0 ttl 64 id 0 protocol 17 offset 0 flags [none]
8     length: 1052 10.1.1.1 > 10.1.1.2)
9     ns3::UdpHeader (
10      length: 1032 49153 > 9)
11      Payload (size=1024)

A primeira seção deste evento de rastreamento estendido (linha 0) é a operação. Temos aqui um símbolo +, que corresponde à operação de enfileiramento para transmissão. A segunda seção (linha 1) é o tempo de simulação, expresso em segundos. Você deve se lembrar do que perguntamos Aplicativo UdpEchoClient comece a enviar pacotes em dois segundos. Aqui vemos a confirmação de que isso realmente está acontecendo.

A próxima seção do exemplo de rastreio (da linha 2) mostra qual origem de rastreio gerou esse evento (indicando o rastreio de namespace). Você pode pensar no namespace de rastreamento da mesma forma que pensaria em um namespace de sistema de arquivos. A raiz do namespace é Lista de nós. Isso corresponde ao contêiner gerenciado no código principal do ns-3. Ele contém todos os nós criados no script. Assim como um sistema de arquivos pode ter diretórios na raiz, Lista de nós podemos ter muitos nós. Portanto, a linha /NodeList/0 refere-se ao nó nulo no NodeList, que normalmente chamamos de "nó 0". Cada nó possui uma lista de dispositivos que foram instalados. Esta lista está localizada a seguir no namespace. Você pode ver que esse evento de rastreamento vem de DeviceList / 0, que é o dispositivo nulo instalado no nó.

Próxima substring, $ ns3 :: PointToPointNetDevice, informa qual dispositivo está na posição zero: a lista de dispositivos do nó zero. Lembre-se de que a operação + encontrada na linha 0 significava que um elemento foi adicionado à fila de transmissão do dispositivo. Isto se reflete nos últimos segmentos do “caminho da trilha”: TxQueue / Enqueue.

As seções restantes do rastreamento devem ser bastante intuitivas. As linhas 3 a 4 indicam que o pacote está encapsulado em um protocolo ponto a ponto. As linhas 5 a 7 mostram que o pacote possui um cabeçalho de versão IP4 e é originado no endereço IP 10.1.1.1 e se destina a 10.1.1.2. As linhas 8 a 9 mostram que este pacote tem um cabeçalho UDP e, finalmente, a linha 10 mostra que a carga útil é de 1024 bytes esperados.

A próxima linha no arquivo de rastreamento mostra que o mesmo pacote foi extraído da fila de transmissão no mesmo nó.

A terceira linha no arquivo de rastreamento mostra que o pacote foi recebido por um dispositivo de rede no host do servidor de eco. Reproduzi o evento abaixo.

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     length: 1052 10.1.1.1 > 10.1.1.2)
6     ns3::UdpHeader (
7       length: 1032 49153 > 9)
8       Payload (size=1024)

Observe que a operação de rastreamento agora é r e o tempo de simulação foi aumentado para 2,25732 segundos. Se você seguiu o tutorial com atenção, isso significa que você deixou o DataRate e o Link Delay dos dispositivos de rede em seus valores padrão. Este tempo verbal deve ser familiar, como você viu na seção anterior.

A entrada do namespace de origem de rastreio (linha 2) foi modificada para refletir que esse evento se origina do nó 1 (/Lista de nós/1) e o pacote é recebido pela fonte de rastreamento (/MacRx). Deve ser bastante fácil acompanhar o movimento do pacote pela topologia observando os rastros restantes no arquivo.

5.3.2 Rastreamento PCAP

Os Device Helpers ns-3 também podem ser usados ​​para criar arquivos de rastreamento no formato .pcap. Acrônimo pcap (geralmente escrito em letras minúsculas) significa captura de pacotes e é na verdade uma API que inclui a definição do formato de arquivo .pcap. O programa mais popular que pode ler e exibir este formato é Wireshark (anteriormente chamado Etéreo). No entanto, existem muitos analisadores de rastreamento de tráfego que utilizam esse formato de pacote. Incentivamos os usuários a usar as diversas ferramentas disponíveis para analisar rastros de pcap. Neste tutorial vamos nos concentrar na visualização de rastreamentos pcap usando tcpdump.

A ativação do rastreamento pcap é feita com uma linha de código.

pointToPoint.EnablePcapAll ("myfirst");

Cole esta linha de código após o código de rastreamento ASCII que acabamos de adicionar scratch / myfirst.cc. Observe que passamos apenas a string "myfirst", não "myfirst.pcap" ou algo semelhante. Isso ocorre porque o parâmetro é um prefixo e não um nome de arquivo completo. Durante a simulação, o assistente criará um arquivo de rastreamento para cada dispositivo ponto a ponto. Os nomes dos arquivos serão construídos usando o prefixo, o número do nó, o número do dispositivo e o sufixo ".pcap".

Para nosso script de exemplo, acabaremos vendo arquivos chamados "meuprimeiro-0-0.pcap"E"meuprimeiro-1-0.pcap", que são rastreamentos pcap para o nó 0-dispositivo 0 e o nó 1-dispositivo 0, respectivamente. Depois de adicionar a linha de código para ativar o rastreamento pcap, você poderá executar o script da maneira usual:

$ ./waf --run scratch/myfirst

Se você olhar no diretório de nível superior da sua distribuição, deverá ver três arquivos: um arquivo de rastreamento ASCII meuprimeiro.tr, que estudamos anteriormente, arquivos meuprimeiro-0-0.pcap и meuprimeiro-1-0.pcap - novos arquivos pcap que acabamos de gerar.

Lendo saída com tcpdump

Por enquanto, a maneira mais fácil de visualizar arquivos pcap é usar o tcpdump.

$ tcpdump -nn -tt -r myfirst-0-0.pcap
reading from file myfirst-0-0.pcap, link-type PPP (PPP)
2.000000 IP 10.1.1.1.49153 > 10.1.1.2.9: UDP, length 1024
2.514648 IP 10.1.1.2.9 > 10.1.1.1.49153: UDP, length 1024
tcpdump -nn -tt -r myfirst-1-0.pcap
reading from file myfirst-1-0.pcap, link-type PPP (PPP)
2.257324 IP 10.1.1.1.49153 > 10.1.1.2.9: UDP, length 1024
2.257324 IP 10.1.1.2.9 > 10.1.1.1.49153: UDP, length 1024

No lixão meuprimeiro-0-0.pcap (dispositivo cliente), você pode ver que o pacote de eco é enviado após 2 segundos de simulação. Se você olhar para o segundo dump (meuprimeiro-1-0.pcap), você verá que o pacote é recebido em 2,257324 segundos. Você verá no segundo dump que o pacote é retornado em 2.257324 segundos e, finalmente, que o pacote foi recebido de volta pelo cliente no primeiro dump em 2.514648 segundos.

Lendo saída com Wireshark

Se você não está familiarizado com Wireshark, existe um site no qual você pode baixar programas e documentação: http://www.wireshark.org/. Wireshark é uma GUI que pode ser usada para exibir esses arquivos de rastreamento. Se você tiver o Wireshark, poderá abrir qualquer um dos arquivos de rastreamento e exibir o conteúdo como se tivesse capturado os pacotes usando um sniffer de pacotes.

Fonte: habr.com

Adicionar um comentário