Network Simulator

Trabalho Final de Comunicação de Dados
Prof.: Eduardo Parente Ribeiro
Aluno: Juliano João Bazzo

O NS (Network Simulator) é um simulador desenvolvido pela University College of Berkely. NS simula eventos discretos com o objetivo centrado em pesquisas em redes de comunicação de dados. Ele fornece um suporte substancial para simulações de protocolos da pilha TCP/IP, roteamento e protocolos multicast.

O simulador funciona baseado em scripts Tcl, porém seu núcleo foi desenvolvido em C++ e grande parcela dos módulos de suporte a tecnologias mais específicas em Otcl - versão orientada a objetos da linguagem Tcl. O seu núcleo pode ser modificado, ou personalizado, de acordo com as necessidades de cada usuário.

Atualmente o NS já é um software confiável, porém está em constante atualização graças aos desenvolvedores e aos usuários que descobrem os bugs para que possam ser corrigidos nas versões seguintes. O software está disponível para os sistemas operacionais Linux, Windows e outros sistemas baseados em Unix e pode ser baixado através do seguinte endereço http://www.isi.edu/nsnam/ns/index.html.

Neste mini tutorial o leitor poderá encontrar uma breve introdução sobre a linguagem TCL e OTCL em seguida exemplos de simulações para serem executadas no NS e finalmente técnicas para a implementação de novos protocolos de roteamento.

OTCL - Tool Command Language OO

N é basicamente um interpretador OTCL com bibliotecas para simulação de redes e é importante que o usuário saiba programar em tal linguagem para que possa operar coerentemente o NS. Nesta seção pretende-se dar ao leitor uma breve idéia sobre as formas e estruturas do código TCL e OTCL.
Nas seções posteriores assume-se que o leitor já possui instalado o NS e também familiarizado com C e C++.

Exemplo 1: Criação de uma de um script em TCL que faz diversos cálculos matemáticos simples e mostra o resultado na tela.

##############################
#        Exemplo 1           #
##############################

proc teste {} {

        #Atribui os valores as variárveis
        set a 10.0
        set b 5

        #mostra o valor de a
        puts $a
        
        # A variável possui o seguinte resultado: c =(a+b)* b
        set c [expr [expr $a + $b] * $b]

        for {set k 0} {$k<10} {incr k} {
                if {$k<5} {
                        puts [expr $k * $c]
                } else {
                        puts [expr $k / $c]
                }
        }
}

#Chama o procedimento teste{}
teste
O próximo exemplo é um programa em TCL Orientado à Objeto ou OTCL. Este exemplo é muito simples, mas mostra a forma com que os objetos são criados e usados no OTCL. Como um usuário normal do NS, as mudanças que fará em seus objetos serão raras. No entanto, como os scripts e simulação do NS são escritos em OTCL, é importante um entendimento desta forma de programação.

Exemplo 2: Criação de um script OTCL que possui 2 classes "Mae" e "Filha" sendo que a classe "Filha" herda as variáveis da classe "Mae".

# Cria uma classe "Mae" e um 
# método "cumprimento"
Class Mae
        Mae instproc cumprimento {} {
                $self instvar idade_
                puts "Mae com $idade_ anos de idade diz: Oi, tudo bem?"
        }

# Cria uma classe "Filha", que herda as
# variáveis da classe "Mãe", e um método
# "cumprimento"
Class Filha -superclass Mae
        Filha instproc cumprimento {} {
                 $self instvar idade_
                 puts "Filha com $idade_ anos de idade diz: Oi, tudo certinho?"
        }

# Cria um objeto da classe Mae e Filha e 
# atribui a idade a elas.
set a [new Mae]
$a set idade_ 45
set b [new Filha]
$b set idade_ 15

# Chama o método cumprimento de cada classe
$a cumprimento
$b cumprimento

Network Simulator

Inicialização

Sua inicialização é através do comando 'ns <tclscript>', onde é o nome do script em tcl que define o cenário de simulação. É possivel também iniciar o ns sem nenhum argumento e adiconar os comandos Tcl no shell Tcl. Tudo no NS depende de um script Tcl. O script pode criar alguma saida na stdout, pode escrever um arquivo de trace ou pode inicializar o NAM para visualização da simulação.
O NS utiliza um programa, o NAM como visualizador das simulações testadas.

NAM

O NAM possui as seguintes características:
- É um programa de visualização de simulação
- Utiliza os arquivos .nam gerados pelos scripts do NS

Exemplos de Scripts de Simulação

Exemplos 3: Scripts de Simulação com Dois Nós

