Manuale del Network Simulator 2 v12
Transcript
Manuale del Network Simulator 2 v12
DIIT Dipartimento di Ingegneria Informatica e delle Telecomunicazioni Facoltà di Ingegneria Manuale “Rapido” del Network Simulator 2 V. 1.2 a cura dell’Ing. Fernando Menta Novembre 2001 1. Introduzione Il Network Simulator 2 o ns2 è un programma in grado di simulare il comportamento di una rete qualsiasi presa in esame dall’utente e di fornirne i risultati desiderati. Inizialmente l’ns è stato sviluppato solo in ambiente Unix, solo di recente è disponibile una versione anche per Windows95/98/2000, quindi è chiaro che in quanto a documentazione, affidabilità e disponibilità, l’ns in ambiente Unix è preferibile, infatti tutto il lavoro di ricerca al momento viene eseguito sotto questa piattaforma. 1.1. Uso del simulatore Sia in Unix che in Windows si lavora in un contesto a riga di comando (per esempio sotto Windows si usa il prompt del DOS) per lanciare il simulatore. L’input del simulatore è costituito da un file di testo che contiene tutte le informazione sulla rete da simulare, quindi si esegue l’ns digitando sul prompt: ns miarete.tcl <invio> Dopo di ciò si attende che il simulatore termini (il tempo di attesa dipende dalla complessità della rete, ma soprattutto dalla lunghezza della simulazione e dai bit-rate in gioco), esso fornirà un file di output che potrà essere analizzato con diversi strumenti. ns.exe Miarete.tcl Out.nam Il file di input è un file di testo con estensione TCL (Tool Command Language), esso contiene sia le specifiche della rete da esaminare, che le direttive del simulatore (fase di schedulazione), sotto forma di linguaggio script (TCL). E’ chiaro allora che questo linguaggio rappresenta il cuore dell’ns, perché tramite esso possiamo impostare la topologia della rete, le specifiche dei vari link e soprattutto i vari protocolli di rete e di trasporto che entrano in competizione nella rete. Le direttive del simulatore sono costituite dal tempo di simulazione, dalla gestione in tempo reale della simulazione e dalla gestione del file di output che può essere personalizzata. 2 Le istruzioni che compongono il linguaggio TCL per ns si trovano documentate nel file nsman.html. 1.2. Descrizione del simulatore La potenza dell’ns sta nel fatto che non ha praticamente nessun limite nel gestire le simulazioni, in quanto riesce ad integrare e a comprendere tutti i livelli di una connessione dal livello fisico fino al livello applicativo; inoltre esso è espandibile ed è attualmente in espansione, e tutti possono contribuire ad espanderlo. Infatti per l’ns sono disponibili i file sorgente scritti in linguaggio C++, e tramite una profonda conoscenza dell’ns, del C++ e del linguaggio TCL è possibile implementare nuovi protocolli personali con nuovi comandi TCL inseriti dall’utente, e quindi ricompilare il tutto in modo da ottenere un nuovo ns.exe in grado di comprendere tali nuovi comandi. Attualmente (ns versione 2.1 beta 8) è possibile gestire le seguenti entità: 1.2.1. Livello fisico: -bit-rate -tempo di ritardo 1.2.2. Livello data-link: -bidirezionalità del link -LAN -protocollo di MAC: CSMA/CD -gestione automatica degli errori con modello statistico di errore Livello di rete: -instradamento automatico con algoritmo di Dijkstra -gestione della coda: Droptail FQ SFQ DRR RED CBQ -Unicast routing e multicast routing. Livello di trasporto: -protocolli TCP: 3 Tahoe Reno Newreno Vegas Sack1 Fack Fulltcp -altri protocolli: UDP RTP RTCP RAP TFRC Livello di applicazione: -sorgenti di traffico: CBR (constant bit rate) TELNET FTP -altre sorgenti programmabili sono: traffico ON/OFF di tipo esponenziale traffico ON/OFF con distribuzione paretiana traffico predefinito in un file apposito. -Interazione tra siti web con protocollo http. Le applicazioni che rappresentano le nostre sorgenti di traffico dati, vengono affidate ai protocolli di trasporto. Sorgenti di traffico particolari quali video, audio ed altri tipi di dati devono essere modellizzate con gli strumenti già presenti, oppure create ad hoc ed inserite nella libreria TCL. Sia le sorgenti di traffico che tutti i vari protocolli supportati sono corredati con tutti i loro possibili parametri, che possono essere modificati anche in tempo reale. E’ possibile implementare reti wire-less e satellitarie. 1.3. Accessori Per esaminari i dati di output del simulatore è possibile utilizzare due programmi in dotazione con l’ns, che sono il NAM (Network Animator) e l’Xgraph (solo per Unix). 4 Il NAM permette di visualizzare graficamente la topologia creata col file TCL ed i flussi di dati tra i vari nodi in tempo reale, compresa la perdita dei pacchetti effettuata dal controllo di congestione dei router. E’ anche possibile visualizzare dei semplici grafici qualitativi sulla bontà del collegamento. L’ Xgraph è un programma specializzato solo in grafici. E’ infine possibile visualizzare i risultati di qualsiasi parametro semplicemente attaccando dei flowmonitor nei link di interesse e leggendo periodicamente dati che è possibile memorizzare in un file di testo (vedere ns-man). 1.4. Note per chi legge Tutte le parole in corsivo che seguiranno, indicheranno parole chiave del simulatore, mentre le parole non in corsivo saranno quelle che possono essere definite a piacere dall’utente. Le parole racchiuse tra < > hanno un significato astratto e possono essere sia variabili che numeri. 5 2. Classe Simulator Abbiamo già detto che l’ns è sviluppato essenzialmente in linguaggio C++, precisamente esso è costituito da una collezione di classi che descrivono le varie entità del simulatore. La classe più importante è la classe Simulator, in quanto, per effettuare una simulazione occorre prima di tutto creare un oggetto Simulator, esso identifica la simulazione stessa. Per creare un oggetto Simulator occorre scrivere: set ns [new Simulator] in questo modo la variabile ns sarà un oggetto simulazione, ovvero la simulazione stessa. Il simulatore viene pilotato ad eventi, gli eventi sono forniti dal cosiddetto ‘schedulatore’, esso si occupa di eseguire gli eventi in ordine di tempo ed uno alla volta (single threaded); se più eventi risultano essere schedulati nello stesso istante, allora essi verranno eseguiti in ordine di schedulazione. Esistono quattro tipi di schedulatori derivati dalla classe Scheduler, ognuno dei quali è implementato usando strutture dati differenti: • list; • heap; • calendar queue (è quello di default); • real-time. 2.1.1. List (classe Scheduler/List) Viene implementato con una semplice struttura a lista i cui elementi sono gli eventi della simulazione sistemati in ordine crescente di tempo. L’inserzione o la cancellazione di un evento richiede una scansione della lista per trovare il punto di inserzione o di cancellazione, quindi tale operazione può richiedere parecchio tempo se il numero di eventi è elevato (proporzionale ad n). 2.1.2. Heap (classe Scheduler/Heap) Con questo schedulatore la struttura cambia da semplice lista ad una struttura heap, essa risulta essere conveniente quando abbiamo un elevato numero di eventi, per cui l’inserzione o la cancellazione di un evento viene ottimizzata, e richiede un tempo proporzionale a O(log n), dove con n si indica il numero di eventi. Il funzionamento di questo schedulatore non è stato ancora completamente verificato. 2.1.3. Calendar (classe Scheduler/Calendar) Questo schedulatore usa una struttura dati analoga ad un calendario, la cui descrizione formale è stata data da R. Brown. 6 2.1.4. RealTime (classe Scheduler/RealTime) Questo schedulatore cerca di sincronizzare l’esecuzione degli eventi in tempo reale. Essa è quindi utile in fase di emulazione, quando andiamo ad interfacciare la nostra rete simulata, con una rete reale esterna al nostro elaboratore. 2.2. Metodi La classe Simulator implementa una serie di metodi, indispensabili per imbastire la simulazione: • use-scheduler <schedulatore>: specifica il tipo di schedulatore da usare nella simulazione, per default viene usato lo schedulatore Calendar; • now: restituisce il tempo corrente in cui si trova la simulazione, es.: set time [$ns now] • at <time> <evento>: schedula l’evento specificato nel tempo specificato in secondi, es.: $ns at 10 “$ftp start” fa partire una sorgente ftp all’istante 10; • run: lancia lo schedulatore e quindi la simulazione, es.: $ns run • halt: mette in pausa lo schedulatore; • namtrace-all <file handler>: abilita la memorizzazione dei dati della simulazione istante per istante nel file indicato da <file handler> (vedi appendice A.2). 7 3. Livello Fisico 3.1. Creare la topologia di rete 3.1.1. Nodi La nostra rete sarà composta essenzialmente da vari nodi interconnessi tra loro tramite link. Per creare l’istanza di un nodo occorre scrivere: set mio_nodo [$ns node] In questo caso ‘mio_nodo’ rappresenta l’istanza di un generico nodo nel caso molto comune di flat addressing-routing. Se invece si vuole creare una topologia gerarchizzata è necessario specificare per ogni nodo un numero aggiuntivo corrispondente al suo livello gerarchico: set mio_nodo [$ns node <x>] dove x rappresenta un numero intero (es: 1,2,3,…) 3.1.2. Links Tra due generici nodi possono essere definiti links unidirezionali o bidirezionali: $ns simplex-link <nodo1> <nodo2> <bw> <delay> <tipo_di_coda> simplex-link non fa altro che creare un link unidirezionale dal nodo1 al nodo2, invece: $ns duplex-link <nodo1> <nodo2> <banda> <ritardo> <tipo_di_coda> crea un link bidirezionale tra il nodo1 ed il nodo2, duplex-link è equivalente a due simplex-link: uno dal nodo1 al nodo2, ed uno dal nodo2 al nodo1. Questa istruzione crea un oggetto link contenente i seguenti parametri: • bandwidth_: rappresenta la capacità del link espressa in bit/s; • delay_: rappresenta il tempo di propagazione del segnale espresso in secondi, quindi, indirettamente, rappresenta la lunghezza del link; • <tipo_di_coda> rappresenta il tipo di active queue management (AQM) utilizzato nel router del link (DropTail, RED, ecc.); i vari tipi di gestione delle code verranno illustrati nel seguente capitolo. Il link può essere istanziato come un oggetto con il seguente codice: set mio_link [[$ns link <nodo1> <nodo2>] link] cioè la variabile ‘mio_link’ sarà un’istanza del relativo link specificato univocamente dai due nodi. A questo punto è possibile cambiare in qualsiasi momento sia la banda che il ritardo del link con i seguenti metodi: $ns bandwidth <nodo1> <nodo2> <banda> 8 oppure: $mio_link set bandwidth_ <val> Nel primo metodo il link viene specificato esplicitamente con i nodi, invece nel secondo viene specificato implicitamente tramite l’istanza ‘mio_link’. Allo stesso modo è possibile modificare il ritardo del link sostituendo nelle due istruzioni precedenti la parola chiave delay al posto di bandwidth. 9 4. Livello di Rete 4.1. Gestione delle code La gestione delle code è implementata mediante vari oggetti coda che discendono tutti dalla classe Queue, essi vengono qui di seguito descritti. 4.1.1. Queue E’ la classe generale degli oggetti coda, i suoi parametri fondamentali sono: • limit_: (default: 50) capacità del buffer in pacchetti; • blocked_: (default: false) indica se la coda è bloccata, cioè incapace di trasmettere pacchetti; • unblock_on_resume_: (default: true) indica che una coda dovrebbe sbloccarsi nel momento in cui l’ultimo pacchetto spedito è stato trasmesso(ma non necessariamente ricevuto). Per modificare la capacità del buffer limit_, vi sono due possibilità: $ns queue-limit <n1> <n2> <limit> oppure: set mio_link [[$ns link <nodo1> <nodo2>] queue] $mio_link set limit_ <val> n1 è il nodo che ospita il buffer, mentre n2 ed n1 identificano il link associato al buffer da modificare. Dalla classe Queue sono stati derivati i seguenti oggetti: 4.1.2. DropTail Essa implementa una semplice coda FIFO in cui i pacchetti che giungono dalle varie sorgenti ad essa connesse vengono memorizzati nel buffer e poi spediti; nel caso in cui non vi sarà più spazio nel buffer per allocare ulteriori pacchetti, essi verranno irrimediabilmente scartati. Per questo tipo di oggetto non sono previsti né metodi, né parametri e né variabili di stato. 4.1.3. FQ (Fair Queueing) Implementa un algoritmo che tenta di allocare per ciascun flusso le stesse risorse di buffer (ed indirettamente le stesse risorse di banda), distinguendo ciascun flusso in base alle informazioni estrapolate dall’header di pacchetto (ID di nodo o di pacchetto). Lo sforzo computazionale di questo algoritmo è notevole, ed aumenta col numero di flussi da gestire. Il numero massimo di flussi gestibile per garantire la fairness è fissato a 32. Parametri: 10 • secsPerByte_: (default: 0) nessuna descrizione. 4.1.4. SFQ (Stochastic Fair Queueing) Implementa la stessa filosofia dell’FQ, ma in maniera differente. Usa delle tabelle di hash per indirizzare i pacchetti verso le code corrispondenti. Le garanzie di fairness sono probabilistiche e dipendono dalla dimensione dell’indice di hash rispetto al numero di flussi trattati dal router. Parametri: • maxqueue_: (default: 40) nessuna descrizione; • buckets_: (default: 16) numero totale di secchi da usare per la tabella di hash, quindi il numero totale di flussi consentiti. 4.1.5. DRR (Deficit Round Robin) Implementa la stessa filosofia dell’FQ, ma in maniera differente. Esso infatti cerca di risolvere i problemi di unfairness che possono scaturire se le dimensioni dei pacchetti spediti variano da flusso a flusso. Parametri: • buckets_: (default: 16) numero totale di secchi da usare per la tabella di hash; • blimit_: (default: 25000) numero totale di bytes consentiti su tutti i flussi; • quantum_: (default: 250) numero totale di bytes che un flusso può spedire; • mask_: (default: 0) se settato ad 1, significa che un particolare flusso consiste di pacchetti aventi lo stesso ID di nodo (e possibilmente ID di porta differente), altrimenti un flusso consiste di pacchetti aventi lo stesso ID sia di nodo che di porta. 4.1.6. RED (Random Early Detection) Questa politica di gestione della coda attua uno scarto preventivo e casuale dei pacchetti ancor prima che il buffer vada in saturazione. Essa non fa altro che calcolare la coda media, ed in base ad essa stabilire la probabilità di scarto dei singoli pacchetti che giungono al router. I quattro parametri fondamentali che governano la probabilità di scarto sono: • q_weight_: (default: 0.002) peso utilizzato nell’algoritmo per il calcolo della coda media (è una sorta di filtraggio passa-basso della coda istantanea); • thresh_: (default: 5) soglia minima per la coda media in pacchetti, al di sotto di questo valore la probabilità di scarto è nulla; • maxthresh_: (default: 15) soglia massima per la coda media in pacchetti, quando la coda media raggiunge questo valore la probabilità di scarto vale 1/linterm; 11 • linterm_: (default: 10) al variare della coda media tra thresh_ e maxthresh_, la probabilità di scarto varia tra 0 e 1/linterm. Variabili di stato: • curq_: valore della coda in pacchetti; • ave_: valore della coda media in pacchetti; • prob1_: probabilità di scarto. Altri parametri configurabili sono: • bytes_: (default: false) abilita il byte-mode, con cui la dimensione del pacchetto in arrivo influisce sulla probabilità di scarto; • queue-in-bytes_: (default: false) abilita la misura della dimensione della coda media ave_ in bytes anziché in pacchetti. Se abilitata, questa opzione fa in modo di moltiplicare i parametri thresh_ e maxthresh_ per il valore contenuto in mean_pktsize_; • wait_: (default: true) abilita la distribuzione temporale uniforme dello scarto dei pacchetti; • setbit_: (default: false) abilita il settaggio del “congestion indication bit” presente nell’header dei pacchetti, anziché effettuare lo scarto diretto dei pacchetti; • drop_tail_: (default: true) abilita lo scarto dei pacchetti che giungono al router quando la coda media del RED eccede il valore maxthresh_; • gentle_: (default: false) attiva la proprietà di incrementare la probabilità di scartare pacchetti prob1_, quando ave_ eccede il valore maxthresh_, a partire dal valore 1/linterm_. 4.1.7. CBQ (Class Based Queueing) Questo tipo di gestione della coda è basato sulle classi di traffico, cioè esso darà maggiori risorse alle classi di traffico a più alta priorità. La priorità la si setta al livello sorgente come si vedrà più avanti. Metodi: • $cbq insert <classe1>: inserisce la classe di traffico classe1 nella gestione del relativo link CBQ; • $cbq bind <classe1> <id1> [<id2>]: associa la classe di traffico classe1 ai pacchetti che hanno un ID pari a id1 o che hanno ID compreso nel range [id1, id2]; • $cbq algorithm <alg>: seleziona un algoritmo interno del CBQ. • ancestor-only; • top-level; • formal. L’oggetto CBQ, a sua volta, ha due discendenti: 12 • CBQ/WRR: questa sottoclasse implementa una schedulazione di tipo weighted round-robin tra le varie classi dello stesso livello di priorità. Parametro: maxpkt_: (default: ) la massima dimensione di un pacchetto in bytes; serve a calcolare la massima allocazione di banda. • CBQ/PRR: questa sottoclasse implementa una schedulazione di tipo packet-by-packet roundrobin tra le varie classi dello stesso livello di priorità. Le classi di traffico vengono definite tramite gli oggetti classe-CBQ. Metodi della classe-CBQ: • $classcbq setparams <parent> <okborrow> <allot> <maxidle> <prio> <level>: serve ad impostare i parametri fondamentali della classi di traffico CBQ predefinita; • $classcbq parent <cbqcl|none>: specifica un parente della classe di traffico predefinita; se si specifica none, allora la classe di traffico è di tipo root, cioè senza antenati; • $classcbq newallot <a>: specifica l’allocazione del link per la classe di traffico predefinita, a∈[0, 1]; • $classcbq install-queue <q>: installa un oggetto Queue nel link CBQ. Quando un oggetto CBQ viene creato, esso inizialmente non contiene nessuna coda. Parametri della classe-CBQ: • okborrow_: (default: ) valore booleano che indica se la classe di traffico specificata ha il permesso di farsi prestare banda dalle classi parente; • allot_: (default: ) massima frazione di banda del link allocata per la classe di traffico specificata, deve essere un numero reale compreso tra 0 ed 1; • maxidle_: (default: ) tempo massimo di attesa in coda per i pacchetti della classe di traffico predefinita. E’ possibile assegnare il valore auto per la decisione automatica di tale tempo; • priority_: (default: ) livello di priorità per la classe di traffico, rispetto alle altre classi. Il range di valori va da 0 (livello più alto) a 10 (livello più basso). Più di una classe può avere lo stesso livello di priorità; • level_: (default: ) livello di una classe di traffico nell’albero della divisione di banda. I nodi foglia sono considerati al livello 1, i loro parenti al livello 2, ecc.; • extradelay_: (default: ) aggiunge il ritardo specificato alla classe di traffico che si vuole ulteriormente ritardare. Quando creiamo un oggetto coda tramite le istruzioni simplex-link o duplex-link, i parametri su elencati contengono i valori di default definiti dallo sviluppatore della classe, per modificare tali parametri esistono due metodologie. La prima è quello più usuale, in cui una volta istanziato il link 13 ‘mio_link’, che supponiamo essere di tipo RED, andiamo a modificare, ad esempio, il parametro thresh_: $mio_link set thresh_ <val> Il secondo metodo è meno ortodosso, poiché consiste nel modificare i parametri del generico oggetto coda, per es., per lo stesso oggetto RED si scrive: Queue/RED set thresh_ <val> Questa istruzione non sortisce effetti fino a quando non si crea un nuovo oggetto coda, che sarà una copia del generico oggetto coda. In definitiva, se si vogliono creare più oggetti coda dello stesso tipo e tutti con le stesse caratteristiche, è conveniente andare a modificare i parametri di default che ci interessano e successivamente creare i vari link. Per leggere un parametro (per es. lo stesso parametro thresh_ del RED) si deve seguire la seguente sintassi: set minth [mio_link set thresh_] 14 5. Livello di Trasporto – Oggetti Agent La classe Agent implementa un gran numero di protocolli di trasporto, il loro numero comunque è in continua crescita, man mano che escono nuove versioni dell’ns. La classe Agent è la classe generale da cui discendono i vari protocolli di trasporto, essa contiene il seguente set di parametri: • addr_: (default: 0) indirizzo del nodo su cui risiede l’Agent; • dst_: (default: 0) indirizzo del nodo di destinazione; • size_: dimensione dei pacchetti da spedire in bytes; • type_: descrive il tipo di pacchetto a seconda del protocollo; • fid_: (default: 0) identificatore di flusso IP; • prio_: (default: 0) livello di priorità IP; • flags_: (default: 0) flag di pacchetto; • defttl_: valore di default del ttl IP. Tali parametri vengono scritti nell’header di ogni pacchetto inviato. Di seguito viene riportata la lista di tutti i protocolli di trasporto supportati in ns: 5.1.1. Trasmettitori TCP • TCP: trasmittente TCP versione “Tahoe”; • TCP/Reno: trasmittente TCP versione “Reno”; • TCP/NewReno: trasmittente TCP versione “NewReno”; • TCP/Sack1: trasmittente TCP versione “Sack”; • TCP/Fack: trasmittente TCP versione “Fack”; • TCP/Vegas: trasmittente TCP versione “Vegas”; 5.1.2. Ricevitori TCP • TCPSink: ricevitore TCP, non usato nel caso di FullTcp; 5.1.3. Altri protocolli UDP, RAP, TFRC, RTP, RTCP, IVS, CtrMcast, Message, SRM, rtProto/DV, Tap, LossMonitor, Null. Per creare un oggetto Agent qualsiasi (es. TCP) occorre eseguire: set mio_agent [new Agent/TCP] 15 5.1.4. Metodi della classe Simulator • attach-agent <nodo> <agent>: attacca <nodo> ad <agent> precedentemente creati; • simplex-connect <src> <dst>: instaura una connessione unidirezionale tra gli agent <src> e <dst>, esso semplicemente imposta l’indirizzo di destinazione e la porta di destinazione di <src> e di <dst>; • connect <src> <dst>: instaura una connessione tra gli agent <src> e <dst>, esso consiste in una doppia chiamata a simplex-connect con i parametri scambiati; • create-connection <srctype> <src> <dsttype> <dst> <pktclass>: questa è una istruzione composta che crea un agent di tipo <srctype> e lo associa a <src> (attach-agent), un secondo agent di tipo <dsttype> e lo associa a <dst>; infine esegue la connect tra <src> e <dst>. Valore restituito: handle dell’agente <src>; • create-connection-list <srctype> <src> <dsttype> <dst> <pktclass>: questa istruzione è molto simile alla precedente, solo che invece di restituire solo l’handle all’agente sorgente, restituisce una lista di agenti sorgente e destinazione. 5.1.5. Metodi della classe Agent • port: restituisce il numero di porta a cui è attaccato l’agent; • dst-port: restituisce il numero di porta della destinazione contenuto nella variabile dst_port_; • attach-app <application>: attacca l’applicazione specificata al nostro agent. Di seguito vengono riportate tutte le varie sottoclassi della classe Agent. 5.2. Protocolli TCP Esistono due classi di protocolli TCP: • quelle che effettuano connessioni unidirezionali, cioè un trasmettitore che invia pacchetti dati ed un ricevitore che si limita ad inviare pacchetti ACK; • quelle che effettuano connessioni bidirezionali, in cui ambedue gli agenti sono in grado di trasmettere ed inviare ACK. I primi ad essere stati implementati in ns sono quelli che effettuano le connessioni unidirezionali, cioè si è preferito implementare separatamente, un agent che fungesse solo da trasmettitore, ed un agent che fungesse solo da ricevitore, anziché implementare un unico agent che supportasse ambedue le funzionalità. Ciò è stato fatto al fine di snellire il codice di ogni singolo agent e quindi di rendere le simulazioni più veloci. Il TCP non fa altro che ricevere segmenti dati da un’applicazione di livello superiore, e di ritrasmetterli, secondo la propria modalità, in rete. 16 Parametri di configurazione • window_: (default: 20) limite superiore della finestra di congestione espressa in pacchetti. Questo parametro rappresenta virtualmente la advertised window che il ricevente TCP invia al trasmittente; nel mondo reale questo parametro viene aggiornato automaticamente, invece in ns ciò non avviene per cui occorre impostare manualmente tale parametro. • maxcwnd_: (default: 0) se diverso da 0 rappresenta il massimo valore in pacchetti della finestra di congestione, se vale 0 tale limite non esiste. • windowInit_: (default: 1) dimensione iniziale della finestra di congestione per la fase di slowstart. • windowOption_: (default: 1) stabilisce l’algoritmo usato per gestire la finestra di congestione; l’algoritmo 1 è lo standard. • windowConstant_: (default: 4) viene usato sol se windowOption_ è diverso da 1. • windowThresh_: (default: 0.002) costante utilizzata nel calcolo della finestra media awnd_. • overhead_: (default: 0) ritardo casuale medio (in secondi) aggiunto tra una trasmissione e la successiva. Se vale 0 non viene aggiunto nessun ritardo. Questo parametro ha effetti solo nella versione del TCP Tahoe, ed è stato aggiunto solo per evitare, in maniera artificiosa, gli effetti di fase nel caso di più sorgenti che trasmettono contemporaneamente. • ecn_: (default: false) se vale true attiva il riconoscimento del bit Explicit Congestion Notification (ECN) presente nei pacchetti. • packetSize_: (default: 1000) la dimensione in bytes dei pacchetti trasmessi. • bugFix_: (default: true) elimina un bug rilevato quando si verificavano delle fast-retransmit multiple dovute a pacchetti scartati in una singola finestra. Questo parametro può quindi essere considerato irrilevante. • slow_start_restart_: (default: true) decide se ripartire da una fase di slow-start dopo una fase di idle. • tcpTick_: (default: 0.1) granulosità del clock in secondi, che scandisce lo scorrere del tempo in un qualsiasi sistema operativo. L’ns emula tale clock, che in realtà varia da un sistema operativo all’altro, con un clock virtuale, per dare la possibilità al ricercatore di simulare il comportamento del protocollo al variare del sistema operativo in cui esso deve agire. Questo parametro ha l’importante effetto di determinare la precisione con cui viene valutato il roundtrip-time. Il valore di default non è comunque un valore standard, e quindi deve essere modificato ad hoc. • maxrto_: (default: 64) valore massimo del timeout in secondi. 17 • maxburst_: (default: 0) se diverso da 0, rappresenta il massimo numero di pacchetti che la sorgente può spedire in risposta ad un singolo ACK. Di tutti questi parametri, quelli ovviamente più importanti (cioè quelli che solitamente si ha il bisogno di modificare) sono: window_ e packetSize_, es.: $mio_agent set window_ <val> Variabili di stato • dupacks_: numero di ACK duplicati rilevati. • seqno_: ultimo numero di sequenza dei dati trasmessi. • t_seqno_: numero di sequenza dell’ultimo pacchetto dati trasmesso. • ack_: ultimo numero di sequenza rilevato negli ACK; • cwnd_: valore attuale della finestra di congestione in pacchetti. • awnd_: valore attuale della finestra di congestione media, essa è una versione filtrata in modalità passa-basso della cwnd_. • ssthresh_: valore attuale della soglia di slow-start. • rtt_: valore attuale del round-trip time in secondi. • srtt_: stima del round-trip time. • rttvar_: stima della deviazione media del round-trip time. • backoff_: fattore di back-off del round-trip time. • maxseq_: massimo numero di sequenza spedito. Di seguito vengono riportati i protocolli supportati da ns dal più vecchio (Tahoe) al più recente. TCP Questa sottoclasse è stata la prima ad essere stata sviluppata, ed in particolare essa implementa la versione di TCP detta Tahoe. TCP/Reno Il TCP Reno è molto simile al Tahoe, eccetto il fatto che esso, in più, implementa il fast recovery. TCP/NewReno Questa versione presenta una miglioria rispetto alla versione Reno, che riguarda la correzione del difetto di quest’ultimo di bloccarsi in alcuni casi particolari. TCP/Sack1 Versione “Selective ACK” che implementa il “Selective repeat”. TCP/Fack Versione “Forward ACK”, è una modifica del TCP Sack. 18 TCP/Vegas Versione “Vegas” con un accurato controllo delle perdite. 5.2.1. Ricevitori TCP Abbiamo detto che per i protocolli a connessione unidirezionale occorrono dei protocolli in ricezione che si limitino esclusivamente ad inviare pacchetti ACK, ecco quindi che vengono di seguito illustrati i protocolli TCP di ricezione da abbinare ai protocolli di trasmissione. TCPSink E’ una sottoclasse della classe Agent, che si occupa esclusivamente di inviare pacchetti di ACK ai trasmettitori TCP. In particolare, un oggetto TCPSink genera un ACK per ogni pacchetto ricevuto. Parametri di configurazione • packetSize_: (default: 40) dimensione in bytes del pacchetto di ACK. TCPSink/DelAck E’ una sottoclasse della classe TCPSink che simula un ricevitore TCP che invia meno di un ACK per ogni pacchetto dati ricevuto, cioè gli stessi ACK vengono inviati con un ritardo pari a interval_. Il protocollo agisce in maniera da ritardare gli ACK se i relativi pacchetti dati sono in ordine di sequenza, per i pacchetti fuori ordine di sequenza, invece, gli ACK vengono generati immediatamente. Parametri di configurazione • interval_: (default: 0.1) ritardo in secondi tra l’invio di un ACK ed il successivo. TCPSink/Sack1 Sotto classe della classe TCPSink utilizzabile quando si utilizza un trasmettitore di tipo SACK. Essa invia un certo numero maxSackBlocks_ di blocchi di informazione di tipo SACK. Parametri di configurazione • maxSackBlocks_: (default: 3) numero di blocchi di informazione SACK da inviare in un unico ACK. TCPSink/Sack1/DelAck Sotto classe della classe Sack1 che implementa gli ACK ritardati analogamente a quanto già visto sopra. Es.: set sorgente [new Agent/TCP/Reno] set ricevente [new Agent/TCPSink] 19 $ns connect $sorgente $ricevente 5.2.2. Connessioni bidirezionali TCP/FullTcp Sottoclasse del TCP in grado di effettuare connessioni bidirezionali. La versione implementata è la Reno. A differenza dei protocolli a connessione unidirezionale, essa effettua la creazione e l’abbattimento di una connessione tramite lo scambio di messaggi SYN/FIN con il meccanismo 3way handshake. Sia il trasmittente che il ricevente devono essere dichiarati nella stessa maniera. Parametri di configurazione • segsperack_: (default: 1) segmenti da ricevere prima di generare un ACK; • segsize_: (default: 536) dimensione del segmento in bytes; • tcprexmtthresh_: (default: 3) numero di ACK duplicati da ricevere prima di far scattare la fase di fast retransmit; • iss_: (default: 0) numero di sequenza iniziale da spedire; • nodelay_: (default: false) disabilita l’algoritmo di Nagle dalla parte del trasmittente; • data_on_syn_: (default: false) spedisce dati anche nei SYN; • dupseg_fix_: (default: true) evita la fase di fast retransmit dovuta ad ACK duplicati; • dupack_reset_: (default: false) ; • interval_: (default: 0.1) ; 5.2.3. Altri aspetti del TCP Nelle simulazioni non vengono spediti dei dati reali, ma soltanto dei pacchetti con una certa dimensione, dove le uniche informazioni significative sono contenute nell’header. Header La struttura dell’header di un pacchetto non è complessa come quella delle reali comunicazioni TCP, ma contiene soltanto i seguenti campi: • ts_: (time stamp) tempo di partenza del pacchetto dalla sorgente; • ts_echo_: valido per gli ACK, esso rappresenta il campo time stamp del pacchetto associato all’ACK; • seqno_: numero di sequenza per il pacchetto, sia dati che ACK; • reason_: ; 20 5.3. Agent/Null Questo è una sottoclasse della classe Agent, che però è del tutto inattiva, e quindi può essere utilizzata solo come ricevitore di pacchetti, ma solo per le trasmissioni non ACK-clocked, in cui la sorgente dei dati non richiede nessuna collaborazione al destinatario (come l’UDP). Variabili si stato • sport_: ; • dport_: ; 5.4. Agent/LossMonitor Analogamente all’Agent/Null, anche il LossMonitor è un Agent di tipo passivo da usare come sink dei pacchetti per sorgenti non ACK-clocked, con la particolarità che esso tiene conto delle statistiche sul traffico ricevuto. Variabili di stato • nlost_: numero di pacchetti persi; • npkts_: numero di pacchetti ricevuti; • bytes_: numero di bytes ricevuti; • lastPktTime_: tempo in cui è stato ricevuto l’ultimo pacchetto; • expected_: il numero di sequenza atteso per il prossimo pacchetto. Metodi • clear: resetta il numero di sequenza atteso expected_ a –1. 5.5. Protocollo UDP Un oggetto della classe UDP si limita a ricevere dati da una applicazione di livello superiore ed a ritrasmetterli in rete con una ben determinata segmentazione. I pacchetti inviati contengono un numero di sequenza monotonicamente crescente ed un time stamp. Questo protocollo implementa un sistema di trasmissione di tipo datagram (non ACK-clocked), quindi come ricevente può essere utilizzato un Agent di tipo Null Parametri di configurazione • packetSize_: (default: 1000) dimensione del pacchetto in bytes. Es.: set sorgente [new Agent/UDP] set ricevente [new Agent/Null] 21 $ns connect $sorgente $ricevente 5.6. Protocollo RAP RAP sta per Rate Adaptation Protocol ed è un protocollo TCP-friendly adatto per lo streaming audio-video in real-time. Esso tenta di emulare il funzionamento di una generica sorgente TCP al variare dello stato di congestione della rete, e quindi cerca di trasmettere circa allo stesso rate. A differenza del TCP e dell’UDP, al RAP non possono essere attaccate delle applicazioni di livello superiore, poiché esso stesso implementa internamente una pseudo-applicazione che trasmette tanti pacchetti quanti ne è in grado di trasmettere il protocollo man mano che la simulazione evolve. variabili di stato • seqno_: (valore iniziale: 0) numero di sequenza dei pacchetti; • ipg_: (valore iniziale: 2) inter packet gap, ovvero intervallo di tempo che intercorre nella spedizione tra un pacchetto ed il successivo in secondi. Il suo inverso rappresenta il rate di trasmissione della sorgente in pacchetti al secondo; • srtt_: (valore iniziale: 2) stima del round trip time (RTT) in secondi; • variance_: (valore iniziale: 0) stima della varianza del RTT; • timeout_: (valore iniziale: 2) valore di scadenza del time out in secondi; Parametri di configurazione • packetSize_: (default: 512) dimensione del pacchetto in bytes; • beta_: (default: 0.5) fattore moltiplicativo per il rate di trasmissione quando viene rilevata una perdita od un time out; • alpha_: (default: 1) fattore correttivo nell’algoritmo dell’incremento del rate; più alpha_ è grande, maggiore è l’incremento del rate; • delta_: (default: 0.5) peso dato alla variazione istantanea del RTT nella valutazione del RTTmedio (srtt_), influisce anche nel calcolo della varianza; • mu_: (default: 1.2) peso dato alla variabile srtt_ nel calcolo di timeout_; • phi_: (default: 4) peso dato alla variabile variance_ nel calcolo di timeout_; • overhead:_ (default: 0) ritardo medio aggiunto alla trasmissione dei pacchetti; • useFineGrain_: (default: false) switch che consente di abilitare la modalità fine grain adaptation; • kfrtt_: (default: 0.9) coefficiente utilizzato nel calcolo del RTT di breve termine, valido solo se useFineGrain_ = true; 22 • kxrtt_: (default: 0.01) coefficiente utilizzato nel calcolo del RTT di lungo termine, valido solo se useFineGrain_ = true; metodi • start: avvia la pseudo applicazione; • stop: termina la pseudo applicazione. Es.: set sorgente [new Agent/RAP] set ricevente [new Agent/RAP] $ns connect $sorgente $ricevente $ns at <ti> “$sorgente start” $ns at <tf> “$sorgente stop” 5.7. Protocollo TFRC TFRC sta per TCP Friendly Rate Control ed è un protocollo TCP-friendly adatto per lo streaming audio-video in real-time. Esso tenta di emulare il funzionamento di una generica sorgente TCP calcolandone analiticamente il throughput in base alle condizioni di rete, e quindi (come per il RAP) cerca di trasmettere circa allo stesso rate. Anche per il TFRC non possono essere attaccate delle applicazioni di livello superiore, poiché esso stesso implementa internamente una pseudoapplicazione che trasmette tanti pacchetti quanti ne è in grado di trasmettere il protocollo man mano che la simulazione evolve. Essendo le funzioni svolte dal ricevitore più snelle di quelle del trasmettitore, è stato implementato l’Agent/TFRCSink (analogamente a quanto già visto per il TCP). variabili di stato • ndatapack_: (valore iniziale: 0) numero di pacchetti spediti; Parametri di configurazione • packetSize_: (default: 1000) dimensione del pacchetto in bytes; • df_: (default: 0.95) fattore di decay per una stima accurata dell’RTT; • tcp_tick_: (default: 0.1) granulosità del clock in secondi, che scandisce lo scorrere del tempo in un qualsiasi sistema operativo (vedi TCP). • overhead:_ (default: 0) ritardo medio aggiunto alla trasmissione dei pacchetti; • ssmult_: (default: 2) fattore moltiplicativo dell’incremento del rate di trasmissione durante la fase di slow-start; 23 • bval_: (default: 1) corrisponde alla costante B nella formula per la determinazione del rate del TCP; • ca_: (default: 1) attiva l’uso della radice dell’RTT nella fase di congestion avoidance; metodi • start: avvia la pseudo applicazione; • stop: termina la pseudo applicazione. TFRCSink E’ una sottoclasse della classe Agent, che si occupa esclusivamente di inviare pacchetti di ACK ai trasmettitori TFRC. Parametri di configurazione • packetSize_: (default: 40) dimensione in bytes del pacchetto di ACK. Es.: set sorgente [new Agent/TFRC] set ricevente [new Agent/TFRCSink] $ns connect $sorgente $ricevente $ns at <ti> “$sorgente start” $ns at <tf> “$sorgente stop” 24 6. Livello applicativo Il livello applicativo è rappresentato dalla classe astratta Application, ed i vari tipi di applicazione supportati in ns sono implementati in sottoclassi della stessa classe Application. Esistono due tipi base di applicazione: generatori di traffico ed applicazioni simulate. Per creare un oggetto Application qualsiasi (es. CBR) occorre eseguire: set mia_app [new Application/Traffic/CBR] 6.1.1. Metodi della classe Application • attach-agent <agent>: attacca <agent> alla nostra applicazione; • start: avvia la applicazione; • stop: termina la applicazione; Es.: $mia_app attach-agent $mio_agent o, equivalentemente: set mia_app [$mio_agent attach-app Traffic/CBR] dove il metodo attach-app appartiene alla classe Agent. 6.2. Generatori di traffico La classe TrafficGenerator (in Otcl viene mappata col nome Traffic) è una sottoclasse della classe Application, anche essa è una classe astratta, da cui discendono quattro applicazioni operative. Queste quattro applicazioni possono essere agganciate al protocollo di trasporto UDP. 6.2.1. Exponetial On/Off (Application/Traffic/Exponential) Implementa una sorgente on/off con distribuzione esponenziale. Nel periodo ‘on’ i pacchetti vengono spediti con un rate fissato, mentre nel periodo ‘off’ non viene spedito alcun pacchetto. Le durate dei periodi ‘on’ e ‘off’ vengono sono variabili aleatorie con distribuzione esponenziale. Parametri di configurazione • packetSize_: (default: 512) dimensione del pacchetto in bytes; • burst_time_: (default: ) durata media in secondi del periodo ‘on’ del generatore; • idle_time_: (default: ) durata media in secondi del periodo ‘off’ del generatore; • rate_: (default: ) rate di trasmissione adottato durante i periodi di ‘on’ in bit/s. 25 6.2.2. Pareto On/Off (Application/Traffic/Pareto) Implementa una sorgente on/off con distribuzione paretiana. Il funzionamento di questa sorgente di traffico è identico a quello esponenziale appena descritto, con la differenza consiste proprio nel tipo di distribuzione casuale che caratterizza i periodi di ‘on ‘ e ‘off’. Parametri di configurazione • packetSize_: (default: 512) dimensione del pacchetto in bytes; • burst_time_: (default: ) durata media in secondi del periodo ‘on’ del generatore; • idle_time_: (default: ) durata media in secondi del periodo ‘off’ del generatore; • rate_: (default: ) rate di trasmissione adottato durante i periodi di ‘on’ in bit/s; • shape_: (default: 1) parametro ‘shape’ che influisce sulla distribuzione paretiana. 6.2.3. CBR (Application/Traffic/CBR) Generatore di traffico di tipo constant-bit-rate. Parametri di configurazione • packetSize_: (default: 512) dimensione del pacchetto in bytes; • rate_: (default: ) rate di trasmissione in bit/s; • interval_: (default: ) intervallo di tempo in secondi tra la trasmissione di un pacchetto ed il successivo, quindi esso equivale all’inverso del rate espresso in pacchetti/s. • random_: (default: 0) se diverso da 0 introduce una variabilità casuale del tempo di spedizione tra un pacchetto ed il successivo, pari a ±50% di interval_; • maxpkts_: (default: 228 ) numero massimo di pacchetti da spedire. Per default vi è un numero altissimo che, praticamente corrisponde ad una trasmissione di dati infinita. I parametri rate_ ed interval_ sono mutuamente esclusivi, nel senso che l’utente deve settare solo uno dei due (e non entrambi), in quanto l’altro si configurerà automaticamente di conseguenza. 6.2.4. Traffic Trace (Application/Traffic/Trace) Genera traffico in base ai dati presenti in un file di ‘trace’ (generato dall’utente). Ogni record del file di trace consiste di due campi da 32-bit: il primo campo deve contenere il tempo (in microsecondi) che il generatore deve attendere prima di trasmettere il pacchetto successivo; il secondo campo deve contenere la lunghezza (in bytes) del pacchetto successivo. Non ci sono parametri di configurazione per questa classe di traffico. 26 6.3. Applicazioni simulate Esistono due tipi di applicazioni simulate: FTP e Telnet; esse sono state create esclusivamente per usufruire del servizio di trasporto TCP (poiché entrambe le applicazioni necessitano della trasmissione affidabile dei dati). 6.3.1. FTP (Application/FTP) Applicazione di tipo File Trasfer Protocol per la trasmissione di blocchi di dati. metodi • produce n: setta il contatore dei pacchetti da spedire ad ‘n’; • producemore n: incrementa il contatore dei pacchetti da spedire di ‘n’; • send n: simile a produce, ma spedisce ‘n’ bytes anziché pacchetti. Parametri di configurazione • maxpkts_: (default: 0) numero massimo di pacchetti generati dall’applicazione. Se vale 0 il numero di pacchetti generato sarà indefinito. 6.3.2. Telnet (Application/Telnet) Applicazione di tipo Telnet, i pacchetti possono essere generati in due modi, a seconda del valore del parametro interval_. Parametri di configurazione • interval_: (default: 0) tempo medio, espresso in secondi, che intercorre tra la generazione di un pacchetto ed il successivo ( inte- packet-gap). Se interval_ è diverso da 0 allora l’inter-packet-gap viene scelto da una distribuzione esponenziale con valore medio pari ad interval_. Se, invece, interval_ vale 0, allora l’inter-packet-gap viene scelto in accordo alla distribuzione “tcplib” contenuta nel file tcplib-telnet.cc Es.: set app [new Application/Traffic/CBR] $app set rate_ <val> set sorgente [new Agent/UDP] $app attach-agent $sorgente set ricevente [new Agent/Null] $ns connect $sorgente $ricevente $ns at <ti> “$app start” $ns at <tf> “$app stop” 27 7. Rilevazione dei dati 7.1. Oggetti QueueMonitor Gli oggetti della classe QueueMonitor sono usati per rilevare la quantità di dati che vengono inoltrati o scartati nella coda di un determinato link. Per creare un monitor si usa: $ns monitor-queue <nodo1> <nodo2> <optional: qtrace> <optional: sampleinterval> viene creato un monitor per monitorare la coda sul <nodo1> per tutti i pacchetti che si dirigono dal <nodo1> al <nodo2>. es.: set mio_monitor [$ns monitor-queue $n1 $n2] questa istruzione istanzia un monitor nella variabile ‘mio_monitor’, il parametro opzionale sampleinterval vale 0.1 per default. N.B. In realtà occorre specificare anche il parametro qtrace, per cui, anche se non se ne fa uso, occorre inserire anche una parola qualunque. es.: set mio_monitor [$ns monitor-queue $n1 $n2 mio_qtrace] dove al posto di mio_qtrace si può mettere una parola qualunque. Durante la simulazione, il monitor, mette a disposizione in lettura le seguenti variabili di stato: • size_: lunghezza della coda istantanea in bytes; • pkts_: lunghezza della coda istantanea in pacchetti; • parrivals_: totale dei pacchetti che giungono al relativo link; • barrivals_: totale dei pacchetti che giungono al relativo link espresso in bytes; • pdepartures_: totale dei pacchetti che vengono spediti nel link; • bdepartures_: totale dei pacchetti che vengono spediti nel link espresso in bytes; • pdrops_: totale dei pacchetti scartati nel link (solitamente dal meccanismo di gestione della coda); • bdrops_: totale dei pacchetti scartati nel link espresso in bytes; • bytesInt_: oggetto Integrator che calcola l’integrale della dimensione della coda in bytes. Il valore corrente di questo integrale è contenuto nella variabile sum_ dell’oggetto bytesInt_; • pktsInt_: oggetto Integrator che calcola l’integrale della dimensione della coda in pacchetti. Il valore corrente di questo integrale è contenuto nella variabile sum_ dell’oggetto pktsInt_. 28 Se per esempio vogliamo leggere il valore della coda in pacchetti dobbiamo fare: set coda [$mio_monitor set pkts_] tra le variabili su elencate, vale la seguente relazione: parrivals_ = pdepartures_ + pdrops_ Lo stesso vale ovviamente anche per le stesse variabili che espresse in bytes (col prefisso ‘b’ anziché ‘p’). 7.2. Oggetti FlowMon L’oggetto QueueMonitor da una rilevazione globale dei pacchetti inoltrati/scartati, se invece siamo interessati a distinguere tali dati per-flusso, dobbiamo utilizzare un oggetto FlowMon cioè monitor di flusso. Per distinguere un flusso dall’altro si può utilizzare il fid (flow identifier) definito nelle sorgenti (fid_ degli oggetti Agent) Possiamo allora andare a stanziare un monitor che distingue i flussi con la seguente sintassi: set mio_flowmon [$ns makeflowmon Fid] la variabile mio_flowmon rappresenterà un monitor di flusso, che dovremo andare ad attaccare ad una determinata coda nel seguente modo: set mio_link [$ns link <nodo1> <nodo2>] $ns attach-fmon $mio_link $mio_flowmon Prima di andare a leggere i dati occorre inserire un classificatore nell’oggetto flowmonitor, es: set flowclassifier [$mio_flowmon classifier] a questo punto siamo in grado di selezionare il fid che ci interessa monitorare con: set flow [$flowclassifier lookup auto 0 0 <fid>] e finalmente possiamo leggere tutti i dati che ci interessano con: set num_pack [$flow set pdepartures_] ovviamente oltre a pdepartures_ possiamo leggere tutte le altre variabili elencate nel paragrafo precedente. Occorre perciò definire un fid diverso per ogni sorgente, nel caso in cui siamo interessati alle singole statistiche. Se, invece, siamo interessati a statistiche aggregate, cioè dei pacchetti inoltrati/scartati da un insieme di flussi, possiamo assegnare il medesimo fid ad un certo numero di sorgenti. In questa maniera, in fase di lettura, otterremo la somma, per esempio, dei pacchetti scartati per un determinato gruppo di flussi. 29 N.B. la lettura delle statistiche non può essere fatta prima che le sorgenti interessate abbiano spedito almeno un pacchetto, altrimenti il simulatore si fermerà e darà un messaggio di errore. Quindi, in fase di schedulazione, bisogna accertarsi che non vengano effettuate letture, in istanti di tempo precedenti all’invio del primo pacchetto da parte delle sorgenti da monitorare. 7.3. Lettura delle variabili di stato In qualunque momento della simulazione è possibile leggere le variabili di stato delle entità coinvolte (Agent, Application, Queue, ecc.). Una volta creato un Agent, ad esempio: set mio_agent [new Agent/TCP] mentre la simulazione gira è possibile schedulare istruzioni del tipo: set val [$mio_agent set agent_var] che non fa altro che leggere il valore della variabile agent_var presente all’interno dell’agent mio_agent e conservarlo in val. es.: set val [$tcp_agent set rtt_] 30 8. Nozioni sul linguaggio TCL E’ un linguaggio script molto potente, simile al C o al delphi. Per effettuare una qualsiasi inizializzazione di variabile occorre fare: set <var> <val> per fare una qualunque operazione matematica occorre usare expr dentro le parentesi quadre [],es.: set x [expr $y+sqrt($z)/cos($y)] non si possono usare le parentesi tonde per annidare le operazioni, ma bisogna utilizzare altri blocchi [expr] annidati. Per valutare una variabile bisogna anteporre il simbolo $ (vedi espressione precedente). Ci sono alcuni casi in cui non bisogna usare il $ prima di una variabile: • subito dopo l’istruzione set; • nella lista dei parametri di ingresso di una procedura Una variabile può facilmente diventare un vettore semplicemente indicandone l’indice, es.: set y(2) <val> per inserire dei commenti, si utilizza il #, tutto ciò che si trova dopo di esso, nella stessa linea, non viene considerato. Il # ha la validità di una linea. Per un buon debugging del vostro script, può essere utile stampare nello schermo i valori delle variabili che vi interessano, a tal proposito si usa l’istruzione puts, es: puts $mia_var per stampare una semplice stringa di testo si fa: puts ciao oppure: puts “ciao” scrive “ciao” nello schermo (prompt del DOS). Per scrivere più cose nella stessa riga si devono usare obbligatoriamente le virgolette, es.: puts “rate: $mio_rate tempo: $mio_tempo” tutti i termini devono essere separati da uno spazio. Per distinguere le variabili dalle stringhe di testo si deve anteporre il simbolo ‘$’. 8.1.1. Gestione dei Files Per aprire un file di testo bisogna fare: set mio_file [open nome_file w] 31 in questo caso il file è stato aperto in scrittura (valgono le stesse regole del C), ma è anche possibile aprirlo in modalità append con a al posto di w. Per scrivere dati in un file (di testo), occorre usare l’istruzione puts. Ad esempio se vogliamo scrivere nel file un valore numerico contenuto nella variabile mia_var, si deve fare: puts $mio_file $mia_var in questo modo, la variabile numerica mia_var sarà convertita in testo e scritta nel file, in modo da poter essere utilizzabile sia da Matlab che da Excel. Per il resto vale la stessa sintassi di puts descritta in precedenza. Per chiudere un file occorre fare: close $mio_file 8.1.2. FOR Sintassi: for {set i 0} {condizione} {incr i} { <blocco di istruzioni> } un esempio di condizione è: “$i < <top>”. al posto di “incr i”, potremmo alternativamente mettere “set i [expr $i+1]” oppure $i+$tick]” dove tick è un numero reale. 8.1.3. IF Sintassi: if {condizione} { <blocco di istruzioni> } else { <blocco di istruzioni> } else if {condizione} { <blocco di istruzioni> } 8.1.4. PROC Sintassi: proc mia_proc {parametri in} { 32 “set i [expr <blocco di istruzioni> } i parametri di ingresso sono opzionali e devono essere separati da uno spazio. Le procedure sono completamente isolate dal resto del codice, per cui qualunque variabile esterna sarà invisibile. Per rendere visibili le variabili esterne in tutti e due i sensi occorre dichiararle come global (vedi esempio). Per chiamare una procedura bisogna scrivere: mia_proc <parametri> i parametri devono essere preceduti da un $,e separati tra loro con uno spazio. 8.1.5. Numeri casuali La generazione di numeri casuali che si sta per descrivere non deriva dal linguaggio TCL nativo, ma costituisce un arricchimento del simulatore. Per generare numeri casuali occorre innanzitutto creare una istanza di variabile casuale nel seguente modo: set mia_random_var [new RandomVariable/Uniform] dove RandomVariable è la classe astratta da cui discendono i veri oggetti random che possiamo usare. La più usata è la sottoclasse Uniform, che è in grado di generare numeri casuali con distribuzione uniforme di probabilità entro un intervallo arbitrario. Tale intervallo può essere definito mediante le due variabili membro min_ e max_ come segue: $mia_random_var set min_ <value> $mia_random_var set max_ <value> per default essi valgono rispettivamente 0 e 1. A questo punto è possibile ricavare i valori casuali nel seguente modo: set rand_val [$mia_random_var value] Per completezza, vengono di seguito esposte le altre sottoclassi: • Pareto. Variabili membro: avg_ (default:1), shape_ (default: 1.2); • Exponential. Variabili membro: avg_ (default: 1); • HyperExponential. Variabili membro: avg_ (default: 1), cov_ (default: 4); Per cambiare il seme di generazione dei numeri casuali si deve usare l’istruzione: ns-random $nuovo_seme Il seme di default è 1, per cui se lo si vuole cambiare, si deve porre un altro valore intero. 33 8.1.6. Lancio della simulazione Lo script TCL serve a costruire la topologia della rete da analizzare e a schedulare i vari eventi che ci interessano, tutto ciò viene elaborato ed immagazzinato in memoria durante il parsing dello script. Per lanciare effettivamente la simulazione, si deve mettere, alla fine dello script TCL, l’istruzione: $ns run che non fa altro che seguire il calendario di eventi che sono stati precedentemente schedulati. 34 9. Precisione dei risultati Per garantire che i risultati siano attendibili, è necessario cominciare i rilevamenti dopo che si è estinto il transitorio di partenza, ad esmpio, quando la coda di un router diventa stabile nel tempo. Ci si prefissa, ad esempio, di ottenere una precisione dell’1% con una probabilità del 90%; supponiamo che x0 sia il risultato numerico della simulazione, la precisione suddetta garantisce che il risultato reale si trova nel 90% dei casi entro un intervallo detto intervallo di confidenza ε pari all’1% di x0 : x0 - ε < x0 < x0 + ε Il simulatore non sa nulla di queste cose, quindi i risultati devono essere gestiti per intero dall’utente, per cui occorre elaborare uno script simulativo che oltre alla simulazione stessa, implementi la precisione dei risultati; per l’esattezza occorre implementare l’algoritmo esposto in fig. 1. In tale grafo di flusso si calcolano a partire dalla seconda simulazione, sia il valore medio che lo scarto quadratico medio del risultato, in base a questi sarà quindi calcolato l’ε t temporaneo. Il gruppo di simulazioni finirà solo quando ε t sarà minore di 10-2 che rappresenta l’ε desiderato o l’intervallo di confidenza desiderato. Per calcolare l’intervallo di confidenza temporaneo ε t si fa uso dei valori dei percentile tpN che sono disponibili in forma tabulata. Un metodo pratico consiste nell’effettuare tante simulazioni tutte in una, per cui, si fa partire la simulazione, e sia aspetta che il transitorio si estingue, ad esempio quando si stabilizza il valore di una certa coda all’interno della topologia. Finito il transitorio, si fa partire una prima fase di regime, al termine della quale otterremo il nostro primo valore (un throughput, un valore di coda media, un ritardo, ecc.) che conserveremo in un vettore. A questo punto, anziché iniziare una nuova simulazione, non facciamo altro che continuare quella precedente, lanciando una seconda fase di regime al fine di ottenere il secondo risultato, che andremo a confrontare col primo, se la precisione non ci soddisferà, si lancerà una terza fase di regime, e così via. Questo modo di procedere rimane valido, perché i valori delle variabili della simulazione sono diversi per ogni regime, e quindi è come se essi venissero originati da condizioni iniziali differenti. 35 ε = 10-2 p = 90% N= 1 calcola il 1° risulato g1 N = N +1 calcola il l'N-esimo risulato gN calcola il valore medio g' = (g1 + g2 + ... + gN) / N calcola lo scarto quadratico medio Σ i (gi -g')2 s = N calcola intervallo di confidenza temporaneo s ε t = t pN N-1 no εt ≤ε ? si risultato g = g' ± ε fine Fig.1 36 10. Passaggio dei parametri dal prompt DOS Quando lanciamo l’ns dalla riga di comando del DOS (es. ns mio_script.tcl <INVIO>), abbiamo la possibilità di passare al simulatore dei parametri supplementari mediante argc e argv (vedi C++). E’ quindi possibile scrivere una riga del tipo: ns mio_script.tcl –banda 100000 –coda 40 <INVIO> per poter passare, ad esempio, due parametri che mi vanno a cambiare il valore di banda di un certo link e la capacità della coda di un certo buffer all’interno della nostra topologia. Questo metodo è utile quando devo lanciare diverse simulazioni in cui cambia solo il valore di banda o della capacità di una coda, senza dover necessariamente modificare il file mio_script.tcl. Per fare ciò, è necessario programmare lo script di simulazione in modo da definire quanti e quali parametri si vuole modificare dall’esterno. Quando lanciamo l’ns dal prompt del DOS, il sistema ,automaticamente, definisce le due variabili argc e argv nel seguente modo: • argc: numero di stringhe presenti nella riga di comando, susseguenti il file tcl; • argv: vettore di argc elementi contenente le stringhe presenti nella riga di comando susseguenti il file tcl. Es., se digito: ns mio_script.tcl –banda 100000 –coda 40 –delay 25 <INVIO> all’interno dello script vi troverete: argc = 6, argv = [‘-banda’, ‘100000’, ‘-coda’, ‘40’, ‘-delay’, ‘25’] Questo metodo di scrivere prima il tag di riconoscimento del parametro da modificare, e poi il valore da sostituire a quello di default, è quello più largamente diffuso; però non è detto che uno non possa usare altri tipi di convenzione più consoni alle proprie esigenze. Ad ogni modo, occorre quindi scrivere delle righe di codice TCL che effettuino il parsing di argv in modo da riconoscere i propri tag, ed assegnare ai parametri ad essi corrispondenti i valori numerici messi di seguito. Per es. a ‘banda’ corrisponderà il parametro B1 a cui assegneremo il valore 100000. Un esempio di parsing di argv è il seguente spezzone di codice TCL: for {set i 0} {$i < $argc} {incr i} { set arg [lindex $argv $i] if {[string range $arg 0 0] != "-"} continue set name [string range $arg 1 end] if {$name == "banda"} { set B1 [lindex $argv [incr i]] } elseif {$name == "coda"} { 37 set K [lindex $argv [incr i]] ecc. ………… } else { puts "ERRORE: Parametro $name sconosciuto!" exit 1 } } dove lindex ritorna la stringa specificata da $i, mentre string range restituisce una sottostringa specificata dagli indici di carattere tra 0 ed end. In questo modo si è usata la convenzione che tutti i tag devono essere preceduti dal trattino’-‘. 38 11. Appendice 11.1. NAM Il NAM (Network AniMator) è un tool che serve a visualizzare l’evoluzione temporale della simulazione. L’ns può memorizzare l’evoluzione temporale della simulazione in un file che chiameremo ‘out.nam’, quindi dobbiamo inserire nel nostro script le seguenti istruzioni: set nf [open out.nam w] $ns namtrace-all $nf namtrace-all è un metodo della classe Simulator, e non fa altro che dire al simulatore di conservare nel file indicato da $nf (out.nam) tutti i dati di cui ha bisogno il NAM, dall’inizio, alla fine della simulazione. E’ chiaro che la dimensione del file out.nam dipende dalla durata della simulazione, inoltre essa dipende anche dalle capacità dei link e dal numero di sorgenti. Per lanciarlo dal prompt del DOS occorre digitare: nam out.nam dove si presuppone che i file nam.exe e out.nam siano nella stessa directory 11.2. Esempio di script TCL #Crea un oggetto simulatore set ns [new Simulator] set durata 200 set tick [expr $durata / 100] set regime [expr 0.4 * $durata] set packet 100 set BW 200KB set n 0 set qsum 0 set asum 0 #Definisce colori diversi per i due flussi $ns color 1 Blue $ns color 2 Red #apre il trace file per il NAM set nf [open out.nam w] $ns namtrace-all $nf #procedura chiama alla fine della simulazione proc finish {} { global ns global nf global rapbw tcpbw durata regime packet BW global qsum asum n $ns flush-trace 39 puts "banda da condividere: $BW" puts "rap BW = [expr [expr $rapbw * $packet] / [expr $durata - $regime]] bytes/s" puts "tcp BW = [expr [expr $tcpbw * $packet] / [expr $durata - $regime]] bytes/s" set qmedia [expr $qsum / $n] puts "valore medio della coda : $qmedia" set amedia [expr $asum / $n] puts "valore medio della coda media : $amedia" #Chiude il trace file close $nf exit 0 } #Crea 4 nodi set tcps [$ns node] set tcpd [$ns node] set red1 [$ns node] set red2 [$ns node] $red1 color red $red2 color red $red1 shape hexagon $red2 shape hexagon $red1 label red-router $red2 label red-router set raps [$ns node] set rapd [$ns node] #Crea i links tra i nodi $ns duplex-link $tcps $red1 10Mb 2ms DropTail $ns duplex-link $red2 $tcpd 10Mb 2ms DropTail $ns duplex-link $red1 $red2 $BW 20ms RED $ns duplex-link $raps $red1 10Mb 2ms DropTail $ns duplex-link $red2 $rapd 10Mb 2ms DropTail set ftp [new Application/FTP] #Crea un agente TCP e lo attacca al nodo set tcp [new Agent/TCP] $ns attach-agent $tcps $tcp $tcp set packetSize_ $packet $tcp set fid_ 1 $ftp attach-agent $tcp #Crea un agente Null (traffic sink) e lo attacca al nodo set tcpsink [new Agent/TCPSink] $ns attach-agent $tcpd $tcpsink ############################################################# #Crea un agente RAP e lo attacca al nodo set rap [new Agent/RAP] $ns attach-agent $raps $rap $rap set packetSize_ $packet $rap set beta_ 0.5 $rap set fid_ 2 set rapsink [new Agent/RAP] $ns attach-agent $rapd $rapsink #Connette le sorgenti ai riceventi 40 $ns connect $tcp $tcpsink $ns connect $rap $rapsink #crea un flow-monitor proc creaflowmon {} { global red1 red2 global counter ns nodes rapbw tcpbw global flowClassifier set rapbw 0 set tcpbw 0 set counter [$ns makeflowmon Fid] set bottleneck [$ns link $red1 $red2] $ns attach-fmon $bottleneck $counter set flowClassifier [$counter classifier] } #segna bw proc segnapacchetti {} { global counter rapbw tcpbw global flowClassifier set flowrap [$flowClassifier lookup auto 0 0 2] set rapshare [$flowrap set pdepartures_] set rapbw [expr $rapshare - $rapbw] set flowtcp [$flowClassifier lookup auto 0 0 1] set tcpshare [$flowtcp set pdepartures_] set tcpbw [expr $tcpshare - $tcpbw] } #tiene traccia della coda del bottleneck set redqc [[$ns link $red1 $red2] queue] proc segnacoda {} { global ns red1 red2 redqc global qsum asum n #set fc [open fc.out w] set redqc [[$ns link $red1 $red2] queue] #$redqc trace curq_ #$redqc attach $fc set cc [$redqc set curq_] set qsum [expr $qsum + $cc] set cc [$redqc set ave_] set asum [expr $asum + $cc] set n [expr $n + 1] } creaflowmon #fase di scheduling $ns at [expr $durata * 0.01] "$ftp start" $ns at [expr $durata * 0.01] "$rap start" 41 $ns at [expr $durata * 0.97] "$rap stop" $ns at [expr $durata * 0.99] "$ftp stop" $ns at $regime "segnapacchetti" $ns at $durata "segnapacchetti" for {set i 1} {$i < $durata} {set i [expr $i + $tick]} { $ns at $i "segnacoda" } $ns at $durata "finish" #lancia la simulazione $ns run 42