Manuale di Network Simulator v.2

Capitolo 13 - Moduli di errore

a cura di Sandro Petrizzelli


 

Introduzione

Questo capitolo del manuale descrive l’implementazione e la configurazione dei cosiddetti moduli di errore, che introducono le perdite di pacchetti nelle simulazioni. Le procedure e le funzioni descritte in questo capitolo possono essere trovate nei file errmodel.cc e errmodel.h.

 

13.1 Implementazione

Un modulo di errore simula errori di livello fisico (link-level errors) oppure perdite, tramite due distinti meccanismi: la presenza di errori su un pacchetto è indicata settando l’apposito “error flag” nel common header del pacchetto stesso, mentre invece la perdita completa del pacchetto viene simulata consegnando il pacchetto, invece che al suo destinatario, ad un apposito “drop target”.

In una simulazione, gli errori possono essere generati tramite un modello molto semplice, specificando un tasso di errore sui pacchetti, oppure tramite modelli statistici ed empirici più complessi. Al fine di supportare una ampia varietà di tali modelli, l’unità di errore può essere specificata in termini di pacchetti o di bit oppure essere basata sul tempo (timer-based).

La classe ErrorModel è derivata dalla classe base Connector. Di conseguenza, essa eredita alcuna metodi per “agganciare” oggetti (come i pacchetti), come ad esempio i metodi target e drop-target. Se esiste un “drop target”, esso riceve tutti i pacchetti che sono stati “corrotti” dal modello di errore. Altrimenti, il modello di errore semplicemente setta il flag “error” del common header del pacchetto, in modo da demandare agli agenti di trasporto la responsabilità di gestire gli errori.

La classe ErrorModel definisce anche un metodo addizionale Tcl denominato unit: esso serve a specificare l’unità di errore desiderata dall’utente. E’ previsto anche il metodo denominato ranvar, per specificare una variabile casuale per la generazione degli errori. A meno di specifiche contrarie, l’unità di errore è sempre espressa in pacchetti, mentre invece la variabile aleatoria è uniformemente distribuita nell’intervallo [0,1].

A titolo di chiarimento, riportiamo un esempio su come creare un modulo di errore con tasso di errore sui pacchetti dell’ 1%:  

# create a loss_module and set its packet error rate to 1 percent

set loss_module [new ErrorModel]

$loss_module set rate_ 0.01

 

# optional: set the unit and random variable

$loss_module unit pkt            ;# error unit: packets (the default)

$loss_module ranvar [new RandomVariable/Uniform]

 

# set target for dropped packets

$loss_module drop-target [new Agent/Null]  

Le prime due righe OTcl effettuano la creazione del modulo di errore (denominato loss_module) e l’impostazione del tasso di errore al valore 0.01 (=1%). Le due righe successive impostano l’unità di errore in pacchetti (come del resto è previsto di default) e la variabile aleatoria ranvar per la generazione degli errori (distribuita uniformemente, come per default). L’ultima riga imposta invece il “drop target” cui il modulo di errore deve consegnare i pacchetti persi.

In C++, la classe ErrorModel contiene sia i meccanismi sia la “politica” per la perdita dei pacchetti. In particolare, il meccanismo per la perdita dei pacchetti è gestito dal metodo recv(), mentre invece la politica per la gestione dei pacchetti corrotti è esseguita dal metodo corrupt():

 

void ErrorModel::recv(Packet* p, Handler* h)

{

   // 1.  Determine the error by calling corrupt(p)

   // 2.  Set the packet's error flag if it is corrupted

   // 3.  If there is no error,  no drop_ target or markecn is true,

   //   let pkt continue, otherwise hand the corrupted packet to drop_

 

   hdr_cmn* ch = hdr_cmn::access(p);

   int error = corrupt(p);

 

   // XXX When we do ECN, the packet is marked but NOT dropped.

   // So we don't resume handler here.

 

   if (!markecn_ && (h && ((error && drop_) || !target_))) {

        // if we drop or there is no target_, then resume handler

        double delay = Random::uniform(8.0 * ch->size() / bandwidth_);

        Scheduler::instance().schedule(h, &intr_, delay);

   }

 

   if (error) {

        ch->error() |= error;

        if (markecn_) {

             hdr_flags* hf = hdr_flags::access(p);

             hf->ce() = 1;

        } else if (drop_) {

             drop_->recv(p);

             return;

        }

   }

 

   if (target_) {

        target_->recv(p, h);

   }

}

 

 

int ErrorModel::corrupt(Packet* p)