#Cria o objeto simulador
set ns [new Simulator] 

#Cria o arquivo de trace para o nam
set nf [open out.nam w] 

#Define que toda a simulação será registrada no out.nam
$ns namtrace-all $nf

#Define o procedimento de encerramento que fecha o simulador e abre o nam
proc finish {} {
        global ns nf
        $ns flush-trace
        #Fecha o trace file
        close $nf
        #Executa nam com o trace file
        exec nam out.nam & 
        exit 0
}


#Cria dois nós. A definição da configuração do nó é a padrão
set n0 [$ns node]
set n1 [$ns node] 

#Cria um link  duplex entre eles, com largura de banda de 1Mbps e delay de 10ms
$ns duplex-link $n0 $n1 1Mb 10ms DropTail

#Chama o procedimento finish após 10 segundos de simulação
$ns at 10.0 "finish"
A saída no NAM é a seguinte:

Exemplos 4:Exemplos de Monitoramento de Fila com Saída Gráfica

Esta seção mostra um exemplo de monitoramento de fila. Este script OTcl foi escrito por Polly Huang e que cria a topologia de rede mostrada na figura abaixo.
 


O link entre os roteadores r1 e r2, chamada rota "vermelha" pode tratar uma fila de até 25 pacotes. O gráfico seguinte ao código mostra o tamanho e tamanho médio da fila.
O gráfico de saída foi obtido através do pacote xgraph que está disponível na maioria dos kernels linux atualmente.

Exemplos 5: Criação de Agentes CBR

O script abaixo cria 3 nós onde um deles é o agente (nó 0) que envia dados a uma taxa constante ao nó 1 que é chamado de nó nulo pois é neste ponto que os pacotes são destruídos.
O script e seus comentários seguem abaixo.
 
set nf [open out.nam w]

#Define que toda a simulação será registrada no out.nam
$ns namtrace-all $nf 

#Define o procedimento de encerramento que fecha o simulador e abre o nam
proc finish {} {
         global ns nf
         $ns flush-trace
         #Fecha o trace file
         close $nf
         #Executa nam com o trace file
         exec nam out.nam &
         exit 0
}

#Cria três nós. A definição da configuração do Nó é a padrão
set n0 [$ns node]
set n1 [$ns node]
set n2 [$ns node]

#Cria um agente CBR(Constant Bit Reate) e vincula ao nó n0
set cbr0 [new Agent/CBR]
$ns attach-agent $n0 $cbr0

#Seta o tamanho do pacote e o intervalo de envio
$cbr0 set packetSize_ 500
$cbr0 set interval_ 0.005

#Cria um agente Null para sincronização de tráfego e vincula ao nodo n1
set null0 [new Agent/Null]
$ns attach-agent $n1 $null0

#Conecta os agentes para estabelecer a comunicação 
$ns connect $cbr0 $null0

#Faz o schedule dos eventos do agente  CBR 
$ns at 0.5 "$cbr0 start"
$ns at 4.5 "$cbr0 stop" 

#Cria um link  duplex entre eles, com largura de bandas de 2Mbps/1Mbps e
#delay de 5ms/10ms respectivamente
$ns duplex-link $n2 $n1 2Mb 5ms DropTail
$ns duplex-link $n0 $n1 1Mb 10ms DropTail
 
#Chama o procedimento finish após 10 segundos de simulação
$ns at 10.0 "finish"
 
#Executa a simulação
$ns run
A saída no NAM é a Seguinte

Exemplos 6: Dois Agentes CBR Utilizando um Link de Baixa Capacidade

Neste exemplo, que é muito parecido com o anterior, foi criado um "gargalo" entre os nós n3-n2. Ao visualizar a simulação é possível notar que há uma considerável perda de pacotes neste link devido a sua baixa capacidade.
O script e seus comentários segue abaixo.
 
set ns [new Simulator]

#Cria o arquivo de trace para o nam
set nf [open out.nam w]

#Define que toda a simulação será registrada no out.nam
$ns namtrace-all $nf

#Define o procedimento de encerramento que fecha o
#simulador e abre o nam
proc finish {} {
        global ns nf
        $ns flush-trace
        #Fecha o trace file
        close $nf

        #Executa nam com o trace file
        exec nam out.nam &
        exit 0
}

#Cria 4 nós
set n0 [$ns node]
set n1 [$ns node]
set n2 [$ns node]
set n3 [$ns node]

#Cria um agente CBR (Constant Bit Reate)
set cbr0 [new Agent/CBR]
set cbr1 [new Agent/CBR]

