/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1997 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the Daedalus Research
* Group at the University of California Berkeley.
* 4. Neither the name of the University nor of the Laboratory may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Contributed by the Daedalus Research Group, UC Berkeley
* (http://daedalus.cs.berkeley.edu)
*
* Multi-state error model patches contributed by Jianping Pan
* (jpan@bbcr.uwaterloo.ca).
*
* @(#) $Header: /nfs/jade/vint/CVSROOT/ns-2/queue/errmodel.cc,v 1.81 2005/09/21 21:45:04 haldar Exp $ (UCB)
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header: /nfs/jade/vint/CVSROOT/ns-2/queue/errmodel.cc,v 1.81 2005/09/21 21:45:04 haldar Exp $ (UCB)";
#endif
#include "config.h"
#include <stdio.h>
#include <ctype.h>
#include "packet.h"
#include "flags.h"
#include "mcast_ctrl.h"
#include "errmodel.h"
#include "srm-headers.h" // to get the hdr_srm structure
#include "classifier.h"
static class ErrorModelClass : public TclClass {
public:
ErrorModelClass() : TclClass("ErrorModel") {}
TclObject* create(int, const char*const*) {
return (new ErrorModel);
}
} class_errormodel;
static class TwoStateErrorModelClass : public TclClass {
public:
TwoStateErrorModelClass() : TclClass("ErrorModel/TwoState") {}
TclObject* create(int, const char*const*) {
return (new TwoStateErrorModel);
}
} class_errormodel_twostate;
static class ComplexTwoStateMarkovModelClass : public TclClass {
public:
ComplexTwoStateMarkovModelClass() : TclClass("ErrorModel/ComplexTwoStateMarkov") {}
TclObject* create(int, const char*const*) {
return (new ComplexTwoStateErrorModel);
}
} class_errormodel_complextwostatemarkov;
static class MultiStateErrorModelClass : public TclClass {
public:
MultiStateErrorModelClass() : TclClass("ErrorModel/MultiState") {}
TclObject* create(int, const char*const*) {
return (new MultiStateErrorModel);
}
} class_errormodel_multistate;
static class TraceErrorModelClass : public TclClass {
public:
TraceErrorModelClass() : TclClass("ErrorModel/Trace") {}
TclObject* create(int, const char*const*) {
return (new TraceErrorModel);
}
} class_traceerrormodel;
static char* eu_names[] = { EU_NAMES };
inline double comb(int n, int k) {
int i;
double sum = 1.0;
for(i = 0; i < k; i++)
sum *= (n - i)/(i + 1);
return sum;
}
ErrorModel::ErrorModel() : et_(0), firstTime_(1), unit_(EU_PKT), ranvar_(0), FECstrength_(1)
{
bind("enable_", &enable_);
bind("rate_", &rate_);
bind("delay_", &delay_);
bind_bw("bandwidth_", &bandwidth_); // required for EU_TIME
bind_bool("markecn_", &markecn_);
bind_bool("delay_pkt_", &delay_pkt_);
}
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);
}
if (strcmp(argv[1], "FECstrength") == 0) {
FECstrength_ = atoi(argv[2]);
return (TCL_OK);
}
if (strcmp(argv[1], "datapktsize") == 0) {
datapktsize_ = atoi(argv[2]);
return (TCL_OK);
}
if (strcmp(argv[1], "cntrlpktsize") == 0) {
cntrlpktsize_ = atoi(argv[2]);
return (TCL_OK);
}
if (strcmp(argv[1], "eventtrace") == 0) {
et_ = (EventTrace *)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);
}
if (strcmp(argv[1], "FECstrength") == 0) {
tcl.resultf("%d", FECstrength_);
return (TCL_OK);
}
}
return Connector::command(argc, argv);
}
void ErrorModel::reset()
{
firstTime_ = 1;
}
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_ && !delay_pkt_ && (h && ((error && drop_) || !target_))) {
// if we drop or there is no target_, then resume handler
double delay = Random::uniform(8.0 * ch->size() / bandwidth_);
if (intr_.uid_ <= 0 )
// schedule only if nothing scheduled already
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 (delay_pkt_) {
// Delay the packet.
Scheduler::instance().schedule(target_, p, delay_);
return;
} else if (drop_) {
drop_->recv(p);
return;
}
}
if (target_) {
target_->recv(p, h);
}
}
int ErrorModel::corrupt(Packet* p)
{
hdr_cmn* ch;
// a temp hack
ch = HDR_CMN(p);
if (enable_ == 0)
return 0;
switch (unit_) {
case EU_TIME:
return (CorruptTime(p) != 0);
case EU_BYTE:
return (CorruptByte(p) != 0);
case EU_BIT:
ch = hdr_cmn::access(p);
ch->errbitcnt() = CorruptBit(p);
return (ch->errbitcnt() != 0);
default:
return (CorruptPkt(p) != 0);
}
return 0;
}
double ErrorModel::PktLength(Packet* p)
{
//double now;
if (unit_ == EU_PKT)
return 1;
int byte = hdr_cmn::access(p)->size();
if (unit_ == EU_BYTE)
return byte;
if (unit_ == EU_BIT)
return 8.0 * byte;
return 8.0 * byte / bandwidth_;
}
double * ErrorModel::ComputeBitErrProb(int size)
{
double *dptr;
int i;
dptr = (double *)calloc((FECstrength_ + 2), sizeof(double));
for (i = 0; i < (FECstrength_ + 1) ; i++)
dptr[i] = comb(size, i) * pow(rate_, (double)i) * pow(1.0 - rate_, (double)(size - i));
// Cumulative probability
for (i = 0; i < FECstrength_ ; i++)
dptr[i + 1] += dptr[i];
dptr[FECstrength_ + 1] = 1.0;
/* printf("Size = %d\n", size);
for (i = 0; i <(FECstrength_ + 2); i++)
printf("Ptr[%d] = %g\n", i, dptr[i]); */
return dptr;
}
int ErrorModel::CorruptPkt(Packet*)
{
// if no random var is specified, assume uniform random variable
double u = ranvar_ ? ranvar_->value() : Random::uniform();
return (u < rate_);
}
int ErrorModel::CorruptByte(Packet* p)
{
// compute pkt error rate, assume uniformly distributed byte error
double per = 1 - pow(1.0 - rate_, PktLength(p));
double u = ranvar_ ? ranvar_->value() : Random::uniform();
return (u < per);
}
int ErrorModel::CorruptBit(Packet* p)
{
double u, *dptr;
int i;
if (firstTime_ && FECstrength_) {
// precompute the probabilies for each bit-error cnts
cntrlprb_ = ComputeBitErrProb(cntrlpktsize_);
dataprb_ = ComputeBitErrProb(datapktsize_);
firstTime_ = 0;
}
u = ranvar_ ? ranvar_->value() : Random::uniform();
dptr = (hdr_cmn::access(p)->size() >= datapktsize_) ? dataprb_ : cntrlprb_;
for (i = 0; i < (FECstrength_ + 2); i++)
if (dptr[i] > u) break;
return(i);
}
int ErrorModel::CorruptTime(Packet *)
{
fprintf(stderr, "Warning: uniform rate error cannot be time-based\n");
return 0;
}
#if 0
/*
* Decide whether or not to corrupt this packet, for a continuous
* time-based error model. The main parameter used is errLength,
* which is the time to the next error, from the last time an error
* occured on the channel. It is dependent on the random variable
* being used internally.
* rate_ is the user-specified mean
*/
int ErrorModel::CorruptTime(Packet *p)
{
/*
* First get MAC header. It has the transmission time (txtime)
* of the packet in one of it's fields. Then, get the time
* interval [t-txtime, t], where t is the current time. The
* goal is to figure out whether the channel would have
* corrupted the packet during that interval.
*/
Scheduler &s = Scheduler::instance();
double now = s.clock(), rv;
int numerrs = 0;
double start = now - hdr_mac::access(p)->txtime();
while (remainLen_ < start) {
rv = ranvar_ ? ranvar_->value() : Random::uniform(rate_);
remainLen_ += rv;
}
while (remainLen_ < now) { /* corrupt the packet */
numerrs++;
rv = ranvar_ ? ranvar_->value() : Random::uniform(rate_);
remainLen_ += rv;
}
return numerrs;
}
#endif
void ErrorModel::trace_event(char *eventtype)
{
if (et_ == NULL) return;
char *wrk = et_->buffer();
char *nwrk = et_->nbuffer();
if (wrk != 0)
sprintf(wrk,
"E "TIME_FORMAT" ErrModelTimer %p %s",
et_->round(Scheduler::instance().clock()), // time
this,
eventtype // event type
);
if (nwrk != 0)
sprintf(nwrk,
"E -t "TIME_FORMAT" ErrModelTimer %p %s",
et_->round(Scheduler::instance().clock()), // time
this,
eventtype // event type
);
et_->trace();
}
/*
* Two-State: error-free and error
*/
TwoStateErrorModel::TwoStateErrorModel() : remainLen_(0), twoStateTimer_(NULL)
{
ranvar_[0] = ranvar_[1] = 0;
}
int TwoStateErrorModel::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
if (strcmp(argv[1], "ranvar") == 0) {
int i = atoi(argv[2]);
if (i < 0 || i > 1) {
tcl.resultf("%s does not has ranvar_[%d]", name_, i);
return (TCL_ERROR);
}
if (argc == 3) {
tcl.resultf("%s", ranvar_[i]->name());
return (TCL_OK);
}
if (argc == 4) {
ranvar_[i] = (RandomVariable*)TclObject::lookup(argv[3]);
if (ranvar_[0] != 0 && ranvar_[1] != 0)
checkUnit();
return (TCL_OK);
}
}
return ErrorModel::command(argc, argv);
}
int TwoStateErrorModel::corruptPkt(Packet* p)
{
#define ZERO 0.00000
int error;
if (firstTime_) {
firstTime_ = 0;
state_ = 0;
remainLen_ = ranvar_[state_]->value();
}
// if remainLen_ is outside the range of 0, then error = state_
error = state_ && (remainLen_ > ZERO);
remainLen_ -= PktLength(p);
// state transition until remainLen_ > 0 to covers the packet length
while (remainLen_ <= ZERO) {
state_ ^= 1; // state transition: 0 <-> 1
remainLen_ += ranvar_[state_]->value();
error |= state_;
}
return error;
}
void TwoStateErrorModel::checkUnit()
{
if (unit_ == EU_TIME) {
// setup timer for keeping states in time
twoStateTimer_ = new TwoStateErrModelTimer(this, &TwoStateErrorModel::transitionState);
transitionState();
}
}
void TwoStateErrorModel::transitionState()
{
char buf[SMALL_LEN];
if (firstTime_) {
firstTime_ = 0;
state_ = 0;
remainLen_ = ranvar_[state_]->value();
twoStateTimer_->sched(remainLen_);
sprintf (buf,"STATE %d, DURATION %f",state_,remainLen_);
trace_event(buf);
return;
}
state_ ^= 1;
remainLen_ = ranvar_[state_]->value();
twoStateTimer_->resched(remainLen_);
sprintf (buf,"STATE %d, DURATION %f",state_,remainLen_);
trace_event(buf);
}
int TwoStateErrorModel::corrupt(Packet* p)
{
if (unit_ == EU_TIME)
return corruptTime(p);
else
return corruptPkt(p);
}
int TwoStateErrorModel::corruptTime(Packet* p)
{
int error = 0;
if (state_ == 1)
error = 1;
return error;
}
ComplexTwoStateErrorModel::ComplexTwoStateErrorModel()
{
em_[0] = new TwoStateErrorModel();
em_[1] = new TwoStateErrorModel();
}
ComplexTwoStateErrorModel::~ComplexTwoStateErrorModel()
{
delete em_[0];
delete em_[1];
}
int ComplexTwoStateErrorModel::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
if (argc == 3) {
if (strcmp(argv[1], "unit") == 0) {
unit_ = STR2EU(argv[2]);
em_[0]->setunit(unit_);
em_[1]->setunit(unit_);
return TCL_OK;
}
if (strcmp(argv[1], "eventtrace") == 0) {
EventTrace* et = (EventTrace *)TclObject::lookup(argv[2]);
em_[0]->et_ = et;
em_[1]->et_ = et;
return (TCL_OK);
}
}
else if (argc == 5) {
if (strcmp(argv[1], "ranvar") == 0) {
int i = atoi(argv[2]);
int j = atoi(argv[3]);
if (i < 0 || i > 1) {
tcl.add_errorf("%s does not has em_[%d]", name_, i);
return (TCL_ERROR);
}
if (j < 0 || j > 1) {
tcl.add_errorf("%s does not has ranvar_[%d]", name_, i);
return (TCL_ERROR);
}
em_[i]->ranvar_[j] = (RandomVariable*)TclObject::lookup(argv[4]);
if (em_[i]->ranvar_[0] != 0 && em_[i]->ranvar_[1] != 0)
em_[i]->checkUnit();
return (TCL_OK);
}
}
return ErrorModel::command(argc, argv);
}
int ComplexTwoStateErrorModel::corruptTime(Packet* p)
{
int error = 0;
if (em_[0]->state_ == 1 && em_[1]->state_ == 1)
error = 1;
return error;
}
int ComplexTwoStateErrorModel::corruptPkt(Packet* p)
{
fprintf(stderr, "Error model defined in time; not in packets\n");
return -1;
}
static char * st_names[]={ST_NAMES};
/*
// MultiState ErrorModel:
// corrupt(pkt) invoke Tcl method "corrupt" to do state transition
// Tcl corrupt either:
// - assign em_, the error-model to be use
// - return the status of the packet
// If em_ is assigned, then invoke em_->corrupt(p)
*/
MultiStateErrorModel::MultiStateErrorModel() : prevTime_(0.0), em_(0)
{
bind("sttype_", &sttype_);
bind("texpired_", &texpired_);
bind("curperiod_", &curperiod_);
}
int MultiStateErrorModel::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
if (argc == 3) {
if (strcmp(argv[1], "error-model") == 0) {
em_ = (ErrorModel*) TclObject::lookup(argv[2]);
return TCL_OK;
}
if (strcmp(argv[1], "sttype") == 0) {
sttype_ = STR2ST(argv[2]);
return TCL_OK;
}
} else if (argc == 2) {
if (strcmp(argv[1], "sttype") == 0) {
tcl.resultf("%s", st_names[sttype_]);
return TCL_OK;
}
if (strcmp(argv[1], "error-model") == 0) {
tcl.resultf("%s", (ErrorModel*) em_->name());
return TCL_OK;
}
}
return ErrorModel::command(argc, argv);
}
int MultiStateErrorModel::corrupt(Packet* p)
{
int retval;
double now;
// static double prevTime_ = 0.0;
Scheduler & s = Scheduler::instance();
now = s.clock();
if (sttype_ == ST_TIME)
if ((now - prevTime_) >= curperiod_)
texpired_ = 1;
Tcl& tcl = Tcl::instance();
tcl.evalf("%s corrupt", name());
retval = em_ ? em_->corrupt(p) : atoi(tcl.result());
if (firstTime_) {
firstTime_ = 0;
prevTime_ = s.clock();
texpired_ = 0;
}
return (retval);
}
TraceErrorModel::TraceErrorModel() : loss_(0), good_(123456789)
{
bind("good_", &good_);
bind("loss_", &loss_);
}
/* opening and reading the trace file/info is done in OTcl */
int TraceErrorModel::corrupt(Packet* p)
{
Tcl& tcl = Tcl::instance();
if (! match(p))
return 0;
if ((good_ <= 0) && (loss_ <= 0)) {
tcl.evalf("%s read",name());
if (good_ < 0)
good_ = 123456789;
}
if (good_-- > 0)
return 0;
return (loss_-- > 0);
}
int TraceErrorModel::match(Packet*)
{
return 1;
}
/*
* Periodic ErrorModel
*/
static class PeriodicErrorModelClass : public TclClass {
public:
PeriodicErrorModelClass() : TclClass("ErrorModel/Periodic") {}
TclObject* create(int, const char*const*) {
return (new PeriodicErrorModel);
}
} class_periodic_error_model;
PeriodicErrorModel::PeriodicErrorModel() : cnt_(0), last_time_(0.0), first_time_(-1.0)
{
bind("period_", &period_);
bind("offset_", &offset_);
bind("burstlen_", &burstlen_);
bind("default_drop_", &default_drop_);
}
int PeriodicErrorModel::corrupt(Packet* p)
{
hdr_cmn *ch = hdr_cmn::access(p);
double now = Scheduler::instance().clock();
if (unit_ == EU_TIME) {
if (first_time_ < 0.0) {
if (now >= offset_) {
first_time_ = last_time_ = now;
return 1;
}
} else {
if ((now - last_time_) > period_) {
last_time_ = now;
return 1;
}
if ((now - last_time_) < burstlen_) {
return 1;
}
}
return 0;
}
cnt_ += (unit_ == EU_PKT) ? 1 : ch->size();
if (default_drop_) {
if (int(first_time_) < 0) {
if (cnt_ >= int(offset_)) {
last_time_ = first_time_ = 1.0;
cnt_ = 0;
return 0;
}
return 0;
} else {
if (cnt_ >= int(period_)) {
cnt_ = 0;
return 0;
}
}
return 1;
} else {
if (int(first_time_) < 0) {
if (cnt_ >= int(offset_)) {
last_time_ = first_time_ = 1.0;
cnt_ = 0;
return 1;
}
return 0;
} else {
if (cnt_ >= int(period_)) {
cnt_ = 0;
return 1;
}
if (cnt_ < burstlen_)
return 1;
}
return 0;
}
}
/*
* List ErrorModel: specify a list of packets/bytes to drop
* can be specified in any order
*/
static class ListErrorModelClass : public TclClass {
public:
ListErrorModelClass() : TclClass("ErrorModel/List") {}
TclObject* create(int, const char*const*) {
return (new ListErrorModel);
}
} class_list_error_model;
int ListErrorModel::corrupt(Packet* p)
{
/* assumes droplist_ is sorted */
int rval = 0; // no drop
if (unit_ == EU_TIME) {
fprintf(stderr,
"ListErrorModel: error, EU_TIME not supported\n");
return 0;
}
if (droplist_ == NULL || dropcnt_ == 0) {
fprintf(stderr, "warning: ListErrorModel: null drop list\n");
return 0;
}
if (unit_ == EU_PKT) {
//printf("TEST: cur_:%d, dropcnt_:%d, droplist_[cur_]:%d, cnt_:%d\n",
//cur_, dropcnt_, droplist_[cur_], cnt_);
if ((cur_ < dropcnt_) && droplist_[cur_] == cnt_) {
rval = 1;
cur_++;
}
cnt_++;
} else if (unit_ == EU_BYTE) {
int sz = hdr_cmn::access(p)->size();
if ((cur_ < dropcnt_) && (cnt_ + sz) >= droplist_[cur_]) {
rval = 1;
cur_++;
}
cnt_ += sz;
}
return (rval);
}
int
ListErrorModel::command(int argc, const char*const* argv)
{
/*
* works for variable args:
* $lem droplist "1 3 4 5"
* and
* $lem droplist 1 3 4 5
*/
Tcl& tcl = Tcl::instance();
if (strcmp(argv[1], "droplist") == 0) {
int cnt;
if ((cnt = parse_droplist(argc-2, argv + 2)) < 0)
return (TCL_ERROR);
tcl.resultf("%u", cnt);
return(TCL_OK);
}
return (ErrorModel::command(argc, argv));
}
int
ListErrorModel::intcomp(const void *p1, const void *p2)
{
int a = *((int*) p1);
int b = *((int*) p2);
return (a - b);
}
/*
* nextval: find the next value in the string
*
* skip white space, update pointer to first non-white-space
* character. Return the number of characters in the next
* token.
*/
int
ListErrorModel::nextval(const char*& p)
{
while (*p && isspace(*p))
++p;
if (!*p) {
/* end of string */
return (0);
}
const char *q = p;
while (*q && !isspace(*q))
++q;
return (q-p);
}
int
ListErrorModel::parse_droplist(int argc, const char *const* argv)
{
int cnt = 0; // counter for argc list
int spaces = 0; // counts # of spaces in an argv entry
int total = 0; // total entries in the drop list
int n; // # of chars in the next drop number
const char *p; // ptr into current string
/*
* loop over argc list: figure out how many numbers
* have been specified
*/
while (cnt < argc) {
p = argv[cnt];
spaces = 0;
while ((n = nextval(p))) {
if (!isdigit(*p)) {
/* problem... */
fprintf(stderr, "ListErrorModel(%s): parse_droplist: unknown drop specifier starting at >>>%s\n",
name(), p);
return (-1);
}
++spaces;
p += n;
}
total += spaces;
cnt++;
}
/*
* parse the numbers, put them in an array (droplist_)
* set dropcnt_ to the total # of drops. Also, free any
* previous drop list.
*/
if ((total == 0) || (dropcnt_ > 0 && droplist_ != NULL)) {
delete[] droplist_;
droplist_ = NULL;
}
if ((dropcnt_ = total) == 0)
return (0);
droplist_ = new int[dropcnt_];
if (droplist_ == NULL) {
fprintf(stderr,
"ListErrorModel(%s): no memory for drop list!\n",
name());
return (-1);
}
int idx = 0;
cnt = 0;
while (cnt < argc) {
p = argv[cnt];
while ((n = nextval(p))) {
/*
* this depends on atoi(s) returning the
* value of the first number in s
*/
droplist_[idx++] = atoi(p);
p += n;
}
cnt++;
}
qsort(droplist_, dropcnt_, sizeof(int), intcomp);
/*
* sanity check the array, looking for (wrong) dups
*/
cnt = 0;
while (cnt < (dropcnt_ - 1)) {
if (droplist_[cnt] == droplist_[cnt+1]) {
fprintf(stderr,
"ListErrorModel: error: dup %d in list\n",
droplist_[cnt]);
total = -1; /* error */
}
++cnt;
}
if (total < 0) {
if (droplist_)
delete[] droplist_;
dropcnt_ = 0;
droplist_ = NULL;
return (-1);
}
#ifdef notdef
printf("sorted list:\n");
{
register i;
for (i =0; i < dropcnt_; i++) {
printf("list[%d] = %d\n", i, droplist_[i]);
}
}
#endif
return dropcnt_;
}
/***** ***/
static class SelectErrorModelClass : public TclClass {
public:
SelectErrorModelClass() : TclClass("SelectErrorModel") {}
TclObject* create(int, const char*const*) {
return (new SelectErrorModel);
}
} class_selecterrormodel;
SelectErrorModel::SelectErrorModel()
{
bind("pkt_type_", (int*)&pkt_type_);
bind("drop_cycle_", &drop_cycle_);
bind("drop_offset_", &drop_offset_);
}
int SelectErrorModel::command(int argc, const char*const* argv)
{
if (strcmp(argv[1], "drop-packet") == 0) {
pkt_type_ = packet_t(atoi(argv[2]));
drop_cycle_ = atoi(argv[3]);
drop_offset_ = atoi(argv[4]);
return TCL_OK;
}
return ErrorModel::command(argc, argv);
}
int SelectErrorModel::corrupt(Packet* p)
{
if (unit_ == EU_PKT) {
hdr_cmn *ch = hdr_cmn::access(p);
// XXX Backward compatibility for cbr agents
if (ch->ptype() == PT_UDP && pkt_type_ == PT_CBR)
pkt_type_ = PT_UDP; // "udp" rather than "cbr"
if (ch->ptype() == pkt_type_ && ch->uid() % drop_cycle_
== drop_offset_) {
//printf ("dropping packet type %d, uid %d\n",
// ch->ptype(), ch->uid());
return 1;
}
}
return 0;
}
/* Error model for srm experiments */
class SRMErrorModel : public SelectErrorModel {
public:
SRMErrorModel();
virtual int corrupt(Packet*);
protected:
int command(int argc, const char*const* argv);
};
static class SRMErrorModelClass : public TclClass {
public:
SRMErrorModelClass() : TclClass("SRMErrorModel") {}
TclObject* create(int, const char*const*) {
return (new SRMErrorModel);
}
} class_srmerrormodel;
SRMErrorModel::SRMErrorModel()
{
}
int SRMErrorModel::command(int argc, const char*const* argv)
{
//int ac = 0;
if (strcmp(argv[1], "drop-packet") == 0) {
pkt_type_ = packet_t(atoi(argv[2]));
drop_cycle_ = atoi(argv[3]);
drop_offset_ = atoi(argv[4]);
return TCL_OK;
}
return ErrorModel::command(argc, argv);
}
int SRMErrorModel::corrupt(Packet* p)
{
if (unit_ == EU_PKT) {
hdr_srm *sh = hdr_srm::access(p);
hdr_cmn *ch = hdr_cmn::access(p);
// XXX Backward compatibility for cbr agents
if (ch->ptype()==PT_UDP && pkt_type_==PT_CBR && sh->type() == SRM_DATA)
pkt_type_ = PT_UDP; // "udp" rather than "cbr"
if ((ch->ptype() == pkt_type_) && (sh->type() == SRM_DATA) &&
(sh->seqnum() % drop_cycle_ == drop_offset_)) {
//printf ("dropping packet type SRM-DATA, seqno %d\n",
//sh->seqnum());
return 1;
}
}
return 0;
}
static class MrouteErrorModelClass : public TclClass {
public:
MrouteErrorModelClass() : TclClass("ErrorModel/Trace/Mroute") {}
TclObject* create(int, const char*const*) {
return (new MrouteErrorModel);
}
} class_mrouteerrormodel;
MrouteErrorModel::MrouteErrorModel() : TraceErrorModel()
{
}
int MrouteErrorModel::command(int argc, const char*const* argv)
{
if (argc == 3) {
if (strcmp(argv[1], "drop-packet") == 0) {
const char* s = argv[2];
int n = strlen(s);
if (n >= this->maxtype()) {
// tcl.result("message type too big");
return (TCL_ERROR);
}
strcpy(msg_type,s);
return(TCL_OK);
}
}
return TraceErrorModel::command(argc, argv);
}
int MrouteErrorModel::match(Packet* p)
{
hdr_mcast_ctrl* ph = hdr_mcast_ctrl::access(p);
int indx = strcspn(ph->type(),"/");
if (!strncmp(ph->type(),msg_type,indx)) {
return 1;
}
return 0;
}
static class ErrorModuleClass : public TclClass {
public:
ErrorModuleClass() : TclClass("ErrorModule") {}
TclObject* create(int, const char*const*) {
return (new ErrorModule);
}
} class_errormodule;
void ErrorModule::recv(Packet *p, Handler *h)
{
classifier_->recv(p, h);
}
int ErrorModule::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
if (argc == 2) {
if (strcmp(argv[1], "classifier") == 0) {
if (classifier_)
tcl.resultf("%s", classifier_->name());
else
tcl.resultf("");
return (TCL_OK);
}
} else if (argc == 3) {
if (strcmp(argv[1], "classifier") == 0) {
classifier_ = (Classifier*)
TclObject::lookup(argv[2]);
if (classifier_ == NULL) {
tcl.resultf("Couldn't look up classifier %s", argv[2]);
return (TCL_ERROR);
}
return (TCL_OK);
}
}
return (Connector::command(argc, argv));
}
#include "config.h"
#ifdef HAVE_STL //pgm uses STL
#include "pgm/pgm.h"
static class PGMErrorModelClass : public TclClass {
public:
PGMErrorModelClass() : TclClass("PGMErrorModel") {}
TclObject* create(int, const char*const*) {
return (new PGMErrorModel);
}
} class_pgm_errormodel;
PGMErrorModel::PGMErrorModel() : ErrorModel(), pgm_type_(-1), count_(0)
{
ndrops_ = 0;
bind("ndrops_", &ndrops_);
}
int PGMErrorModel::command(int argc, const char*const* argv)
{
if (strcmp(argv[1], "drop-packet") == 0) {
if (!strcasecmp(argv[2], "SPM")) {
pgm_type_ = PGM_SPM;
}
else if (!strcasecmp(argv[2], "ODATA")) {
pgm_type_ = PGM_ODATA;
}
else if (!strcasecmp(argv[2], "RDATA")) {
pgm_type_ = PGM_RDATA;
}
else if (!strcasecmp(argv[2], "NAK")) {
pgm_type_ = PGM_NAK;
}
else if (!strcasecmp(argv[2], "NCF")) {
pgm_type_ = PGM_NCF;
}
else {
fprintf(stderr, "PGMErrorModel: drop-packet PGM type \"%s\" unknown.\n", argv[2]);
return TCL_ERROR;
}
drop_cycle_ = atoi(argv[3]);
drop_offset_ = atoi(argv[4]);
return TCL_OK;
}
return ErrorModel::command(argc, argv);
}
int PGMErrorModel::corrupt(Packet* p)
{
if (unit_ == EU_PKT) {
hdr_cmn *ch = HDR_CMN(p);
hdr_pgm *hp = HDR_PGM(p);
if ((ch->ptype() == PT_PGM) && (hp->type_ == pgm_type_)) {
count_++;
if (count_ % drop_cycle_ == drop_offset_) {
#ifdef PGM_DEBUG
printf ("DROPPING PGM packet type %d, seqno %d\n", pgm_type_, hp->seqno_);
#endif
++ndrops_;
return 1;
}
}
}
return 0;
}
#endif //HAVE_STL
//
// LMS Error Model
//
#include "rtp.h"
#include "mcast/lms.h"
static class LMSErrorModelClass : public TclClass {
public:
LMSErrorModelClass() : TclClass("LMSErrorModel") {}
TclObject* create(int, const char*const*) {
return (new LMSErrorModel);
}
} class_lms_errormodel;
LMSErrorModel::LMSErrorModel() : ErrorModel()
{
ndrops_ = 0;
bind("ndrops_", &ndrops_);
}
int LMSErrorModel::command(int argc, const char*const* argv)
{
if (strcmp(argv[1], "drop-packet") == 0)
{
pkt_type_ = packet_t(atoi(argv[2]));
drop_cycle_ = atoi(argv[3]);
drop_offset_ = atoi(argv[4]);
return TCL_OK;
}
return ErrorModel::command(argc, argv);
}
int LMSErrorModel::corrupt(Packet* p)
{
if (unit_ == EU_PKT)
{
hdr_cmn *ch = HDR_CMN(p);
hdr_lms *lh = HDR_LMS(p);
hdr_rtp *rh = HDR_RTP(p);
if ((ch->ptype() == pkt_type_) && (lh->type_ != LMS_DMCAST) &&
(rh->seqno() % drop_cycle_ == drop_offset_))
{
#ifdef LMS_DEBUG
printf ("Error Model: DROPPING pkt type %d, seqno %d\n", pkt_type_, rh->seqno());
#endif
++ndrops_;
return 1;
}
}
return 0;
}
void TwoStateErrModelTimer::expire(Event *e)
{
(*a_.*call_back_)();
}
syntax highlighted by Code2HTML, v. 0.9.1