{

   if (enable_ == 0)

        return 0;

   switch (unit_) {

   case EU_TIME:

        return (CorruptTime(p) != 0);

   case EU_BYTE:

        return (CorruptByte(p) != 0);

   default:

        return (CorruptPkt(p) != 0);

   }

   return 0;

}

 

Segue la definizione della classe ErrorModel in C++: 

enum ErrorUnit { EU_PKT=0, EU_BIT, EU_TIME };

class ErrorModel : public Connector {

public:

    ErrorModel();

    virtual void recv(Packet*, Handler*);

    virtual void reset();

    virtual int corrupt(Packet*);

    inline double rate() { return rate_; }

    inline ErrorUnit unit() { return unit_; }

 

protected:

    virtual int command(int argc, const char*const* argv);

    int CorruptPkt(Packet*);

    int CorruptTime(Packet*);

    int CorruptByte(Packet*);

    double PktLength(Packet*);

 

    int enable_;                // true if this error module is turned on

    int markecn_;               // mark ecn instead of dropping on corruption?

    int firstTime_;             // to not corrupt first packet in byte model

    ErrorUnit unit_;              // error unit in pkts, bytes, or time

    double rate_;                  // uniform error rate in pkt or byte

    double bandwidth_;       // bandwidth of the link

    RandomVariable *ranvar_;      // the underlying random variate generator

    Event intr_;                  // set callback to queue

};

Questa classe, così com’è, implementa solo una semplice politica basata su un singolo tasso di errore, espresso in termini di pacchetti o di bit. Politiche più sofisticate possono invece essere implementate in C++ derivando apposite classi da ErrorModel e ridefinendo il metodo corrupt().

Il metodo command(), della classe ErrorModel, è così definito:

 

int ErrorModel::command(int argc, const char*const* argv)

{

   Tcl& tcl = Tcl::instance();

   //ErrorModel *em;

   if (argc == 3) {

        if (strcmp(argv[1], "unit") == 0) {

             unit_ = STR2EU(argv[2]);

             return (TCL_OK);

        }

        if (strcmp(argv[1], "ranvar") == 0) {

             ranvar_ = (RandomVariable*) TclObject::lookup(argv[2]);

             return (TCL_OK);

        }

   } else if (argc == 2) {

        if (strcmp(argv[1], "unit") == 0) {

             tcl.resultf("%s", eu_names[unit_]);

             return (TCL_OK);

        }

        if (strcmp(argv[1], "ranvar") == 0) {

             tcl.resultf("%s", ranvar_->name());

             return (TCL_OK);

        }

   }

   return Connector::command(argc, argv);

}

 

Come si può notare, essa prevede due soli metodi: unit e ranvar, cui possono essere passati sia 2 sia 3 argomenti.

 

13.2 Configurazione

Il paragrafo precedente era dedicato al modulo di errore in generale. Vogliamo invece vedere adesso come usare un modulo di errore in una simulazione.

Per usare un modulo di errore, bisogna inserirlo in un oggetto di classe SimpleLink. Dato che un oggetto di tale classe è un oggetto composto ( [1] ), un modulo di errore può essere piazzato in diverse posizioni.

La prima possibilità consiste nel porre il modulo di errore prima del modulo che implementa la coda di uscita del SimpleLink. Questo si ottiene con i seguenti due metodi OTcl:  

·      SimpleLink::errormodule args: quando un modulo di errore è fornito come parametro (args), questo metodo lo inserisce nel simple link, subito dopo il modulo “queue”, e imposta il drop-target del modulo di errore come “trace object” del link. Da notare che questo richiede la seguente istruzione di configurazione: ns namtrace-all seguito dalla configurazione del link, seguito a sua volta dall’inserimento del modulo di errore. Quando invece nessun argomento viene fornito, il metodo restituisce semplicemente il modulo di errore usato nel link, se presente. Questo metodo è definito nel file ns/tcl/lib/ns-link.tcl;

·      Simulator::lossmodel <em> <src> <dst>: questa istruzione chiama il metodo  SimpleLink::errormodule descritto poco fa, al fine di inserire un dato modulo di errore all’interno di un SimpleLink che unisce <src> con <dst>. Questo metodo è definito nel file ns/tcl/lib/ns-lib.tcl.  

La seconda possibilità consiste nell’inserire il modulo di errore, sempre in un SimpleLink, dopo la coda e prima del modulo “delay”. Anche in questo caso sono previsti due metodi:  