$ns attach-agent $n0 $cbr0
$ns attach-agent $n1 $cbr1

#Seta o tamanho do pacote e o intervalo de envio
$cbr0 set packetSize_ 500
$cbr0 set interval_ 0.005

$cbr1 set packetSize_ 500 
$cbr1 set interval_ 0.01

#Cria um agente Null para sincronização de tráfego e vincula ao nodo n1
set null0 [new Agent/Null]
ns attach-agent $n3 $null0

#Conecta os agentes para estabelecer a comunicação
$ns connect $cbr0 $null0
$ns connect $cbr1 $null0

# Identificação do fluxo de pacotes
# O parametro 'fid_' significa 'flow id'. 
$cbr0 set fid_ 1
$cbr1 set fid_ 2
 
#Setando a cor dos pacotes
$ns color 1 Blue
$ns color 2 Red
 
#Faz o schedule dos eventos do agente  CBR
$ns at 0.5 "$cbr0 start"
$ns at 10.5 "$cbr0 stop"

$ns at 0.0 "$cbr1 start"
$ns at 12.0 "$cbr1 stop"

#Setar a orientação entre ele, note que o link n3-n2 é um "gargalo na rede"
$ns duplex-link $n0 $n2 10Mb 10ms DropTail
$ns duplex-link $n1 $n2 10Mb 10ms DropTail
$ns duplex-link $n3 $n2 1Mb 10ms DropTail
 
#Chama o procedimento finish apos 5 segundos
#de simulação
$ns at 13.0 "finish"

#Executa a simulação
$ns run
A saída no NAM é a Seguinte

Exemplos 7: Roteamento

Neste exemplo, é simulada uma queda entre os nós n2 e n3 e o NS faz automaticamente o roteamento buscando o caminho mais curto (menor número de hops) para enviar o fluxo de pacotes.
É interessante notar na saída do NAM que no momento da queda há a perda dos pacotes que estavam em tráfego e os que ainda estão sendo tratados pelo nó.
O script e seus comentários seguem abaixo.
 
set ns [new Simulator]
set nf [open out.nam w]
$ns namtrace-all $nf
proc finish {} {
        global ns nf
        $ns flush-trace
        #Fecha o trace file
        close $nf

        #Executa nam com o trace file
        exec nam out.nam &
        exit 0
}

#Cria 5 nós
set n0 [$ns node]
set n1 [$ns node]
set n2 [$ns node]
set n3 [$ns node]
set n4 [$ns node]

#Cria um agente CBR (Constant Bit Reate)
set cbr0 [new Agent/CBR]
set cbr1 [new Agent/CBR]

$ns attach-agent $n0 $cbr0
$ns attach-agent $n1 $cbr1

#Seta o tamanho do pacote e o intervalo de envio
$cbr1 set packetSize_ 500
$cbr1 set interval_ 0.01

#Cria um agente Null para sincronização de tráfego
#o e vincula ao nodo n1
set null0 [new Agent/Null]
ns attach-agent $n3 $null0

#Conecta os agentes para estabelecer a comunicação
$ns connect $cbr0 $null0
$ns connect $cbr1 $null0

# Identificação do fluxo de pacotes
# O parametro 'fid_' significa 'flow id'.
$cbr0 set fid_ 1
$cbr1 set fid_ 2

#Setando a cor dos pacotes
$ns color 1 Blue
$ns color 2 Red


#Faz o schedule dos eventos do agente  CBR
$ns at 0.5 "$cbr0 start"
$ns at 10.5 "$cbr0 stop"

$ns at 0.0 "$cbr1 start"
$ns at 12.0 "$cbr1 stop"

#Setar a orientação entre ele
$ns duplex-link $n0 $n2 1Mb 10ms DropTail
$ns duplex-link $n1 $n2 1Mb 10ms DropTail
$ns duplex-link $n3 $n2 10Mb 10ms DropTail
$ns duplex-link $n0 $n4 1Mb 10ms DropTail
$ns duplex-link $n4 $n3 1Mb 10ms DropTail

#Re-roteamento
$ns rtmodel-at 3.0 down $n2 $n3
$ns rtmodel-at 8.0 up $n2 $n3

# Seta o protocolo de roteamento alternativo
$ns rtproto DV

#Chama o procedimento finish apos 13 segundos
$ns at 13.0 "finish"

#Executa a simulação
$ns run
A saída no NAM é a Seguinte

Exemplos 8: Criação de Agentes TCP e UDP com Tráfego Exponencial

