Manuale di Network Simulator v.2
Capitolo 13 - Moduli di errore
a cura di Sandro Petrizzelli
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.
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.
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.
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
};
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_;
};
Continua con: Indice Capitolo 12 Capitolo 14
Aggiornamento: 24 agosto 2001