·      SimpleLink::insert-linkloss args: il comportamento di questo metodo è identico a quello del metodo SimpleLink::errormodule visto prima, con la differenza che esso inserisce il modulo di errore subito dopo l’oggetto “queue”. Questo metodo è definito nel file ns/tcl/lib/ns-link.tcl;

·      Simulator::link-lossmodel <em> <src> <dst>: questa istruzione chiama il metodo  SimpleLink::linkloss descritto poco fa ed è definito nel file ns/tcl/lib/ns-lib.tcl.  

Il file .nam generato da un modulo di errore inserito tramite gli ultimi due metodi non richiede alcun speciale trattamento e può essere visualizzato usando una qualsiasi versione del programma NAM.

L’ultima possibilità è quella di inserire il modulo di errore dopo il modulo “delay”. Questo può essere fatto tramite il metodo Link::install-error. Tuttavia, segnaliamo che, almeno allo stato attuale, questa API non produce alcuna traccia (trace), in quanto è stata predisposta solo in previsione di estensioni future.

 

13.3 Modello di errore “multi-state”

Il modulo di errore cosiddetto “multi-state” implementa delle transizioni di stato basate sul tempo (timer-based error state transition): in pratica, le transizioni da uno “stato di errore” al successivo avvengono al termine di un intervallo di tempo opportunamente calcolato. La scelta dello stato successivo viene effettuata usando una apposita matrice di transizione di stato.

Per creare un modello di errore di questo tipo, bisogna impostare i seguenti parametri (definiti nel file ns/tcl/lib/ns-errmodel.tcl):  

·      states: vettore di stati (ogni stato corrisponde ad un diverso modello di errore);

·      periods: vettore delle “durate medie” degli stati;

·      trans: matrice delle transizioni di stato;

·      transunit: da scegliere tra pacchetti, byte o secondi;

·      sttype: tipo di transizioni di stato da usare, da scegliere tra secondi o pacchetti;

·      nstates: numero di stati;

·      start: stato di partenza.  

Segue un semplice script di esempio per creare un modello di errore multi-state:    

set tmp [new ErrorModel/Uniform 0 pkt]

set tmp1 [new ErrorModel/Uniform .9 pkt]

set tmp2 [new ErrorModel/Uniform .5 pkt]

 

# Array of states (error models)

set m_states [list $tmp $tmp1 $tmp2]

 

# Durations for each of the states, tmp, tmp1 and tmp2, respectively

set m_periods [list 0 .0075 .00375]

 

# Transition state model matrix

set m_transmx { {0.95 0.05 0}

{0 0 1}

{1 0 0} }

set m_trunit pkt

 

# Use time-based transition

set m_sttype time

set m_nstates 3

set m_nstart [lindex $m_states 0]

set em [new ErrorModel/MultiState $m_states $m_periods $m_transmx

$m_trunit $m_sttype $m_nstates $m_nstart]  

Le prime tre righe creano tre distinti modelli di errore (tmp, tmp1 e tmp2), ciascuno caratterizzato da un tasso di errore costante, espresso in termini di pacchetti persi (pkt): il primo modello di errore, che servirà nella parte iniziale della simulazione, prevede un tasso nullo di perdita; il secondo modello prevede un tasso del 90% di perdita di pacchetti, mentre il terzo prevede un tasso di perdita del 50%. Tali tre modelli vanno a riempire il vettore degli stati, denominato m_states.

Successivamente, viene creato il vettore m_periods con la durata media di ciascuno stato.

Viene poi definita la matrice delle transizioni di stato, m_transmx, quadrata di dimensione 3 (dove 3 è ovviamente il numero degli stati previsti). Insieme ad essa, viene anche definita l’unità su cui basare le transizioni di stato: si scelgono i pacchetti (set m_trunit pkt).

Le successive tre istruzioni definiscono i parametri delle transizioni timer-based: si sceglie il tipo di transizione di stato (set m_sttype time), il numero di stati (set m_nstates 3) e lo stato di partenza (set m_nstart [lindex $m_states 0]).

Infine, l’ultima istruzione crea e configura il modello di errore, denominato em, secondo le impostazioni create nelle righe precedenti.

Riportiamo infine le definizioni della classe MultiStateErrorModel e di una sua versione particolare, relativa a due soli stati (TwoStateErrorModel):  

class MultiStateErrorModel : public ErrorModel {

public:

   MultiStateErrorModel();

   virtual int corrupt(Packet*);

protected:

   int command(int argc, const char*const* argv);