Aqui é criado agentes TCP e UDP sendo que o tráfego UDP possui uma característica exponencial. É interessante notar que o agente TCP deve estar conectado a um nó do tipo "TCPSinkNl" pois este tem que enviar as resposta ack ao transmissor.
Para o UDP o nó nulo pode ser simples pois o UDP é um serviço não confiável e não é necessário a transmissão do ack pelo receptor.
Segue o código abaixo.
 
set ns [new Simulator]
set nf [open out.nam w]
$ns namtrace-all $nf
proc finish {} {
        global ns nf
        $ns flush-trace
        #Fecha o trace file
        close $nf

        #Executa nam com o trace file
        exec nam out.nam &
        exit 0
}

#Cria 7 nós
set n0 [$ns node]
set n1 [$ns node]
set n2 [$ns node]
set n3 [$ns node]
set n4 [$ns node]
set n5 [$ns node]
set n6 [$ns node]

#Atribui os agentes
set cbr0 [new Agent/CBR]
set cbr1 [new Agent/CBR]

set tcp2 [new Agent/TCP]

set exp5 [new Application/Traffic/Exponential]
set udp5 [new Agent/UDP]

$ns attach-agent $n0 $cbr0
$ns attach-agent $n1 $cbr1
$ns attach-agent $n2 $tcp2
$ns attach-agent $n5 $udp5

#Seta o tamanho do pacote e o intervalo de envio
$cbr1 set packetSize_ 500
$cbr1 set interval_ 0.01

set size 1500
set burst 5s
set idle 2s
set rate 10M

$exp5 set packet-size $size
$exp5 set burst-time $burst
$exp5 set idle-time $idle
$exp5 set rate $rate
$exp5 attach-agent $udp5


#Cria um agente Null , para sincronização de tráfeg
#o e vincula ao nodo n1
set null0 [new Agent/Null]
$ns attach-agent $n3 $null0

set sink [new Agent/TCPSink]
$ns attach-agent $n6 $sink

#Conecta os agentes para estabelecer a comunicação
$ns connect $cbr0 $null0
$ns connect $cbr1 $null0
$ns connect $tcp2 $sink
$ns connect $udp5 $null0

set ftp [new Application/FTP]
$ftp attach-agent $tcp2

# Identificação do fluxo de pacotes
# O parametro 'fid_' significa 'flow id'.
$cbr0 set fid_ 1
$cbr1 set fid_ 2
$ftp set fid_ 3
$udp5 set fid_ 4

#Setando a cor dos pacotes
$ns color 1 Blue
$ns color 2 Red
$ns color 3 Black
$ns color 4 White

#Faz o schedule dos eventos do agente
$ns at 0.0 "$ftp start"
$ns at 11.5 "$ftp stop"

$ns at 0.5 "$cbr0 start"
$ns at 12.0 "$cbr0 stop"

$ns at 0.8 "$cbr1 start"
$ns at 12.0 "$cbr1 stop"

$ns at 0.0 "$exp5 start"
$ns at 10.5 "$exp5 stop"

#Setar a orientação entre ele
$ns duplex-link $n0 $n2 1Mb 10ms DropTail
$ns duplex-link $n1 $n2 1Mb 10ms DropTail
$ns duplex-link $n3 $n2 1Mb 10ms DropTail
$ns duplex-link $n0 $n4 1Mb 10ms DropTail
$ns duplex-link $n4 $n5 1Mb 10ms DropTail
$ns duplex-link $n5 $n3 1Mb 10ms DropTail
$ns duplex-link $n1 $n5 1Mb 10ms DropTail
$ns duplex-link $n4 $n6 1Mb 10ms DropTail

#Re-roteamento
$ns rtmodel-at 1.5 down $n2 $n3
$ns rtmodel-at 4.0 up $n2 $n3

# Seta o protocolo de roteamento alternativo
$ns rtproto DV

#Chama o procedimento finish apos 13 segundos
#de simulação
$ns at 13.0 "finish"

#Executa a simulação
$ns run
A saída no NAM é a Seguinte

Implementação de Novos Protocolos de Roteamento

Para a implementação de novos protocolos de roteamento é necessário definir novas classes Agentes, que irão conter o código do novo protocolo, e a classe packet onde serão definidos os novos tipos de pacotes do protocolo.

Agentes

Os agentes representam pontos finais onde os pacotes da camada de rede são construídos ou consumidos e são usados na implementação de várias camadas. A classe Agente tem uma implementação parcial em OTcl e parte em C++. A implementação em C++ encontra-se nos arquivos ~ns/agente.cpp e ~ns/agent.h e a parte em OTcl está em ~ns/tcl/lib/ns-agent.tcl

Estado do Agente

A Classe Agente em C++ inclui diversos campos que simulam um pacote antes de ser enviado. Estes campos são os seguintes:

- Addr_: Endereço do Nodo de origem
- Dst_: Para onde os pacotes estão sendo enviados
- Size_: Tamanho do pacote em bytes
- Type_: Tipo de pacote
- Prio_: O campo IP de prioridade
- Flags _: os flags do pacote
- Defttl_: O valor default do ttl
Essas variáveis podem ser modificadas em qualquer classe derivada da classe Agente.

Métodos

A classe Agente suporta a geração e recepção de pacotes. As seguintes funções são responsáveis por estas funções e geralmente não são sobrescritas pelas classes derivadas:

Packet* allocpkt(): Aloca um novo pacote e assinala seus campos. Esta função preenche os seguintes campos em um tipo de pacote normal: uid, ptype, size e os seguintes campos do cabeçalho IP: src,dst,plowid,prio,ttl. E também completa de zeros os seguintes Flags do cabeçalho: ecn,pri,usr1,usr2. Qualquer informação extra no pacote deve ser tratada nas funções derivadas da classe Agente.

Packet* allocpkt(int):Aloca um novo pacote com o "data payload" de n bytes e assinala seus campos.

As funções a seguir também são definidas na classe Agente, mas devem ser sobrescritas pelas classes que derivam Agente:

Void timeout(timeout number):Função de controle do timeout

Void recv(Packet*, Handler *): Função principal que controla a recepção de pacotes do Agente. Este função é o ponto de entrada na recepção de pacotes e é invocada pelos outros nós quando enviam um pacote. Na maioria dos casos os Agentes não fazem uso do segundo argumento(este é o handler do pacote que está enviando o pacote).

Usando o OTcl

Os agentes são criados através do OTcl e o estado interno do agente pode ser modificado através da função set em TCL. É interessante notar que alguns campos que definem os estados do Agente em TCL só existem em OTCL e não é possível acessá-los a partir do C++.

Criação e manipulação dos agentes

O processo de criação de um Agente em TCL é o seguinte:

Métodos em OTcl

Os métodos do Agente implementados em OTcl são os seguintes :
port: Identificador da porta do Agente
dst-port:Identificador da porta destino
attach-source:Cria e liga um Objeto Source a um Agente

Exemplo: Agentes TCP

O seguinte código OTcl cria um agente TCP e atribui seus estados:

A instrução new Agente/TCP resulta na criação do objeto C++TcpAgent. Seu construtor invoca primeiro o construtor da classe base Agente e depois efetua seus proprios bindings.

O Agente TcpAgent é inicializado nesse exemplo quando o FTP recebe a diretiva start no tempo 1.2 segundos. A operação start é definida em ~ns/tcl/lib.ns-source.tcl

Application/FTP instproc start{}{
[$self agente] send –1
}
 

Neste caso a variável agente está vinculada ao agente TCP e send –1 é analogo a enviar um arquivo extremamente grande. A chamada send faz com que o agente TCP comece a enviar pacotes.
Neste caso a função output do Agente TCP efetua as seguintes chamadas:

void TcpAgent::output(int seqno, int reason) {  
         &nbs p; Packet * p=  allocpkt();  
         hdr_tcp * tcph = (hdr_tcp*) p->access(off_tcp); 
         double now = Scheduler::instance().clock();  
         tcph->seqno = seqno; 
         tcph->ts() = now; 
         Connector::send(p,0); 
         ... 
}
Esta rotina primeiro aloca um novo pacote (com seus cabeçalhos e os cabeçalhos IP preenchidos) e depois preenche os campos especificos para o Agente. Para encontrar o cabeçalho TCP no pacote a variavel off_tcp_ deve estar apropriadamente inicializada. O método access da classe Packet retorna o respectivo cabeçalho, o qual é preenchido e o send da classe Conector é chamado para enviar o pacote. O operador de escopo :: do C++ foi utilizado para evitar chamar a função send do Agente.
 
 

No agente que está recebendo os pacotes a entrada é processada através das funções recv() e ack().

Referências

1. Demos para Simulação (University of Southern California)

2. Tutorial NS de Marc Greis

3. Base de Dados de Scripts de Simulação

4. Tutorial da UFMG (Autor:Ricardo Rabelo)

5. Tutorial de Jae Chung e Mark Claypoolos

Download dos scripts mostrados nesse trabalho