   int sttype_;         // type of state trans: 1: 'pkt' prob, 0: 'time'

   int texpired_;       // timed-state expired?

   double curperiod_;   // the duration of the current state

   ErrorModel* em_;     // current error model to use

};

 

class TwoStateErrorModel : public ErrorModel {

public:

   TwoStateErrorModel();

   virtual int corrupt(Packet*);

protected:

   int command(int argc, const char*const* argv);

   int state_;        // state: 0=error-free, 1=error

   double remainLen_;    // remaining length of the current state

   RandomVariable *ranvar_[2]; // ranvar staying length for each state

};

 

13.4 Comandi principali

Vogliamo qui analizzare rapidamente i principali comandi usati nella simulazioni per la configurazione dei moduli di errore.

Cominciamo con un semplice esempio di come creare e configurare un modulo di errore:  

set em [new ErrorModel]

$em unit pkt

$em set rate_ 0.02

$em ranvar [new RandomVariable/Uniform]

$em drop-target [new Agent/Null]  

I comandi per inserire un modulo di errore in un link semplice sono i seguenti:  

·      $simplelink errormodule <args>

Questo comando inserisce il modulo di errore prima dell’oggetto “queue” del link. In questo caso, il drop-target del modulo di errore punta all’elemento “drophead_” del link.

 

·      $ns_ lossmodel <em> <src> <dst>

Questo comando inserisce il modulo di errore prima dell’oggetto “queue” in un link tra <src> e <dst>.

 

·      $simplelink insert-linkloss <args>

Questo comando inserisce il modulo di errore dopo l’oggetto “queue”, ma prima dell’oggetto “delay”, in un link. Questo perché il programma NAM può visualizzare un pacchetto cancellato solo se esso si trova nel link oppure nella coda. Il drop-target del modulo di errore punta anche in questo caso all’elemento “drophead_” del link.

 

·      $ns_ link-lossmodel <em> <src> <dst>

Questo comando ha lo stesso effetto del comando precedente, in quanto inserisce il modulo di errore, dopo l’oggetto “queue”, in un link tra <src> e <dst>.

 

In aggiunta ai metodi appena descritti per la classe ErrorModel, ci sono diversi altri tipi di modulo di errore derivati da questa classe base, come ad esempio SRMErrorModel, ErrorModel/Trace, ErrorModel/Periodic e altri. Le loro definizioni si trovano nei file ns/tcl/lib/ns-errmodel.tcl and ns/tcl/lib/ns-default.tcl. Ad esempio:

 

/*

 * error model that reads a loss trace (instead of a math/computed model)

*/

 

class TraceErrorModel : public ErrorModel {

public:

   TraceErrorModel();

   virtual int match(Packet* p);

   virtual int corrupt(Packet* p);

protected:

   double loss_;

   double good_;

};

 

 

/*

 * periodic packet drops (drop every nth packet we see)

 * this can be conveniently combined with a flow-based classifier

 * to achieve drops in particular flows

 */

 

class PeriodicErrorModel : public ErrorModel {

public:

   PeriodicErrorModel();

   virtual int corrupt(Packet*);

protected:

   int cnt_;

   double period_;

   double offset_;

   double burstlen_;

   double last_time_;

   double first_time_;

};

 

 

/*

 * List error model: specify which packets to drop in tcl

 */

 

class ListErrorModel : public ErrorModel {

public:

   ListErrorModel() : cnt_(0), droplist_(NULL),

        dropcnt_(0), cur_(0) { }

   ~ListErrorModel() { if (droplist_) delete droplist_; }

   virtual int corrupt(Packet*);

   int command(int argc, const char*const* argv);

protected:

   int parse_droplist(int argc, const char *const*);

   static int nextval(const char*&p);

   static int intcomp(const void*, const void*);        // for qsort

   int cnt_; /* cnt of pkts/bytes we've seen */

   int* droplist_;     /* array of pkt/byte #s to affect */

   int dropcnt_;     /* # entries in droplist_ total */

   int cur_; /* current index into droplist_ */

};

 

 

/* For Selective packet drop */

 

class SelectErrorModel : public ErrorModel {

public:

   SelectErrorModel();

   virtual int corrupt(Packet*);

protected:

   int command(int argc, const char*const* argv);

   packet_t pkt_type_;

   int drop_cycle_;

   int drop_offset_;

};  



[1] Si veda, in proposito, il capitolo 6

   


Continua con:        Indice       Capitolo 12      Capitolo 14


Aggiornamento: 24 agosto 2001

home