win32.c
/*
* Copyright (c) 1996 The 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 Network Research
* Group at Lawrence Berkeley National Laboratory.
* 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.
*
* This module contributed by John Brezak .
* January 31, 1996
*
* @(#) $Header$ (LBL)
*/
#ifdef WIN32
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "config.h"
#include
/* forward declarations */
int WinGetUserName(ClientData, Tcl_Interp*, int ac, char**av);
int WinGetHostName(ClientData, Tcl_Interp*, int ac, char**av);
int WinPutRegistry(ClientData, Tcl_Interp*, int ac, char**av);
int WinGetRegistry(ClientData, Tcl_Interp*, int ac, char**av);
void TkConsoleCreate();
int TkConsoleInit(Tcl_Interp* interp);
int
uname(struct utsname *ub)
{
char *ptr;
DWORD version;
SYSTEM_INFO sysinfo;
char hostname[4096];
version = GetVersion();
GetSystemInfo(&sysinfo);
switch (sysinfo.wProcessorArchitecture) {
case PROCESSOR_ARCHITECTURE_INTEL:
(void)strcpy(ub->machine, "ix86");
break;
case PROCESSOR_ARCHITECTURE_MIPS :
(void)strcpy(ub->machine, "mips");
break;
case PROCESSOR_ARCHITECTURE_ALPHA:
(void)strcpy(ub->machine, "alpha");
break;
case PROCESSOR_ARCHITECTURE_PPC:
(void)strcpy(ub->machine, "ppc");
break;
default:
(void)strcpy(ub->machine, "unknown");
break;
}
if (version < 0x80000000) {
(void)strcpy(ub->version, "NT");
}
else if (LOBYTE(LOWORD(version))<4) {
(void)strcpy(ub->version, "Win32s");
}
else /* Win95 */ {
(void)strcpy(ub->version, "Win95");
}
(void)sprintf(ub->release, "%u.%u",
(DWORD)(LOBYTE(LOWORD(version))),
(DWORD)(HIBYTE(LOWORD(version))));
(void)strcpy(ub->sysname, "Windows");
if (gethostname(hostname, sizeof(hostname)) == 0) {
if (ptr = strchr(hostname, '.'))
*ptr = '\0';
}
else {
perror("uname: gethostname failed");
strcpy(hostname, "FAILURE");
}
strncpy(ub->nodename, hostname, sizeof(ub->nodename));
ub->nodename[_SYS_NMLN - 1] = '\0';
return 0;
}
int strcasecmp(const char *s1, const char *s2)
{
return stricmp(s1, s2);
}
uid_t getuid(void)
{
return 1;
}
gid_t getgid(void)
{
return 0;
}
int gethostid(void)
{
/*XXX*/
return 0;
}
__inline int nice(int pri)
{
return 0;
}
extern void TkWinXInit(HINSTANCE hInstance);
extern int main(int argc, const char *argv[]);
extern int __argc;
extern char **__argv;
static char argv0[255]; /* Buffer used to hold argv0. */
char *__progname = "mash";
void
ShowMessage(int level, char *msg)
{
MessageBeep(level);
MessageBox(NULL, msg, __progname,
level | MB_OK | MB_TASKMODAL | MB_SETFOREGROUND);
}
int SetupConsole()
{
/*
* stuff from knowledge base Q105305 (see that for details)
* open a console and do the work around to get the console to work in all
* cases
*/
int hCrt;
FILE *hf=0;
const COORD screenSz = {80, 5000}; /* size of console buffer */
AllocConsole();
hf=0;
hCrt = _open_osfhandle(
(long)GetStdHandle(STD_OUTPUT_HANDLE), _O_TEXT);
if (hCrt!=-1) hf = _fdopen(hCrt, "w");
if (hf!=0) *stdout = *hf;
if (hCrt==-1 || hf==0 || 0!=setvbuf(stdout, NULL, _IONBF, 0)) {
ShowMessage(MB_ICONINFORMATION,
"unable to mount reroute stdout");
return FALSE;
}
SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), screenSz);
hf=0;
hCrt = _open_osfhandle(
(long)GetStdHandle(STD_ERROR_HANDLE), _O_TEXT);
if (hCrt!=-1) hf = _fdopen(hCrt, "w");
if (hf!=0) *stderr = *hf;
if (hCrt==-1 || hf==0 || 0!=setvbuf(stderr, NULL, _IONBF, 0)) {
ShowMessage(MB_ICONINFORMATION,
"reroute stderr failed in SetupConsole");
return FALSE;
}
hf=0;
hCrt = _open_osfhandle((long)GetStdHandle(STD_INPUT_HANDLE), _O_TEXT);
if (hCrt!=-1) hf = _fdopen(hCrt, "r");
if (hf!=0) *stdin = *hf;
if (hCrt==-1 || hf==0 || 0!=setvbuf(stdin, NULL, _IONBF, 0)) {
ShowMessage(MB_ICONINFORMATION,
"reroute stdin failed in SetupConsole");
return FALSE;
}
return TRUE;
}
int APIENTRY
WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpszCmdLine,
int nCmdShow)
{
char *p;
WSADATA WSAdata;
int retcode;
setlocale(LC_ALL, "C");
/* XXX
* initialize our socket interface plus the tcl 7.5 socket
* interface (since they redefine some routines we call).
* eventually we should just call the tcl sockets but at
* the moment that's hard to set up since they only support
* tcp in the notifier.
*/
if (WSAStartup(MAKEWORD (1, 1), &WSAdata)) {
perror("Windows Sockets init failed");
abort();
}
/* TclHasSockets(NULL);
TkWinXInit(hInstance); */
/*
* Increase the application queue size from default value of 8.
* At the default value, cross application SendMessage of WM_KILLFOCUS
* will fail because the handler will not be able to do a PostMessage!
* This is only needed for Windows 3.x, since NT dynamically expands
* the queue.
*/
SetMessageQueue(64);
GetModuleFileName(NULL, argv0, 255);
p = argv0;
__progname = strrchr(p, '/');
if (__progname != NULL) {
__progname++;
}
else {
__progname = strrchr(p, '\\');
if (__progname != NULL) {
__progname++;
} else {
__progname = p;
}
}
if (__argc>1) {
SetupConsole();
}
retcode=main(__argc, (const char**)__argv);
if (retcode!=0) {
assert(FALSE); /* don't die without letting user know why */
}
return retcode;
}
#if 0
static char szTemp[4096];
int
printf(const char *fmt, ...)
{
int retval;
va_list ap;
va_start (ap, fmt);
retval = vsprintf(szTemp, fmt, ap);
OutputDebugString(szTemp);
ShowMessage(MB_ICONINFORMATION, szTemp);
va_end (ap);
return(retval);
}
int
fprintf(FILE *f, const char *fmt, ...)
{
int retval;
va_list ap;
va_start (ap, fmt);
if (f == stderr) {
retval = vsprintf(szTemp, fmt, ap);
OutputDebugString(szTemp);
ShowMessage(MB_ICONERROR, szTemp);
va_end (ap);
}
else
retval = vfprintf(f, fmt, ap);
return(retval);
}
void
perror(const char *msg)
{
DWORD cMsgLen;
CHAR *msgBuf;
DWORD dwError = GetLastError();
cMsgLen = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_ALLOCATE_BUFFER | 40, NULL,
dwError,
MAKELANGID(0, SUBLANG_ENGLISH_US),
(LPTSTR) &msgBuf, 512,
NULL);
if (!cMsgLen)
fprintf(stderr, "%s%sError code %lu\n",
msg?msg:"", msg?": ":"", dwError);
else {
fprintf(stderr, "%s%s%s\n", msg?msg:"", msg?": ":"", msgBuf);
LocalFree((HLOCAL)msgBuf);
}
}
int
WinPutsCmd(clientData, interp, argc, argv)
ClientData clientData; /* ConsoleInfo pointer. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
char **argv; /* Argument strings. */
{
int i, newline;
char *fileId;
i = 1;
newline = 1;
if ((argc >= 2) && (strcmp(argv[1], "-nonewline") == 0)) {
newline = 0;
i++;
}
if ((i < (argc-3)) || (i >= argc)) {
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" ?-nonewline? ?fileId? string\"", (char *) NULL);
return TCL_ERROR;
}
/*
* The code below provides backwards compatibility with an old
* form of the command that is no longer recommended or documented.
*/
if (i == (argc-3)) {
if (strncmp(argv[i+2], "nonewline", strlen(argv[i+2])) != 0) {
Tcl_AppendResult(interp, "bad argument \"", argv[i+2],
"\": should be \"nonewline\"", (char *) NULL);
return TCL_ERROR;
}
newline = 0;
}
if (i == (argc-1)) {
fileId = "stdout";
} else {
fileId = argv[i];
i++;
}
if (strcmp(fileId, "stdout") == 0 || strcmp(fileId, "stderr") == 0) {
char *result;
int level;
if (newline) {
int len = strlen(argv[i]);
result = ckalloc(len+2);
memcpy(result, argv[i], len);
result[len] = '\n';
result[len+1] = 0;
} else {
result = argv[i];
}
if (strcmp(fileId, "stdout") == 0) {
level = MB_ICONINFORMATION;
} else {
level = MB_ICONERROR;
}
OutputDebugString(result);
ShowMessage(level, result);
if (newline)
ckfree(result);
return TCL_OK;
} else {
extern int Tcl_PutsCmd(ClientData clientData, Tcl_Interp *interp,
int argc, char **argv);
return (Tcl_PutsCmd(clientData, interp, argc, argv));
}
}
#endif
int
WinGetUserName(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
char *argv[]; /* Argument strings. */
{
char user[256];
int size = sizeof(user);
if (!GetUserName(user, &size)) {
Tcl_AppendResult(interp, "GetUserName failed", NULL);
return TCL_ERROR;
}
Tcl_AppendResult(interp, user, NULL);
return TCL_OK;
}
int WinGetHostName(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
char *argv[]; /* Argument strings. */
{
char hostname[MAXGETHOSTSTRUCT];
if (SOCKET_ERROR == gethostname(hostname, MAXGETHOSTSTRUCT)) {
Tcl_AddErrorInfo(interp, "gethostname failed!");
}
Tcl_AppendResult(interp, hostname, NULL);
return TCL_OK;
}
static HKEY
regroot(root)
char *root;
{
if (strcasecmp(root, "HKEY_LOCAL_MACHINE") == 0)
return HKEY_LOCAL_MACHINE;
else if (strcasecmp(root, "HKEY_CURRENT_USER") == 0)
return HKEY_CURRENT_USER;
else if (strcasecmp(root, "HKEY_USERS") == 0)
return HKEY_USERS;
else if (strcasecmp(root, "HKEY_CLASSES_ROOT") == 0)
return HKEY_CLASSES_ROOT;
else
return NULL;
}
int
WinGetRegistry(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
char **argv; /* Argument strings. */
{
HKEY hKey, hRootKey;
DWORD dwType;
DWORD len, retCode;
CHAR *regRoot, *regPath, *keyValue, *keyData;
int retval = TCL_ERROR;
if (argc != 3) {
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
"key value\"", (char *) NULL);
return TCL_ERROR;
}
regRoot = argv[1];
keyValue = argv[2];
regPath = strchr(regRoot, '\\');
*regPath++ = '\0';
if ((hRootKey = regroot(regRoot)) == NULL) {
Tcl_AppendResult(interp, "Unknown registry root \"",
regRoot, "\"", NULL);
return (TCL_ERROR);
}
retCode = RegOpenKeyEx(hRootKey, regPath, 0,
KEY_READ, &hKey);
if (retCode == ERROR_SUCCESS) {
retCode = RegQueryValueEx(hKey, keyValue, NULL, &dwType,
NULL, &len);
if (retCode == ERROR_SUCCESS &&
dwType == REG_SZ && len) {
keyData = (CHAR *) ckalloc(len);
retCode = RegQueryValueEx(hKey, keyValue, NULL, NULL,
keyData, &len);
if (retCode == ERROR_SUCCESS) {
Tcl_AppendResult(interp, keyData, NULL);
free(keyData);
retval = TCL_OK;
}
}
RegCloseKey(hKey);
}
if (retval == TCL_ERROR) {
Tcl_AppendResult(interp, "Cannot find registry entry \"", regRoot,
"\\", regPath, "\\", keyValue, "\"", NULL);
}
return (retval);
}
int
WinPutRegistry(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
char **argv; /* Argument strings. */
{
HKEY hKey, hRootKey;
DWORD retCode;
CHAR *regRoot, *regPath, *keyValue, *keyData;
DWORD new;
int result = TCL_OK;
if (argc != 4) {
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
"key value data\"", (char *) NULL);
return TCL_ERROR;
}
regRoot = argv[1];
keyValue = argv[2];
keyData = argv[3];
regPath = strchr(regRoot, '\\');
*regPath++ = '\0';
if ((hRootKey = regroot(regRoot)) == NULL) {
Tcl_AppendResult(interp, "Unknown registry root \"",
regRoot, "\"", NULL);
return (TCL_ERROR);
}
retCode = RegCreateKeyEx(hRootKey, regPath, 0,
"",
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL,
&hKey, &new);
if (retCode == ERROR_SUCCESS) {
retCode = RegSetValueEx(hKey, keyValue, 0, REG_SZ, keyData, strlen(keyData));
if (retCode != ERROR_SUCCESS) {
Tcl_AppendResult(interp, "unable to set key \"", regRoot, "\\",
regPath, "\" with value \"", keyValue, "\"",
(char *) NULL);
result = TCL_ERROR;
}
RegCloseKey(hKey);
}
else {
Tcl_AppendResult(interp, "unable to create key \"", regRoot, "\\",
regPath, "\"", (char *) NULL);
result = TCL_ERROR;
}
return (result);
}
int platformInit(Tcl_Interp* interp)
{
/* tcl.CreateCommand("puts", WinPutsCmd, (ClientData)0); */
Tcl_CreateCommand(interp, "getusername", WinGetUserName,
(ClientData)0, (Tcl_CmdDeleteProc*)0);
Tcl_CreateCommand(interp, "gethostname", WinGetHostName,
(ClientData)0, (Tcl_CmdDeleteProc*)0);
Tcl_CreateCommand(interp, "putregistry", WinPutRegistry,
(ClientData)0, (Tcl_CmdDeleteProc*)0);
Tcl_CreateCommand(interp, "getregistry", WinGetRegistry,
(ClientData)0, (Tcl_CmdDeleteProc*)0);
#ifndef NO_TK
/*
* Initialize the console only if we are running as an interactive
* application.
*/
if (0==strcmp(Tcl_GetVar(interp, "tcl_interactive",
TCL_GLOBAL_ONLY), "1")) {
/*
* Create the console channels and install them as the standard
* channels. All I/O will be discarded until TkConsoleInit is
* called to attach the console to a text widget.
*/
TkConsoleCreate();
if (TkConsoleInit(interp) == TCL_ERROR) {
fprintf(stderr, "error calling TkConsoleInit\n");
}
}
#endif
return TCL_OK;
}
#endif
Decapsulator.cc
#include "Decapsulator.h"
#include "ip.h"
#include "packet.h"
#include "encap.h"
static class DecapsulatorClass : public TclClass {
public:
DecapsulatorClass() : TclClass("Agent/Decapsulator") {}
TclObject* create(int, const char*const*) {
return (new Decapsulator());
}
} class_decapsulator;
int Decapsulator::off_encap_= 0; //static variable, will be set in TCL
Decapsulator::Decapsulator() : Agent(PT_ENCAPSULATED)
{
bind("off_encap_", &off_encap_);
}
Packet* const Decapsulator::decapPacket(Packet* const p)
{
hdr_cmn* ch= (hdr_cmn*)p->access(hdr_cmn::offset_);
if (ch->ptype() == PT_ENCAPSULATED) {
hdr_encap* eh= (hdr_encap*)p->access(off_encap_);
return eh->decap();
}
return 0;
}
void Decapsulator::recv(Packet* p, Handler* h)
{
Packet *decap_p= decapPacket(p);
if (decap_p) {
send(decap_p, h);
Packet::free(p);
return;
}
send(p, h);
}
Encapsulator.cc
#include "packet.h"
#include "ip.h"
#include "Encapsulator.h"
#include "encap.h"
static class EncapsulatorClass : public TclClass {
public:
EncapsulatorClass() : TclClass("Agent/Encapsulator") {}
TclObject* create(int, const char*const*) {
return (new Encapsulator());
}
} class_encapsulator;
Encapsulator::Encapsulator() :
Agent(PT_ENCAPSULATED),
d_target_(0)
{
bind("status_", &status_);
bind("off_encap_", &off_encap_);
bind("overhead_", &overhead_);
}
int Encapsulator::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
if (argc == 2) {
if (strcmp(argv[1], "decap-target") == 0) {
if (d_target_ != 0) tcl.result(d_target_->name());
return (TCL_OK);
}
}
else if (argc == 3) {
if (strcmp(argv[1], "decap-target") == 0) {
d_target_ = (NsObject*)TclObject::lookup(argv[2]);
// even if it's zero, it's OK, we'll just not send to such
// a target then.
return (TCL_OK);
}
}
return (Agent::command(argc, argv));
}
void Encapsulator::recv(Packet* p, Handler* h)
{
if (d_target_) {
Packet *copy_p= p->copy();
d_target_->recv(copy_p, h);
}
if (status_) {
Packet* ep= allocpkt(); //sizeof(Packet*));
hdr_encap* eh= (hdr_encap*)ep->access(off_encap_);
eh->encap(p);
//Packet** pp= (Packet**) encap_p->accessdata();
//*pp= p;
hdr_cmn* ch_e= (hdr_cmn*)ep->access(off_cmn_);
hdr_cmn* ch_p= (hdr_cmn*)p->access(off_cmn_);
ch_e->ptype()= PT_ENCAPSULATED;
ch_e->size()= ch_p->size() + overhead_;
ch_e->timestamp()= ch_p->timestamp();
send(ep, h);
}
else send(p, h); //forward the packet as it is
}
ack-recons.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1997 The 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 at Berkeley.
* 4. Neither the name of the University nor of the Research Group 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.
*
* ack-recons.cc: contributed by the Daedalus Research Group,
* UC Berkeley (http://daedalus.cs.berkeley.edu).
*/
/*
* TCP Ack reconstructor. This object sits on the other end of a constrained
* link and intersperses TCP acks to the source (without violating the e2e
* semantics of TCP acks). This allows us to get good performance for TCP
* over various asymmetric networks, in conjunction with techniques to reduce
* the frequency of acks (such as ack filtering) with making any changes to
* the TCP source (e.g., like those implemented in tcp-asym.cc).
*/
#include "template.h"
#include "ack-recons.h"
static class AckReconsControllerClass : public TclClass {
public:
AckReconsControllerClass() : TclClass("AckReconsControllerClass") { }
TclObject* create(int, const char*const*) {
return (new AckReconsController);
}
} class_ackrecons_controller;
static class AckReconsClass : public TclClass {
public:
AckReconsClass() : TclClass("Agent/AckReconsClass") { }
TclObject* create(int, const char*const* argv) {
return new AckRecons(atoi(argv[4]), atoi(argv[5]));
}
} class_ackrecons;
/*
* Demux a packet to the right ack reconstructor.
*/
void
AckReconsController::recv(Packet *p, Handler *)
{
Tcl& tcl = Tcl::instance();
hdr_ip *ip = (hdr_ip *)p->access(off_ip_);
tcl.evalf("%s demux %d %d", name(),
ip->saddr(), ip->daddr());
AckRecons *ackRecons =
(AckRecons *) TclObject::lookup(tcl.result());
if (ackRecons == NULL) {
printf("Error: malformed ack reconstructor\n");
abort();
}
ackRecons->spq_ = spq_;
ackRecons->recv(p);
}
void
AckRecons::recv(Packet *pkt)
{
double now = Scheduler::instance().clock();
hdr_tcp *tcph = (hdr_tcp *) pkt->access(off_tcp_);
int &ack = tcph->seqno(), a, i;
Tcl& tcl = Tcl::instance();
#ifdef DEBUG
printf("%f\tRecd ack %d\n", now, ack);
#endif
if (ackTemplate_ == 0)
ackTemplate_ = pkt->copy();
if (adaptive_)
tcl.evalf("%s ackbw %d %f\n", name(), ack, now);
/* The ack spacing policy is implemented in Tcl for flexibility */
tcl.evalf("%s spacing %d\n", name(), ack);
/*
* If the difference in acks is less than a threshold, let
* it go through. Later, we will look for rapid ack arrivals
* to smooth them out and avoid the adverse effects of ack comp.
*/
if ((!ackPending_ && ack-lastAck_ <= deltaAckThresh_) || dupacks_) {
if (ack == lastRealAck_)
dupacks_++;
else if (ack > lastAck_) {
dupacks_ = 0;
lastAck_ = ack;
lastTime_ = now;
}
spq_->reconsAcks_ = 0;
spq_->enque(pkt);
spq_->reconsAcks_ = 1;
#ifdef DEBUG
printf("\t%f\tEnqueuing ack %d in order\n", now, ack);
#endif
} else {
if (ack == lastRealAck_)
dupacks_++;
/* Intersperse some acks and schedule their transmissions. */
double starttime = max(now, lastTime_);
for (a = lastAck_+delack_, i=0; a <= ack; a += delack_, i++)
sendack(a, starttime + i*ackSpacing_ - now);
if ((a-ack)%delack_)
sendack(ack, starttime + i*ackSpacing_ - now);
Packet::free(pkt);
}
if (ack >= lastRealAck_) {
lastRealAck_ = ack;
lastRealTime_ = now;
}
}
int
AckRecons::command(int argc, const char*const* argv)
{
return Agent::command(argc, argv);
}
/*
* Arrange to send ack a at time t from now.
*/
void
AckRecons::sendack(int ack, double t)
{
Packet *ackp = ackTemplate_->copy();
Scheduler &s = Scheduler::instance();
hdr_tcp *th = (hdr_tcp *) ackp->access(off_tcp_);
th->seqno() = ack;
/* Set no_ts_ in flags because we don't want an rtt sample for this */
if (th->ts() == ((hdr_tcp *) ackp->access(off_tcp_))->ts()) {
hdr_flags *fh = (hdr_flags *) ackp->access(off_flags_);
fh->no_ts_ = 1;
th->ts_ = s.clock(); /* for debugging purposes only */
}
s.schedule((Handler *)this, (Event *)ackp, t);
ackPending_++;
#ifdef DEBUG
printf("\t%f\tScheduling ack %d to be sent at %f\n",
s.clock(), ack, s.clock() + t);
#endif
}
/*
* Handle scheduling of acks.
*/
void
AckRecons::handle(Event *e)
{
Packet *p = (Packet *) e;
hdr_tcp *th = (hdr_tcp *) p->access(off_tcp_);
ackPending_--;
if (lastAck_ < th->seqno()) {
spq_->reconsAcks_ = 0;
/*
* need to do queue's recv here, so that a deque is
* forced if the queue isn't blocked. It's not
* sufficient to call spq_->recv() alone.
*/
target_->recv(p); /* maybe do acksfirst for this ack? */
spq_->reconsAcks_ = 1;
lastTime_ = Scheduler::instance().clock();
lastAck_ = th->seqno();
#ifdef DEBUG
printf("%f\tSending scheduled ack %d\n",lastTime_,th->seqno());
#endif
} else {
Packet::free(p);
#ifdef DEBUG
printf("%f\tack %d superceded by ack %d at %f\n",
Scheduler::instance().clock(), th->seqno(), lastAck_,
lastTime_);
#endif
}
}
acto-adc.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) Xerox Corporation 1997. All rights reserved.
*
* License is granted to copy, to use, and to make and to use derivative
* works for research and evaluation purposes, provided that Xerox is
* acknowledged in all documentation pertaining to any such copy or
* derivative work. Xerox grants no other licenses expressed or
* implied. The Xerox trade name should not be used in any advertising
* without its written permission.
*
* XEROX CORPORATION MAKES NO REPRESENTATIONS CONCERNING EITHER THE
* MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE
* FOR ANY PARTICULAR PURPOSE. The software is provided "as is" without
* express or implied warranty of any kind.
*
* These notices must be retained in any copies of any part of this
* software.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$";
#endif
//Acceptance region Tangent at Origin Admission Control
#include "adc.h"
#include
#include
class ACTO_ADC : public ADC {
public:
ACTO_ADC();
void teardown_action(int,double,int);
protected:
int admit_flow(int,double,int);
int rejected_;
double s_;
};
ACTO_ADC::ACTO_ADC() : rejected_(0)
{
bind("s_", &s_);
type_ = new char[5];
strcpy(type_, "ACTO");
}
int ACTO_ADC::admit_flow(int cl,double r,int b)
{
double p=peak_rate(cl,r,b);
if (backoff_) {
if (rejected_)
return 0;
}
if (exp(p*s_)*est_[cl]->avload() <= bandwidth_) {
if (dobump_) {
est_[cl]->change_avload(p);
}
return 1;
}
else {
rejected_=1;
return 0;
}
}
void ACTO_ADC::teardown_action(int /*cl*/,double /*r*/,int /*b*/)
{
rejected_=0;
}
static class ACTO_ADCClass : public TclClass {
public :
ACTO_ADCClass() : TclClass("ADC/ACTO") {}
TclObject* create(int,const char*const*) {
return (new ACTO_ADC());
}
}class_acto_adc;
actp-adc.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) Xerox Corporation 1997. All rights reserved.
*
* License is granted to copy, to use, and to make and to use derivative
* works for research and evaluation purposes, provided that Xerox is
* acknowledged in all documentation pertaining to any such copy or
* derivative work. Xerox grants no other licenses expressed or
* implied. The Xerox trade name should not be used in any advertising
* without its written permission.
*
* XEROX CORPORATION MAKES NO REPRESENTATIONS CONCERNING EITHER THE
* MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE
* FOR ANY PARTICULAR PURPOSE. The software is provided "as is" without
* express or implied warranty of any kind.
*
* These notices must be retained in any copies of any part of this
* software.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$";
#endif
//Acceptance region Tangent at Peak Admission Control
#include "adc.h"
#include
#include
class ACTP_ADC : public ADC {
public:
ACTP_ADC();
void teardown_action(int,double,int);
void rej_action(int,double,int);
protected:
int admit_flow(int,double,int);
int rejected_;
double s_;
double sump_;
};
ACTP_ADC::ACTP_ADC() : rejected_(0), sump_(0)
{
bind("s_", &s_);
type_ = new char[5];
strcpy(type_, "ACTP");
}
int ACTP_ADC::admit_flow(int cl,double r,int b)
{
//get peak rate this class of flow
double p=peak_rate(cl,r,b);
if (backoff_) {
if (rejected_)
return 0;
}
//fprintf (stderr,"%f %f %f\n",sump_*(1-exp(-p*s_)),exp(-p*s_)*est_[cl]->avload(),est_[cl]->avload());
if (sump_*(1-exp(-p*s_))+exp(-p*s_)*est_[cl]->avload() <= bandwidth_) {
sump_+= p;
if (dobump_) {
est_[cl]->change_avload(p);
}
return 1;
}
else {
rejected_=1;
return 0;
}
}
void ACTP_ADC::rej_action(int cl,double r,int b)
{
double p=peak_rate(cl,r,b);
sump_ -= p;
}
void ACTP_ADC::teardown_action(int cl,double r,int b)
{
rejected_=0;
double p=peak_rate(cl,r,b);
sump_ -= p;
}
static class ACTP_ADCClass : public TclClass {
public:
ACTP_ADCClass() : TclClass("ADC/ACTP") {}
TclObject* create(int,const char*const*) {
return (new ACTP_ADC());
}
}class_actp_adc;
adaptive-receiver.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) Xerox Corporation 1997. All rights reserved.
*
* License is granted to copy, to use, and to make and to use derivative
* works for research and evaluation purposes, provided that Xerox is
* acknowledged in all documentation pertaining to any such copy or
* derivative work. Xerox grants no other licenses expressed or
* implied. The Xerox trade name should not be used in any advertising
* without its written permission.
*
* XEROX CORPORATION MAKES NO REPRESENTATIONS CONCERNING EITHER THE
* MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE
* FOR ANY PARTICULAR PURPOSE. The software is provided "as is" without
* express or implied warranty of any kind.
*
* These notices must be retained in any copies of any part of this
* software.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$";
#endif
#include "config.h"
#include "agent.h"
#include "tclcl.h"
#include "packet.h"
#include "ip.h"
#include "rtp.h"
#include "adaptive-receiver.h"
#include
#define myabs(r) (r<0)?-r:r
AdaptiveRcvr::AdaptiveRcvr() : Agent(PT_NTYPE)
{
//bind("npkts_",&npkts_);
//bind("ndelay_",&ndelay_);
//bind("nvar_",&nvar_);
bind("off_rtp_",&off_rtp_);
}
void AdaptiveRcvr::recv(Packet *pkt,Handler*)
{
int delay;
int seq_no;
hdr_cmn* ch= (hdr_cmn*)pkt->access(off_cmn_);
//hdr_ip* iph = (hdr_ip*)pkt->access(off_ip_);
hdr_rtp *rh=(hdr_rtp*) pkt->access(off_rtp_);
seq_no= rh->seqno();
register u_int32_t send_time = (int)ch->timestamp();
u_int32_t local_time= (u_int32_t)(Scheduler::instance().clock() * SAMPLERATE);
delay=adapt(pkt,local_time);
Tcl::instance().evalf("%s print-delay-stats %f %f %f",name(),send_time/SAMPLERATE,local_time/SAMPLERATE,(local_time+delay)/SAMPLERATE);
Packet::free(pkt);
}
adc.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) Xerox Corporation 1997. All rights reserved.
*
* License is granted to copy, to use, and to make and to use derivative
* works for research and evaluation purposes, provided that Xerox is
* acknowledged in all documentation pertaining to any such copy or
* derivative work. Xerox grants no other licenses expressed or
* implied. The Xerox trade name should not be used in any advertising
* without its written permission.
*
* XEROX CORPORATION MAKES NO REPRESENTATIONS CONCERNING EITHER THE
* MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE
* FOR ANY PARTICULAR PURPOSE. The software is provided "as is" without
* express or implied warranty of any kind.
*
* These notices must be retained in any copies of any part of this
* software.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$";
#endif
#include "adc.h"
#include
ADC::ADC() :bandwidth_(0), tchan_(0)
{
bind_bw("bandwidth_",&bandwidth_);
bind_bool("backoff_",&backoff_);
bind("src_", &src_);
bind("dst_", &dst_);
bind_bool("dobump_", &dobump_);
}
int ADC::command(int argc,const char*const*argv)
{
Tcl& tcl = Tcl::instance();
if (argc==2) {
if (strcmp(argv[1],"start") ==0) {
/* $adc start */
est_[1]->start();
return (TCL_OK);
}
} else if (argc==4) {
if (strcmp(argv[1],"attach-measmod") == 0) {
/* $adc attach-measmod $meas $cl */
MeasureMod *meas_mod = (MeasureMod *)TclObject::lookup(argv[2]);
if (meas_mod== 0) {
tcl.resultf("no measuremod found");
return(TCL_ERROR);
}
int cl=atoi(argv[3]);
est_[cl]->setmeasmod(meas_mod);
return(TCL_OK);
} else if (strcmp(argv[1],"attach-est") == 0 ) {
/* $adc attach-est $est $cl */
Estimator *est_mod = (Estimator *)TclObject::lookup(argv[2]);
if (est_mod== 0) {
tcl.resultf("no estmod found");
return(TCL_ERROR);
}
int cl=atoi(argv[3]);
setest(cl,est_mod);
return(TCL_OK);
}
}
else if (argc == 3) {
if (strcmp(argv[1], "attach") == 0) {
int mode;
const char* id = argv[2];
tchan_ = Tcl_GetChannel(tcl.interp(), (char*)id, &mode);
if (tchan_ == 0) {
tcl.resultf("ADC: trace: can't attach %s for writing", id);
return (TCL_ERROR);
}
return (TCL_OK);
}
if (strcmp(argv[1], "setbuf") == 0) {
/* some sub classes actually do something here */
return(TCL_OK);
}
}
return (NsObject::command(argc,argv));
}
address.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1990-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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*
* $Header$
*/
#include
#include
#include "address.h"
#include "route.h"
static class AddressClass : public TclClass {
public:
AddressClass() : TclClass("Address") {}
TclObject* create(int, const char*const*) {
return (new Address());
}
} class_address;
Address* Address::instance_;
Address::Address() : NodeShift_(NULL), NodeMask_(NULL), McastShift_(0),McastMask_(0), levels_(0)
{ }
Address::~Address()
{
delete [] NodeShift_;
delete [] NodeMask_;
}
int Address::command(int argc, const char*const* argv)
{
int i, c, temp=0;
Tcl& tcl = Tcl::instance();
if ((instance_ == 0) || (instance_ != this))
instance_ = this;
if (argc == 3) {
if (strcmp(argv[1], "str2addr") == 0) {
tcl.resultf("%d", str2addr(argv[2]));
return (TCL_OK);
}
}
if (argc == 4) {
// The following code is no longer supported in the
// 32-bit addressing
// if (strcmp(argv[1], "portbits-are") == 0) {
// PortShift_ = atoi(argv[2]);
// PortMask_ = atoi(argv[3]);
// return (TCL_OK);
// }
if (strcmp(argv[1], "mcastbits-are") == 0) {
McastShift_ = atoi(argv[2]);
McastMask_ = atoi(argv[3]);
return (TCL_OK);
}
}
if (argc >= 4) {
if (strcmp(argv[1], "add-hier") == 0) {
/*
* add-hier
*/
int level = atoi(argv[2]);
int mask = atoi(argv[3]);
int shift = atoi(argv[4]);
if (levels_ < level)
levels_ = level;
NodeShift_[level] = shift;
NodeMask_[level] = mask;
return (TCL_OK);
}
if (strcmp(argv[1], "idsbits-are") == 0) {
temp = (argc - 2)/2;
if (levels_) {
if (temp != levels_) {
tcl.resultf("#idshiftbits don't match with #hier levels\n");
return (TCL_ERROR);
}
}
else
levels_ = temp;
NodeShift_ = new int[levels_ + 1];
for (i = 3, c = 1; c <= levels_; c++, i+=2)
NodeShift_[c] = atoi(argv[i]);
return (TCL_OK);
}
if (strcmp(argv[1], "idmbits-are") == 0) {
temp = (argc - 2)/2;
if (levels_) {
if (temp != levels_) {
tcl.resultf("#idmaskbits don't match with #hier levels\n");
return (TCL_ERROR);
}
}
else
levels_ = temp;
NodeMask_ = new int[levels_ + 1];
for (i = 3, c = 1; c <= levels_; c++, i+=2)
NodeMask_[c] = atoi(argv[i]);
return (TCL_OK);
}
}
return TclObject::command(argc, argv);
}
char *Address::print_nodeaddr(int address)
{
int a;
char temp[SMALL_LEN];
char str[SMALL_LEN];
char *addrstr;
str[0] = '\0';
for (int i=1; i <= levels_; i++) {
a = address >> NodeShift_[i];
if (levels_ > 1)
a = a & NodeMask_[i];
//if (i < levels_)
sprintf(temp, "%d.", a);
//else
//sprintf(temp, "%d", a);
strcat(str, temp);
}
int len;
addrstr = new char[len= strlen(str)];
str[len-1]= 0; //kill the last dot
strcpy(addrstr, str);
// printf("Nodeaddr - %s\n",addrstr);
return(addrstr);
}
char *Address::get_subnetaddr(int address)
{
int a;
char temp[SMALL_LEN];
char str[SMALL_LEN];
char *addrstr;
if (levels_ > 1) {
str[0] = '\0';
for (int i=1; i < levels_; i++) {
a = address >> NodeShift_[i];
a = a & NodeMask_[i];
if (i < (levels_-1))
sprintf(temp, "%d.", a);
else
sprintf(temp, "%d", a);
strcat(str, temp);
}
addrstr = new char[strlen(str)+1];
strcpy(addrstr, str);
//printf("Subnet_addr - %s\n",addrstr);
return(addrstr);
}
return NULL;
}
// returns nodeaddr in integer form (relevant especially for hier-addr)
int Address::get_nodeaddr(int address)
{
int a;
char *temp;
temp = print_nodeaddr(address);
a = str2addr(temp);
delete [] temp;
return a;
}
//Sets address in pkthdr format (having port and node fields)
int Address::create_ipaddr(int nodeid, int portid)
{
return nodeid;
// The following code is obsolete
#if 0
int address;
if (levels_ < 2)
address = (nodeid & NodeMask_[1]) << NodeShift_[1];
else
address = nodeid;
address = ((portid & PortMask_) << PortShift_) | \
((~(PortMask_) << PortShift_) & address);
return address;
#endif
}
int Address::get_lastaddr(int address)
{
int a;
a = address >> NodeShift_[levels_];
a = a & NodeMask_[levels_];
return a;
}
char *Address::print_portaddr(int address)
{
char str[SMALL_LEN];
char *addrstr;
str[0] = '\0';
#if 0
int a;
a = address >> PortShift_;
a = a & PortMask_;
#endif
sprintf(str, "%d", address);
addrstr = new char[strlen(str)+1];
strcpy(addrstr, str);
// printf("Portaddr - %s\n",addrstr);
return(addrstr);
}
// Convert address in string format to binary format (int).
int Address::str2addr(const char *str) const
{
if (levels_ < 2)
return atoi(str);
/*
int istr[levels_], addr= 0;
*/
/*
* for VC++
*/
int *istr= new int[levels_];
int addr= 0;
RouteLogic::ns_strtok((char*)str, istr);
for (int i = 0; i < levels_; i++) {
addr = set_word_field(addr, --istr[i],
NodeShift_[i+1], NodeMask_[i+1]);
}
delete [] istr;
return addr;
}
agent.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1990-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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (LBL)";
#endif
#include
#include
#include "config.h"
#include "agent.h"
#include "ip.h"
#include "flags.h"
#include "address.h"
#include "app.h"
#ifndef min
#define min(a, b) (((a) < (b)) ? (a) : (b))
#endif
static class AgentClass : public TclClass {
public:
AgentClass() : TclClass("Agent") {}
TclObject* create(int, const char*const*) {
return (new Agent(PT_NTYPE));
}
} class_agent;
int Agent::uidcnt_; /* running unique id */
Agent::Agent(packet_t pkttype) :
size_(0), type_(pkttype),
channel_(0), traceName_(NULL),
oldValueList_(NULL), app_(0)
{
off_ip_ = hdr_ip::offset();
}
void
Agent::delay_bind_init_all()
{
delay_bind_init_one("agent_addr_");
delay_bind_init_one("agent_port_");
delay_bind_init_one("dst_addr_");
delay_bind_init_one("dst_port_");
delay_bind_init_one("fid_");
delay_bind_init_one("prio_");
delay_bind_init_one("flags_");
delay_bind_init_one("ttl_");
delay_bind_init_one("class_");
#ifdef OFF_HDR
delay_bind_init_one("off_ip_");
#endif
Connector::delay_bind_init_all();
}
int
Agent::delay_bind_dispatch(const char *varName, const char *localName, TclObject *tracer)
{
if (delay_bind(varName, localName, "agent_addr_", (int*)&(here_.addr_), tracer)) return TCL_OK;
if (delay_bind(varName, localName, "agent_port_", (int*)&(here_.port_), tracer)) return TCL_OK;
if (delay_bind(varName, localName, "dst_addr_", (int*)&(dst_.addr_), tracer)) return TCL_OK;
if (delay_bind(varName, localName, "dst_port_", (int*)&(dst_.port_), tracer)) return TCL_OK;
if (delay_bind(varName, localName, "fid_", (int*)&fid_, tracer)) return TCL_OK;
if (delay_bind(varName, localName, "prio_", (int*)&prio_, tracer)) return TCL_OK;
if (delay_bind(varName, localName, "flags_", (int*)&flags_, tracer)) return TCL_OK;
if (delay_bind(varName, localName, "ttl_", &defttl_, tracer)) return TCL_OK;
if (delay_bind(varName, localName, "off_ip_", &off_ip_, tracer)) return TCL_OK;
if (delay_bind(varName, localName, "class_", (int*)&fid_, tracer)) return TCL_OK;
return Connector::delay_bind_dispatch(varName, localName, tracer);
}
Agent::~Agent()
{
if (oldValueList_ != NULL) {
OldValue *p;
while (oldValueList_ != NULL) {
oldValueList_ = oldValueList_->next_;
delete p;
p = oldValueList_;
}
}
}
int Agent::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
if (argc == 2) {
if (strcmp(argv[1], "delete-agent-trace") == 0) {
if ((traceName_ == 0) || (channel_ == 0))
return (TCL_OK);
deleteAgentTrace();
return (TCL_OK);
} else if (strcmp(argv[1], "show-monitor") == 0) {
if ((traceName_ == 0) || (channel_ == 0))
return (TCL_OK);
monitorAgentTrace();
return (TCL_OK);
} else if (strcmp(argv[1], "close") == 0) {
close();
return (TCL_OK);
} else if (strcmp(argv[1], "listen") == 0) {
listen();
return (TCL_OK);
} else if (strcmp(argv[1], "dump-namtracedvars") == 0) {
enum_tracedVars();
return (TCL_OK);
}
}
else if (argc == 3) {
if (strcmp(argv[1], "attach") == 0) {
int mode;
const char* id = argv[2];
channel_ = Tcl_GetChannel(tcl.interp(), (char*)id, &mode);
if (channel_ == 0) {
tcl.resultf("trace: can't attach %s for writing", id);
return (TCL_ERROR);
}
return (TCL_OK);
} else if (strcmp(argv[1], "add-agent-trace") == 0) {
// we need to write nam traces and set agent trace name
if (channel_ == 0) {
tcl.resultf("agent %s: no trace file attached", name_);
return (TCL_OK);
}
addAgentTrace(argv[2]);
return (TCL_OK);
} else if (strcmp(argv[1], "connect") == 0) {
connect((nsaddr_t)atoi(argv[2]));
return (TCL_OK);
} else if (strcmp(argv[1], "send") == 0) {
sendmsg(atoi(argv[2]));
return (TCL_OK);
} else if (strcmp(argv[1], "set_pkttype") == 0) {
set_pkttype(packet_t(atoi(argv[2])));
return (TCL_OK);
}
}
else if (argc == 4) {
if (strcmp(argv[1], "sendmsg") == 0) {
sendmsg(atoi(argv[2]), argv[3]);
return (TCL_OK);
}
}
else if (argc == 5) {
if (strcmp(argv[1], "sendto") == 0) {
sendto(atoi(argv[2]), argv[3], (nsaddr_t)atoi(argv[4]));
return (TCL_OK);
}
}
if (strcmp(argv[1], "tracevar") == 0) {
// wrapper of TclObject's trace command, because some tcl
// agents (e.g. srm) uses it.
const char* args[4];
char tmp[6];
strcpy(tmp, "trace");
args[0] = argv[0];
args[1] = tmp;
args[2] = argv[2];
if (argc > 3)
args[3] = argv[3];
return (Connector::command(argc, args));
}
return (Connector::command(argc, argv));
}
void Agent::flushAVar(TracedVar *v)
{
char wrk[256], value[128];
int n;
// XXX we need to keep track of old values. What's the best way?
v->value(value, 128);
if (strcmp(value, "") == 0)
// no value, because no writes has occurred to this var
return;
sprintf(wrk, "f -t %.17f -s %d -d %d -n %s -a %s -o %s -T v -x",
Scheduler::instance().clock(), addr(), dst_.addr_,
v->name(), traceName_, value);
n = strlen(wrk);
wrk[n] = '\n';
wrk[n+1] = 0;
(void)Tcl_Write(channel_, wrk, n+1);
}
void Agent::deleteAgentTrace()
{
char wrk[256];
// XXX we don't know InstVar outside of Tcl! Is there any
// tracedvars hidden in InstVar? If so, shall we have a tclclInt.h?
TracedVar* var = tracedvar_;
for ( ; var != 0; var = var->next_)
flushAVar(var);
// we need to flush all var values to trace file,
// so nam can do backtracing
sprintf(wrk, "a -t %.17f -s %d -d %d -n %s -x",
Scheduler::instance().clock(), here_.addr_,
dst_.addr_, traceName_);
if (traceName_ != NULL)
delete[] traceName_;
traceName_ = NULL;
}
OldValue* Agent::lookupOldValue(TracedVar *v)
{
OldValue *p = oldValueList_;
while ((p != NULL) && (p->var_ != v))
p = p->next_;
return p;
}
void Agent::insertOldValue(TracedVar *v, const char *value)
{
OldValue *p = new OldValue;
assert(p != NULL);
strncpy(p->val_, value, min(strlen(value)+1, TRACEVAR_MAXVALUELENGTH));
p->var_ = v;
p->next_ = NULL;
if (oldValueList_ == NULL)
oldValueList_ = p;
else {
p->next_ = oldValueList_;
oldValueList_ = p;
}
}
// callback from traced variable updates
void Agent::trace(TracedVar* v)
{
if (channel_ == 0)
return;
char wrk[256], value[128];
int n;
// XXX we need to keep track of old values. What's the best way?
v->value(value, 128);
// XXX hack: how do I know ns has not started yet?
// if there's nothing in value, return
static int started = 0;
if (!started) {
Tcl::instance().eval("[Simulator instance] is-started");
if (Tcl::instance().result()[0] == '0')
// Simulator not started, do nothing
return;
// remember for next time (so we don't always have to call to tcl)
started = 1;
};
OldValue *ov = lookupOldValue(v);
if (ov != NULL) {
sprintf(wrk,
"f -t %.17f -s %d -d %d -n %s -a %s -v %s -o %s -T v",
Scheduler::instance().clock(), here_.addr_,
dst_.addr_, v->name(), traceName_, value, ov->val_);
strncpy(ov->val_,
value,
min(strlen(value)+1, TRACEVAR_MAXVALUELENGTH));
} else {
// if there is value, insert it into old value list
sprintf(wrk, "f -t %.17f -s %d -d %d -n %s -a %s -v %s -T v",
Scheduler::instance().clock(), here_.addr_,
dst_.addr_, v->name(), traceName_, value);
insertOldValue(v, value);
}
n = strlen(wrk);
wrk[n] = '\n';
wrk[n+1] = 0;
(void)Tcl_Write(channel_, wrk, n+1);
}
void Agent::monitorAgentTrace()
{
char wrk[256];
int n;
double curTime = (&Scheduler::instance() == NULL ? 0 :
Scheduler::instance().clock());
sprintf(wrk, "v -t %.17f monitor_agent %d %s",
curTime, here_.addr_, traceName_);
n = strlen(wrk);
wrk[n] = '\n';
wrk[n+1] = 0;
if (channel_)
(void)Tcl_Write(channel_, wrk, n+1);
}
void Agent::addAgentTrace(const char *name)
{
char wrk[256];
int n;
double curTime = (&Scheduler::instance() == NULL ? 0 :
Scheduler::instance().clock());
sprintf(wrk, "a -t %.17f -s %d -d %d -n %s",
curTime, here_.addr_, dst_.addr_, name);
n = strlen(wrk);
wrk[n] = '\n';
wrk[n+1] = 0;
if (channel_)
(void)Tcl_Write(channel_, wrk, n+1);
// keep agent trace name
if (traceName_ != NULL)
delete[] traceName_;
traceName_ = new char[strlen(name)+1];
strcpy(traceName_, name);
}
void Agent::timeout(int)
{
}
/*
* Callback to application to notify the reception of a number of bytes
*/
void Agent::recvBytes(int nbytes)
{
if (app_)
app_->recv(nbytes);
}
/*
* Callback to application to notify the termination of a connection
*/
void Agent::idle()
{
if (app_)
app_->resume();
}
/*
* Assign application pointer for callback purposes
*/
void Agent::attachApp(Application *app)
{
app_ = app;
}
void Agent::close()
{
}
void Agent::listen()
{
}
/*
* This function is a placeholder in case applications want to dynamically
* connect to agents (presently, must be done at configuration time).
*/
void Agent::connect(nsaddr_t /*dst*/)
{
/*
dst_ = dst;
*/
}
/*
* Place holders for sending application data
*/
void Agent::sendmsg(int /*sz*/, AppData* /*data*/, const char* /*flags*/)
{
fprintf(stderr,
"Agent::sendmsg(int, AppData*, const char*) not implemented\n");
abort();
}
void Agent::sendto(int /*sz*/, AppData* /*data*/, const char* /*flags*/)
{
fprintf(stderr,
"Agent::sendmsg(int, AppData*, const char*) not implemented\n");
abort();
}
void Agent::sendmsg(int /*nbytes*/, const char* /*flags*/)
{
}
/*
* This function is a placeholder in case applications want to dynamically
* connect to agents (presently, must be done at configuration time).
*/
void Agent::sendto(int /*nbytes*/, const char /*flags*/[], nsaddr_t /*dst*/)
{
/*
dst_ = dst;
sendmsg(nbytes, flags);
*/
}
void Agent::recv(Packet* p, Handler*)
{
if (app_)
app_->recv(hdr_cmn::access(p)->size());
/*
* didn't expect packet (or we're a null agent?)
*/
Packet::free(p);
}
/*
* initpkt: fill in all the generic fields of a pkt
*/
void
Agent::initpkt(Packet* p) const
{
hdr_cmn* ch = hdr_cmn::access(p);
ch->uid() = uidcnt_++;
ch->ptype() = type_;
ch->size() = size_;
ch->timestamp() = Scheduler::instance().clock();
ch->iface() = UNKN_IFACE.value(); // from packet.h (agent is local)
ch->direction() = hdr_cmn::NONE;
ch->ref_count() = 0; /* reference count */
ch->error() = 0; /* pkt not corrupt to start with */
hdr_ip* iph = hdr_ip::access(p);
iph->saddr() = here_.addr_;
iph->sport() = here_.port_;
iph->daddr() = dst_.addr_;
iph->dport() = dst_.port_;
//DEBUG
//if (dst_ != -1)
// printf("pl break\n");
iph->flowid() = fid_;
iph->prio() = prio_;
iph->ttl() = defttl_;
hdr_flags* hf = hdr_flags::access(p);
hf->ecn_capable_ = 0;
hf->ecn_ = 0;
hf->eln_ = 0;
hf->ecn_to_echo_ = 0;
hf->fs_ = 0;
hf->no_ts_ = 0;
hf->pri_ = 0;
hf->cong_action_ = 0;
}
/*
* allocate a packet and fill in all the generic fields
*/
Packet*
Agent::allocpkt() const
{
Packet* p = Packet::alloc();
initpkt(p);
return (p);
}
/* allocate a packet and fill in all the generic fields and allocate
* a buffer of n bytes for data
*/
Packet*
Agent::allocpkt(int n) const
{
Packet* p = allocpkt();
if (n > 0)
p->allocdata(n);
return(p);
}
alloc-address.cc
/* -*- 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 Research Group 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.
*/
/* functions invoked to allocate bits to the ns-address space */
#include
#include
#include "config.h"
#include
class AllocAddr : public TclObject {
public:
AllocAddr();
~AllocAddr();
int command(int argc, const char*const* argv);
protected:
void get_mask(nsmask_t *mask, int fieldsize);
void alloc(int n);
bool check_size(int n);
bool find_free(int len, int *pos);
bool test(int which);
void set_field(int offset, int len, nsmask_t *mask, int *shift);
void free_field(int len);
void mark(int i);
int size_;
int *bitmap_;
};
class AllocAddrClass : public TclClass {
public:
AllocAddrClass() : TclClass("AllocAddr") {}
TclObject* create(int, const char*const*) {
return (new AllocAddr());
}
} AllocAddr_class;
int AllocAddr::command(int argc, const char*const* argv)
{
int offset,
addrsize,
shift,
fieldlen;
nsmask_t mask;
Tcl& tcl = Tcl::instance();
if (argc == 3) {
if (strcmp(argv[1], "freebit") == 0) {
fieldlen = atoi(argv[2]);
assert(fieldlen > 0);
free_field(fieldlen);
return (TCL_OK);
}
}
else if (argc == 4) {
if (strcmp(argv[1], "setbit") == 0) {
fieldlen = atoi(argv[2]);
addrsize = atoi(argv[3]);
if (!check_size(addrsize)) {
tcl.result("setbit: Size_ increased: Reallocate bits");
return (TCL_ERROR);
}
if (!find_free(fieldlen, &offset)) {
tcl.result("setbit: no contiguous space found\n");
return (TCL_ERROR);
}
set_field(offset, fieldlen, &mask, &shift);
// TESTING
tcl.resultf("%d %d", mask, shift);
return (TCL_OK);
}
}
else if (argc == 5) {
int oldfldlen;
if (strcmp(argv[1], "expand-port") == 0) {
fieldlen = atoi(argv[2]);
addrsize = atoi(argv[3]);
oldfldlen = atoi(argv[4]);
if (!check_size(addrsize)) {
tcl.result("expand-port: Size_ increased: Reallocate bits");
return (TCL_ERROR);
}
if (!find_free(fieldlen, &offset)) {
tcl.result("expand-port: no contiguous space found\n");
return (TCL_ERROR);
}
int i, k;
for (i = offset, k = 0; k < fieldlen; k++, i--) {
bitmap_[i] = 1;
}
shift = offset - (fieldlen - 1);
get_mask(&mask, fieldlen + oldfldlen);
// TESTING
tcl.resultf("%d %d", mask, shift);
return (TCL_OK);
}
}
return TclObject::command(argc, argv);
}
AllocAddr::AllocAddr()
{
size_ = 0;
bitmap_ = 0;
}
AllocAddr::~AllocAddr()
{
delete [] bitmap_;
}
void AllocAddr::alloc(int n)
{
size_ = n;
bitmap_ = new int[n];
for (int i=0; i < n; i++)
bitmap_[i] = 0;
}
bool AllocAddr::check_size(int n)
{
if (n <= size_)
return 1;
assert (n > 0);
if (size_ == 0) {
alloc(n);
return 1;
}
if (n > size_)
return 0;
return 1;
// this check is no longer needed, as now bits are re-allocated every time
// the size changes.
// int *old = bitmap_;
// int osize = size_;
// alloc(n);
// for (int i = 0; i < osize; i++)
// bitmap_[i] = old[i];
// delete [] old;
}
void AllocAddr::mark(int i)
{
bitmap_[i] = 1;
}
void AllocAddr::get_mask(nsmask_t *mask, int fieldsize)
{
// int temp = (int)(pow(2, (double)fieldsize));
*mask = (1 << fieldsize) - 1;
}
bool AllocAddr::test(int which)
{
assert(which <= size_);
if (bitmap_[which] == 1)
return TRUE;
else
return FALSE;
}
bool AllocAddr::find_free(int len, int *pos)
{
int count = 0;
int temp = 0;
for (int i = (size_ - 1); i >= 0; i--)
if (!test(i)) {
/**** check if n contiguous bits are free ****/
temp = i;
for (int k = 0; (k < len) && (i >=0); k++, i--){
if(test(i)) {
count = 0;
break;
}
count++;
}
if (count == len) {
*pos = temp;
return 1;
}
}
return 0;
}
void AllocAddr::set_field(int offset, int len, nsmask_t *mask, int *shift)
{
int i, k;
for (k = 0, i = offset; k < len; k++, i--) {
bitmap_[i] = 1;
}
*shift = offset - (len-1);
get_mask(mask, len);
}
void AllocAddr::free_field(int len)
{
int count = 0;
for (int i = 0; i < size_; i++) {
if (test(i)) {
bitmap_[i] = 0;
count++;
}
if (count == len)
break;
}
}
antenna.cc
/* -*- 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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*/
/* Ported from CMU/Monarch's code, nov'98 -Padma.
*
antenna.cc
$Id: AllCode__.html 980 2002-12-12 09:36:58Z ffilali $
*/
#include
static class AntennaClass : public TclClass {
public:
AntennaClass() : TclClass("Antenna") {}
TclObject* create(int, const char*const*) {
return (new Antenna);
}
} class_Antenna;
Antenna::Antenna()
{
X_ = 0; Y_= 0; Z_= 0;
bind("X_", &X_);
bind("Y_", &Y_);
bind("Z_", &Z_);
}
double
Antenna::getTxGain(double /*dX*/, double /*dY*/, double /*dZ*/,
double /*lambda*/)
{
return 1.0;
}
double
Antenna::getRxGain(double /*dX*/, double /*dY*/, double /*dZ*/,
double /*lambda*/)
{
return 1.0;
}
Antenna *
Antenna::copy()
{
return this;
}
void
Antenna::release()
{
;
}
app.cc
/* -*- 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, http://daedalus.cs.berkeley.edu
*
*/
#include "app.h"
#include "agent.h"
static class ApplicationClass : public TclClass {
public:
ApplicationClass() : TclClass("Application") {}
TclObject* create(int, const char*const*) {
return (new Application);
}
} class_application;
Application::Application() : enableRecv_(0), enableResume_(0)
{
}
int Application::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
if (argc == 2) {
if (strcmp(argv[1], "start") == 0) {
// enableRecv_ only if recv() exists in Tcl
tcl.evalf("[%s info class] info instprocs", name_);
char result[1024];
sprintf(result, " %s ", tcl.result());
enableRecv_ = (strstr(result, " recv ") != 0);
enableResume_ = (strstr(result, " resume ") != 0);
start();
return (TCL_OK);
}
if (strcmp(argv[1], "stop") == 0) {
stop();
return (TCL_OK);
}
if (strcmp(argv[1], "agent") == 0) {
tcl.resultf("%s", agent_->name());
return (TCL_OK);
}
}
else if (argc == 3) {
if (strcmp(argv[1], "attach-agent") == 0) {
agent_ = (Agent*) TclObject::lookup(argv[2]);
if (agent_ == 0) {
tcl.resultf("no such agent %s", argv[2]);
return(TCL_ERROR);
}
agent_->attachApp(this);
return(TCL_OK);
}
if (strcmp(argv[1], "send") == 0) {
send(atoi(argv[2]));
return(TCL_OK);
}
}
return (Process::command(argc, argv));
}
void Application::start()
{
}
void Application::stop()
{
}
void Application::send(int nbytes)
{
agent_->sendmsg(nbytes);
}
void Application::recv(int nbytes)
{
if (! enableRecv_)
return;
Tcl& tcl = Tcl::instance();
tcl.evalf("%s recv %d", name_, nbytes);
}
void Application::resume()
{
if (! enableResume_)
return;
Tcl& tcl = Tcl::instance();
tcl.evalf("%s resume", name_);
}
arp.cc
/*-*- 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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*/
/* Ported from CMU/Monarch's code, nov'98 -Padma. */
/* arp.cc
basic arp cache and MAC addr resolution
$Id: AllCode__.html 980 2002-12-12 09:36:58Z ffilali $
Note: code in this file violates the convention that addresses of
type Af_INET stored in nsaddr_t variables are stored in 24/8 format.
Many variables in nsaddr_t's in this file store ip addrs as simple ints.
*/
#include
#include "delay.h"
//#include "debug.h"
#include "mac.h"
#include "arp.h"
#include "topography.h"
#include "cmu-trace.h"
#include "mobilenode.h"
#include "ll.h"
#include "packet.h"
#include
// #define DEBUG
static class ARPTableClass : public TclClass {
public:
ARPTableClass() : TclClass("ARPTable") {}
TclObject* create(int, const char*const* argv) {
return (new ARPTable(argv[4], argv[5]));
}
} class_arptable;
static class ARPHeaderClass : public PacketHeaderClass {
public:
ARPHeaderClass() : PacketHeaderClass("PacketHeader/ARP",
sizeof(hdr_arp)) { }
} class_arphdr;
/* ======================================================================
Address Resolution (ARP) Table
====================================================================== */
ARPTable_List ARPTable::athead_ = { 0 };
void
ARPTable::Terminate()
{
ARPEntry *ll;
for(ll = arphead_.lh_first; ll; ll = ll->arp_link_.le_next) {
if(ll->hold_) {
drop(ll->hold_, DROP_END_OF_SIMULATION);
ll->hold_ = 0;
}
}
}
ARPTable::ARPTable(const char *tclnode, const char *tclmac) : LinkDelay() {
LIST_INIT(&arphead_);
node_ = (MobileNode*) TclObject::lookup(tclnode);
assert(node_);
mac_ = (Mac*) TclObject::lookup(tclmac);
assert(mac_);
off_mac_ = hdr_mac::offset_;
bind("off_ll_", &off_ll_);
//bind("off_mac_", &off_mac_);
bind("off_arp_", &off_arp_);
LIST_INSERT_HEAD(&athead_, this, link_);
}
int
ARPTable::command(int argc, const char*const* argv)
{
if (argc == 2 && strcasecmp(argv[1], "reset") == 0)
{
Terminate();
//FALL-THROUGH to give parents a chance to reset
}
return LinkDelay::command(argc, argv);
}
int
ARPTable::arpresolve(nsaddr_t dst, Packet *p, LL *ll)
{
ARPEntry *llinfo ;
assert(initialized());
llinfo = arplookup(dst);
#ifdef DEBUG
fprintf(stderr, "%d - %s\n", node_->address(), __FUNCTION__);
#endif
if(llinfo && llinfo->up_) {
mac_->hdr_dst((char*) HDR_MAC(p), llinfo->macaddr_);
return 0;
}
if(llinfo == 0) {
/*
* Create a new ARP entry
*/
llinfo = new ARPEntry(&arphead_, dst);
}
if(llinfo->count_ >= ARP_MAX_REQUEST_COUNT) {
/*
* Because there is not necessarily a scheduled event between
* this callback and the point where the callback can return
* to this point in the code, the order of operations is very
* important here so that we don't get into an infinite loop.
* - josh
*/
Packet *t = llinfo->hold_;
llinfo->count_ = 0;
llinfo->hold_ = 0;
hdr_cmn* ch;
if(t) {
ch = HDR_CMN(t);
if (ch->xmit_failure_) {
ch->xmit_reason_ = 0;
ch->xmit_failure_(t, ch->xmit_failure_data_);
}
else {
drop(t, DROP_IFQ_ARP_FULL);
}
}
ch = HDR_CMN(p);
if (ch->xmit_failure_) {
ch->xmit_reason_ = 0;
ch->xmit_failure_(p, ch->xmit_failure_data_);
}
else {
drop(p, DROP_IFQ_ARP_FULL);
}
return EADDRNOTAVAIL;
}
llinfo->count_++;
if(llinfo->hold_)
drop(llinfo->hold_, DROP_IFQ_ARP_FULL);
llinfo->hold_ = p;
/*
* We don't have a MAC address for this node. Send an ARP Request.
*
* XXX: Do I need to worry about the case where I keep ARPing
* for the SAME destination.
*/
int src = node_->address(); // this host's IP addr
arprequest(src, dst, ll);
return EADDRNOTAVAIL;
}
ARPEntry*
ARPTable::arplookup(nsaddr_t dst)
{
ARPEntry *a;
for(a = arphead_.lh_first; a; a = a->nextarp()) {
if(a->ipaddr_ == dst)
return a;
}
return 0;
}
void
ARPTable::arprequest(nsaddr_t src, nsaddr_t dst, LL *ll)
{
Scheduler& s = Scheduler::instance();
Packet *p = Packet::alloc();
hdr_cmn *ch = HDR_CMN(p);
char *mh = (char*) HDR_MAC(p);
hdr_ll *lh = HDR_LL(p);
hdr_arp *ah = HDR_ARP(p);
ch->uid() = 0;
ch->ptype() = PT_ARP;
ch->size() = ARP_HDR_LEN;
ch->iface() = -2;
ch->error() = 0;
mac_->hdr_dst(mh, MAC_BROADCAST);
mac_->hdr_src(mh, ll->mac_->addr());
mac_->hdr_type(mh, ETHERTYPE_ARP);
lh->seqno() = 0;
lh->lltype() = LL_DATA;
ch->direction() = hdr_cmn::DOWN; // send this pkt down
ah->arp_hrd = ARPHRD_ETHER;
ah->arp_pro = ETHERTYPE_IP;
ah->arp_hln = ETHER_ADDR_LEN;
ah->arp_pln = sizeof(nsaddr_t);
ah->arp_op = ARPOP_REQUEST;
ah->arp_sha = ll->mac_->addr();
ah->arp_spa = src;
ah->arp_tha = 0; // what were're looking for
ah->arp_tpa = dst;
s.schedule(ll->downtarget_, p, delay_);
}
void
ARPTable::arpinput(Packet *p, LL *ll)
{
Scheduler& s = Scheduler::instance();
hdr_arp *ah = HDR_ARP(p);
ARPEntry *llinfo;
assert(initialized());
#ifdef DEBUG
fprintf(stderr,
"%d - %s\n\top: %x, sha: %x, tha: %x, spa: %x, tpa: %x\n",
node_->address(), __FUNCTION__, ah->arp_op,
ah->arp_sha, ah->arp_tha, ah->arp_spa, ah->arp_tpa);
#endif
if((llinfo = arplookup(ah->arp_spa)) == 0) {
/*
* Create a new ARP entry
*/
llinfo = new ARPEntry(&arphead_, ah->arp_spa);
}
assert(llinfo);
llinfo->macaddr_ = ah->arp_sha;
llinfo->up_ = 1;
/*
* Can we send whatever's being held?
*/
if(llinfo->hold_) {
hdr_cmn *ch = HDR_CMN(llinfo->hold_);
char *mh = (char*) HDR_MAC(llinfo->hold_);
hdr_ip *ih = HDR_IP(llinfo->hold_);
// XXXHACK for now:
// Future work: separate port-id from IP address ??
int dst = Address::instance().get_nodeaddr(ih->daddr());
if((ch->addr_type() == NS_AF_NONE &&
dst == ah->arp_spa) ||
(NS_AF_INET == ch->addr_type() &&
ch->next_hop() == ah->arp_spa)) {
#ifdef DEBUG
fprintf(stderr, "\tsending HELD packet.\n");
#endif
mac_->hdr_dst(mh, ah->arp_sha);
s.schedule(ll->downtarget_, llinfo->hold_, delay_);
llinfo->hold_ = 0;
}
else {
fprintf(stderr, "\tfatal ARP error...\n");
exit(1);
}
}
if(ah->arp_op == ARPOP_REQUEST &&
ah->arp_tpa == node_->address()) {
hdr_cmn *ch = HDR_CMN(p);
char *mh = (char*)HDR_MAC(p);
hdr_ll *lh = HDR_LL(p);
ch->size() = ARP_HDR_LEN;
ch->error() = 0;
ch->direction() = hdr_cmn::DOWN; // send this pkt down
mac_->hdr_dst(mh, ah->arp_sha);
mac_->hdr_src(mh, ll->mac_->addr());
mac_->hdr_type(mh, ETHERTYPE_ARP);
lh->seqno() = 0;
lh->lltype() = LL_DATA;
// ah->arp_hrd =
// ah->arp_pro =
// ah->arp_hln =
// ah->arp_pln =
ah->arp_op = ARPOP_REPLY;
ah->arp_tha = ah->arp_sha;
ah->arp_sha = ll->mac_->addr();
nsaddr_t t = ah->arp_spa;
ah->arp_spa = ah->arp_tpa;
ah->arp_tpa = t;
s.schedule(ll->downtarget_, p, delay_);
return;
}
Packet::free(p);
}
bi-connector.cc
/* -*- 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 MASH Research
* Group at the University of California Berkeley.
* 4. Neither the name of the University nor of the Research Group 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.
*/
#include "packet.h"
#include "bi-connector.h"
static class BiConnectorClass : public TclClass {
public:
BiConnectorClass() : TclClass("BiConnector") {}
TclObject* create(int, const char*const*) {
return (new BiConnector);
}
} class_biconnector;
BiConnector::BiConnector() : uptarget_(0), downtarget_(0), drop_(0)
{}
int BiConnector::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
/*XXX*/
if (argc == 2) {
if (strcmp(argv[1], "up-target") == 0) {
if (uptarget_ != 0)
tcl.result(uptarget_->name());
return (TCL_OK);
}
if (strcmp(argv[1], "down-target") == 0) {
if (downtarget_ != 0)
tcl.result(downtarget_->name());
return (TCL_OK);
}
if (strcmp(argv[1], "drop-target") == 0) {
if (drop_ != 0)
tcl.resultf("%s", drop_->name());
return (TCL_OK);
}
if (strcmp(argv[1], "isDynamic") == 0) {
return TCL_OK;
}
}
else if (argc == 3) {
TclObject *obj;
if( (obj = TclObject::lookup(argv[2])) == 0) {
fprintf(stderr, "%s lookup failed\n", argv[1]);
return TCL_ERROR;
}
if (strcmp(argv[1], "up-target") == 0) {
if (*argv[2] == '0') {
uptarget_ = 0;
return (TCL_OK);
}
uptarget_ = (NsObject*) obj;
if (uptarget_ == 0) {
tcl.resultf("no such object %s", argv[2]);
return (TCL_ERROR);
}
return (TCL_OK);
}
if (strcmp(argv[1], "down-target") == 0) {
if (*argv[2] == '0') {
downtarget_ = 0;
return (TCL_OK);
}
downtarget_ = (NsObject*) obj;
if (downtarget_ == 0) {
tcl.resultf("no such object %s", argv[2]);
return (TCL_ERROR);
}
return (TCL_OK);
}
if (strcmp(argv[1], "drop-target") == 0) {
drop_ = (NsObject*) obj;
if (drop_ == 0) {
tcl.resultf("no object %s", argv[2]);
return (TCL_ERROR);
}
return (TCL_OK);
}
}
return (NsObject::command(argc, argv));
}
void BiConnector::recv(Packet* p, Handler* h)
{
hdr_cmn *ch = HDR_CMN(p);
switch (ch->direction()) {
case hdr_cmn::UP :
sendUp(p, h);
break;
case hdr_cmn::DOWN :
sendDown(p, h);
break;
default:
printf("Error: Packet Direction not specified; Using default 'UP' direction\n\n");
sendUp(p, h);
}
}
void BiConnector::drop(Packet* p)
{
if (drop_ != 0)
drop_->recv(p);
else
Packet::free(p);
}
void BiConnector::drop(Packet* p, const char *s)
{
if (drop_ != 0)
drop_->recv(p, s);
else
Packet::free(p);
}
cbq.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1997 The 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 Network Research
* Group at Lawrence Berkeley National Laboratory.
* 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.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (LBL)";
#endif
//
// new version of cbq using the ns-2 fine-grain
// objects. Also, re-orginaize CBQ to look more like how
// its description reads in ToN v3n4 and simplify extraneous stuff -KF
//
// there is a 1-1 relationship between classes and queues, except
// that internal nodes in the LS tree don't have queues
//
// Definitions:
// overlimit:
// recently used more than allocated link-sharing bandwidth
// (in bytes/sec averaged over specified interval)
//
// level:
// all leaves are at level 1
// interior nodes are at a level 1 greater than
// the highest level number of any of its children
//
// unsatisfied:
// (leaf): underlimit and has demand
// (interior): underlimit and has some descendant w/demand
// [either a leaf or interior descendant]
//
// formal link-sharing:
// class may continue unregulated if either:
// 1> class is underlimit or at-limit
// 2> class has a under(at)-limit ancestor at level i
// and no unsatisfied classes at any levels < i
//
// ancestors-only link-sharing:
// class may continue unregulated if either:
// 1> class is under/at limit
// 2> class has an UNDER-limit ancestor [at-limit not ok]
//
// top-level link-sharing:
// class may continue unregulated if either:
// 1> class is under/at limit
// 2> class has an UNDER-limit ancestor with level
// <= the value of "top-level"
#include "queue-monitor.h"
#include "queue.h"
#include "delay.h"
#define MAXPRIO 10 /* # priorities in scheduler */
#define MAXLEVEL 32 /* max depth of link-share tree(s) */
#define LEAF_LEVEL 1 /* level# for leaves */
#define POWEROFTWO 16
class CBQClass : public Connector {
public:
friend class CBQueue;
friend class WRR_CBQueue;
CBQClass();
int command(int argc, const char*const* argv);
void recv(Packet*, Handler*); // from upstream classifier
protected:
void newallot(double); // change an allotment
void update(Packet*, double); // update when sending pkt
void delayed(double); // when overlim/can't borrow
int satisfied(double); // satisfied?
int demand(); // do I have demand?
int leaf(); // am I a leaf class?
int ancestor(CBQClass*p); // are we an ancestor of p?
int desc_with_demand(); // any desc has demand?
CBQueue* cbq_; // the CBQueue I'm part of
CBQClass* peer_; // peer at same sched prio level
CBQClass* level_peer_; // peer at same LS level
CBQClass* lender_; // parent I can borrow from
Queue* q_; // underlying queue
QueueMonitor* qmon_; // monitor for the queue
double allotment_; // frac of link bw
double maxidle_; // bound on idle time
double maxrate_; // bound on bytes/sec rate
double extradelay_; // adjustment to delay
double last_time_; // last xmit time this class
double undertime_; // will become unsat/eligible
double avgidle_; // EWMA of idle
int pri_; // priority for scheduler
int level_; // depth in link-sharing tree
int delayed_; // boolean-was I delayed
int bytes_alloc_; // for wrr only
int permit_borrowing_; // ok to borrow?
};
class CBQueue : public Queue {
public:
CBQueue();
void reset();
void enque(Packet*) { abort(); }
void recv(Packet*, Handler*);
LinkDelay* link() const { return (link_); }
CBQClass* level(int n) const { return levels_[n]; }
Packet* deque();
virtual int command(int argc, const char*const* argv);
virtual void addallot(int, double) { }
Packet* pending_pkt() const { return (pending_pkt_); }
void sched();
int toplevel() { // are we using toplevel?
// return (eligible_ == &eligible_toplevel);
return (eligible_ == TOPLEVEL);
}
void toplevel_arrival(CBQClass*, double);
protected:
Event intr_;
int algorithm(const char *);
virtual int insert_class(CBQClass*);
int send_permitted(CBQClass*, double);
CBQClass* find_lender(CBQClass*, double);
void toplevel_departure(CBQClass*, double);
CBQClass* last_lender_;
Packet* pending_pkt_; // queued packet
LinkDelay* link_; // managed link
CBQClass* active_[MAXPRIO]; // classes at prio of index
CBQClass* levels_[MAXLEVEL+1]; // LL of classes per level
int maxprio_; // highest prio# seen
int maxpkt_; // largest pkt (used by WRR)
int maxlevel_; // highest level# seen
int toplevel_; // for top-level LS
// typedef int (CBQueue::*eligible_type_)(CBQClass*, double);
// eligible_type_ eligible_; // eligible function
enum eligible_type_ { NONE, FORMAL, ANCESTORS, TOPLEVEL };
eligible_type_ eligible_;
int eligible_formal(CBQClass*, double);
int eligible_ancestors(CBQClass*, double) { return (1); }
int eligible_toplevel(CBQClass* cl, double) {
return(cl->level_ <= toplevel_);
}
};
static class CBQQueueClass : public TclClass {
public:
CBQQueueClass() : TclClass("Queue/CBQ") { }
TclObject* create(int, const char*const*) {
return (new CBQueue);
}
} class_cbq;
static class CBQClassClass : public TclClass {
public:
CBQClassClass() : TclClass("CBQClass") { }
TclObject* create(int, const char*const*) {
return (new CBQClass);
}
} class_cbqclass;
CBQueue::CBQueue() : last_lender_(NULL), pending_pkt_(NULL), link_(NULL),
maxprio_(-1), maxpkt_(-1), maxlevel_(-1), toplevel_(MAXLEVEL),
// eligible_((eligible_type_)NULL)
eligible_(NONE)
{
bind("maxpkt_", &maxpkt_);
memset(active_, '\0', sizeof(active_));
memset(levels_, '\0', sizeof(levels_));
}
/*
* schedule ourselves, used by CBQClass::recv
*/
void
CBQueue::sched()
{
Scheduler& s = Scheduler::instance();
blocked_ = 1;
s.schedule(&qh_, &intr_, 0);
}
/*
* invoked by passing a packet from one of our managed queues
* basically provides a queue of one packet
*/
void
CBQueue::recv(Packet* p, Handler*)
{
if (pending_pkt_ != NULL)
abort();
blocked_ = 1;
pending_pkt_ = p;
}
void
CBQueue::reset()
{
// don't do anything
// in particular, don't let Queue::reset() call
// our deque() method
}
int
CBQueue::algorithm(const char *arg)
{
if (*arg == '0' || (strcmp(arg, "ancestor-only") == 0)) {
// eligible_ = &eligible_ancestors;
eligible_ = ANCESTORS;
return (1);
} else if (*arg == '1' || (strcmp(arg, "top-level") == 0)) {
// eligible_ = &eligible_toplevel;
eligible_ = TOPLEVEL;
return (1);
} else if (*arg == '2' || (strcmp(arg, "formal") == 0)) {
// eligible_ = &eligible_formal;
eligible_ = FORMAL;
return (1);
} else if (*arg == '3' || (strcmp(arg, "old-formal") == 0)) {
fprintf(stderr, "CBQ: old-formal LS not supported\n");
return (-1);
}
return (-1);
}
/*
*
* toplevel_arrival: called only using TL link sharing on arrival
* toplevel_departure: called only using TL link sharing on departure
*/
void
CBQueue::toplevel_departure(CBQClass *cl, double now)
{
if (toplevel_ >= last_lender_->level_) {
if ((cl->qmon_->pkts() <= 1) ||
last_lender_->undertime_ > now) {
toplevel_ = MAXLEVEL;
} else {
toplevel_ = last_lender_->level_;
}
}
}
void
CBQueue::toplevel_arrival(CBQClass *cl, double now)
{
if (toplevel_ > 1) {
if (cl->undertime_ < now)
toplevel_ = 1;
else if (toplevel_ > 2 && cl->permit_borrowing_ && cl->lender_ != NULL) {
if (cl->lender_->undertime_ < now)
toplevel_ = 2;
}
}
}
/*
* deque: this gets invoked by way of our downstream
* (i.e. linkdelay) neighbor doing a 'resume' on us
* via our handler (by Queue::resume()), or by our upstream
* neighbor when it gives us a packet when we were
* idle
*/
Packet *
CBQueue::deque()
{
Scheduler& s = Scheduler::instance();
double now = s.clock();
CBQClass* first = NULL;
CBQClass* eligible = NULL;
CBQClass* cl;
register int prio;
Packet* rval;
int none_found = 0;
/*
* prio runs from 0 .. maxprio_
*
* round-robin through all the classes at priority 'prio'
* if any class is ok to send, resume it's queue
* go on to next lowest priority (higher prio nuber) and repeat
* [lowest priority number is the highest priority]
*/
for (prio = 0; prio <= maxprio_; prio++) {
// see if there is any class at this prio
if ((cl = active_[prio]) == NULL) {
// nobody at this prio level
continue;
}
// look for underlimit peer with something to send
do {
// anything to send?
if (cl->demand()) {
if (first == NULL && cl->permit_borrowing_ && cl->lender_ != NULL)
first = cl;
if (send_permitted(cl, now)) {
// ok to send
eligible = cl;
goto found;
} else {
// not ok right now
cl->delayed(now);
}
}
cl = cl->peer_; // move to next at same prio
} while (cl != active_[prio]);
}
// did not find anyone so let first go
// eligible will be NULL at this point
if (first != NULL) {
none_found = 1;
eligible = first;
}
found:
if (eligible != NULL) {
active_[eligible->pri_] = eligible->peer_;
// eligible->q_->unblock();
eligible->q_->resume(); // fills in pending
if (pending_pkt_ && !none_found) {
eligible->update(pending_pkt_, now);
if (toplevel())
toplevel_departure(eligible, now);
}
}
rval = pending_pkt_;
pending_pkt_ = NULL;
return (rval);
}
/*
* we are permitted to send if either
* 1> we are not overlimit (ie we are underlimit or at limit)
* 2> one of the varios algorithm-dependent conditions is met
*
* if we are permitted, who did we borrow from? [could be ourselves
* if we were not overlimit]
*/
int CBQueue::send_permitted(CBQClass* cl, double now)
{
if (cl->undertime_ < now) {
cl->delayed_ = 0;
last_lender_ = cl;
return (1);
} else if (cl->permit_borrowing_ &&
(((cl = find_lender(cl, now)) != NULL))) {
last_lender_ = cl;
return (1);
}
return (0);
}
/*
* find_lender(class, time)
*
* find a lender for the provided class according to the
* various algorithms
*
*/
CBQClass*
CBQueue::find_lender(CBQClass* cl, double now)
{
if ((!cl->permit_borrowing_) || ((cl = cl->lender_) == NULL))
return (NULL); // no ancestor to borrow from
while (cl != NULL) {
// skip past overlimit ancestors
// if using TL and we're above the TL limit
// do early out
if (cl->undertime_ > now) {
if (toplevel() && cl->level_ > toplevel_)
return (NULL);
cl = cl->lender_;
continue;
}
// found what may be an eligible
// lender, check using per-algorithm eligibility
// criteria
// XXX we explicitly invoke this indirect method with
// the "this" pointer because MS VC++ can't parse it
// without it...
// if ((this->*eligible_)(cl, now))
// return (cl);
switch (eligible_) {
case TOPLEVEL:
if (eligible_toplevel(cl, now))
return (cl);
break;
case ANCESTORS:
if (eligible_ancestors(cl, now))
return (cl);
break;
case FORMAL:
if (eligible_formal(cl, now))
return (cl);
break;
default:
fprintf(stderr, "Wrong eligible_\n");
abort();
}
cl = cl->lender_;
}
return (cl);
}
/*
* rule #2 for formal link-sharing
* class must have no unsatisfied classes below it
*/
int
CBQueue::eligible_formal(CBQClass *cl, double now)
{
int level;
CBQClass *p;
// check from leaf level to (cl->level - 1)
for (level = LEAF_LEVEL; level < cl->level_; level++) {
p = levels_[level];
while (p != NULL) {
if (!p->satisfied(now))
return (0);
p = p->level_peer_;
}
}
return (1);
}
/*
* insert a class into the cbq object
*/
int
CBQueue::insert_class(CBQClass *p)
{
p->cbq_ = this;
/*
* Add to circularly-linked list "active_"
* of peers for the given priority.
*/
if (p->pri_ < 0 || p->pri_ > (MAXPRIO-1)) {
fprintf(stderr, "CBQ class %s has invalid pri %d\n",
p->name(), p->pri_);
return (-1);
}
if (p->q_ != NULL) {
// only leaf nodes (which have associated queues)
// are scheduled
if (active_[p->pri_] != NULL) {
p->peer_ = active_[p->pri_]->peer_;
active_[p->pri_]->peer_ = p;
} else {
p->peer_ = p;
active_[p->pri_] = p;
}
if (p->pri_ > maxprio_)
maxprio_ = p->pri_;
}
/*
* Compute maxrate from allotment.
* convert to bytes/sec
* and store the highest prio# we've seen
*/
if (p->allotment_ < 0.0 || p->allotment_ > 1.0) {
fprintf(stderr, "CBQ class %s has invalid allot %f\n",
p->name(), p->allotment_);
return (-1);
}
if (link_ == NULL) {
fprintf(stderr, "CBQ obj %s has no link!\n", name());
return (-1);
}
if (link_->bandwidth() <= 0.0) {
fprintf(stderr, "CBQ obj %s has invalid link bw %f on link %s\n",
name(), link_->bandwidth(), link_->name());
return (-1);
}
p->maxrate_ = p->allotment_ * (link_->bandwidth() / 8.0);
addallot(p->pri_, p->allotment_);
/*
* Add to per-level list
* and store the highest level# we've seen
*/
if (p->level_ <= 0 || p->level_ > MAXLEVEL) {
fprintf(stderr, "CBQ class %s has invalid level %d\n",
p->name(), p->level_);
return (-1);
}
p->level_peer_ = levels_[p->level_];
levels_[p->level_] = p;
if (p->level_ > maxlevel_)
maxlevel_ = p->level_;
/*
* Check that parent and borrow linkage are acyclic.
*/
#ifdef notdef
check_for_cycles(CBQClass::parent);
check_for_cycles(CBQClass::borrow);
#endif
return 0;
}
int CBQueue::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
if (argc == 3) {
if (strcmp(argv[1], "insert-class") == 0) {
CBQClass *cl = (CBQClass*)TclObject::lookup(argv[2]);
if (cl == 0) {
tcl.resultf("CBQ: no class object %s",
argv[2]);
return (TCL_ERROR);
}
if (insert_class(cl) < 0) {
tcl.resultf("CBQ: trouble inserting class %s",
argv[2]);
return (TCL_ERROR);
}
return (TCL_OK);
}
if (strcmp(argv[1], "link") == 0) {
LinkDelay* del = (LinkDelay*)TclObject::lookup(argv[2]);
if (del == 0) {
tcl.resultf("CBQ: no LinkDelay object %s",
argv[2]);
return(TCL_ERROR);
}
link_ = del;
return (TCL_OK);
}
if (strcmp(argv[1], "algorithm") == 0) {
if (algorithm(argv[2]) < 0)
return (TCL_ERROR);
return (TCL_OK);
}
}
return (Queue::command(argc, argv));
}
class WRR_CBQueue : public CBQueue {
public:
WRR_CBQueue() {
memset(M_, '\0', sizeof(M_));
memset(alloc_, '\0', sizeof(alloc_));
memset(cnt_, '\0', sizeof(cnt_));
}
void addallot(int prio, double diff) {
alloc_[prio] += diff;
setM();
}
protected:
Packet *deque();
int insert_class(CBQClass*);
void setM();
double alloc_[MAXPRIO];
double M_[MAXPRIO];
int cnt_[MAXPRIO]; // # classes at prio of index
int command(int argc, const char*const* argv);
};
static class WRR_CBQQueueClass : public TclClass {
public:
WRR_CBQQueueClass() : TclClass("Queue/CBQ/WRR") { }
TclObject* create(int, const char*const*) {
return (new WRR_CBQueue);
}
} class_wrr_cbq;
int WRR_CBQueue::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
if (strcmp(argv[1], "insert-class") == 0) {
CBQClass *cl = (CBQClass*)TclObject::lookup(argv[2]);
if (cl == 0) {
tcl.resultf("WRR-CBQ: no class object %s",
argv[2]);
return (TCL_ERROR);
}
if (insert_class(cl) < 0) {
tcl.resultf("WRR-CBQ: trouble inserting class %s",
argv[2]);
return (TCL_ERROR);
}
return (TCL_OK);
}
return (CBQueue::command(argc, argv));
}
Packet *
WRR_CBQueue::deque()
{
double now = Scheduler::instance().clock();
CBQClass* first = NULL;
CBQClass* eligible = NULL;
CBQClass* next_eligible = NULL;
CBQClass* cl;
register int prio;
int deficit, done;
int none_found = 0;
Packet* rval;
/*
* prio runs from 0 .. maxprio_
*
* round-robin through all the classes at priority 'prio'
* if any class is ok to send, resume it's queue
* go on to next lowest priority (higher prio nuber) and repeat
* [lowest priority number is the highest priority]
*/
for (prio = 0; prio <= maxprio_; prio++) {
// see if there is any class at this prio
if ((cl = active_[prio]) == NULL) {
// nobody at this prio level
continue;
}
/*
* The WRR round for this priority level starts at deficit 0.
* The round ends if some class is found that is ready
* to send and has positive "bytes_alloc_".
* Status advances to deficit 1 if some class was found
* that was able to send except for insufficient
* "bytes_alloc_".
* If status was deficit 1 at the end of the first round,
* then status advances to deficit 2.
* Another round of WRR is then begun at deficit 2, looking
* for a class to send even with insufficient "bytes_alloc_".
*/
deficit = done = 0;
while (!done) {
// look for class at this priority level ok to send
do {
// set up "weight" for WRR
if (deficit < 2 && cl->bytes_alloc_ <= 0)
cl->bytes_alloc_ +=
(int)(cl->allotment_ * M_[cl->pri_]);
// anything to send?
if (cl->demand()) {
if (first == NULL && cl->permit_borrowing_ && cl->lender_ != NULL)
first = cl;
if (!send_permitted(cl, now)) {
// not ok to send right now
cl->delayed(now);
} else {
// ok to send, if class has
// enough "weight" for WRR.
int bytes = cl->bytes_alloc_;
if (bytes > 0 || deficit > 1) {
eligible = cl;
goto found;
} else
deficit = 1;
}
}
cl->bytes_alloc_ = 0;
cl = cl->peer_;
} while (cl != active_[prio] && cl != 0);
if (deficit == 1)
deficit = 2;
else
done = 1;
}
}
// did not find anyone so let first go
if ((eligible == NULL) && first != NULL) {
none_found = 1;
eligible = first;
}
found:
// do accounting
if (eligible != NULL) {
next_eligible = eligible->peer_;
eligible->q_->resume();
if (pending_pkt_ != NULL && !none_found) {
// reduce our alloc
// by the packet size. If we're
// still positive, we get to go again
int bytes = eligible->bytes_alloc_;
hdr_cmn* hdr = (hdr_cmn*)pending_pkt_->access(off_cmn_);
if (bytes > 0) {
eligible->bytes_alloc_ -= hdr->size();
}
bytes = eligible->bytes_alloc_;
if (bytes > 0) {
next_eligible = eligible;
}
eligible->update(pending_pkt_, now);
if (toplevel())
toplevel_departure(eligible, now);
}
active_[eligible->pri_] = next_eligible;
}
rval = pending_pkt_;
pending_pkt_ = NULL;
return (rval);
}
int
WRR_CBQueue::insert_class(CBQClass *p)
{
if (CBQueue::insert_class(p) < 0)
return (-1);
++cnt_[p->pri_];
setM();
return (0);
}
void
WRR_CBQueue::setM()
{
int i;
for (i = 0; i <= maxprio_; i++) {
if (alloc_[i] > 0.0)
// allocate "cnt_[i] * maxpkt_" bytes to each
// priority level:
M_[i] = cnt_[i] * maxpkt_ * 1.0 / alloc_[i];
// allocate "alloc_[i] * 2.0 * cnt_[i] * maxpkt_"
// bytes to each priority level:
// M_[i] = 2.0 * cnt_[i] * maxpkt_;
else
M_[i] = 0.0;
if (M_[i] < 0.0) {
fprintf(stderr, "M_[i]: %f, cnt_[i]: %d, maxpkt_: %d, alloc_[i]: %f\n",
M_[i], cnt_[i], maxpkt_, alloc_[i]);
abort();
}
}
return;
}
/******************** CBQClass definitions **********************/
CBQClass::CBQClass() : cbq_(0), peer_(0), level_peer_(0), lender_(0),
q_(0), qmon_(0), allotment_(0.0), maxidle_(-1.0), maxrate_(0.0),
extradelay_(0.0), last_time_(0.0), undertime_(0.0), avgidle_(0.0),
pri_(-1), level_(-1), delayed_(0), bytes_alloc_(0),
permit_borrowing_(1)
{
/* maxidle_ is no longer bound; it is now a method interface */
bind("priority_", &pri_);
bind("level_", &level_);
bind("extradelay_", &extradelay_);
bind_bool("okborrow_", &permit_borrowing_);
if (pri_ < 0 || pri_ > (MAXPRIO-1))
abort();
if (level_ <= 0 || level_ > MAXLEVEL)
abort();
}
// why can't these two be inline (?)
int
CBQClass::demand()
{
return (qmon_->pkts() > 0);
}
int
CBQClass::leaf()
{
return (level_ == LEAF_LEVEL);
}
/*
* we are upstream from the queue
* the queue should be unblocked if the downstream
* cbq is not busy and blocked otherwise
*
* we get our packet from the classifier, because of
* this the handler is NULL. Besides the queue downstream
* from us (Queue::recv) ignores the handler anyhow
*
*/
void
CBQClass::recv(Packet *pkt, Handler *h)
{
if (cbq_->toplevel()) {
Scheduler* s;
if ((s = &Scheduler::instance()) != NULL)
cbq_->toplevel_arrival(this, s->clock());
}
send(pkt, h); // queue packet downstream
if (!cbq_->blocked()) {
cbq_->sched();
}
return;
}
/*
* update a class' statistics and all parent classes
* up to the root
*/
void CBQClass::update(Packet* p, double now)
{
double idle, avgidle;
hdr_cmn* hdr = (hdr_cmn*)p->access(off_cmn_);
int pktsize = hdr->size();
double tx_time = cbq_->link()->txtime(p);
double fin_time = now + tx_time;
idle = (fin_time - last_time_) - (pktsize / maxrate_);
avgidle = avgidle_;
avgidle += (idle - avgidle) / POWEROFTWO;
if (maxidle_ < 0) {
fprintf(stderr,
"CBQClass: warning: maxidle_ not configured!\n");
} else if (avgidle > maxidle_)
avgidle = maxidle_;
avgidle_ = avgidle;
if (avgidle <= 0) {
undertime_ = fin_time + tx_time *
(1.0 / allotment_ - 1.0);
undertime_ += (1-POWEROFTWO) * avgidle;
}
last_time_ = fin_time;
// tail-recurse up to root of tree performing updates
if (lender_)
lender_->update(p, now);
return;
}
/*
* satisfied: is this class satisfied?
*/
int
CBQClass::satisfied(double now)
{
if (leaf()) {
/* leaf is unsat if underlimit with backlog */
if (undertime_ < now && demand())
return (0);
else
return (1);
}
if (undertime_ < now && desc_with_demand())
return (0);
return (1);
}
/*
* desc_with_demand: is there a descendant of this class with demand
* really, is there a leaf which is a descendant of me with
* a backlog
*/
int
CBQClass::desc_with_demand()
{
CBQClass *p = cbq_->level(LEAF_LEVEL);
for (; p != NULL; p = p->level_peer_) {
if (p->demand() && ancestor(p))
return (1);
}
return (0);
}
/*
* happens when a class is unable to send because it
* is being regulated
*/
void CBQClass::delayed(double now)
{
double delay = undertime_ - now + extradelay_;
if (delay > 0 && !delayed_) {
undertime_ += extradelay_;
undertime_ -= (1-POWEROFTWO) * avgidle_;
delayed_ = 1;
}
}
/*
* return 1 if we are an ancestor of p, 0 otherwise
*/
int
CBQClass::ancestor(CBQClass *p)
{
if (!p->permit_borrowing_ || p->lender_ == NULL)
return (0);
else if (p->lender_ == this)
return (1);
return (ancestor(p->lender_));
}
/*
* change an allotment
*/
void
CBQClass::newallot(double bw)
{
if (allotment_ < 0)
allotment_ = 0;
if (bw < 0)
bw = 0;
maxrate_ = bw * ( cbq_->link()->bandwidth() / 8.0 );
double diff = bw - allotment_;
allotment_ = bw;
cbq_->addallot(pri_, diff);
return;
}
/*
* OTcl Interface
*/
/*
* $class1 parent $class2
* $class1 borrow $class2
* $class1 qdisc $queue
* $class1 allot
* $class1 allot new-bw
*/
int CBQClass::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
if (argc == 2) {
if (strcmp(argv[1], "allot") == 0) {
tcl.resultf("%g", allotment_);
return (TCL_OK);
}
if (strcmp(argv[1], "cbq") == 0) {
if (cbq_ != NULL)
tcl.resultf("%s", cbq_->name());
else
tcl.resultf("");
return(TCL_OK);
}
if (strcmp(argv[1], "qdisc") == 0) {
if (q_ != NULL)
tcl.resultf("%s", q_->name());
else
tcl.resultf("");
return (TCL_OK);
}
if (strcmp(argv[1], "qmon") == 0) {
if (qmon_ != NULL)
tcl.resultf("%s", qmon_->name());
else
tcl.resultf("");
return (TCL_OK);
}
} else if (argc == 3) {
// for now these are the same
if ((strcmp(argv[1], "parent") == 0)) {
if (strcmp(argv[2], "none") == 0) {
lender_ = NULL;
return (TCL_OK);
}
lender_ = (CBQClass*)TclObject::lookup(argv[2]);
if (lender_ != NULL)
return (TCL_OK);
return (TCL_ERROR);
}
if (strcmp(argv[1], "qdisc") == 0) {
q_ = (Queue*) TclObject::lookup(argv[2]);
if (q_ != NULL)
return (TCL_OK);
tcl.resultf("couldn't find object %s",
argv[2]);
return (TCL_ERROR);
}
if (strcmp(argv[1], "qmon") == 0) {
qmon_ = (QueueMonitor*) TclObject::lookup(argv[2]);
if (qmon_ != NULL)
return (TCL_OK);
return (TCL_ERROR);
}
if (strcmp(argv[1], "allot") == 0) {
double bw = atof(argv[2]);
if (bw < 0.0)
return (TCL_ERROR);
if (allotment_ != 0.0) {
tcl.resultf(" class %s already has allotment of %f!",
name(), allotment_);
return (TCL_ERROR);
}
allotment_ = bw;
return (TCL_OK);
}
if (strcmp(argv[1], "newallot") == 0) {
double bw = atof(argv[2]);
if (bw < 0.0)
return (TCL_ERROR);
newallot(bw);
return (TCL_OK);
}
if (strcmp(argv[1], "maxidle") == 0) {
double m = atof(argv[2]);
if (m < 0.0) {
tcl.resultf("invalid maxidle value %s (must be non-negative)",
argv[2]);
return (TCL_ERROR);
}
maxidle_ = m;
return (TCL_OK);
}
}
return (Connector::command(argc, argv));
}
cbr_traffic.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) Xerox Corporation 1997. All rights reserved.
*
* License is granted to copy, to use, and to make and to use derivative
* works for research and evaluation purposes, provided that Xerox is
* acknowledged in all documentation pertaining to any such copy or derivative
* work. Xerox grants no other licenses expressed or implied. The Xerox trade
* name should not be used in any advertising without its written permission.
*
* XEROX CORPORATION MAKES NO REPRESENTATIONS CONCERNING EITHER THE
* MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE
* FOR ANY PARTICULAR PURPOSE. The software is provided "as is" without
* express or implied warranty of any kind.
*
* These notices must be retained in any copies of any part of this software.
*/
#include
#include "random.h"
#include "trafgen.h"
#include "ranvar.h"
/*
* Constant bit rate traffic source. Parameterized by interval, (optional)
* random noise in the interval, and packet size.
*/
class CBR_Traffic : public TrafficGenerator {
public:
CBR_Traffic();
virtual double next_interval(int&);
//HACK so that udp agent knows interpacket arrival time within a burst
inline double interval() { return (interval_); }
protected:
virtual void start();
void init();
double rate_; /* send rate during on time (bps) */
double interval_; /* packet inter-arrival time during burst (sec) */
double random_;
int seqno_;
int maxpkts_;
};
static class CBRTrafficClass : public TclClass {
public:
CBRTrafficClass() : TclClass("Application/Traffic/CBR") {}
TclObject* create(int, const char*const*) {
return (new CBR_Traffic());
}
} class_cbr_traffic;
CBR_Traffic::CBR_Traffic() : seqno_(0)
{
bind_bw("rate_", &rate_);
bind("random_", &random_);
bind("packetSize_", &size_);
bind("maxpkts_", &maxpkts_);
}
void CBR_Traffic::init()
{
// compute inter-packet interval
interval_ = (double)(size_ << 3)/(double)rate_;
if (agent_)
agent_->set_pkttype(PT_CBR);
}
void CBR_Traffic::start()
{
init();
running_ = 1;
timeout();
}
double CBR_Traffic::next_interval(int& size)
{
// Recompute interval in case rate_ or size_ has changes
interval_ = (double)(size_ << 3)/(double)rate_;
double t = interval_;
if (random_)
t += interval_ * Random::uniform(-0.5, 0.5);
size = size_;
if (++seqno_ < maxpkts_)
return(t);
else
return(-1);
}
channel.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1996 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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory and the Daedalus
* research group at UC 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 Giao Nguyen, http://daedalus.cs.berkeley.edu/~gnguyen
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (UCB)";
#endif
//#include "template.h"
#include
#include "trace.h"
#include "delay.h"
#include "object.h"
#include "packet.h"
#include "mac.h"
#include "channel.h"
#include "list.h"
#include "phy.h"
#include "wireless-phy.h"
#include "mobilenode.h"
#include "ip.h"
#include "dsr/hdr_sr.h"
#include "gridkeeper.h"
static class ChannelClass : public TclClass {
public:
ChannelClass() : TclClass("Channel") {}
TclObject* create(int, const char*const*) {
return (new Channel);
}
} class_channel;
/*static class DuplexChannelClass : public TclClass {
public:
DuplexChannelClass() : TclClass("Channel/Duplex") {}
TclObject* create(int, const char*const*) {
return (new DuplexChannel);
}
} class_channel_duplex; */
static class WirelessChannelClass : public TclClass {
public:
WirelessChannelClass() : TclClass("Channel/WirelessChannel") {}
TclObject* create(int, const char*const*) {
return (new WirelessChannel);
}
} class_Wireless_channel;
/* ==================================================================
NS Initialization Functions
=================================================================*/
static int ChannelIndex = 0;
Channel::Channel() : TclObject()
{
index_ = ChannelIndex++;
LIST_INIT(&ifhead_);
bind_time("delay_", &delay_);
}
int Channel::command(int argc, const char*const* argv)
{
if (argc == 3) {
TclObject *obj;
if( (obj = TclObject::lookup(argv[2])) == 0) {
fprintf(stderr, "%s lookup failed\n", argv[1]);
return TCL_ERROR;
}
if (strcmp(argv[1], "trace-target") == 0) {
trace_ = (Trace*) obj;
return (TCL_OK);
}
else if(strcmp(argv[1], "addif") == 0) {
((Phy*) obj)->insertchnl(&ifhead_);
((Phy*) obj)->setchnl(this);
return TCL_OK;
}
// add interface for grid_keeper_
/*else if(strncasecmp(argv[1], "grid_keeper", 5) == 0) {
grid_keeper_ = (GridKeeper*)obj;
return TCL_OK;
}*/
} else if (argc == 2) {
Tcl& tcl = Tcl::instance();
if (strcmp(argv[1], "trace-target") == 0) {
tcl.resultf("%s", trace_->name());
return (TCL_OK);
}
else if(strcmp(argv[1], "id") == 0) {
tcl.resultf("%d", index_);
return TCL_OK;
}
}
return TclObject::command(argc, argv);
}
void Channel::recv(Packet* p, Handler* h)
{
// Scheduler& s = Scheduler::instance();
// hdr_cmn::access(p)->direction() = hdr_cmn::UP;
// s.schedule(target_, p, txstop_ + delay_ - s.clock());
sendUp(p, (Phy*)h);
}
void
Channel::sendUp(Packet* p, Phy *tifp)
{
Scheduler &s = Scheduler::instance();
Phy *rifp = ifhead_.lh_first;
Node *tnode = tifp->node();
Node *rnode = 0;
Packet *newp;
double propdelay = 0.0;
struct hdr_cmn *hdr = HDR_CMN(p);
hdr->direction() = hdr_cmn::UP;
if (GridKeeper::instance()) {
int i;
GridKeeper* gk = GridKeeper::instance();
int size = gk->size_;
MobileNode **outlist = new MobileNode *[size];
// memset(outlist, 0, size * sizeof(MobileNode *));
int out_index = gk->get_neighbors((MobileNode*)tnode,
outlist);
for (i=0; i < out_index; i ++) {
newp = p->copy();
rnode = outlist[i];
propdelay = get_pdelay(tnode, rnode);
rifp = (rnode->ifhead_).lh_first;
for(; rifp; rifp = rifp->nextnode()){
if (rifp->channel() == this){
s.schedule(rifp, newp, propdelay);
break;
}
}
}
delete [] outlist;
} else {
for( ; rifp; rifp = rifp->nextchnl()) {
rnode = rifp->node();
if(rnode == tnode)
continue;
/*
* Each node needs to get their own copy of this packet.
* Since collisions occur at the receiver, we can have
* two nodes canceling and freeing the *same* simulation
* event.
*
*/
newp = p->copy();
propdelay = get_pdelay(tnode, rnode);
/*
* Each node on the channel receives a copy of the
* packet. The propagation delay determines exactly
* when the receiver's interface detects the first
* bit of this packet.
*/
s.schedule(rifp, newp, propdelay);
}
}
Packet::free(p);
}
double
Channel::get_pdelay(Node* /*tnode*/, Node* /*rnode*/)
{
// Dummy function
return delay_;
}
void
Channel::dump(void)
{
Phy *n;
fprintf(stdout, "Network Interface List\n");
for(n = ifhead_.lh_first; n; n = n->nextchnl() )
n->dump();
fprintf(stdout, "--------------------------------------------------\n");
}
// Wireless extensions
class MobileNode;
WirelessChannel::WirelessChannel(void) : Channel() {}
double
WirelessChannel::get_pdelay(Node* tnode, Node* rnode)
{
// Scheduler &s = Scheduler::instance();
MobileNode* tmnode = (MobileNode*)tnode;
MobileNode* rmnode = (MobileNode*)rnode;
double propdelay = 0;
propdelay = tmnode->propdelay(rmnode);
assert(propdelay >= 0.0);
if (propdelay == 0.0) {
/* if the propdelay is 0 b/c two nodes are on top of
each other, move them slightly apart -dam 7/28/98 */
propdelay = 2 * DBL_EPSILON;
//printf ("propdelay 0: %d->%d at %f\n",
// tmnode->address(), rmnode->address(), s.clock());
}
return propdelay;
}
// send():
// The packet occupies the channel for the transmission time, txtime
// If collision occur (>1 pkts overlap), corrupt all pkts involved
// by setting the error bit or discard them
// int Channel::send(Packet* p, Phy *tifp)
// {
// // without collision, return 0
// Scheduler& s = Scheduler::instance();
// double now = s.clock();
// // busy = time when the channel are still busy with earlier tx
// double busy = max(txstop_, cwstop_);
// // txstop = when the channel is no longer busy from this tx
// txstop_ = max(busy, now + txtime);
// // now < busy => collision
// // mark the pkt error bit, EF_COLLISION
// // drop if there is a drop target, drop_
// if (now < busy) {
// // if still transmit earlier packet, pkt_, then corrupt it
// if (pkt_ && pkt_->time_ > now) {
// hdr_cmn::access(pkt_)->error() |= EF_COLLISION;
// if (drop_) {
// s.cancel(pkt_);
// drop(pkt_);
// pkt_ = 0;
// }
// }
// // corrupts the current packet p, and drop if drop_ exists
// hdr_cmn::access(p)->error() |= EF_COLLISION;
// if (drop_) {
// drop(p);
// return 1;
// }
// }
// // if p was not dropped, call recv() or hand it to trace_ if present
// pkt_ = p;
// trace_ ? trace_->recv(p, 0) : recv(p, 0);
// return 0;
// }
// contention():
// The MAC calls this Channel::contention() to enter contention period
// It determines when the contention window is over, cwstop_,
// and schedule a callback to the MAC for the actual send()
// void Channel::contention(Packet* p, Handler* h)
// {
// Scheduler& s = Scheduler::instance();
// double now = s.clock();
// if (now > cwstop_) {
// cwstop_ = now + delay_;
// numtx_ = 0;
// }
// numtx_++;
// s.schedule(h, p, cwstop_ - now);
// }
// jam():
// Jam the channel for a period txtime
// Some MAC protocols use this to let other MAC detect collisions
// int Channel::jam(double txtime)
// {
// // without collision, return 0
// double now = Scheduler::instance().clock();
// if (txstop_ > now) {
// txstop_ = max(txstop_, now + txtime);
// return 1;
// }
// txstop_ = now + txtime;
// return (now < cwstop_);
// }
// int DuplexChannel::send(Packet* p, double txtime)
// {
// double now = Scheduler::instance().clock();
// txstop_ = now + txtime;
// trace_ ? trace_->recv(p, 0) : recv(p, 0);
// return 0;
// }
// void DuplexChannel::contention(Packet* p, Handler* h)
// {
// Scheduler::instance().schedule(h, p, delay_);
// numtx_ = 1;
// }
chost.cc
/* -*- 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 Research Group 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.
*/
/*
* Functions invoked by TcpSessionAgent
*/
#include "nilist.h"
#include "chost.h"
#include "tcp-int.h"
#include "random.h"
CorresHost::CorresHost() : slink(), TcpFsAgent(),
lastackTS_(0), dontAdjustOwnd_(0), dontIncrCwnd_(0), rexmtSegCount_(0),
connWithPktBeforeFS_(NULL)
{
nActive_ = nTimeout_ = nFastRec_ = 0;
ownd_ = 0;
owndCorrection_ = 0;
closecwTS_ = 0;
connIter_ = new Islist_iter (conns_);
rtt_seg_ = NULL;
}
/*
* Open up the congestion window.
*/
void
CorresHost::opencwnd(int /*size*/, IntTcpAgent *sender)
{
if (cwnd_ < ssthresh_) {
/* slow-start (exponential) */
cwnd_ += 1;
} else {
/* linear */
//double f;
if (!proxyopt_) {
switch (wnd_option_) {
case 0:
if ((count_ = count_ + winInc_) >= cwnd_) {
count_ = 0;
cwnd_ += winInc_;
}
break;
case 1:
/* This is the standard algorithm. */
cwnd_ += winInc_ / cwnd_;
break;
default:
#ifdef notdef
/*XXX*/
error("illegal window option %d", wnd_option_);
#endif
abort();
}
} else { // proxy
switch (wnd_option_) {
case 0:
case 1:
if (sender->highest_ack_ >= sender->wndIncSeqno_) {
cwnd_ += winInc_;
sender->wndIncSeqno_ = 0;
}
break;
default:
#ifdef notdef
/*XXX*/
error("illegal window option %d", wnd_option_);
#endif
abort();
}
}
}
// if maxcwnd_ is set (nonzero), make it the cwnd limit
if (maxcwnd_ && (int(cwnd_) > maxcwnd_))
cwnd_ = maxcwnd_;
return;
}
void
CorresHost::closecwnd(int how, double ts, IntTcpAgent *sender)
{
if (proxyopt_) {
if (!sender || ts > sender->closecwTS_)
closecwnd(how, sender);
}
else {
if (ts > closecwTS_)
closecwnd(how, sender);
}
}
void
CorresHost::closecwnd(int how, IntTcpAgent *sender)
{
int sender_ownd = 0;
if (sender)
sender_ownd = sender->maxseq_ - sender->highest_ack_;
closecwTS_ = Scheduler::instance().clock();
if (proxyopt_) {
if (sender)
sender->closecwTS_ = closecwTS_;
how += 10;
}
switch (how) {
case 0:
case 10:
/* timeouts */
/*
* XXX we use cwnd_ instead of ownd_ here, which may not be
* appropriate if the sender does not fully utilize the
* available congestion window (ownd_ < cwnd_).
*/
ssthresh_ = int(cwnd_ * winMult_);
cwnd_ = int(wndInit_);
break;
case 1:
/* Reno dup acks, or after a recent congestion indication. */
/*
* XXX we use cwnd_ instead of ownd_ here, which may not be
* appropriate if the sender does not fully utilize the
* available congestion window (ownd_ < cwnd_).
*/
cwnd_ *= winMult_;
ssthresh_ = int(cwnd_);
if (ssthresh_ < 2)
ssthresh_ = 2;
break;
case 11:
/* Reno dup acks, or after a recent congestion indication. */
/* XXX fix this -- don't make it dependent on ownd */
cwnd_ = ownd_ - sender_ownd*(1-winMult_);
ssthresh_ = int(cwnd_);
if (ssthresh_ < 2)
ssthresh_ = 2;
break;
case 3:
case 13:
/* idle time >= t_rtxcur_ */
cwnd_ = wndInit_;
break;
default:
abort();
}
fcnt_ = 0.;
count_ = 0;
if (sender)
sender->count_ = 0;
}
Segment*
CorresHost::add_pkts(int /*size*/, int seqno, int sessionSeqno, int daddr, int dport,
int sport, double ts, IntTcpAgent *sender)
{
class Segment *news;
ownd_ += 1;
news = new Segment;
news->seqno_ = seqno;
news->sessionSeqno_ = sessionSeqno;
news->daddr_ = daddr;
news->dport_ = dport;
news->sport_ = sport;
news->ts_ = ts;
news->size_ = 1;
news->dupacks_ = 0;
news->later_acks_ = 0;
news->thresh_dupacks_ = 0;
news->partialack_ = 0;
news->rxmitted_ = 0;
news->sender_ = sender;
seglist_.append(news);
return news;
}
void
CorresHost::adjust_ownd(int size)
{
if (double(owndCorrection_) < size)
ownd_ -= min(double(ownd_), size - double(owndCorrection_));
owndCorrection_ -= min(double(owndCorrection_),size);
if (double(ownd_) < -0.5 || double(owndCorrection_ < -0.5))
printf("In adjust_ownd(): ownd_ = %g owndCorrection_ = %g\n", double(ownd_), double(owndCorrection_));
}
int
CorresHost::clean_segs(int /*size*/, Packet *pkt, IntTcpAgent *sender, int sessionSeqno, int amt_data_acked)
{
Segment *cur, *prev=NULL, *newseg;
int i;
//int rval = -1;
/* remove all acked pkts from list */
int latest_susp_loss = rmv_old_segs(pkt, sender, amt_data_acked);
/*
* XXX If no new data is acked and the last time we shrunk the window
* covers the most recent suspected loss, update the estimate of the amount
* of outstanding data.
*/
if (amt_data_acked == 0 && latest_susp_loss <= recover_ &&
!dontAdjustOwnd_ && last_cwnd_action_ != CWND_ACTION_TIMEOUT) {
owndCorrection_ += min(double(ownd_),1);
ownd_ -= min(double(ownd_),1);
}
/*
* A pkt is a candidate for retransmission if it is the leftmost one in the
* unacked window for the connection AND has at least NUMDUPACKS
* dupacks/later acks AND (at least one dupack OR a later packet also
* with the threshold number of dup/later acks). A pkt is also a candidate
* for immediate retransmission if it has partialack_ set, indicating that
* a partial new ack has been received for it.
*/
for (i=0; i < rexmtSegCount_; i++) {
int remove_flag = 0;
/*
* curArray_ only contains segments that are the first oldest
* unacked segments of their connection (i.e., they are at the left
* edge of their window) and have either received a
* partial ack and/or have received at least NUMDUPACKS
* dupacks/later acks. Thus, segments in curArray_ have a high
* probability (but not certain) of being eligible for
* retransmission. Using curArray_ avoids having to scan
* through all the segments.
*/
cur = curArray_[i];
prev = prevArray_[i];
if (cur->partialack_ || cur->dupacks_ > 0 ||
cur->sender_->num_thresh_dupack_segs_ > 1 ) {
if (cur->thresh_dupacks_) {
cur->thresh_dupacks_ = 0;
cur->sender_->num_thresh_dupack_segs_--;
}
if (cur->sessionSeqno_ <= recover_ &&
last_cwnd_action_ != CWND_ACTION_TIMEOUT /* XXX 2 */)
dontAdjustOwnd_ = 1;
if ((cur->sessionSeqno_ > recover_) ||
(last_cwnd_action_ == CWND_ACTION_TIMEOUT /* XXX 2 */)
|| (proxyopt_ && cur->seqno_ > cur->sender_->recover_)
|| (proxyopt_ && cur->sender_->last_cwnd_action_ == CWND_ACTION_TIMEOUT /* XXX 2 */)) {
/* new loss window */
closecwnd(1, cur->ts_, cur->sender_);
recover_ = sessionSeqno - 1;
last_cwnd_action_ = CWND_ACTION_DUPACK /* XXX 1 */;
cur->sender_->recover_ = cur->sender_->maxseq_;
cur->sender_->last_cwnd_action_ =
CWND_ACTION_DUPACK /* XXX 1 */;
dontAdjustOwnd_ = 0;
}
if ((newseg = cur->sender_->rxmit_last(TCP_REASON_DUPACK,
cur->seqno_, cur->sessionSeqno_, cur->ts_))) {
newseg->rxmitted_ = 1;
adjust_ownd(cur->size_);
if (!dontAdjustOwnd_) {
owndCorrection_ +=
min(double(ownd_),cur->dupacks_);
ownd_ -= min(double(ownd_),cur->dupacks_);
}
seglist_.remove(cur, prev);
remove_flag = 1;
delete cur;
}
/*
* if segment just removed used to be the one just previous
* to the next segment in the array, update prev for the
* next segment
*/
if (remove_flag && cur == prevArray_[i+1])
prevArray_[i+1] = prev;
}
}
rexmtSegCount_ = 0;
return(0);
}
int
CorresHost::rmv_old_segs(Packet *pkt, IntTcpAgent *sender, int amt_data_acked)
{
Islist_iter seg_iter(seglist_);
Segment *cur, *prev=0;
int found = 0;
int done = 0;
int new_data_acked = 0;
int partialack = 0;
int latest_susp_loss = -1;
hdr_tcp *tcph = (hdr_tcp*)pkt->access(off_tcp_);
if (tcph->ts_echo() > lastackTS_)
lastackTS_ = tcph->ts_echo();
while (((cur = seg_iter()) != NULL) &&
(!done || tcph->ts_echo() > cur->ts_)) {
int remove_flag = 0;
/* ack for older pkt of another connection */
if (sender != cur->sender_ && tcph->ts_echo() > cur->ts_) {
if (!disableIntLossRecov_)
cur->later_acks_++;
latest_susp_loss =
max(latest_susp_loss,cur->sessionSeqno_);
dontIncrCwnd_ = 1;
}
/* ack for same connection */
else if (sender == cur->sender_) {
/* found packet acked */
if (tcph->seqno() == cur->seqno_ &&
tcph->ts_echo() == cur->ts_)
found = 1;
/* higher ack => clean up acked packets */
if (tcph->seqno() >= cur->seqno_) {
adjust_ownd(cur->size_);
seglist_.remove(cur, prev);
remove_flag = 1;
new_data_acked += cur->size_;
if (new_data_acked >= amt_data_acked)
done = 1;
if (prev == cur)
prev = NULL;
if (cur == rtt_seg_)
rtt_seg_ = NULL;
delete cur;
if (seg_iter.get_cur() && prev)
seg_iter.set_cur(prev);
else if (seg_iter.get_cur())
seg_iter.set_cur(seg_iter.get_last());
}
/* partial new ack => rexmt immediately */
/* XXX should we check recover for session? */
else if (amt_data_acked > 0 &&
tcph->seqno() == cur->seqno_-1 &&
cur->seqno_ <= sender->recover_ &&
sender->last_cwnd_action_ == CWND_ACTION_DUPACK) {
cur->partialack_ = 1;
partialack = 1;
latest_susp_loss =
max(latest_susp_loss,cur->sessionSeqno_);
if (new_data_acked >= amt_data_acked)
done = 1;
dontIncrCwnd_ = 1;
}
/*
* If no new data has been acked AND this segment has
* not been retransmitted before AND the ack indicates
* that this is the next segment to be acked, then
* increment dupack count.
*/
else if (!amt_data_acked && !cur->rxmitted_ &&
tcph->seqno() == cur->seqno_-1) {
cur->dupacks_++;
latest_susp_loss =
max(latest_susp_loss,cur->sessionSeqno_);
done = 1;
dontIncrCwnd_ = 1;
}
}
if (cur->dupacks_+cur->later_acks_ >= NUMDUPACKS &&
!cur->thresh_dupacks_) {
cur->thresh_dupacks_ = 1;
cur->sender_->num_thresh_dupack_segs_++;
}
if (amt_data_acked==0 && tcph->seqno()==cur->seqno_-1)
done = 1;
/* XXX we could check for rexmt candidates here if we ignore
the num_thresh_dupack_segs_ check */
if (!remove_flag &&
cur->seqno_ == cur->sender_->highest_ack_ + 1 &&
(cur->dupacks_ + cur->later_acks_ >= NUMDUPACKS ||
cur->partialack_)) {
curArray_[rexmtSegCount_] = cur;
prevArray_[rexmtSegCount_] = prev;
rexmtSegCount_++;
}
if (!remove_flag)
prev = cur;
}
/* partial ack => terminate fast start mode */
if (partialack && fs_enable_ && fs_mode_) {
timeout(TCP_TIMER_RESET);
rexmtSegCount_ = 0;
}
return latest_susp_loss;
}
void
CorresHost::add_agent(IntTcpAgent *agent, int /*size*/, double winMult,
int winInc, int /*ssthresh*/)
{
if (nActive_ >= MAX_PARALLEL_CONN) {
printf("In add_agent(): reached limit of number of parallel conn (%d); returning\n", nActive_);
return;
}
nActive_++;
if ((!fixedIw_ && nActive_ > 1) || cwnd_ == 0)
cwnd_ += 1; /* XXX should this be done? */
wndInit_ = 1;
winMult_ = winMult;
winInc_ = winInc;
/* ssthresh_ = ssthresh;*/
conns_.append(agent);
}
int
CorresHost::ok_to_snd(int /*size*/)
{
if (ownd_ <= -0.5)
printf("In ok_to_snd(): ownd_ = %g owndCorrection_ = %g\n", double(ownd_), double(owndCorrection_));
return (cwnd_ >= ownd_+1);
}
classifier-addr.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1996-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 MASH Research
* Group at the University of California Berkeley.
* 4. Neither the name of the University nor of the Research Group 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.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$";
#endif
#include "classifier-addr.h"
int AddressClassifier::classify(Packet *p) {
hdr_ip* iph = hdr_ip::access(p);
return mshift(iph->daddr());
};
static class AddressClassifierClass : public TclClass {
public:
AddressClassifierClass() : TclClass("Classifier/Addr") {}
TclObject* create(int, const char*const*) {
return (new AddressClassifier());
}
} class_address_classifier;
/* added for mobileip code Ya, 2/99*/
static class ReserveAddressClassifierClass : public TclClass {
public:
ReserveAddressClassifierClass() : TclClass("Classifier/Addr/Reserve") {}
TclObject* create(int, const char*const*) {
return (new ReserveAddressClassifier());
}
} class_reserve_address_classifier;
int ReserveAddressClassifier::command(int argc, const char*const* argv)
{
// Tcl& tcl = Tcl::instance();
if (argc == 3 && strcmp(argv[1],"reserve-port") == 0) {
reserved_ = atoi(argv[2]);
alloc((maxslot_ = reserved_ - 1));
return(TCL_OK);
}
return (AddressClassifier::command(argc, argv));
}
void ReserveAddressClassifier::clear(int slot)
{
slot_[slot] = 0;
if (slot == maxslot_) {
while (--maxslot_ >= reserved_ && slot_[maxslot_] == 0)
;
}
}
int ReserveAddressClassifier::classify(Packet *p) {
hdr_ip* iph = hdr_ip::access(p);
return iph->dport();
};
int ReserveAddressClassifier::getnxt(NsObject *nullagent)
{
int i;
for (i=reserved_; i < nslot_; i++)
if (slot_[i]==0 || slot_[i]==nullagent)
return i;
i=nslot_;
alloc(nslot_);
return i;
}
static class BcastAddressClassifierClass : public TclClass {
public:
BcastAddressClassifierClass() : TclClass("Classifier/Hash/Dest/Bcast") {}
TclObject* create(int, const char*const*) {
return (new BcastAddressClassifier());
}
} class_bcast_address_classifier;
NsObject* BcastAddressClassifier::find(Packet* p)
{
NsObject* node = NULL;
int cl = classify(p);
if (cl < 0 || cl >= nslot_ || (node = slot_[cl]) == 0) {
if (cl == BCAST_ADDR_MASK) {
// limited broadcast; assuming no such packet
// would be delivered back to sender
return bcast_recver_;
}
if (default_target_)
return default_target_;
/*
* Sigh. Can't pass the pkt out to tcl because it's
* not an object.
*/
Tcl::instance().evalf("%s no-slot %d", name(), cl);
/*
* Try again. Maybe callback patched up the table.
*/
cl = classify(p);
if (cl < 0 || cl >= nslot_ || (node = slot_[cl]) == 0)
return (NULL);
}
return (node);
}
int BcastAddressClassifier::command(int argc, const char*const* argv)
{
// Tcl& tcl = Tcl::instance();
if (argc == 3 && strcmp(argv[1],"bcast-receiver") == 0) {
bcast_recver_ = (NsObject*)TclObject::lookup(argv[2]);
return(TCL_OK);
}
return (AddressClassifier::command(argc, argv));
}
classifier-bst.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* classifier-bst.cc
* Copyright (C) 1999 by USC/ISI
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation, advertising
* materials, and other materials related to such distribution and use
* acknowledge that the software was developed by the University of
* Southern California, Information Sciences Institute. The name of the
* University may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$";
#endif
#include
#include
#include
#include "config.h"
#include "packet.h"
#include "ip.h"
#include "classifier.h"
#include "classifier-mcast.h"
#include "address.h"
#include "trace.h"
#include "ump.h"
int hdr_ump::offset_;
struct upstream_info {
int dst;
int node_id;
char* oiflink;
struct upstream_info* next;
};
class MCastBSTClassifier : public MCastClassifier {
public:
MCastBSTClassifier();
~MCastBSTClassifier();
static const char STARSYM[]; //"source" field for shared trees
protected:
virtual int classify(Packet * p);
upstream_info *oif2RP_;
int32_t node_id_;
void insert_upstream_info(int dst);
virtual void recv(Packet *p, Handler *h);
void upstream_add(int dst, char *oif2RP, int node_id);
upstream_info* upstream_find(int dst);
};
const char MCastBSTClassifier::STARSYM[]= "x"; //"source" field for shared trees
static class MCastBSTClassifierClass : public TclClass {
public:
MCastBSTClassifierClass() : TclClass("Classifier/Multicast/BST") {}
TclObject* create(int, const char*const*) {
return (new MCastBSTClassifier());
}
} class_mcast_bidir_classifier;
MCastBSTClassifier::MCastBSTClassifier()
{
oif2RP_ = NULL;
node_id_ = -1;
}
MCastBSTClassifier::~MCastBSTClassifier()
{
clearAll();
}
int MCastBSTClassifier::classify(Packet *pkt)
{
hdr_cmn* h = hdr_cmn::access(pkt);
hdr_ip* ih = hdr_ip::access(pkt);
nsaddr_t src = ih->saddr(); /*XXX*/
nsaddr_t dst = ih->daddr();
int iface = h->iface();
Tcl& tcl = Tcl::instance();
hashnode* p = lookup(src, dst, iface);
//printf("%s, src %d, dst %d, iface %d, p %d\n", name(), src, dst, iface, p);
if (p == 0)
p = lookup_star(dst, iface);
if (p == 0) {
if ((p = lookup(src, dst)) == 0)
p = lookup_star(dst);
if (p == 0) {
// Didn't find an entry.
tcl.evalf("%s new-group %ld %ld %d cache-miss",
name(), src, dst, iface);
// XXX see McastProto.tcl for the return values 0 -
// once, 1 - twice
//printf("cache-miss result= %s\n", tcl.result());
int res= atoi(tcl.result());
if (res)
insert_upstream_info(dst);
return (res)? Classifier::TWICE : Classifier::ONCE;
}
if (p->iif == ANY_IFACE.value()) // || iface == UNKN_IFACE.value())
return p->slot;
tcl.evalf("%s new-group %ld %ld %d wrong-iif",
name(), src, dst, iface);
//printf("wrong-iif result= %s\n", tcl.result());
int res= atoi(tcl.result());
return (res)? Classifier::TWICE : Classifier::ONCE;
}
return p->slot;
}
void MCastBSTClassifier::recv(Packet *p, Handler *h)
{
hdr_cmn* h_cmn = hdr_cmn::access(p);
hdr_ip* ih = hdr_ip::access(p);
hdr_ump* ump = hdr_ump::access(p);
nsaddr_t dst = ih->daddr();
Tcl& tcl = Tcl::instance();
upstream_info *u_info;
if (node_id_ == -1) {
tcl.evalf("[%s set node_] id", name());
sscanf(tcl.result(), "%d", &node_id_);
} // if
nsaddr_t src = ih->saddr(); /*XXX*/
NsObject *node = find(p);
if (node == NULL) {
Packet::free(p);
return;
} // if
if ((node_id_ != src) && (ump->isSet) && (ump->umpID_ != node_id_)) {
// If this node is not the next hop upstream router,
// and if there are no receivers connected to it, then
// we should immediately drop the packet before we
// trigger a chace miss
int rpf_iface;
tcl.evalf("%s check-rpf-link %d %d", name(), node_id_, dst);
sscanf(tcl.result(), "%d", &rpf_iface);
if (rpf_iface != h_cmn->iface()) {
// The RPF check has failed so we have to drop
// the packet. Otherwise, we will create
// duplicate packets on the net.
Packet::free(p);
return;
// The following code demonstrates how we
// could generate duplicates if RPF check is
// ignord. Do not reset the UMP and then we
// will have even more duplicates. Remember
// to comment out the previous two lines
// ih->flowid()++;
} else
ump->isSet = 0;
} // if
if (src == node_id_) {
memset(ump, 0, sizeof(hdr_ump)); // Initialize UMP to 0
// We need to set the UMP option. We need to find the
// next hop router to the source and place it in the
// UMP option.
u_info = upstream_find(dst);
if (!u_info) {
printf("Error: Mcast info does not exist\n");
exit(0);
} // if
ump->isSet = 1;
ump->umpID_ = node_id_;
} // if
// if (ump->isSet) {
// if (ump->umpID_ != node_id_) {
// // By now we are sure that the packet has
// // arrived on an RPF link. So the UMP portion
// // of the packet should be cleared before it
// // is sent downstream
// ump->isSet = 0;
// } // if
// } // if
if (ump->isSet) {
u_info = upstream_find(dst);
if (node_id_ == u_info->node_id)
// If the next hop is the node itself, then we
// are at the RP.
ump->isSet = 0;
else {
ump->umpID_ = u_info->node_id;
ump->oif = strdup(u_info->oiflink);
} // else
} else {
int match;
tcl.evalf("%s match-BST-iif %d %d", name(),
h_cmn->iface(), dst);
sscanf(tcl.result(), "%d", (int *)&match);
if (!match) {
Packet::free(p);
return;
} // else
} // else
node->recv(p, h);
} // MCastBSTClassifier::recv
void MCastBSTClassifier::upstream_add(int dst, char *link, int node_id)
{
struct upstream_info *next,
*current;
if (oif2RP_) {
next = oif2RP_;
do {
current = next;
if (current->dst == dst) {
free(current->oiflink);
current->oiflink = strdup(link);
current->node_id = node_id;
return;
} // if
next = current->next;
} while (next);
next = new(struct upstream_info);
next->dst = dst;
next->oiflink = strdup(link);
next->node_id = node_id;
current->next = next;
} else {
oif2RP_ = new(struct upstream_info);
oif2RP_->dst = dst;
oif2RP_->oiflink = strdup(link);
oif2RP_->node_id = node_id;
oif2RP_->next = NULL;
} // else
} // MCastBSTClassifier::upstream_add
upstream_info* MCastBSTClassifier::upstream_find(int dst)
{
upstream_info *index;
index = oif2RP_;
while (index) {
if (index->dst == dst)
return index;
index = index->next;
} // while
return NULL;
} // MCastBSTClassifier::upstream_find
void MCastBSTClassifier::insert_upstream_info(int dst)
{
char temp_str[100];
int nodeID;
Tcl& tcl = Tcl::instance();
tcl.evalf("%s info class", name());
sscanf(tcl.result(), "%s", temp_str);
tcl.evalf("%s upstream-link %d", name(), dst);
sscanf(tcl.result(), "%s %d", temp_str, &nodeID);
upstream_add(dst, temp_str, nodeID);
} // MCastBSTClassifier::insert_upstream_info
classifier-hash.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1997 The 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 Network Research
* Group at Lawrence Berkeley National Laboratory.
* 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.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (LBL)";
#endif
//
// a generalized classifier for mapping (src/dest/flowid) fields
// to a bucket. "buckets_" worth of hash table entries are created
// at init time, and other entries in the same bucket are created when
// needed
//
//
extern "C" {
#include
}
#include
#include "config.h"
#include "packet.h"
#include "ip.h"
#include "classifier.h"
#include "classifier-hash.h"
/****************** HashClassifier Methods ************/
int HashClassifier::classify(Packet * p) {
int slot= lookup(p);
if (slot >= 0 && slot <=maxslot_)
return (slot);
else if (default_ >= 0)
return (default_);
return (unknown(p));
} // HashClassifier::classify
int HashClassifier::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
/*
* $classifier set-hash $hashbucket src dst fid $slot
*/
if (argc == 7) {
if (strcmp(argv[1], "set-hash") == 0) {
//xxx: argv[2] is ignored for now
nsaddr_t src = atoi(argv[3]);
nsaddr_t dst = atoi(argv[4]);
int fid = atoi(argv[5]);
int slot = atoi(argv[6]);
if (0 > set_hash(src, dst, fid, slot))
return TCL_ERROR;
return TCL_OK;
}
} else if (argc == 6) {
/* $classifier lookup $hashbuck $src $dst $fid */
if (strcmp(argv[1], "lookup") == 0) {
nsaddr_t src = atoi(argv[3]);
nsaddr_t dst = atoi(argv[4]);
int fid = atoi(argv[5]);
int slot= get_hash(src, dst, fid);
if (slot>=0 && slot <=maxslot_) {
tcl.resultf("%s", slot_[slot]->name());
return (TCL_OK);
}
tcl.resultf("");
return (TCL_OK);
}
} else if (argc == 5) {
/* $classifier del-hash src dst fid */
if (strcmp(argv[1], "del-hash") == 0) {
nsaddr_t src = atoi(argv[2]);
nsaddr_t dst = atoi(argv[3]);
int fid = atoi(argv[4]);
Tcl_HashEntry *ep= Tcl_FindHashEntry(&ht_,
hashkey(src, dst,
fid));
if (ep) {
int slot= (int)Tcl_GetHashValue(ep);
Tcl_DeleteHashEntry(ep);
tcl.resultf("%u", slot);
return (TCL_OK);
}
return (TCL_ERROR);
}
}
return (Classifier::command(argc, argv));
}
/************** TCL linkage ****************/
static class SrcDestHashClassifierClass : public TclClass {
public:
SrcDestHashClassifierClass() : TclClass("Classifier/Hash/SrcDest") {}
TclObject* create(int, const char*const*) {
return new SrcDestHashClassifier;
}
} class_hash_srcdest_classifier;
static class FidHashClassifierClass : public TclClass {
public:
FidHashClassifierClass() : TclClass("Classifier/Hash/Fid") {}
TclObject* create(int, const char*const*) {
return new FidHashClassifier;
}
} class_hash_fid_classifier;
static class DestHashClassifierClass : public TclClass {
public:
DestHashClassifierClass() : TclClass("Classifier/Hash/Dest") {}
TclObject* create(int, const char*const*) {
return new DestHashClassifier;
}
} class_hash_dest_classifier;
static class SrcDestFidHashClassifierClass : public TclClass {
public:
SrcDestFidHashClassifierClass() : TclClass("Classifier/Hash/SrcDestFid") {}
TclObject* create(int, const char*const*) {
return new SrcDestFidHashClassifier;
}
} class_hash_srcdestfid_classifier;
// DestHashClassifier methods
int DestHashClassifier::classify(Packet *p)
{
int slot= lookup(p);
if (slot >= 0 && slot <=maxslot_)
return (slot);
else if (default_ >= 0)
return (default_);
return -1;
} // HashClassifier::classify
int DestHashClassifier::command(int argc, const char*const* argv)
{
if (argc == 4) {
// $classifier install $dst $node
if (strcmp(argv[1], "install") == 0) {
nsaddr_t dst = atoi(argv[2]);
NsObject *node = (NsObject*)TclObject::lookup(argv[3]);
int slot = getnxt(node);
install(slot, node);
if (set_hash(0, dst, 0, slot) >= 0)
return TCL_OK;
else
return TCL_ERROR;
} // if
}
return(HashClassifier::command(argc, argv));
} // command
classifier-mac.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1996-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 MASH Research
* Group at the University of California Berkeley.
* 4. Neither the name of the University nor of the Research Group 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 Giao Nguyen, http://daedalus.cs.berkeley.edu/~gnguyen
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (UCB)";
#endif
#include "packet.h"
#include "mac.h"
#include "classifier.h"
class MacClassifier : public Classifier {
public:
MacClassifier() : bcast_(0) {
bind("bcast_", &bcast_);
}
void recv(Packet*, Handler*);
int bcast_;
};
static class MacClassifierClass : public TclClass {
public:
MacClassifierClass() : TclClass("Classifier/Mac") {}
TclObject* create(int, const char*const*) {
return (new MacClassifier());
}
} class_mac_classifier;
void MacClassifier::recv(Packet* p, Handler*)
{
Mac* mac;
hdr_mac* mh = HDR_MAC(p);
if (bcast_ || mh->macDA() == BCAST_ADDR || (mac = (Mac *)find(p)) == 0) {
// Replicate packets to all slots (broadcast)
int macSA = mh->macSA();
for (int i = 0; i < maxslot_; ++i) {
if ((mac = (Mac *)slot_[i]) && mac->addr() != macSA)
mac->recv(p->copy(), 0);
}
if (maxslot_ >= 0) {
mac= (Mac *)slot_[maxslot_];
if (mac->addr() != macSA) {
mac->recv(p, 0);
return;
}
}
Packet::free(p);
return;
}
mac->recv(p, 0);
}
classifier-mcast.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1996-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 MASH Research
* Group at the University of California Berkeley.
* 4. Neither the name of the University nor of the Research Group 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.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$";
#endif
#include
#include "config.h"
#include "packet.h"
#include "ip.h"
#include "classifier.h"
#include "classifier-mcast.h"
const char MCastClassifier::STARSYM[]= "x"; //"source" field for shared trees
static class MCastClassifierClass : public TclClass {
public:
MCastClassifierClass() : TclClass("Classifier/Multicast") {}
TclObject* create(int, const char*const*) {
return (new MCastClassifier());
}
} class_mcast_classifier;
MCastClassifier::MCastClassifier()
{
memset(ht_, 0, sizeof(ht_));
memset(ht_star_, 0, sizeof(ht_star_));
}
MCastClassifier::~MCastClassifier()
{
clearAll();
}
void MCastClassifier::clearHash(hashnode* h[], int size)
{
for (int i = 0; i < size; ++i) {
hashnode* p = h[i];
while (p != 0) {
hashnode* n = p->next;
delete p;
p = n;
}
}
memset(h, 0, size * sizeof(hashnode*));
}
void MCastClassifier::clearAll()
{
clearHash(ht_, HASHSIZE);
clearHash(ht_star_, HASHSIZE);
}
MCastClassifier::hashnode*
MCastClassifier::lookup(nsaddr_t src, nsaddr_t dst, int iface) const
{
int h = hash(src, dst);
hashnode* p;
for (p = ht_[h]; p != 0; p = p->next) {
if (p->src == src && p->dst == dst)
if (p->iif == iface ||
//p->iif == UNKN_IFACE.value() ||
iface == ANY_IFACE.value())
break;
}
return (p);
}
MCastClassifier::hashnode*
MCastClassifier::lookup_star(nsaddr_t dst, int iface) const
{
int h = hash(0, dst);
hashnode* p;
for (p = ht_star_[h]; p != 0; p = p->next) {
if (p->dst == dst &&
(iface == ANY_IFACE.value() || p->iif == iface))
break;
}
return (p);
}
int MCastClassifier::classify(Packet *pkt)
{
hdr_cmn* h = hdr_cmn::access(pkt);
hdr_ip* ih = hdr_ip::access(pkt);
nsaddr_t src = ih->saddr();
nsaddr_t dst = ih->daddr();
int iface = h->iface();
Tcl& tcl = Tcl::instance();
hashnode* p = lookup(src, dst, iface);
//printf("%s, src %d, dst %d, iface %d, p %d\n", name(), src, dst, iface, p);
if (p == 0)
p = lookup_star(dst, iface);
if (p == 0) {
if ((p = lookup(src, dst)) == 0)
p = lookup_star(dst);
if (p == 0) {
// Didn't find an entry.
tcl.evalf("%s new-group %ld %ld %d cache-miss",
name(), src, dst, iface);
// XXX see McastProto.tcl for the return values 0 -
// once, 1 - twice
//printf("cache-miss result= %s\n", tcl.result());
int res= atoi(tcl.result());
return (res)? Classifier::TWICE : Classifier::ONCE;
}
if (p->iif == ANY_IFACE.value()) // || iface == UNKN_IFACE.value())
return p->slot;
tcl.evalf("%s new-group %ld %ld %d wrong-iif",
name(), src, dst, iface);
//printf("wrong-iif result= %s\n", tcl.result());
int res= atoi(tcl.result());
return (res)? Classifier::TWICE : Classifier::ONCE;
}
return p->slot;
}
int MCastClassifier::findslot()
{
int i;
for (i = 0; i < nslot_; ++i)
if (slot_[i] == 0)
break;
return (i);
}
void MCastClassifier::set_hash(hashnode* ht[], nsaddr_t src, nsaddr_t dst,
int slot, int iface)
{
int h = hash(src, dst);
hashnode* p = new hashnode;
p->src = src;
p->dst = dst;
p->slot = slot;
p->iif = iface;
p->next = ht[h];
ht[h] = p;
}
int MCastClassifier::command(int argc, const char*const* argv)
{
if (argc == 6) {
if (strcmp(argv[1], "set-hash") == 0) {
// $classifier set-hash $src $group $slot $iif
// $iif can be:(1)
// (2) "*" - matches any interface
// (3) "?" - interface is unknown (usually this means that
// the packet came from a local agent)
nsaddr_t src = strtol(argv[2], (char**)0, 0);
nsaddr_t dst = strtol(argv[3], (char**)0, 0);
int slot = atoi(argv[4]);
int iface = (strcmp(argv[5], ANY_IFACE.name())==0) ? ANY_IFACE.value()
: (strcmp(argv[5], UNKN_IFACE.name())==0) ? UNKN_IFACE.value()
: atoi(argv[5]);
if (strcmp(STARSYM, argv[2]) == 0) {
// install a entry: give 0 as src, but can be anything
set_hash(ht_star_, 0, dst, slot, iface);
} else {
//install a entry
set_hash(ht_, src, dst, slot, iface);
}
return (TCL_OK);
}
if (strcmp(argv[1], "change-iface") == 0) {
// $classifier change-iface $src $dst $olfiif $newiif
nsaddr_t src = strtol(argv[2], (char**)0, 0);
nsaddr_t dst = strtol(argv[3], (char**)0, 0);
int oldiface = atoi(argv[4]);
int newiface = atoi(argv[5]);
if (strcmp(STARSYM, argv[2]) == 0) {
change_iface(dst, oldiface, newiface);
} else {
change_iface(src, dst, oldiface, newiface);
}
return (TCL_OK);
}
} else if (argc == 5) {
if (strcmp(argv[1], "lookup") == 0) {
// $classifier lookup $src $group $iface
// returns name of the object (replicator)
Tcl &tcl = Tcl::instance();
nsaddr_t src = strtol(argv[2], (char**)0, 0);
nsaddr_t dst = strtol(argv[3], (char**)0, 0);
int iface = atoi(argv[4]);
hashnode* p= (strcmp(STARSYM, argv[2]) == 0) ? lookup_star(dst, iface)
: lookup(src, dst, iface);
if ((p == 0) || (slot_[p->slot] == 0))
tcl.resultf("");
else
tcl.resultf("%s", slot_[p->slot]->name());
return (TCL_OK);
}
} else if (argc == 4) {
if (strcmp(argv[1], "lookup-iface") == 0) {
// $classifier lookup-iface $src $group
// returns incoming iface
Tcl &tcl = Tcl::instance();
nsaddr_t src = strtol(argv[2], (char**)0, 0);
nsaddr_t dst = strtol(argv[3], (char**)0, 0);
hashnode* p= (strcmp(argv[2], STARSYM) == 0) ? lookup_star(dst)
: lookup(src, dst);
if (p == 0)
tcl.resultf("");
else
tcl.resultf("%d", p->iif);
return (TCL_OK);
}
} else if (argc == 2) {
if (strcmp(argv[1], "clearAll") == 0) {
clearAll();
return (TCL_OK);
}
}
return (Classifier::command(argc, argv));
}
/* interface look up for the interface code*/
void MCastClassifier::change_iface(nsaddr_t src, nsaddr_t dst, int oldiface, int newiface)
{
hashnode* p = lookup(src, dst, oldiface);
if (p) p->iif = newiface;
}
void MCastClassifier::change_iface(nsaddr_t dst, int oldiface, int newiface)
{
hashnode* p = lookup_star(dst, oldiface);
if (p) p->iif = newiface;
}
classifier-mpath.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* classifier-mpath.cc
*
* Copyright (C) 1997 by USC/ISI
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation, advertising
* materials, and other materials related to such distribution and use
* acknowledge that the software was developed by the University of
* Southern California, Information Sciences Institute. The name of the
* University may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (USC/ISI)";
#endif
#include "classifier.h"
class MultiPathForwarder : public Classifier {
public:
MultiPathForwarder() : ns_(0) {}
virtual int classify(Packet*) {
int cl;
int fail = ns_;
do {
cl = ns_++;
ns_ %= (maxslot_ + 1);
} while (slot_[cl] == 0 && ns_ != fail);
return cl;
}
private:
int ns_;
};
static class MultiPathClass : public TclClass {
public:
MultiPathClass() : TclClass("Classifier/MultiPath") {}
TclObject* create(int, const char*const*) {
return (new MultiPathForwarder());
}
} class_multipath;
classifier-port.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* classifier-port.cc
* Copyright (C) 1999 by USC/ISI
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation, advertising
* materials, and other materials related to such distribution and use
* acknowledge that the software was developed by the University of
* Southern California, Information Sciences Institute. The name of the
* University may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* @(#) $Header$ (USC/ISI)
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$";
#endif
#include "classifier-port.h"
int PortClassifier::classify(Packet *p) {
// Port classifier returns the destination port. No shifting
// or masking is required since in the 32-bit addressing,
// ports are stored in a seperate variable.
hdr_ip* iph = hdr_ip::access(p);
return iph->dport();
};
static class PortClassifierClass : public TclClass {
public:
PortClassifierClass() : TclClass("Classifier/Port") {}
TclObject* create(int, const char*const*) {
return (new PortClassifier());
}
} class_address_classifier;
static class ReservePortClassifierClass : public TclClass {
public:
ReservePortClassifierClass() : TclClass("Classifier/Port/Reserve") {}
TclObject* create(int, const char*const*) {
return (new ReservePortClassifier());
}
} class_reserve_port_classifier;
int ReservePortClassifier::command(int argc, const char*const* argv)
{
// Tcl& tcl = Tcl::instance();
if (argc == 3 && strcmp(argv[1],"reserve-port") == 0) {
reserved_ = atoi(argv[2]);
alloc((maxslot_ = reserved_ - 1));
return(TCL_OK);
}
return (Classifier::command(argc, argv));
}
void ReservePortClassifier::clear(int slot)
{
slot_[slot] = 0;
if (slot == maxslot_) {
while (--maxslot_ >= reserved_ && slot_[maxslot_] == 0)
;
}
}
int ReservePortClassifier::getnxt(NsObject *nullagent)
{
int i;
for (i=reserved_; i < nslot_; i++)
if (slot_[i]==0 || slot_[i]==nullagent)
return i;
i=nslot_;
alloc(nslot_);
return i;
}
classifier-virtual.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1996-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 MASH Research
* Group at the University of California Berkeley.
* 4. Neither the name of the University nor of the Research Group 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.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$";
#endif
extern "C" {
#include
}
#include "config.h"
#include "packet.h"
#include "ip.h"
#include "classifier.h"
#include "route.h"
#include "object.h"
#include "address.h"
#include
class VirtualClassifier : public Classifier {
public:
VirtualClassifier() : routelogic_(0) {
Tcl_InitHashTable(&ht_, TCL_ONE_WORD_KEYS);
}
~VirtualClassifier() {
Tcl_DeleteHashTable(&ht_);
}
protected:
NsObject* next_;
Tcl_HashTable ht_;
RouteLogic *routelogic_;
NsObject* target_;
bool enableHrouting_;
char nodeaddr_[SMALL_LEN];
int classify(Packet *const p) {
hdr_ip* iph = hdr_ip::access(p);
return mshift(iph->daddr());
}
void recv(Packet* p, Handler* h) {
if (!routelogic_) {
Tcl &tcl = Tcl::instance();
tcl.eval("[Simulator instance] get-routelogic");
routelogic_= (RouteLogic*) TclObject::lookup(tcl.result());
//tcl.evalf("%s info class", tcl.result());
//cout << "created..." << tcl.result() << endl;
}
/* first we find the next hop by asking routelogic
* then we use a hash next_hop -> target_object
* thus, the size of the table is at most N-1
*/
Tcl &tcl = Tcl::instance();
hdr_ip* iph = hdr_ip::access(p);
char* adst= Address::instance().print_nodeaddr(iph->daddr());
//adst[strlen(adst)-1]= 0;
target_= 0;
int next_hopIP;
routelogic_->lookup_flat(nodeaddr_, adst, next_hopIP);
delete [] adst;
int newEntry;
Tcl_HashEntry *ep= Tcl_CreateHashEntry(&ht_, (const char*)next_hopIP,
&newEntry);
if (newEntry) {
tcl.evalf("%s find %d", name(), next_hopIP);
Tcl_SetHashValue(ep, target_= (NsObject*)tcl.lookup(tcl.result()));
} else {
target_= (NsObject*)Tcl_GetHashValue(ep);
}
if (!target_) {
/*
* XXX this should be "dropped" somehow. Right now,
* these events aren't traced.
*/
Packet::free(p);
return;
}
target_->recv(p,h);
}
int command(int argc, const char*const* argv) {
if (argc == 3) {
if (strcmp(argv[1], "nodeaddr") == 0) {
strcpy(nodeaddr_, argv[2]);
return(TCL_OK);
}
}
return (NsObject::command(argc, argv));
}
};
static class VirtualClassifierClass : public TclClass {
public:
VirtualClassifierClass() : TclClass("Classifier/Virtual") {}
TclObject* create(int, const char*const*) {
return (new VirtualClassifier());
}
} class_virtual_classifier;
classifier.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1996 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 MASH Research
* Group at the University of California Berkeley.
* 4. Neither the name of the University nor of the Research Group 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.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$";
#endif
#include
#include "config.h"
#include "classifier.h"
#include "packet.h"
static class ClassifierClass : public TclClass {
public:
ClassifierClass() : TclClass("Classifier") {}
TclObject* create(int, const char*const*) {
return (new Classifier());
}
} class_classifier;
Classifier::Classifier() : slot_(0), nslot_(0), maxslot_(-1)
, shift_(0), mask_(0xffffffff)
{
default_target_ = 0;
bind("offset_", &offset_);
bind("shift_", &shift_);
bind("mask_", &mask_);
}
int Classifier::classify(Packet *p)
{
return (mshift(*((int*) p->access(offset_))));
}
Classifier::~Classifier()
{
delete [] slot_;
}
void Classifier::alloc(int slot)
{
NsObject** old = slot_;
int n = nslot_;
if (old == 0)
nslot_ = 32;
while (nslot_ <= slot)
nslot_ <<= 1;
slot_ = new NsObject*[nslot_];
memset(slot_, 0, nslot_ * sizeof(NsObject*));
for (int i = 0; i < n; ++i)
slot_[i] = old[i];
delete [] old;
}
void Classifier::install(int slot, NsObject* p)
{
if (slot >= nslot_)
alloc(slot);
slot_[slot] = p;
if (slot >= maxslot_)
maxslot_ = slot;
}
void Classifier::clear(int slot)
{
slot_[slot] = 0;
if (slot == maxslot_) {
while (--maxslot_ >= 0 && slot_[maxslot_] == 0)
;
}
}
int Classifier::getnxt(NsObject *nullagent)
{
int i;
for (i=0; i < nslot_; i++)
if (slot_[i]==0 || slot_[i]==nullagent)
return i;
i=nslot_;
alloc(nslot_);
return i;
}
/*
* objects only ever see "packet" events, which come either
* from an incoming link or a local agent (i.e., packet source).
*/
void Classifier::recv(Packet* p, Handler*h)
{
NsObject* node = find(p);
if (node == NULL) {
/*
* XXX this should be "dropped" somehow. Right now,
* these events aren't traced.
*/
Packet::free(p);
return;
}
node->recv(p,h);
}
/*
* perform the mapping from packet to object
* perform upcall if no mapping
*/
NsObject* Classifier::find(Packet* p)
{
NsObject* node = NULL;
int cl = classify(p);
if (cl < 0 || cl >= nslot_ || (node = slot_[cl]) == 0) {
if (default_target_) return default_target_;
/*
* Sigh. Can't pass the pkt out to tcl because it's
* not an object.
*/
Tcl::instance().evalf("%s no-slot %ld", name(), cl);
if (cl == TWICE) {
/*
* Try again. Maybe callback patched up the table.
*/
cl = classify(p);
if (cl < 0 || cl >= nslot_ || (node = slot_[cl]) == 0)
return (NULL);
}
}
return (node);
}
int Classifier::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
if(argc == 2) {
if (strcmp(argv[1], "defaulttarget") == 0) {
if (default_target_ != 0)
tcl.result(default_target_->name());
return (TCL_OK);
}
}
if (argc == 3) {
/*
* $classifier alloc-port nullagent
*/
if (strcmp(argv[1],"alloc-port") == 0) {
int slot;
NsObject* nullagent =(NsObject*)TclObject::lookup(argv[2]);
slot=getnxt(nullagent);
tcl.resultf("%u",slot);
return(TCL_OK);
}
/*
* $classifier clear $slot
*/
if (strcmp(argv[1], "clear") == 0) {
int slot = atoi(argv[2]);
clear(slot);
return (TCL_OK);
}
/*
* $classifier installNext $node
*/
if (strcmp(argv[1], "installNext") == 0) {
int slot = maxslot_ + 1;
NsObject* node = (NsObject*)TclObject::lookup(argv[2]);
if (node == NULL) {
tcl.resultf("Classifier::installNext attempt to install non-object %s into classifier", argv[2]);
return TCL_ERROR;
};
install(slot, node);
tcl.resultf("%u", slot);
return TCL_OK;
}
/*
* $classifier slot snum
* returns the name of the object in slot # snum
*/
if (strcmp(argv[1], "slot") == 0) {
int slot = atoi(argv[2]);
if (slot >= 0 && slot < nslot_ && slot_[slot] != NULL) {
tcl.resultf("%s", slot_[slot]->name());
return TCL_OK;
}
tcl.resultf("Classifier: no object at slot %d", slot);
return (TCL_ERROR);
}
/*
* $classifier findslot $node
* finds the slot containing $node
*/
if (strcmp(argv[1], "findslot") == 0) {
int slot = 0;
NsObject* node = (NsObject*)TclObject::lookup(argv[2]);
if (node == NULL) {
return (TCL_ERROR);
}
while (slot < nslot_) {
if (strcmp(slot_[slot]->name(), argv[2]) == 0) {
tcl.resultf("%u", slot);
return (TCL_OK);
}
slot++;
}
tcl.result("-1");
return (TCL_OK);
}
if(strcmp(argv[1], "defaulttarget") == 0) {
default_target_=(NsObject*)TclObject::lookup(argv[2]);
if(default_target_ == 0)
return TCL_ERROR;
return TCL_OK;
}
} else if (argc == 4) {
/*
* $classifier install $slot $node
*/
if (strcmp(argv[1], "install") == 0) {
int slot = atoi(argv[2]);
NsObject* node = (NsObject*)TclObject::lookup(argv[3]);
install(slot, node);
return (TCL_OK);
}
}
return (NsObject::command(argc, argv));
}
cmu-trace.cc
/* -*- 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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*
* Ported from CMU/Monarch's code, appropriate copyright applies.
* nov'98 -Padma.
*
* $Header$
*/
#include
#include
#include
#include
#include
#include // DSR
#include
#include
#include
#include //TORA
#include // IMEP
#include //AODV
#include
#include
//#define LOG_POSITION
//extern char* pt_names[];
static class CMUTraceClass : public TclClass {
public:
CMUTraceClass() : TclClass("CMUTrace") { }
TclObject* create(int, const char*const* argv) {
return (new CMUTrace(argv[4], *argv[5]));
}
} cmutrace_class;
CMUTrace::CMUTrace(const char *s, char t) : Trace(t)
{
bzero(tracename, sizeof(tracename));
strncpy(tracename, s, MAX_ID_LEN);
if(strcmp(tracename, "RTR") == 0) {
tracetype = TR_ROUTER;
}
else if(strcmp(tracename, "TRP") == 0) {
tracetype = TR_ROUTER;
}
else if(strcmp(tracename, "MAC") == 0) {
tracetype = TR_MAC;
}
else if(strcmp(tracename, "IFQ") == 0) {
tracetype = TR_IFQ;
}
else if(strcmp(tracename, "AGT") == 0) {
tracetype = TR_AGENT;
}
else {
fprintf(stderr, "CMU Trace Initialized with invalid type\n");
exit(1);
}
assert(type_ == DROP || type_ == SEND || type_ == RECV);
newtrace_ = 0;
for (int i=0 ; i < MAX_NODE ; i++) nodeColor[i] = 3 ;
node_ = 0;
off_mac_ = hdr_mac::offset_;
//bind("off_mac_", &off_mac_);
bind("off_arp_", &off_arp_);
bind("off_SR_", &off_sr_);
bind("off_TORA_", &off_TORA_);
bind("off_IMEP_", &off_IMEP_);
bind("off_AODV_", &off_AODV_);
}
void
CMUTrace::format_mac(Packet *p, const char *why, int offset)
{
struct hdr_cmn *ch = HDR_CMN(p);
struct hdr_ip *ih = HDR_IP(p);
struct hdr_mac802_11 *mh = HDR_MAC802_11(p);
double x = 0.0, y = 0.0, z = 0.0;
char op = (char) type_;
Node* thisnode = Node::get_node_by_address(src_);
double energy = -1;
if (thisnode) {
if (thisnode->energy_model()) {
energy = thisnode->energy();
}
}
// hack the IP address to convert pkt format to hostid format
// for now until port ids are removed from IP address. -Padma.
int src = Address::instance().get_nodeaddr(ih->saddr());
if(tracetype == TR_ROUTER && type_ == SEND) {
if(src_ != src)
op = FWRD;
}
// Use new ns trace format to replace the old cmu trace format)
if (newtrace_) {
node_->getLoc(&x, &y, &z);
// consistence
if ( op == DROP ) { op = 'd';}
// basic trace infomation + basic exenstion
sprintf(wrk_ + offset,
"%c -t %.9f -Hs %d -Hd %d -Ni %d -Nx %.2f -Ny %.2f -Nz %.2f -Ne %f -Nl %3s -Nw %s ",
op, // event type
Scheduler::instance().clock(), // time
src_, // this node
ch->next_hop_, // next hop
src_, // this node
x, // x coordinate
y, // y coordinate
z, // z coordinate
energy, // energy, -1 = not existing
tracename, // trace level
why); // reason
// mac layer extension
offset = strlen(wrk_);
sprintf(wrk_ + offset,
"-Ma %x -Md %x -Ms %x -Mt %x ",
mh->dh_duration,
ETHER_ADDR(mh->dh_da),
ETHER_ADDR(mh->dh_sa),
GET_ETHER_TYPE(mh->dh_body));
return;
}
#ifdef LOG_POSITION
double x = 0.0, y = 0.0, z = 0.0;
node_->getLoc(&x, &y, &z);
#endif
sprintf(wrk_ + offset,
#ifdef LOG_POSITION
"%c %.9f %d (%6.2f %6.2f) %3s %4s %d %s %d [%x %x %x %x] ",
#else
"%c %.9f _%d_ %3s %4s %d %s %d [%x %x %x %x] ",
#endif
op,
Scheduler::instance().clock(),
src_, // this node
#ifdef LOG_POSITION
x,
y,
#endif
tracename,
why,
ch->uid(), // identifier for this event
packet_info.name(ch->ptype()),
ch->size(),
//*((u_int16_t*) &mh->dh_fc),
mh->dh_duration,
ETHER_ADDR(mh->dh_da),
ETHER_ADDR(mh->dh_sa),
GET_ETHER_TYPE(mh->dh_body));
offset = strlen(wrk_);
if (thisnode) {
if (thisnode->energy_model()) {
sprintf(wrk_ + offset,
"[energy %f] ",
thisnode->energy());
}
}
}
void
CMUTrace::format_ip(Packet *p, int offset)
{
struct hdr_cmn *ch = HDR_CMN(p);
struct hdr_ip *ih = HDR_IP(p);
// hack the IP address to convert pkt format to hostid format
// for now until port ids are removed from IP address. -Padma.
int src = Address::instance().get_nodeaddr(ih->saddr());
int dst = Address::instance().get_nodeaddr(ih->daddr());
if (newtrace_) {
sprintf(wrk_ + offset,
"-Is %d.%d -Id %d.%d -It %s -Il %d -If %d -Ii %d -Iv %d ",
src, // packet src
ih->sport(), // src port
dst, // packet dest
ih->dport(), // dst port
packet_info.name(ch->ptype()), // packet type
ch->size(), // packet size
ih->flowid(), // flow id
ch->uid(), // unique id
ih->ttl_); // ttl
} else {
sprintf(wrk_ + offset, "------- [%d:%d %d:%d %d %d] ",
src, ih->sport(),
dst, ih->dport(),
ih->ttl_, (ch->next_hop_ < 0) ? 0 : ch->next_hop_);
}
}
void
CMUTrace::format_arp(Packet *p, int offset)
{
struct hdr_arp *ah = HDR_ARP(p);
if (newtrace_) {
sprintf(wrk_ + offset,
"-P arp -Po %s -Pms %d -Ps %d -Pmd %d -Pd %d ",
ah->arp_op == ARPOP_REQUEST ? "REQUEST" : "REPLY",
ah->arp_sha,
ah->arp_spa,
ah->arp_tha,
ah->arp_tpa);
} else {
sprintf(wrk_ + offset,
"------- [%s %d/%d %d/%d]",
ah->arp_op == ARPOP_REQUEST ? "REQUEST" : "REPLY",
ah->arp_sha,
ah->arp_spa,
ah->arp_tha,
ah->arp_tpa);
}
}
void
CMUTrace::format_dsr(Packet *p, int offset)
{
hdr_sr *srh = (hdr_sr*)p->access(off_sr_);
if (newtrace_) {
sprintf(wrk_ + offset,
"-P dsr -Ph %d -Pq %d -Ps %d -Pp %d -Pn %d -Pl %d -Pe %d->%d -Pw %d -Pm %d -Pc %d -Pb %d->%d ",
srh->num_addrs(), // how many nodes travered
srh->route_request(),
srh->rtreq_seq(),
srh->route_reply(),
srh->rtreq_seq(),
srh->route_reply_len(),
// the dest of the src route
srh->reply_addrs()[0].addr,
srh->reply_addrs()[srh->route_reply_len()-1].addr,
srh->route_error(),
srh->num_route_errors(),
srh->down_links()[srh->num_route_errors() - 1].tell_addr,
srh->down_links()[srh->num_route_errors() - 1].from_addr,
srh->down_links()[srh->num_route_errors() - 1].to_addr);
return;
}
sprintf(wrk_ + offset,
"%d [%d %d] [%d %d %d %d->%d] [%d %d %d %d->%d]",
srh->num_addrs(),
srh->route_request(),
srh->rtreq_seq(),
srh->route_reply(),
srh->rtreq_seq(),
srh->route_reply_len(),
// the dest of the src route
srh->reply_addrs()[0].addr,
srh->reply_addrs()[srh->route_reply_len()-1].addr,
srh->route_error(),
srh->num_route_errors(),
srh->down_links()[srh->num_route_errors() - 1].tell_addr,
srh->down_links()[srh->num_route_errors() - 1].from_addr,
srh->down_links()[srh->num_route_errors() - 1].to_addr);
}
void
CMUTrace::format_msg(Packet *, int)
{
}
void
CMUTrace::format_tcp(Packet *p, int offset)
{
struct hdr_cmn *ch = HDR_CMN(p);
struct hdr_tcp *th = HDR_TCP(p);
if( newtrace_ ) {
sprintf(wrk_ + offset,
"-Pn tcp -Ps %d -Pa %d -Pf %d -Po %d ",
th->seqno_,
th->ackno_,
ch->num_forwards(),
ch->opt_num_forwards());
} else {
sprintf(wrk_ + offset,
"[%d %d] %d %d",
th->seqno_,
th->ackno_,
ch->num_forwards(),
ch->opt_num_forwards());
}
}
void
CMUTrace::format_rtp(Packet *p, int offset)
{
struct hdr_cmn *ch = HDR_CMN(p);
struct hdr_rtp *rh = HDR_RTP(p);
struct hdr_ip *ih = HDR_IP(p);
Node* thisnode = Node::get_node_by_address(src_);
//hacking, needs to change later,
int dst = Address::instance().get_nodeaddr(ih->daddr());
if (dst == src_){
// I just received a cbr data packet
if (thisnode->powersaving()) {
//thisnode->set_node_sleep(0);
thisnode->set_node_state(INROUTE);
}
}
if (newtrace_) {
sprintf(wrk_ + offset,
"-Pn cbr -Pi %d -Pf %d -Po %d ",
rh->seqno_,
ch->num_forwards(),
ch->opt_num_forwards());
} else {
sprintf(wrk_ + offset,
"[%d] %d %d",
rh->seqno_,
ch->num_forwards(),
ch->opt_num_forwards());
}
}
void
CMUTrace::format_imep(Packet *p, int offset)
{
struct hdr_imep *im = HDR_IMEP(p);
#define U_INT16_T(x) *((u_int16_t*) &(x))
if (newtrace_) {
sprintf(wrk_ + offset,
"-P imep -Pa %c -Ph %c -Po %c -Pl 0x%04x ] ",
(im->imep_block_flags & BLOCK_FLAG_ACK) ? 'A' : '-',
(im->imep_block_flags & BLOCK_FLAG_HELLO) ? 'H' : '-',
(im->imep_block_flags & BLOCK_FLAG_OBJECT) ? 'O' : '-',
U_INT16_T(im->imep_length));
} else {
sprintf(wrk_ + offset,
"[%c %c %c 0x%04x] ",
(im->imep_block_flags & BLOCK_FLAG_ACK) ? 'A' : '-',
(im->imep_block_flags & BLOCK_FLAG_HELLO) ? 'H' : '-',
(im->imep_block_flags & BLOCK_FLAG_OBJECT) ? 'O' : '-',
U_INT16_T(im->imep_length));
}
#undef U_INT16_T
}
void
CMUTrace::format_tora(Packet *p, int offset)
{
struct hdr_tora *th = HDR_TORA(p);
struct hdr_tora_qry *qh = HDR_TORA_QRY(p);
struct hdr_tora_upd *uh = HDR_TORA_UPD(p);
struct hdr_tora_clr *ch = HDR_TORA_CLR(p);
switch(th->th_type) {
case TORATYPE_QRY:
if (newtrace_) {
sprintf(wrk_ + offset,
"-P tora -Pt 0x%x -Pd %d -Pc QUERY ",
qh->tq_type, qh->tq_dst);
} else {
sprintf(wrk_ + offset, "[0x%x %d] (QUERY)",
qh->tq_type, qh->tq_dst);
}
break;
case TORATYPE_UPD:
if (newtrace_) {
sprintf(wrk_ + offset,
"-P tora -Pt 0x%x -Pd %d (%f %d %d %d %d) -Pc UPDATE ",
uh->tu_type,
uh->tu_dst,
uh->tu_tau,
uh->tu_oid,
uh->tu_r,
uh->tu_delta,
uh->tu_id);
} else {
sprintf(wrk_ + offset,
"-Pt 0x%x -Pd %d -Pa %f -Po %d -Pr %d -Pe %d -Pi %d -Pc UPDATE ",
uh->tu_type,
uh->tu_dst,
uh->tu_tau,
uh->tu_oid,
uh->tu_r,
uh->tu_delta,
uh->tu_id);
}
break;
case TORATYPE_CLR:
if (newtrace_) {
sprintf(wrk_ + offset,
"-P tora -Pt 0x%x -Pd %d -Pa %f -Po %d -Pc CLEAR ",
ch->tc_type,
ch->tc_dst,
ch->tc_tau,
ch->tc_oid);
} else {
sprintf(wrk_ + offset, "[0x%x %d %f %d] (CLEAR)",
ch->tc_type,
ch->tc_dst,
ch->tc_tau,
ch->tc_oid);
}
break;
}
}
void
CMUTrace::format_aodv(Packet *p, int offset)
{
struct hdr_aodv *ah = HDR_AODV(p);
struct hdr_aodv_request *rq = HDR_AODV_REQUEST(p);
struct hdr_aodv_reply *rp = HDR_AODV_REPLY(p);
switch(ah->ah_type) {
case AODVTYPE_RREQ:
if (newtrace_) {
sprintf(wrk_ + offset,
"-P aodv -Pt 0x%x -Ph %d -Pb %d -Pd %d -Pds %d -Ps %d -Pss %d -Pc REQUEST ",
rq->rq_type,
rq->rq_hop_count,
rq->rq_bcast_id,
rq->rq_dst,
rq->rq_dst_seqno,
rq->rq_src,
rq->rq_src_seqno);
} else {
sprintf(wrk_ + offset,
"[0x%x %d %d [%d %d] [%d %d]] (REQUEST)",
rq->rq_type,
rq->rq_hop_count,
rq->rq_bcast_id,
rq->rq_dst,
rq->rq_dst_seqno,
rq->rq_src,
rq->rq_src_seqno);
}
break;
case AODVTYPE_RREP:
case AODVTYPE_UREP:
case AODVTYPE_HELLO:
if (newtrace_) {
sprintf(wrk_ + offset,
"-P aodv -Pt 0x%x -Ph %d -Pd %d -Pds %d -Pl %d -Pc %s ",
rp->rp_type,
rp->rp_hop_count,
rp->rp_dst,
rp->rp_dst_seqno,
rp->rp_lifetime,
rp->rp_type == AODVTYPE_RREP ? "REPLY" :
(rp->rp_type == AODVTYPE_UREP ? "UNSOLICITED REPLY" :
"HELLO"));
} else {
sprintf(wrk_ + offset,
"[0x%x %d [%d %d] %d] (%s)",
rp->rp_type,
rp->rp_hop_count,
rp->rp_dst,
rp->rp_dst_seqno,
rp->rp_lifetime,
rp->rp_type == AODVTYPE_RREP ? "REPLY" :
(rp->rp_type == AODVTYPE_UREP ? "UNSOLICITED REPLY" :
"HELLO"));
}
break;
default:
#ifdef WIN32
fprintf(stderr,
"CMUTrace::format_aodv: invalid AODV packet type\n");
#else
fprintf(stderr,
"%s: invalid AODV packet type\n", __FUNCTION__);
#endif
abort();
}
}
void
CMUTrace::nam_format(Packet *p, int offset)
{
Node* srcnode = 0 ;
Node* dstnode = 0 ;
Node* nextnode = 0 ;
struct hdr_cmn *ch = HDR_CMN(p);
struct hdr_ip *ih = HDR_IP(p);
char op = (char) type_;
char colors[32];
int next_hop = -1 ;
int dst = Address::instance().get_nodeaddr(ih->daddr());
nextnode = Node::get_node_by_address(ch->next_hop_);
if (nextnode) next_hop = nextnode->nodeid();
srcnode = Node::get_node_by_address(src_);
dstnode = Node::get_node_by_address(ch->next_hop_);
double distance = 0;
if ((srcnode) && (dstnode)) {
MobileNode* tmnode = (MobileNode*)srcnode;
MobileNode* rmnode = (MobileNode*)dstnode;
distance = tmnode->propdelay(rmnode) * 300000000 ;
}
double energy = -1;
double initenergy = -1;
//default value for changing node color with respect to energy depletion
double l1 = 0.5;
double l2 = 0.2;
if (srcnode) {
if (srcnode->energy_model()) {
energy = srcnode->energy();
initenergy = srcnode->initialenergy();
l1 = srcnode->energy_level1();
l2 = srcnode->energy_level2();
}
}
int energyLevel = 0 ;
double energyLeft = (double)(energy/initenergy) ;
if ((energyLeft <= 1 ) && (energyLeft >= l1 )) energyLevel = 3;
if ((energyLeft >= l2 ) && (energyLeft < l1 )) energyLevel = 2;
if ((energyLeft > 0 ) && (energyLeft < l2 )) energyLevel = 1;
if (energyLevel == 0) strcpy(colors,"-c black -o red");
else if (energyLevel == 1) strcpy(colors,"-c red -o yellow");
else if (energyLevel == 2) strcpy(colors,"-c yellow -o green");
else if (energyLevel == 3) strcpy(colors,"-c green -o black");
// convert to nam format
if (op == 's') op = 'h' ;
if (op == 'D') op = 'd' ;
if (op == 'h') {
sprintf(nwrk_ ,
"+ -t %.9f -s %d -d %d -p %s -e %d -c 2 -a 0 -i %d -k %3s ",
Scheduler::instance().clock(),
src_, // this node
next_hop,
packet_info.name(ch->ptype()),
ch->size(),
ch->uid(),
tracename);
offset = strlen(nwrk_);
namdump();
sprintf(nwrk_ ,
"- -t %.9f -s %d -d %d -p %s -e %d -c 2 -a 0 -i %d -k %3s",
Scheduler::instance().clock(),
src_, // this node
next_hop,
packet_info.name(ch->ptype()),
ch->size(),
ch->uid(),
tracename);
offset = strlen(nwrk_);
namdump();
}
// if nodes are too far from each other
// nam won't dump SEND event 'cuz it's
// gonna be dropped later anyway
// this value 250 is pre-calculated by using
// two-ray ground refelction model with fixed
// transmission power 3.652e-10
// if ((type_ == SEND) && (distance > 250 )) return ;
if(tracetype == TR_ROUTER && type_ == RECV && dst != -1 ) return ;
if(type_ == RECV && dst == -1 )dst = src_ ; //broadcasting event
if (energy != -1) { //energy model being turned on
if (nodeColor[src_] != energyLevel ) { //only dump it when node
sprintf(nwrk_ , //color change
"n -t %.9f -s %d -S COLOR %s",
Scheduler::instance().clock(),
src_, // this node
colors);
offset = strlen(nwrk_);
namdump();
nodeColor[src_] = energyLevel ;
}
}
sprintf(nwrk_ ,
"%c -t %.9f -s %d -d %d -p %s -e %d -c 2 -a 0 -i %d -k %3s",
op,
Scheduler::instance().clock(),
src_, // this node
next_hop,
packet_info.name(ch->ptype()),
ch->size(),
ch->uid(),
tracename);
offset = strlen(nwrk_);
namdump();
}
void CMUTrace::format(Packet* p, const char *why)
{
hdr_cmn *ch = HDR_CMN(p);
int offset = 0;
/*
* Log the MAC Header
*/
format_mac(p, why, offset);
#ifdef NAM_TRACE
if (namChan_) nam_format(p, offset);
#endif
offset = strlen(wrk_);
switch(ch->ptype()) {
case PT_MAC:
break;
case PT_ARP:
format_arp(p, offset);
break;
default:
format_ip(p, offset);
offset = strlen(wrk_);
switch(ch->ptype()) {
case PT_AODV:
format_aodv(p, offset);
break;
case PT_TORA:
format_tora(p, offset);
break;
case PT_IMEP:
format_imep(p, offset);
break;
case PT_DSR:
format_dsr(p, offset);
break;
case PT_MESSAGE:
case PT_UDP:
format_msg(p, offset);
break;
case PT_TCP:
case PT_ACK:
format_tcp(p, offset);
break;
case PT_CBR:
format_rtp(p, offset);
break;
default:
fprintf(stderr, "%s - invalid packet type (%s).\n",
__PRETTY_FUNCTION__, packet_info.name(ch->ptype()));
exit(1);
}
}
}
int
CMUTrace::command(int argc, const char*const* argv)
{
if(argc == 3) {
if(strcmp(argv[1], "node") == 0) {
node_ = (MobileNode*) TclObject::lookup(argv[2]);
if(node_ == 0)
return TCL_ERROR;
return TCL_OK;
}
if (strcmp(argv[1], "newtrace") == 0) {
newtrace_ = atoi(argv[2]);
return TCL_OK;
}
}
return Trace::command(argc, argv);
}
/*ARGSUSED*/
void
CMUTrace::recv(Packet *p, Handler *h)
{
if (!node_energy()) {
Packet::free(p);
return;
}
//struct hdr_ip *ih = HDR_IP(p);
// hack the IP address to convert pkt format to hostid format
// for now until port ids are removed from IP address. -Padma.
// int src = Address::instance().get_nodeaddr(ih->saddr());
assert(initialized());
/*
* Agent Trace "stamp" the packet with the optimal route on
* sending.
*/
if(tracetype == TR_AGENT && type_ == SEND) {
//assert(src_ == src);
God::instance()->stampPacket(p);
}
#if 0
/*
* When the originator of a packet drops the packet, it may or may
* not have been stamped by GOD. Stamp it before logging the
* information.
*/
if(src_ == src && type_ == DROP) {
God::instance()->stampPacket(p);
}
#endif
format(p, "---");
dump();
//namdump();
if(target_ == 0)
Packet::free(p);
else
send(p, h);
}
void
CMUTrace::recv(Packet *p, const char* why)
{
assert(initialized() && type_ == DROP);
if (!node_energy()) {
Packet::free(p);
return;
}
#if 0
/*
* When the originator of a packet drops the packet, it may or may
* not have been stamped by GOD. Stamp it before logging the
* information.
*/
if(src_ == ih->saddr()) {
God::instance()->stampPacket(p);
}
#endif
format(p, why);
dump();
//namdump();
Packet::free(p);
}
int
CMUTrace::node_energy()
{
Node* thisnode = Node::get_node_by_address(src_);
double energy = 1;
if (thisnode) {
if (thisnode->energy_model()) {
energy = thisnode->energy();
}
}
if (energy > 0) return 1;
//printf("DEBUG: node %d dropping pkts due to energy = 0\n", src_);
return 0;
}
codeword.cc
/*
* (c) 1997-98 StarBurst Communications Inc.
*
* THIS SOFTWARE IS PROVIDED BY THE 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 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.
*
* Author: Christoph Haenle, chris@cs.vu.nl
* File: codeword.cc
* Last change: Dec 07, 1998
*
* This software may freely be used only for non-commercial purposes
*/
#include "config.h" // for memcpy...
#include "codeword.h"
#include
#include // due to definition of NULL
#include // due to printf
static unsigned char minbit_array[256];
static unsigned char bitcount_array[256];
// because of the lack of static initialization on class-basis in C++,
// this is a workaround.
static int dummy = initialize_codeword();
// list of primitive polynomials over GF(2).
CW_PATTERN_t Codeword::primitive_polynomial[Codeword::MAX_DEGREE+1] = {
"1", // 1 (group size 1)
"11", // 1+x
"111", // 1+x+x^2
"1011", // 1+x+x^3 (group size 4)
"10011", // 1+x+x^4
"100101", // 1+x^2+x^5
"1100111", // 1+x+x^2+x^5+x^6
"11100101", // 1+x^2+x^5+x^6+x^7 (group size 8)
"101101001", // 1+x^3+x^5+x^6+x^8
"1100010011", // 1+x^1+x^4+x^8+x^9
"11101001101", // 1+x^2+x^3+x^6+x^8+x^9+x^10
"110010011011", // 1+x+x^3+x^4+x^7+x^10+x^11
"1001010111001", // 1+x^3+x^4+x^5+x^7+x^9+x^12
"11000001111001", // 1+x^3+x^4+x^5+x^6+x^12+x^13
"110100100101111", // 1+x+x^2+x^3+x^5+x^8+x^11+x^13+x^14
"1100010011000101", // 1+x^2+x^6+x^7+x^10+x^14+x^15 (group size 16)
"11101010001010101", // 1+x^2+x^4+x^6+x^10+x^12+x^14+x^15+x^16
"101101011001011011", // 1+x+x^3+x^4+x^6+x^9+x^10+x^12+x^14+x^15+x^17
"1101101001010011011", // 1+x+x^3+x^4+x^7+x^9+x^12+x^14+x^15+x^17+x^18
"11100101101010010011",
"101010101101010010011", // 1+x+x^4+x^7+x^9+x^11+x^12+x^14+x^16+x^18+x^20
"1100010101110010011001",
"10100100110001101011001",
"101001011000010110110111",
"1010101010100101110110001",
"11001000110011010101011001",
"100011000010001110100111011", // group size: 27
"1100110100111010101010100011", // 1+x+x^5+x^7+x^9+x^11+x^13+x^15+x^16+x^17+x^20+x^22+x^23+x^26+x^27
"10100110100111010101010100011", // 1+x+x^5+x^7+x^9+x^11+x^13+x^15+x^16+x^17+x^20+x^22+x^23+x^26+x^28
"100110011000111010101010100011", // 1+x+x^5+x^7+x^9+x^11+x^13+x^15+x^16+x^17+x^21+x^22+x^25+x^26+x^29
"1010100101000111010110100100011", // 1+x+x^5+x^8+x^10+x^11+x^13+x^15+x^16+x^17+x^21+x^23+x^26+x^28+x^30
"10110010100101100011010011011011", // 1+x+x^3+x^4+x^6+x^7+x^10+x^12+x^13+x^17+x^18+x^20+x^23+x^25+x^28+x^29+x^31 (group size 32)
"0", // invalid, group size currently not supported
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"0",
"1101100011100110100100111001001010110010100101010100100111011011" // 1+x+x^3+x^4+x^6+x^7+x^8+x^11+x^14+x^16+x^18+x^20+x^23+x^25+x^28+x^29+x^31+x^33+x^36+x^39+x^40+x^41+x^44+x^47+x^49+x^50+x^53+x^54+x^55+x^59+x^60+x^62+x^63 (group size 64)
};
// to generate primitive polynomials, see for example
// http://www-kommsys.fernuni-hagen.de/~rieke/primitiv/test.phtml.en
Codeword::Codeword() :
k(1),
n(1),
cw_index(0),
cw_pat(0),
cw_saved(0)
{
}
void Codeword::setSourceWordLen(unsigned long k_)
{
k = k_;
n = ((CW_PATTERN_t) 1) << (k-1);
cw_index = (unsigned long) 0;
cw_pat = 1;
cw_saved = 0;
assert(k <= 8 * sizeof(CW_PATTERN_t));
if(k > MAX_DEGREE + 1 || primitive_polynomial[k-1] == 0) {
fprintf(stderr, "codeword.cc: sorry, the group size %lu is not supported.\n",
(unsigned long) k);
exit(0);
}
if(k > 8 * sizeof(CW_PATTERN_t)) {
fprintf(stderr, "codeword.cc: sorry, the group size %lu that you selected\n",
(unsigned long) k);
fprintf(stderr, "requires a datatype of at least %lu bits. To achieve this,\n",
(unsigned long) k);
fprintf(stderr, "adjust Codeword::size in file \"codeword.h\" accordingly.\n");
exit(0);
}
}
// systematic code, with optimization hack, originally derived from Reed-Muller codes of
// order 1.
CW_PATTERN_t Codeword::getNextCwPat()
{
assert(cw_pat != 0); // or else prior call to setSourceWordLen(...) has not been made
CW_PATTERN_t ret;
CW_PATTERN_t cw_tmp;
// step 1
if(cw_index != 0) {
cw_pat <<= 1;
if(cw_pat >= n) {
cw_pat ^= primitive_polynomial[k-1];
assert(cw_pat < n);
}
}
// step 2
if(cw_index == k) { cw_saved = cw_pat; cw_tmp = n-1; }
else if(cw_pat == n-1) cw_tmp = cw_saved;
else cw_tmp = cw_pat;
// step 3
if(cw_index < k) ret = (CW_PATTERN_t) 1 << cw_index;
else ret = (cw_tmp << 1) | 1;
// step 4
cw_index = (cw_index + 1) & (n-1); // "& (n-1)" is equivalent to "% n" here
return ret;
}
void init_bitcount_array(unsigned char* arr, unsigned int nb_bits)
{
unsigned long bitcount_array_size = ((unsigned long) 1) << nb_bits;
unsigned long i;
unsigned int bit;
int count;
assert(sizeof(unsigned long) == 4); // we need this for the following
assert(0 < nb_bits && nb_bits <= 32); // or else this function must be revised
assert(arr != NULL);
for(i = 0; i < bitcount_array_size; ++i) {
count = 0;
// to avoid warning: unsigned value >= 0 is always 1
// for(bit = 0; 0 <= bit && bit < nb_bits; bit++) {
for(bit = 0; bit < nb_bits; bit++) {
if(i & ((unsigned long) 1 << bit)) {
count++;
}
}
arr[i] = count;
}
}
void init_minbit_array(unsigned char* arr, unsigned int minbits)
{
unsigned int minbit_array_size = (unsigned int) 1 << minbits;
unsigned int i;
unsigned int bit;
assert(minbits == 8); // or else minbit(...) needs a revision!
assert(arr != NULL);
for(i = 0; i < minbit_array_size; ++i) {
// to avoid warning: unsigned value >= 0 is always 1
//for(bit = 0; 0 <= bit && bit < minbits; bit++) {
for(bit = 0; bit < minbits; bit++) {
if(i & ((unsigned int) 1 << bit)) {
arr[i] = (unsigned char) bit;
break;
}
}
}
}
// returns bit position of least significant bit in cw_pat
unsigned int minbit(unsigned long val)
{
unsigned int i;
assert(val);
for(i = 0; val; ++i) {
assert(i < sizeof(CW_PATTERN_t));
if(val & 255) {
return minbit_array[(unsigned int) (val & 255)] + 8*i;
}
val >>= 8;
}
assert(false); // value is 0
return 0; // compiler, be quiet.
}
// counts number of bits in cw_pat
unsigned int bitcount(unsigned long val)
{
unsigned int res = 0;
while(val) {
res += bitcount_array[(unsigned int) (val & 255)];
val >>= 8;
}
return res;
}
/* function to be called once at the beginning */
int initialize_codeword()
{
assert(sizeof(minbit_array) / sizeof(unsigned char) == 256);
init_minbit_array(minbit_array, 8);
init_bitcount_array(bitcount_array, 8);
return 0;
}
// constructor:
ExtraLongUInt::ExtraLongUInt()
{
memset(value, 0, sizeof(value));
}
// constructor: create from "binary-string" (string of 0's and 1's):
ExtraLongUInt::ExtraLongUInt(const char* val)
{
const unsigned int len = strlen(val);
char digit;
assert(len <= 8 * sizeof(value)); // or else overflow
memset(value, 0, sizeof(value));
for(unsigned int i = 0; i < len; ++i) {
digit = val[len - 1 - i];
assert(digit == '0' || digit == '1'); // or else val is not a binary number
if(digit == '1') {
value[i / (8 * sizeof(unsigned long))] |=
(unsigned long) 1 << (i % (8 * sizeof(unsigned long)));
}
}
}
// constructor:
ExtraLongUInt::ExtraLongUInt(int val)
{
assert(val >= 0);
memset(value, 0, sizeof(value));
value[0] = val;
}
// constructor:
ExtraLongUInt::ExtraLongUInt(unsigned int val)
{
memset(value, 0, sizeof(value));
value[0] = val;
}
// constructor:
ExtraLongUInt::ExtraLongUInt(unsigned long val)
{
memset(value, 0, sizeof(value));
value[0] = val;
}
bool ExtraLongUInt::operator == (const ExtraLongUInt& other) const
{
for(unsigned int i = 0; i < sizeof(value) / sizeof(unsigned long); ++i) {
if(value[i] != other.value[i]) {
return false;
}
}
return true;
}
bool ExtraLongUInt::operator != (const ExtraLongUInt& other) const
{
return (*this == other) ? false : true;
}
bool ExtraLongUInt::operator < (const ExtraLongUInt& other) const
{
unsigned int i;
for(i = sizeof(value) / sizeof(unsigned long) - 1;
value[i] == other.value[i] && i > 0; --i) {
}
return value[i] < other.value[i] ? true : false;
}
bool ExtraLongUInt::operator > (const ExtraLongUInt& other) const
{
unsigned int i;
for(i = sizeof(value) / sizeof(unsigned long) - 1;
value[i] == other.value[i] && i > 0; --i) {
}
return value[i] > other.value[i] ? true : false;
}
bool ExtraLongUInt::operator <= (const ExtraLongUInt& other) const
{
return (*this > other) ? false : true;
}
bool ExtraLongUInt::operator >= (const ExtraLongUInt& other) const
{
return (*this < other) ? false : true;
}
ExtraLongUInt ExtraLongUInt::operator + (const ExtraLongUInt& other) const
{
ExtraLongUInt res = 0;
unsigned long c = 0;
const int shift = 8 * sizeof(unsigned long) - 1;
const unsigned long msb_mask = (unsigned long) 1 << shift;
const unsigned long lsbs_mask = ~msb_mask;
unsigned long x, y;
for(unsigned int i = 0; i < sizeof(value) / sizeof(unsigned long); ++i) {
x = value[i];
y = other.value[i];
res.value[i] = (x & lsbs_mask) + (y & lsbs_mask) + c;
c = ((x >> shift) + (y >> shift) + (res.value[i] >> shift)) >> 1;
res.value[i] ^= (x & msb_mask) ^ (y & msb_mask);
}
return res;
}
ExtraLongUInt ExtraLongUInt::operator - (const ExtraLongUInt& other) const
{
return *this + ~other + 1;
}
ExtraLongUInt ExtraLongUInt::operator & (const ExtraLongUInt& other) const
{
ExtraLongUInt res;
for(unsigned int i = 0; i < sizeof(value) / sizeof(unsigned long); ++i) {
res.value[i] = value[i] & other.value[i];
}
return res;
}
ExtraLongUInt ExtraLongUInt::operator | (const ExtraLongUInt& other) const
{
ExtraLongUInt res;
for(unsigned int i = 0; i < sizeof(value) / sizeof(unsigned long); ++i) {
res.value[i] = value[i] | other.value[i];
}
return res;
}
ExtraLongUInt ExtraLongUInt::operator ^ (const ExtraLongUInt& other) const
{
ExtraLongUInt res;
for(unsigned int i = 0; i < sizeof(value) / sizeof(unsigned long); ++i) {
res.value[i] = value[i] ^ other.value[i];
}
return res;
}
ExtraLongUInt ExtraLongUInt::operator ~() const
{
ExtraLongUInt res;
for(unsigned int i = 0; i < sizeof(value) / sizeof(unsigned long); ++i) {
res.value[i] = ~value[i];
}
return res;
}
bool ExtraLongUInt::operator ! () const
{
for(unsigned int i = 0; i < sizeof(value) / sizeof(unsigned long); ++i) {
if(value[i]) {
return false;
}
}
return true;
}
ExtraLongUInt ExtraLongUInt::operator << (unsigned int bits) const
{
ExtraLongUInt res = 0;
const int index = bits / (8 * sizeof(unsigned long));
const int shifts = bits % (8 * sizeof(unsigned long));
unsigned int i;
if(sizeof(value) > index * sizeof(unsigned long)) {
if(shifts == 0) { // this is because (1 >> 32) is not 0 (gcc)
memcpy(&res.value[index],
value,
sizeof(value) - index * sizeof(unsigned long));
} else {
const unsigned long mask = (~(unsigned long) 0) >> shifts;
assert(sizeof(value) >= sizeof(unsigned long));
res.value[index] = (value[0] & mask) << shifts;
for(i = index + 1; i < sizeof(value) / sizeof(unsigned long); ++i) {
res.value[i] = ((value[i - index ] & mask) << shifts) |
(value[i - index-1] >> ((8 * sizeof(unsigned long)) - shifts));
}
}
}
return res;
}
ExtraLongUInt ExtraLongUInt::operator >> (unsigned int bits) const
{
ExtraLongUInt res = 0;
const int index = bits / (8 * sizeof(unsigned long));
const int shifts = bits % (8 * sizeof(unsigned long));
unsigned int i;
if(sizeof(value) > index * sizeof(unsigned long)) {
if(shifts == 0) { // this is because (1 >> 32) is not 0 (gcc)
memcpy(res.value,
&value[index],
sizeof(value) - index * sizeof(unsigned long));
} else {
const unsigned long mask = (~(unsigned long) 0) >> (8 * sizeof(unsigned long) - shifts);
assert(sizeof(value) >= sizeof(unsigned long));
for(i = 0; i < sizeof(value) / sizeof(unsigned long) - index - 1; ++i) {
res.value[i] = (value[i+index ] >> shifts) |
((value[i+index+1] & mask) << ((8 * sizeof(unsigned long)) - shifts));
}
res.value[i] = value[i+index] >> shifts;
}
}
return res;
}
ExtraLongUInt ExtraLongUInt::operator << (const ExtraLongUInt& bits) const
{
return *this << bits.value[0];
}
ExtraLongUInt ExtraLongUInt::operator >> (const ExtraLongUInt& bits) const
{
return *this >> bits.value[0];
}
const ExtraLongUInt& ExtraLongUInt::operator <<= (unsigned int bits)
{
*this = *this << bits;
return *this;
}
const ExtraLongUInt& ExtraLongUInt::operator >>= (unsigned int bits)
{
*this = *this >> bits;
return *this;
}
const ExtraLongUInt& ExtraLongUInt::operator &= (const ExtraLongUInt& other)
{
*this = *this & other;
return *this;
}
const ExtraLongUInt& ExtraLongUInt::operator |= (const ExtraLongUInt& other)
{
*this = *this | other;
return *this;
}
const ExtraLongUInt& ExtraLongUInt::operator ^= (const ExtraLongUInt& other)
{
*this = *this ^ other;
return *this;
}
void ExtraLongUInt::print(char* buf) const
{
int i, fin;
for(i = 8 * sizeof(value) - 1; !(value[i / (8 * sizeof(unsigned long))] &
((unsigned long) 1 << (i % (8 * sizeof(unsigned long))))) && i > 0; --i)
;
for(fin = i; i >= 0; --i) {
buf[fin-i] = (value[i / (8 * sizeof(unsigned long))] &
(unsigned long) 1 << (i % (8 * sizeof(unsigned long)))) ?
'1' : '0';
}
buf[fin+1] = '\0';
}
unsigned int ExtraLongUInt::bitcount() const
{
unsigned int res = 0;
for(unsigned int i = 0; i < sizeof(value) / sizeof(unsigned long); ++i) {
res += ::bitcount(value[i]);
}
return res;
}
unsigned int ExtraLongUInt::minbit() const
{
for(unsigned int i = 0; i < sizeof(value) / sizeof(unsigned long); ++i) {
if(value[i]) {
return i * 8 * sizeof(unsigned long) + ::minbit(value[i]);
}
}
assert(false); // value is 0
return 0; // compiler, be quiet.
}
// returns bit position of least significant bit in cw_pat
unsigned int minbit(const ExtraLongUInt& val)
{
return val.minbit();
}
// returns bit position of least significant bit in cw_pat
unsigned int bitcount(const ExtraLongUInt& val)
{
return val.bitcount();
}
connector.cc
/* -*- 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 MASH Research
* Group at the University of California Berkeley.
* 4. Neither the name of the University nor of the Research Group 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.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ ";
#endif
#include "packet.h"
#include "connector.h"
static class ConnectorClass : public TclClass {
public:
ConnectorClass() : TclClass("Connector") {}
TclObject* create(int, const char*const*) {
return (new Connector);
}
} class_connector;
Connector::Connector() : target_(0), drop_(0)
{
}
int Connector::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
/*XXX*/
if (argc == 2) {
if (strcmp(argv[1], "target") == 0) {
if (target_ != 0)
tcl.result(target_->name());
return (TCL_OK);
}
if (strcmp(argv[1], "drop-target") == 0) {
if (drop_ != 0)
tcl.resultf("%s", drop_->name());
return (TCL_OK);
}
if (strcmp(argv[1], "isDynamic") == 0) {
return TCL_OK;
}
}
else if (argc == 3) {
if (strcmp(argv[1], "target") == 0) {
if (*argv[2] == '0') {
target_ = 0;
return (TCL_OK);
}
target_ = (NsObject*)TclObject::lookup(argv[2]);
if (target_ == 0) {
tcl.resultf("no such object %s", argv[2]);
return (TCL_ERROR);
}
return (TCL_OK);
}
if (strcmp(argv[1], "drop-target") == 0) {
drop_ = (NsObject*)TclObject::lookup(argv[2]);
if (drop_ == 0) {
tcl.resultf("no object %s", argv[2]);
return (TCL_ERROR);
}
return (TCL_OK);
}
}
return (NsObject::command(argc, argv));
}
void Connector::recv(Packet* p, Handler* h)
{
send(p, h);
}
void Connector::drop(Packet* p)
{
if (drop_ != 0)
drop_->recv(p);
else
Packet::free(p);
}
void Connector::drop(Packet* p, const char *s)
{
if (drop_ != 0)
drop_->recv(p, s);
else
Packet::free(p);
}
consrcvr.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) Xerox Corporation 1997. All rights reserved.
*
* License is granted to copy, to use, and to make and to use derivative
* works for research and evaluation purposes, provided that Xerox is
* acknowledged in all documentation pertaining to any such copy or
* derivative work. Xerox grants no other licenses expressed or
* implied. The Xerox trade name should not be used in any advertising
* without its written permission.
*
* XEROX CORPORATION MAKES NO REPRESENTATIONS CONCERNING EITHER THE
* MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE
* FOR ANY PARTICULAR PURPOSE. The software is provided "as is" without
* express or implied warranty of any kind.
*
* These notices must be retained in any copies of any part of this
* software.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$";
#endif
#include "agent.h"
#include "config.h"
#include "tclcl.h"
#include "packet.h"
#include "rtp.h"
#include "adaptive-receiver.h"
#ifndef WIN32
// VC 5.0 doesn't have this
#include
#endif
//#define CONS_OFFSET 0.025*SAMPLERATE
#define CONS_OFFSET 200
class ConsRcvr : public AdaptiveRcvr {
public:
ConsRcvr();
protected:
int adapt(Packet *pkt, u_int32_t time);
int offset_;
};
static class ConsRcvrClass : public TclClass {
public:
ConsRcvrClass() : TclClass("Agent/ConsRcvr") {}
TclObject* create(int, const char*const*) {
return (new ConsRcvr());
}
} class_cons_rcvr;
ConsRcvr::ConsRcvr() : offset_(CONS_OFFSET)
{
}
int ConsRcvr::adapt(Packet *pkt, u_int32_t local_clock)
{
int delay;
hdr_cmn* ch = (hdr_cmn*)pkt->access(off_cmn_);
register u_int32_t tstamp = (int)ch->timestamp();
if (((tstamp+offset_) < local_clock) || (offset_ == -1)) {
/*increase the offset */
if (offset_ < (int)(local_clock-(tstamp+offset_)))
offset_ += local_clock -(tstamp+offset_);
else
offset_ += offset_;
}
delay=offset_-(local_clock-tstamp);
return delay;
}
ctrMcast.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* ctrMcast.cc
* Copyright (C) 1997 by USC/ISI
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation, advertising
* materials, and other materials related to such distribution and use
* acknowledge that the software was developed by the University of
* Southern California, Information Sciences Institute. The name of the
* University may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* Contributed by Polly Huang (USC/ISI), http://www-scf.usc.edu/~bhuang
*
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (USC/ISI)";
#endif
#include "agent.h"
#include "ip.h"
#include "ctrMcast.h"
int hdr_CtrMcast::offset_;
static class CtrMcastHeaderClass : public PacketHeaderClass {
public:
CtrMcastHeaderClass() : PacketHeaderClass("PacketHeader/CtrMcast",
sizeof(hdr_CtrMcast)) {
bind_offset(&hdr_CtrMcast::offset_);
}
} class_CtrMcast_hdr;
class CtrMcastEncap : public Agent {
public:
CtrMcastEncap() : Agent(PT_CtrMcast_Encap) {
bind("off_CtrMcast_", &off_CtrMcast_);
}
int command(int argc, const char*const* argv);
void recv(Packet* p, Handler*);
protected:
int off_CtrMcast_;
};
class CtrMcastDecap : public Agent {
public:
CtrMcastDecap() : Agent(PT_CtrMcast_Decap) {
bind("off_CtrMcast_", &off_CtrMcast_);
}
int command(int argc, const char*const* argv);
void recv(Packet* p, Handler*);
protected:
int off_CtrMcast_;
};
static class CtrMcastEncapclass : public TclClass {
public:
CtrMcastEncapclass() : TclClass("Agent/CtrMcast/Encap") {}
TclObject* create(int, const char*const*) {
return (new CtrMcastEncap);
}
} class_CtrMcastEncap;
static class CtrMcastDecapclass : public TclClass {
public:
CtrMcastDecapclass() : TclClass("Agent/CtrMcast/Decap") {}
TclObject* create(int, const char*const*) {
return (new CtrMcastDecap);
}
} class_CtrMcastDecap;
int CtrMcastEncap::command(int argc, const char*const* argv)
{
return Agent::command(argc, argv);
}
int CtrMcastDecap::command(int argc, const char*const* argv)
{
return Agent::command(argc, argv);
}
void CtrMcastEncap::recv(Packet* p, Handler*)
{
hdr_CtrMcast* ch = (hdr_CtrMcast*)p->access(off_CtrMcast_);
hdr_ip* ih = (hdr_ip*)p->access(off_ip_);
ch->src() = ih->saddr();
ch->group() = ih->daddr();
ch->flowid() = ih->flowid();
ih->saddr() = addr();
ih->sport() = port();
ih->daddr() = daddr();
ih->dport() = dport();
ih->flowid() = fid_;
target_->recv(p);
}
void CtrMcastDecap::recv(Packet* p, Handler*)
{
hdr_CtrMcast* ch = (hdr_CtrMcast*)p->access(off_CtrMcast_);
hdr_ip* ih = (hdr_ip*)p->access(off_ip_);
ih->saddr() = ch->src();
ih->daddr() = ch->group();
ih->flowid() = ch->flowid();
target_->recv(p);
}
delay.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1996-1997 The 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 Network Research
* Group at Lawrence Berkeley National Laboratory.
* 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.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (LBL)";
#endif
#include "delay.h"
#include "mcast_ctrl.h"
#include "ctrMcast.h"
static class LinkDelayClass : public TclClass {
public:
LinkDelayClass() : TclClass("DelayLink") {}
TclObject* create(int /* argc */, const char*const* /* argv */) {
return (new LinkDelay);
}
} class_delay_link;
LinkDelay::LinkDelay() : dynamic_(0), itq_(0)
{
bind_bw("bandwidth_", &bandwidth_);
bind_time("delay_", &delay_);
}
int LinkDelay::command(int argc, const char*const* argv)
{
if (argc == 2) {
if (strcmp(argv[1], "isDynamic") == 0) {
dynamic_ = 1;
itq_ = new PacketQueue();
return TCL_OK;
}
} else if (argc == 6) {
if (strcmp(argv[1], "pktintran") == 0) {
int src = atoi(argv[2]);
int grp = atoi(argv[3]);
int from = atoi(argv[4]);
int to = atoi(argv[5]);
pktintran (src, grp);
Tcl::instance().evalf("%s puttrace %d %d %d %d %d %d %d %d", name(), total_[0], total_[1], total_[2], total_[3], src, grp, from, to);
return TCL_OK;
}
}
return Connector::command(argc, argv);
}
void LinkDelay::recv(Packet* p, Handler* h)
{
double txt = txtime(p);
Scheduler& s = Scheduler::instance();
if (dynamic_) {
Event* e = (Event*)p;
e->time_= txt + delay_;
itq_->enque(p); // for convinience, use a queue to store packets in transit
s.schedule(this, p, txt + delay_);
} else {
s.schedule(target_, p, txt + delay_);
}
s.schedule(h, &intr_, txt);
}
void LinkDelay::send(Packet* p, Handler*)
{
target_->recv(p, (Handler*) NULL);
}
void LinkDelay::reset()
{
Scheduler& s= Scheduler::instance();
if (itq_ && itq_->length()) {
Packet *np;
// walk through packets currently in transit and kill 'em
while ((np = itq_->deque()) != 0) {
s.cancel(np);
drop(np);
}
}
}
void LinkDelay::handle(Event* e)
{
Packet *p = itq_->deque();
assert(p->time_ == e->time_);
send(p, (Handler*) NULL);
}
void LinkDelay::pktintran(int src, int group)
{
int reg = 1;
int prune = 30;
int graft = 31;
int data = 0;
for (int i=0; i<4; i++) {
total_[i] = 0;
}
if (! dynamic_)
return;
int len = itq_->length();
while (len) {
len--;
Packet* p = itq_->lookup(len);
hdr_ip* iph = hdr_ip::access(p);
if (iph->flowid() == prune) {
if (iph->saddr() == src && iph->daddr() == group) {
total_[0]++;
}
} else if (iph->flowid() == graft) {
if (iph->saddr() == src && iph->daddr() == group) {
total_[1]++;
}
} else if (iph->flowid() == reg) {
hdr_CtrMcast* ch = hdr_CtrMcast::access(p);
if (ch->src() == src+1 && ch->group() == group) {
total_[2]++;
}
} else if (iph->flowid() == data) {
if (iph->saddr() == src+1 && iph->daddr() == group) {
total_[3]++;
}
}
}
//printf ("%f %d %d %d %d\n", Scheduler::instance().clock(), total_[0], total_[1], total_[2],total_[3]);
}
delaymodel.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* delaymodel.cc
* Copyright (C) 1997 by USC/ISI
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation, advertising
* materials, and other materials related to such distribution and use
* acknowledge that the software was developed by the University of
* Southern California, Information Sciences Institute. The name of the
* University may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* Contributed by Polly Huang (USC/ISI), http://www-scf.usc.edu/~bhuang
*
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (UCB)";
#endif
#include "packet.h"
#include "delaymodel.h"
static class DelayModelClass : public TclClass {
public:
DelayModelClass() : TclClass("DelayModel") {}
TclObject* create(int, const char*const*) {
return (new DelayModel);
}
} class_delaymodel;
DelayModel::DelayModel() : Connector(), bandwidth_(0)
{
}
int DelayModel::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
if (argc == 3) {
if (strcmp(argv[1], "ranvar") == 0) {
ranvar_ = (RandomVariable*) TclObject::lookup(argv[2]);
return (TCL_OK);
} else if (strcmp(argv[1], "bandwidth") == 0) {
bandwidth_ = atof(argv[2]);
return (TCL_OK);
}
} else if (argc == 2) {
if (strcmp(argv[1], "ranvar") == 0) {
tcl.resultf("%s", ranvar_->name());
return (TCL_OK);
}
}
return Connector::command(argc, argv);
}
void DelayModel::recv(Packet* p, Handler*)
{
double delay = ranvar_->value();
//static int tmp = 0;
double txt = txtime(p);
Scheduler& s = Scheduler::instance();
//printf ("trans %f, delay %f\n", txt, delay);
s.schedule(target_, p, txt + delay);
//s.schedule(h, &intr_, txt);
}
dem.cc
/* -*- 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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*/
/* Ported from CMU/Monarch's code, nov'98 -Padma.*/
#include "config.h"
#include
#include
#include
#include
#include
int
DEMFile::open()
{
if((demfile = fopen(fname, "r")) == 0)
return EINVAL;
else
return 0;
}
int
DEMFile::read_int()
{
read_field();
return (atoi(tempbuf));
}
float
DEMFile::read_float()
{
read_field();
return (atof(tempbuf));
}
void
DEMFile::read_field()
{
int i;
char ch;
bzero(tempbuf, sizeof(tempbuf));
while(! feof(demfile) && isspace((ch = fgetc(demfile))) );
tempbuf[0] = ch;
for(i = 1; ; i++) {
if(feof(demfile)) {
fprintf(stderr, "%s: EOF\n", __PRETTY_FUNCTION__);
exit(1);
}
tempbuf[i] = fgetc(demfile);
if(tempbuf[i] == 'D')
tempbuf[i] = 'E';
if(isspace(tempbuf[i]))
break;
}
}
void
DEMFile::resolution(double &r)
{
r = 5.0;
}
void
DEMFile::range(double &x, double &y)
{
#if 0
int i;
double minx = 0.0, miny = 0.0, maxx = 0.0, maxy = 0.0;
for(i = 0; i < 4; i++) {
if(minx == 0.0 || minx > a.corners[i][0]) {
minx = a.corners[i][0];
miny = a.corners[i][1];
}
else if(minx == a.corners[i][0] && miny > a.corners[i][1]) {
miny = a.corners[i][1];
}
if(maxx == 0.0 || maxx < a.corners[i][0]) {
maxx = a.corners[i][0];
maxy = a.corners[i][1];
}
else if(maxx == a.corners[i][0] && maxy < a.corners[i][1]) {
maxy = a.corners[i][1];
}
}
x = maxx - minx;
y = maxy - miny;
#else
x = y = (double) a.cols - 1;
#endif
}
/* ======================================================================
Returns a pointer to the "grid".
====================================================================== */
int*
DEMFile::process()
{
int i, j, offset = 0;
char ch;
if(fname == 0)
return 0;
if(open())
return 0;
/* ============================================================
A Record
============================================================ */
bzero(a.q_name, sizeof(a.q_name));
for(i = 0; ! feof(demfile) && i < (int) sizeof(a.q_name) - 1; i++) {
ch = fgetc(demfile);
if(! isspace(ch)) {
a.q_name[offset] = ch;
offset++;
}
else {
if(offset && ! isspace(a.q_name[offset-1])) {
a.q_name[offset] = ch;
offset++;
}
}
}
a.dl_code = read_int();
a.p_code = read_int();
a.pr_code = read_int();
a.z_code = read_int();
/* Map Projection Parameters */
for(i = 0; i < 15; i++)
a.p_parm[i] = read_float();
a.g_units = read_int();
a.e_units = read_int();
a.sides = read_int();
for(i = 0; i < 4; i++) {
a.corners[i][0] = read_float();
a.corners[i][1] = read_float();
}
a.min_elevation = read_float();
a.max_elevation = read_float();
a.angle = read_float();
#if 0
a.a_code = read_int();
a.x_res = read_float();
a.y_res = read_float();
a.z_res = read_float();
#else
read_field();
#endif
a.rows = read_int();
a.cols = read_int();
grid = (int*) malloc(sizeof(int) * a.cols * a.cols);
/* ============================================================
B Records
============================================================ */
for(int rows = 0; rows < a.cols; rows++) {
b.row_id = read_int();
b.col_id = read_int();
b.rows = read_int();
b.cols = read_int();
b.x_gpc = read_float();
b.y_gpc = read_float();
b.elevation = read_float();
b.min_elevation = read_float();
b.max_elevation = read_float();
i = rows * a.cols;
for(j = 0; j < b.rows; j++)
grid[i+j] = read_int();
}
return grid;
}
void
DEMFile::dump_ARecord()
{
int i;
fprintf(stdout, "*** A RECORD ***\n");
fprintf(stdout, "Quadrangle name: %s\n", a.q_name);
fprintf(stdout, "DEM Level code: %d\n", a.dl_code);
fprintf(stdout, "Pattern code: %d\n", a.p_code);
fprintf(stdout, "Planimetric reference system code: %d\n", a.pr_code);
fprintf(stdout, "Zone code: %d\n", a.z_code);
fprintf(stdout, "Map Projection Parameters:\n");
for(i = 0; i < 15; i++)
fprintf(stdout, " %f\n",
a.p_parm[i]);
fprintf(stdout, "Ground planimetric coordinates: %d\n", a.g_units);
fprintf(stdout, "Elevation coordinates: %d\n", a.e_units);
fprintf(stdout, "Sides: %d\n", a.sides);
fprintf(stdout, "Corners:\n");
for(i = 0; i < 4; i++)
fprintf(stdout, " %f, %f\n",
a.corners[i][0], a.corners[i][1]);
fprintf(stdout, "Min Elevation: %f\n", a.min_elevation);
fprintf(stdout, "Max Elevation: %f\n", a.max_elevation);
fprintf(stdout, "Clockwise angle: %f\n", a.angle);
fprintf(stdout, "Accuracy code: %d\n", a.a_code);
fprintf(stdout, "Spatial Resolution:\n");
fprintf(stdout, " %f (x)\n", a.x_res);
fprintf(stdout, " %f (y)\n", a.y_res);
fprintf(stdout, " %f (z)\n", a.z_res);
fprintf(stdout, "Rows: %d\n", a.rows);
fprintf(stdout, "Columns: %d\n", a.cols);
}
void
DEMFile::dump_BRecord()
{
fprintf(stdout, "*** B RECORD ***\n");
fprintf(stdout, "Row ID: %d\n", b.row_id);
fprintf(stdout, "Column ID: %d\n", b.col_id);
fprintf(stdout, "Rows: %d\n", b.rows);
fprintf(stdout, "Columns: %d\n", b.cols);
fprintf(stdout, "Ground planimetric coordinates: %f, %f\n",
b.x_gpc, b.y_gpc);
fprintf(stdout, "Elevation: %f\n", b.elevation);
fprintf(stdout, "Min Elevation: %f\n", b.min_elevation);
fprintf(stdout, "Max Elevation: %f\n", b.max_elevation);
}
void
DEMFile::dump_grid()
{
int i, j;
fprintf(stdout, "*** X,Y GRID ***\n");
for(i = 0; i < a.cols; i++) {
fprintf(stdout, "ROW %5d --- ", i);
for(j = 0; j < a.cols; j++) {
fprintf(stdout, "%5d ", grid[i*a.cols + j]);
}
fprintf(stdout, "\n");
}
}
#if 0
void main(int argc, char** argv)
{
DEMFile *F;
if(argc != 2)
exit(1);
F = new DEMFile(argv[1]);
F->process();
F->dump_grid();
}
#endif
drop-tail.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1994 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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (LBL)";
#endif
#include "drop-tail.h"
static class DropTailClass : public TclClass {
public:
DropTailClass() : TclClass("Queue/DropTail") {}
TclObject* create(int, const char*const*) {
return (new DropTail);
}
} class_drop_tail;
int
DropTail::command(int argc, const char*const* argv) {
if (argc == 3) {
if (!strcmp(argv[1], "packetqueue-attach")) {
delete q_;
if (!(q_ = (PacketQueue*) TclObject::lookup(argv[2])))
return (TCL_ERROR);
else {
pq_ = q_;
return (TCL_OK);
}
}
}
return Queue::command(argc, argv);
}
/*
* drop-tail
*/
void DropTail::enque(Packet* p)
{
q_->enque(p);
if (q_->length() >= qlim_) {
if (drop_front_) { /* remove from head of queue */
Packet *pp = q_->deque();
drop(pp);
} else {
q_->remove(p);
drop(p);
}
}
}
Packet* DropTail::deque()
{
return q_->deque();
}
drr.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) Xerox Corporation 1997. All rights reserved.
*
* License is granted to copy, to use, and to make and to use derivative
* works for research and evaluation purposes, provided that Xerox is
* acknowledged in all documentation pertaining to any such copy or derivative
* work. Xerox grants no other licenses expressed or implied. The Xerox trade
* name should not be used in any advertising without its written permission.
*
* XEROX CORPORATION MAKES NO REPRESENTATIONS CONCERNING EITHER THE
* MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE
* FOR ANY PARTICULAR PURPOSE. The software is provided "as is" without
* express or implied warranty of any kind.
*
* These notices must be retained in any copies of any part of this software.
*
* This file contributed by Sandeep Bajaj , Mar 1997.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (Xerox)";
#endif
#include "config.h" // for string.h
#include
#include "queue.h"
class PacketDRR;
class DRR;
class PacketDRR : public PacketQueue {
PacketDRR(): pkts(0),src(-1),bcount(0),prev(0),next(0),deficitCounter(0),turn(0) {}
friend DRR;
protected :
int pkts;
int src; //to detect collisions keep track of actual src address
int bcount; //count of bytes in each flow to find the max flow;
PacketDRR *prev;
PacketDRR *next;
int deficitCounter;
int turn;
inline PacketDRR * activate(PacketDRR *head) {
if (head) {
this->prev = head->prev;
this->next = head;
head->prev->next = this;
head->prev = this;
return head;
}
this->prev = this;
this->next = this;
return this;
}
inline PacketDRR * idle(PacketDRR *head) {
if (head == this) {
if (this->next == this)
return 0;
this->next->prev = this->prev;
this->prev->next = this->next;
return this->next;
}
this->next->prev = this->prev;
this->prev->next = this->next;
return head;
}
};
class DRR : public Queue {
public :
DRR();
virtual int command(int argc, const char*const* argv);
Packet *deque(void);
void enque(Packet *pkt);
int hash(Packet *pkt);
void clear();
protected:
int buckets_ ; //total number of flows allowed
int blimit_; //total number of bytes allowed across all flows
int quantum_; //total number of bytes that a flow can send
int mask_; /*if set hashes on just the node address otherwise on
node+port address*/
int bytecnt ; //cumulative sum of bytes across all flows
int pktcnt ; // cumulative sum of packets across all flows
int flwcnt ; //total number of active flows
PacketDRR *curr; //current active flow
PacketDRR *drr ; //pointer to the entire drr struct
int off_ip_;
inline PacketDRR *getMaxflow (PacketDRR *curr) { //returns flow with max pkts
int i;
PacketDRR *tmp;
PacketDRR *maxflow=curr;
for (i=0,tmp=curr; i < flwcnt; i++,tmp=tmp->next) {
if (maxflow->bcount < tmp->bcount)
maxflow=tmp;
}
return maxflow;
}
public:
//returns queuelength in packets
inline int length () {
return pktcnt;
}
//returns queuelength in bytes
inline int blength () {
return bytecnt;
}
};
static class DRRClass : public TclClass {
public:
DRRClass() : TclClass("Queue/DRR") {}
TclObject* create(int, const char*const*) {
return (new DRR);
}
} class_drr;
DRR::DRR()
{
buckets_=16;
quantum_=250;
drr=0;
curr=0;
flwcnt=0;
bytecnt=0;
pktcnt=0;
mask_=0;
bind("buckets_",&buckets_);
bind("blimit_",&blimit_);
bind("quantum_",&quantum_);
bind("mask_",&mask_);
bind ("off_ip_",&off_ip_);
}
void DRR::enque(Packet* pkt)
{
PacketDRR *q,*remq;
int which;
hdr_cmn *ch=(hdr_cmn*)pkt->access(off_cmn_);
hdr_ip *iph = (hdr_ip*)pkt->access(off_ip_);
if (!drr)
drr=new PacketDRR[buckets_];
which= hash(pkt) % buckets_;
q=&drr[which];
/*detect collisions here */
int compare=(!mask_ ? ((int)iph->saddr()) : ((int)iph->saddr()&0xfff0));
if (q->src ==-1)
q->src=compare;
else
if (q->src != compare)
fprintf(stderr,"Collisions between %d and %d src addresses\n",q->src,(int)iph->saddr());
q->enque(pkt);
++q->pkts;
++pktcnt;
q->bcount += ch->size();
bytecnt +=ch->size();
if (q->pkts==1)
{
curr = q->activate(curr);
q->deficitCounter=0;
++flwcnt;
}
while (bytecnt > blimit_) {
Packet *p;
hdr_cmn *remch;
hdr_ip *remiph;
remq=getMaxflow(curr);
p=remq->deque();
remch=(hdr_cmn*)p->access(off_cmn_);
remiph=(hdr_ip*)p->access(off_ip_);
remq->bcount -= remch->size();
bytecnt -= remch->size();
drop(p);
--remq->pkts;
--pktcnt;
if (remq->pkts==0) {
curr=remq->idle(curr);
--flwcnt;
}
}
}
Packet *DRR::deque(void)
{
hdr_cmn *ch;
hdr_ip *iph;
Packet *pkt=0;
if (bytecnt==0) {
//fprintf (stderr,"No active flow\n");
return(0);
}
while (!pkt) {
if (!curr->turn) {
curr->deficitCounter+=quantum_;
curr->turn=1;
}
pkt=curr->lookup(0);
ch=(hdr_cmn*)pkt->access(off_cmn_);
iph= (hdr_ip*)pkt->access(off_ip_);
if (curr->deficitCounter >= ch->size()) {
curr->deficitCounter -= ch->size();
pkt=curr->deque();
curr->bcount -= ch->size();
--curr->pkts;
--pktcnt;
bytecnt -= ch->size();
if (curr->pkts == 0) {
curr->turn=0;
--flwcnt;
curr->deficitCounter=0;
curr=curr->idle(curr);
}
return pkt;
}
else {
curr->turn=0;
curr=curr->next;
pkt=0;
}
}
return 0; // not reached
}
void DRR::clear()
{
PacketDRR *q =drr;
int i = buckets_;
if (!q)
return;
while (i--) {
if (q->pkts) {
fprintf(stderr, "Changing non-empty bucket from drr\n");
exit(1);
}
++q;
}
delete[](drr);
drr = 0;
}
/*
*Allows one to change blimit_ and bucket_ for a particular drrQ :
*
*
*/
int DRR::command(int argc, const char*const* argv)
{
if (argc==3) {
if (strcmp(argv[1], "blimit") == 0) {
blimit_ = atoi(argv[2]);
if (bytecnt > blimit_)
{
fprintf (stderr,"More packets in buffer than the new limit");
exit (1);
}
return (TCL_OK);
}
if (strcmp(argv[1], "buckets") == 0) {
clear();
buckets_ = atoi(argv[2]);
return (TCL_OK);
}
if (strcmp(argv[1],"quantum") == 0) {
quantum_ = atoi(argv[2]);
return (TCL_OK);
}
if (strcmp(argv[1],"mask")==0) {
mask_= atoi(argv[2]);
return (TCL_OK);
}
}
return (Queue::command(argc, argv));
}
int DRR::hash(Packet* pkt)
{
hdr_ip *iph=(hdr_ip*)pkt->access(off_ip_);
int i;
if (mask_)
i = (int)iph->saddr() & (0xfff0);
else
i = (int)iph->saddr();
return ((i + (i >> 8) + ~(i>>4)) % ((2<<23)-1))+1; // modulo a large prime
}
dynalink.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (C) 1997 by USC/ISI
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation, advertising
* materials, and other materials related to such distribution and use
* acknowledge that the software was developed by the University of
* Southern California, Information Sciences Institute. The name of the
* University may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
*/
// Other copyrights might apply to parts of this software and are so
// noted when applicable.
//
// Author: Kannan Varadhan
// Version Date: Mon Jun 30 15:51:33 PDT 1997
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (USC/ISI)";
#endif
#include "connector.h"
// #include "packet.h"
// #include "queue.h"
class DynamicLink : public Connector {
public:
DynamicLink() : down_(0), status_(1) { bind("status_", &status_); }
protected:
int command(int argc, const char*const* argv);
void recv(Packet* p, Handler* h);
NsObject* down_;
int status_;
};
static class DynamicLinkClass : public TclClass {
public:
DynamicLinkClass() : TclClass("DynamicLink") {}
TclObject* create(int, const char*const*) {
return (new DynamicLink);
}
} class_dynamic_link;
int DynamicLink::command(int argc, const char*const* argv)
{
if (argc == 2) {
if (strcmp(argv[1], "status?") == 0) {
Tcl::instance().result(status_ ? "up" : "down");
return TCL_OK;
}
}
return Connector::command(argc, argv);
}
void DynamicLink::recv(Packet* p, Handler* h)
{
if (status_)
target_->recv(p, h);
else
drop(p);
}
energy-model.cc
// Contributed by Satish Kumar
extern "C" {
#include
#include
};
#include "energy-model.h"
static class EnergyModelClass:public TclClass
{
public:
EnergyModelClass ():TclClass ("EnergyModel") { }
TclObject *create (int, const char *const *argv)
{
return (new EnergyModel (atof(argv[4]),atof(argv[5]),atof(argv[6])));
}
} class_energy_model;
errmodel.cc
/* -*- 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$ (UCB)
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (UCB)";
#endif
#include "config.h"
#include
#include
#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 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 };
ErrorModel::ErrorModel() : firstTime_(1), unit_(EU_PKT), ranvar_(0)
{
bind("enable_", &enable_);
bind("rate_", &rate_);
bind_bw("bandwidth_", &bandwidth_); // required for EU_TIME
bind_bool("markecn_", &markecn_);
}
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);
}
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_ && (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;
}
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;
return 8.0 * byte / bandwidth_;
}
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::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
/*
* Two-State: error-free and error
*/
TwoStateErrorModel::TwoStateErrorModel() : remainLen_(0)
{
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);
}
// else if (argc == 4)
ranvar_[i] = (RandomVariable*)TclObject::lookup(argv[3]);
return (TCL_OK);
}
return ErrorModel::command(argc, argv);
}
int TwoStateErrorModel::corrupt(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;
}
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() : 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_);
}
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 (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()
{
bind("off_mcast_ctrl_", &off_mcast_ctrl_);
}
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*)p->access(off_mcast_ctrl_);
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));
}
estimator.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) Xerox Corporation 1997. All rights reserved.
*
* License is granted to copy, to use, and to make and to use derivative
* works for research and evaluation purposes, provided that Xerox is
* acknowledged in all documentation pertaining to any such copy or
* derivative work. Xerox grants no other licenses expressed or
* implied. The Xerox trade name should not be used in any advertising
* without its written permission.
*
* XEROX CORPORATION MAKES NO REPRESENTATIONS CONCERNING EITHER THE
* MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE
* FOR ANY PARTICULAR PURPOSE. The software is provided "as is" without
* express or implied warranty of any kind.
*
* These notices must be retained in any copies of any part of this
* software.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$";
#endif
#include "estimator.h"
Estimator::Estimator() : meas_mod_(0),avload_(0.0),est_timer_(this), measload_(0.0), tchan_(0), omeasload_(0), oavload_(0)
{
bind("period_",&period_);
bind("src_", &src_);
bind("dst_", &dst_);
avload_.tracer(this);
avload_.name("\"Estimated Util.\"");
measload_.tracer(this);
measload_.name("\"Measured Util.\"");
}
int Estimator::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
if (argc==2) {
if (strcmp(argv[1],"load-est") == 0) {
tcl.resultf("%.3f",double(avload_));
return(TCL_OK);
} else if (strcmp(argv[1],"link-utlzn") == 0) {
tcl.resultf("%.3f",meas_mod_->bitcnt()/period_);
return(TCL_OK);
}
}
if (argc == 3) {
if (strcmp(argv[1], "attach") == 0) {
int mode;
const char* id = argv[2];
tchan_ = Tcl_GetChannel(tcl.interp(), (char*)id, &mode);
if (tchan_ == 0) {
tcl.resultf("Estimator: trace: can't attach %s for writing", id);
return (TCL_ERROR);
}
return (TCL_OK);
}
if (strcmp(argv[1], "setbuf") == 0) {
/* some sub classes actually do something here */
return(TCL_OK);
}
}
return NsObject::command(argc,argv);
}
void Estimator::setmeasmod (MeasureMod *measmod)
{
meas_mod_=measmod;
}
void Estimator::start()
{
avload_=0;
measload_ = 0;
est_timer_.resched(period_);
}
void Estimator::stop()
{
est_timer_.cancel();
}
void Estimator::timeout(int)
{
estimate();
est_timer_.resched(period_);
}
void Estimator_Timer::expire(Event* /*e*/)
{
est_->timeout(0);
}
void Estimator::trace(TracedVar* v)
{
char wrk[500];
double *p, newval;
/* check for right variable */
if (strcmp(v->name(), "\"Estimated Util.\"") == 0) {
p = &oavload_;
}
else if (strcmp(v->name(), "\"Measured Util.\"") == 0) {
p = &omeasload_;
}
else {
fprintf(stderr, "Estimator: unknown trace var %s\n", v->name());
return;
}
newval = double(*((TracedDouble*)v));
if (tchan_) {
int n;
double t = Scheduler::instance().clock();
/* f -t 0.0 -s 1 -a SA -T v -n Num -v 0 -o 0 */
sprintf(wrk, "f -t %g -s %d -a %s:%d-%d -T v -n %s -v %g -o %g",
t, src_, actype_, src_, dst_, v->name(), newval, *p);
n = strlen(wrk);
wrk[n] = '\n';
wrk[n+1] = 0;
(void)Tcl_Write(tchan_, wrk, n+1);
}
*p = newval;
return;
}
void Estimator::setactype(const char* type)
{
actype_ = new char[strlen(type)+1];
strcpy(actype_, type);
return;
}
expavg-est.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) Xerox Corporation 1997. All rights reserved.
*
* License is granted to copy, to use, and to make and to use derivative
* works for research and evaluation purposes, provided that Xerox is
* acknowledged in all documentation pertaining to any such copy or
* derivative work. Xerox grants no other licenses expressed or
* implied. The Xerox trade name should not be used in any advertising
* without its written permission.
*
* XEROX CORPORATION MAKES NO REPRESENTATIONS CONCERNING EITHER THE
* MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE
* FOR ANY PARTICULAR PURPOSE. The software is provided "as is" without
* express or implied warranty of any kind.
*
* These notices must be retained in any copies of any part of this
* software.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$";
#endif
#include
#include "estimator.h"
class ExpAvg_Est : public Estimator {
public:
ExpAvg_Est() {bind("w_",&w_);};
protected:
void estimate();
double w_;
};
void ExpAvg_Est::estimate()
{
avload_=(1-w_)*avload_+w_*meas_mod_->bitcnt()/period_;
//printf("%f %f %f\n",Scheduler::instance().clock(),avload_,meas_mod_->bitcnt()/period_);
fflush(stdout);
meas_mod_->resetbitcnt();
}
static class ExpAvg_EstClass : public TclClass {
public:
ExpAvg_EstClass() : TclClass ("Est/ExpAvg") {}
TclObject* create(int,const char*const*) {
return (new ExpAvg_Est());
}
}class_expavg_est;
expoo.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) Xerox Corporation 1997. All rights reserved.
*
* License is granted to copy, to use, and to make and to use derivative
* works for research and evaluation purposes, provided that Xerox is
* acknowledged in all documentation pertaining to any such copy or derivative
* work. Xerox grants no other licenses expressed or implied. The Xerox trade
* name should not be used in any advertising without its written permission.
*
* XEROX CORPORATION MAKES NO REPRESENTATIONS CONCERNING EITHER THE
* MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE
* FOR ANY PARTICULAR PURPOSE. The software is provided "as is" without
* express or implied warranty of any kind.
*
* These notices must be retained in any copies of any part of this software.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (Xerox)";
#endif
#include
#include "random.h"
#include "trafgen.h"
#include "ranvar.h"
/* implement an on/off source with exponentially distributed on and
* off times. parameterized by average burst time, average idle time,
* burst rate and packet size.
*/
class EXPOO_Traffic : public TrafficGenerator {
public:
EXPOO_Traffic();
virtual double next_interval(int&);
virtual void timeout();
protected:
void init();
double ontime_; /* average length of burst (sec) */
double offtime_; /* average length of idle time (sec) */
double rate_; /* send rate during on time (bps) */
double interval_; /* packet inter-arrival time during burst (sec) */
unsigned int rem_; /* number of packets left in current burst */
/* new stuff using RandomVariable */
ExponentialRandomVariable burstlen_;
ExponentialRandomVariable Offtime_;
};
static class EXPTrafficClass : public TclClass {
public:
EXPTrafficClass() : TclClass("Application/Traffic/Exponential") {}
TclObject* create(int, const char*const*) {
return (new EXPOO_Traffic());
}
} class_expoo_traffic;
EXPOO_Traffic::EXPOO_Traffic() : burstlen_(0.0), Offtime_(0.0)
{
bind_time("burst_time_", &ontime_);
bind_time("idle_time_", Offtime_.avgp());
bind_bw("rate_", &rate_);
bind("packetSize_", &size_);
}
void EXPOO_Traffic::init()
{
/* compute inter-packet interval during bursts based on
* packet size and burst rate. then compute average number
* of packets in a burst.
*/
interval_ = (double)(size_ << 3)/(double)rate_;
burstlen_.setavg(ontime_/interval_);
rem_ = 0;
if (agent_)
agent_->set_pkttype(PT_EXP);
}
double EXPOO_Traffic::next_interval(int& size)
{
double t = interval_;
if (rem_ == 0) {
/* compute number of packets in next burst */
rem_ = int(burstlen_.value() + .5);
/* make sure we got at least 1 */
if (rem_ == 0)
rem_ = 1;
/* start of an idle period, compute idle time */
t += Offtime_.value();
}
rem_--;
size = size_;
return(t);
}
void EXPOO_Traffic::timeout()
{
if (! running_)
return;
/* send a packet */
// The test tcl/ex/test-rcvr.tcl relies on the "NEW_BURST" flag being
// set at the start of any exponential burst ("talkspurt").
if (nextPkttime_ != interval_ || nextPkttime_ == -1)
agent_->sendmsg(size_, "NEW_BURST");
else
agent_->sendmsg(size_);
/* figure out when to send the next one */
nextPkttime_ = next_interval(size_);
/* schedule it */
if (nextPkttime_ > 0)
timer_.resched(nextPkttime_);
}
filter.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* filter.cc
* Copyright (C) 1998 by USC/ISI
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation, advertising
* materials, and other materials related to such distribution and use
* acknowledge that the software was developed by the University of
* Southern California, Information Sciences Institute. The name of the
* University may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header: /usr/src/mash/repository/vint/ns-2/filter.cc ";
#endif
#include "packet.h"
#include "filter.h"
static class FilterClass : public TclClass {
public:
FilterClass() : TclClass("Filter") {}
TclObject* create(int, const char*const*) {
return (new Filter);
}
} class_filter;
Filter::Filter() : filter_target_(0)
{
}
Filter::filter_e Filter::filter(Packet* /*p*/)
{
return PASS; // As simple connector
}
void Filter::recv(Packet* p, Handler* h)
{
switch(filter(p)) {
case DROP :
if (h) h->handle(p);
drop(p);
break;
case DUPLIC :
if (filter_target_)
filter_target_->recv(p->copy(), h);
/* fallthrough */
case PASS :
send(p, h);
break;
case FILTER :
if (filter_target_)
filter_target_->recv(p, h);
break;
}
}
int Filter::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
if (argc == 2) {
if (strcmp(argv[1], "filter-target") == 0) {
if (filter_target_ != 0)
tcl.result(target_->name());
return TCL_OK;
}
}
else if (argc == 3) {
if (strcmp(argv[1], "filter-target") == 0) {
filter_target_ = (NsObject*)TclObject::lookup(argv[2]);
return TCL_OK;
}
}
return Connector::command(argc, argv);
}
static class FieldFilterClass : public TclClass {
public:
FieldFilterClass() : TclClass("Filter/Field") {}
TclObject* create(int, const char*const*) {
return (new FieldFilter);
}
} class_filter_field;
FieldFilter::FieldFilter()
{
bind("offset_", &offset_);
bind("match_", &match_);
}
Filter::filter_e FieldFilter::filter(Packet *p)
{
return (*(int *)p->access(offset_) == match_) ? FILTER : PASS;
}
/* 10-5-98, Polly Huang, Filters that filter on multiple fields */
static class MultiFieldFilterClass : public TclClass {
public:
MultiFieldFilterClass() : TclClass("Filter/MultiField") {}
TclObject* create(int, const char*const*) {
return (new MultiFieldFilter);
}
} class_filter_multifield;
MultiFieldFilter::MultiFieldFilter() : field_list_(0)
{
}
void MultiFieldFilter::add_field(fieldobj *p)
{
p->next = field_list_;
field_list_ = p;
}
MultiFieldFilter::filter_e MultiFieldFilter::filter(Packet *p)
{
fieldobj* tmpfield;
tmpfield = field_list_;
while (tmpfield != 0) {
if (*(int *)p->access(tmpfield->offset) == tmpfield->match)
tmpfield = tmpfield->next;
else
return (PASS);
}
return(FILTER);
}
int MultiFieldFilter::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
if (argc == 2) {
if (strcmp(argv[1], "filter-target") == 0) {
if (filter_target_ != 0)
tcl.result(target_->name());
return TCL_OK;
}
}
else if (argc == 3) {
if (strcmp(argv[1], "filter-target") == 0) {
filter_target_ = (NsObject*)TclObject::lookup(argv[2]);
return TCL_OK;
}
}
else if (argc == 4) {
if (strcmp(argv[1], "filter-field") == 0) {
fieldobj *tmp = new fieldobj;
tmp->offset = atoi(argv[2]);
tmp->match = atoi(argv[3]);
add_field(tmp);
return TCL_OK;
}
}
return Connector::command(argc, argv);
}
flowmon.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1997 The 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 Network Research
* Group at Lawrence Berkeley National Laboratory.
* 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.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (LBL)";
#endif
//
// flow-monitor, basically a port from the ns-1 flow manager,
// but architected somewhat differently to better fit the ns-2
// object framework -KF
//
#include
#include "config.h"
#include "queue-monitor.h"
#include "classifier.h"
#include "ip.h"
// for convenience, we need most of the stuff in a QueueMonitor
// plus some flow info which looks like pkt header info
class Flow : public EDQueueMonitor {
public:
Flow() : src_(-1), dst_(-1), fid_(-1), type_(PT_NTYPE) {
bind("off_ip_" ,&off_ip_);
bind("src_", &src_);
bind("dst_", &dst_);
bind("flowid_", &fid_);
}
nsaddr_t src() const { return (src_); }
nsaddr_t dst() const { return (dst_); }
int flowid() const { return (fid_); }
packet_t ptype() const { return (type_); }
void setfields(Packet *p) {
hdr_ip* hdr = (hdr_ip*)p->access(off_ip_);
hdr_cmn* chdr = (hdr_cmn*)p->access(off_cmn_);
src_ = hdr->saddr();
dst_ = hdr->daddr();
fid_ = hdr->flowid();
type_ = chdr->ptype();
}
protected:
int off_ip_;
nsaddr_t src_;
nsaddr_t dst_;
int fid_;
packet_t type_;
};
/*
* flow monitoring is performed like queue-monitoring with
* a classifier to demux by flow
*/
class FlowMon : public EDQueueMonitor {
public:
FlowMon();
void in(Packet*); // arrivals
void out(Packet*); // departures
void drop(Packet*); // all drops (incl
void edrop(Packet*); // "early" drops
int command(int argc, const char*const* argv);
protected:
void dumpflows();
void dumpflow(Tcl_Channel, Flow*);
void fformat(Flow*);
char* flow_list();
Classifier* classifier_;
Tcl_Channel channel_;
int enable_in_; // enable per-flow arrival state
int enable_out_; // enable per-flow depart state
int enable_drop_; // enable per-flow drop state
int enable_edrop_; // enable per-flow edrop state
char wrk_[2048]; // big enough to hold flow list
};
FlowMon::FlowMon() : classifier_(NULL), channel_(NULL),
enable_in_(1), enable_out_(1), enable_drop_(1), enable_edrop_(1)
{
bind_bool("enable_in_", &enable_in_);
bind_bool("enable_out_", &enable_out_);
bind_bool("enable_drop_", &enable_drop_);
bind_bool("enable_edrop_", &enable_edrop_);
}
void
FlowMon::in(Packet *p)
{
Flow* desc;
EDQueueMonitor::in(p);
if (!enable_in_)
return;
if ((desc = ((Flow*)classifier_->find(p))) != NULL) {
desc->setfields(p);
desc->in(p);
}
}
void
FlowMon::out(Packet *p)
{
Flow* desc;
EDQueueMonitor::out(p);
if (!enable_out_)
return;
if ((desc = ((Flow*)classifier_->find(p))) != NULL) {
desc->setfields(p);
desc->out(p);
}
}
void
FlowMon::drop(Packet *p)
{
Flow* desc;
EDQueueMonitor::drop(p);
if (!enable_drop_)
return;
if ((desc = ((Flow*)classifier_->find(p))) != NULL) {
desc->setfields(p);
desc->drop(p);
}
}
void
FlowMon::edrop(Packet *p)
{
Flow* desc;
EDQueueMonitor::edrop(p);
if (!enable_edrop_)
return;
if ((desc = ((Flow*)classifier_->find(p))) != NULL) {
desc->setfields(p);
desc->edrop(p);
}
}
void
FlowMon::dumpflows()
{
register int i, j = classifier_->maxslot();
Flow* f;
for (i = 0; i <= j; i++) {
if ((f = (Flow*)classifier_->slot(i)) != NULL)
dumpflow(channel_, f);
}
}
char*
FlowMon::flow_list()
{
register const char* z;
register int i, j = classifier_->maxslot();
Flow* f;
register char* p = wrk_;
register char* q;
q = p + sizeof(wrk_) - 2;
*p = '\0';
for (i = 0; i <= j; i++) {
if ((f = (Flow*)classifier_->slot(i)) != NULL) {
z = f->name();
while (*z && p < q)
*p++ = *z++;
*p++ = ' ';
}
if (p >= q) {
fprintf(stderr, "FlowMon:: flow list exceeded working buffer\n");
fprintf(stderr, "\t recompile ns with larger FlowMon::wrk_[] array\n");
exit (1);
}
}
if (p != wrk_)
*--p = '\0';
return (wrk_);
}
void
FlowMon::fformat(Flow* f)
{
double now = Scheduler::instance().clock();
#if defined(HAVE_INT64)
sprintf(wrk_, "%8.3f %d %d %d %d %d %d " STRTOI64_FMTSTR " " STRTOI64_FMTSTR " %d %d " STRTOI64_FMTSTR " " STRTOI64_FMTSTR " %d %d %d %d %d %d",
#else /* no 64-bit int */
sprintf(wrk_, "%8.3f %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d",
#endif
now, // 1: time
f->flowid(), // 2: flowid
0, // 3: category
f->ptype(), // 4: type (from common header)
f->flowid(), // 5: flowid (formerly class)
f->src(), // 6: sender
f->dst(), // 7: receiver
f->parrivals(), // 8: arrivals this flow (pkts)
f->barrivals(), // 9: arrivals this flow (bytes)
f->epdrops(), // 10: early drops this flow (pkts)
f->ebdrops(), // 11: early drops this flow (bytes)
parrivals(), // 12: all arrivals (pkts)
barrivals(), // 13: all arrivals (bytes)
epdrops(), // 14: total early drops (pkts)
ebdrops(), // 15: total early drops (bytes)
pdrops(), // 16: total drops (pkts)
bdrops(), // 17: total drops (bytes)
f->pdrops(), // 18: drops this flow (pkts) [includes edrops]
f->bdrops() // 19: drops this flow (bytes) [includes edrops]
);
};
void
FlowMon::dumpflow(Tcl_Channel tc, Flow* f)
{
fformat(f);
if (tc != 0) {
int n = strlen(wrk_);
wrk_[n++] = '\n';
wrk_[n] = '\0';
(void)Tcl_Write(tc, wrk_, n);
wrk_[n-1] = '\0';
}
}
int
FlowMon::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);
}
if (strcmp(argv[1], "dump") == 0) {
dumpflows();
return (TCL_OK);
}
if (strcmp(argv[1], "flows") == 0) {
tcl.result(flow_list());
return (TCL_OK);
}
} else if (argc == 3) {
if (strcmp(argv[1], "classifier") == 0) {
classifier_ = (Classifier*)
TclObject::lookup(argv[2]);
if (classifier_ == NULL)
return (TCL_ERROR);
return (TCL_OK);
}
if (strcmp(argv[1], "attach") == 0) {
int mode;
const char* id = argv[2];
channel_ = Tcl_GetChannel(tcl.interp(),
(char*) id, &mode);
if (channel_ == NULL) {
tcl.resultf("FlowMon (%s): can't attach %s for writing",
name(), id);
return (TCL_ERROR);
}
return (TCL_OK);
}
}
return (EDQueueMonitor::command(argc, argv));
}
static class FlowMonitorClass : public TclClass {
public:
FlowMonitorClass() : TclClass("QueueMonitor/ED/Flowmon") {}
TclObject* create(int, const char*const*) {
return (new FlowMon);
}
} flow_monitor_class;
static class FlowClass : public TclClass {
public:
FlowClass() : TclClass("QueueMonitor/ED/Flow") {}
TclObject* create(int, const char*const*) {
return (new Flow);
}
} flow_class;
fq.cc
/* -*- 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 MASH Research
* Group at the University of California Berkeley.
* 4. Neither the name of the University nor of the Research Group 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.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (ANS)";
#endif
#include "config.h"
#include
#include "queue.h"
/*XXX*/
#define MAXFLOW 32
class FQ : public Queue {
public:
FQ();
virtual int command(int argc, const char*const* argv);
Packet *deque(void);
void enque(Packet *pkt);
void recv(Packet* p, Handler* h);
protected:
int update();
struct flowState {
Queue* q_;
Packet* hol_; /* head-of-line packet for each flow */
double finishTime_; /* FQ finish time for hol packet */
double delta_;
Handler* handler_;
} fs_[MAXFLOW];
inline double finish(const flowState& fs, int nactive)
{
return (fs.finishTime_ + fs.delta_ * nactive);
}
int maxflow_;
double secsPerByte_;
int off_ip_;
};
static class FQClass : public TclClass {
public:
FQClass() : TclClass("Queue/FQ") {}
TclObject* create(int, const char*const*) {
return (new FQ);
}
} class_fq;
FQ::FQ()
{
for (int i = 0; i < MAXFLOW; ++i) {
fs_[i].q_ = 0;
fs_[i].hol_ = 0;
fs_[i].finishTime_ = 0.;
}
maxflow_ = -1;
secsPerByte_ = 0.;
bind("secsPerByte_", &secsPerByte_);
bind("off_ip_", &off_ip_);
}
int FQ::command(int argc, const char*const* argv)
{
if (argc == 4) {
if (strcmp(argv[1], "install") == 0) {
int flowID = atoi(argv[2]);
fs_[flowID].q_ = (Queue*)TclObject::lookup(argv[3]);
if (flowID > maxflow_)
maxflow_ = flowID;
/*XXX*/
if (flowID >= MAXFLOW)
abort();
return (TCL_OK);
}
}
return (Queue::command(argc, argv));
}
/*XXX this is quite inefficient.*/
int FQ::update()
{
int nactive = 0;
for (int i = 0; i <= maxflow_; ++i) {
Queue* q = fs_[i].q_;
if (q != 0) {
if (fs_[i].hol_ == 0) {
Packet* p = q->deque();
if (p != 0) {
fs_[i].hol_ = p;
++nactive;
}
} else
++nactive;
}
}
return (nactive);
}
Packet* FQ::deque()
{
int nactive = update();
int target = -1;
double best;
for (int i = 0; i <= maxflow_; ++i) {
if (fs_[i].hol_ != 0) {
if (target < 0) {
target = i;
best = finish(fs_[i], nactive);
} else {
double F = finish(fs_[i], nactive);
if (F < best) {
target = i;
best = F;
}
}
}
}
if (target >= 0) {
Packet* p = fs_[target].hol_;
fs_[target].hol_ = 0;
fs_[target].finishTime_ = best;
/* let this upstream queue resume */
Handler* h = fs_[target].handler_;
/*XXX null event okay because queue doesn't use it*/
h->handle(0);
return (p);
}
return (0);
}
/*
* Called when one of our queues is unblocked by us in FQ::deque
* (or gets its first packet).
*/
void FQ::recv(Packet* p, Handler* handler)
{
hdr_ip* h = (hdr_ip*)p->access(off_ip_);
int flowid = h->flowid();
/* shouldn't be called when head-of-line is pending */
if (flowid >= MAXFLOW || fs_[flowid].hol_ != 0)
abort();
/*
* Put this packet at the head-of-line for its queue
* and set up scheduling state according to the
* standard fair-queueing "finish time" equation.
*/
fs_[flowid].hol_ = p;
double now = Scheduler::instance().clock();
if (now > fs_[flowid].finishTime_)
fs_[flowid].finishTime_ = now;
fs_[flowid].handler_ = handler;
int size = ((hdr_cmn*)p->access(off_cmn_))->size();
fs_[flowid].delta_ = size * secsPerByte_;
if (!blocked_) {
/*
* We're not blocked. Get a packet and send it on.
* We perform an extra check because the queue
* might drop the packet even if it was
* previously empty! (e.g., RED can do this.)
*/
p = deque();
if (p != 0) {
blocked_ = 1;
target_->recv(p, &qh_);
}
}
}
void FQ::enque(Packet*)
{
/* should never be called because we override recv */
abort();
}
fsm.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/* fsm.cc
* Copyright (C) 1999 by USC/ISI
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation, advertising
* materials, and other materials related to such distribution and use
* acknowledge that the software was developed by the University of
* Southern California, Information Sciences Institute. The name of the
* University may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* Contributed by Polly Huang (USC/ISI), http://www-scf.usc.edu/~bhuang
*
* @(#) $Header$ (LBL)
*/
#include "fsm.h"
#include
#ifndef MAX
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#endif
#ifndef MIN
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#endif
FSM* FSM::instance_;
TahoeAckFSM* TahoeAckFSM::instance_;
RenoAckFSM* RenoAckFSM::instance_;
TahoeDelAckFSM* TahoeDelAckFSM::instance_;
RenoDelAckFSM* RenoDelAckFSM::instance_;
void
FSMState::number_all()
{
if (processed())
return;
static int next_i = 0;
print_i_ = ++next_i;
//
int i;
for (i = 0; i < 17; i++)
if (drop_[i])
drop_[i]->number_all();
}
void
FSMState::reset_all_processed()
{
if (print_i_ == 0)
number_all();
// requires a full traversal always to work
if (!processed())
return;
print_i_ = -print_i_;
int i;
for (i = 0; i < 17; i++)
if (drop_[i])
drop_[i]->reset_all_processed();
}
void
FSMState::print_all(int level)
{
if (processed())
return;
const int SPACES_PER_LEVEL = 2;
printf("#%-2d %*s %d:\n", print_i_, level * SPACES_PER_LEVEL + 1, " ", batch_size_);
int i;
for (i = 0; i <= batch_size_; i++) {
static char *delay_names[] = {"done", "error", "RTT", "timeout" };
assert(transition_[i] >= -1 && transition_[i] <= TIMEOUT);
printf(" %*s %d %s -> #%d\n", level * SPACES_PER_LEVEL + 3, " ",
i,
delay_names[transition_[i]+1],
drop_[i] ? drop_[i]->print_i_ : 0);
if (drop_[i])
drop_[i]->print_all(level + 1);
};
}
static void
report_stat_terminus(int desired_pkts, // # needed
int pkts, // # got so far
int rtts, // # of rtt events
int timeouts, // # of to events
int ps, // # of times taken a prob. p event (pkt received OK)
int qs, // # of times taken a prob. q event (pkt dropped OK)
int num_states, // size of the stack
int num_state_names,
FSMState **states,
char *state_names)
{
// print states and probability
printf("%s: p^%d*q^%d, %d rtt, %d timeouts, %d states:",
(pkts > desired_pkts ? "exceeded-pkts" :
(pkts == desired_pkts ? "desired_pkts" : "unimplemented-qs")),
ps, qs,
rtts, timeouts,
num_states);
char ch = ' ';
int i;
for (i = 0; i < num_states; i++) {
printf ("%c#%d", ch, states[i]->print_i_);
ch = ',';
};
printf(" [%.*s]\n", num_state_names, state_names);
}
/*
* FSMState::print_all_stats:
* Walk through the tcp state table exhaustively.
* Recurse to handle errors.
* Very hairy.
* johnh.
*/
void
FSMState::print_all_stats(int desired_pkts_total, // # needed
int pkts, // # got so far
int rtts, // # of rtt events
int timeouts, // # of to events
int ps, // # of times taken a prob. p event (pkt received OK)
int qs, // # of times taken a prob. q event (pkt dropped OK)
int num_states, // size of the stack
int num_state_names)
{
int i;
#define LARGER_NUMBER_OF_STATES 31 // was 17
static FSMState *states[LARGER_NUMBER_OF_STATES];
static char state_names[LARGER_NUMBER_OF_STATES*4]; // xxx: this is just some random big size :-(
if (pkts >= desired_pkts_total || qs > 5) {
// done; print states and probability
// (give up when we're where we want to be [good],
// or we've taken too many losses [to prevent recursion])
report_stat_terminus(desired_pkts_total, pkts, rtts, timeouts, ps, qs, num_states, num_state_names, states, state_names);
return;
};
// remember us!
states[num_states] = this;
num_states++;
// xxx: doesn't handle TCP tail behavior
//
// first, consider the no-loss case
//
int desired_pkts_remaining = desired_pkts_total - pkts;
int desired_pkts_this_round = MIN(desired_pkts_remaining, batch_size_);
for (i = 0; i< desired_pkts_this_round; i++)
state_names[num_state_names + i] = 's';
if (desired_pkts_remaining > desired_pkts_this_round) {
// more to do? take a rtt hit and keep going
state_names[num_state_names + desired_pkts_this_round] = '.';
drop_[0]->print_all_stats(desired_pkts_total,
pkts + desired_pkts_this_round,
rtts + 1, timeouts,
ps + desired_pkts_this_round, qs,
num_states,
num_state_names + desired_pkts_this_round + 1);
} else {
// no more to do... report out
report_stat_terminus(desired_pkts_total,
pkts + desired_pkts_this_round,
rtts, timeouts,
ps + desired_pkts_this_round, qs,
num_states,
num_state_names + desired_pkts_this_round,
states,
state_names);
};
//
// now consider losses
//
int desired_pkts_with_loss = MAX(desired_pkts_this_round - 1, 0);
// loop through losing the i'th packet for all possible i's.
// Can't loop through more than we could have sent.
for (i = 1; i <= desired_pkts_this_round; i++) {
// keep track of sending patterns
if (i > 1)
state_names[num_state_names + i - 2] = 's';
state_names[num_state_names + i - 1] = 'd';
state_names[num_state_names + desired_pkts_this_round] = (transition_[i] == RTT ? '.' : '-');
// can we even have any?
if (qs) {
// not if we already had one!
report_stat_terminus(desired_pkts_total,
pkts + i - 1,
rtts, timeouts,
ps + i - 1, qs + 1,
num_states,
num_state_names + i,
states,
state_names);
} else {
// recurse... assume the rest made it
drop_[i]->print_all_stats(desired_pkts_total, pkts + desired_pkts_with_loss,
rtts + (transition_[i] == RTT ? 1 : 0),
timeouts + (transition_[i] == TIMEOUT ? 1 : 0),
ps + desired_pkts_with_loss, qs + 1,
num_states,
num_state_names + desired_pkts_this_round + 1);
// 2nd drop somewhere in this round?
int remaining_pkts_this_round = desired_pkts_this_round - i;
if (qs == 0 && remaining_pkts_this_round > 0) {
// yes, generate the probs
int j;
for (j = i+1; j <= desired_pkts_this_round; j++) {
if (j > i+1)
state_names[num_state_names + j - 1] = 's';
state_names[num_state_names + j] = 'd';
report_stat_terminus(desired_pkts_total,
pkts + j - 2,
rtts, timeouts,
ps + j - 2, qs + 2,
num_states,
num_state_names + j,
states,
state_names);
};
};
};
};
}
void
FSM::print_FSM(FSMState* state)
{
#if 0
int i;
if (state != NULL) {
for (i=0; i<17; i++) {
if (state->drop_[i] != NULL) {
if (i==0)
printf("%d->(%d) ", state->transition_[i], state->drop_[i]->batch_size_);
else
printf("\n%d->(%d) ", state->transition_[i], state->drop_[i]->batch_size_);
print_FSM(state->drop_[i]);
}
}
}
#else /* ! 0 */
state->reset_all_processed();
state->print_all(0);
#endif /* 0 */
}
void
FSM::print_FSM_stats(FSMState* state, int n)
{
state->reset_all_processed();
state->print_all_stats(n);
fflush(stdout);
}
static class TahoeAckFSMClass : public TclClass {
public:
TahoeAckFSMClass() : TclClass("FSM/TahoeAck") {}
TclObject* create(int , const char*const*) {
return (new TahoeAckFSM);
}
} class_tahoeackfsm;
static class RenoAckFSMClass : public TclClass {
public:
RenoAckFSMClass() : TclClass("FSM/RenoAck") {}
TclObject* create(int , const char*const*) {
return (new RenoAckFSM);
}
} class_renoackfsm;
static class TahoeDelAckFSMClass : public TclClass {
public:
TahoeDelAckFSMClass() : TclClass("FSM/TahoeDelAck") {}
TclObject* create(int , const char*const*) {
return (new TahoeDelAckFSM);
}
} class_tahoedelackfsm;
static class RenoDelAckFSMClass : public TclClass {
public:
RenoDelAckFSMClass() : TclClass("FSM/RenoDelAck") {}
TclObject* create(int , const char*const*) {
return (new RenoDelAckFSM);
}
} class_renodelackfsm;
// ***********************************************
// Tahoe-Ack TCP Connection Finite State Machine *
// ***********************************************
TahoeAckFSM::TahoeAckFSM() : FSM(), start_state_(NULL)
{
int i;
FSMState* tmp;
instance_ = this;
// (wnd, ssh) == (1, 20)
start_state_ = new FSMState;
//printf("creating Tahoe Ack FSM\n");
for (i=0; i<17; i++) {
start_state_->drop_[i] = NULL;
start_state_->transition_[i] = -1;
}
start_state_->batch_size_ = 1;
// (wnd, ssh) == (2, 20)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 2;
start_state_->drop_[0] = tmp;
start_state_->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 2;
start_state_->drop_[0]->drop_[2] = tmp;
start_state_->drop_[0]->transition_[2] = RTT;
// (wnd, ssh) == (4, 20)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 4;
start_state_->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 2;
start_state_->drop_[0]->drop_[0]->drop_[2] = tmp;
start_state_->drop_[0]->drop_[0]->transition_[2] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 4;
start_state_->drop_[0]->drop_[0]->drop_[3] = tmp;
start_state_->drop_[0]->drop_[0]->transition_[3] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 6;
start_state_->drop_[0]->drop_[0]->drop_[4] = tmp;
start_state_->drop_[0]->drop_[0]->transition_[4] = RTT;
//(wnd, ssh) == (8, 20)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 8;
start_state_->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 2;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[2] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->transition_[2] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 4;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[3] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->transition_[3] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 6;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[4] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->transition_[4] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 8;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[5] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->transition_[5] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 10;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[6] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->transition_[6] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 12;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[7] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->transition_[7] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 14;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[8] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->transition_[8] = RTT;
//(wnd, ssh) == (16, 20)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 16;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 1;
for (i=1; i<17; i++) start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[i] = tmp;
for (i=1; i<14; i++)
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[i] = RTT;
for (i=14; i<17; i++)
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[i] = TIMEOUT;
//(wnd, ssh) == (1, 2), timeout
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 1;
start_state_->drop_[1] = tmp;
start_state_->transition_[1] = TIMEOUT;
start_state_->drop_[0]->drop_[1] = tmp;
start_state_->drop_[0]->transition_[1] = TIMEOUT;
start_state_->drop_[0]->drop_[2]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[2]->transition_[0] = TIMEOUT;
start_state_->drop_[0]->drop_[0]->drop_[1] = tmp;
start_state_->drop_[0]->drop_[0]->transition_[1] = RTT;
start_state_->drop_[0]->drop_[0]->drop_[2]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[2]->transition_[0] = RTT;
//(wnd, ssh) == (2, 2)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 2;
start_state_->drop_[1]->drop_[0] = tmp;
start_state_->drop_[1]->transition_[0] = RTT;
//(wnd, ssh) == (2.5, 2)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 2;
start_state_->drop_[1]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[1]->drop_[0]->transition_[0] = RTT;
//(wnd, ssh) == (3, 2)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 3;
start_state_->drop_[1]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[1]->drop_[0]->drop_[0]->transition_[0] = RTT;
//(wnd, ssh) == (4, 2)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 4;
start_state_->drop_[1]->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[1]->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
//(wnd, ssh) == (5, 2)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 5;
start_state_->drop_[1]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[1]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
//(wnd, ssh) == (6, 2)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 6;
start_state_->drop_[1]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[1]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
//(wnd, ssh) == (7, 2)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 7;
start_state_->drop_[1]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[1]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
//(wnd, ssh) == (7, 2)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 7;
start_state_->drop_[1]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[1]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
//(wnd, ssh) == (1, 3)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 1;
start_state_->drop_[0]->drop_[0]->drop_[3]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[3]->transition_[0] = RTT;
start_state_->drop_[0]->drop_[0]->drop_[4]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[4]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 2;
start_state_->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[3]->drop_[0]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 3;
start_state_->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 4;
start_state_->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 5;
start_state_->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 5;
start_state_->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 6;
start_state_->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
//(wnd, ssh) == (1, 4)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 1;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[1] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->transition_[1] = RTT;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[2]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[2]->transition_[0] = 0;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 2;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[1]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[1]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 4;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[1]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[1]->drop_[0]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 4;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[1]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[1]->drop_[0]->drop_[0]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 5;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[1]->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[1]->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 6;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[1]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[1]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
//(wnd, ssh) == (1, 5)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 1;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[3]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[3]->transition_[0] = 0;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[4]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[4]->transition_[0] = 0;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 2;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[3]->drop_[0]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 4;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 5;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 6;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
//(wnd, ssh) == (1, 6)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 1;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[5]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[5]->transition_[0] = 0;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[6]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[6]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 2;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[5]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[5]->drop_[0]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 4;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[5]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[5]->drop_[0]->drop_[0]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 6;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[5]->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[5]->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
//(wnd, ssh) == (1, 7)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 1;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[7]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[7]->transition_[0] = RTT;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[8]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[8]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 2;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[7]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[7]->drop_[0]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 4;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[7]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[7]->drop_[0]->drop_[0]->transition_[0] = RTT;
//print_FSM(start_state_);
//printf("\n");
}
// **********************************************
// Reno-ACK TCP Connection Finite State Machine *
// **********************************************
RenoAckFSM::RenoAckFSM() : FSM(), start_state_(NULL)
{
int i;
FSMState* tmp;
//printf("creating Reno Ack FSM\n");
instance_ = this;
// (wnd, ssh) == (1, 20)
start_state_ = new FSMState;
for (i=0; i<17; i++) {
start_state_->drop_[i] = NULL;
start_state_->transition_[i] = -1;
}
start_state_->batch_size_ = 1;
// (wnd, ssh) == (2, 20)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 2;
start_state_->drop_[0] = tmp;
start_state_->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 2;
start_state_->drop_[0]->drop_[2] = tmp;
start_state_->drop_[0]->transition_[2] = RTT;
// (wnd, ssh) == (4, 20)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 4;
start_state_->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 2;
start_state_->drop_[0]->drop_[0]->drop_[2] = tmp;
start_state_->drop_[0]->drop_[0]->transition_[2] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 4;
start_state_->drop_[0]->drop_[0]->drop_[3] = tmp;
start_state_->drop_[0]->drop_[0]->transition_[3] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 6;
start_state_->drop_[0]->drop_[0]->drop_[4] = tmp;
start_state_->drop_[0]->drop_[0]->transition_[4] = RTT;
//(wnd, ssh) == (8, 20)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 8;
start_state_->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 4;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[3] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->transition_[3] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 6;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[4] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->transition_[4] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 8;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[5] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->transition_[5] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 10;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[6] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->transition_[6] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 6;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[6]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[6]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 12;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[7] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->transition_[7] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 14;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[8] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->transition_[8] = RTT;
//(wnd, ssh) == (16, 20)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 16;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 1;
for (i=1; i<17; i++) start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[i] = tmp;
for (i=1; i<14; i++)
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[i] = RTT;
for (i=14; i<17; i++)
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[i] = TIMEOUT;
//(wnd, ssh) == (1, 2), timeout
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 1;
start_state_->drop_[1] = tmp;
start_state_->transition_[1] = TIMEOUT;
start_state_->drop_[0]->drop_[1] = tmp;
start_state_->drop_[0]->transition_[1] = TIMEOUT;
start_state_->drop_[0]->drop_[2]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[2]->transition_[0] = TIMEOUT;
//(wnd, ssh) == (2, 2)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 2;
start_state_->drop_[1]->drop_[0] = tmp;
start_state_->drop_[1]->transition_[0] = RTT;
start_state_->drop_[0]->drop_[0]->drop_[1] = tmp;
start_state_->drop_[0]->drop_[0]->transition_[1] = RTT;
start_state_->drop_[0]->drop_[0]->drop_[2]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[2]->transition_[0] = RTT;
//(wnd, ssh) == (2.5, 2)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 2;
start_state_->drop_[1]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[1]->drop_[0]->transition_[0] = RTT;
//(wnd, ssh) == (3, 2)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 3;
start_state_->drop_[1]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[1]->drop_[0]->drop_[0]->transition_[0] = RTT;
//(wnd, ssh) == (4, 2)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 4;
start_state_->drop_[1]->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[1]->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
//(wnd, ssh) == (5, 2)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 5;
start_state_->drop_[1]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[1]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
//(wnd, ssh) == (6, 2)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 6;
start_state_->drop_[1]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[1]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
//(wnd, ssh) == (7, 2)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 7;
start_state_->drop_[1]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[1]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
//(wnd, ssh) == (7.5, 2)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 7;
start_state_->drop_[1]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[1]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
//(wnd, ssh) == (3, 3)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 3;
start_state_->drop_[0]->drop_[0]->drop_[3]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[3]->transition_[0] = RTT;
start_state_->drop_[0]->drop_[0]->drop_[4]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[4]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 3;
start_state_->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[3]->drop_[0]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 4;
start_state_->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 5;
start_state_->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 6;
start_state_->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
//(wnd, ssh) == (4, 4)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 4;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[1] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->transition_[1] = RTT;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[2] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->transition_[2] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 4;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[1]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[1]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 5;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[1]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[1]->drop_[0]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 6;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[1]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[1]->drop_[0]->drop_[0]->transition_[0] = RTT;
//(wnd, ssh) == (5, 5)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 1;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[3]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[3]->transition_[0] = 0;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[4]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[4]->transition_[0] = 0;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 5;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[3]->drop_[0]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 6;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 7;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
//(wnd, ssh) == (6, 6)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 1;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[5]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[5]->transition_[0] = 0;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 6;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[5]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[5]->drop_[0]->transition_[0] = RTT;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[6]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[6]->drop_[0]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 7;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[5]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[5]->drop_[0]->drop_[0]->transition_[0] = RTT;
//(wnd, ssh) == (7, 7)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 7;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[7]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[7]->transition_[0] = RTT;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[8]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[8]->transition_[0] = RTT;
//print_FSM(start_state_);
//printf("\n");
}
// *****************************************************
// Tahoe-Delay Ack TCP Connection Finite State Machine *
// *****************************************************
TahoeDelAckFSM::TahoeDelAckFSM() : FSM(), start_state_(NULL)
{
int i;
FSMState* tmp;
//printf("creating Tahoe DelAck FSM\n");
instance_ = this;
// (wnd, ssh) == (1, 20)
start_state_ = new FSMState;
for (i=0; i<17; i++) {
start_state_->drop_[i] = NULL;
start_state_->transition_[i] = -1;
}
start_state_->batch_size_ = 1;
// (wnd, ssh) == (2, 20)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 2;
start_state_->drop_[0] = tmp;
start_state_->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 2;
start_state_->drop_[0]->drop_[2] = tmp;
start_state_->drop_[0]->transition_[2] = RTT;
// (wnd, ssh) == (3, 20)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 3;
start_state_->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 2;
start_state_->drop_[0]->drop_[0]->drop_[2] = tmp;
start_state_->drop_[0]->drop_[0]->transition_[2] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 3;
start_state_->drop_[0]->drop_[0]->drop_[3] = tmp;
start_state_->drop_[0]->drop_[0]->transition_[3] = RTT;
//(wnd, ssh) == (5, 20)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 5;
start_state_->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 2;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[2] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->transition_[2] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 3;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[3] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->transition_[3] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 5;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[4] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->transition_[4] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 6;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[5] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->transition_[5] = RTT;
//(wnd, ssh) == (8, 20)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 8;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 2;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[2] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[2] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 3;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[3] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[3] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 5;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[4] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[4] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 6;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[5] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[5] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 8;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[6] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[6] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 9;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[7] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[7] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 11;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[8] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[8] = RTT;
//(wnd, ssh) == (12, 20)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 12;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 1;
for (i=1; i<13; i++) start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[i] = tmp;
for (i=1; i<10; i++)
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[i] = RTT;
for (i=10; i<13; i++)
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[i] = TIMEOUT;
//(wnd, ssh) == (1, 2), timeout
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 1;
start_state_->drop_[1] = tmp;
start_state_->transition_[1] = TIMEOUT;
start_state_->drop_[0]->drop_[1] = tmp;
start_state_->drop_[0]->transition_[1] = TIMEOUT;
start_state_->drop_[0]->drop_[2]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[2]->transition_[0] = TIMEOUT;
start_state_->drop_[0]->drop_[0]->drop_[1] = tmp;
start_state_->drop_[0]->drop_[0]->transition_[1] = TIMEOUT;
start_state_->drop_[0]->drop_[0]->drop_[2]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[2]->transition_[0] = RTT;
start_state_->drop_[0]->drop_[0]->drop_[3]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[3]->transition_[0] = RTT;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[1] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->transition_[1] = RTT;
//(wnd, ssh) == (2, 2)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 2;
start_state_->drop_[1]->drop_[0] = tmp;
start_state_->drop_[1]->transition_[0] = RTT;
//(wnd, ssh) == (2.5, 2)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 2;
start_state_->drop_[1]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[1]->drop_[0]->transition_[0] = RTT;
//(wnd, ssh) == (2.9, 2)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 2;
start_state_->drop_[1]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[1]->drop_[0]->drop_[0]->transition_[0] = RTT;
//(wnd, ssh) == (3, 2)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 3;
start_state_->drop_[1]->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[1]->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
//(wnd, ssh) == (3.3, 2)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 3;
start_state_->drop_[1]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[1]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
//(wnd, ssh) == (4, 2)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 4;
start_state_->drop_[1]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[1]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
//(wnd, ssh) == (4.7, 2)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 4;
start_state_->drop_[1]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[1]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
//(wnd, ssh) == (5, 2)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 5;
start_state_->drop_[1]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[1]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
//(wnd, ssh) == (6, 2)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 6;
start_state_->drop_[1]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[1]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
//(wnd, ssh) == (1, 3)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 1;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[2]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[2]->transition_[0] = RTT;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[3]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[3]->transition_[0] = RTT;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[4]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[4]->transition_[0] = RTT;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[5]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[5]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 2;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[3]->drop_[0]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 3;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 3;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 4;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 4;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 5;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
//(wnd, ssh) == (1, 4)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 1;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[1] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[1] = RTT;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[2]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[2]->transition_[0] = 0;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[3]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[3]->transition_[0] = 0;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 2;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[1]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[1]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 3;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[1]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[1]->drop_[0]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 4;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[1]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[1]->drop_[0]->drop_[0]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 4;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[1]->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[1]->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
//(wnd, ssh) == (1, 5)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 1;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[4]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[4]->transition_[0] = 0;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[5]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[5]->transition_[0] = 0;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[6]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[6]->transition_[0] = RTT;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[7]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[7]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 2;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[4]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[4]->drop_[0]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 3;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[4]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[4]->drop_[0]->drop_[0]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 5;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[4]->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[4]->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
//(wnd, ssh) == (1, 6)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 1;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[8]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[8]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 2;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[8]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[8]->drop_[0]->transition_[0] = RTT;
//print_FSM(start_state_);
//printf("\n");
}
// ****************************************************
// Reno-Delay Ack TCP Connection Finite State Machine *
// ****************************************************
RenoDelAckFSM::RenoDelAckFSM() : FSM(), start_state_(NULL)
{
int i;
FSMState* tmp;
//printf("creating Reno DelAck FSM\n");
instance_ = this;
// (wnd, ssh) == (1, 20)
start_state_ = new FSMState;
for (i=0; i<17; i++) {
start_state_->drop_[i] = NULL;
start_state_->transition_[i] = -1;
}
start_state_->batch_size_ = 1;
// (wnd, ssh) == (2, 20)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 2;
start_state_->drop_[0] = tmp;
start_state_->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 2;
start_state_->drop_[0]->drop_[2] = tmp;
start_state_->drop_[0]->transition_[2] = RTT;
// (wnd, ssh) == (3, 20)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 3;
start_state_->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 2;
start_state_->drop_[0]->drop_[0]->drop_[2] = tmp;
start_state_->drop_[0]->drop_[0]->transition_[2] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 3;
start_state_->drop_[0]->drop_[0]->drop_[3] = tmp;
start_state_->drop_[0]->drop_[0]->transition_[3] = RTT;
//(wnd, ssh) == (5, 20)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 5;
start_state_->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 2;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[2] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->transition_[2] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 3;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[3] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->transition_[3] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 5;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[4] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->transition_[4] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 6;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[5] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->transition_[5] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 2;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[2]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[2]->transition_[0] = RTT;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[4]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[4]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 3;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[3]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[3]->transition_[0] = RTT;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[5]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[5]->transition_[0] = RTT;
//(wnd, ssh) == (8, 20)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 8;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 3;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[2] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[2] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 4;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[3] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[3] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 4;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[2]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[2]->transition_[0] = RTT;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[3]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[3]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 1;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[4] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[4] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 2;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[5] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[5] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 8;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[6] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[6] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 9;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[7] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[7] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 11;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[8] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[8] = RTT;
//(wnd, ssh) == (12, 20)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 12;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 1;
for (i=1; i<13; i++) start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[i] = tmp;
for (i=1; i<10; i++)
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[i] = RTT;
for (i=10; i<13; i++)
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[i] = TIMEOUT;
//(wnd, ssh) == (1, 2), timeout
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 1;
start_state_->drop_[1] = tmp;
start_state_->transition_[1] = TIMEOUT;
start_state_->drop_[0]->drop_[1] = tmp;
start_state_->drop_[0]->transition_[1] = TIMEOUT;
start_state_->drop_[0]->drop_[2]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[2]->transition_[0] = TIMEOUT;
start_state_->drop_[0]->drop_[0]->drop_[1] = tmp;
start_state_->drop_[0]->drop_[0]->transition_[1] = TIMEOUT;
start_state_->drop_[0]->drop_[0]->drop_[2]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[2]->transition_[0] = TIMEOUT;
//(wnd, ssh) == (2, 2)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 2;
start_state_->drop_[1]->drop_[0] = tmp;
start_state_->drop_[1]->transition_[0] = RTT;
//(wnd, ssh) == (2.5, 2)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 2;
start_state_->drop_[1]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[1]->drop_[0]->transition_[0] = RTT;
//(wnd, ssh) == (2.9, 2)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 2;
start_state_->drop_[1]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[1]->drop_[0]->drop_[0]->transition_[0] = RTT;
//(wnd, ssh) == (3, 2)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 3;
start_state_->drop_[1]->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[1]->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
//(wnd, ssh) == (3.3, 2)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 3;
start_state_->drop_[1]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[1]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
//(wnd, ssh) == (4, 2)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 4;
start_state_->drop_[1]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[1]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
//(wnd, ssh) == (4.7, 2)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 4;
start_state_->drop_[1]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[1]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
//(wnd, ssh) == (5, 2)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 5;
start_state_->drop_[1]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[1]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
//(wnd, ssh) == (6, 2)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 6;
start_state_->drop_[1]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[1]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
//(wnd, ssh) == (2, 2), rtt
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 2;
start_state_->drop_[0]->drop_[0]->drop_[3]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[3]->transition_[0] = RTT;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[1] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->transition_[1] = RTT;
//(wnd, ssh) == (2.5, 2)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 2;
start_state_->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[3]->drop_[0]->transition_[0] = RTT;
//(wnd, ssh) == (3, 2)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 3;
start_state_->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0]->transition_[0] = RTT;
//(wnd, ssh) == (4, 2)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 4;
start_state_->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
//(wnd, ssh) == (4.3, 2)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 4;
start_state_->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
//(wnd, ssh) == (4.7, 2)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 4;
start_state_->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
//(wnd, ssh) == (5, 2)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 5;
start_state_->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
//(wnd, ssh) == (3.3, 3)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 3;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[2]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[2]->drop_[0]->transition_[0] = RTT;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[3]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[3]->drop_[0]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 4;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[2]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[2]->drop_[0]->drop_[0]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 4;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[2]->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[2]->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 5;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[2]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[2]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 5;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[2]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[2]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[0] = RTT;
//(wnd, ssh) == (4, 4)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 4;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[1] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->transition_[1] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 4;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[1]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[1]->transition_[0] = RTT;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[2]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[2]->drop_[0]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 5;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[1]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[1]->drop_[0]->transition_[0] = RTT;
//(wnd, ssh) == (5, 5)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 5;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[4]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[4]->transition_[0] = 0;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[5]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[5]->transition_[0] = 0;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[6]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[6]->transition_[0] = RTT;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[7]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[7]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 5;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[4]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[4]->drop_[0]->transition_[0] = RTT;
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 5;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[4]->drop_[0]->drop_[0]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[4]->drop_[0]->drop_[0]->transition_[0] = RTT;
//(wnd, ssh) == (6, 6)
tmp = new FSMState;
for (i=0; i<17; i++) {
tmp->drop_[i] = NULL;
tmp->transition_[i] = -1;
}
tmp->batch_size_ = 6;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[8]->drop_[0] = tmp;
start_state_->drop_[0]->drop_[0]->drop_[0]->drop_[0]->drop_[8]->transition_[0] = RTT;
//print_FSM(start_state_);
//printf("\n");
}
god.cc
/* -*- 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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*/
/* Ported from CMU/Monarch's code, nov'98 -Padma.*/
/* god.cc
General Operations Director
perform operations requiring omnipotence in the simulation
NOTE: Tcl node indexs are 0 based, NS C++ node IP addresses (and the
node->index() are 1 based.
$Id: AllCode__.html 980 2002-12-12 09:36:58Z ffilali $
*/
#include
#include
#include
#include
God* God::instance_;
static class GodClass : public TclClass {
public:
GodClass() : TclClass("God") {}
TclObject* create(int, const char*const*) {
return (new God);
}
} class_God;
God::God()
{
min_hops = 0;
num_nodes = 0;
}
int
God::hops(int i, int j)
{
return min_hops[i * num_nodes + j];
}
void
God::stampPacket(Packet *p)
{
hdr_cmn *ch = HDR_CMN(p);
struct hdr_ip *ih = HDR_IP(p);
nsaddr_t src = ih->saddr();
nsaddr_t dst = ih->daddr();
assert(min_hops);
if (!packet_info.data_packet(ch->ptype())) return;
if (dst > num_nodes || src > num_nodes) return; // broadcast pkt
ch->opt_num_forwards() = min_hops[src * num_nodes + dst];
}
void
God::recv(Packet *, Handler *)
{
abort();
}
int
God::command(int argc, const char* const* argv)
{
Tcl& tcl = Tcl::instance();
if ((instance_ == 0) || (instance_ != this))
instance_ = this;
if (argc == 2) {
if(strcmp(argv[1], "dump") == 0) {
int i, j;
for(i = 1; i < num_nodes; i++) {
fprintf(stdout, "%2d) ", i);
for(j = 1; j < num_nodes; j++)
fprintf(stdout, "%2d ",
min_hops[i * num_nodes + j]);
fprintf(stdout, "\n");
}
return TCL_OK;
}
if(strcmp(argv[1], "num_nodes") == 0) {
tcl.resultf("%d", nodes());
return TCL_OK;
}
}
else if(argc == 3) {
if (strcasecmp(argv[1], "num_nodes") == 0) {
assert(num_nodes == 0);
// allow for 0 based to 1 based conversion
num_nodes = atoi(argv[2]) + 1;
min_hops = new int[num_nodes * num_nodes];
bzero((char*) min_hops,
sizeof(int) * num_nodes * num_nodes);
instance_ = this;
return TCL_OK;
}
}
else if(argc == 5) {
if (strcasecmp(argv[1], "set-dist") == 0) {
int i = atoi(argv[2]);
int j = atoi(argv[3]);
int d = atoi(argv[4]);
assert(i >= 0 && i < num_nodes);
assert(j >= 0 && j < num_nodes);
min_hops[i * num_nodes + j] = d;
min_hops[j * num_nodes + i] = d;
return TCL_OK;
}
}
return BiConnector::command(argc, argv);
}
gridkeeper.cc
/*
* An optimizer for some wireless simulations
*
* Helps most when some nodes are mostly stationary.
* We hope you can share your experience with the gridkeeper with us.
*
* Ported from Sun's mobility code
*/
#include "gridkeeper.h"
static double d2(double x1, double x2, double y1, double y2)
{
return ((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
/*
static class GridHandlerClass : public TclClass {
public:
GridHandlerClass() : TclClass("GridHandler") {}
TclObject* create(int, const char*const*){
return (new GridHandler());
}
} class_grid_handler;
*/
GridHandler::GridHandler()
{
}
void GridHandler::handle(Event *e)
{
MoveEvent *me = (MoveEvent *)e;
MobileNode **pptr;
MobileNode * token_ = me->token_;
if ((pptr = me->leave_)) {
while (*pptr) {
if ((*pptr)->address_ == token_->address_) break;
pptr = &((*pptr)->next_);
}
if (!(*pptr)) abort();
else {
*pptr = token_->next_;
token_->next_ = 0;
}
}
if ((pptr = me->enter_)) {
if (token_->next_) abort(); // can't be in more than one grid
token_->next_ = *pptr;
*pptr = token_;
}
delete me;
// dump info in the gridkeeper for debug only
// dump();
}
GridKeeper* GridKeeper::instance_;
static class GridKeeperClass : public TclClass {
public:
GridKeeperClass() : TclClass("GridKeeper") {}
TclObject* create(int, const char*const*) {
return (new GridKeeper);
}
} class_grid_keeper;
GridKeeper::GridKeeper() : size_(0),grid_(NULL), dim_x_(0), dim_y_(0)
{
gh_ = new GridHandler();
}
GridKeeper::~GridKeeper()
{
int i;
for (i = 0; i <= dim_x_; i++) {
delete [] grid_[i];
}
delete [] grid_;
}
int GridKeeper::command(int argc, const char*const* argv)
{
int i, grid_x, grid_y;
Tcl& tcl = Tcl::instance();
MobileNode *mn;
if (argc == 2) {
if (strcmp(argv[1], "dump") == 0) {
dump();
return (TCL_OK);
}
}
if (argc == 3) {
if (strcmp(argv[1], "addnode") == 0) {
mn = (MobileNode *)TclObject::lookup(argv[2]);
grid_x = aligngrid((int)mn->X, dim_x_);
grid_y = aligngrid((int)mn->Y, dim_y_);
mn->next_ = grid_[grid_x][grid_y];
grid_[grid_x][grid_y] = mn;
size_++;
return (TCL_OK);
}
}
if (argc == 4 && strcmp(argv[1], "dimension") == 0) {
if (instance_ == 0) instance_ = this;
dim_x_ = strtol(argv[2], (char**)0, 0);
dim_y_ = strtol(argv[3], (char**)0, 0);
if (dim_x_ <= 0 || dim_y_ <= 0) {
tcl.result("illegal grid dimension");
return (TCL_ERROR);
}
grid_ = new MobileNode **[dim_x_];
for (i = 0; i < dim_x_; i++) {
grid_[i] = new MobileNode *[dim_y_];
bzero((void *)grid_[i], sizeof(MobileNode *)*dim_y_);
}
return (TCL_OK);
}
return (TclObject::command(argc, argv));
}
void GridKeeper::new_moves(MobileNode *mn)
{
double x = mn->X, y = mn->Y;
double ss = mn->speed;
double vx = (mn->dX)*ss, vy = (mn->dY)*ss;
// double interval = mn->position_update_interval;
double endx, endy, pother, tm;
int i, j, endi, gother, grid_x, grid_y;
MoveEvent *me;
Scheduler& s = Scheduler::instance();
endx = mn->destX;
endy = mn->destY;
if (vx > 0) {
endi = min(dim_x_-1, (int)endx);
for (i = (int)x+1; i <= endi; i++) {
tm = (i-x)/vx;
pother = vy*tm + y;
j = (int)pother;
me = new MoveEvent;
if (j == pother && j != 0 && j != dim_y_) {
if (vy > 0) gother = j - 1;
else if (vy < 0) gother = j + 1;
else gother = j;
}
else {
gother = j;
}
me->leave_ = &(grid_[aligngrid(i-1, dim_x_)][aligngrid(gother, dim_y_)]);
me->grid_x_ = grid_x = aligngrid(i, dim_x_);
me->grid_y_ = grid_y = aligngrid(j, dim_y_);
me->enter_ = &(grid_[grid_x][grid_y]);
me->token_ = mn;
s.schedule(gh_, me, tm);
}
}
else if (vx < 0) {
endi = (int)endx;
for (i = (int)x; i > endi; i--) {
if (i == dim_x_) continue;
tm = (i-x)/vx;
pother = vy*tm + y;
j = (int)pother;
me = new MoveEvent;
if (j == pother && j != 0 && j != dim_y_) {
if (vy > 0) gother = j - 1;
else if (vy < 0) gother = j + 1;
else gother = j;
}
else {
gother = j;
}
me->leave_ = &grid_[aligngrid(i, dim_x_)][aligngrid(gother, dim_y_)];
me->grid_x_ = grid_x = aligngrid(i-1, dim_x_);
me->grid_y_ = grid_y = aligngrid(j, dim_y_);
me->enter_ = &grid_[grid_x][grid_y];
me->token_ = mn;
s.schedule(gh_, me, tm);
}
}
if (vy > 0) {
endi = min(dim_y_-1, (int)endy);
for (j = (int)y+1; j <= endi; j++) {
tm = (j-y)/vy;
pother = vx*tm + x;
i = (int)pother;
me = new MoveEvent;
if (i == pother && i != 0 && i != dim_x_ && vx != 0) continue;
me->leave_ = &grid_[aligngrid(i, dim_x_)][aligngrid(j-1, dim_y_)];
me->grid_x_ = grid_x = aligngrid(i, dim_x_);
me->grid_y_ = grid_y = aligngrid(j, dim_y_);
me->enter_ = &grid_[grid_x][grid_y];
me->token_ = mn;
s.schedule(gh_, me, tm);
}
}
else if (vy < 0) {
endi = (int)endy;
for (j = (int)y; j > endi; j--) {
if (j == dim_y_) continue;
tm = (j-y)/vy;
pother = vx*tm + x;
i = (int)pother;
me = new MoveEvent;
if (i == pother && i != 0 && i != dim_x_ && vx != 0) continue;
me->leave_ = &grid_[aligngrid(i, dim_x_)][aligngrid(j, dim_y_)];
me->grid_x_ = grid_x = aligngrid(i, dim_x_);
me->grid_y_ = grid_y = aligngrid(j-1, dim_y_);
me->enter_ = &grid_[grid_x][grid_y];
me->token_ = mn;
s.schedule(gh_, me, tm);
}
}
}
int GridKeeper::get_neighbors(MobileNode* mn, MobileNode **output)
{
int grid_x, grid_y, index = 0, i, j, ulx, uly, lly, adj;
MobileNode *pgd;
double mnx, mny, mnr, sqmnr;
mn->update_position();
mnx = mn->X;
mny = mn->Y;
grid_x = aligngrid((int)mn->X, dim_x_);
grid_y = aligngrid((int)mn->Y, dim_y_);
sqmnr = (mnr = mn->radius_)*mnr;
adj = (int)ceil(mnr);
ulx = min(dim_x_-1, grid_x + adj);
uly = min(dim_y_-1, grid_y + adj);
lly = max(0, grid_y - adj);
for (i = max(0, grid_x - adj); i <= ulx; i++) {
for (j = lly; j <= uly; j++) {
for (pgd = grid_[i][j]; pgd != 0; pgd = pgd->next_) {
if (mn->address_ == pgd->address_) continue;
pgd->update_position();
if (d2(pgd->X, mnx, pgd->Y, mny) < sqmnr)
output[index++] = pgd;
}
}
}
return index;
}
void GridKeeper::dump()
{
int i,j;
MobileNode *pgd;
for (i = 0; i< dim_x_; i++) {
for (j = 0; j < dim_y_; j++) {
if (grid_[i][j] == 0) continue;
printf("grid[%d][%d]: ",i,j);
for (pgd = grid_[i][j]; pgd != 0; pgd = pgd->next_) {
printf("%d ",pgd->address_);
}
printf("\n");
}
}
printf("-------------------------------\n");
}
hackloss.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
#ifndef lint
static const char rcsid[] =
"@(#) $Header$";
#endif
#include "connector.h"
#include "packet.h"
#include "queue.h"
class HackLossyLink : public Connector {
public:
HackLossyLink() : down_(0), src_(0), dst_(0), fid_(0), ctr_(0), nth_(0){
bind("off_ip_", &off_ip_);
}
protected:
int command(int argc, const char*const* argv);
void recv(Packet* p, Handler* h);
NsObject* down_;
int src_, dst_, fid_;
int ctr_, nth_;
int off_ip_;
};
static class HackLossyLinkClass : public TclClass {
public:
HackLossyLinkClass() : TclClass("HackLossyLink") {}
TclObject* create(int, const char*const*) {
return (new HackLossyLink);
}
} class_dynamic_link;
int HackLossyLink::command(int argc, const char*const* argv)
{
if (strcmp(argv[1], "down-target") == 0) {
NsObject* p = (NsObject*)TclObject::lookup(argv[2]);
if (p == 0) {
Tcl::instance().resultf("no object %s", argv[2]);
return TCL_ERROR;
}
down_ = p;
return TCL_OK;
}
if (strcmp(argv[1], "show-params") == 0) {
Tcl::instance().resultf("src_ = %d, dst_ = %d, fid_ = %d, nth_ = %d",
src_, dst_, fid_, nth_);
return TCL_OK;
}
if (strcmp(argv[1], "set-params") == 0) {
src_ = atoi(argv[2]);
dst_ = atoi(argv[3]);
fid_ = atoi(argv[4]);
return TCL_OK;
}
if (strcmp(argv[1], "nth") == 0) {
nth_ = atoi(argv[2]);
return TCL_OK;
}
return Connector::command(argc, argv);
}
void HackLossyLink::recv(Packet* p, Handler* h)
{
hdr_ip* iph = (hdr_ip*) p->access(off_ip_);
if (nth_ && (iph->flowid() == fid_) &&
(iph->saddr() == src_) && (iph->daddr() == dst_) &&
((++ctr_ % nth_) == 0))
down_->recv(p); // XXX Why no handler?
else
target_->recv(p, h);
}
hb-adc.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) Xerox Corporation 1997. All rights reserved.
*
* License is granted to copy, to use, and to make and to use derivative
* works for research and evaluation purposes, provided that Xerox is
* acknowledged in all documentation pertaining to any such copy or
* derivative work. Xerox grants no other licenses expressed or
* implied. The Xerox trade name should not be used in any advertising
* without its written permission.
*
* XEROX CORPORATION MAKES NO REPRESENTATIONS CONCERNING EITHER THE
* MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE
* FOR ANY PARTICULAR PURPOSE. The software is provided "as is" without
* express or implied warranty of any kind.
*
* These notices must be retained in any copies of any part of this
* software.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$";
#endif
//Hoeffding Bounds Admission Control
#include "adc.h"
#include
#include
class HB_ADC : public ADC {
public:
HB_ADC();
void teardown_action(int,double,int);
void rej_action(int,double,int);
protected:
int admit_flow(int,double,int);
int rejected_;
double epsilon_;
double sump2_;
};
HB_ADC::HB_ADC() : rejected_(0), sump2_(0)
{
bind("epsilon_", &epsilon_);
type_ = new char[3];
strcpy(type_, "HB");
}
int HB_ADC::admit_flow(int cl,double r,int b)
{
//get peak rate this class of flow
double p=peak_rate(cl,r,b);
if (backoff_) {
if (rejected_)
return 0;
}
//printf("Peak rate: %f Avload: %f Rem %f %f %f\n",p,est_[cl]->avload(),sqrt(log(1/epsilon_)*sump2_/2),log(1/epsilon_),sump2_);
if ((p+est_[cl]->avload()+sqrt(log(1/epsilon_)*sump2_/2)) <= bandwidth_) {
sump2_+= p*p;
est_[cl]->change_avload(p);
return 1;
}
else {
rejected_=1;
return 0;
}
}
void HB_ADC::rej_action(int cl,double r,int b)
{
double p=peak_rate(cl,r,b);
sump2_ -= p*p;
}
void HB_ADC::teardown_action(int cl,double r,int b)
{
rejected_=0;
double p=peak_rate(cl,r,b);
sump2_ -= p*p;
}
static class HB_ADCClass : public TclClass {
public:
HB_ADCClass() : TclClass("ADC/HB") {}
TclObject* create(int,const char*const*) {
return (new HB_ADC());
}
}class_hb_adc;
integrator.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1996 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 MASH Research
* Group at the University of California Berkeley.
* 4. Neither the name of the University nor of the Research Group 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.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$";
#endif
#include
#include "integrator.h"
static class IntegratorClass : public TclClass {
public:
IntegratorClass() : TclClass("Integrator") {}
TclObject* create(int, const char*const*) {
return (new Integrator);
}
} integrator_class;
Integrator::Integrator() : lastx_(0.), lasty_(0.), sum_(0.)
{
bind("lastx_", &lastx_);
bind("lasty_", &lasty_);
bind("sum_", &sum_);
}
void Integrator::set(double x, double y)
{
lastx_ = x;
lasty_ = y;
sum_ = 0.;
}
void Integrator::newPoint(double x, double y)
{
sum_ += (x - lastx_) * lasty_;
lastx_ = x;
lasty_ = y;
}
int Integrator::command(int argc, const char*const* argv)
{
if (argc == 2) {
if (strcmp(argv[1], "reset") == 0) {
set(0., 0.);
return (TCL_OK);
}
} else if (argc == 4) {
if (strcmp(argv[1], "newpoint") == 0) {
double x = atof(argv[2]);
double y = atof(argv[3]);
newPoint(x, y);
return (TCL_OK);
}
}
return (TclObject::command(argc, argv));
}
/*
* interface for the 'Samples' class, will probably want to move
* to some sort of "stats" file at some point
*/
static class SamplesClass : public TclClass {
public:
SamplesClass() : TclClass("Samples") {}
TclObject* create(int, const char*const*) {
return (new Samples);
}
} samples_class;
int Samples::command(int argc, const char*const* argv)
{
if (argc == 2) {
if (strcmp(argv[1], "mean") == 0) {
if (cnt_ > 0) {
Tcl::instance().resultf("%g", mean());
return (TCL_OK);
}
Tcl::instance().resultf("tried to take mean with no sample points");
return (TCL_ERROR);
}
if (strcmp(argv[1], "cnt") == 0) {
Tcl::instance().resultf("%u", cnt());
return (TCL_OK);
}
if (strcmp(argv[1], "variance") == 0) {
if (cnt_ == 1) {
Tcl::instance().resultf("0.0");
return (TCL_OK);
}
if (cnt_ > 2) {
Tcl::instance().resultf("%g", variance());
return (TCL_OK);
}
return (TCL_ERROR);
}
if (strcmp(argv[1], "reset") == 0) {
reset();
return (TCL_OK);
}
} else if ( argc == 3 ) {
if ( strcmp(argv[1],"newpoint") == 0 ) {
double x = atof(argv[2]);
newPoint(x);
return (TCL_OK);
}
}
return (TclObject::command(argc, argv));
}
ip.cc
/* -*- 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 MASH Research
* Group at the University of California Berkeley.
* 4. Neither the name of the University nor of the Research Group 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.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$";
#endif
#include "packet.h"
#include "ip.h"
int hdr_ip::offset_;
static class IPHeaderClass : public PacketHeaderClass {
public:
IPHeaderClass() : PacketHeaderClass("PacketHeader/IP",
sizeof(hdr_ip)) {
bind_offset(&hdr_ip::offset_);
}
void export_offsets() {
field_offset("src_", OFFSET(hdr_ip, src_));
field_offset("dst_", OFFSET(hdr_ip, dst_));
field_offset("ttl_", OFFSET(hdr_ip, ttl_));
field_offset("fid_", OFFSET(hdr_ip, fid_));
field_offset("prio_", OFFSET(hdr_ip, prio_));
}
} class_iphdr;
ivs.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1996-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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (LBL)";
#endif
#include
#include
#include "message.h"
#include "trace.h"
#include "agent.h"
/* ivs data packet; ctrl packets are sent back as "messages" */
struct hdr_ivs {
double ts_; /* timestamp sent at source */
u_int8_t S_;
u_int8_t R_;
u_int8_t state_;
u_int8_t rshft_;
u_int8_t kshft_;
u_int16_t key_;
double maxrtt_;
int seqno_;
static int offset_;
inline static int& offset() { return offset_; }
inline static hdr_ivs* access(Packet* p) {
return (hdr_ivs*) p->access(offset_);
}
/* per-field member functions */
double& ts() { return (ts_); }
u_int8_t& S() { return (S_); }
u_int8_t& R() { return (R_); }
u_int8_t& state() { return (state_); }
u_int8_t& rshft() { return (rshft_); }
u_int8_t& kshft() { return (kshft_); }
u_int16_t& key() { return (key_); }
double& maxrtt() { return (maxrtt_); }
int& seqno() { return (seqno_); }
};
int hdr_ivs::offset_;
static class IvsHeaderClass : public PacketHeaderClass {
public:
IvsHeaderClass() : PacketHeaderClass("PacketHeader/IVS",
sizeof(hdr_ivs)) {
bind_offset(&hdr_ivs::offset_);
}
} class_ivshdr;
class IvsSource : public Agent {
public:
IvsSource();
protected:
void reset();
void recv(Packet *pkt, Handler*);
void sendpkt();
int S_;
int R_;
int state_;
#define ST_U 0
#define ST_L 1
#define ST_C 2
int rttShift_;
int keyShift_;
int key_;
double maxrtt_;
int off_ivs_;
int off_msg_;
};
struct Mc_Hole {
int start;
int end;
double time;
Mc_Hole* next;
};
class IvsReceiver : public Agent {
public:
IvsReceiver();
int command(int argc, const char*const* argv);
protected:
void recv(Packet *pkt, Handler*);
void update_ipg(double now);
int lossMeter(double timeDiff, u_int32_t seq, double maxrtt);
void upcall_respond(double ts, int matchS);
void upcall_rtt_solicit(double ts, int rshift);
int state_;
u_int32_t nextSeq_;
double timeMean_;
double timeVar_;
double ipg_; /* interpkt gap (estimator) */
Mc_Hole* head_;
Mc_Hole* tail_;
double lastPktTime_;
int ignoreR_;
double lastTime_; /* last time a resp pkt sent */
int key_;
int off_ivs_;
int off_msg_;
};
static class IvsSourceClass : public TclClass {
public:
IvsSourceClass() : TclClass("Agent/IVS/Source") {}
TclObject* create(int, const char*const*) {
return (new IvsSource());
}
} class_ivs_source;
static class IvsReceiverClass : public TclClass {
public:
IvsReceiverClass() : TclClass("Agent/IVS/Receiver") {}
TclObject* create(int, const char*const*) {
return (new IvsReceiver());
}
} class_ivs_receiver;
IvsSource::IvsSource() : Agent(PT_MESSAGE), S_(0), R_(0), state_(ST_U),
rttShift_(0), keyShift_(0), key_(0), maxrtt_(0)
{
bind("S_", &S_);
bind("R_", &R_);
bind("state_", &state_);
bind("rttShift_", &rttShift_);
bind("keyShift_", &keyShift_);
bind("key_", &key_);
bind("maxrtt_", &maxrtt_);
bind("off_ivs_", &off_ivs_);
bind("off_msg_", &off_msg_);
}
void IvsSource::reset()
{
}
/*
* main reception path - should only see acks, otherwise the
* network connections are misconfigured
*/
void IvsSource::recv(Packet* pkt, Handler*)
{
char wrk[128];/*XXX*/
Tcl& tcl = Tcl::instance();
hdr_msg *q = (hdr_msg*)pkt->access(off_msg_);
sprintf(wrk, "%s handle {%s}", name(), q->msg());
tcl.eval(wrk);
Packet::free(pkt);
}
#ifdef notdef
void IvsSource::probe_timeout()
{
rndStart_ = now;
if (keyShift_ == 15) {
if (key_ == 0) {
if (solicitedResponses_ == 0)
estReceivers_ = 0;
/*
* Got through a round without being LOADED.
* increase send rate.
*/
if (state_ == ST_U)
increase();
/* Reset keys et al */
S_ = 1;
state_ = ST_U;
/*XXX*/
setRttSolicit(mcstate);
solicitedResponses_ = 0;
keyShift_ = startShift_;
/*XXX do all this in tcl? */
setkey();
} else { mcstate->hdr.key = 0; }
} else {
if (probeTimeout_ > 0)
++keyShift_;
}
sched(pktTime + 2 * maxrtt_, IVS_TIMER_PROBE);
}
#endif
void IvsSource::sendpkt()
{
Packet* pkt = allocpkt();
hdr_ivs *p = (hdr_ivs*)pkt->access(off_ivs_);
/*fill in ivs fields */
p->ts() = Scheduler::instance().clock();
p->S() = S_;
p->R() = R_;
p->state() = state_;
p->rshft() = rttShift_;
p->kshft() = keyShift_;
p->key() = key_;
p->maxrtt() = maxrtt_;
target_->recv(pkt, (Handler *)0);
}
IvsReceiver::IvsReceiver() : Agent(PT_MESSAGE), state_(ST_U),
nextSeq_(0),
timeMean_(0.), timeVar_(0.),/*XXX*/
ipg_(0.),
head_(0),
tail_(0),
lastPktTime_(0.),
ignoreR_(0),
lastTime_(0.),
key_(0)
{
bind("ignoreR_", &ignoreR_);
bind("key_", &key_);
bind("state_", &state_);
bind("packetSize_", &size_);
bind("off_ivs_", &off_ivs_);
bind("off_msg_", &off_msg_);
}
inline void IvsReceiver::update_ipg(double v)
{
/* Update the estimated interpacket gap */
ipg_ = (15 * ipg_ + v) / 16;
}
/*
* timestamp comes in milliseconds since start of connection according to
* remote clock
* now is milliseconds since start of connection
* rtt in milliseconds
* This congestion meter is not terribly good at figuring out when the net is
* loaded, since the loss of a packet over a rtt is a transitory event
* Eventually we ought to have a memory thing, that records state once a
* maxrtt, with thresholds to decide current state
*/
int IvsReceiver::lossMeter(double timeDiff, u_int32_t seq, double maxrtt)
{
/*
* The congestion signal is calculated here by measuring the loss in a
* given period of packets - if the threshold for lost packets is
* passed then signal Congested. If there are no lost packets,
* then we are at UNLOADED, else LOADED
*/
/* if sequence number is next, increase expected number */
double now = Scheduler::instance().clock();
if (nextSeq_ == 0)
nextSeq_ = seq + 1;
else if (seq == nextSeq_)
nextSeq_++;
else if (seq > nextSeq_) {
#ifdef notdef
if (trace_ != 0) {
sprintf(trace_->buffer(), "d %g %d",
lastPktTime_, seq - nextSeq_);
trace_->dump();
}
#endif
/* This is definitely a hole */
Mc_Hole* hole = new Mc_Hole;
hole->time = now;
hole->start = nextSeq_;
hole->end = seq - 1;
hole->next = 0;
/* Now add it to the list */
if (head_ == NULL) {
head_ = hole;
tail_ = hole;
} else {
tail_->next = hole;
tail_ = hole;
}
nextSeq_ = seq + 1;
} else {
/* XXX can't happen in current ns simulations */
fprintf(stderr, "ns: ivs rcvr: seq number went backward\n");
abort();
}
/* update the calculation of the variance in the rtt */
/* get the time averaged mean of the difference */
if (timeMean_ == 0)
timeMean_ = timeDiff;
else
timeMean_ = (7 * timeMean_ + timeDiff) / 8;
timeDiff -= timeMean_;
if (timeDiff < 0)
timeDiff = -timeDiff;
timeVar_ = (7 * timeVar_ + timeDiff) / 8;
int lostPkts = 0;
/*
* Check down the list of holes, discarding those that before
* now-rttvar-rtt, counting those that fall within
* now-rttvar to now-rttvar-rtt
*/
if (head_ == 0)
return (ST_U);
Mc_Hole *cur = head_, *prev = NULL;
double validEnd = now - 2 * timeVar_;
double validStart = validEnd - maxrtt;
/* for each hole, if it is older than required, dump it */
/* If it is valid, add the size to the loss count */
/* Go to the next hole */
while (cur != NULL) {
if (cur->time < validStart) {
if (prev == NULL)
head_ = cur->next;
else
prev->next = cur->next;
delete cur;
if (prev == NULL)
cur = head_;
else
cur = prev->next;
} else {
if (cur->time < validEnd)
lostPkts += cur->end - cur->start + 1;
prev = cur;
cur = cur->next;
}
}
/*
* Update the moving average calculation of the number of holes, if
* nowMs is another rtt away
*/
double pps = (ipg_ != 0) ? maxrtt / ipg_ : 0.;
/*XXX*/
#ifdef notdef
if (trace_ != 0) {
double now = Scheduler::instance().clock();
sprintf(trace_->buffer(), "%.17g %g", now,
(double)lostPkts / pps);
trace_->dump();
}
#endif
/*XXX*/
#define LOSSCONGTH 15
#define LOSSLOADTH 5
/* If the rtt is smaller than the ipg, set the thresholds to 0,1,2 */
if ((pps * LOSSCONGTH) / 100 < 2)
pps = 200 / LOSSCONGTH;
if (lostPkts <= (LOSSLOADTH * pps) / 100)
return (ST_U);
else if (lostPkts <= (LOSSCONGTH * pps) / 100)
return (ST_L);
else
return (ST_C);
}
void IvsReceiver::recv(Packet* pkt, Handler*)
{
hdr_ivs *p = (hdr_ivs*)pkt->access(off_ivs_);
double now = Scheduler::instance().clock();
if (lastPktTime_ == 0.) {
lastPktTime_ = now;
Packet::free(pkt);
return;
}
update_ipg(now - lastPktTime_);
double ts = p->ts();
int prevState = state_;
state_ = lossMeter(now - ts, p->seqno(), p->maxrtt());
lastPktTime_ = now;
/* If soliciting rtt */
if (p->R() && !ignoreR_)
/* upcall into tcl */
upcall_rtt_solicit(ts, p->rshft());
/*
* send a response if we're congested and its over an rtt since
* we last sent one OR
* any response is solicited to estimate size and we match the key OR
* we're LOADED and we match the key and its over an rtt since we last
* sent a response
*/
if (now - lastTime_ < p->maxrtt() && state_ <= prevState) {
Packet::free(pkt);
return;
}
int shift = p->kshft();
int match;
if (p->key() == 0)
match = 1;
else
match = (key_ >> shift) == (p->key() >> shift);
int matchS = match ? p->S() : 0;
if (state_ == ST_C || matchS || (match && state_ == ST_L)) {
upcall_respond(ts, matchS);
lastTime_ = now;
}
Packet::free(pkt);
}
void IvsReceiver::upcall_respond(double ts, int matchS)
{
Tcl::instance().evalf("%s respond %.17g %d", name(), ts, matchS);
}
void IvsReceiver::upcall_rtt_solicit(double ts, int rshift)
{
Tcl::instance().evalf("%s solicit-rtt %.17g %d", name(), ts, rshift);
}
int IvsReceiver::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
if (argc == 3) {
if (strcmp(argv[1], "send") == 0) {
Packet* pkt = allocpkt();
hdr_msg* p = (hdr_msg*)pkt->access(off_msg_);
const char* s = argv[2];
int n = strlen(s);
if (n >= p->maxmsg()) {
tcl.result("message too big");
Packet::free(pkt);
return (TCL_ERROR);
}
strcpy(p->msg(), s);
target_->recv(pkt, (Handler*)0);
return (TCL_OK);
}
}
return (Agent::command(argc, argv));
}
lanRouter.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* lanRouter.cc
* Copyright (C) 1998 by USC/ISI
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation, advertising
* materials, and other materials related to such distribution and use
* acknowledge that the software was developed by the University of
* Southern California, Information Sciences Institute. The name of the
* University may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header: /usr/src/mash/repository/vint/ns-2/lanRouter.cc";
#endif
#include
#include
#include "lanRouter.h"
#include "address.h"
#include "ip.h"
static class LanRouterClass : public TclClass {
public:
LanRouterClass() : TclClass("LanRouter") {}
TclObject* create(int, const char*const*) {
return (new LanRouter());
}
} class_mac;
int LanRouter::next_hop(Packet *p) {
if (switch_ && switch_->classify(p)==1) {
return -1;
}
if (!routelogic_) return -1;
hdr_ip* iph= hdr_ip::access(p);
char* adst= Address::instance().print_nodeaddr(iph->daddr());
int next_hopIP;
if (enableHrouting_) {
char* bdst;
routelogic_->lookup_hier(lanaddr_, adst, next_hopIP);
// hacking: get rid of the last "."
bdst = Address::instance().print_nodeaddr(next_hopIP);
// bdst[strlen(bdst)-1] = '\0';
Tcl &tcl = Tcl::instance();
tcl.evalf("[Simulator instance] get-node-id-by-addr %s", bdst);
sscanf(tcl.result(), "%d", &next_hopIP);
delete [] bdst;
} else {
routelogic_->lookup_flat(lanaddr_, adst, next_hopIP);
}
delete [] adst;
return next_hopIP;
}
int LanRouter::command(int argc, const char*const* argv)
{
// Tcl& tcl = Tcl::instance();
if (argc == 3) {
// cmd lanaddr
if (strcmp(argv[1], "lanaddr") == 0) {
strcpy(lanaddr_, argv[2]);
return (TCL_OK);
}
// cmd routing hier|flat
if (strcmp(argv[1], "routing") == 0) {
if (strcmp(argv[2], "hier")==0)
enableHrouting_= true;
else if (strcmp(argv[2], "flat")==0)
enableHrouting_= false;
else return (TCL_ERROR);
return (TCL_OK);
}
// cmd switch
if (strcmp(argv[1], "switch") == 0) {
switch_ = (Classifier*) TclObject::lookup(argv[2]);
return (TCL_OK);
}
// cmd routelogic
if (strcmp(argv[1], "routelogic") == 0) {
routelogic_ = (RouteLogic*) TclObject::lookup(argv[2]);
return (TCL_OK);
}
}
return NsObject::command(argc, argv);
}
ll.cc
/* -*- 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, http://daedalus.cs.berkeley.edu
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (UCB)";
#endif
#include
#include
#include
#include
#include
int hdr_ll::offset_;
static class LLHeaderClass : public PacketHeaderClass {
public:
LLHeaderClass() : PacketHeaderClass("PacketHeader/LL",
sizeof(hdr_ll)) {
bind_offset(&hdr_ll::offset_);
}
} class_hdr_ll;
static class LLClass : public TclClass {
public:
LLClass() : TclClass("LL") {}
TclObject* create(int, const char*const*) {
return (new LL);
}
} class_ll;
LL::LL() : LinkDelay(), seqno_(0), ackno_(0), macDA_(0), ifq_(0),
mac_(0), lanrouter_(0), arptable_(0), varp_(0),
downtarget_(0), uptarget_(0)
{
bind("macDA_", &macDA_);
}
int LL::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
if (argc == 3) {
if (strcmp(argv[1], "ifq") == 0) {
ifq_ = (Queue*) TclObject::lookup(argv[2]);
return (TCL_OK);
}
if(strcmp(argv[1], "arptable") == 0) {
arptable_ = (ARPTable*)TclObject::lookup(argv[2]);
assert(arptable_);
return TCL_OK;
}
if(strcmp(argv[1], "varp") == 0) {
varp_ = (VARPTable*)TclObject::lookup(argv[2]);
assert(varp_);
return TCL_OK;
}
if (strcmp(argv[1], "mac") == 0) {
mac_ = (Mac*) TclObject::lookup(argv[2]);
assert(mac_);
return (TCL_OK);
}
if (strcmp(argv[1], "down-target") == 0) {
downtarget_ = (NsObject*) TclObject::lookup(argv[2]);
return (TCL_OK);
}
if (strcmp(argv[1], "up-target") == 0) {
uptarget_ = (NsObject*) TclObject::lookup(argv[2]);
return (TCL_OK);
}
if (strcmp(argv[1], "lanrouter") == 0) {
lanrouter_ = (LanRouter*) TclObject::lookup(argv[2]);
return (TCL_OK);
}
}
else if (argc == 2) {
if (strcmp(argv[1], "ifq") == 0) {
tcl.resultf("%s", ifq_->name());
return (TCL_OK);
}
if (strcmp(argv[1], "mac") == 0) {
tcl.resultf("%s", mac_->name());
return (TCL_OK);
}
if (strcmp(argv[1], "down-target") == 0) {
tcl.resultf("%s", downtarget_->name());
return (TCL_OK);
}
if (strcmp(argv[1], "up-target") == 0) {
tcl.resultf("%s", uptarget_->name());
return (TCL_OK);
}
}
return LinkDelay::command(argc, argv);
}
void LL::recv(Packet* p, Handler* /*h*/)
{
hdr_cmn *ch = HDR_CMN(p);
//char *mh = (char*) HDR_MAC(p);
//struct hdr_sr *hsr = HDR_SR(p);
/*
* Sanity Check
*/
assert(initialized());
if(p->incoming) {
p->incoming = 0;
}
// If direction = UP, then pass it up the stack
// Otherwise, set direction to DOWN and pass it down the stack
if(ch->direction() == hdr_cmn::UP) {
//if(mac_->hdr_type(mh) == ETHERTYPE_ARP)
if(ch->ptype_ == PT_ARP)
arptable_->arpinput(p, this);
else
uptarget_ ? sendUp(p) : drop(p);
return;
}
ch->direction() = hdr_cmn::DOWN;
sendDown(p);
}
void LL::sendDown(Packet* p)
{
hdr_cmn *ch = HDR_CMN(p);
hdr_ip *ih = HDR_IP(p);
// XXX HACK for now - Padma, 03/99.
nsaddr_t dst = (nsaddr_t)Address::instance().get_nodeaddr(ih->daddr());
//nsaddr_t dst = ih->dst();
hdr_ll *llh = HDR_LL(p);
char *mh = (char*)p->access(hdr_mac::offset_);
llh->seqno_ = ++seqno_;
llh->lltype() = LL_DATA;
mac_->hdr_src(mh, mac_->addr());
mac_->hdr_type(mh, ETHERTYPE_IP);
int tx = 0;
switch(ch->addr_type()) {
case NS_AF_ILINK:
mac_->hdr_dst((char*) HDR_MAC(p), ch->next_hop());
break;
case NS_AF_INET:
dst = ch->next_hop();
/* FALL THROUGH */
case NS_AF_NONE:
if (IP_BROADCAST == (u_int32_t) dst)
{
mac_->hdr_dst((char*) HDR_MAC(p), MAC_BROADCAST);
break;
}
/* Assuming arptable is present, send query */
if (arptable_) {
tx = arptable_->arpresolve(dst, p, this);
break;
}
//if (varp_) {
//tx = varp_->arpresolve(dst, p);
//break;
//}
/* FALL THROUGH */
default:
int IPnh = (lanrouter_) ? lanrouter_->next_hop(p) : -1;
if (IPnh < 0)
mac_->hdr_dst((char*) HDR_MAC(p),macDA_);
else if (varp_)
tx = varp_->arpresolve(IPnh, p);
else
mac_->hdr_dst((char*) HDR_MAC(p), IPnh);
break;
}
if (tx == 0) {
Scheduler& s = Scheduler::instance();
// let mac decide when to take a new packet from the queue.
s.schedule(downtarget_, p, delay_);
}
}
void LL::sendUp(Packet* p)
{
Scheduler& s = Scheduler::instance();
if (hdr_cmn::access(p)->error() > 0)
drop(p);
else
s.schedule(uptarget_, p, delay_);
}
loss-monitor.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1994-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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (LBL)";
#endif
#include "agent.h"
#include "config.h"
#include "tclcl.h"
#include "packet.h"
#include "ip.h"
#include "rtp.h"
class LossMonitor : public Agent {
public:
LossMonitor();
int command(int argc, const char*const* argv);
void recv(Packet* pkt, Handler*);
protected:
int nlost_;
int npkts_;
int expected_;
int bytes_;
int seqno_;
double last_packet_time_;
int off_rtp_;
};
static class LossMonitorClass : public TclClass {
public:
LossMonitorClass() : TclClass("Agent/LossMonitor") {}
TclObject* create(int, const char*const*) {
return (new LossMonitor());
}
} class_loss_mon;
LossMonitor::LossMonitor() : Agent(PT_NTYPE)
{
bytes_ = 0;
nlost_ = 0;
npkts_ = 0;
expected_ = -1;
last_packet_time_ = 0.;
seqno_ = 0;
bind("nlost_", &nlost_);
bind("npkts_", &npkts_);
bind("bytes_", &bytes_);
bind("lastPktTime_", &last_packet_time_);
bind("expected_", &expected_);
bind("off_rtp_", &off_rtp_);
}
void LossMonitor::recv(Packet* pkt, Handler*)
{
hdr_rtp* p = (hdr_rtp*)pkt->access(off_rtp_);
seqno_ = p->seqno();
bytes_ += ((hdr_cmn*)pkt->access(off_cmn_))->size();
++npkts_;
/*
* Check for lost packets
*/
if (expected_ >= 0) {
int loss = seqno_ - expected_;
if (loss > 0) {
nlost_ += loss;
Tcl::instance().evalf("%s log-loss", name());
}
}
last_packet_time_ = Scheduler::instance().clock();
expected_ = seqno_ + 1;
Packet::free(pkt);
}
/*
* $proc interval $interval
* $proc size $size
*/
int LossMonitor::command(int argc, const char*const* argv)
{
if (argc == 2) {
if (strcmp(argv[1], "clear") == 0) {
expected_ = -1;
return (TCL_OK);
}
}
return (Agent::command(argc, argv));
}
mac-802_11.cc
/* -*- 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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*/
/* Ported from CMU/Monarch's code, nov'98 -Padma.*/
#include "delay.h"
#include "connector.h"
#include "packet.h"
#include "random.h"
// #define DEBUG
//#include
#include "arp.h"
#include "ll.h"
#include "mac.h"
#include "mac-timers.h"
#include "mac-802_11.h"
#include "cmu-trace.h"
#define CHECK_BACKOFF_TIMER() \
{ \
if(is_idle() && mhBackoff_.paused()) \
mhBackoff_.resume(difs_); \
if(! is_idle() && mhBackoff_.busy() && ! mhBackoff_.paused()) \
mhBackoff_.pause(); \
}
#define TRANSMIT(p, t) \
{ \
tx_active_ = 1; \
\
/* \
* If I'm transmitting without doing CS, such as when \
* sending an ACK, any incoming packet will be "missed" \
* and hence, must be discarded. \
*/ \
if(rx_state_ != MAC_IDLE) { \
struct hdr_mac802_11 *dh = HDR_MAC802_11(p); \
\
assert(dh->dh_fc.fc_type == MAC_Type_Control); \
assert(dh->dh_fc.fc_subtype == MAC_Subtype_ACK); \
\
assert(pktRx_); \
struct hdr_cmn *ch = HDR_CMN(pktRx_); \
\
ch->error() = 1; /* force packet discard */ \
} \
\
/* \
* pass the packet on the "interface" which will in turn \
* place the packet on the channel. \
* \
* NOTE: a handler is passed along so that the Network \
* Interface can distinguish between incoming and \
* outgoing packets. \
*/ \
downtarget_->recv(p->copy(), this); \
\
mhSend_.start(t); \
\
mhIF_.start(TX_Time(p)); \
}
#define SET_RX_STATE(x) \
{ \
rx_state_ = (x); \
\
CHECK_BACKOFF_TIMER(); \
}
#define SET_TX_STATE(x) \
{ \
tx_state_ = (x); \
\
CHECK_BACKOFF_TIMER(); \
}
/* ======================================================================
Global Variables
====================================================================== */
//extern char* pt_names[];
static PHY_MIB PMIB =
{
DSSS_CWMin, DSSS_CWMax, DSSS_SlotTime, DSSS_CCATime,
DSSS_RxTxTurnaroundTime, DSSS_SIFSTime, DSSS_PreambleLength,
DSSS_PLCPHeaderLength
};
static MAC_MIB MMIB =
{
0 /* MAC_RTSThreshold */, MAC_ShortRetryLimit,
MAC_LongRetryLimit, MAC_FragmentationThreshold,
MAC_MaxTransmitMSDULifetime, MAC_MaxReceiveLifetime,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
/* ======================================================================
TCL Hooks for the simulator
====================================================================== */
static class Mac802_11Class : public TclClass {
public:
Mac802_11Class() : TclClass("Mac/802_11") {}
TclObject* create(int, const char*const*) {
return (new Mac802_11(&PMIB, &MMIB));
}
} class_mac802_11;
/* ======================================================================
Mac Class Functions
====================================================================== */
Mac802_11::Mac802_11(PHY_MIB *p, MAC_MIB *m) : Mac(),
mhIF_(this), mhNav_(this), mhRecv_(this), mhSend_(this),
mhDefer_(this, p->SlotTime), mhBackoff_(this, p->SlotTime)
{
macmib_ = m;
phymib_ = p;
nav_ = 0.0;
tx_state_ = rx_state_ = MAC_IDLE;
tx_active_ = 0;
pktRTS_ = 0;
pktCTRL_ = 0;
cw_ = phymib_->CWMin;
ssrc_ = slrc_ = 0;
sifs_ = phymib_->SIFSTime;
pifs_ = sifs_ + phymib_->SlotTime;
difs_ = sifs_ + 2*phymib_->SlotTime;
eifs_ = sifs_ + difs_ + DATA_Time(ETHER_ACK_LEN +
phymib_->PreambleLength/8 + phymib_->PLCPHeaderLength/8);
tx_sifs_ = sifs_ - phymib_->RxTxTurnaroundTime;
tx_pifs_ = tx_sifs_ + phymib_->SlotTime;
tx_difs_ = tx_sifs_ + 2 * phymib_->SlotTime;
sta_seqno_ = 1;
cache_ = 0;
cache_node_count_ = 0;
}
int
Mac802_11::command(int argc, const char*const* argv)
{
if (argc == 3) {
if (strcmp(argv[1], "log-target") == 0) {
logtarget_ = (NsObject*) TclObject::lookup(argv[2]);
if(logtarget_ == 0)
return TCL_ERROR;
return TCL_OK;
}
if(strcmp(argv[1], "nodes") == 0) {
if(cache_) return TCL_ERROR;
cache_node_count_ = atoi(argv[2]);
cache_ = new Host[cache_node_count_ + 1];
assert(cache_);
bzero(cache_, sizeof(Host) * (cache_node_count_+1 ));
return TCL_OK;
}
}
return Mac::command(argc, argv);
}
/* ======================================================================
Debugging Routines
====================================================================== */
void
Mac802_11::trace_pkt(Packet *p) {
struct hdr_cmn *ch = HDR_CMN(p);
struct hdr_mac802_11* dh = HDR_MAC802_11(p);
u_int16_t *t = (u_int16_t*) &dh->dh_fc;
fprintf(stderr, "\t[ %2x %2x %2x %2x ] %x %s %d\n",
*t, dh->dh_duration,
ETHER_ADDR(dh->dh_da), ETHER_ADDR(dh->dh_sa),
index_, packet_info.name(ch->ptype()), ch->size());
}
void
Mac802_11::dump(char *fname)
{
fprintf(stderr,
"\n%s --- (INDEX: %d, time: %2.9f)\n",
fname, index_, Scheduler::instance().clock());
fprintf(stderr,
"\ttx_state_: %x, rx_state_: %x, nav: %2.9f, idle: %d\n",
tx_state_, rx_state_, nav_, is_idle());
fprintf(stderr,
"\tpktTx_: %x, pktRx_: %x, pktRTS_: %x, pktCTRL_: %x, callback: %x\n",
(int) pktTx_, (int) pktRx_, (int) pktRTS_,
(int) pktCTRL_, (int) callback_);
fprintf(stderr,
"\tDefer: %d, Backoff: %d (%d), Recv: %d, Timer: %d Nav: %d\n",
mhDefer_.busy(), mhBackoff_.busy(), mhBackoff_.paused(),
mhRecv_.busy(), mhSend_.busy(), mhNav_.busy());
fprintf(stderr,
"\tBackoff Expire: %f\n",
mhBackoff_.expire());
}
/* ======================================================================
Packet Headers Routines
====================================================================== */
inline int
Mac802_11::hdr_dst(char* hdr, int dst )
{
struct hdr_mac802_11 *dh = (struct hdr_mac802_11*) hdr;
//dst = (u_int32_t)(dst);
if(dst > -2)
STORE4BYTE(&dst, (dh->dh_da));
return ETHER_ADDR(dh->dh_da);
}
inline int
Mac802_11::hdr_src(char* hdr, int src )
{
struct hdr_mac802_11 *dh = (struct hdr_mac802_11*) hdr;
//src = (u_int32_t)(src);
if(src > -2)
STORE4BYTE(&src, (dh->dh_sa));
return ETHER_ADDR(dh->dh_sa);
}
inline int
Mac802_11::hdr_type(char* hdr, u_int16_t type)
{
struct hdr_mac802_11 *dh = (struct hdr_mac802_11*) hdr;
if(type)
//*((u_int16_t*) dh->dh_body) = type;
STORE2BYTE(&type,(dh->dh_body));
//return *((u_int16_t*) dh->dh_body);
return GET2BYTE(dh->dh_body);
}
/* ======================================================================
Misc Routines
====================================================================== */
inline int
Mac802_11::is_idle()
{
if(rx_state_ != MAC_IDLE)
return 0;
if(tx_state_ != MAC_IDLE)
return 0;
if(nav_ > Scheduler::instance().clock())
return 0;
return 1;
}
void
Mac802_11::discard(Packet *p, const char* why)
{
hdr_mac802_11* mh = HDR_MAC802_11(p);
hdr_cmn *ch = HDR_CMN(p);
#if 0
/* old logic 8/8/98 -dam */
/*
* If received below the RXThreshold, then just free.
*/
if(p->txinfo_.Pr < p->txinfo_.ant.RXThresh) {
Packet::free(p);
//p = 0;
return;
}
#endif // 0
/* if the rcvd pkt contains errors, a real MAC layer couldn't
necessarily read any data from it, so we just toss it now */
if(ch->error() != 0) {
Packet::free(p);
//p = 0;
return;
}
switch(mh->dh_fc.fc_type) {
case MAC_Type_Management:
drop(p, why);
//drop(p);
return;
case MAC_Type_Control:
switch(mh->dh_fc.fc_subtype) {
case MAC_Subtype_RTS:
if((u_int32_t)ETHER_ADDR(mh->dh_sa) == \
(u_int32_t)index_) {
drop(p, why);
return;
}
/* fall through - if necessary */
case MAC_Subtype_CTS:
case MAC_Subtype_ACK:
if((u_int32_t)ETHER_ADDR(mh->dh_da) == \
(u_int32_t)index_) {
drop(p, why);
return;
}
break;
default:
fprintf(stderr, "invalid MAC Control subtype\n");
exit(1);
}
break;
case MAC_Type_Data:
switch(mh->dh_fc.fc_subtype) {
case MAC_Subtype_Data:
if((u_int32_t)ETHER_ADDR(mh->dh_da) == \
(u_int32_t)index_ ||
(u_int32_t)ETHER_ADDR(mh->dh_sa) == \
(u_int32_t)index_ ||
(u_int32_t)ETHER_ADDR(mh->dh_da) == MAC_BROADCAST) {
//if (*(mh->dh_da) == (u_char)index_ ||
// *(mh->dh_sa) == (u_char)index_ ||
// *(mh->dh_sa) == (u_char)MAC_BROADCAST) {
//drop(p, why);
drop(p);
return;
}
break;
default:
fprintf(stderr, "invalid MAC Data subtype\n");
exit(1);
}
break;
default:
fprintf(stderr, "invalid MAC type (%x)\n", mh->dh_fc.fc_type);
trace_pkt(p);
exit(1);
}
Packet::free(p);
//p = 0;
}
void
Mac802_11::capture(Packet *p)
{
/*
* Update the NAV so that this does not screw
* up carrier sense.
*/
set_nav(usec(eifs_ + TX_Time(p)));
Packet::free(p);
//p = 0;
}
void
Mac802_11::collision(Packet *p)
{
switch(rx_state_) {
case MAC_RECV:
SET_RX_STATE(MAC_COLL);
/* fall through */
case MAC_COLL:
assert(pktRx_);
assert(mhRecv_.busy());
/*
* Since a collision has occurred, figure out
* which packet that caused the collision will
* "last" the longest. Make this packet,
* pktRx_ and reset the Recv Timer if necessary.
*/
if(TX_Time(p) > mhRecv_.expire()) {
mhRecv_.stop();
discard(pktRx_, DROP_MAC_COLLISION);
pktRx_ = p;
mhRecv_.start(TX_Time(pktRx_));
}
else {
discard(p, DROP_MAC_COLLISION);
}
break;
default:
assert(0);
}
}
void
Mac802_11::tx_resume()
{
assert(mhSend_.busy() == 0);
assert(mhDefer_.busy() == 0);
if(pktCTRL_) {
/*
* Need to send a CTS or ACK.
*/
mhDefer_.start(sifs_);
}
else if(pktRTS_) {
if(mhBackoff_.busy() == 0)
mhDefer_.start(difs_);
}
else if(pktTx_) {
if(mhBackoff_.busy() == 0)
mhDefer_.start(difs_);
}
else if(callback_) {
Handler *h = callback_;
callback_ = 0;
h->handle((Event*) 0);
}
SET_TX_STATE(MAC_IDLE);
}
void
Mac802_11::rx_resume()
{
assert(pktRx_ == 0);
assert(mhRecv_.busy() == 0);
SET_RX_STATE(MAC_IDLE);
}
/* ======================================================================
Timer Handler Routines
====================================================================== */
void
Mac802_11::backoffHandler()
{
if(pktCTRL_) {
assert(mhSend_.busy() || mhDefer_.busy());
return;
}
if(check_pktRTS() == 0)
return;
if(check_pktTx() == 0)
return;
}
void
Mac802_11::deferHandler()
{
assert(pktCTRL_ || pktRTS_ || pktTx_);
if(check_pktCTRL() == 0)
return;
assert(mhBackoff_.busy() == 0);
//if (mhBackoff_.busy() != 0)
//{
// printf("deferHandler:mhBackoff_ busy!\n");
// return;
//}
if(check_pktRTS() == 0)
return;
if(check_pktTx() == 0)
return;
}
void
Mac802_11::navHandler()
{
if(is_idle() && mhBackoff_.paused())
mhBackoff_.resume(difs_);
}
void
Mac802_11::recvHandler()
{
recv_timer();
}
void
Mac802_11::sendHandler()
{
send_timer();
}
void
Mac802_11::txHandler()
{
tx_active_ = 0;
}
/* ======================================================================
The "real" Timer Handler Routines
====================================================================== */
void
Mac802_11::send_timer()
{
switch(tx_state_) {
/*
* Sent a RTS, but did not receive a CTS.
*/
case MAC_RTS:
RetransmitRTS();
break;
/*
* Sent a CTS, but did not receive a DATA packet.
*/
case MAC_CTS:
assert(pktCTRL_);
Packet::free(pktCTRL_); pktCTRL_ = 0;
break;
/*
* Sent DATA, but did not receive an ACK packet.
*/
case MAC_SEND:
RetransmitDATA();
break;
/*
* Sent an ACK, and now ready to resume transmission.
*/
case MAC_ACK:
assert(pktCTRL_);
Packet::free(pktCTRL_); pktCTRL_ = 0;
break;
case MAC_IDLE:
break;
default:
assert(0);
}
tx_resume();
}
/* ======================================================================
Outgoing Packet Routines
====================================================================== */
int
Mac802_11::check_pktCTRL()
{
struct hdr_mac802_11 *mh;
double timeout;
if(pktCTRL_ == 0)
return -1;
if(tx_state_ == MAC_CTS || tx_state_ == MAC_ACK)
return -1;
mh = HDR_MAC802_11(pktCTRL_);
switch(mh->dh_fc.fc_subtype) {
/*
* If the medium is not IDLE, don't send the CTS.
*/
case MAC_Subtype_CTS:
if(! is_idle()) {
discard(pktCTRL_, DROP_MAC_BUSY); pktCTRL_ = 0;
return 0;
}
SET_TX_STATE(MAC_CTS);
timeout = (mh->dh_duration * 1e-6) + CTS_Time; // XXX
break;
/*
* IEEE 802.11 specs, section 9.2.8
* Acknowledments are sent after an SIFS, without regard to
* the busy/idle state of the medium.
*/
case MAC_Subtype_ACK:
SET_TX_STATE(MAC_ACK);
timeout = ACK_Time;
break;
default:
fprintf(stderr, "check_pktCTRL:Invalid MAC Control subtype\n");
exit(1);
}
TRANSMIT(pktCTRL_, timeout);
return 0;
}
int
Mac802_11::check_pktRTS()
{
struct hdr_mac802_11 *mh;
double timeout;
assert(mhBackoff_.busy() == 0);
if(pktRTS_ == 0)
return -1;
//struct hdr_cmn *ch = HDR_CMN(pktRTS_);
mh = HDR_MAC802_11(pktRTS_);
switch(mh->dh_fc.fc_subtype) {
case MAC_Subtype_RTS:
if(! is_idle()) {
inc_cw();
mhBackoff_.start(cw_, is_idle());
return 0;
}
SET_TX_STATE(MAC_RTS);
timeout = CTSTimeout;
break;
default:
fprintf(stderr, "check_pktRTS:Invalid MAC Control subtype\n");
exit(1);
}
TRANSMIT(pktRTS_, timeout);
return 0;
}
int
Mac802_11::check_pktTx()
{
struct hdr_mac802_11 *mh;
double timeout;
assert(mhBackoff_.busy() == 0);
if(pktTx_ == 0)
return -1;
mh = HDR_MAC802_11(pktTx_);
int len = HDR_CMN(pktTx_)->size();
switch(mh->dh_fc.fc_subtype) {
case MAC_Subtype_Data:
if(! is_idle()) {
sendRTS(ETHER_ADDR(mh->dh_da));
inc_cw();
mhBackoff_.start(cw_, is_idle());
return 0;
}
SET_TX_STATE(MAC_SEND);
if((u_int32_t)ETHER_ADDR(mh->dh_da) != MAC_BROADCAST)
//timeout = ACKTimeout(netif_->txtime(pktTx_))+5;
// why 10 ? buggy
//timeout = ACKTimeout(len) + 10;
timeout = ACKTimeout(len);
else
timeout = TX_Time(pktTx_);
break;
default:
fprintf(stderr, "check_pktTx:Invalid MAC Control subtype\n");
//printf("pktRTS:%x, pktCTS/ACK:%x, pktTx:%x\n",pktRTS_, pktCTRL_,pktTx_);
exit(1);
}
TRANSMIT(pktTx_, timeout);
return 0;
}
/*
* Low-level transmit functions that actually place the packet onto
* the channel.
*/
void
Mac802_11::sendRTS(int dst)
{
Packet *p = Packet::alloc();
hdr_cmn* ch = HDR_CMN(p);
struct rts_frame *rf = (struct rts_frame*)p->access(hdr_mac::offset_);
//struct hdr_mac802_11 *mh = HDR_MAC802_11(p);
assert(pktTx_);
assert(pktRTS_ == 0);
/*
* If the size of the packet is larger than the
* RTSThreshold, then perform the RTS/CTS exchange.
*
* XXX: also skip if destination is a broadcast
*/
if( (u_int32_t) HDR_CMN(pktTx_)->size() < macmib_->RTSThreshold ||
(u_int32_t) dst == MAC_BROADCAST) {
Packet::free(p);
//p = 0;
return;
}
ch->uid() = 0;
ch->ptype() = PT_MAC;
ch->size() = ETHER_RTS_LEN;
ch->iface() = -2;
ch->error() = 0;
bzero(rf, MAC_HDR_LEN);
rf->rf_fc.fc_protocol_version = MAC_ProtocolVersion;
rf->rf_fc.fc_type = MAC_Type_Control;
rf->rf_fc.fc_subtype = MAC_Subtype_RTS;
rf->rf_fc.fc_to_ds = 0;
rf->rf_fc.fc_from_ds = 0;
rf->rf_fc.fc_more_frag = 0;
rf->rf_fc.fc_retry = 0;
rf->rf_fc.fc_pwr_mgt = 0;
rf->rf_fc.fc_more_data = 0;
rf->rf_fc.fc_wep = 0;
rf->rf_fc.fc_order = 0;
rf->rf_duration = RTS_DURATION(pktTx_);
//ETHER_ADDR(rf->rf_ra) = dst;
STORE4BYTE(&dst, (rf->rf_ra));
//ETHER_ADDR(rf->rf_ta) = index_;
STORE4BYTE(&index_, (rf->rf_ta));
// rf->rf_fcs;
pktRTS_ = p;
}
void
Mac802_11::sendCTS(int dst, double rts_duration)
{
Packet *p = Packet::alloc();
hdr_cmn* ch = HDR_CMN(p);
struct cts_frame *cf = (struct cts_frame*)p->access(hdr_mac::offset_);
//struct hdr_mac802_11 *mh = HDR_MAC802_11(p);
assert(pktCTRL_ == 0);
ch->uid() = 0;
ch->ptype() = PT_MAC;
ch->size() = ETHER_CTS_LEN;
ch->iface() = -2;
ch->error() = 0;
//ch->direction() = hdr_cmn::DOWN;
bzero(cf, MAC_HDR_LEN);
cf->cf_fc.fc_protocol_version = MAC_ProtocolVersion;
cf->cf_fc.fc_type = MAC_Type_Control;
cf->cf_fc.fc_subtype = MAC_Subtype_CTS;
cf->cf_fc.fc_to_ds = 0;
cf->cf_fc.fc_from_ds = 0;
cf->cf_fc.fc_more_frag = 0;
cf->cf_fc.fc_retry = 0;
cf->cf_fc.fc_pwr_mgt = 0;
cf->cf_fc.fc_more_data = 0;
cf->cf_fc.fc_wep = 0;
cf->cf_fc.fc_order = 0;
cf->cf_duration = CTS_DURATION(rts_duration);
//ETHER_ADDR(cf->cf_ra) = dst;
STORE4BYTE(&dst, (cf->cf_ra));
//STORE4BYTE(&dst, (mh->dh_da));
// cf->cf_fcs;
pktCTRL_ = p;
}
void
Mac802_11::sendACK(int dst)
{
Packet *p = Packet::alloc();
hdr_cmn* ch = HDR_CMN(p);
struct ack_frame *af = (struct ack_frame*)p->access(hdr_mac::offset_);
//struct hdr_mac802_11 *mh = HDR_MAC802_11(p);
assert(pktCTRL_ == 0);
ch->uid() = 0;
ch->ptype() = PT_MAC;
ch->size() = ETHER_ACK_LEN;
ch->iface() = -2;
ch->error() = 0;
//ch->direction() = hdr_cmn::DOWN;
bzero(af, MAC_HDR_LEN);
af->af_fc.fc_protocol_version = MAC_ProtocolVersion;
af->af_fc.fc_type = MAC_Type_Control;
af->af_fc.fc_subtype = MAC_Subtype_ACK;
af->af_fc.fc_to_ds = 0;
af->af_fc.fc_from_ds = 0;
af->af_fc.fc_more_frag = 0;
af->af_fc.fc_retry = 0;
af->af_fc.fc_pwr_mgt = 0;
af->af_fc.fc_more_data = 0;
af->af_fc.fc_wep = 0;
af->af_fc.fc_order = 0;
af->af_duration = ACK_DURATION();
//ETHER_ADDR(af->af_ra) = dst;
STORE4BYTE(&dst, (af->af_ra));
// af->af_fcs;
pktCTRL_ = p;
}
void
Mac802_11::sendDATA(Packet *p)
{
hdr_cmn* ch = HDR_CMN(p);
struct hdr_mac802_11* dh = HDR_MAC802_11(p);
assert(pktTx_ == 0);
/*
* Update the MAC header
*/
ch->size() += ETHER_HDR_LEN;
dh->dh_fc.fc_protocol_version = MAC_ProtocolVersion;
dh->dh_fc.fc_type = MAC_Type_Data;
dh->dh_fc.fc_subtype = MAC_Subtype_Data;
//printf(".....p = %x, mac-subtype-%d\n",p,dh->dh_fc.fc_subtype);
dh->dh_fc.fc_to_ds = 0;
dh->dh_fc.fc_from_ds = 0;
dh->dh_fc.fc_more_frag = 0;
dh->dh_fc.fc_retry = 0;
dh->dh_fc.fc_pwr_mgt = 0;
dh->dh_fc.fc_more_data = 0;
dh->dh_fc.fc_wep = 0;
dh->dh_fc.fc_order = 0;
if((u_int32_t)ETHER_ADDR(dh->dh_da) != MAC_BROADCAST)
dh->dh_duration = DATA_DURATION();
else
dh->dh_duration = 0;
pktTx_ = p;
}
/* ======================================================================
Retransmission Routines
====================================================================== */
void
Mac802_11::RetransmitRTS()
{
assert(pktTx_);
assert(pktRTS_);
assert(mhBackoff_.busy() == 0);
macmib_->RTSFailureCount++;
ssrc_ += 1; // STA Short Retry Count
if(ssrc_ >= macmib_->ShortRetryLimit) {
discard(pktRTS_, DROP_MAC_RETRY_COUNT_EXCEEDED); pktRTS_ = 0;
/* tell the callback the send operation failed
before discarding the packet */
hdr_cmn *ch = HDR_CMN(pktTx_);
if (ch->xmit_failure_) {
/*
* Need to remove the MAC header so that
* re-cycled packets don't keep getting
* bigger.
*/
ch->size() -= ETHER_HDR_LEN;
ch->xmit_reason_ = XMIT_REASON_RTS;
ch->xmit_failure_(pktTx_->copy(),
ch->xmit_failure_data_);
}
//printf("(%d)....discarding RTS:%x\n",index_,pktRTS_);
discard(pktTx_, DROP_MAC_RETRY_COUNT_EXCEEDED); pktTx_ = 0;
ssrc_ = 0;
rst_cw();
}
else {
//printf("(%d)...retxing RTS:%x\n",index_,pktRTS_);
struct rts_frame *rf;
rf = (struct rts_frame*)pktRTS_->access(hdr_mac::offset_);
rf->rf_fc.fc_retry = 1;
inc_cw();
mhBackoff_.start(cw_, is_idle());
}
}
void
Mac802_11::RetransmitDATA()
{
struct hdr_cmn *ch;
struct hdr_mac802_11 *mh;
u_int32_t *rcount, *thresh;
assert(mhBackoff_.busy() == 0);
assert(pktTx_);
assert(pktRTS_ == 0);
ch = HDR_CMN(pktTx_);
mh = HDR_MAC802_11(pktTx_);
/*
* Broadcast packets don't get ACKed and therefore
* are never retransmitted.
*/
if((u_int32_t)ETHER_ADDR(mh->dh_da) == MAC_BROADCAST) {
Packet::free(pktTx_); pktTx_ = 0;
/*
* Backoff at end of TX.
*/
rst_cw();
mhBackoff_.start(cw_, is_idle());
return;
}
macmib_->ACKFailureCount++;
if((u_int32_t) ch->size() <= macmib_->RTSThreshold) {
rcount = &ssrc_;
thresh = &macmib_->ShortRetryLimit;
}
else {
rcount = &slrc_;
thresh = &macmib_->LongRetryLimit;
}
(*rcount)++;
if(*rcount > *thresh) {
macmib_->FailedCount++;
/* tell the callback the send operation failed
before discarding the packet */
hdr_cmn *ch = HDR_CMN(pktTx_);
if (ch->xmit_failure_) {
ch->size() -= ETHER_HDR_LEN;
ch->xmit_reason_ = XMIT_REASON_ACK;
ch->xmit_failure_(pktTx_->copy(),
ch->xmit_failure_data_);
}
discard(pktTx_, DROP_MAC_RETRY_COUNT_EXCEEDED); pktTx_ = 0;
//printf("(%d)DATA discarded: count exceeded\n",index_);
*rcount = 0;
rst_cw();
}
else {
struct hdr_mac802_11 *dh;
dh = HDR_MAC802_11(pktTx_);
dh->dh_fc.fc_retry = 1;
sendRTS(ETHER_ADDR(mh->dh_da));
//printf("(%d)retxing data:%x..sendRTS..\n",index_,pktTx_);
inc_cw();
mhBackoff_.start(cw_, is_idle());
}
}
/* ======================================================================
Incoming Packet Routines
====================================================================== */
void
Mac802_11::send(Packet *p, Handler *h)
{
struct hdr_mac802_11* dh = HDR_MAC802_11(p);
/*
* drop the packet if the node is in sleep mode
XXX sleep mode can't stop node from sending packets
*/
if ((netif_->node())->sleep()) {
netif_->node()->set_node_sleep(0);
netif_->node()->set_node_state(INROUTE);
}
callback_ = h;
sendDATA(p);
sendRTS(ETHER_ADDR(dh->dh_da));
/*
* Assign the data packet a sequence number.
*/
dh->dh_scontrol = sta_seqno_++;
/*
* If the medium is IDLE, we must wait for a DIFS
* Space before transmitting.
*/
if(mhBackoff_.busy() == 0) {
if(is_idle()) {
/*
* If we are already deferring, there is no
* need to reset the Defer timer.
*/
if(mhDefer_.busy() == 0)
mhDefer_.start(difs_);
}
/*
* If the medium is NOT IDLE, then we start
* the backoff timer.
*/
else {
mhBackoff_.start(cw_, is_idle());
}
}
}
void
Mac802_11::recv(Packet *p, Handler *h)
{
struct hdr_cmn *hdr = HDR_CMN(p);
//hdr_mac802_11 *mh = HDR_MAC802_11(p);
//u_int32_t dst = ETHER_ADDR(mh->dh_da);
//u_int32_t src = ETHER_ADDR(mh->dh_sa);
/*
* Sanity Check
*/
assert(initialized());
/*
* Handle outgoing packets.
*/
if(hdr->direction() == hdr_cmn::DOWN) {
send(p, h);
return;
}
/*
* Handle incoming packets.
*
* We just received the 1st bit of a packet on the network
* interface.
*
*/
/*
* If the interface is currently in transmit mode, then
* it probably won't even see this packet. However, the
* "air" around me is BUSY so I need to let the packet
* proceed. Just set the error flag in the common header
* to that the packet gets thrown away.
*/
if(tx_active_ && hdr->error() == 0) {
hdr->error() = 1;
}
if(rx_state_ == MAC_IDLE) {
SET_RX_STATE(MAC_RECV);
pktRx_ = p;
/*
* Schedule the reception of this packet, in
* txtime seconds.
*/
mhRecv_.start(TX_Time(p));
}
else {
/*
* If the power of the incoming packet is smaller than the
* power of the packet currently being received by at least
* the capture threshold, then we ignore the new packet.
*/
if(pktRx_->txinfo_.RxPr / p->txinfo_.RxPr >= p->txinfo_.CPThresh) {
capture(p);
}
else {
collision(p);
}
}
}
void
Mac802_11::recv_timer()
{
u_int32_t src;
hdr_cmn *ch = HDR_CMN(pktRx_);
hdr_mac802_11 *mh = HDR_MAC802_11(pktRx_);
u_int32_t dst = ETHER_ADDR(mh->dh_da);
// XXX debug
//struct cts_frame *cf = (struct cts_frame*)pktRx_->access(hdr_mac::offset_);
//u_int32_t src = ETHER_ADDR(mh->dh_sa);
u_int8_t type = mh->dh_fc.fc_type;
u_int8_t subtype = mh->dh_fc.fc_subtype;
assert(pktRx_);
assert(rx_state_ == MAC_RECV || rx_state_ == MAC_COLL);
/*
* If the interface is in TRANSMIT mode when this packet
* "arrives", then I would never have seen it and should
* do a silent discard without adjusting the NAV.
*/
if(tx_active_) {
Packet::free(pktRx_);
goto done;
}
/*
* Handle collisions.
*/
if(rx_state_ == MAC_COLL) {
discard(pktRx_, DROP_MAC_COLLISION);
set_nav(usec(eifs_));
goto done;
}
/*
* Check to see if this packet was received with enough
* bit errors that the current level of FEC still could not
* fix all of the problems - ie; after FEC, the checksum still
* failed.
*/
if( ch->error() ) {
Packet::free(pktRx_);
set_nav(usec(eifs_));
goto done;
}
/*
* IEEE 802.11 specs, section 9.2.5.6
* - update the NAV (Network Allocation Vector)
*/
if(dst != (u_int32_t)index_) {
set_nav(mh->dh_duration);
}
/* tap out - */
if (tap_ && type == MAC_Type_Data &&
MAC_Subtype_Data == subtype )
tap_->tap(pktRx_);
/*
* Adaptive Fidelity Algorithm Support - neighborhood infomation collection
*
* Hacking: Before filter the packet, log the neighbor node
* I can hear the packet, the src is my neighbor
*/
if (netif_->node()->adaptivefidelity()) {
src = ETHER_ADDR(mh->dh_sa);
netif_->node()->add_neighbor(src);
}
/*
* Address Filtering
*/
if(dst != (u_int32_t)index_ && dst != MAC_BROADCAST) {
/*
* We don't want to log this event, so we just free
* the packet instead of calling the drop routine.
*/
discard(pktRx_, "---");
goto done;
}
switch(type) {
case MAC_Type_Management:
discard(pktRx_, DROP_MAC_PACKET_ERROR);
goto done;
break;
case MAC_Type_Control:
switch(subtype) {
case MAC_Subtype_RTS:
recvRTS(pktRx_);
break;
case MAC_Subtype_CTS:
recvCTS(pktRx_);
break;
case MAC_Subtype_ACK:
recvACK(pktRx_);
break;
default:
fprintf(stderr,"recvTimer1:Invalid MAC Control Subtype %x\n",
subtype);
exit(1);
}
break;
case MAC_Type_Data:
switch(subtype) {
case MAC_Subtype_Data:
recvDATA(pktRx_);
break;
default:
fprintf(stderr, "recv_timer2:Invalid MAC Data Subtype %x\n",
subtype);
exit(1);
}
break;
default:
fprintf(stderr, "recv_timer3:Invalid MAC Type %x\n", subtype);
exit(1);
}
done:
pktRx_ = 0;
rx_resume();
}
void
Mac802_11::recvRTS(Packet *p)
{
struct rts_frame *rf = (struct rts_frame*)p->access(hdr_mac::offset_);
if(tx_state_ != MAC_IDLE) {
discard(p, DROP_MAC_BUSY);
return;
}
/*
* If I'm responding to someone else, discard this RTS.
*/
if(pktCTRL_) {
discard(p, DROP_MAC_BUSY);
return;
}
sendCTS(ETHER_ADDR(rf->rf_ta), rf->rf_duration);
/*
* Stop deferring - will be reset in tx_resume().
*/
if(mhDefer_.busy()) mhDefer_.stop();
tx_resume();
mac_log(p);
}
void
Mac802_11::recvCTS(Packet *p)
{
if(tx_state_ != MAC_RTS) {
discard(p, DROP_MAC_INVALID_STATE);
return;
}
assert(pktRTS_);
Packet::free(pktRTS_); pktRTS_ = 0;
assert(pktTx_);
// debug
//struct hdr_mac802_11 *mh = HDR_MAC802_11(pktTx_);
//printf("(%d):recvCTS:pktTx_-%x,mac-subtype-%d & pktCTS_:%x\n",index_,pktTx_,mh->dh_fc.fc_subtype,p);
mhSend_.stop();
/*
* The successful reception of this CTS packet implies
* that our RTS was successful. Hence, we can reset
* the Short Retry Count and the CW.
*/
ssrc_ = 0;
rst_cw();
tx_resume();
mac_log(p);
}
void
Mac802_11::recvDATA(Packet *p)
{
struct hdr_mac802_11 *dh = HDR_MAC802_11(p);
u_int32_t dst, src, size;
{ struct hdr_cmn *ch = HDR_CMN(p);
dst = ETHER_ADDR(dh->dh_da);
src = ETHER_ADDR(dh->dh_sa);
size = ch->size();
/*
* Adjust the MAC packet size - ie; strip
* off the mac header
*/
ch->size() -= ETHER_HDR_LEN;
ch->num_forwards() += 1;
}
/*
* If we sent a CTS, clean up...
*/
if(dst != MAC_BROADCAST) {
if(size >= macmib_->RTSThreshold) {
if (tx_state_ == MAC_CTS) {
assert(pktCTRL_);
Packet::free(pktCTRL_); pktCTRL_ = 0;
mhSend_.stop();
/*
* Our CTS got through.
*/
//printf("(%d): RECVING DATA!\n",index_);
ssrc_ = 0;
rst_cw();
}
else {
discard(p, DROP_MAC_BUSY);
//printf("(%d)..discard DATA\n",index_);
return;
}
sendACK(src);
tx_resume();
}
/*
* We did not send a CTS and there's no
* room to buffer an ACK.
*/
else {
if(pktCTRL_) {
discard(p, DROP_MAC_BUSY);
return;
}
sendACK(src);
if(mhSend_.busy() == 0)
tx_resume();
}
}
/* ============================================================
Make/update an entry in our sequence number cache.
============================================================ */
if(dst != MAC_BROADCAST) {
Host *h = &cache_[src];
if(h->seqno && h->seqno == dh->dh_scontrol) {
discard(p, DROP_MAC_DUPLICATE);
return;
}
h->seqno = dh->dh_scontrol;
}
/*
* Pass the packet up to the link-layer.
* XXX - we could schedule an event to account
* for this processing delay.
*/
p->incoming = 1;
uptarget_->recv(p, (Handler*) 0);
}
void
Mac802_11::recvACK(Packet *p)
{
struct hdr_cmn *ch = HDR_CMN(p);
if(tx_state_ != MAC_SEND) {
discard(p, DROP_MAC_INVALID_STATE);
return;
}
//printf("(%d)...................recving ACK:%x\n",index_,p);
assert(pktTx_);
Packet::free(pktTx_); pktTx_ = 0;
mhSend_.stop();
/*
* The successful reception of this ACK packet implies
* that our DATA transmission was successful. Hence,
* we can reset the Short/Long Retry Count and the CW.
*/
if((u_int32_t) ch->size() <= macmib_->RTSThreshold)
ssrc_ = 0;
else
slrc_ = 0;
/*
* Backoff before sending again.
*/
rst_cw();
assert(mhBackoff_.busy() == 0);
mhBackoff_.start(cw_, is_idle());
tx_resume();
mac_log(p);
}
mac-802_3.cc
/*
mac-802_3.cc
$Id: AllCode__.html 980 2002-12-12 09:36:58Z ffilali $
*/
#include
#include
#include
#include
#include
//#define MAC_DEBUG
#ifndef MAC_DEBUG
#define FPRINTF(s, f, t, index, func) do {} while (0)
#else
static double xtime= 0.0;
# define FPRINTF(s, f, t, index, func) \
do { fprintf(s, f, t, index, func); xtime= t; } while (0)
#endif MAC_DEBUG
inline void MacHandler::cancel() {
FPRINTF(stderr, "%.15f : %d : %s\n", Scheduler::instance().clock(), mac->index_, __PRETTY_FUNCTION__);
Scheduler& s = Scheduler::instance();
assert(busy_);
s.cancel(&intr);
busy_ = 0;
}
inline void Mac8023HandlerSend::cancel() {
assert(busy_);
Scheduler &s= Scheduler::instance();
s.cancel(&intr);
busy_= 0;
p_= 0;
}
inline void MacHandlerRecv::cancel() {
Scheduler& s = Scheduler::instance();
assert(busy_ && p_);
s.cancel(&intr);
busy_ = 0;
Packet::free(p_);
p_= 0;
}
inline void MacHandlerRetx::cancel() {
Scheduler& s = Scheduler::instance();
assert(busy_ && p_);
s.cancel(&intr);
}
inline void MacHandlerIFS::cancel() {
//fprintf (stderr, "cancelled dtime= %.15f\n", intr.time_- Scheduler::instance().clock());
MacHandler::cancel();
}
static class Mac802_3Class : public TclClass {
public:
Mac802_3Class() : TclClass("Mac/802_3") {}
TclObject* create(int, const char*const*) {
return (new Mac802_3);
}
} class_mac802_3;
void Mac8023HandlerSend::handle(Event*) {
FPRINTF(stderr, "%.15f : %d : %s\n", Scheduler::instance().clock(), mac->index_, __PRETTY_FUNCTION__);
assert(p_);
/* Transmission completed successfully */
busy_ = 0;
p_= 0;
mac->mhRetx_.free();
mac->mhRetx_.reset();
mac->mhIFS_.schedule(mac->netif_->txtime(IEEE_8023_IFS_BITS/8.0));
}
void Mac8023HandlerSend::schedule(const Packet *p, double t) {
FPRINTF(stderr, "%.15f : %d : %s\n", Scheduler::instance().clock(), mac->index_, __PRETTY_FUNCTION__);
Scheduler& s = Scheduler::instance();
assert(!busy_);
s.schedule(this, &intr, t);
busy_ = 1;
p_= p;
}
void MacHandlerRecv::handle(Event* ) {
/* Reception Successful */
FPRINTF(stderr, "%.15f : %d : %s\n", Scheduler::instance().clock(), mac->index_, __PRETTY_FUNCTION__);
busy_ = 0;
mac->recv_complete(p_);
p_= 0;
}
void MacHandlerRecv::schedule(Packet *p, double t) {
FPRINTF(stderr, "%.15f : %d : %s\n", Scheduler::instance().clock(), mac->index_, __PRETTY_FUNCTION__);
Scheduler& s = Scheduler::instance();
assert(p && !busy_);
s.schedule(this, &intr, t);
busy_ = 1;
p_ = p;
}
bool MacHandlerRetx::schedule(double delta) {
FPRINTF(stderr, "%.15f : %d : %s\n", Scheduler::instance().clock(), mac->index_, __PRETTY_FUNCTION__);
Scheduler& s = Scheduler::instance();
assert(p_ && !busy_);
int k, r;
if(try_ < IEEE_8023_ALIMIT) {
k = min(try_, IEEE_8023_BLIMIT);
r = Random::integer(1 << k);
s.schedule(this, &intr, r * mac->netif_->txtime(IEEE_8023_SLOT_BITS/8.0) + delta);
busy_ = 1;
return true;
}
return false;
}
void MacHandlerRetx::handle(Event *) {
FPRINTF(stderr, "%.15f : %d : %s\n", Scheduler::instance().clock(), mac->index_, __PRETTY_FUNCTION__);
assert(p_);
busy_= 0;
++try_;
mac->transmit(p_);
}
inline void MacHandlerIFS::schedule(double t) {
FPRINTF(stderr, "%.15f : %d : %s\n", Scheduler::instance().clock(), mac->index_, __PRETTY_FUNCTION__);
assert(!busy_);
Scheduler &s= Scheduler::instance();
s.schedule(this, &intr, t);
busy_= 1;
}
inline void MacHandlerIFS::handle(Event*) {
FPRINTF(stderr, "%.15f : %d : %s\n", Scheduler::instance().clock(), mac->index_, __PRETTY_FUNCTION__);
busy_= 0;
mac->resume();
}
Mac802_3::Mac802_3() : Mac(),
mhRecv_(this), mhRetx_(this), mhIFS_(this), mhSend_(this) {
}
void Mac802_3::sendUp(Packet *p, Handler *) {
FPRINTF(stderr, "%.15f : %d : %s\n", Scheduler::instance().clock(), index_, __PRETTY_FUNCTION__);
/* just received the 1st bit of a packet */
if (state_ != MAC_IDLE && mhIFS_.busy()) {
// this must mean either that
// a. mhIFS_ is about to expire now - concurrent events ordering - or
// b. (txtime + ifs + propdelay) < (propdelay + txtime + ifs) for the prev. packet
// so we assume that IFS is over and resume
#ifdef MAC_DEBUG
#define EPS 1.0e-15
if (mhIFS_.expire() - Scheduler::instance().clock() > EPS) {
fprintf(stderr, "mhIFS_: %.20f, time= %.20f, diff= %e\n",
mhIFS_.expire(), Scheduler::instance().clock(),
mhIFS_.expire() - Scheduler::instance().clock());
assert(0);
}
#undef EPS
#endif
mhIFS_.cancel();
resume();
}
if(state_ == MAC_IDLE) {
state_ = MAC_RECV;
assert(!mhRecv_.busy());
/* the last bit will arrive in txtime seconds */
mhRecv_.schedule(p, netif_->txtime(p));
} else {
collision(p); //received packet while sending or receiving
}
}
void Mac802_3::sendDown(Packet *p, Handler *h) {
FPRINTF(stderr, "%.15f : %d : %s\n", Scheduler::instance().clock(), index_, __PRETTY_FUNCTION__);
assert(initialized());
assert(h);
assert(netif_->txtime(IEEE_8023_MINFRAME) >
2*netif_->channel()->maxdelay()); /* max prop. delay is limited by specs:
about 25us for 10Mbps
and 2.5us for 100Mbps
*/
int size= (HDR_CMN(p)->size() += ETHER_HDR_LEN); //XXX also preamble?
hdr_mac *mh= HDR_MAC(p);
mh->padding_= 0;
if (size > IEEE_8023_MAXFRAME) {
static bool warnedMAX= false;
if (!warnedMAX) {
fprintf(stderr, "Mac802_3: frame is too big: %d\n", size);
warnedMAX= true;
}
} else if (size < IEEE_8023_MINFRAME) {
// pad it to fit MINFRAME
mh->padding_= IEEE_8023_MINFRAME - size;
HDR_CMN(p)->size() += mh->padding_;
}
callback_ = h;
mhRetx_.packet(p); //packet's buffered by mhRetx in case of retransmissions
transmit(p);
}
void Mac802_3::transmit(Packet *p) {
FPRINTF(stderr, "%.15f : %d : %s\n", Scheduler::instance().clock(), index_, __PRETTY_FUNCTION__);
assert(callback_);
if(mhSend_.packet()) {
fprintf(stderr, "index: %d\n", index_);
fprintf(stderr, "Retx Timer: %d\n", mhRetx_.busy());
fprintf(stderr, "IFS Timer: %d\n", mhIFS_.busy());
fprintf(stderr, "Recv Timer: %d\n", mhRecv_.busy());
fprintf(stderr, "Send Timer: %d\n", mhSend_.busy());
exit(1);
}
/* Perform carrier sense - if we were sending before, never mind state_ */
if (mhIFS_.busy() || (state_ != MAC_IDLE)) {
/* we'll try again when IDLE. It'll happen either when
reception completes, or if collision. Either way,
we call resume() */
return;
}
HDR_CMN(p)->direction()= hdr_cmn::DOWN; //down
double txtime = netif_->txtime(p);
/* Schedule transmission of the packet's last bit */
mhSend_.schedule(p, txtime);
// pass the packet to the PHY: need to send a copy,
// because there may be collision and it may be freed
downtarget_->recv(p->copy());
state_= MAC_SEND;
}
void Mac802_3::collision(Packet *p) {
FPRINTF(stderr, "%.15f : %d : %s\n", Scheduler::instance().clock(), index_, __PRETTY_FUNCTION__);
Packet::free(p);
if (mhIFS_.busy()) mhIFS_.cancel();
double ifstime= netif_->txtime((IEEE_8023_JAMSIZE+IEEE_8023_IFS_BITS)/8); //jam time + ifs
mhIFS_.schedule(ifstime);
switch(state_) {
case MAC_SEND:
if (mhSend_.busy()) mhSend_.cancel();
if (!mhRetx_.busy()) {
/* schedule retransmissions */
if (!mhRetx_.schedule(ifstime)) {
p= mhRetx_.packet();
HDR_CMN(p)->size() -= (ETHER_HDR_LEN + HDR_MAC(p)->padding_);
drop(p); // drop if backed off far enough
mhRetx_.reset();
}
}
break;
case MAC_RECV:
// more than 2 packets collisions possible
if (mhRecv_.busy()) mhRecv_.cancel();
break;
default:
assert("SHOULD NEVER HAPPEN" == 0);
}
}
void Mac802_3::recv_complete(Packet *p) {
FPRINTF(stderr, "%.15f : %d : %s\n", Scheduler::instance().clock(), index_, __PRETTY_FUNCTION__);
assert(!mhRecv_.busy());
assert(!mhSend_.busy());
hdr_cmn *ch= HDR_CMN(p);
/* Address Filtering */
hdr_mac *mh= HDR_MAC(p);
int dst= mh->macDA();
if ((dst != BCAST_ADDR) && (dst != index_)) {
Packet::free(p);
goto done;
}
/* Strip off the mac header and padding if any */
ch->size() -= (ETHER_HDR_LEN + mh->padding_);
/* xxx FEC here */
if( ch->error() ) {
drop(p);
goto done;
}
/* we could schedule an event to account for mac-delay */
uptarget_->recv(p, (Handler*) 0);
done:
mhIFS_.schedule(netif_->txtime(IEEE_8023_IFS_BITS/8));// wait for one IFS, then resume
}
/* we call resume() in these cases:
- successful transmission
- whole packet's received
- collision and backoffLimit's exceeded
- collision while receiving */
void Mac802_3::resume() {
FPRINTF(stderr, "%.15f : %d : %s\n", Scheduler::instance().clock(), index_, __PRETTY_FUNCTION__);
assert(!mhRecv_.busy());
assert(!mhSend_.busy());
assert(!mhIFS_.busy());
state_= MAC_IDLE;
if (mhRetx_.packet()) {
if (!mhRetx_.busy()) {
// we're not backing off and not sensing carrier right now: send
transmit(mhRetx_.packet());
}
} else {
if (callback_ && !mhRetx_.busy()) {
//WARNING: calling callback_->handle may change the value of callback_
Handler* h= callback_;
callback_= 0;
h->handle(0);
}
}
}
mac-csma.cc
/* -*- 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 Giao Nguyen, http://daedalus.cs.berkeley.edu/~gnguyen
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (UCB)";
#endif
#include "template.h"
#include "random.h"
#include "channel.h"
#include "mac-csma.h"
static class MacCsmaClass : public TclClass {
public:
MacCsmaClass() : TclClass("Mac/Csma") {}
TclObject* create(int, const char*const*) {
return (new MacCsma);
}
} class_mac_csma;
static class MacCsmaCdClass : public TclClass {
public:
MacCsmaCdClass() : TclClass("Mac/Csma/Cd") {}
TclObject* create(int, const char*const*) {
return (new MacCsmaCd);
}
} class_mac_csma_cd;
static class MacCsmaCaClass : public TclClass {
public:
MacCsmaCaClass() : TclClass("Mac/Csma/Ca") {}
TclObject* create(int, const char*const*) {
return (new MacCsmaCa);
}
} class_mac_csma_ca;
void
MacHandlerEoc::handle(Event* e)
{
mac_->endofContention((Packet*)e);
}
MacCsma::MacCsma() : txstart_(0), rtx_(0), csense_(1), hEoc_(this)
{
bind_time("ifs_", &ifs_);
bind_time("slotTime_", &slotTime_);
bind("cwmin_", &cwmin_);
bind("cwmax_", &cwmax_);
bind("rtxLimit_", &rtxLimit_);
bind("csense_", &csense_);
cw_ = cwmin_;
}
void MacCsma::resume(Packet* p)
{
Scheduler& s = Scheduler::instance();
s.schedule(callback_, &intr_, ifs_ + slotTime_ * cwmin_);
if (p != 0)
drop(p);
callback_ = 0;
state(MAC_IDLE);
rtx_ = 0;
cw_ = cwmin_;
}
void MacCsma::send(Packet* p)
{
Scheduler& s = Scheduler::instance();
double delay = channel_->txstop() + ifs_ - s.clock();
// if channel is not ready, then wait
// else content for the channel
/* XXX floating point operations differences have been
observed on the resulting delay value on Pentium II and
SunSparc. E.g.
PentiumII SunSparc
-------------------------------
channel_->txstop_= 0.11665366666666668 0.11665366666666668
binary 0x3fbddd03c34ab4a2 0x3fbddd03c34ab4a2
ifs_= 5.1999999999999997e-05 5.1999999999999997e-05
binary 0x3f0b43526527a205 0x3f0b43526527a205
s.clock_= 0.11670566666666668 0.11670566666666668
binary 0x3fbde06c2d975996 0x3fbde06c2d975996
delay= 3.5033282698437862e-18 0
binary 0x3c50280000000000 0x0000000000000000
Because of that the value of (csense_ && delay > 0) was different. Fixed by
changing 0 to EPS
*/
static const double EPS= 1.0e-12; //seems appropriate (less than nanosec)
if (csense_ && delay > EPS)
s.schedule(&hSend_, p, delay + 0.000001);
else {
txstart_ = s.clock();
channel_->contention(p, &hEoc_);
}
}
void MacCsma::backoff(Handler* h, Packet* p, double delay)
{
Scheduler& s = Scheduler::instance();
double now = s.clock();
// if retransmission time within limit, do exponential backoff
// else drop the packet and resume
if (++rtx_ < rtxLimit_) {
delay += max(channel_->txstop() + ifs_ - now, 0.0);
int slot = Random::integer(cw_);
s.schedule(h, p, delay + slotTime_ * slot);
cw_ = min(2 * cw_, cwmax_);
}
else
resume(p);
}
void MacCsma::endofContention(Packet* p)
{
Scheduler& s = Scheduler::instance();
double txt = txtime(p) - (s.clock() - txstart_);
hdr_mac::access(p)->txtime() = txt;
channel_->send(p, txt);
s.schedule(&hRes_, &eEoc_, txt);
rtx_ = 0;
cw_ = cwmin_;
}
void MacCsmaCd::endofContention(Packet* p)
{
// If there is a collision, backoff
if (channel_->collision()) {
channel_->jam(0);
backoff(&hSend_, p);
}
else
MacCsma::endofContention(p);
}
void MacCsmaCa::send(Packet* p)
{
Scheduler& s = Scheduler::instance();
double delay = channel_->txstop() + ifs_ - s.clock();
if (csense_ && delay > 0)
backoff(&hSend_, p);
else {
txstart_ = s.clock();
channel_->contention(p, &hEoc_);
}
}
mac-multihop.cc
/* -*- 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 research group 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.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (UCB)";
#endif
#include "template.h"
#include "channel.h"
#include "mac-multihop.h"
/*
* For debugging.
*/
void dump_iphdr(hdr_ip *iph)
{
printf("\tsrc = %d, ", iph->saddr());
printf("\tdst = %d\n", iph->daddr());
}
static class MultihopMacClass : public TclClass {
public:
MultihopMacClass() : TclClass("Mac/Multihop") {}
TclObject* create(int, const char*const*) {
return (new MultihopMac);
}
} class_mac_multihop;
MultihopMac::MultihopMac() : mode_(MAC_IDLE), peer_(0),
pendingPollEvent_(0), pkt_(0),
ph_(this), pah_(this), pnh_(this), pth_(this), bh_(this)
{
/* Bind a bunch of variables to access from Tcl */
bind_time("tx_rx_", &tx_rx_);
bind_time("rx_tx_", &rx_tx_);
bind_time("rx_rx_", &rx_rx_);
bind_time("backoffBase_", &backoffBase_);
backoffTime_ = backoffBase_;
}
/*
* Returns 1 iff the specified MAC is in the prescribed state, AND all
* the other MACs are IDLE.
*/
int MultihopMac::checkInterfaces(int state)
{
MultihopMac *p;
if (!(mode_ & state))
return 0;
else if (macList_ == 0)
return 1;
for (p = (MultihopMac *)macList_; p != this && p != NULL;
p = (MultihopMac *)(p->macList())) {
if (p->mode() != MAC_IDLE) {
return 0;
}
}
return 1;
}
/*
* Poll a peer node prior to a send. There can be at most one POLL
* outstanding from a node at any point in time. This is achieved implicitly
* because there can be at most one packet down from LL (thru IFQ) to this MAC.
*/
void MultihopMac::poll(Packet *p)
{
Scheduler& s = Scheduler::instance();
MultihopMac *pm = (MultihopMac*) getPeerMac(p);
PollEvent *pe = new PollEvent(pm, this);
pendingPollEvent_ = new PollEvent(pm, this);
pkt_ = p->copy(); /* local copy for poll retries */
double timeout = max(pm->rx_tx(), tx_rx_) + 4*pollTxtime(MAC_POLLSIZE);
s.schedule(&bh_, pendingPollEvent_, timeout);
/* If the other interfaces are idle, then go ahead, else not. */
if (checkInterfaces(MAC_IDLE)) {
mode_ = MAC_POLLING;
peer_ = pm;
s.schedule(pm->ph(), (Event *)pe, pollTxtime(MAC_POLLSIZE));
}
}
/*
* Handle a POLL request from a peer node's MAC.
*/
void
PollHandler::handle(Event *e)
{
PollEvent *pe = (PollEvent *) e;
Scheduler& s = Scheduler::instance();
MultihopMac* pm = mac_->peer(); /* sensible val only in MAC_RCV mode */
/*
* Send POLLACK if either IDLE or currently receiving
* from same mac as the poller.
*/
if (mac_->checkInterfaces(MAC_IDLE)) { // all interfaces must be IDLE
mac_->mode(MAC_RCV);
pm = pe->peerMac();
mac_->peer(pm);
PollEvent *pae = new PollEvent(pm, mac_); // POLLACK event
double t = mac_->pollTxtime(MAC_POLLACKSIZE) +
max(mac_->tx_rx(), pm->rx_tx());
s.schedule(pm->pah(), pae, t);
} else {
// printf("ignoring poll %d\n", mac_->label());
// could send NACKPOLL but don't (at least for now)
}
}
/*
* Handle a POLLACK from a peer node's MAC.
*/
void
PollAckHandler::handle(Event *e)
{
PollEvent *pe = (PollEvent *) e;
Scheduler& s = Scheduler::instance();
if (mac_->checkInterfaces(MAC_POLLING | MAC_IDLE)) {
mac_->backoffTime(mac_->backoffBase());
mac_->mode(MAC_SND);
mac_->peer(pe->peerMac());
s.cancel(mac_->pendingPE()); /* cancel pending timeout */
free(mac_->pendingPE());
mac_->pendingPE(NULL);
mac_->send(mac_->pkt()); /* send saved packet */
}
}
void
PollNackHandler::handle(Event *)
{
}
void
BackoffHandler::handle(Event *)
{
Scheduler& s = Scheduler::instance();
if (mac_->mode() == MAC_POLLING)
mac_->mode(MAC_IDLE);
double bTime = mac_->backoffTime(2*mac_->backoffTime());
bTime = (1+Random::integer(MAC_TICK)*1./MAC_TICK)*bTime +
2*mac_->backoffBase();
// printf("backing off %d\n", mac_->label());
s.schedule(mac_->pth(), mac_->pendingPE(), bTime);
}
void
PollTimeoutHandler::handle(Event *)
{
mac_->poll(mac_->pkt());
}
/*
* Actually send the data frame.
*/
void MultihopMac::send(Packet *p)
{
Scheduler& s = Scheduler::instance();
if (mode_ != MAC_SND)
return;
double txt = txtime(p);
hdr_mac::access(p)->txtime() = txt;
channel_->send(p, txt); // target is peer's mac handler
s.schedule(callback_, &intr_, txt); // callback to higher layer (LL)
mode_ = MAC_IDLE;
}
/*
* This is the call from the higher layer of the protocol stack (i.e., LL)
*/
void MultihopMac::recv(Packet* p, Handler *h)
{
if (h == 0) { /* from MAC classifier (pass pkt to LL) */
mode_ = MAC_IDLE;
Scheduler::instance().schedule(target_, p, delay_);
return;
}
callback_ = h;
hdr_mac* mh = hdr_mac::access(p);
mh->macSA() = addr_;
if (mh->ftype() == MF_ACK) {
mode_ = MAC_SND;
send(p);
} else {
mh->ftype() = MF_DATA;
poll(p); /* poll first */
}
}
mac-timers.cc
/* -*- 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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*/
/* Ported from CMU/Monarch's code, nov'98 -Padma.*/
#include
#include
#include
#include
// #define DEBUG
//#include
#include
#include
#include
#include
#include
/*
* Force timers to expire on slottime boundries.
*/
// #define USE_SLOT_TIME
#define ROUND_TIME() \
{ \
assert(slottime); \
double rmd = remainder(s.clock() + rtime, slottime); \
if(rmd > 0.0) \
rtime += (slottime - rmd); \
else \
rtime += (-rmd); \
}
/* ======================================================================
Timers
====================================================================== */
void
MacTimer::start(double time)
{
Scheduler &s = Scheduler::instance();
assert(busy_ == 0);
busy_ = 1;
paused_ = 0;
stime = s.clock();
rtime = time;
assert(rtime >= 0.0);
s.schedule(this, &intr, rtime);
}
void
MacTimer::stop(void)
{
Scheduler &s = Scheduler::instance();
assert(busy_);
if(paused_ == 0)
s.cancel(&intr);
busy_ = 0;
paused_ = 0;
stime = 0.0;
rtime = 0.0;
}
/* ======================================================================
Defer Timer
====================================================================== */
void
DeferTimer::start(double time)
{
Scheduler &s = Scheduler::instance();
assert(busy_ == 0);
busy_ = 1;
paused_ = 0;
stime = s.clock();
rtime = time;
#ifdef USE_SLOT_TIME
ROUND_TIME();
#endif
assert(rtime >= 0.0);
s.schedule(this, &intr, rtime);
}
void
DeferTimer::handle(Event *)
{
busy_ = 0;
paused_ = 0;
stime = 0.0;
rtime = 0.0;
mac->deferHandler();
}
/* ======================================================================
NAV Timer
====================================================================== */
void
NavTimer::handle(Event *)
{
busy_ = 0;
paused_ = 0;
stime = 0.0;
rtime = 0.0;
mac->navHandler();
}
/* ======================================================================
Receive Timer
====================================================================== */
void
RxTimer::handle(Event *)
{
busy_ = 0;
paused_ = 0;
stime = 0.0;
rtime = 0.0;
mac->recvHandler();
}
/* ======================================================================
Send Timer
====================================================================== */
void
TxTimer::handle(Event *)
{
busy_ = 0;
paused_ = 0;
stime = 0.0;
rtime = 0.0;
mac->sendHandler();
}
/* ======================================================================
Interface Timer
====================================================================== */
void
IFTimer::handle(Event *)
{
busy_ = 0;
paused_ = 0;
stime = 0.0;
rtime = 0.0;
mac->txHandler();
}
/* ======================================================================
Backoff Timer
====================================================================== */
void
BackoffTimer::handle(Event *)
{
busy_ = 0;
paused_ = 0;
stime = 0.0;
rtime = 0.0;
difs_wait = 0.0;
mac->backoffHandler();
}
void
BackoffTimer::start(int cw, int idle)
{
Scheduler &s = Scheduler::instance();
assert(busy_ == 0);
busy_ = 1;
paused_ = 0;
stime = s.clock();
rtime = (Random::random() % cw) * mac->phymib_->SlotTime;
#ifdef USE_SLOT_TIME
ROUND_TIME();
#endif
difs_wait = 0.0;
if(idle == 0)
paused_ = 1;
else {
assert(rtime >= 0.0);
s.schedule(this, &intr, rtime);
}
}
void
BackoffTimer::pause()
{
Scheduler &s = Scheduler::instance();
//the caculation below make validation pass for linux though it
// looks dummy
double st = s.clock();
double rt = stime + difs_wait;
double sr = st - rt;
double mst = (mac->phymib_->SlotTime);
int slots = int (sr/mst);
//int slots = (int) ((s.clock() - (stime + difs_wait)) / mac->phymib_->SlotTime);
if(slots < 0)
slots = 0;
assert(busy_ && ! paused_);
paused_ = 1;
rtime -= (slots * mac->phymib_->SlotTime);
assert(rtime >= 0.0);
difs_wait = 0.0;
s.cancel(&intr);
}
void
BackoffTimer::resume(double difs)
{
Scheduler &s = Scheduler::instance();
assert(busy_ && paused_);
paused_ = 0;
stime = s.clock();
/*
* The media should be idle for DIFS time before we start
* decrementing the counter, so I add difs time in here.
*/
difs_wait = difs;
/*
#ifdef USE_SLOT_TIME
ROUND_TIME();
#endif
*/
assert(rtime + difs_wait >= 0.0);
s.schedule(this, &intr, rtime + difs_wait);
}
mac.cc
/* -*- 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 Giao Nguyen, http://daedalus.cs.berkeley.edu/~gnguyen
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (UCB)";
#endif
//#include "classifier.h"
#include
#include
#include
int hdr_mac::offset_;
static class MacHeaderClass : public PacketHeaderClass {
public:
MacHeaderClass() : PacketHeaderClass("PacketHeader/Mac",
sizeof(hdr_mac)) {
bind_offset(&hdr_mac::offset_);
}
void export_offsets() {
field_offset("macSA_", OFFSET(hdr_mac, macSA_));
field_offset("macDA_", OFFSET(hdr_mac, macDA_));
}
} class_hdr_mac;
static class MacClass : public TclClass {
public:
MacClass() : TclClass("Mac") {}
TclObject* create(int, const char*const*) {
return (new Mac);
}
} class_mac;
void
MacHandlerResume::handle(Event*)
{
mac_->resume();
}
void
MacHandlerSend::handle(Event* e)
{
mac_->sendDown((Packet*)e);
}
/* =================================================================
Mac Class Functions
==================================================================*/
static int MacIndex = 0;
Mac::Mac() : BiConnector(), netif_(0), tap_(0), ll_(0), channel_(0), callback_(0), hRes_(this), hSend_(this), state_(MAC_IDLE), pktRx_(0), pktTx_(0)
{
index_ = MacIndex++;
// bandwidth_ = 2.0 * 1e6;
bind_bw("bandwidth_", &bandwidth_);
off_mac_ = hdr_mac::offset_;
bind_time("delay_", &delay_);
//bind("off_mac_", &off_mac_);
}
int Mac::command(int argc, const char*const* argv)
{
if(argc == 2) {
Tcl& tcl = Tcl::instance();
if(strcmp(argv[1], "id") == 0) {
tcl.resultf("%d", addr());
return TCL_OK;
}
else if (strcmp(argv[1], "channel") == 0) {
tcl.resultf("%s", channel_->name());
return (TCL_OK);
}
/*else if (strcmp(argv[1], "classifier") == 0) {
tcl.resultf("%s", mcl_->name());
return (TCL_OK);
}
else if (strcmp(argv[1], "maclist") == 0) {
tcl.resultf("%s", macList_->name());
return (TCL_OK);
}*/
}
else if (argc == 3) {
TclObject *obj;
if( (obj = TclObject::lookup(argv[2])) == 0) {
fprintf(stderr, "%s lookup failed\n", argv[1]);
return TCL_ERROR;
}
// if (strcmp(argv[1], "channel") == 0) {
// channel_ = (Channel*) obj;
// return (TCL_OK);
// }
else if (strcmp(argv[1], "netif") == 0) {
netif_ = (Phy*) obj;
return TCL_OK;
}
else if (strcmp(argv[1], "log-target") == 0) {
logtarget_ = (NsObject*) obj;
if(logtarget_ == 0)
return TCL_ERROR;
return TCL_OK;
}
// else if (strcmp(argv[1], "up-target") == 0) {
// uptarget_ = (NsObject*) obj;
// return TCL_OK;
//}
/* else if (strcmp(argv[1], "down-target") == 0) {
downtarget_ = (NsObject*) obj;
return TCL_OK;
}*/
/*else if (strcmp(argv[1], "classifier") == 0) {
mcl_ = (Classifier*) obj;
return (TCL_OK);
}*/
/*if (strcmp(argv[1], "maclist") == 0) {
macList_ = (Mac*) obj;
return (TCL_OK);
}*/
}
return BiConnector::command(argc, argv);
}
void Mac::recv(Packet* p, Handler* h)
{
if (hdr_cmn::access(p)->direction() == hdr_cmn::UP) {
sendUp(p);
return;
}
callback_ = h;
hdr_mac* mh = HDR_MAC(p);
mh->set(MF_DATA, index_);
state(MAC_SEND);
sendDown(p);
}
void Mac::sendUp(Packet* p)
{
char* mh = (char*)p->access(hdr_mac::offset_);
int dst = this->hdr_dst(mh);
state(MAC_IDLE);
if (((u_int32_t)dst != MAC_BROADCAST) && (dst != index_)) {
drop(p);
return;
}
Scheduler::instance().schedule(uptarget_, p, \
delay_);
}
void Mac::sendDown(Packet* p)
{
Scheduler& s = Scheduler::instance();
double txt = txtime(p);
downtarget_->recv(p, this);
s.schedule(&hRes_, &intr_, txt);
}
void Mac::resume(Packet* p)
{
if (p != 0)
drop(p);
state(MAC_IDLE);
callback_->handle(&intr_);
}
//Mac* Mac::getPeerMac(Packet* p)
//{
//return (Mac*) mcl_->slot(hdr_mac::access(p)->macDA());
//}
mcast_ctrl.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* mcast_ctrl.cc
* Copyright (C) 1997 by USC/ISI
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation, advertising
* materials, and other materials related to such distribution and use
* acknowledge that the software was developed by the University of
* Southern California, Information Sciences Institute. The name of the
* University may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* Contributed by Polly Huang (USC/ISI), http://www-scf.usc.edu/~bhuang
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (LBL)";
#endif
#include "agent.h"
#include "packet.h"
#include "mcast_ctrl.h"
class mcastControlAgent : public Agent {
public:
mcastControlAgent() : Agent(PT_NTYPE) {
bind("packetSize_", &size_);
bind("off_mcast_ctrl_", &off_mcast_ctrl_);
}
void recv(Packet* pkt, Handler*) {
hdr_mcast_ctrl* ph = (hdr_mcast_ctrl*)pkt->access(off_mcast_ctrl_);
hdr_cmn* ch = (hdr_cmn*)pkt->access(off_cmn_);
// Agent/Mcast/Control instproc recv type from src group iface
Tcl::instance().evalf("%s recv %s %d %d", name(),
ph->type(), ch->iface(), ph->args());
Packet::free(pkt);
}
/*
* $proc send $type $src $group
*/
#define CASE(c,str,type) \
case (c): if (strcmp(argv[2], (str)) == 0) { \
type_ = (type); \
break; \
} else { \
/*FALLTHROUGH*/ \
}
int command(int argc, const char*const* argv) {
if (argc == 4) {
if (strcmp(argv[1], "send") == 0) {
switch (*argv[2]) {
CASE('p', "prune", PT_PRUNE);
CASE('g', "graft", PT_GRAFT);
CASE('X', "graftAck", PT_GRAFTACK);
CASE('j', "join", PT_JOIN);
CASE('a', "assert", PT_ASSERT);
default:
Tcl& tcl = Tcl::instance();
tcl.result("invalid control message");
return (TCL_ERROR);
}
Packet* pkt = allocpkt();
hdr_mcast_ctrl* ph = (hdr_mcast_ctrl*)pkt->access(off_mcast_ctrl_);
strcpy(ph->type(), argv[2]);
ph->args() = atoi(argv[3]);
send(pkt, 0);
return (TCL_OK);
}
}
return (Agent::command(argc, argv));
}
protected:
int off_mcast_ctrl_;
};
//
// Now put the standard OTcl linkage templates here
//
static class mcastControlHeaderClass : public PacketHeaderClass {
public:
mcastControlHeaderClass() : PacketHeaderClass("PacketHeader/mcastCtrl",
sizeof(hdr_mcast_ctrl)) {}
} class_mcast_ctrl_hdr;
static class mcastControlClass : public TclClass {
public:
mcastControlClass() : TclClass("Agent/Mcast/Control") {}
TclObject* create(int, const char*const*) {
return (new mcastControlAgent());
}
} class_mcast_ctrl;
measuremod.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) Xerox Corporation 1997. All rights reserved.
*
* License is granted to copy, to use, and to make and to use derivative
* works for research and evaluation purposes, provided that Xerox is
* acknowledged in all documentation pertaining to any such copy or
* derivative work. Xerox grants no other licenses expressed or
* implied. The Xerox trade name should not be used in any advertising
* without its written permission.
*
* XEROX CORPORATION MAKES NO REPRESENTATIONS CONCERNING EITHER THE
* MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE
* FOR ANY PARTICULAR PURPOSE. The software is provided "as is" without
* express or implied warranty of any kind.
*
* These notices must be retained in any copies of any part of this
* software.
*/
//Basic Measurement block derived from a connector
//measures bits sent and average packet delay in a measurement interval
#include "packet.h"
#include "connector.h"
#include "measuremod.h"
static class MeasureModClass : public TclClass {
public:
MeasureModClass() : TclClass("MeasureMod") {}
TclObject* create(int, const char*const*) {
return (new MeasureMod());
}
}class_measuremod;
MeasureMod::MeasureMod() : nbits_(0),npkts_(0)
{
}
void MeasureMod::recv(Packet *p,Handler *h)
{
hdr_cmn *ch=(hdr_cmn*)p->access(off_cmn_);
nbits_ += ch->size()<<3;
npkts_++;
send(p,h);
}
message.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1994-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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (LBL)";
#endif
#include "agent.h"
#include "random.h"
#include "message.h"
int hdr_msg::offset_;
static class MessageHeaderClass : public PacketHeaderClass {
public:
MessageHeaderClass() : PacketHeaderClass("PacketHeader/Message",
sizeof(hdr_msg)) {
bind_offset(&hdr_msg::offset_);
}
} class_msghdr;
class MessageAgent : public Agent {
public:
MessageAgent();
int command(int argc, const char*const* argv);
void recv(Packet*, Handler*);
};
static class MessageClass : public TclClass {
public:
MessageClass() : TclClass("Agent/Message") {}
TclObject* create(int, const char*const*) {
return (new MessageAgent());
}
} class_message;
MessageAgent::MessageAgent() : Agent(PT_MESSAGE)
{
bind("packetSize_", &size_);
}
void MessageAgent::recv(Packet* pkt, Handler*)
{
hdr_msg* mh = hdr_msg::access(pkt);
char wrk[128];/*XXX*/
sprintf(wrk, "%s recv {%s}", name(), mh->msg());
Tcl& tcl = Tcl::instance();
tcl.eval(wrk);
Packet::free(pkt);
}
/*
* $proc handler $handler
* $proc send $msg
*/
int MessageAgent::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
if (argc == 3) {
if (strcmp(argv[1], "send") == 0) {
Packet* pkt = allocpkt();
hdr_msg* mh = hdr_msg::access(pkt);
const char* s = argv[2];
int n = strlen(s);
if (n >= mh->maxmsg()) {
tcl.result("message too big");
Packet::free(pkt);
return (TCL_ERROR);
}
strcpy(mh->msg(), s);
send(pkt, 0);
return (TCL_OK);
}
}
return (Agent::command(argc, argv));
}
mftp.cc
/*
* (c) 1997-98 StarBurst Communications Inc.
*
* THIS SOFTWARE IS PROVIDED BY THE 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 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.
*
* Author: Christoph Haenle, chris@cs.vu.nl
* File: mftp.cc
* Last change: Dec 07, 1998
*
* This software may freely be used only for non-commercial purposes
*/
// This file contains functionality common to both MFTP sender and receivers.
#include "mftp.h"
MFTPAgent::MFTPAgent() :
Agent(PT_MFTP), FileSize(0), FileDGrams(0),
dtu_size(0), dtus_per_block(0), dtus_per_group(0),
nb_groups(0)
{
bind("dtuSize_", &dtuSize_);
bind("fileSize_", &fileSize_);
bind("dtusPerBlock_", &dtusPerBlock_);
bind("dtusPerGroup_", &dtusPerGroup_);
bind("seekCount_", &seekCount_);
bind("off_mftp_", &off_mftp_);
bind("off_cmn_", &off_cmn_);
};
int MFTPAgent::init()
{
if(dtusPerBlock_ % 8 != 0) {
Tcl& tcl = Tcl::instance();
tcl.resultf("%s: dtusPerBlock_ must be a multiple of 8", name_);
return TCL_ERROR;
}
dtu_size = dtuSize_;
FileSize = fileSize_;
dtus_per_block = dtusPerBlock_;
dtus_per_group = dtusPerGroup_;
seekCount_ = 0;
FileDGrams = (FileSize + dtu_size - 1) / dtu_size;
nb_groups = (FileDGrams + dtus_per_group - 1) / dtus_per_group;
return TCL_OK;
}
mftp_rcv.cc
/*
* (c) 1997-98 StarBurst Communications Inc.
*
* THIS SOFTWARE IS PROVIDED BY THE 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 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.
*
* Author: Christoph Haenle, chris@cs.vu.nl
* File: mftp_rcv.cc
* Last change: Dec 14, 1998
*
* This software may freely be used only for non-commercial purposes
*/
// This file contains functionality specific to an MFTP receiver.
#include
#include "config.h"
#include "tclcl.h"
#include "mftp_rcv.h"
#include "ip.h" // due to declaration of hdr_ip
#define min(a, b) ((a) < (b) ? (a) : (b))
static class MFTPRcvAgentClass : public TclClass {
public:
MFTPRcvAgentClass() : TclClass("Agent/MFTP/Rcv") {}
TclObject* create(int, const char*const*) {
return (new MFTPRcvAgent());
}
} class_mftprcv_agent;
MFTPRcvAgent::MFTPRcvAgent()
: MFTPAgent(),
CurrentPass(0),
CurrentGroup(0),
CwPat(0),
FileDGramsReceived(0),
FseekOffset(0),
cw_matrixline_buf(NULL)
{
bind("reply_addr_", (int*)&reply_.addr_);
bind("reply_port_", (int*)&reply_.port_);
}
// inspect a Tcl command (overloaded function):
int MFTPRcvAgent::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
if(strcmp(argv[1], "send") == 0) {
if(strcmp(argv[2], "nak") == 0) {
unsigned long pass_nb, block_nb;
int nb_scanned = 0;
nb_scanned += sscanf(argv[3], "%lu", &pass_nb);
nb_scanned += sscanf(argv[4], "%lu", &block_nb);
assert(nb_scanned == 2);
send_nak(pass_nb, block_nb);
return TCL_OK;
}
} else if (strcmp(argv[1], "start") == 0) {
if(dtusPerBlock_ % 8 != 0) {
tcl.resultf("%s: dtusPerBlock_ must be a multiple of 8", name_);
return TCL_ERROR;
}
init();
return TCL_OK;
}
return Agent::command(argc, argv);
}
// process reception of a packet
void MFTPRcvAgent::recv(Packet* p, Handler* h)
{
hdr_ip* ih = (hdr_ip*) p->access(off_ip_);
hdr_mftp* mh = (hdr_mftp*) p->access(off_mftp_);
if(ih->daddr() == 0) {
// packet from local agent
fprintf(stderr, "%s: send not allowed with Agent/MFTP/Rcv\n", name_);
assert(false);
} else {
switch(mh->type) {
case hdr_mftp::PDU_DATA_TRANSFER:
recv_data(mh->spec.data);
break;
case hdr_mftp::PDU_STATUS_REQUEST:
recv_status_req(mh->spec.statReq);
break;
case hdr_mftp::PDU_NAK:
// as we are a member of the group as well, we receive all data we have sent.
break;
default:
assert(false); // received unknown packet type
}
Packet::free(p);
}
}
// Destructor:
MFTPRcvAgent::~MFTPRcvAgent()
{
// Note: delete on a NULL-pointer has no effect
delete [] cw_matrixline_buf;
}
void MFTPRcvAgent::init()
{
MFTPAgent::init();
// allocate cw_matrix_line_buf
assert(cw_matrixline_buf == NULL);
cw_matrixline_buf = new CW_MATRIXLINE_t[FileDGrams];
assert(cw_matrixline_buf != NULL); // or else no memory is left!
// should return an error instead of terminating the program
// reset array:
cw_matrixlines_reset();
}
/* process a received status request packet */
void MFTPRcvAgent::recv_status_req(hdr_mftp::Spec::StatReq& statreq)
{
Tcl& tcl = Tcl::instance();
// read the PDU_STATUS_REQUEST-specific fields:
tcl.evalf("%s recv status-req %lu %lu %lu %lf", name_,
(unsigned long) statreq.pass_nb,
(unsigned long) statreq.block_lo,
(unsigned long) statreq.block_hi,
(double) statreq.RspBackoffWindow);
}
// send a nak packet:
void MFTPRcvAgent::send_nak(unsigned long pass_nb, unsigned long block_nb)
{
assert(FileDGrams > 0);
assert(0 <= block_nb && block_nb < nb_blocks());
Tcl& tcl = Tcl::instance();
// start_group_nb corresponds to first bit of NAK-bitmap:
unsigned long start_group_nb = dtus_per_block * block_nb;
// end_group_nb corresponds to last group number of NAK-bitmap plus one
unsigned long end_group_nb = min(nb_groups, dtus_per_block * (block_nb + 1));
// number of valid bits in the outgoing nak-bitmap
unsigned long n = end_group_nb - start_group_nb;
// number of status bytes in pdu
const unsigned long nak_bytes = (n+7) / 8;
unsigned long bit_count = 0;
// allocate (get) new packet and dynamically allocate extra space for nak-bitmap:
Packet* p = Agent::allocpkt((n+7) / 8);
unsigned char* nak_bitmap = (unsigned char*) p->accessdata();
// clear NAK-bitmap first:
memset(nak_bitmap, 0, nak_bytes);
// loop over all groups in rangs of nak and set nak-bit for those that are not still full
for(unsigned long group_nb = start_group_nb, bit = 1 << (start_group_nb % 8);
group_nb < end_group_nb; ++group_nb) {
if(is_group_full(group_nb) == false) {
*nak_bitmap |= bit;
bit_count++;
}
if(bit == 128) {
bit = 1;
nak_bitmap++;
} else {
bit <<= 1;
}
}
if(bit_count > 0) {
hdr_ip* iph = (hdr_ip*)p->access(off_ip_);
hdr_mftp* hdr = (hdr_mftp*) p->access(off_mftp_);
hdr_cmn* ch = (hdr_cmn*) p->access(off_cmn_);
// now generate the header
iph->dst() = reply_; // overwrite settings from Agent::allocpkt()
ch->size() = sizeof(hdr_mftp);
hdr->type = hdr_mftp::PDU_NAK;
hdr->spec.nak.pass_nb = pass_nb;
hdr->spec.nak.block_nb = block_nb;
hdr->spec.nak.nak_count = bit_count;
// transmit packet
target_->recv(p);
}
else {
Packet::free(p); // do not transmit NAK-packet if it consists of 0 NAK-bits !!
// HACK: @ requires optimation still!
}
tcl.resultf("%lu", bit_count);
}
// process_packet: decides if a received packet is "useful" and
// stores meta-information for proper decoding
int MFTPRcvAgent::process_packet(CW_PATTERN_t cw_pat,
unsigned long group_nb, unsigned long dtu_nb)
{
CW_PATTERN_t bit;
CW_MATRIXLINE_t new_row;
unsigned long j; // j iterates over the dtus of group "group_nb"
unsigned long finish = get_dtus_per_group(group_nb);
// finish counts the number of dtus in group "group_nb"
new_row.left = cw_pat;
for(j = 0; j < finish; j++) {
CW_PATTERN_t line_pat = cw_matrixline_buf[j * nb_groups + group_nb].left;
if(line_pat != 0) {
bit = new_row.left & ((CW_PATTERN_t) 1 << minbit(line_pat));
if(bit != 0) {
new_row.left ^= line_pat;
}
}
}
if(new_row.left != 0) { // linear independent?
bit = (CW_PATTERN_t) 1 << minbit(new_row.left);
for(j = 0; j < finish; j++) {
if((bit & cw_matrixline_buf[j * nb_groups + group_nb].left) != 0) {
cw_matrixline_buf[j * nb_groups + group_nb].left ^= new_row.left;
}
}
// register pattern of codeword the received packet is composed of (possibly altered).
// must be done at last for that this line gets not erased by XORing with itself
// in the previous loop.
cw_matrixline_buf[dtu_nb * nb_groups + group_nb].left = new_row.left;
return 1; // packet was a "useful" packet (i.e. is linear independent
// from the other ones received so far)
}
else {
return 0; //linear dependent codeword-pattern received, i.e. useless
}
}
int MFTPRcvAgent::findStoreLocation(unsigned long group_nb, unsigned long seek_offset, unsigned long* dtu_nb)
{
unsigned long start_dtu_nb;
assert(0 <= group_nb && group_nb < nb_groups);
assert(seek_offset % dtu_size == 0 ||
seek_offset == FileSize);
if(seek_offset == FileSize) {
*dtu_nb = group_nb; // start over from the beginning
} else {
unsigned long curr_dtu_nb = FseekOffset / dtu_size;
// pay attention to "unsigned" when substracting
*dtu_nb = curr_dtu_nb - curr_dtu_nb % nb_groups;
*dtu_nb += group_nb;
// check if seeking backwards. If yes, increment dtu_nb by nb_groups to
// always seeks forwards (unless end of file is reached):
if(*dtu_nb < curr_dtu_nb) {
*dtu_nb += nb_groups;
}
}
if(*dtu_nb >= FileDGrams) {
// this might happen if some groups have less packets than
// dtus_per_group:
*dtu_nb = group_nb; // start over from the beginning
}
start_dtu_nb = *dtu_nb;
assert(start_dtu_nb < FileDGrams);
do {
if(! cw_matrixline_buf[*dtu_nb].left) {
return 1;
}
*dtu_nb += nb_groups;
if(*dtu_nb >= FileDGrams) {
*dtu_nb = group_nb; // start over from the beginning
}
} while(*dtu_nb != start_dtu_nb);
return 0; // group "group_nb" is already full
}
// initializes all matrix-lines to zero, i.e. no (encoded) packets
// at all are received so far:
void MFTPRcvAgent::cw_matrixlines_reset()
{
assert(0 <= FileDGrams);
memset(cw_matrixline_buf, 0, sizeof(CW_MATRIXLINE_t) * FileDGrams);
}
// returns true if group "group_nb" is full, false if there is at least one packet missing
bool MFTPRcvAgent::is_group_full(unsigned long group_nb)
{
unsigned long nb_dtus = get_dtus_per_group(group_nb);
unsigned long i;
assert(0 <= group_nb && group_nb < nb_groups);
for(i = 0; i < nb_dtus &&
cw_matrixline_buf[i * nb_groups + group_nb].left != 0; ++i)
;
return (i == nb_dtus) ? true : false; // if loop was left before nb_dtus was reached,
// then there is some line in the matrix that is
// all "0", i.e. a packet is still missing.
}
// recv_data: process received data packet;
// takes (received) coded packets and processes them.
int MFTPRcvAgent::recv_data(hdr_mftp::Spec::Data& data)
{
Tcl& tcl = Tcl::instance();
unsigned long seek_offset;
unsigned long dtu_nb; // position (in terms of datagram number) where incoming
// packet is stored in file
// read the PDU_DATA_TRANSFER-specific fields:
CurrentPass = data.pass_nb;
CurrentGroup = data.group_nb;
CwPat = data.cw_pat;
// validate fields:
// (actually, assert should be replaced by just ignoring the packet in case
// the parameters are invalid, as some corrupt packet might reach the receiver
// in real world, i.e. this would not be a bug in the software!)
assert(0 <= CurrentPass);
assert(0 <= CurrentGroup && CurrentGroup < nb_groups);
if(findStoreLocation(CurrentGroup, FseekOffset, &dtu_nb)) {
// arriving packet belongs to a not already full group:
assert(dtu_nb % nb_groups == CurrentGroup);
assert(0 <= dtu_nb && dtu_nb < FileDGrams);
if(process_packet(CwPat,
CurrentGroup,
dtu_nb / nb_groups)) {
cw_matrixline_buf[dtu_nb].right = CwPat;
// arriving packet is useful (i.e. linearly independent from the others
// of the group, thus store packet on disk:
char buf[8 * sizeof(CW_PATTERN_t) + 1];
CwPat.print(buf);
tcl.evalf("%s recv useful %lu %lu %s",
name_,
(unsigned long) CurrentPass,
(unsigned long) CurrentGroup,
(char*) buf);
seek_offset = dtu_nb * dtu_size;
if(dtu_nb == FileDGrams - 1) {
// the last dtu of the file might not fit into the file, as with
// erasure correction, the dtu size must always be the same,
// i.e. dtu_size. So we don't write the packet on disk.
// Rather, we store it in a special place in main memory.
// (ommitted)
} else {
// prepare to write the new packet to the file system:
if(FseekOffset != seek_offset) {
// seek to file-position seek_offset (omitted)
FseekOffset = seek_offset;
seekCount_++;
}
// write data to file here (omitted)
FseekOffset += dtu_size;
} // else
// increment number of good dtus received
FileDGramsReceived++;
// if all packets have been received, decode the file and send a done-message
if (FileDGramsReceived == FileDGrams) {
// decode file here. Involves the file, the cw_matrixline_buf-array and
// the last packet (cached in memory). Additional disk activity for the
// receivers will be required.
// (omitted)
char buf[8 * sizeof(CW_PATTERN_t) + 1];
CwPat.print(buf);
tcl.evalf("%s done-notify %lu %lu %s",
name_,
(unsigned long) CurrentPass,
(unsigned long) CurrentGroup,
(char*) buf);
return(0); // we are ready!
}
} // if(process_packet...)
else {
char buf[8 * sizeof(CW_PATTERN_t) + 1];
CwPat.print(buf);
tcl.evalf("%s recv dependent %lu %lu %s",
name_,
(unsigned long) CurrentPass,
(unsigned long) CurrentGroup,
(char*) buf);
return(0); // we are ready!
}
} // if(findStoreLocation...)
else {
// we received a packet that belongs to an already full group
char buf[8 * sizeof(CW_PATTERN_t) + 1];
CwPat.print(buf);
tcl.evalf("%s recv group-full %lu %lu %s",
name_,
(unsigned long) CurrentPass,
(unsigned long) CurrentGroup,
(char*) buf);
}
return(0);
}
mftp_snd.cc
/*
* (c) 1997-98 StarBurst Communications Inc.
*
* THIS SOFTWARE IS PROVIDED BY THE 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 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.
*
* Author: Christoph Haenle, chris@cs.vu.nl
* File: mftp_snd.cc
* Last change: Dec 07, 1998
*
* This software may freely be used only for non-commercial purposes
*/
// This file contains functionality specific to the MFTP sender.
#include // strtoul, etc.
#include
#include
#include "config.h"
#include "tclcl.h"
#include "agent.h"
#include "packet.h"
#include "ip.h"
#include "mftp_snd.h"
#include "trace.h"
#include "bitops.h" // due to IS_BITSET, etc.
#define min(a, b) ((a) < (b) ? (a) : (b))
static class MFTPSndAgentClass : public TclClass {
public:
MFTPSndAgentClass() : TclClass("Agent/MFTP/Snd") {}
TclObject* create(int, const char*const*) {
return (new MFTPSndAgent());
}
} class_mftpsnd_agent;
static class MFTPHeaderClass : public PacketHeaderClass {
public:
MFTPHeaderClass() : PacketHeaderClass("PacketHeader/MFTP",
sizeof(hdr_mftp)) {}
} class_mftphdr;
MFTPSndAgent::MFTPSndAgent()
: MFTPAgent(),
naks(0),
retx(0),
fseek_offset(0),
read_ahead_bufsize(0),
CurrentPass(0),
CurrentGroup(0),
CwPat(0),
MinGroupNbInBuf(0),
NbGroupsInBuf(0)
{
bind("readAheadBufsize_", &readAheadBufsize_);
bind_time("txStatusDelay_", &txStatusDelay_);
bind("nakCount_", &nakCount_);
}
MFTPSndAgent::~MFTPSndAgent()
{
delete [] naks; // NOTE: delete on NULL pointer has no effect
delete [] retx;
}
int MFTPSndAgent::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
if(strcmp(argv[1], "send") == 0) {
if(strcmp(argv[2], "data") == 0) {
return send_data();
} else if(strcmp(argv[2], "statreq") == 0) {
unsigned long pass_nb, block_lo, block_hi;
double rsp_backoff_window=2343.2343;
int nb_scanned = 0;
if(argc == 7) {
nb_scanned += sscanf(argv[3], "%lu", &pass_nb);
nb_scanned += sscanf(argv[4], "%lu", &block_lo);
nb_scanned += sscanf(argv[5], "%lu", &block_hi);
nb_scanned += sscanf(argv[6], "%lf", &rsp_backoff_window);
}
if(nb_scanned != 4) {
tcl.resultf("%s: wrong number of parameters for \"send statreq\"", name_);
return TCL_ERROR;
}
send_status_request(pass_nb, block_lo, block_hi, rsp_backoff_window);
return TCL_OK;
}
}
if(strcmp(argv[1], "start") == 0) {
if(MFTPAgent::init() == TCL_ERROR) {
return TCL_ERROR;
};
init_user_file((unsigned long) readAheadBufsize_);
return TCL_OK;
}
return Agent::command(argc, argv);
}
void MFTPSndAgent::recv(Packet* p, Handler* h)
{
hdr_ip* ih = (hdr_ip*) p->access(off_ip_);
hdr_mftp* mh = (hdr_mftp*) p->access(off_mftp_);
if(ih->daddr() == 0) {
assert(false); // Packet from local agent.
} else {
switch(mh->type) {
case hdr_mftp::PDU_DATA_TRANSFER:
case hdr_mftp::PDU_STATUS_REQUEST:
// as the sender is a member of the multicast group as well,
// it receives all data it has sent. So just ignore it.
break;
case hdr_mftp::PDU_NAK:
process_nak(mh->spec.nak, p->accessdata(), CurrentPass-1); // -1 because we have
// incremented the pass-number already in send_data.
break;
default:
assert(false); // unknown packet type (also possible: just ignore packet rather than exit)
}
Packet::free(p);
}
}
void MFTPSndAgent::send_status_request(unsigned long pass_nb,
unsigned long block_lo,
unsigned long block_hi,
double rsp_backoff_window)
{
Packet* p = Agent::allocpkt();
hdr_mftp* hdr = (hdr_mftp*) p->access(off_mftp_);
assert(FileDGrams > 0); // we need this requirement here
// initialize the header of the status request packet:
hdr->type = hdr_mftp::PDU_STATUS_REQUEST;
hdr->spec.statReq.pass_nb = pass_nb;
hdr->spec.statReq.block_lo = block_lo;
hdr->spec.statReq.block_hi = block_hi;
hdr->spec.statReq.RspBackoffWindow = rsp_backoff_window;
// transmit packet
hdr_cmn* ch = (hdr_cmn*) p->access(off_cmn_);
ch->size() = sizeof(hdr_mftp);
target_->recv(p);
}
// process incoming nak:
void MFTPSndAgent::process_nak(hdr_mftp::Spec::Nak& nak,
unsigned char* nak_bitmap,
unsigned long currentPass)
{
assert(1 <= nak.nak_count && nak.nak_count <= nb_groups); // or else some receiver is fooling us.
assert(nak.pass_nb <= currentPass); // pass greater than requested? => a receiver is fooling us.
Tcl& tcl = Tcl::instance();
tcl.evalf("%s recv nak %lu %lu %lu", name_,
(unsigned long) nak.pass_nb,
(unsigned long) nak.block_nb,
(unsigned long) nak.nak_count);
assert(dtus_per_block % 8 == 0); // This property is required for the following
// start_group_nb corresponds to first bit of NAK-bitmap:
const unsigned long start_group_nb = dtus_per_block * nak.block_nb;
// end_group_nb corresponds to last group number of NAK-bitmap plus one
const unsigned long end_group_nb = min(nb_groups, dtus_per_block * (nak.block_nb + 1));
// get starting index into naks-array for this block
const unsigned long nak_index = start_group_nb / 8;
// number of status bytes in pdu
const unsigned long nak_bytes = (end_group_nb - start_group_nb + 7) / 8;
// pointer to location in array at which the received nak bitmap must be
// or'd to the sender-bitmap (the bitmap in which the sender collects the naks)
unsigned char* nak_array = naks + nak_index;
// if this nak pdu is from a previous pass (i.e. a delayed nak), ignore the status
// bits for dtu's that we've just retransmitted in the current pass:
if(nak.pass_nb < currentPass) {
unsigned char* retx_array = retx + nak_index;
for(unsigned long i = 0; i < nak_bytes; i++) {
if(*nak_bitmap) {
// "AND out" bits for already transmitted packets and
// "OR in" the result into newly constructed NAK bitmap
*nak_array |= (*nak_bitmap & (~*retx_array));
}
nak_array++;
retx_array++;
nak_bitmap++;
}
}
else {
assert(nak.pass_nb == currentPass); // this nak belongs to the current pass
for(unsigned long i = 0; i < nak_bytes; i++) {
if(*nak_bitmap) {
// "OR in" NAK byte into newly constructed NAK bitmap
*nak_array |= *nak_bitmap;
}
nak_array++;
nak_bitmap++;
}
}
nakCount_++; // increment total number of received nak-packets
}
void MFTPSndAgent::init_user_file(unsigned long readAheadBufsize)
{
read_ahead_bufsize = readAheadBufsize;
fseek_offset = 0;
// initialize codeword pattern
iterator.setSourceWordLen(dtus_per_group);
CwPat = iterator.getNextCwPat();
// free arrays from a possible previous transmission (no effect on NULL pointers)
delete [] naks;
delete [] retx;
// allocate naks bitmap init'd to all nak'd:
naks = new unsigned char[(nb_groups + 7) / 8];
assert(naks != NULL); // or else we ran out of memory
SET_ALL_BITS(naks, nb_groups);
// allocate retransmission bitmap init'd to none retransmitted:
retx = new unsigned char[(nb_groups + 7) / 8];
assert(retx != NULL); // or else we ran out of memory
RESET_ALL_BITS(retx, nb_groups);
CurrentPass = CurrentGroup = MinGroupNbInBuf = NbGroupsInBuf = 0;
}
// reads as many groups into the read-ahead-buffer as there is space, starting with
// group CurrentGroup. The groups that were not not NACK'd by anyone will be
// skipped (and the corresponding areas in the read-ahead-buffer will be skipped
// as well).
void MFTPSndAgent::fill_read_ahead_buf()
{
unsigned int dtu_pos; // loops over [0..dtus_per_group)
unsigned long seek_offset; // where to position the head for disk seeks
unsigned long buf_pos = 0; // position where data is written (into main memory) when
// read from disk, relative to the start of read_ahead_buf
CW_PATTERN_t cw_pat_tmp = CwPat;
unsigned long i;
unsigned long len;
// switch to next group that must be read:
MinGroupNbInBuf = CurrentGroup;
NbGroupsInBuf = min(read_ahead_bufsize / (bitcount(CwPat) * dtu_size),
nb_groups - MinGroupNbInBuf);
while(cw_pat_tmp != 0) {
dtu_pos = minbit(cw_pat_tmp);
assert(0 <= dtu_pos && dtu_pos < dtus_per_group);
assert(MinGroupNbInBuf + NbGroupsInBuf <= nb_groups);
cw_pat_tmp &= ~((CW_PATTERN_t) 1 << dtu_pos); // clear bit at position "dtu_pos"
for(i = MinGroupNbInBuf;
i < MinGroupNbInBuf + NbGroupsInBuf; ++i) {
// continue with for-loop if group i was not NACKed by anyone
if(IS_BIT_CLEARED(naks, i)) {
buf_pos += dtu_size;
continue;
}
// Note: there is never data accessed "outside" the file as the while-loop
// is left as soon as the last (possibly partial) DTU has been read.
seek_offset = (dtu_pos * nb_groups + i) * dtu_size;
if(seek_offset >= FileSize) {
// we can get there if the last group(s) have fewer than
// dtus_per_group packets. If we get here, we are ready.
return; // OK
}
if (fseek_offset != seek_offset) {
// do the fseek here (omitted)
seekCount_++;
fseek_offset = seek_offset;
}
// determine number of bytes to read
len = min(dtu_size, FileSize - fseek_offset);
// read len bytes from file here (omitted)
fseek_offset += len;
buf_pos += len;
if(len < dtu_size) {
// we get here if the last dtu is smaller than dtu_size and if
// we have just read that last dtu
assert(fseek_offset == FileSize); // we must be at EOF
// clear rest of read-ahead-buffer here (omitted)
buf_pos = bitcount(CwPat) * NbGroupsInBuf * dtu_size;
return; // that's it, no more packets to process
}
assert(len == dtu_size);
assert(buf_pos <= bitcount(CwPat) * NbGroupsInBuf * dtu_size);
} // for
} // while
// we get here only if no group was read with less than dtus_per_group packets and
// the if not the last packet was read (in case it is too short)
assert(buf_pos == bitcount(CwPat) * NbGroupsInBuf * dtu_size);
}
// send_data() sends next data packet.
// In tcl's result buffer, return
// 0, if current pass not yet finished
// -1, if reached the end of the current pass
int MFTPSndAgent::send_data()
{
Packet* p = Agent::allocpkt();
hdr_mftp* hdr = (hdr_mftp*) p->access(off_mftp_);
CW_PATTERN_t mask;
Tcl& tcl = Tcl::instance();
assert(0 <= CurrentGroup && CurrentGroup < nb_groups);
assert(NbGroupsInBuf >= 0);
assert(0 <= MinGroupNbInBuf && MinGroupNbInBuf + NbGroupsInBuf <= nb_groups);
// now comes NACK processing: loop until end of file or until
// a nak bit is detected:
while(CurrentGroup < nb_groups && IS_BIT_CLEARED(naks, CurrentGroup)) {
CurrentGroup++; // proceed to next bit of the nak bitmap
}
// do not transmit packet if
// (1) CurrentGroup has reached the total number of groups ("end of pass") or
// (2) CwPat has only bits set that refer to some packets that are cut off in
// the current group (for example, if CurrentGroup has only 5 packets
// with nb_groups=8 and if CwPat=64+32)
if(CurrentGroup != nb_groups &&
((mask = (~(CW_PATTERN_t) 0) >> (8 * sizeof(CW_PATTERN_t) - get_dtus_per_group(CurrentGroup)))
& CwPat) != 0) {
assert(CurrentGroup < nb_groups);
// see if the read-ahead-buffer is exhausted so that we must load new data
// from file. Only groups with a corresponding NAK-bit are read, that is,
// those that were requested for retransmission
assert(MinGroupNbInBuf <= CurrentGroup);
if(CurrentGroup >= MinGroupNbInBuf + NbGroupsInBuf) { // exhausted?
fill_read_ahead_buf(); // load new data from file
}
assert(MinGroupNbInBuf <= CurrentGroup &&
CurrentGroup < MinGroupNbInBuf + NbGroupsInBuf);
// produce an encoded packet here (omitted)
// generate the header
hdr->type = hdr_mftp::PDU_DATA_TRANSFER;
hdr->spec.data.pass_nb = CurrentPass;
hdr->spec.data.group_nb = CurrentGroup;
hdr->spec.data.cw_pat = CwPat & mask;
char buf[8 * sizeof(CW_PATTERN_t) + 1];
(CwPat & mask).print(buf);
tcl.evalf("%s send notify %lu %lu %s",
name_,
(unsigned long) CurrentPass,
(unsigned long) CurrentGroup,
(char*) buf);
hdr_cmn* ch = (hdr_cmn*) p->access(off_cmn_);
ch->size() = sizeof(hdr_mftp) + dtu_size;
// transmit packet
target_->recv(p);
RESET_BIT(naks, CurrentGroup); // reset the dtu status bit in the nak bitmap
SET_BIT(retx, CurrentGroup); // set the dtus status bit in the retransmission bitmap
CurrentGroup++;
} // if
// if last group of the file:
if(CurrentGroup == nb_groups || !(CwPat & mask)) { // end of pass?
do {
CwPat = iterator.getNextCwPat(); // get next codeword for new pass
} while(!(CwPat & ((~(CW_PATTERN_t) 0) >> (8 * sizeof(CW_PATTERN_t) - get_dtus_per_group(0)))));
// } while(!(CwPat & ((((CW_PATTERN_t) 1) << get_dtus_per_group(0)) - 1)));
// prepare a new pas:
MinGroupNbInBuf = 0;
NbGroupsInBuf = 0;
CurrentGroup = 0;
// reset retransmission bitmap for dealing with of latent naks
RESET_ALL_BITS(retx, nb_groups);
// the first dtus_per_group passes must be transmitted "in full"
if(CurrentPass < dtus_per_group - 1) {
SET_ALL_BITS(naks, nb_groups);
}
tcl.evalf("%s pass-finished %lu %lu", name_,
(unsigned long) CurrentPass,
(unsigned long) nb_blocks());
CurrentPass++;
tcl.result("-1"); // return end-of-pass to the caller
return TCL_OK;
}
tcl.result("0"); // end-of-pass not yet reached
return TCL_OK;
}
mip-reg.cc
/*
* Copyright (c) Sun Microsystems, Inc. 1998 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 Sun Microsystems, Inc.
*
* 4. The name of the Sun Microsystems, Inc nor may not be used to endorse or
* promote products derived from this software without specific prior
* written permission.
*
* SUN MICROSYSTEMS MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS
* SOFTWARE FOR ANY PARTICULAR PURPOSE. The software is provided "as is"
* without express or implied warranty of any kind.
*
* These notices must be retained in any copies of any part of this software.
*/
// #ident "@(#)mip-reg.cc 1.4 98/08/30 SMI"
#include
#include
#include
#include
#include
#define AGENT_ADS_SIZE 48
#define REG_REQUEST_SIZE 52
static class MIPHeaderClass : public PacketHeaderClass {
public:
MIPHeaderClass() : PacketHeaderClass("PacketHeader/MIP",
sizeof(hdr_mip)) {
}
} class_miphdr;
static class MIPBSAgentClass : public TclClass {
public:
MIPBSAgentClass() : TclClass("Agent/MIPBS") {}
TclObject* create(int, const char*const*) {
return (new MIPBSAgent());
}
} class_mipbsagent;
MIPBSAgent::MIPBSAgent() : Agent(PT_UDP), beacon_(1.0),
bcast_target_(0), ragent_(0), timer_(this), adlftm_(~0)
{
bind("adSize_", &size_);
//bind("shift_", &shift_);
//bind("mask_", &mask_);
bind("ad_lifetime_", &adlftm_);
bind("off_mip_", &off_mip_);
size_ = AGENT_ADS_SIZE;
seqno_ = -1;
}
void MIPBSAgent::recv(Packet* p, Handler *)
{
Tcl& tcl = Tcl::instance();
char *objname = NULL;
NsObject *obj = NULL;
hdr_mip *miph = (hdr_mip *)p->access(off_mip_);
hdr_ip *iph = (hdr_ip *)p->access(off_ip_);
hdr_cmn *ch = (hdr_cmn*)p->access(off_cmn_);
int nodeaddr = Address::instance().get_nodeaddr(addr());
switch (miph->type_) {
case MIPT_REG_REQUEST:
//if (miph->ha_ == (addr_ >> shift_ & mask_)) {
if (miph->ha_ == (Address::instance().get_nodeaddr(addr()))){
if (miph->ha_ == miph->coa_) { // back home
tcl.evalf("%s clear-reg %d", name_,
miph->haddr_);
}
else {
tcl.evalf("%s encap-route %d %d %lf", name_,
miph->haddr_, miph->coa_,
miph->lifetime_);
}
iph->dst() = iph->src();
miph->type_ = MIPT_REG_REPLY;
}
else {
//iph->dst() = iph->dst() & ~(~(nsaddr_t)0 << shift_) | (miph->ha_ & mask_) << shift_;
iph->daddr() = miph->ha_;
iph->dport() = 0;
}
iph->saddr() = addr();
iph->sport() = port();
// by now should be back to normal route
// if dst is the mobile
// also initialise forward counter to 0. otherwise routing
// agent is going to think pkt is looping and drop it!!
ch->num_forwards() = 0;
send(p, 0);
break;
case MIPT_REG_REPLY:
//assert(miph->coa_ == (addr_ >> shift_ & mask_));
assert(miph->coa_ == nodeaddr);
tcl.evalf("%s get-link %d %d", name_, nodeaddr, miph->haddr_);
//
// XXX hacking mobileip. all this should go away
// when mobileIP for sun-wired model is no longer reqd.
//
obj = (NsObject*)tcl.lookup(objname = tcl.result());
if (strlen(objname) == 0)
objname = "XXX";
tcl.evalf("%s decap-route %d %s %lf", name_, miph->haddr_,
objname, miph->lifetime_);
iph->src() = iph->dst();
//iph->dst() = iph->dst() & ~(~(nsaddr_t)0 << shift_) |(miph->haddr_ & mask_) << shift_;
iph->daddr() = miph->haddr_;
iph->dport() = 0;
if (obj == NULL)
obj = ragent_;
obj->recv(p, (Handler*)0);
break;
case MIPT_SOL:
//tcl.evalf("%s get-link %d %d", name_, addr_ >> shift_ & mask_,miph->haddr_);
tcl.evalf("%s get-link %d %d",name_,nodeaddr,miph->haddr_);
send_ads(miph->haddr_, (NsObject*)tcl.lookup(tcl.result()));
Packet::free(p);
break;
default:
Packet::free(p);
break;
}
}
void MIPBSAgent::timeout(int )
{
send_ads();
timer_.resched(beacon_);
}
int MIPBSAgent::command(int argc, const char*const* argv)
{
if (argc == 3) {
if (strcmp(argv[1], "beacon-period") == 0) {
beacon_ = atof(argv[2]);
timer_.resched(Random::uniform(0, beacon_));
return TCL_OK;
}
if (strcmp(argv[1], "bcast-target") == 0) {
bcast_target_ = (NsObject *)TclObject::lookup(argv[2]);
return TCL_OK;
}
if (strcmp(argv[1], "ragent") == 0) {
ragent_ = (NsObject *)TclObject::lookup(argv[2]);
return TCL_OK;
}
}
return (Agent::command(argc, argv));
}
void MIPBSAgent::send_ads(int dst, NsObject *target)
{
Packet *p = allocpkt();
hdr_mip *h = (hdr_mip *)p->access(off_mip_);
hdr_ip *iph = (hdr_ip *)p->access(off_ip_);
h->haddr_ = h->ha_ = -1;
//h->coa_ = addr_ >> shift_ & mask_;
h->coa_ = Address::instance().get_nodeaddr(addr());
h->type_ = MIPT_ADS;
h->lifetime_ = adlftm_;
h->seqno_ = ++seqno_;
if (dst != -1) {
//hdr_ip *iph = (hdr_ip *)p->access(off_ip_);
//iph->dst() = iph->dst() & ~(~(nsaddr_t)0 << shift_) | (dst & mask_) << shift_;
iph->daddr() = dst;
iph->dport() = 0;
}
else {
// if bcast pkt
sendOutBCastPkt(p);
}
if (target == NULL) {
if (bcast_target_) bcast_target_->recv(p, (Handler*) 0);
else if (target_) target_->recv(p, (Handler*) 0);
else Packet::free(p); // drop; may log in future code
}
else target->recv(p, (Handler*)0);
}
void
MIPBSAgent::sendOutBCastPkt(Packet *p)
{
hdr_ip *iph = (hdr_ip*)p->access(off_ip_);
hdr_cmn *hdrc = (hdr_cmn *)p->access (off_cmn_);
hdrc->next_hop_ = IP_BROADCAST;
hdrc->addr_type_ = NS_AF_INET;
iph->daddr() = IP_BROADCAST;
iph->dport() = 0;
}
void AgtListTimer::expire(Event *) {
a_->timeout(MIP_TIMER_AGTLIST);
}
static class MIPMHAgentClass : public TclClass {
public:
MIPMHAgentClass() : TclClass("Agent/MIPMH") {}
TclObject* create(int, const char*const*) {
return (new MIPMHAgent());
}
} class_mipmhagent;
MIPMHAgent::MIPMHAgent() : Agent(PT_UDP), ha_(-1), coa_(-1),
beacon_(1.0),bcast_target_(0),agts_(0),rtx_timer_(this),
agtlist_timer_(this),reglftm_(~0),adlftm_(0.0), node_ (0)
{
bind("home_agent_", &ha_);
bind("rreqSize_", &size_);
bind("reg_rtx_", ®_rtx_);
//bind("shift_", &shift_);
//bind("mask_", &mask_);
bind("reg_lifetime_", ®lftm_);
bind("off_mip_", &off_mip_);
size_ = REG_REQUEST_SIZE;
seqno_ = -1;
}
void MIPMHAgent::recv(Packet* p, Handler *)
{
Tcl& tcl = Tcl::instance();
hdr_mip *miph = (hdr_mip *)p->access(off_mip_);
switch (miph->type_) {
case MIPT_REG_REPLY:
if (miph->coa_ != coa_) break; // not pending
tcl.evalf("%s update-reg %d", name_, coa_);
if (rtx_timer_.status() == TIMER_PENDING)
rtx_timer_.cancel();
break;
case MIPT_ADS:
{
AgentList **ppagts = &agts_, *ptr;
while (*ppagts) {
if ((*ppagts)->node_ == miph->coa_) break;
ppagts = &(*ppagts)->next_;
}
if (*ppagts) {
ptr = *ppagts;
*ppagts = ptr->next_;
ptr->expire_time_ = beacon_ +
Scheduler::instance().clock();
ptr->lifetime_ = miph->lifetime_;
ptr->next_ = agts_;
agts_ = ptr;
if (coa_ == miph->coa_) {
seqno_++;
reg();
}
}
else { // new ads
ptr = new AgentList;
ptr->node_ = miph->coa_;
ptr->expire_time_ = beacon_ +
Scheduler::instance().clock();
ptr->lifetime_ = miph->lifetime_;
ptr->next_ = agts_;
agts_ = ptr;
coa_ = miph->coa_;
// The MHagent now should update the Mobilenode
// about the changed coa_ : node updates its
// base-station to new coa_ accordingly.
if(node_)
node_->set_base_stn(coa_);
adlftm_ = miph->lifetime_;
seqno_++;
reg();
}
}
break;
default:
break;
}
Packet::free(p);
}
void MIPMHAgent::timeout(int tno)
{
switch (tno) {
case MIP_TIMER_SIMPLE:
reg();
break;
case MIP_TIMER_AGTLIST:
{
double now = Scheduler::instance().clock();
AgentList **ppagts = &agts_, *ptr;
int coalost = 0;
while (*ppagts) {
if ((*ppagts)->expire_time_ < now) {
ptr = *ppagts;
*ppagts = ptr->next_;
if (ptr->node_ == coa_) {
coa_ = -1;
coalost = 1;
}
delete ptr;
}
else ppagts = &(*ppagts)->next_;
}
agtlist_timer_.resched(beacon_);
if (coalost) {
seqno_++;
reg();
}
}
break;
default:
break;
}
}
int MIPMHAgent::command(int argc, const char*const* argv)
{
if (argc == 3) {
if (strcmp(argv[1], "beacon-period") == 0) {
beacon_ = atof(argv[2]);
timeout(MIP_TIMER_AGTLIST);
agtlist_timer_.resched(beacon_);
rtx_timer_.resched(Random::uniform(0, beacon_));
return TCL_OK;
}
else if (strcmp(argv[1], "bcast-target") == 0) {
bcast_target_ = (NsObject *)TclObject::lookup(argv[2]);
return TCL_OK;
}
else if (strcmp (argv[1], "node") == 0) {
node_ = (MobileNode*)TclObject::lookup(argv[2]);
if (node_ == 0) {
fprintf (stderr, "%s: %s lookup of %s failed\n", __FILE__, argv[1], argv[2]);
return TCL_ERROR;
}
return TCL_OK;
}
}
// later: agent solicitation (now done!), start of simulation, ...
return (Agent::command(argc, argv));
}
void MIPMHAgent::reg()
{
rtx_timer_.resched(reg_rtx_);
if (agts_ == 0) {
send_sols();
return;
}
if (coa_ < 0) {
coa_ = agts_->node_;
adlftm_ = agts_->lifetime_;
}
Tcl& tcl = Tcl::instance();
Packet *p = allocpkt();
hdr_ip *iph = (hdr_ip *)p->access(off_ip_);
//iph->dst() = iph->dst() & ~(~(nsaddr_t)0 << shift_) | (coa_ & mask_) << shift_;
iph->daddr() = coa_;
iph->dport() = 0;
hdr_mip *h = (hdr_mip *)p->access(off_mip_);
//h->haddr_ = addr_ >> shift_ & mask_;
h->haddr_ = Address::instance().get_nodeaddr(addr());
h->ha_ = ha_;
h->coa_ = coa_;
h->type_ = MIPT_REG_REQUEST;
h->lifetime_ = min(reglftm_, adlftm_);
h->seqno_ = seqno_;
tcl.evalf("%s get-link %d %d", name_, h->haddr_, coa_);
NsObject *target = (NsObject *)tcl.lookup(tcl.result());
if (target != NULL)
((NsObject *)tcl.lookup(tcl.result()))->recv(p, (Handler*) 0);
else
send(p, 0);
}
void MIPMHAgent::send_sols()
{
Packet *p = allocpkt();
hdr_mip *h = (hdr_mip *)p->access(off_mip_);
h->coa_ = -1;
//h->haddr_ = addr_ >> shift_ & mask_;
h->haddr_ = Address::instance().get_nodeaddr(addr());
h->ha_ = ha_;
h->type_ = MIPT_SOL;
h->lifetime_ = reglftm_;
h->seqno_ = seqno_;
sendOutBCastPkt(p);
if (bcast_target_)
bcast_target_->recv(p, (Handler*) 0);
else if (target_)
target_->recv(p, (Handler*) 0);
else
Packet::free(p); // drop; may log in future code
}
void MIPMHAgent::sendOutBCastPkt(Packet *p)
{
hdr_ip *iph = (hdr_ip*)p->access(off_ip_);
hdr_cmn *hdrc = (hdr_cmn *)p->access (off_cmn_);
hdrc->next_hop_ = IP_BROADCAST;
hdrc->addr_type_ = NS_AF_INET;
iph->daddr() = IP_BROADCAST;
iph->dport() = 0;
}
mip.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) Sun Microsystems, Inc. 1998 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 Sun Microsystems, Inc.
*
* 4. The name of the Sun Microsystems, Inc nor may not be used to endorse or
* promote products derived from this software without specific prior
* written permission.
*
* SUN MICROSYSTEMS MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS
* SOFTWARE FOR ANY PARTICULAR PURPOSE. The software is provided "as is"
* without express or implied warranty of any kind.
*
* These notices must be retained in any copies of any part of this software.
*/
// #ident "@(#)mip.cc 1.4 98/08/21 SMI"
#include
#include "mip.h"
#define IP_HEADER_SIZE 20
static class IPinIPHeaderClass : public PacketHeaderClass {
public:
IPinIPHeaderClass() : PacketHeaderClass("PacketHeader/IPinIP",
sizeof(hdr_ipinip*)) {
}
} class_ipiniphdr;
static class MIPEncapsulatorClass : public TclClass {
public:
MIPEncapsulatorClass() : TclClass("MIPEncapsulator") {}
TclObject* create(int, const char*const*) {
return (new MIPEncapsulator());
}
} class_mipencapsulator;
MIPEncapsulator::MIPEncapsulator() : Connector(), mask_(0xffffffff),
shift_(8), defttl_(32)
{
bind("addr_", (int*)&(here_.addr_));
bind("port_", (int*)&(here_.port_));
bind("shift_", &shift_);
bind("mask_", &mask_);
bind("off_ip_", &off_ip_);
bind("off_ipinip_", &off_ipinip_);
bind("ttl_", &defttl_);
}
void MIPEncapsulator::recv(Packet* p, Handler *h)
{
Tcl& tcl = Tcl::instance();
hdr_ip* hdr = (hdr_ip*)p->access(off_ip_);
hdr_ipinip **ppinhdr = (hdr_ipinip **)p->access(off_ipinip_);
if (--hdr->ttl_ <= 0) {
/*
* XXX this should be "dropped" somehow. Right now,
* these events aren't traced.
*/
hdr_ipinip *ptr = *ppinhdr, *temp;
while (ptr != NULL) {
temp = ptr;
ptr = ptr->next_;
delete temp;
}
*ppinhdr = NULL;
Packet::free(p);
return;
}
hdr_ipinip *inhdr = new hdr_ipinip;
//int dst = ((hdr->dst() >> shift_) & mask_);
int dst = Address::instance().get_nodeaddr(hdr->daddr());
tcl.evalf("%s tunnel-exit %d", name_, dst);
int te = atoi(tcl.result());
inhdr->next_ = *ppinhdr;
*ppinhdr = inhdr;
inhdr->hdr_ = *hdr;
hdr->saddr() = here_.addr_;
hdr->sport() = here_.port_;
//hdr->dst() = addr_ & ~(~(nsaddr_t)0 << shift_) | (te & mask_) << shift_;;
hdr->daddr() = te;
hdr->dport() = 1;
hdr->ttl() = defttl_;
((hdr_cmn*)p->access(off_cmn_))->size() += IP_HEADER_SIZE;
target_->recv(p,h);
}
static class MIPDecapsulatorClass : public TclClass {
public:
MIPDecapsulatorClass() : TclClass("Classifier/Addr/MIPDecapsulator") {}
TclObject* create(int, const char*const*) {
return (new MIPDecapsulator());
}
} class_mipdecapsulator;
MIPDecapsulator::MIPDecapsulator() : AddressClassifier()
{
//def_target_ = NULL;
bind("off_ipinip_", &off_ipinip_);
bind("off_ip_", &off_ip_);
}
// int MIPDecapsulator::command(int argc, const char*const* argv)
// {
// if (argc == 3) {
// if (strcmp(argv[1], "def-target") == 0) {
// def_target_ = (NsObject *)TclObject::lookup(argv[2]);
// return TCL_OK;
// }
// }
// return (AddressClassifier::command(argc, argv));
// }
void MIPDecapsulator::recv(Packet* p, Handler *h)
{
hdr_ipinip **ppinhdr = (hdr_ipinip **)p->access(off_ipinip_);
// restore inner header
hdr_ip *pouthdr = (hdr_ip *)p->access(off_ip_);
assert(ppinhdr);
hdr_ip *pinhdr = &(*ppinhdr)->hdr_;
*ppinhdr = (*ppinhdr)->next_;
*pouthdr = *pinhdr;
NsObject* link = find(p);
// for mobilenodes use default-target which is probably the
// RA. cannot use node_entry point instead, as hier address
// of MH point to HA. hence hand decapsulated pkt directly
// to RA.
if (link == NULL || pinhdr->ttl_ <= 0) {
/*
* XXX this should be "dropped" somehow. Right now,
* these events aren't traced.
*/
hdr_ipinip *ptr = *ppinhdr, *temp;
while (ptr != NULL) {
temp = ptr;
ptr = ptr->next_;
delete temp;
}
*ppinhdr = NULL;
delete pinhdr;
Packet::free(p);
return;
}
delete pinhdr;
((hdr_cmn*)p->access(off_cmn_))->size() -= IP_HEADER_SIZE;
link->recv(p,h);
}
misc.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1994 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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*
* miscellaneous "ns" commands
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (LBL)";
#endif
#include
#include
#ifndef WIN32
#include
#endif
#include
#include "config.h"
#include "scheduler.h"
#include "random.h"
#if defined(HAVE_INT64)
class Add64Command : public TclCommand {
public:
Add64Command() : TclCommand("ns-add64") {}
virtual int command(int argc, const char*const* argv);
};
int Add64Command::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
if (argc == 3) {
char res[22]; /* A 64 bit int at most 20 digits */
int64_t d1 = STRTOI64(argv[1], NULL, 0);
int64_t d2 = STRTOI64(argv[2], NULL, 0);
sprintf(res, STRTOI64_FMTSTR, d1+d2);
tcl.resultf("%s", res);
return (TCL_OK);
}
tcl.add_error("ns-add64 requires two arguments.");
return (TCL_ERROR);
}
#endif
class RandomCommand : public TclCommand {
public:
RandomCommand() : TclCommand("ns-random") { }
virtual int command(int argc, const char*const* argv);
};
/*
* ns-random
* ns-random $seed
*/
int RandomCommand::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
if (argc == 1) {
sprintf(tcl.buffer(), "%u", Random::random());
tcl.result(tcl.buffer());
} else if (argc == 2) {
int seed = atoi(argv[1]);
if (seed == 0)
seed = Random::seed_heuristically();
else
Random::seed(seed);
tcl.resultf("%d", seed);
}
return (TCL_OK);
}
extern "C" char version_string[];
class VersionCommand : public TclCommand {
public:
VersionCommand() : TclCommand("ns-version") { }
virtual int command(int, const char*const*) {
Tcl::instance().result(version_string);
return (TCL_OK);
}
};
class TimeAtofCommand : public TclCommand {
public:
TimeAtofCommand() : TclCommand("time_atof") { }
virtual int command(int argc, const char*const* argv) {
if (argc != 2)
return (TCL_ERROR);
char* s = (char*) argv[1];
char wrk[32];
char* cp = wrk;
while (isdigit(*s) || *s == 'e' ||
*s == '+' || *s == '-' || *s == '.')
*cp++ = *s++;
*cp = 0;
double v = atof(wrk);
switch (*s) {
case 'm':
v *= 1e-3;
break;
case 'u':
v *= 1e-6;
break;
case 'n':
v *= 1e-9;
break;
case 'p':
v *= 1e-12;
break;
}
Tcl::instance().resultf("%g", v);
return (TCL_OK);
}
};
void init_misc(void)
{
(void)new VersionCommand;
(void)new RandomCommand;
(void)new TimeAtofCommand;
#if defined(HAVE_INT64)
(void)new Add64Command;
#endif
}
mobilenode.cc
/*-*- 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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*/
/* Ported from CMU/Monarch's code, nov'98 -Padma.
* CMU-Monarch project's Mobility extensions ported by Padma Haldar,
* 11/98.
*/
#include
#include
#include
#include
#include
#include
//#include
#include
#include
#include
#include
#include
#include
#include
#include
// XXX Must supply the first parameter in the macro otherwise msvc
// is unhappy.
static LIST_HEAD(_dummy_MobileNodeList, MobileNode) nodehead = { 0 };
//static int MobileNodeIndex = 0;
static class MobileNodeClass : public TclClass {
public:
MobileNodeClass() : TclClass("Node/MobileNode") {}
TclObject* create(int, const char*const*) {
return (new MobileNode);
}
} class_mobilenode;
/*
* PositionHandler()
*
* Updates the position of a mobile node every N seconds, where N is
* based upon the speed of the mobile and the resolution of the topography.
*
*/
void
PositionHandler::handle(Event*)
{
Scheduler& s = Scheduler::instance();
#if 0
fprintf(stderr, "*** POSITION HANDLER for node %d (time: %f) ***\n",
node->address(), s.clock());
#endif
/*
* Update current location
*/
node->update_position();
/*
* Choose a new random speed and direction
*/
#ifdef DEBUG
fprintf(stderr, "%d - %s: calling random_destination()\n",
node->address_, __PRETTY_FUNCTION__);
#endif
node->random_destination();
s.schedule(&node->pos_handle, &node->pos_intr,
node->position_update_interval);
}
/* ======================================================================
Mobile Node
====================================================================== */
MobileNode::MobileNode(void) : Node(), pos_handle(this)
#ifdef NAM_TRACE
, namChan_(0)
#endif
{
X = 0.0; Y = 0.0; Z = 0.0; speed = 0.0;
dX=0.0; dY=0.0; dZ=0.0;
destX=0.0; destY=0.0;
// address_ = MobileNodeIndex++;
random_motion_ = 0;
base_stn_ = -1;
T = 0;
position_update_interval = POSITION_UPDATE_INTERVAL;
position_update_time = 0.0;
LIST_INSERT_HEAD(&nodehead, this, link); // node list
LIST_INIT(&ifhead_); // interface list
bind("X_", &X);
bind("Y_", &Y);
bind("Z_", &Z);
bind("speed_", &speed);
// bind("position_update_interval_", &position_update_interval);
}
int
MobileNode::command(int argc, const char*const* argv)
{
if(argc == 2) {
//Tcl& tcl = Tcl::instance();
// if(strcmp(argv[1], "id") == 0) {
// tcl.resultf("%d", address_);
// return TCL_OK;
// }
if(strcmp(argv[1], "start") == 0) {
start();
return TCL_OK;
}
if(strcmp(argv[1], "log-movement") == 0) {
#ifdef DEBUG
fprintf(stderr,
"%d - %s: calling update_position()\n",
address_, __PRETTY_FUNCTION__);
#endif
update_position();
log_movement();
return TCL_OK;
}
if(strcmp(argv[1], "log-energy") == 0) {
log_energy(1);
return TCL_OK;
}
}
else if(argc == 3) {
if(strcmp(argv[1], "radius") == 0) {
radius_ = strtod(argv[2],NULL);
return TCL_OK;
}
if(strcmp(argv[1], "namattach") == 0) {
Tcl& tcl = Tcl::instance();
int mode;
const char* id = argv[2];
namChan_ = Tcl_GetChannel(tcl.interp(), (char*)id,
&mode);
if (namChan_ == 0) {
tcl.resultf("trace: can't attach %s for writing", id);
return (TCL_ERROR);
}
return (TCL_OK);
}
if(strcmp(argv[1], "random-motion") == 0) {
random_motion_ = atoi(argv[2]);
return TCL_OK;
}
else if(strcmp(argv[1], "addif") == 0) {
WirelessPhy *n = (WirelessPhy*) TclObject::lookup(argv[2]);
if(n == 0)
return TCL_ERROR;
n->insertnode(&ifhead_);
n->setnode(this);
return TCL_OK;
}
else if(strcmp(argv[1], "topography") == 0) {
T = (Topography*) TclObject::lookup(argv[2]);
if(T == 0)
return TCL_ERROR;
return TCL_OK;
}
else if(strcmp(argv[1], "log-target") == 0) {
log_target = (Trace*) TclObject::lookup(argv[2]);
if(log_target == 0)
return TCL_ERROR;
return TCL_OK;
}
else if (strcmp(argv[1],"base-station") == 0) {
//base_stn_ = (MobileNode*) TclObject::lookup(argv[2]);
base_stn_ = atoi(argv[2]);
if(base_stn_ == -1)
return TCL_ERROR;
return TCL_OK;
}
} else if (argc == 5) {
if (strcmp(argv[1], "setdest") == 0)
{ /* setdest */
#ifdef DEBUG
fprintf(stderr, "%d - %s: calling set_destination()\n",
address_, __FUNCTION__);
#endif
if(set_destination(atof(argv[2]), atof(argv[3]), atof(argv[4])) < 0)
return TCL_ERROR;
return TCL_OK;
}
}
return Node::command(argc, argv);
}
/* ======================================================================
Other class functions
====================================================================== */
void
MobileNode::dump(void)
{
Phy *n;
fprintf(stdout, "Index: %d\n", address_);
fprintf(stdout, "Network Interface List\n");
for(n = ifhead_.lh_first; n; n = n->nextnode() )
n->dump();
fprintf(stdout, "--------------------------------------------------\n");
}
/* ======================================================================
Position Functions
====================================================================== */
void
MobileNode::start()
{
Scheduler& s = Scheduler::instance();
if(random_motion_ == 0) {
log_movement();
return;
}
assert(initialized());
random_position();
#ifdef DEBUG
fprintf(stderr, "%d - %s: calling random_destination()\n",
address_, __PRETTY_FUNCTION__);
#endif
random_destination();
s.schedule(&pos_handle, &pos_intr, position_update_interval);
}
void
MobileNode::log_movement()
{
if (!log_target) return;
Scheduler& s = Scheduler::instance();
sprintf(log_target->buffer(),
"M %.5f %d (%.2f, %.2f, %.2f), (%.2f, %.2f), %.2f",
s.clock(), address_, X, Y, Z, destX, destY, speed);
log_target->dump();
}
void
MobileNode::log_energy(int flag)
{
if(!log_target) return;
Scheduler &s = Scheduler::instance();
if (flag) {
sprintf(log_target->buffer(),"N -t %f -n %d -e %f", s.clock(), address_,energy()); } else {
sprintf(log_target->buffer(),"N -t %f -n %d -e 0 ", s.clock(), address_);
}
log_target->dump();
}
void
MobileNode::idle_energy_patch(float total, float P_idle)
{
float real_idle = total-(total_sndtime_+total_rcvtime_+total_sleeptime_);
//printf("total=%f send=%f rcv=%f, slp=%f\n",total, total_sndtime_,total_rcvtime_,total_sleeptime_);
//printf("real_idle=%f\n",real_idle);
energy_model_-> DecrIdleEnergy(real_idle, P_idle);
//set node energy into zero
if ((this->energy_model())->energy() < 0) {
// saying node died
this->energy_model()->setenergy(0);
this->log_energy(0);
}
}
void
MobileNode::bound_position()
{
double minX;
double maxX;
double minY;
double maxY;
int recheck = 1;
assert(T != 0);
minX = T->lowerX();
maxX = T->upperX();
minY = T->lowerY();
maxY = T->upperY();
while(recheck) {
recheck = 0;
if(X < minX) {
X = minX + (minX - X);
recheck = 1;
}
if(X > maxX) {
X = maxX - (X - maxX);
recheck = 1;
}
if(Y < minY) {
Y = minY + (minY - Y);
recheck = 1;
}
if(Y > maxY) {
Y = maxY- (Y - maxY);
recheck = 1;
}
if(recheck) {
fprintf(stderr, "Adjust position of node %d\n",address_);
}
}
}
int
MobileNode::set_destination(double x, double y, double s)
{
assert(initialized());
if(x >= T->upperX() || x <= T->lowerX())
return -1;
if(y >= T->upperY() || y <= T->lowerY())
return -1;
update_position(); // figure out where we are now
destX = x;
destY = y;
speed = s;
dX = destX - X;
dY = destY - Y;
dZ = 0.0; // this isn't used, since flying isn't allowed
if(destX != X || destY != Y) {
// normalize dx, dy to unit len
double len = sqrt( (dX * dX) + (dY * dY) );
dX /= len;
dY /= len;
}
position_update_time = Scheduler::instance().clock();
#ifdef DEBUG
fprintf(stderr, "%d - %s: calling log_movement()\n", address_, __FUNCTION__);
#endif
log_movement();
/* update gridkeeper */
if (GridKeeper::instance()){
GridKeeper* gp = GridKeeper::instance();
gp-> new_moves(this);
}
#ifdef NAM_TRACE
if (namChan_ != 0) {
sprintf(nwrk_,
"n -t %f -s %d -x %f -y %f -u %f -v %f -T %f",
Scheduler::instance().clock(),
nodeid_,
X,Y,
speed*dX, speed*dY,
((speed*dX) != 0 ) ? (destX-X)/(speed*dX) : speed*dX
);
namdump();
}
#endif
return 0;
}
#ifdef NAM_TRACE
void MobileNode::namdump()
{
int n = 0;
/* Otherwise nwrk_ isn't initialized */
if (namChan_ != 0)
n = strlen(nwrk_);
if ((n > 0) && (namChan_ != 0)) {
/*
* tack on a newline (temporarily) instead
* of doing two writes
*/
nwrk_[n] = '\n';
nwrk_[n + 1] = 0;
(void)Tcl_Write(namChan_, nwrk_, n + 1);
nwrk_[n] = 0;
}
}
#endif
void
MobileNode::update_position()
{
double now = Scheduler::instance().clock();
double interval = now - position_update_time;
if(interval == 0.0)
return;
X += dX * (speed * interval);
Y += dY * (speed * interval);
if ((dX > 0 && X > destX) || (dX < 0 && X < destX))
X = destX; // correct overshoot (slow? XXX)
if ((dY > 0 && Y > destY) || (dY < 0 && Y < destY))
Y = destY; // correct overshoot (slow? XXX)
bound_position();
Z = T->height(X, Y);
#if 0
fprintf(stderr, "Node: %d, X: %6.2f, Y: %6.2f, Z: %6.2f, time: %f\n",
address_, X, Y, Z, now);
#endif
position_update_time = now;
}
void
MobileNode::random_position()
{
if(T == 0) {
fprintf(stderr, "No TOPOLOGY assigned\n");
exit(1);
}
X = Random::uniform() * T->upperX();
Y = Random::uniform() * T->upperY();
Z = T->height(X, Y);
position_update_time = 0.0;
}
void
MobileNode::random_destination()
{
if(T == 0) {
fprintf(stderr, "No TOPOLOGY assigned\n");
exit(1);
}
random_speed();
#ifdef DEBUG
fprintf(stderr, "%d - %s: calling set_destination()\n",
address_, __FUNCTION__);
#endif
(void) set_destination(Random::uniform() * T->upperX(),
Random::uniform() * T->upperY(),
speed);
}
void
MobileNode::random_direction()
{
/* this code isn't used anymore -dam 1/22/98 */
double len;
dX = (double) Random::random();
dY = (double) Random::random();
len = sqrt( (dX * dX) + (dY * dY) );
dX /= len;
dY /= len;
dZ = 0.0; // we're not flying...
/*
* Determine the sign of each component of the
* direction vector.
*/
if(X > (T->upperX() - 2*T->resol())) {
if(dX > 0) dX = -dX;
}
else if(X < (T->lowerX() + 2*T->resol())) {
if(dX < 0) dX = -dX;
}
else if(Random::uniform() <= 0.5) {
dX = -dX;
}
if(Y > (T->upperY() - 2*T->resol())) {
if(dY > 0) dY = -dY;
}
else if(Y < (T->lowerY() + 2*T->resol())) {
if(dY < 0) dY = -dY;
}
else if(Random::uniform() <= 0.5) {
dY = -dY;
}
#if 0
fprintf(stderr, "Location: (%f, %f), Direction: (%f, %f)\n",
X, Y, dX, dY);
#endif
}
void
MobileNode::random_speed()
{
speed = Random::uniform() * MAX_SPEED;
}
double
MobileNode::distance(MobileNode *m)
{
update_position(); // update my position
m->update_position(); // update m's position
double Xpos = (X - m->X) * (X - m->X);
double Ypos = (Y - m->Y) * (Y - m->Y);
double Zpos = (Z - m->Z) * (Z - m->Z);
return sqrt(Xpos + Ypos + Zpos);
}
double
MobileNode::propdelay(MobileNode *m)
{
return distance(m) / SPEED_OF_LIGHT;
}
modulation.cc
/* -*- 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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*/
/* Ported from CMU/Monarch's code, nov'98 -Padma.*/
#include
#include
#include
//#include
#include
/* ======================================================================
Binary Phase Shift Keying
====================================================================== */
BPSK::BPSK()
{
Rs = 0;
}
BPSK::BPSK(int S)
{
Rs = S;
}
int
BPSK::BitError(double Pr)
{
double Pe; // probability of error
double x;
int nbit = 0; // number of bit errors tolerated
if(nbit == 0) {
Pe = ProbBitError(Pr);
}
else {
Pe = ProbBitError(Pr, nbit);
}
// quick check
if(Pe == 0.0)
return 0; // no bit errors
// scale the error probabilty
Pe *= 1e3;
x = (double)(((int)Random::uniform()) % 1000);
if(x < Pe)
return 1; // bit error
else
return 0; // no bit errors
}
double
BPSK::ProbBitError(double)
{
double Pe = 0.0;
return Pe;
}
double
BPSK::ProbBitError(double, int)
{
double Pe = 0.0;
return Pe;
}
ms-adc.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) Xerox Corporation 1997. All rights reserved.
*
* License is granted to copy, to use, and to make and to use derivative
* works for research and evaluation purposes, provided that Xerox is
* acknowledged in all documentation pertaining to any such copy or
* derivative work. Xerox grants no other licenses expressed or
* implied. The Xerox trade name should not be used in any advertising
* without its written permission.
*
* XEROX CORPORATION MAKES NO REPRESENTATIONS CONCERNING EITHER THE
* MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE
* FOR ANY PARTICULAR PURPOSE. The software is provided "as is" without
* express or implied warranty of any kind.
*
* These notices must be retained in any copies of any part of this
* software.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$";
#endif
//Measured Sum Admission control
#include "adc.h"
#include
class MS_ADC : public ADC {
public:
MS_ADC();
void rej_action(int, double,int);
protected:
int admit_flow(int,double,int);
inline virtual double get_rate(int /*cl*/, double r,int /*b*/)
{ return r ; };
double utilization_;
};
MS_ADC::MS_ADC()
{
bind("utilization_",&utilization_);
type_ = new char[5];
strcpy(type_, "MSAC");
}
void MS_ADC::rej_action(int cl,double r,int b)
{
double rate = get_rate(cl,r,b);
est_[cl]->change_avload(-rate);
}
int MS_ADC::admit_flow(int cl,double r,int b)
{
double rate = get_rate(cl,r,b);
if (rate+est_[cl]->avload() < utilization_* bandwidth_) {
est_[cl]->change_avload(rate);
return 1;
}
return 0;
}
static class MS_ADCClass : public TclClass {
public:
MS_ADCClass() : TclClass("ADC/MS") {}
TclObject* create(int,const char*const*) {
return (new MS_ADC());
}
}class_ms_adc;
/* a measured sum algorithm that uses peak rather than token rate
*/
class MSPK_ADC : public MS_ADC {
public:
MSPK_ADC() { };
protected:
inline virtual double get_rate(int cl,double r,int b){
return(peak_rate(cl,r,b));};
};
static class MSPK_ADCClass : public TclClass {
public:
MSPK_ADCClass() : TclClass("ADC/MSPK") {}
TclObject* create(int,const char*const*) {
return (new MSPK_ADC());
}
} class_mspk_adc;
net-interface.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* net-interface.cc
* Copyright (C) 1997 by USC/ISI
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation, advertising
* materials, and other materials related to such distribution and use
* acknowledge that the software was developed by the University of
* Southern California, Information Sciences Institute. The name of the
* University may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* Ported by Polly Huang (USC/ISI), http://www-scf.usc.edu/~bhuang
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (USC/ISI)";
#endif
#include "net-interface.h"
int NetworkInterface::command(int argc, const char*const* argv) {
if (argc > 1) {
if (strcmp(argv[1], "label") == 0) {
if (argc == 2) {
Tcl::instance().resultf("%d", intf_label_);
return TCL_OK;
}
if (argc == 3) {
intf_label_ = atoi(argv[2]);
return TCL_OK;
}
}
}
return (Connector::command(argc, argv));
}
void NetworkInterface::recv(Packet* p, Handler* h) {
hdr_cmn* ch = (hdr_cmn*) p->access(off_cmn_);
#ifdef LEO_DEBUG
printf("Marking to %d\n", intf_label_);
#endif
ch->iface() = intf_label_;
ch->direction()= hdr_cmn::NONE; //direction: none
send(p, h);
}
static class NetworkInterfaceClass : public TclClass {
public:
NetworkInterfaceClass() : TclClass("NetworkInterface") {}
TclObject* create(int, const char*const*) {
return (new NetworkInterface);
}
} class_networkinterface;
nilist.cc
/* -*- 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 Research Group 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.
*/
#include
/*#include */
#include "nilist.h"
template
T Slist::get()
{
Tlink* lnk = (Tlink*) slist_base::get();
T i = lnk->info;
delete lnk;
return i;
}
void slist_base::insert(slink *a)
{
count_++;
if (last_)
a->next_ = last_->next_;
else
last_ = a;
last_->next_ = a;
}
#include
void slist_base::remove(slink *a, slink *prev)
{
remove_count_++; /* XXX */
count_--;
if (prev && prev->next_ != a)
printf("In remove(): Error: prev->next!=a prev=%p a=%p\n", prev, a);
if (last_ == NULL)
return;
if (prev == NULL)
if (last_->next_ == a)
prev = last_;
else
return;
prev->next_ = a->next_;
if (last_ == a) // a was last in list
last_ = prev;
if (last_->next_ == a) // a was only one in list
last_ = NULL;
}
void slist_base::append(slink *a)
{
append_count_++; /* XXX */
count_++;
if (last_) {
a->next_ = last_->next_;
last_ = last_->next_ = a;
}
else
last_ = a->next_ = a;
}
slink *slist_base::get()
{
count_--;
if (last_ == 0) return NULL;
slink * f = last_->next_;
if (f == last_)
last_ = 0;
else
last_->next_ = f->next_;
return f;
}
slink *slist_base::find(int key)
{
slink *cur = last_;
if (last_ == 0) return NULL;
do {
cur = cur->next_;
if (cur->key_ == key)
return cur;
} while (cur != last_);
return NULL;
}
slist_base_iter::slist_base_iter(slist_base &s)
{
cs = &s;
ce = cs->last_;
}
slink *
slist_base_iter::operator() ()
// return 0 to indicate end of iteration
{
slink *ret = ce ? (ce=ce->next_) : 0;
if (ce == cs->last_) ce = 0;
return ret;
}
template T* Slist_iter::operator() ()
{
Tlink *lnk = (Tlink *) slist_base_iter::operator() ();
return lnk ? &lnk->info : 0;
}
node.cc
/* -*- 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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*/
/* Ported from CMU/Monarch's code, nov'98 -Padma.
* CMU-Monarch project's Mobility extensions ported by Padma Haldar,
* 10/98.
*
* $Header$
*/
#include
#include
#include
#include
#include
static class LinkHeadClass : public TclClass {
public:
LinkHeadClass() : TclClass("Connector/LinkHead") {}
TclObject* create(int, const char*const*) {
return (new LinkHead);
}
} class_link_head;
LinkHead::LinkHead() : net_if_(0), node_(0), type_(0) { }
int32_t LinkHead::label() {
if (net_if_)
return net_if_->intf_label();
printf("Configuration error: Network Interface missing\n");
exit(1);
// Make msvc happy
return 0;
}
int LinkHead::command(int argc, const char*const* argv)
{
//Tcl& tcl = Tcl::instance();
if (argc == 2) {
} else if (argc == 3) {
if(strcmp(argv[1], "setnetinf") == 0) {
net_if_ =
(NetworkInterface*) TclObject::lookup(argv[2]);
if (net_if_ == 0)
return TCL_ERROR;
return TCL_OK;
} else if(strcmp(argv[1], "setnode") == 0) {
node_ = (Node*) TclObject::lookup(argv[2]);
if (node_ == 0)
return TCL_ERROR;
return TCL_OK;
}
}
return (Connector::command(argc, argv));
}
static class NodeClass : public TclClass {
public:
NodeClass() : TclClass("Node") {}
TclObject* create(int, const char*const*) {
return (new Node);
}
} class_node;
struct node_head Node::nodehead_ = { 0 }; // replaces LIST_INIT macro
Node::Node(void) : address_(-1), energy_model_(NULL), sleep_mode_(0), total_sleeptime_(0),
total_rcvtime_(0), total_sndtime_(0), adaptivefidelity_(0),
powersavingflag_(0), namDefinedFlag_(0), last_time_gosleep(0),max_inroute_time_(300),
maxttl_(5)
{
LIST_INIT(&ifhead_);
LIST_INIT(&linklisthead_);
insert(&(Node::nodehead_)); // insert self into static list of nodes
neighbor_list.neighbor_cnt_ = 0;
neighbor_list.head = NULL;
}
Node::~Node()
{
LIST_REMOVE(this, entry);
}
int
Node::command(int argc, const char*const* argv)
{
if (argc == 2) {
Tcl& tcl = Tcl::instance();
if(strcmp(argv[1], "address?") == 0) {
tcl.resultf("%d", address_);
return TCL_OK;
} else if(strcmp(argv[1], "powersaving") == 0) {
powersavingflag_ = 1;
start_powersaving();
return TCL_OK;
} else if(strcmp(argv[1], "adaptivefidelity") == 0) {
adaptivefidelity_ = 1;
powersavingflag_ = 1;
start_powersaving();
return TCL_OK;
} else if(strcmp(argv[1], "namDefined") == 0) {
namDefinedFlag_ = 1;
return TCL_OK;
} else if (strcmp(argv[1], "energy") == 0) {
Tcl& tcl = Tcl::instance();
tcl.resultf("%f",this->energy());
return TCL_OK;
} else if (strcmp(argv[1], "adjustenergy") == 0) {
// assume every 10 sec schedule and 1.15 W
// idle energy consumption. needs to be
// parameterized.
idle_energy_patch(10, 1.15);
total_sndtime_ = 0;
total_rcvtime_ = 0;
total_sleeptime_ = 0;
return TCL_OK;
}
}
if (argc == 3) {
if(strcmp(argv[1], "addif") == 0) {
WiredPhy* phyp = (WiredPhy*) TclObject::lookup(argv[2]);
if(phyp == 0)
return TCL_ERROR;
phyp->insertnode(&ifhead_);
phyp->setnode(this);
return TCL_OK;
} else if (strcmp(argv[1], "addr") == 0) {
address_ = Address::instance().\
str2addr(argv[2]);
return TCL_OK;
} else if (strcmp(argv[1], "nodeid") == 0) {
nodeid_ = atoi(argv[2]);
return TCL_OK;
} else if (strcmp(argv[1], "addenergymodel") == 0) {
energy_model_ = (EnergyModel *) TclObject::lookup(argv[2]);
if(!energy_model_)
return TCL_ERROR;
return TCL_OK;
} else if(strcmp(argv[1], "addlinkhead") == 0) {
LinkHead* slhp = (LinkHead*) TclObject::lookup(argv[2]);
if (slhp == 0)
return TCL_ERROR;
slhp->insertlink(&linklisthead_);
return TCL_OK;
} else if (strcmp(argv[1], "setsleeptime") == 0) {
afe_->set_sleeptime(atof(argv[2]));
afe_->set_sleepseed(atof(argv[2]));
return TCL_OK;
} else if (strcmp(argv[1], "setenergy") == 0) {
energy_model_->setenergy(atof(argv[2]));
return TCL_OK;
} else if (strcmp(argv[1], "settalive") == 0) {
max_inroute_time_ = atof(argv[2]);
return TCL_OK;
} else if (strcmp(argv[1], "maxttl") == 0) {
maxttl_ = atoi(argv[2]);
return TCL_OK;
}
}
if (argc == 4) {
if(strcmp(argv[1], "idleenergy") == 0) {
this->idle_energy_patch(atof(argv[2]),atof(argv[3]));
return TCL_OK;
}
}
return TclObject::command(argc,argv);
}
// Given an interface label for a NetworkInterface on this node, we return
// the head of that link
NsObject* Node::intf_to_target(int32_t label)
{
LinkHead *lhp = linklisthead_.lh_first;
for (; lhp; lhp = lhp->nextlinkhead())
if (label == lhp->label())
return ((NsObject*) lhp);
return NULL;
}
void
Node::start_powersaving()
{
snh_ = new SoftNeighborHandler(this);
snh_->start();
afe_ = new AdaptiveFidelityEntity(this);
afe_->start();
state_ = POWERSAVING_STATE;
state_start_time_ = Scheduler::instance().clock();
}
void
Node::set_node_sleep(int status)
{
Tcl& tcl=Tcl::instance();
//static float last_time_gosleep;
// status = 1 to set node into sleep mode
// status = 0 to put node back to idel mode.
// time in the sleep mode should be used as credit to idle time energy consumption
if (status) {
last_time_gosleep = Scheduler::instance().clock();
//printf("id=%d : put node into sleep at %f\n",address_,last_time_gosleep);
sleep_mode_ = status;
if (namDefinedFlag_) tcl.evalf("%s add-mark m1 blue hexagon",name());
} else {
sleep_mode_ = status;
if (namDefinedFlag_) tcl.evalf("%s delete-mark m1",name());
//printf("id= %d last_time_sleep = %f\n",address_,last_time_gosleep);
if (last_time_gosleep) {
total_sleeptime_ += Scheduler::instance().clock()-last_time_gosleep;
last_time_gosleep = 0;
}
}
}
void
Node::set_node_state(int state)
{
switch (state_) {
case POWERSAVING_STATE:
case WAITING:
state_ = state;
state_start_time_ = Scheduler::instance().clock();
break;
case INROUTE:
if (state == POWERSAVING_STATE) {
state_ = state;
}
if (state == INROUTE) {
// a data packet is forwarded, needs to reset state_start_time_
state_start_time_= Scheduler::instance().clock();
}
break;
default:
printf("Wrong state, quit...\n");
exit(1);
}
}
void
Node::idle_energy_patch(float /*total*/, float /*P_idle*/)
{
/*
float real_idle = total-(total_sndtime_+total_rcvtime_+total_sleeptime_);
//printf("real_idle=%f\n",real_idle);
energy_model_-> DecrIdleEnergy(real_idle, P_idle);
//set node energy into zero
if ((this->energy_model())->energy() <= 0) {
// saying node died
this->energy_model()->setenergy(0);
this->log_energy(0);
}
*/
}
void
Node::add_neighbor(u_int32_t nodeid)
{
neighbor_list_item *np;
np = neighbor_list.head;
for (; np; np = np->next) {
if (np->id == nodeid) {
np->ttl = maxttl_;
break;
}
}
if (!np) { // insert this new entry
np = new neighbor_list_item;
np->id = nodeid;
np->ttl = maxttl_;
np->next = neighbor_list.head;
neighbor_list.head = np;
neighbor_list.neighbor_cnt_++;
}
}
void
Node::scan_neighbor()
{
neighbor_list_item *np, *lp;
if (neighbor_list.neighbor_cnt_ > 0) {
lp = neighbor_list.head;
np = lp->next;
for (; np; np = np->next) {
np->ttl--;
if (np->ttl <= 0){
lp->next = np->next;
delete np;
np = lp;
neighbor_list.neighbor_cnt_--;
}
lp = np;
}
// process the first element
np = neighbor_list.head;
np->ttl--;
if (np->ttl <= 0) {
neighbor_list.head = np->next;
delete np;
neighbor_list.neighbor_cnt_--;
}
}
}
void
SoftNeighborHandler::start()
{
Scheduler &s = Scheduler::instance();
s.schedule(this, &intr, CHECKFREQ);
}
void
SoftNeighborHandler::handle(Event *)
{
Scheduler &s = Scheduler::instance();
nid_->scan_neighbor();
s.schedule(this, &intr, CHECKFREQ);
}
void
AdaptiveFidelityEntity::start()
{
float start_idletime;
Scheduler &s = Scheduler::instance();
sleep_time_ = 2;
sleep_seed_ = 2;
idle_time_ = 10;
start_idletime = Random::uniform(0, idle_time_);
//printf("node id_ = %d starttime=%f\n", nid_, start_idletime);
nid_->set_node_sleep(0);
s.schedule(this, &intr, start_idletime);
}
void
AdaptiveFidelityEntity::handle(Event *)
{
Scheduler &s = Scheduler::instance();
int node_state = nid_->state();
//printf("Node %d at State %d at time %f is in sleep %d \n", nid_->address(), node_state, s.clock(), nid_->sleep());
//printf("node id = %d: sleep_time=%f\n",nid_->address(),sleep_time_);
switch (node_state) {
case POWERSAVING_STATE:
if (nid_->sleep()) {
// node is in sleep mode, wake it up
nid_->set_node_sleep(0);
adapt_it();
s.schedule(this, &intr, idle_time_);
} else {
// node is in idle mode, put it into sleep
nid_->set_node_sleep(1);
adapt_it();
s.schedule(this, &intr, sleep_time_);
}
break;
case INROUTE:
// 100s is the maximum INROUTE time.
if (s.clock()-(nid_->state_start_time()) < nid_->max_inroute_time()) {
s.schedule(this, &intr, idle_time_);
} else {
nid_->set_node_state(POWERSAVING_STATE);
adapt_it();
nid_->set_node_sleep(1);
s.schedule(this, &intr, sleep_time_);
}
break;
case WAITING:
// 10s is the maximum WAITING time
if (s.clock()-(nid_->state_start_time()) < MAX_WAITING_TIME) {
s.schedule(this, &intr, idle_time_);
} else {
nid_->set_node_state(POWERSAVING_STATE);
adapt_it();
nid_->set_node_sleep(1);
s.schedule(this, &intr, sleep_time_);
}
break;
default:
fprintf(stderr, "Illegal Node State!");
abort();
}
}
void
AdaptiveFidelityEntity::adapt_it()
{
float delay;
// use adaptive fidelity
if (nid_->adaptivefidelity()) {
int neighbors = nid_->getneighbors();
if (!neighbors) neighbors=1;
delay = sleep_seed_*Random::uniform(1,neighbors);
this->set_sleeptime(delay);
}
}
// return the node instance from the static node list
Node * Node::get_node_by_address (nsaddr_t id)
{
Node * tnode = nodehead_.lh_first;
for (; tnode; tnode = tnode->nextnode()) {
if (tnode->address_ == id ) {
return (tnode);
}
}
return NULL;
}
#ifdef zero
static class HierNodeClass : public TclClass {
public:
HierNodeClass() : TclClass("HierNode") {}
TclObject* create(int, const char*const*) {
return (new HierNode);
}
} class_hier_node;
static class ManualRtNodeClass : public TclClass {
public:
ManualRtNodeClass() : TclClass("ManualRtNode") {}
TclObject* create(int, const char*const*) {
return (new ManualRtNode);
}
} class_ManualRt_node;
static class VirtualClassifierNodeClass : public TclClass {
public:
VirtualClassifierNodeClass() : TclClass("VirtualClassifierNode") {}
TclObject* create(int, const char*const*) {
return (new VirtualClassifierNode);
}
} class_VirtualClassifier_node;
#endif
ns-process.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
//
// Copyright (c) 1997 by the University of Southern California
// All rights reserved.
//
// Permission to use, copy, modify, and distribute this software and its
// documentation in source and binary forms for non-commercial purposes
// and without fee is hereby granted, provided that the above copyright
// notice appear in all copies and that both the copyright notice and
// this permission notice appear in supporting documentation. and that
// any documentation, advertising materials, and other materials related
// to such distribution and use acknowledge that the software was
// developed by the University of Southern California, Information
// Sciences Institute. The name of the University may not be used to
// endorse or promote products derived from this software without
// specific prior written permission.
//
// THE UNIVERSITY OF SOUTHERN CALIFORNIA makes no representations about
// the suitability of this software for any purpose. THIS SOFTWARE IS
// PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
// INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
//
// Other copyrights might apply to parts of this software and are so
// noted when applicable.
//
// ADU and ADU processor
//
// $Header$
#include "ns-process.h"
static class ProcessClass : public TclClass {
public:
ProcessClass() : TclClass("Process") {}
TclObject* create(int, const char*const*) {
return (new Process);
}
} class_process;
void Process::process_data(int, AppData*)
{
abort();
}
AppData* Process::get_data(int&, AppData*)
{
abort();
/* NOTREACHED */
return NULL; // Make msvc happy
}
int Process::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
if (strcmp(argv[1], "target") == 0) {
if (argc == 2) {
tcl.resultf("%s", target()->name());
return TCL_OK;
} else if (argc == 3) {
Process *p = (Process *)TclObject::lookup(argv[2]);
if (p == NULL) {
fprintf(stderr, "Non-existent media app %s\n",
argv[2]);
abort();
}
target() = p;
return TCL_OK;
}
}
return TclObject::command(argc, argv);
}
ns_tclsh.cc
/*
*
* Provides a default version of the Tcl_AppInit procedure for
* use in wish and similar Tk-based applications.
*
* Copyright (c) 1993 The Regents of the University of California.
* All rights reserved.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
* OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
* CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*/
#include
#include "config.h"
extern init_misc();
extern "C" {
int
Tcl_AppInit(Tcl_Interp *interp)
{
if (Tcl_Init(interp) == TCL_ERROR ||
Otcl_Init(interp) == TCL_ERROR)
return TCL_ERROR;
Tcl_SetVar(interp, "tcl_rcFileName", "~/.ns.tcl", TCL_GLOBAL_ONLY);
Tcl::init(interp, "ns");
extern EmbeddedTcl et_ns_lib;
et_ns_lib.load();
init_misc();
return (TCL_OK);
}
int
main(int argc, char** argv)
{
Tcl_Main(argc, argv, Tcl_AppInit);
return 0; /* Needed only to prevent compiler warning. */
}
}
null-estimator.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) Xerox Corporation 1997. All rights reserved.
*
* License is granted to copy, to use, and to make and to use derivative
* works for research and evaluation purposes, provided that Xerox is
* acknowledged in all documentation pertaining to any such copy or
* derivative work. Xerox grants no other licenses expressed or
* implied. The Xerox trade name should not be used in any advertising
* without its written permission.
*
* XEROX CORPORATION MAKES NO REPRESENTATIONS CONCERNING EITHER THE
* MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE
* FOR ANY PARTICULAR PURPOSE. The software is provided "as is" without
* express or implied warranty of any kind.
*
* These notices must be retained in any copies of any part of this
* software.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$";
#endif
/* Dummy estimator that only measures actual utilization */
#include
#include "estimator.h"
class Null_Est : public Estimator {
public:
Null_Est();
protected:
void estimate();
};
Null_Est::Null_Est()
{
}
void Null_Est::estimate() {
measload_ = meas_mod_->bitcnt()/period_;
meas_mod_->resetbitcnt();
}
static class Null_EstClass : public TclClass {
public:
Null_EstClass() : TclClass ("Est/Null") {}
TclObject* create(int,const char*const*) {
return (new Null_Est());
}
}class_null_est;
object.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1994 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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (LBL)";
#endif
#include
#include "object.h"
#include "packet.h"
#include "flags.h"
NsObject::~NsObject()
{
}
NsObject::NsObject()
{
#ifdef OFF_HDR
#else
off_cmn_ = hdr_cmn::offset();
off_flags_ = hdr_flags::offset();
#endif
// Turn off debug by default
debug_ = 0;
}
void
NsObject::delay_bind_init_all()
{
#ifdef OFF_HDR
delay_bind_init_one("off_cmn_");
delay_bind_init_one("off_flags_");
#endif
delay_bind_init_one("debug_");
}
int
NsObject::delay_bind_dispatch(const char *varName, const char *localName, TclObject *tracer)
{
#ifdef OFF_HDR
if (delay_bind(varName, localName, "off_cmn_", &off_cmn_, tracer))
return TCL_OK;
if (delay_bind(varName, localName, "off_flags_", &off_flags_, tracer))
return TCL_OK;
#endif
if (delay_bind_bool(varName, localName, "debug_", &debug_, tracer))
return TCL_OK;
return TclObject::delay_bind_dispatch(varName, localName, tracer);
}
void NsObject::reset()
{
}
int NsObject::command(int argc, const char*const* argv)
{
if (argc == 2) {
if (strcmp(argv[1], "reset") == 0) {
reset();
return (TCL_OK);
}
}
return (TclObject::command(argc, argv));
}
/*
* Packets may be handed to NsObjects at sheduled points
* in time since a node is an event handler and a packet
* is an event. Packets should be the only type of event
* scheduled on a node so we can carry out the cast below.
*/
void NsObject::handle(Event* e)
{
recv((Packet*)e);
}
void NsObject::recv(Packet *p, const char*)
{
Packet::free(p);
}
// Debugging output for all TclObjects. By default, print to stdout
void NsObject::debug(const char *fmt, ...)
{
if (!debug_)
return;
va_list ap;
va_start(ap, fmt);
vprintf(fmt, ap);
}
omni-antenna.cc
/* -*- 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.
* Ported from CMU/Monarch's code, nov'98 -Padma.
omni-antenna.cc
$Id: AllCode__.html 980 2002-12-12 09:36:58Z ffilali $
*/
#include
static class OmniAntennaClass : public TclClass {
public:
OmniAntennaClass() : TclClass("Antenna/OmniAntenna") {}
TclObject* create(int, const char*const*) {
return (new OmniAntenna);
}
} class_OmniAntenna;
OmniAntenna::OmniAntenna() {
Gt_ = 1.0;
Gr_ = 1.0;
bind("Gt_", &Gt_);
bind("Gr_", &Gr_);
}
packet.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1994-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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (LBL)";
#endif
#include "packet.h"
#include "flags.h"
p_info packet_info;
char* p_info::name_[PT_NTYPE+1];
int Packet::hdrlen_ = 0; // size of a packet's header
Packet* Packet::free_; // free list
int hdr_cmn::offset_; // static offset of common header
int hdr_flags::offset_; // static offset of flags header
PacketHeaderClass::PacketHeaderClass(const char* classname, int hdrlen) :
TclClass(classname), hdrlen_(hdrlen), offset_(0)
{
}
TclObject* PacketHeaderClass::create(int, const char*const*)
{
return (0);
}
void PacketHeaderClass::bind()
{
TclClass::bind();
Tcl& tcl = Tcl::instance();
tcl.evalf("%s set hdrlen_ %d", classname_, hdrlen_);
export_offsets();
add_method("offset");
}
void PacketHeaderClass::export_offsets()
{
}
void PacketHeaderClass::field_offset(const char* fieldname, int offset)
{
Tcl& tcl = Tcl::instance();
tcl.evalf("%s set offset_(%s) %d", classname_, fieldname, offset);
}
int PacketHeaderClass::method(int ac, const char*const* av)
{
Tcl& tcl = Tcl::instance();
int argc = ac - 2;
const char*const* argv = av + 2;
if (argc == 3) {
if (strcmp(argv[1], "offset") == 0) {
if (offset_) {
*offset_ = atoi(argv[2]);
return TCL_OK;
}
tcl.resultf("Warning: cannot set offset_ for %s",
classname_);
return TCL_OK;
}
}
else if (argc == 2) {
if (strcmp(argv[1], "offset") == 0) {
if (offset_) {
tcl.resultf("%d", *offset_);
return TCL_OK;
}
}
}
return TclClass::method(argc, argv);
}
class CommonHeaderClass : public PacketHeaderClass {
public:
CommonHeaderClass() : PacketHeaderClass("PacketHeader/Common",
sizeof(hdr_cmn)) {
bind_offset(&hdr_cmn::offset_);
}
void export_offsets() {
field_offset("ptype_", OFFSET(hdr_cmn, ptype_));
field_offset("size_", OFFSET(hdr_cmn, size_));
field_offset("uid_", OFFSET(hdr_cmn, uid_));
field_offset("error_", OFFSET(hdr_cmn, error_));
};
} class_cmnhdr;
class FlagsHeaderClass : public PacketHeaderClass {
public:
FlagsHeaderClass() : PacketHeaderClass("PacketHeader/Flags",
sizeof(hdr_flags)) {
bind_offset(&hdr_flags::offset_);
}
} class_flagshdr;
/* manages active packet header types */
class PacketHeaderManager : public TclObject {
public:
PacketHeaderManager() {
bind("hdrlen_", &Packet::hdrlen_);
}
};
static class PacketHeaderManagerClass : public TclClass {
public:
PacketHeaderManagerClass() : TclClass("PacketHeaderManager") {}
TclObject* create(int, const char*const*) {
return (new PacketHeaderManager);
}
} class_packethdr_mgr;
param-adc.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) Xerox Corporation 1997. All rights reserved.
*
* License is granted to copy, to use, and to make and to use derivative
* works for research and evaluation purposes, provided that Xerox is
* acknowledged in all documentation pertaining to any such copy or
* derivative work. Xerox grants no other licenses expressed or
* implied. The Xerox trade name should not be used in any advertising
* without its written permission.
*
* XEROX CORPORATION MAKES NO REPRESENTATIONS CONCERNING EITHER THE
* MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE
* FOR ANY PARTICULAR PURPOSE. The software is provided "as is" without
* express or implied warranty of any kind.
*
* These notices must be retained in any copies of any part of this
* software.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$";
#endif
/* Parameter-based admission control. Admission decisions are
* based on the sum of reserved rates.
*/
#include "adc.h"
#include
class Param_ADC : public ADC {
public:
Param_ADC();
void teardown_action(int,double,int);
void rej_action(int,double,int);
void trace(TracedVar* v);
protected:
int admit_flow(int,double,int);
double utilization_;
TracedDouble resv_rate_;
double oresv_rate_;
};
Param_ADC::Param_ADC() : resv_rate_(0), oresv_rate_(0)
{
bind("utilization_",&utilization_);
type_ = new char[5];
strcpy(type_, "PBAC");
resv_rate_.tracer(this);
resv_rate_.name("\"Reserved Rate\"");
}
void Param_ADC::rej_action(int /*cl*/,double p,int /*r*/)
{
resv_rate_-=p;
}
void Param_ADC::teardown_action(int /*cl*/,double p,int /*r*/)
{
resv_rate_-=p;
}
int Param_ADC::admit_flow(int /*cl*/,double r,int /*b*/)
{
if (resv_rate_ + r <= utilization_ * bandwidth_) {
resv_rate_ +=r;
return 1;
}
return 0;
}
static class Param_ADCClass : public TclClass {
public:
Param_ADCClass() : TclClass("ADC/Param") {}
TclObject* create(int,const char*const*) {
return (new Param_ADC());
}
}class_param_adc;
void Param_ADC::trace(TracedVar* v)
{
char wrk[500];
double *p, newval;
/* check for right variable */
if (strcmp(v->name(), "\"Reserved Rate\"") == 0) {
p = &oresv_rate_;
}
else {
fprintf(stderr, "PBAC: unknown trace var %s\n", v->name());
return;
}
newval = double(*((TracedDouble*)v));
if (tchan_) {
int n;
double t = Scheduler::instance().clock();
/* f -t 0.0 -s 1 -a SA -T v -n Num -v 0 -o 0 */
sprintf(wrk, "f -t %g -s %d -a %s:%d-%d -T v -n %s -v %g -o %g",
t, src_, type_, src_, dst_, v->name(), newval, *p);
n = strlen(wrk);
wrk[n] = '\n';
wrk[n+1] = 0;
(void)Tcl_Write(tchan_, wrk, n+1);
}
*p = newval;
return;
}
pareto.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) Xerox Corporation 1997. All rights reserved.
*
* License is granted to copy, to use, and to make and to use derivative
* works for research and evaluation purposes, provided that Xerox is
* acknowledged in all documentation pertaining to any such copy or derivative
* work. Xerox grants no other licenses expressed or implied. The Xerox trade
* name should not be used in any advertising without its written permission.
*
* XEROX CORPORATION MAKES NO REPRESENTATIONS CONCERNING EITHER THE
* MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE
* FOR ANY PARTICULAR PURPOSE. The software is provided "as is" without
* express or implied warranty of any kind.
*
* These notices must be retained in any copies of any part of this software.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (Xerox)";
#endif
#include "random.h"
#include "trafgen.h"
/* implement an on/off source with average on and off times taken
* from a pareto distribution. (enough of these sources multiplexed
* produces aggregate traffic that is LRD). It is parameterized
* by the average burst time, average idle time, burst rate, and
* pareto shape parameter and packet size.
*/
class POO_Traffic : public TrafficGenerator {
public:
POO_Traffic();
virtual double next_interval(int&);
int on() { return on_ ; }
protected:
void init();
double ontime_; /* average length of burst (sec) */
double offtime_; /* average idle period (sec) */
double rate_; /* send rate during burst (bps) */
double interval_; /* inter-packet time at burst rate */
double burstlen_; /* average # packets/burst */
double shape_; /* pareto shape parameter */
unsigned int rem_; /* number of packets remaining in current burst */
double p1_; /* parameter for pareto distribution to compute
* number of packets in burst.
*/
double p2_; /* parameter for pareto distribution to compute
* length of idle period.
*/
int on_; /* denotes whether in the on or off state */
};
static class POOTrafficClass : public TclClass {
public:
POOTrafficClass() : TclClass("Application/Traffic/Pareto") {}
TclObject* create(int, const char*const*) {
return (new POO_Traffic());
}
} class_poo_traffic;
POO_Traffic::POO_Traffic()
{
bind_time("burst_time_", &ontime_);
bind_time("idle_time_", &offtime_);
bind_bw("rate_", &rate_);
bind("shape_", &shape_);
bind("packetSize_", &size_);
}
void POO_Traffic::init()
{
interval_ = (double)(size_ << 3)/(double)rate_;
burstlen_ = ontime_/interval_;
rem_ = 0;
on_ = 0;
p1_ = burstlen_ * (shape_ - 1.0)/shape_;
p2_ = offtime_ * (shape_ - 1.0)/shape_;
if (agent_)
agent_->set_pkttype(PT_PARETO);
}
double POO_Traffic::next_interval(int& size)
{
double t = interval_;
on_ = 1;
if (rem_ == 0) {
/* compute number of packets in next burst */
rem_ = int(Random::pareto(p1_, shape_) + .5);
/* make sure we got at least 1 */
if (rem_ == 0)
rem_ = 1;
/* start of an idle period, compute idle time */
t += Random::pareto(p2_, shape_);
on_ = 0;
}
rem_--;
size = size_;
return(t);
}
phy.cc
/* -*- 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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*
* @(#) $Header:
*
* Ported from CMU/Monarch's code, nov'98 -Padma Haldar.
* phy.cc
*/
#include
#include "config.h"
#include
#include
#include
class Mac;
static int InterfaceIndex = 0;
Phy::Phy() : BiConnector() {
index_ = InterfaceIndex++;
bandwidth_ = 0.0;
channel_ = 0;
node_ = 0;
head_ = 0;
}
int
Phy::command(int argc, const char*const* argv) {
if (argc == 2) {
Tcl& tcl = Tcl::instance();
if(strcmp(argv[1], "id") == 0) {
tcl.resultf("%d", index_);
return TCL_OK;
}
}
else if(argc == 3) {
TclObject *obj;
if( (obj = TclObject::lookup(argv[2])) == 0) {
fprintf(stderr, "%s lookup failed\n", argv[1]);
return TCL_ERROR;
}
if (strcmp(argv[1], "channel") == 0) {
assert(channel_ == 0);
channel_ = (Channel*) obj;
downtarget_ = (NsObject*) obj;
// LIST_INSERT_HEAD() is done by Channel
return TCL_OK;
}
else if (strcmp(argv[1], "node") == 0) {
assert(node_ == 0);
node_ = (Node*) obj;
// LIST_INSERT_HEAD() is done by Node
return TCL_OK;
}
else if (strcmp(argv[1], "linkhead") == 0) {
head_ = (LinkHead*) obj;
return (TCL_OK);
}
}
return BiConnector::command(argc, argv);
}
void
Phy::recv(Packet* p, Handler*)
{
struct hdr_cmn *hdr = HDR_CMN(p);
//struct hdr_sr *hsr = HDR_SR(p);
/*
* Handle outgoing packets
*/
switch(hdr->direction()) {
case hdr_cmn::DOWN :
/*
* The MAC schedules its own EOT event so we just
* ignore the handler here. It's only purpose
* it distinguishing between incoming and outgoing
* packets.
*/
sendDown(p);
return;
case hdr_cmn::UP :
if (sendUp(p) == 0) {
/*
* XXX - This packet, even though not detected,
* contributes to the Noise floor and hence
* may affect the reception of other packets.
*/
Packet::free(p);
return;
} else {
uptarget_->recv(p, (Handler*) 0);
}
break;
default:
printf("Direction for pkt-flow not specified; Sending pkt up the stack on default.\n\n");
if (sendUp(p) == 0) {
/*
* XXX - This packet, even though not detected,
* contributes to the Noise floor and hence
* may affect the reception of other packets.
*/
Packet::free(p);
return;
} else {
uptarget_->recv(p, (Handler*) 0);
}
}
}
/* NOTE: this might not be the best way to structure the relation
between the actual interfaces subclassed from net-if(phy) and
net-if(phy).
It's fine for now, but if we were to decide to have the interfaces
themselves properly handle multiple incoming packets (they currently
require assistance from the mac layer to do this), then it's not as
generic as I'd like. The way it is now, each interface will have to
have it's own logic to keep track of the packets that are arriving.
Seems like this is general service that net-if could provide.
Ok. A fair amount of restructuring is going to have to happen here
when/if net-if keep track of the noise floor at their location. I'm
gonna punt on it for now.
Actually, this may be all wrong. Perhaps we should keep a separate
noise floor per antenna, which would mean the particular interface types
would have to track noise floor themselves, since only they know what
kind of antenna diversity they have. -dam 8/7/98 */
// double
// Phy::txtime(Packet *p) const
// {
// hdr_cmn *hdr = HDR_CMN(p);
// return hdr->size() * 8.0 / Rb_;
// }
void
Phy::dump(void) const
{
fprintf(stdout, "\tINDEX: %d\n",
index_);
fprintf(stdout, "\tuptarget: %x, channel: %x",
(u_int32_t) uptarget_, (u_int32_t) channel_);
}
ping.cc
/*
* File: Code for a new 'Ping' Agent Class for the ns
* network simulator
* Author: Marc Greis (greis@cs.uni-bonn.de), May 1998
*
* IMPORTANT: Incase of any changes made to this file ,
* tutorial/examples/ping.cc file (used in Greis' tutorial) should
* be updated as well.
*/
#include "ping.h"
static class PingHeaderClass : public PacketHeaderClass {
public:
PingHeaderClass() : PacketHeaderClass("PacketHeader/Ping",
sizeof(hdr_ping)) {}
} class_pinghdr;
static class PingClass : public TclClass {
public:
PingClass() : TclClass("Agent/Ping") {}
TclObject* create(int, const char*const*) {
return (new PingAgent());
}
} class_ping;
PingAgent::PingAgent() : Agent(PT_PING)
{
bind("packetSize_", &size_);
bind("off_ping_", &off_ping_);
}
int PingAgent::command(int argc, const char*const* argv)
{
if (argc == 2) {
if (strcmp(argv[1], "send") == 0) {
// Create a new packet
Packet* pkt = allocpkt();
// Access the Ping header for the new packet:
hdr_ping* hdr = (hdr_ping*)pkt->access(off_ping_);
// Set the 'ret' field to 0, so the receiving node knows
// that it has to generate an echo packet
hdr->ret = 0;
// Store the current time in the 'send_time' field
hdr->send_time = Scheduler::instance().clock();
// Send the packet
send(pkt, 0);
// return TCL_OK, so the calling function knows that the
// command has been processed
return (TCL_OK);
}
}
// If the command hasn't been processed by PingAgent()::command,
// call the command() function for the base class
return (Agent::command(argc, argv));
}
void PingAgent::recv(Packet* pkt, Handler*)
{
// Access the IP header for the received packet:
hdr_ip* hdrip = (hdr_ip*)pkt->access(off_ip_);
// Access the Ping header for the received packet:
hdr_ping* hdr = (hdr_ping*)pkt->access(off_ping_);
// Is the 'ret' field = 0 (i.e. the receiving node is being pinged)?
if (hdr->ret == 0) {
// Send an 'echo'. First save the old packet's send_time
double stime = hdr->send_time;
// Discard the packet
Packet::free(pkt);
// Create a new packet
Packet* pktret = allocpkt();
// Access the Ping header for the new packet:
hdr_ping* hdrret = (hdr_ping*)pktret->access(off_ping_);
// Set the 'ret' field to 1, so the receiver won't send another echo
hdrret->ret = 1;
// Set the send_time field to the correct value
hdrret->send_time = stime;
// Send the packet
send(pktret, 0);
} else {
// A packet was received. Use tcl.eval to call the Tcl
// interpreter with the ping results.
// Note: In the Tcl code, a procedure 'Agent/Ping recv {from rtt}'
// has to be defined which allows the user to react to the ping
// result.
char out[100];
// Prepare the output to the Tcl interpreter. Calculate the round
// trip time
sprintf(out, "%s recv %d %3.1f", name(),
hdrip->src_.addr_ >> Address::instance().NodeShift_[1],
(Scheduler::instance().clock()-hdr->send_time) * 1000);
Tcl& tcl = Tcl::instance();
tcl.eval(out);
// Discard the packet
Packet::free(pkt);
}
}
pkt-counter.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* pkt-counter.cc
* Copyright (C) 1997 by USC/ISI
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation, advertising
* materials, and other materials related to such distribution and use
* acknowledge that the software was developed by the University of
* Southern California, Information Sciences Institute. The name of the
* University may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* Ported by Polly Huang (USC/ISI), http://www-scf.usc.edu/~bhuang
*
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (USC/ISI)";
#endif
#include "connector.h"
class PktCounter : public Connector {
public:
PktCounter() : count_(0) {
}
int command(int argc, const char*const* argv);
void recv(Packet* pkt, Handler* h) {
count_++;
send(pkt, h);
}
protected:
int count_;
};
static class PktCounterClass : public TclClass {
public:
PktCounterClass() : TclClass("PktCounter") {}
TclObject* create(int, const char*const*) {
return (new PktCounter);
}
} class_pktcounter;
int PktCounter::command(int argc, const char*const* argv)
{
if (argc == 2) {
if (strcmp(argv[1], "reset") == 0) {
count_ = 0;
return (TCL_OK);
}
if (strcmp(argv[1], "value") == 0) {
Tcl& tcl = Tcl::instance();
tcl.resultf("%d", count_);
return (TCL_OK);
}
}
return (Connector::command(argc, argv));
}
pointsample-est.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) Xerox Corporation 1997. All rights reserved.
*
* License is granted to copy, to use, and to make and to use derivative
* works for research and evaluation purposes, provided that Xerox is
* acknowledged in all documentation pertaining to any such copy or
* derivative work. Xerox grants no other licenses expressed or
* implied. The Xerox trade name should not be used in any advertising
* without its written permission.
*
* XEROX CORPORATION MAKES NO REPRESENTATIONS CONCERNING EITHER THE
* MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE
* FOR ANY PARTICULAR PURPOSE. The software is provided "as is" without
* express or implied warranty of any kind.
*
* These notices must be retained in any copies of any part of this
* software.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$";
#endif
#include "estimator.h"
#include
#include
class PointSample_Est : public Estimator {
public:
PointSample_Est() {};
protected:
void estimate();
};
void PointSample_Est::estimate()
{
avload_=meas_mod_->bitcnt()/period_;
//printf("%f %f\n",Scheduler::instance().clock(),avload_);
fflush(stdout);
meas_mod_->resetbitcnt();
}
static class PointSample_EstClass : public TclClass {
public:
PointSample_EstClass() : TclClass ("Est/PointSample") {}
TclObject* create(int,const char*const*) {
return (new PointSample_Est());
}
}class_pointsample_est;
priqueue.cc
/* -*- 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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*/
/* Ported from CMU/Monarch's code, nov'98 -Padma.*/
/* -*- c++ -*-
priqueue.cc
A simple priority queue with a remove packet function
$Id: AllCode__.html 980 2002-12-12 09:36:58Z ffilali $
*/
#include
#include
#include
#include
//#include
#include "priqueue.h"
typedef int (*PacketFilter)(Packet *, void *);
PriQueue_List PriQueue::prhead = { 0 };
static class PriQueueClass : public TclClass {
public:
PriQueueClass() : TclClass("Queue/DropTail/PriQueue") {}
TclObject* create(int, const char*const*) {
return (new PriQueue);
}
} class_PriQueue;
PriQueue::PriQueue() : DropTail()
{
bind("Prefer_Routing_Protocols", &Prefer_Routing_Protocols);
LIST_INSERT_HEAD(&prhead, this, link);
}
int
PriQueue::command(int argc, const char*const* argv)
{
if (argc == 2 && strcasecmp(argv[1], "reset") == 0)
{
Terminate();
//FALL-THROUGH to give parents a chance to reset
}
return DropTail::command(argc, argv);
}
void
PriQueue::recv(Packet *p, Handler *h)
{
struct hdr_cmn *ch = HDR_CMN(p);
if(Prefer_Routing_Protocols) {
switch(ch->ptype()) {
case PT_DSR:
case PT_MESSAGE:
case PT_TORA:
case PT_AODV:
recvHighPriority(p, h);
break;
default:
Queue::recv(p, h);
}
}
else {
Queue::recv(p, h);
}
}
void
PriQueue::recvHighPriority(Packet *p, Handler *)
// insert packet at front of queue
{
q_->enqueHead(p);
if (q_->length() >= qlim_)
{
Packet *to_drop = q_->lookup(q_->length()-1);
q_->remove(to_drop);
drop(to_drop);
}
if (!blocked_) {
/*
* We're not blocked. Get a packet and send it on.
* We perform an extra check because the queue
* might drop the packet even if it was
* previously empty! (e.g., RED can do this.)
*/
p = deque();
if (p != 0) {
blocked_ = 1;
target_->recv(p, &qh_);
}
}
}
void
PriQueue::filter(PacketFilter filter, void * data)
// apply filter to each packet in queue,
// - if filter returns 0 leave packet in queue
// - if filter returns 1 remove packet from queue
{
int i = 0;
while (i < q_->length())
{
Packet *p = q_->lookup(i);
if (filter(p,data))
{
q_->remove(p); // decrements q len
}
else i++;
}
}
Packet*
PriQueue::filter(nsaddr_t id)
{
Packet *p = 0;
Packet *pp = 0;
struct hdr_cmn *ch;
for(p = q_->head(); p; p = p->next_) {
ch = HDR_CMN(p);
if(ch->next_hop() == id)
break;
pp = p;
}
/*
* Deque Packet
*/
if(p) {
if(pp == 0)
q_->remove(p);
else
q_->remove(p, pp);
}
return p;
}
/*
* Called at the end of the simulation to purge the IFQ.
*/
void
PriQueue::Terminate()
{
Packet *p;
while((p = deque())) {
//drop(p, DROP_END_OF_SIMULATION);
drop(p);
}
}
propagation.cc
/* -*- 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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*
propagation.cc
$Id: AllCode__.html 980 2002-12-12 09:36:58Z ffilali $
*/
#include
#include
#include
#include
class PacketStamp;
int
Propagation::command(int argc, const char*const* argv)
{
TclObject *obj;
if(argc == 3)
{
if( (obj = TclObject::lookup(argv[2])) == 0)
{
fprintf(stderr, "Propagation: %s lookup of %s failed\n", argv[1],
argv[2]);
return TCL_ERROR;
}
if (strcasecmp(argv[1], "topography") == 0)
{
topo = (Topography*) obj;
return TCL_OK;
}
}
return TclObject::command(argc,argv);
}
/* As new network-intefaces are added, add a default method here */
double
Propagation::Pr(PacketStamp *, PacketStamp *, Phy *)
{
fprintf(stderr,"Propagation model %s not implemented for generic NetIF\n",
name);
abort();
return 0; // Make msvc happy
}
double
Propagation::Pr(PacketStamp *, PacketStamp *, WirelessPhy *)
{
fprintf(stderr,
"Propagation model %s not implemented for SharedMedia interface\n",
name);
abort();
return 0; // Make msvc happy
}
ptypes2tcl.cc
#include
#include
#include "packet.h"
// The following copied from ~tclcl/tcl2c++.c
/*
* Define TCL2C_INT if your compiler has problems with long strings.
*/
#if defined(WIN32) || defined(_WIN32) || defined(__alpha__) || defined(__hpux)
#define TCL2C_INT
#endif
char* p_info::name_[PT_NTYPE+1];
void
printLine(char *s) {
#ifdef TCL2C_INT
for (unsigned int i = 0; i < strlen(s); i++)
if ((i > 0) && ((i % 20) == 0))
printf("%u,\n", s[i]);
else
printf("%u,", s[i]);
printf("%u,%u,\n", '\\', '\n');
#else
printf("%s\\n\\\n", s);
#endif
}
char *
lcase(const char *s) {
static char charbuf[512];
char* to = charbuf;
while ((*to++ = tolower(*s++)))
/* NOTHING */;
*to = '\0';
return charbuf;
}
int main() {
p_info pinfo;
#ifdef TCL2C_INT
printf("static const char code[] = {\n");
#else
printLine("static const char code[] = \"");
#endif
printLine("global ptype pvals");
printLine("set ptype(error) -1");
printLine("set pvals(-1) error");
char strbuf[512];
for (int i = 0; i < PT_NTYPE; i++) {
sprintf(strbuf, "set ptype(%s) %d", lcase(pinfo.name(packet_t(i))), i);
printLine(strbuf);
sprintf(strbuf, "set pvals(%d) %s", i, pinfo.name(packet_t(i)));
printLine(strbuf);
}
printLine("proc ptype2val {str} {");
printLine("global ptype");
printLine("set str [string tolower $str]");
printLine("if ![info exists ptype($str)] {");
printLine("set str error");
printLine("}");
printLine("set ptype($str)");
printLine("}");
printLine("proc pval2type {val} {");
printLine("global pvals");
printLine("if ![info exists pvals($val)] {");
printLine("set val -1");
printLine("}");
printLine("set pvals($val)");
printLine("}");
#ifdef TCL2C_INT
printf("0 };\n");
#else
printf("\";\n");
#endif
printf("#include \"config.h\"\n");
printf("EmbeddedTcl et_ns_ptypes(code);\n");
return 0;
}
queue-monitor.cc
/* -*- 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 MASH Research
* Group at the University of California Berkeley.
* 4. Neither the name of the University nor of the Research Group 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.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$";
#endif
#include "queue-monitor.h"
int QueueMonitor::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
if (argc == 2) {
if (strcmp(argv[1], "get-bytes-integrator") == 0) {
if (bytesInt_)
tcl.resultf("%s", bytesInt_->name());
else
tcl.resultf("");
return (TCL_OK);
}
if (strcmp(argv[1], "get-pkts-integrator") == 0) {
if (pktsInt_)
tcl.resultf("%s", pktsInt_->name());
else
tcl.resultf("");
return (TCL_OK);
}
if (strcmp(argv[1], "get-delay-samples") == 0) {
if (delaySamp_)
tcl.resultf("%s", delaySamp_->name());
else
tcl.resultf("");
return (TCL_OK);
}
}
if (argc == 3) {
if (strcmp(argv[1], "set-bytes-integrator") == 0) {
bytesInt_ = (Integrator *)
TclObject::lookup(argv[2]);
if (bytesInt_ == NULL)
return (TCL_ERROR);
return (TCL_OK);
}
if (strcmp(argv[1], "set-pkts-integrator") == 0) {
pktsInt_ = (Integrator *)
TclObject::lookup(argv[2]);
if (pktsInt_ == NULL)
return (TCL_ERROR);
return (TCL_OK);
}
if (strcmp(argv[1], "set-delay-samples") == 0) {
delaySamp_ = (Samples*)
TclObject::lookup(argv[2]);
if (delaySamp_ == NULL)
return (TCL_ERROR);
return (TCL_OK);
}
if (strcmp(argv[1], "trace") == 0) {
int mode;
const char* id = argv[2];
channel_ = Tcl_GetChannel(tcl.interp(), (char*)id, &mode);
if (channel_ == 0) {
tcl.resultf("trace: can't attach %s for writing", id);
return (TCL_ERROR);
}
return (TCL_OK);
}
}
if (argc == 4) {
if (strcmp(argv[1], "set-src-dst") == 0) {
srcId_ = atoi(argv[2]);
dstId_ = atoi(argv[3]);
return (TCL_OK);
}
}
return TclObject::command(argc, argv); // else control reaches end of
// non-void function, see? :-)
}
static class QueueMonitorClass : public TclClass {
public:
QueueMonitorClass() : TclClass("QueueMonitor") {}
TclObject* create(int, const char*const*) {
return (new QueueMonitor());
}
} queue_monitor_class;
void
QueueMonitor::printStats() {
char wrk[500];
int n;
double now = Scheduler::instance().clock();
sprintf(wrk, "%-6.3f %d %d %d %d", now, srcId_, dstId_, size_, pkts_);
n = strlen(wrk);
wrk[n] = '\n';
wrk[n+1] = 0;
(void)Tcl_Write(channel_, wrk, n+1);
wrk[n] = 0;
}
// packet arrival to a queue
void QueueMonitor::in(Packet* p)
{
hdr_cmn* hdr = (hdr_cmn*)p->access(off_cmn_);
double now = Scheduler::instance().clock();
int pktsz = hdr->size();
barrivals_ += pktsz;
parrivals_++;
size_ += pktsz;
pkts_++;
if (bytesInt_)
bytesInt_->newPoint(now, double(size_));
if (pktsInt_)
pktsInt_->newPoint(now, double(pkts_));
if (delaySamp_)
hdr->timestamp() = now;
if (channel_)
printStats();
}
void QueueMonitor::out(Packet* p)
{
hdr_cmn* hdr = (hdr_cmn*)p->access(off_cmn_);
double now = Scheduler::instance().clock();
int pktsz = hdr->size();
size_ -= pktsz;
pkts_--;
bdepartures_ += pktsz;
pdepartures_++;
if (bytesInt_)
bytesInt_->newPoint(now, double(size_));
if (pktsInt_)
pktsInt_->newPoint(now, double(pkts_));
if (delaySamp_)
delaySamp_->newPoint(now - hdr->timestamp());
if (channel_)
printStats();
}
void QueueMonitor::drop(Packet* p)
{
hdr_cmn* hdr = (hdr_cmn*)p->access(off_cmn_);
double now = Scheduler::instance().clock();
int pktsz = hdr->size();
size_ -= pktsz;
pkts_--;
bdrops_ += pktsz;
pdrops_++;
if (bytesInt_)
bytesInt_->newPoint(now, double(size_));
if (pktsInt_)
pktsInt_->newPoint(now, double(pkts_));
if (channel_)
printStats();
}
static class SnoopQueueInClass : public TclClass {
public:
SnoopQueueInClass() : TclClass("SnoopQueue/In") {}
TclObject* create(int, const char*const*) {
return (new SnoopQueueIn());
}
} snoopq_in_class;
static class SnoopQueueOutClass : public TclClass {
public:
SnoopQueueOutClass() : TclClass("SnoopQueue/Out") {}
TclObject* create(int, const char*const*) {
return (new SnoopQueueOut());
}
} snoopq_out_class;
static class SnoopQueueDropClass : public TclClass {
public:
SnoopQueueDropClass() : TclClass("SnoopQueue/Drop") {}
TclObject* create(int, const char*const*) {
return (new SnoopQueueDrop());
}
} snoopq_drop_class;
static class SnoopQueueEDropClass : public TclClass {
public:
SnoopQueueEDropClass() : TclClass("SnoopQueue/EDrop") {}
TclObject* create(int, const char*const*) {
return (new SnoopQueueEDrop);
}
} snoopq_edrop_class;
static class QueueMonitorEDClass : public TclClass {
public:
QueueMonitorEDClass() : TclClass("QueueMonitor/ED") {}
TclObject* create(int, const char*const*) {
return (new EDQueueMonitor);
}
} queue_monitor_ed_class;
/*
* a 'QueueMonitorCompat', which is used by the compat
* code to produce the link statistics used available in ns-1
*
* in ns-1, the counters are the number of departures
*/
#include "ip.h"
QueueMonitorCompat::QueueMonitorCompat()
{
bind("off_ip_", &off_ip_);
memset(pkts_, 0, sizeof(pkts_));
memset(bytes_, 0, sizeof(bytes_));
memset(drops_, 0, sizeof(drops_));
memset(flowstats_, 0, sizeof(flowstats_));
}
/*
* create an entry in the flowstats_ array.
*/
void
QueueMonitorCompat::flowstats(int flowid)
{
Tcl& tcl = Tcl::instance();
/*
* here is the deal. we are in C code. we'd like to do
* flowstats_[flowid] = new Samples;
* but, we want to create an object that can be
* referenced via tcl. (in particular, we want ->name_
* to be valid.)
*
* so, how do we do this?
*
* well, the answer is, call tcl to create it. then,
* do a lookup on the result from tcl!
*/
tcl.evalf("new Samples");
flowstats_[flowid] = (Samples*)TclObject::lookup(tcl.result());
if (flowstats_[flowid] == 0) {
abort();
/*NOTREACHED*/
}
}
void QueueMonitorCompat::out(Packet* pkt)
{
hdr_cmn* hdr = (hdr_cmn*)pkt->access(off_cmn_);
hdr_ip* iph = (hdr_ip*)pkt->access(off_ip_);
double now = Scheduler::instance().clock();
int fid = iph->flowid();
if (fid >= MAXFLOW) {
abort();
/*NOTREACHED*/
}
// printf("QueueMonitorCompat::out(), fid=%d\n", fid);
bytes_[fid] += ((hdr_cmn*)pkt->access(off_cmn_))->size();
pkts_[fid]++;
if (flowstats_[fid] == 0) {
flowstats(fid);
}
flowstats_[fid]->newPoint(now - hdr->timestamp());
QueueMonitor::out(pkt);
}
void QueueMonitorCompat::in(Packet* pkt)
{
hdr_cmn* hdr = (hdr_cmn*)pkt->access(off_cmn_);
double now = Scheduler::instance().clock();
// QueueMonitor::in() *may* do this, but we always need it...
hdr->timestamp() = now;
QueueMonitor::in(pkt);
}
void QueueMonitorCompat::drop(Packet* pkt)
{
hdr_ip* iph = (hdr_ip*)pkt->access(off_ip_);
int fid = iph->flowid();
if (fid >= MAXFLOW) {
abort();
/*NOTREACHED*/
}
++drops_[fid];
QueueMonitor::drop(pkt);
}
int QueueMonitorCompat::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
int fid;
if (argc == 3) {
fid = atoi(argv[2]);
if (strcmp(argv[1], "bytes") == 0) {
if (fid >= MAXFLOW) {
abort();
/*NOTREACHED*/
}
tcl.resultf("%d", bytes_[fid]);
return TCL_OK;
} else if (strcmp(argv[1], "pkts") == 0) {
if (fid >= MAXFLOW) {
abort();
/*NOTREACHED*/
}
tcl.resultf("%d", pkts_[fid]);
return TCL_OK;
} else if (strcmp(argv[1], "drops") == 0) {
if (fid >= MAXFLOW) {
abort();
/*NOTREACHED*/
}
tcl.resultf("%d", drops_[fid]);
return TCL_OK;
} else if (strcmp(argv[1], "get-class-delay-samples") == 0) {
if (fid >= MAXFLOW) {
abort();
/*NOTREACHED*/
}
if (flowstats_[fid] == 0) {
/*
* instantiate one if user actually
* cares enough to ask for it!
*
* (otherwise, need to return "",
* and then special-case caller to
* handle this null return.)
*/
flowstats(fid);
}
tcl.resultf("%s", flowstats_[fid]->name());
return TCL_OK;
}
}
return (QueueMonitor::command(argc, argv));
}
static class QueueMonitorCompatClass : public TclClass {
public:
QueueMonitorCompatClass() : TclClass("QueueMonitor/Compat") {}
TclObject* create(int, const char*const*) {
return (new QueueMonitorCompat);
}
} queue_monitor_compat_class;
queue.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1996-1997 The 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 Network Research
* Group at Lawrence Berkeley National Laboratory.
* 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.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (LBL)";
#endif
#include "queue.h"
#include
void PacketQueue::remove(Packet* target)
{
for (Packet *pp= 0, *p= head_; p; pp= p, p= p->next_) {
if (p == target) {
if (!pp) deque();
else {
if (p == tail_)
tail_= pp;
pp->next_= p->next_;
--len_;
}
return;
}
}
fprintf(stderr, "PacketQueue:: remove() couldn't find target\n");
abort();
}
/*
* Remove packet pkt located after packet prev on the queue. Either p or prev
* could be NULL. If prev is NULL then pkt must be the head of the queue.
*/
void PacketQueue::remove(Packet* pkt, Packet *prev) //XXX: screwy
{
if (pkt) {
if (head_ == pkt)
PacketQueue::deque(); /* decrements len_ internally */
else {
prev->next_ = pkt->next_;
if (tail_ == pkt)
tail_ = prev;
--len_;
}
}
return;
}
void QueueHandler::handle(Event*)
{
queue_.resume();
}
Queue::Queue() : Connector(), blocked_(0), unblock_on_resume_(1), qh_(*this),
pq_(0) /* temporarily NULL */
{
bind("limit_", &qlim_);
bind_bool("blocked_", &blocked_);
bind_bool("unblock_on_resume_", &unblock_on_resume_);
}
void Queue::recv(Packet* p, Handler*)
{
enque(p);
if (!blocked_) {
/*
* We're not blocked. Get a packet and send it on.
* We perform an extra check because the queue
* might drop the packet even if it was
* previously empty! (e.g., RED can do this.)
*/
p = deque();
if (p != 0) {
blocked_ = 1;
target_->recv(p, &qh_);
}
}
}
void Queue::resume()
{
Packet* p = deque();
if (p != 0) {
target_->recv(p, &qh_);
} else {
if (unblock_on_resume_)
blocked_ = 0;
else
blocked_ = 1;
}
}
void Queue::reset()
{
Packet* p;
while ((p = deque()) != 0)
drop(p);
}
random.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1995 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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*
* "@(#) $Header$ (LBL)";
*/
#ifndef WIN32
#include
#include "config.h"
#include "random.h"
RANDOM_RETURN_TYPE
random()
{
printf("random() called in ns.\nRandom is not portable, please use Random::uniform() instead.\n");
abort();
}
#endif /* !WIN32 */
ranvar.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) Xerox Corporation 1997. All rights reserved.
*
* License is granted to copy, to use, and to make and to use derivative
* works for research and evaluation purposes, provided that Xerox is
* acknowledged in all documentation pertaining to any such copy or derivative
* work. Xerox grants no other licenses expressed or implied. The Xerox trade
* name should not be used in any advertising without its written permission.
*
* XEROX CORPORATION MAKES NO REPRESENTATIONS CONCERNING EITHER THE
* MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE
* FOR ANY PARTICULAR PURPOSE. The software is provided "as is" without
* express or implied warranty of any kind.
*
* These notices must be retained in any copies of any part of this software.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (Xerox)";
#endif
#include
#include "ranvar.h"
RandomVariable::RandomVariable()
{
rng_ = RNG::defaultrng();
}
int RandomVariable::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
if (argc == 2) {
if (strcmp(argv[1], "value") == 0) {
tcl.resultf("%6e", value());
return(TCL_OK);
}
}
if (argc == 3) {
if (strcmp(argv[1], "use-rng") == 0) {
rng_ = (RNG*)TclObject::lookup(argv[2]);
if (rng_ == 0) {
tcl.resultf("no such RNG %s", argv[2]);
return(TCL_ERROR);
}
return(TCL_OK);
}
}
return(TclObject::command(argc, argv));
}
static class UniformRandomVariableClass : public TclClass {
public:
UniformRandomVariableClass() : TclClass("RandomVariable/Uniform"){}
TclObject* create(int, const char*const*) {
return(new UniformRandomVariable());
}
} class_uniformranvar;
UniformRandomVariable::UniformRandomVariable()
{
bind("min_", &min_);
bind("max_", &max_);
}
UniformRandomVariable::UniformRandomVariable(double min, double max)
{
min_ = min;
max_ = max;
}
double UniformRandomVariable::value()
{
return(rng_->uniform(min_, max_));
}
static class ExponentialRandomVariableClass : public TclClass {
public:
ExponentialRandomVariableClass() : TclClass("RandomVariable/Exponential") {}
TclObject* create(int, const char*const*) {
return(new ExponentialRandomVariable());
}
} class_exponentialranvar;
ExponentialRandomVariable::ExponentialRandomVariable()
{
bind("avg_", &avg_);
}
ExponentialRandomVariable::ExponentialRandomVariable(double avg)
{
avg_ = avg;
}
double ExponentialRandomVariable::value()
{
return(rng_->exponential(avg_));
}
static class ParetoRandomVariableClass : public TclClass {
public:
ParetoRandomVariableClass() : TclClass("RandomVariable/Pareto") {}
TclObject* create(int, const char*const*) {
return(new ParetoRandomVariable());
}
} class_paretoranvar;
ParetoRandomVariable::ParetoRandomVariable()
{
bind("avg_", &avg_);
bind("shape_", &shape_);
}
ParetoRandomVariable::ParetoRandomVariable(double avg, double shape)
{
avg_ = avg;
shape_ = shape;
}
double ParetoRandomVariable::value()
{
/* yuck, user wants to specify shape and avg, but the
* computation here is simpler if we know the 'scale'
* parameter. right thing is to probably do away with
* the use of 'bind' and provide an API such that we
* can update the scale everytime the user updates shape
* or avg.
*/
return(rng_->pareto(avg_ * (shape_ -1)/shape_, shape_));
}
/* Pareto distribution of the second kind, aka. Lomax distribution */
static class ParetoIIRandomVariableClass : public TclClass {
public:
ParetoIIRandomVariableClass() : TclClass("RandomVariable/ParetoII") {}
TclObject* create(int, const char*const*) {
return(new ParetoIIRandomVariable());
}
} class_paretoIIranvar;
ParetoIIRandomVariable::ParetoIIRandomVariable()
{
bind("avg_", &avg_);
bind("shape_", &shape_);
}
ParetoIIRandomVariable::ParetoIIRandomVariable(double avg, double shape)
{
avg_ = avg;
shape_ = shape;
}
double ParetoIIRandomVariable::value()
{
return(rng_->paretoII(avg_ * (shape_ - 1), shape_));
}
static class NormalRandomVariableClass : public TclClass {
public:
NormalRandomVariableClass() : TclClass("RandomVariable/Normal") {}
TclObject* create(int, const char*const*) {
return(new NormalRandomVariable());
}
} class_normalranvar;
NormalRandomVariable::NormalRandomVariable()
{
bind("avg_", &avg_);
bind("std_", &std_);
}
double NormalRandomVariable::value()
{
return(rng_->normal(avg_, std_));
}
static class LogNormalRandomVariableClass : public TclClass {
public:
LogNormalRandomVariableClass() : TclClass("RandomVariable/LogNormal") {}
TclObject* create(int, const char*const*) {
return(new LogNormalRandomVariable());
}
} class_lognormalranvar;
LogNormalRandomVariable::LogNormalRandomVariable()
{
bind("avg_", &avg_);
bind("std_", &std_);
}
double LogNormalRandomVariable::value()
{
return(rng_->lognormal(avg_, std_));
}
static class ConstantRandomVariableClass : public TclClass {
public:
ConstantRandomVariableClass() : TclClass("RandomVariable/Constant"){}
TclObject* create(int, const char*const*) {
return(new ConstantRandomVariable());
}
} class_constantranvar;
ConstantRandomVariable::ConstantRandomVariable()
{
bind("val_", &val_);
}
ConstantRandomVariable::ConstantRandomVariable(double val)
{
val_ = val;
}
double ConstantRandomVariable::value()
{
return(val_);
}
/* Hyperexponential distribution code adapted from code provided
* by Ion Stoica.
*/
static class HyperExponentialRandomVariableClass : public TclClass {
public:
HyperExponentialRandomVariableClass() :
TclClass("RandomVariable/HyperExponential") {}
TclObject* create(int, const char*const*) {
return(new HyperExponentialRandomVariable());
}
} class_hyperexponentialranvar;
HyperExponentialRandomVariable::HyperExponentialRandomVariable()
{
bind("avg_", &avg_);
bind("cov_", &cov_);
alpha_ = .95;
}
HyperExponentialRandomVariable::HyperExponentialRandomVariable(double avg, double cov)
{
alpha_ = .95;
avg_ = avg;
cov_ = cov;
}
double HyperExponentialRandomVariable::value()
{
double temp, res;
double u = Random::uniform();
temp = sqrt((cov_ * cov_ - 1.0)/(2.0 * alpha_ * (1.0 - alpha_)));
if (u < alpha_)
res = Random::exponential(avg_ - temp * (1.0 - alpha_) * avg_);
else
res = Random::exponential(avg_ + temp * (alpha_) * avg_);
return(res);
}
/*
// Empirical Random Variable:
// CDF input from file with the following column
// 1. Possible values in a distrubutions
// 2. Number of occurances for those values
// 3. The CDF for those value
// code provided by Giao Nguyen
*/
static class EmpiricalRandomVariableClass : public TclClass {
public:
EmpiricalRandomVariableClass() : TclClass("RandomVariable/Empirical"){}
TclObject* create(int, const char*const*) {
return(new EmpiricalRandomVariable());
}
} class_empiricalranvar;
EmpiricalRandomVariable::EmpiricalRandomVariable() : minCDF_(0), maxCDF_(1), maxEntry_(32), table_(0)
{
bind("minCDF_", &minCDF_);
bind("maxCDF_", &maxCDF_);
bind("interpolation_", &interpolation_);
bind("maxEntry_", &maxEntry_);
}
int EmpiricalRandomVariable::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
if (argc == 3) {
if (strcmp(argv[1], "loadCDF") == 0) {
if (loadCDF(argv[2]) == 0) {
tcl.resultf("%s loadCDF %s: invalid file",
name(), argv[2]);
return (TCL_ERROR);
}
return (TCL_OK);
}
}
return RandomVariable::command(argc, argv);
}
int EmpiricalRandomVariable::loadCDF(const char* filename)
{
FILE* fp;
char line[256];
CDFentry* e;
fp = fopen(filename, "r");
if (fp == 0)
return 0;
if (table_ == 0)
table_ = new CDFentry[maxEntry_];
for (numEntry_=0; fgets(line, 256, fp); numEntry_++) {
if (numEntry_ >= maxEntry_) { // resize the CDF table
maxEntry_ *= 2;
e = new CDFentry[maxEntry_];
for (int i=numEntry_-1; i >= 0; i--)
e[i] = table_[i];
delete table_;
table_ = e;
}
e = &table_[numEntry_];
// Use * and l together raises a warning
sscanf(line, "%lf %*f %lf", &e->val_, &e->cdf_);
}
return numEntry_;
}
double EmpiricalRandomVariable::value()
{
if (numEntry_ <= 0)
return 0;
double u = rng_->uniform(minCDF_, maxCDF_);
int mid = lookup(u);
if (mid && interpolation_ && u < table_[mid].cdf_)
return interpolate(u, table_[mid-1].cdf_, table_[mid-1].val_,
table_[mid].cdf_, table_[mid].val_);
return table_[mid].val_;
}
double EmpiricalRandomVariable::interpolate(double x, double x1, double y1, double x2, double y2)
{
double value = y1 + (x - x1) * (y2 - y1) / (x2 - x1);
if (interpolation_ == INTER_INTEGRAL) // round up
return ceil(value);
return value;
}
int EmpiricalRandomVariable::lookup(double u)
{
// always return an index whose value is >= u
int lo, hi, mid;
if (u <= table_[0].cdf_)
return 0;
for (lo=1, hi=numEntry_-1; lo < hi; ) {
mid = (lo + hi) / 2;
if (u > table_[mid].cdf_)
lo = mid + 1;
else hi = mid;
}
return lo;
}
red.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1990-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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*
*
* Here is one set of parameters from one of Sally's simulations
* (this is from tcpsim, the older simulator):
*
* ed [ q_weight=0.002 thresh=5 linterm=30 maxthresh=15
* mean_pktsize=500 dropmech=random-drop queue-size=60
* plot-file=none bytes=false doubleq=false dqthresh=50
* wait=true ]
*
* 1/"linterm" is the max probability of dropping a packet.
* There are different options that make the code
* more messy that it would otherwise be. For example,
* "doubleq" and "dqthresh" are for a queue that gives priority to
* small (control) packets,
* "bytes" indicates whether the queue should be measured in bytes
* or in packets,
* "dropmech" indicates whether the drop function should be random-drop
* or drop-tail when/if the queue overflows, and
* the commented-out Holt-Winters method for computing the average queue
* size can be ignored.
* "wait" indicates whether the gateway should wait between dropping
* packets.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (LBL)";
#endif
#include
#include
#include "config.h"
#include "template.h"
#include "random.h"
#include "flags.h"
#include "delay.h"
#include "red.h"
static class REDClass : public TclClass {
public:
REDClass() : TclClass("Queue/RED") {}
TclObject* create(int, const char*const*) {
return (new REDQueue);
}
} class_red;
REDQueue::REDQueue() : link_(NULL), bcount_(0), de_drop_(NULL),
tchan_(0), idle_(1)
{
bind_bool("bytes_", &edp_.bytes); // boolean: use bytes?
bind_bool("queue_in_bytes_", &qib_); // boolean: q in bytes?
// _RENAMED("queue-in-bytes_", "queue_in_bytes_");
bind("thresh_", &edp_.th_min); // minthresh
bind("maxthresh_", &edp_.th_max); // maxthresh
bind("mean_pktsize_", &edp_.mean_pktsize); // avg pkt size
bind("q_weight_", &edp_.q_w); // for EWMA
bind_bool("wait_", &edp_.wait);
bind("linterm_", &edp_.max_p_inv);
bind_bool("setbit_", &edp_.setbit); // mark instead of drop
bind_bool("gentle_", &edp_.gentle); // increase the packet
// drop prob. slowly
// when ave queue
// exceeds maxthresh
bind_bool("drop_tail_", &drop_tail_); // drop last pkt
// _RENAMED("drop-tail_", "drop_tail_");
bind_bool("drop_front_", &drop_front_); // drop first pkt
// _RENAMED("drop-front_", "drop_front_");
bind_bool("drop_rand_", &drop_rand_); // drop pkt at random
// _RENAMED("drop-rand_", "drop_rand_");
bind_bool("ns1_compat_", &ns1_compat_); // ns-1 compatibility
// _RENAMED("ns1-compat_", "ns1_compat_");
bind("ave_", &edv_.v_ave); // average queue sie
bind("prob1_", &edv_.v_prob1); // dropping probability
bind("curq_", &curq_); // current queue size
q_ = new PacketQueue(); // underlying queue
pq_ = q_;
reset();
#ifdef notdef
print_edp();
print_edv();
#endif
}
void REDQueue::reset()
{
/*
* If queue is measured in bytes, scale min/max thresh
* by the size of an average packet (which is specified by user).
*/
if (qib_) {
edp_.th_min *= edp_.mean_pktsize;
edp_.th_max *= edp_.mean_pktsize;
}
/*
* Compute the "packet time constant" if we know the
* link bandwidth. The ptc is the max number of (avg sized)
* pkts per second which can be placed on the link.
* The link bw is given in bits/sec, so scale mean psize
* accordingly.
*/
if (link_)
edp_.ptc = link_->bandwidth() /
(8. * edp_.mean_pktsize);
edv_.v_ave = 0.0;
edv_.v_slope = 0.0;
edv_.count = 0;
edv_.count_bytes = 0;
edv_.old = 0;
edv_.v_a = 1 / (edp_.th_max - edp_.th_min);
edv_.v_b = - edp_.th_min / (edp_.th_max - edp_.th_min);
if (edp_.gentle) {
edv_.v_c = ( 1.0 - 1 / edp_.max_p_inv ) / edp_.th_max;
edv_.v_d = 2 / edp_.max_p_inv - 1.0;
}
idle_ = 1;
if (&Scheduler::instance() != NULL)
idletime_ = Scheduler::instance().clock();
else
idletime_ = 0.0; /* sched not instantiated yet */
Queue::reset();
bcount_ = 0;
}
/*
* Compute the average queue size.
* The code contains two alternate methods for this, the plain EWMA
* and the Holt-Winters method.
* nqueued can be bytes or packets
*/
void REDQueue::run_estimator(int nqueued, int m)
{
float f, f_sl, f_old;
f = (float)edv_.v_ave;
f_sl = (float)edv_.v_slope;
#define RED_EWMA
#ifdef RED_EWMA
while (--m >= 1) {
f_old = f;
f *= 1.0 - (float)edp_.q_w;
}
f_old = f;
f *= 1.0 - (float)edp_.q_w;
f += (float)edp_.q_w * nqueued;
#endif
#ifdef RED_HOLT_WINTERS
while (--m >= 1) {
f_old = f;
f += f_sl;
f *= 1.0 - edp_.q_w;
f_sl *= 1.0 - 0.5 * edp_.q_w;
f_sl += 0.5 * edp_.q_w * (f - f_old);
}
f_old = f;
f += f_sl;
f *= 1.0 - edp_.q_w;
f += edp_.q_w * nqueued;
f_sl *= 1.0 - 0.5 * edp_.q_w;
f_sl += 0.5 * edp_.q_w * (f - f_old);
#endif
edv_.v_ave = f;
edv_.v_slope = f_sl;
}
/*
* Return the next packet in the queue for transmission.
*/
Packet* REDQueue::deque()
{
Packet *p;
p = q_->deque();
if (p != 0) {
idle_ = 0;
bcount_ -= ((hdr_cmn*)p->access(off_cmn_))->size();
} else {
idle_ = 1;
// deque() may invoked by Queue::reset at init
// time (before the scheduler is instantiated).
// deal with this case
if (&Scheduler::instance() != NULL)
idletime_ = Scheduler::instance().clock();
else
idletime_ = 0.0;
}
return (p);
}
/*
* should the packet be dropped/marked due to a probabilistic drop?
*/
int
REDQueue::drop_early(Packet* pkt)
{
double p;
int no_ecn = 0;
hdr_cmn* ch = (hdr_cmn*)pkt->access(off_cmn_);
if (edp_.gentle && edv_.v_ave >= edp_.th_max) {
// p ranges from max_p to 1 as the average queue
// size ranges from th_max to twice th_max
p = edv_.v_c * edv_.v_ave + edv_.v_d;
no_ecn = 1;
} else {
// p ranges from 0 to max_p as the average queue
// size ranges from th_min to th_max
p = edv_.v_a * edv_.v_ave + edv_.v_b;
p /= edp_.max_p_inv;
}
edv_.v_prob1 = p;
if (edv_.v_prob1 > 1.0)
edv_.v_prob1 = 1.0;
double count1 = edv_.count;
if (edp_.bytes)
count1 = (double) (edv_.count_bytes/edp_.mean_pktsize);
if (edp_.wait) {
if (count1 * p < 1.0)
p = 0.0;
else if (count1 * p < 2.0)
p /= (2 - count1 * p);
else
p = 1.0;
} else {
if (count1 * p < 1.0)
p /= (1.0 - count1 * p);
else
p = 1.0;
}
if (edp_.bytes && p < 1.0) {
p = p * ch->size() / edp_.mean_pktsize;
}
if (p > 1.0)
p = 1.0;
edv_.v_prob = p;
// drop probability is computed, pick random number and act
double u = Random::uniform();
if (u <= edv_.v_prob) {
// DROP or MARK
edv_.count = 0;
edv_.count_bytes = 0;
hdr_flags* hf = (hdr_flags*)pickPacketForECN(pkt)->access(off_flags_);
if (edp_.setbit && hf->ect() && no_ecn==0) {
hf->ce() = 1; // mark Congestion Experienced bit
return (0); // no drop
} else {
return (1); // drop
}
}
return (0); // no DROP/mark
}
/*
* Pick packet for early congestion notification (ECN). This packet is then
* marked or dropped. Having a separate function do this is convenient for
* supporting derived classes that use the standard RED algorithm to compute
* average queue size but use a different algorithm for choosing the packet for
* ECN notification.
*/
Packet*
REDQueue::pickPacketForECN(Packet* pkt)
{
return pkt; /* pick the packet that just arrived */
}
/*
* Pick packet to drop. Having a separate function do this is convenient for
* supporting derived classes that use the standard RED algorithm to compute
* average queue size but use a different algorithm for choosing the victim.
*/
Packet*
REDQueue::pickPacketToDrop()
{
int victim;
if (drop_front_)
victim = min(1, q_->length()-1);
else if (drop_rand_)
victim = Random::integer(q_->length());
else /* default is drop_tail_ */
victim = q_->length() - 1;
return(q_->lookup(victim));
}
/*
* Receive a new packet arriving at the queue.
* The average queue size is computed. If the average size
* exceeds the threshold, then the dropping probability is computed,
* and the newly-arriving packet is dropped with that probability.
* The packet is also dropped if the maximum queue size is exceeded.
*
* "Forced" drops mean a packet arrived when the underlying queue was
* full or when the average q size exceeded maxthresh.
* "Unforced" means a RED random drop.
*
* For forced drops, either the arriving packet is dropped or one in the
* queue is dropped, depending on the setting of drop_tail_.
* For unforced drops, the arriving packet is always the victim.
*/
#define DTYPE_NONE 0 /* ok, no drop */
#define DTYPE_FORCED 1 /* a "forced" drop */
#define DTYPE_UNFORCED 2 /* an "unforced" (random) drop */
void REDQueue::enque(Packet* pkt)
{
/*
* if we were idle, we pretend that m packets arrived during
* the idle period. m is set to be the ptc times the amount
* of time we've been idle for
*/
int m = 0;
if (idle_) {
// A packet that arrives to an idle queue will never
// be dropped.
double now = Scheduler::instance().clock();
/* To account for the period when the queue was empty. */
idle_ = 0;
m = int(edp_.ptc * (now - idletime_));
}
/*
* Run the estimator with either 1 new packet arrival, or with
* the scaled version above [scaled by m due to idle time]
* (bcount_ maintains the byte count in the underlying queue).
* If the underlying queue is able to delete packets without
* us knowing, then bcount_ will not be maintained properly!
*/
run_estimator(qib_ ? bcount_ : q_->length(), m + 1);
/*
* count and count_bytes keeps a tally of arriving traffic
* that has not been dropped (i.e. how long, in terms of traffic,
* it has been since the last early drop)
*/
hdr_cmn* ch = (hdr_cmn*)pkt->access(off_cmn_);
++edv_.count;
edv_.count_bytes += ch->size();
/*
* DROP LOGIC:
* q = current q size, ~q = averaged q size
* 1> if ~q > maxthresh, this is a FORCED drop
* 2> if minthresh < ~q < maxthresh, this may be an UNFORCED drop
* 3> if (q+1) > hard q limit, this is a FORCED drop
*/
register double qavg = edv_.v_ave;
int droptype = DTYPE_NONE;
int qlen = qib_ ? bcount_ : q_->length();
int qlim = qib_ ? (qlim_ * edp_.mean_pktsize) : qlim_;
curq_ = qlen; // helps to trace queue during arrival, if enabled
if (qavg >= edp_.th_min && qlen > 1) {
if ((!edp_.gentle && qavg >= edp_.th_max) ||
(edp_.gentle && qavg >= 2 * edp_.th_max)) {
droptype = DTYPE_FORCED;
} else if (edv_.old == 0) {
/*
* The average queue size has just crossed the
* threshold from below to above "minthresh", or
* from above "minthresh" with an empty queue to
* above "minthresh" with a nonempty queue.
*/
edv_.count = 1;
edv_.count_bytes = ch->size();
edv_.old = 1;
} else if (drop_early(pkt)) {
droptype = DTYPE_UNFORCED;
}
} else {
/* No packets are being dropped. */
edv_.v_prob = 0.0;
edv_.old = 0;
}
if (qlen >= qlim) {
// see if we've exceeded the queue size
droptype = DTYPE_FORCED;
}
if (droptype == DTYPE_UNFORCED) {
/* pick packet for ECN, which is dropping in this case */
Packet *pkt_to_drop = pickPacketForECN(pkt);
/*
* If the packet picked is different that the one that just arrived,
* add it to the queue and remove the chosen packet.
*/
if (pkt_to_drop != pkt) {
q_->enque(pkt);
bcount_ += ch->size();
q_->remove(pkt_to_drop);
bcount_ -= ((hdr_cmn*)pkt_to_drop->access(off_cmn_))->size();
pkt = pkt_to_drop; /* XXX okay because pkt is not needed anymore */
}
// deliver to special "edrop" target, if defined
if (de_drop_ != NULL)
de_drop_->recv(pkt);
else
drop(pkt);
} else {
/* forced drop, or not a drop: first enqueue pkt */
q_->enque(pkt);
bcount_ += ch->size();
/* drop a packet if we were told to */
if (droptype == DTYPE_FORCED) {
/* drop random victim or last one */
pkt = pickPacketToDrop();
q_->remove(pkt);
bcount_ -= ((hdr_cmn*)pkt->access(off_cmn_))->size();
drop(pkt);
if (!ns1_compat_) {
// bug-fix from Philip Liu,
edv_.count = 0;
edv_.count_bytes = 0;
}
}
}
return;
}
int REDQueue::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
if (argc == 2) {
if (strcmp(argv[1], "reset") == 0) {
reset();
return (TCL_OK);
}
if (strcmp(argv[1], "early-drop-target") == 0) {
if (de_drop_ != NULL)
tcl.resultf("%s", de_drop_->name());
return (TCL_OK);
}
} else if (argc == 3) {
// attach a file for variable tracing
if (strcmp(argv[1], "attach") == 0) {
int mode;
const char* id = argv[2];
tchan_ = Tcl_GetChannel(tcl.interp(), (char*)id, &mode);
if (tchan_ == 0) {
tcl.resultf("RED: trace: can't attach %s for writing", id);
return (TCL_ERROR);
}
return (TCL_OK);
}
// tell RED about link stats
if (strcmp(argv[1], "link") == 0) {
LinkDelay* del = (LinkDelay*)TclObject::lookup(argv[2]);
if (del == 0) {
tcl.resultf("RED: no LinkDelay object %s",
argv[2]);
return(TCL_ERROR);
}
// set ptc now
link_ = del;
edp_.ptc = link_->bandwidth() /
(8. * edp_.mean_pktsize);
return (TCL_OK);
}
if (strcmp(argv[1], "early-drop-target") == 0) {
NsObject* p = (NsObject*)TclObject::lookup(argv[2]);
if (p == 0) {
tcl.resultf("no object %s", argv[2]);
return (TCL_ERROR);
}
de_drop_ = p;
return (TCL_OK);
}
if (!strcmp(argv[1], "packetqueue-attach")) {
delete q_;
if (!(q_ = (PacketQueue*) TclObject::lookup(argv[2])))
return (TCL_ERROR);
else {
pq_ = q_;
return (TCL_OK);
}
}
}
return (Queue::command(argc, argv));
}
/*
* Routine called by TracedVar facility when variables change values.
* Currently used to trace values of avg queue size, drop probability,
* and the instantaneous queue size seen by arriving packets.
* Note that the tracing of each var must be enabled in tcl to work.
*/
void
REDQueue::trace(TracedVar* v)
{
char wrk[500], *p;
if (((p = strstr(v->name(), "ave")) == NULL) &&
((p = strstr(v->name(), "prob")) == NULL) &&
((p = strstr(v->name(), "curq")) == NULL)) {
fprintf(stderr, "RED:unknown trace var %s\n",
v->name());
return;
}
if (tchan_) {
int n;
double t = Scheduler::instance().clock();
// XXX: be compatible with nsv1 RED trace entries
if (*p == 'c') {
sprintf(wrk, "Q %g %d", t, int(*((TracedInt*) v)));
} else {
sprintf(wrk, "%c %g %g", *p, t,
double(*((TracedDouble*) v)));
}
n = strlen(wrk);
wrk[n] = '\n';
wrk[n+1] = 0;
(void)Tcl_Write(tchan_, wrk, n+1);
}
return;
}
/* for debugging help */
void REDQueue::print_edp()
{
printf("mean_pktsz: %d\n", edp_.mean_pktsize);
printf("bytes: %d, wait: %d, setbit: %d\n",
edp_.bytes, edp_.wait, edp_.setbit);
printf("minth: %f, maxth: %f\n", edp_.th_min, edp_.th_max);
printf("max_p_inv: %f, qw: %f, ptc: %f\n",
edp_.max_p_inv, edp_.q_w, edp_.ptc);
printf("qlim: %d, idletime: %f\n", qlim_, idletime_);
printf("=========\n");
}
void REDQueue::print_edv()
{
printf("v_a: %f, v_b: %f\n", edv_.v_a, edv_.v_b);
}
replicator.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1996 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 MASH Research
* Group at the University of California Berkeley.
* 4. Neither the name of the University nor of the Research Group 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.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$";
#endif
#include "classifier.h"
#include "packet.h"
#include "ip.h"
/*
* A replicator is not really a packet classifier but
* we simply find convenience in leveraging its slot table.
* (this object used to implement fan-out on a multicast
* router as well as broadcast LANs)
*/
class Replicator : public Classifier {
public:
Replicator();
void recv(Packet*, Handler* h = 0);
virtual int classify(Packet*) {/*NOTREACHED*/ return -1;};
protected:
virtual int command(int argc, const char*const* argv);
int ignore_;
};
static class ReplicatorClass : public TclClass {
public:
ReplicatorClass() : TclClass("Classifier/Replicator") {}
TclObject* create(int, const char*const*) {
return (new Replicator());
}
} class_replicator;
Replicator::Replicator() : ignore_(0)
{
bind("ignore_", &ignore_);
}
void Replicator::recv(Packet* p, Handler*)
{
hdr_ip* iph = hdr_ip::access(p);
hdr_cmn* ch = hdr_cmn::access(p);
if (maxslot_ < 0) {
if (!ignore_)
Tcl::instance().evalf("%s drop %ld %ld %d", name(),
iph->saddr(), iph->daddr(), ch->iface());
Packet::free(p);
return;
}
for (int i = 0; i < maxslot_; ++i) {
NsObject* o = slot_[i];
if (o != 0)
o->recv(p->copy());
}
/* we know that maxslot is non-null */
slot_[maxslot_]->recv(p);
}
int Replicator::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
if (argc == 2) {
/*
* $replicator slot
*/
if (strcmp(argv[1], "slots") == 0) {
if (maxslot_ < 0) {
tcl.result("");
return (TCL_OK);
}
for (int i = 0; i <= maxslot_; i++) {
if (slot_[i] == 0)
continue;
tcl.resultf("%s %s", tcl.result(),
slot_[i]->name());
}
return (TCL_OK);
}
}
return Classifier::command(argc, argv);
}
resv.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) Xerox Corporation 1997. All rights reserved.
*
* License is granted to copy, to use, and to make and to use derivative
* works for research and evaluation purposes, provided that Xerox is
* acknowledged in all documentation pertaining to any such copy or
* derivative work. Xerox grants no other licenses expressed or
* implied. The Xerox trade name should not be used in any advertising
* without its written permission.
*
* XEROX CORPORATION MAKES NO REPRESENTATIONS CONCERNING EITHER THE
* MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE
* FOR ANY PARTICULAR PURPOSE. The software is provided "as is" without
* express or implied warranty of any kind.
*
* These notices must be retained in any copies of any part of this
* software.
*/
#include "packet.h"
#include "resv.h"
int hdr_resv::offset_;
static class ResvHeaderClass : public PacketHeaderClass {
public:
ResvHeaderClass() : PacketHeaderClass("PacketHeader/Resv",
sizeof(hdr_resv)) {
bind_offset(&hdr_resv::offset_);
}
void export_offsets() {
field_offset("rate_", OFFSET(hdr_resv, rate_));
field_offset("bucket_", OFFSET(hdr_resv, bucket_));
field_offset("decision_", OFFSET(hdr_resv, decision_));
}
} class_resvhdr;
rlm.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1994-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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*/
#ifndef lint
static char rcsid[] =
"@(#) $Header$ (LBL)";
#endif
#include "agent.h"
#include "node.h"
struct srcpkt {
double off;
int level;
int size;
srcpkt* next;
};
class RLM_Sender : public Agent {
public:
RLM_Sender();
~RLM_Sender();
int command(int argc, const char*const* argv);
protected:
virtual void timeout(int);
void start();
void stop();
void sched_next();
void sendpkt(int size, int level);
void addpkt(double off, int size, int level);
int running_;
double interval_;
srcpkt* pkt_;
srcpkt* allpkt_;
/*XXX make size dynamic*/
#define MAXLEVEL 32
int seqno_[MAXLEVEL];
int blkno_;
};
static class RLMMatcher : public Matcher {
public:
RLMMatcher() : Matcher("agent") {}
TclObject* match(const char* id) {
if (strcmp(id, "rlm-sender") == 0)
return (new RLM_Sender());
return (0);
}
} matcher_rlm;
RLM_Sender::RLM_Sender() : Agent(PT_CBR), pkt_(0), allpkt_(0)
{
Tcl& tcl = Tcl::instance();
link_time("ns_rlm", "interval", &interval_, 0);
link_int("ns_rlm", "packet-size", &size_, 0);
running_ = 0;
memset(seqno_, 0, sizeof(seqno_));
blkno_ = 0;
}
RLM_Sender::~RLM_Sender()
{
/*XXX free allpkt_ */
}
void RLM_Sender::start()
{
if (allpkt_ != 0) {
running_ = 1;
pkt_ = allpkt_;
sched(pkt_->off, 0);
}
}
void RLM_Sender::stop()
{
running_ = 0;
}
void RLM_Sender::sched_next()
{
srcpkt* p = pkt_;
srcpkt* n = p->next;
double t;
if (n == 0) {
t = interval_;
n = allpkt_;
++blkno_;
blkitem_ = 0;
} else
t = 0.;
t += n->off - p->off;
pkt_ = n;
sched(t, 0);
}
void RLM_Sender::timeout(int)
{
if (running_) {
sendpkt(pkt_->size, pkt_->level);
sched_next();
}
}
void RLM_Sender::sendpkt(int size, int level)
{
Packet* p = allocpkt(seqno_[level]++);
IPHeader *iph = IPHeader::access(p->bits());
RLMHeader *rh = RLMHeader::access(p->bits());
rh->blkno() = blkno_;
rh->blkitem() = blkitem_++;
iph->size() = size;
iph->daddr() += level;
#ifdef notdef
iph->class() += level;/*XXX*/
#endif
node_->transmit(p);
}
extern double time_atof(const char*);
void RLM_Sender::addpkt(double off, int size, int level)
{
srcpkt* sp = new srcpkt;
sp->next = 0;
sp->off = off;
sp->size = size;
sp->level = level;
srcpkt** p;
for (p = &allpkt_; *p != 0; p = &(*p)->next)
if (sp->off < (*p)->off)
break;
*p = sp;
}
/*
* $obj start
* $obj stop
* $obj packet $off $size $level
*/
int RLM_Sender::command(int argc, const char*const* argv)
{
if (argc == 2) {
if (strcmp(argv[1], "start") == 0) {
start();
return (TCL_OK);
}
if (strcmp(argv[1], "stop") == 0) {
stop();
return (TCL_OK);
}
}
if (argc == 5) {
if (strcmp(argv[1], "packet") == 0) {
double off = time_atof(argv[2]);
int size = atoi(argv[3]);
int level = atoi(argv[4]);
addpkt(off, size, level);
return (TCL_OK);
}
}
return (Agent::command(argc, argv));
}
class RLM_Receiver : public Agent {
public:
RLM_Receiver();
int command(int argc, const char*const* argv);
void recv(Packet* pkt);
protected:
char* proc_;
int loss_;
int expected_;
int bytes_;
int pblk_;
int pseq_[MAXLEVEL];
};
static class RLM_ReceiverMatcher : public Matcher {
public:
RLM_ReceiverMatcher() : Matcher("agent") {}
TclObject* match(const char* id) {
if (strcmp(id, "rlm-receiver") == 0)
return (new RLM_Receiver());
return (0);
}
} matcher_cbr;
RLM_Receiver::RLM_Receiver() : Agent(-1), proc_(0), expected_(-1)
{
bytes_ = 0;
loss_ = 0;
link_int(0, "loss", &loss_, 1);
link_int(0, "bytes", &bytes_, 1);
}
void RLM_Receiver::recv(Packet* pkt)
{
IPHeader *iph = IPHeader::access(pkt->bits());
SequenceHeader *sh = SequenceHeader::access(pkt->bits());
bytes_ += iph->size();
int seqno = sh->seqno();
int blkno = seqno >> 16;
seqno &= 0xffff;
/*
* Check for lost packets
*/
if (expected_ >= 0) {
int loss = sh->seqno() - expected_;
if (loss > 0) {
loss_ += loss;
if (proc_ != 0)
Tcl::instance().eval(proc_);
}
}
expected_ = sh->seqno() + 1;
Packet::free(pkt);
}
/*
* $proc interval $interval
* $proc size $size
*/
int RLM_Receiver::command(int argc, const char*const* argv)
{
if (argc == 2) {
if (strcmp(argv[1], "clear") == 0) {
expected_ = -1;
return (TCL_OK);
}
} else if (argc == 3) {
if (strcmp(argv[1], "proc") == 0) {
const char* proc = argv[2];
int n = strlen(proc) + 1;
delete proc_;
proc_ = new char[n];
strcpy(proc_, proc);
return (TCL_OK);
}
}
return (Agent::command(argc, argv));
}
rng.cc
/* -*- 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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (LBL)";
#endif
/* new random number generator */
#ifndef WIN32
#include // for gettimeofday
#include
#endif
#include
#include "rng.h"
#include "config.h" // for gettimeofday
#ifndef MAXINT
#define MAXINT 2147483647 // XX [for now]
#endif
/*
* RNGImplementation
*/
/*
* Generate a periodic sequence of pseudo-random numbers with
* a period of 2^31 - 2. The generator is the "minimal standard"
* multiplicative linear congruential generator of Park, S.K. and
* Miller, K.W., "Random Number Generators: Good Ones are Hard to Find,"
* CACM 31:10, Oct. 88, pp. 1192-1201.
*
* The algorithm implemented is: Sn = (a*s) mod m.
* The modulus m can be approximately factored as: m = a*q + r,
* where q = m div a and r = m mod a.
*
* Then Sn = g(s) + m*d(s)
* where g(s) = a(s mod q) - r(s div q)
* and d(s) = (s div q) - ((a*s) div m)
*
* Observations:
* - d(s) is either 0 or 1.
* - both terms of g(s) are in 0, 1, 2, . . ., m - 1.
* - |g(s)| <= m - 1.
* - if g(s) > 0, d(s) = 0, else d(s) = 1.
* - s mod q = s - k*q, where k = s div q.
*
* Thus Sn = a(s - k*q) - r*k,
* if (Sn <= 0), then Sn += m.
*
* To test an implementation for A = 16807, M = 2^31-1, you should
* get the following sequences for the given starting seeds:
*
* s0, s1, s2, s3, . . . , s10000, . . . , s551246
* 1, 16807, 282475249, 1622650073, . . . , 1043618065, . . . , 1003
* 1973272912, 1207871363, 531082850, 967423018
*
* It is important to check for s10000 and s551246 with s0=1, to guard
* against overflow.
*/
/*
* The sparc assembly code [no longer here] is based on Carta, D.G., "Two Fast
* Implementations of the 'Minimal Standard' Random Number
* Generator," CACM 33:1, Jan. 90, pp. 87-88.
*
* ASSUME that "the product of two [signed 32-bit] integers (a, sn)
* will occupy two [32-bit] registers (p, q)."
* Thus: a*s = (2^31)p + q
*
* From the observation that: x = y mod z is but
* x = z * the fraction part of (y/z),
* Let: sn = m * Frac(as/m)
*
* For m = 2^31 - 1,
* sn = (2^31 - 1) * Frac[as/(2^31 -1)]
* = (2^31 - 1) * Frac[as(2^-31 + 2^-2(31) + 2^-3(31) + . . .)]
* = (2^31 - 1) * Frac{[(2^31)p + q] [2^-31 + 2^-2(31) + 2^-3(31) + . .
.]}
* = (2^31 - 1) * Frac[p+(p+q)2^-31+(p+q)2^-2(31)+(p+q)3^(-31)+ . . .]
*
* if p+q < 2^31:
* sn = (2^31 - 1) * Frac[p + a fraction + a fraction + a fraction + . . .]
* = (2^31 - 1) * [(p+q)2^-31 + (p+q)2^-2(31) + (p+q)3^(-31) + . . .]
* = p + q
*
* otherwise:
* sn = (2^31 - 1) * Frac[p + 1.frac . . .]
* = (2^31 - 1) * (-1 + 1.frac . . .)
* = (2^31 - 1) * [-1 + (p+q)2^-31 + (p+q)2^-2(31) + (p+q)3^(-31) + . . .]
* = p + q - 2^31 + 1
*/
#define A 16807L /* multiplier, 7**5 */
#define M 2147483647L /* modulus, 2**31 - 1; both used in random */
#define INVERSE_M ((double)4.656612875e-10) /* (1.0/(double)M) */
long
RNGImplementation::next()
{
long L, H;
L = A * (seed_ & 0xffff);
H = A * (seed_ >> 16);
seed_ = ((H & 0x7fff) << 16) + L;
seed_ -= 0x7fffffff;
seed_ += H >> 15;
if (seed_ <= 0) {
seed_ += 0x7fffffff;
}
return(seed_);
}
double
RNGImplementation::next_double()
{
long i = next();
return i * INVERSE_M;
}
/*
* RNG implements a nice front-end around RNGImplementation
*/
#ifndef stand_alone
static class RNGClass : public TclClass {
public:
RNGClass() : TclClass("RNG") {}
TclObject* create(int, const char*const*) {
return(new RNG());
}
} class_rng;
#endif /* stand_alone */
/* default RNG */
RNG* RNG::default_ = NULL;
double
RNG::normal(double avg, double std)
{
static int parity = 0;
static double nextresult;
double sam1, sam2, rad;
if (std == 0) return avg;
if (parity == 0) {
sam1 = 2*uniform() - 1;
sam2 = 2*uniform() - 1;
while ((rad = sam1*sam1 + sam2*sam2) >= 1) {
sam1 = 2*uniform() - 1;
sam2 = 2*uniform() - 1;
}
rad = sqrt((-2*log(rad))/rad);
nextresult = sam2 * rad;
parity = 1;
return (sam1 * rad * std + avg);
}
else {
parity = 0;
return (nextresult * std + avg);
}
}
#ifndef stand_alone
int
RNG::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
if (argc == 3) {
if (strcmp(argv[1], "testint") == 0) {
int n = atoi(argv[2]);
tcl.resultf("%d", uniform(n));
return (TCL_OK);
}
if (strcmp(argv[1], "testdouble") == 0) {
double d = atof(argv[2]);
tcl.resultf("%6e", uniform(d));
return (TCL_OK);
}
if (strcmp(argv[1], "seed") == 0) {
int s = atoi(argv[2]);
// NEEDSWORK: should be a way to set seed to PRDEF_SEED_SORUCE
if (s) {
if (s <= 0 || (unsigned int)s >= MAXINT) {
tcl.resultf("Setting random number seed to known bad value.");
return TCL_ERROR;
};
set_seed(RAW_SEED_SOURCE, s);
} else set_seed(HEURISTIC_SEED_SOURCE, 0);
return(TCL_OK);
}
} else if (argc == 2) {
if (strcmp(argv[1], "next-random") == 0) {
tcl.resultf("%u", uniform_positive_int());
return(TCL_OK);
}
if (strcmp(argv[1], "seed") == 0) {
tcl.resultf("%u", stream_.seed());
return(TCL_OK);
}
if (strcmp(argv[1], "default") == 0) {
default_ = this;
return(TCL_OK);
}
#if 0
if (strcmp(argv[1], "test") == 0) {
if (test())
tcl.resultf("RNG test failed");
else
tcl.resultf("RNG test passed");
return(TCL_OK);
}
#endif
} else if (argc == 4) {
if (strcmp(argv[1], "normal") == 0) {
double avg = strtod(argv[2], NULL);
double std = strtod(argv[3], NULL);
tcl.resultf("%g", normal(avg, std));
return (TCL_OK);
} else if (strcmp(argv[1], "lognormal") == 0) {
double avg = strtod(argv[2], NULL);
double std = strtod(argv[3], NULL);
tcl.resultf("%g", lognormal(avg, std));
return (TCL_OK);
}
}
return(TclObject::command(argc, argv));
}
#endif /* stand_alone */
void
RNG::set_seed(RNGSources source, int seed)
{
/* The following predefined seeds are evenly spaced around
* the 2^31 cycle. Each is approximately 33,000,000 elements
* apart.
*/
#define N_SEEDS_ 64
static long predef_seeds[N_SEEDS_] = {
1973272912L, 188312339L, 1072664641L, 694388766L,
2009044369L, 934100682L, 1972392646L, 1936856304L,
1598189534L, 1822174485L, 1871883252L, 558746720L,
605846893L, 1384311643L, 2081634991L, 1644999263L,
773370613L, 358485174L, 1996632795L, 1000004583L,
1769370802L, 1895218768L, 186872697L, 1859168769L,
349544396L, 1996610406L, 222735214L, 1334983095L,
144443207L, 720236707L, 762772169L, 437720306L,
939612284L, 425414105L, 1998078925L, 981631283L,
1024155645L, 822780843L, 701857417L, 960703545L,
2101442385L, 2125204119L, 2041095833L, 89865291L,
898723423L, 1859531344L, 764283187L, 1349341884L,
678622600L, 778794064L, 1319566104L, 1277478588L,
538474442L, 683102175L, 999157082L, 985046914L,
722594620L, 1695858027L, 1700738670L, 1995749838L,
1147024708L, 346983590L, 565528207L, 513791680L
};
static long heuristic_sequence = 0;
switch (source) {
case RAW_SEED_SOURCE:
// use it as it is
break;
case PREDEF_SEED_SOURCE:
if (seed < 0 || seed >= N_SEEDS_)
abort();
seed = predef_seeds[seed];
break;
case HEURISTIC_SEED_SOURCE:
timeval tv;
gettimeofday(&tv, 0);
heuristic_sequence++; // Always make sure we're different than last time.
seed = (tv.tv_sec ^ tv.tv_usec ^ (heuristic_sequence << 8)) & 0x7fffffff;
break;
};
// set it
// NEEDSWORK: should we throw out known bad seeds?
// (are there any?)
if (seed < 0)
seed = -seed;
stream_.set_seed(seed);
// Toss away the first few values of heuristic seed.
// In practice this makes sequential heuristic seeds
// generate different first values.
//
// How many values to throw away should be the subject
// of careful analysis. Until then, I just throw away
// ``a bunch''. --johnh
if (source == HEURISTIC_SEED_SOURCE) {
int i;
for (i = 0; i < 128; i++) {
stream_.next();
};
};
}
/*
* RNGTest:
* Make sure the RNG makes known values.
* Optionally, print out some stuff.
*
* Simple test program:
* #include "rng.h"
* void main() { RNGTest test; test.verbose(); }
*/
#ifdef rng_test
RNGTest::RNGTest()
{
RNG rng(RNG::RAW_SEED_SOURCE, 1L);
int i;
long r;
for (i = 0; i < 10000; i++)
r = rng.uniform_positive_int();
if (r != 1043618065L)
abort();
for (i = 10000; i < 551246; i++)
r = rng.uniform_positive_int();
if (r != 1003L)
abort();
}
void
RNGTest::first_n(RNG::RNGSources source, long seed, int n)
{
RNG rng(source, seed);
for (int i = 0; i < n; i++) {
int r = rng.uniform_positive_int();
printf("%10d ", r);
};
printf("\n");
}
void
RNGTest::verbose()
{
printf ("default: ");
first_n(RNG::RAW_SEED_SOURCE, 1L, 5);
int i;
for (i = 0; i < 64; i++) {
printf ("predef source %2d: ", i);
first_n(RNG::PREDEF_SEED_SOURCE, i, 5);
};
printf("heuristic seeds should be different from each other and on each run.\n");
for (i = 0; i < 64; i++) {
printf ("heuristic source %2d: ", i);
first_n(RNG::HEURISTIC_SEED_SOURCE, i, 5);
};
}
#endif /* rng_test */
route.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1991-1994 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 University of
* California, Berkeley and the Network Research Group at
* Lawrence Berkeley Laboratory.
* 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.
*
* Routing code for general topologies based on min-cost routing algorithm in
* Bertsekas' book. Written originally by S. Keshav, 7/18/88
* (his work covered by identical UC Copyright)
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (LBL)";
#endif
#include
#include
#include "config.h"
#include "route.h"
#include "address.h"
class RouteLogicClass : public TclClass {
public:
RouteLogicClass() : TclClass("RouteLogic") {}
TclObject* create(int, const char*const*) {
return (new RouteLogic());
}
} routelogic_class;
void RouteLogic::reset_all()
{
delete[] adj_;
delete[] route_;
adj_ = 0;
route_ = 0;
size_ = 0;
}
int RouteLogic::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
if (argc == 2) {
if (strcmp(argv[1], "compute") == 0) {
if (adj_ == 0) {
//fprintf(stderr, "No adjacency info; Probably a disconnected topology! Route not computed..\n");
return (TCL_OK);
}
compute_routes();
return (TCL_OK);
}
if (strcmp(argv[1], "hier-compute") == 0) {
if (hadj_ == 0) {
return (TCL_OK);
}
hier_compute();
return (TCL_OK);
}
if (strcmp(argv[1], "hier-print") == 0) {
hier_print_hadj();
return (TCL_OK);
}
if (strcmp(argv[1], "hier-print-route") == 0) {
hier_print_route();
return (TCL_OK);
}
if (strcmp(argv[1], "reset") == 0) {
reset_all();
return (TCL_OK);
}
}
else if (argc > 2) {
if (strcmp(argv[1], "insert") == 0) {
int src = atoi(argv[2]) + 1;
int dst = atoi(argv[3]) + 1;
if (src <= 0 || dst <= 0) {
tcl.result("negative node number");
return (TCL_ERROR);
}
double cost = (argc == 5 ? atof(argv[4]) : 1);
insert(src, dst, cost);
return (TCL_OK);
}
if (strcmp(argv[1], "hlevel-is") == 0) {
level_ = atoi(argv[2]);
if (level_ < 1) {
tcl.result("send-hlevel: # hierarchy levels should be non-zero");
return (TCL_ERROR);
}
return (TCL_OK);
}
if (strcmp(argv[1], "send-num-of-domains") == 0) {
D_ = atoi(argv[2]) + 1;
if (D_ <= 1) {
tcl.result("send-num-of-domains: # domains should be non-zero");
return (TCL_ERROR);
}
return (TCL_OK);
}
if (strcmp(argv[1], "send-num-of-clusters") == 0) {
if (argc != D_ + 1) {
tcl.result("send-num-of-clusters: # args do not match as expected\n");
return (TCL_ERROR);
}
C_ = new int[D_];
int i, j = 2;
for (i = 1; i < D_; i++) {
C_[i] = atoi(argv[j]) + 1;
j++;
}
hier_init();
return (TCL_OK);
}
if(strcmp(argv[1], "send-num-of-nodes") == 0) {
int i, j, k=2, Ctotal=0 ;
for (i=1; i < D_; i++)
Ctotal = Ctotal + (C_[i]-1);
if (argc != (Ctotal + 2)) {
tcl.result("send-hlastdata: # args do not match");
return (TCL_ERROR);
}
for (i=1; i < D_; i++)
for (j=1; (j < C_[i]); j++) {
cluster_size_[INDEX(i, j, Cmax_)] = atoi(argv[k]);
k++;
}
return (TCL_OK);
}
if (strcmp(argv[1], "hier-insert") == 0) {
if(Cmax_== 0 || D_== 0) {
tcl.result("Required Hier_data not sent");
return (TCL_ERROR);
}
int i;
int src_addr[SMALL_LEN], dst_addr[SMALL_LEN];
/* initializing src and dst addr */
// for (i=0; i < SMALL_LEN; i++){
// src_addr[i] = 0;
// dst_addr[i] = 0;
// }
str2address(argv, src_addr, dst_addr);
// for (i=0; i < HIER_LEVEL; i++)
for (i=0; i < level_; i++)
if (src_addr[i]<=0 || dst_addr[i]<=0){
tcl.result ("negative node number");
return (TCL_ERROR);
}
int cost = (argc == 5 ? atoi(argv[4]) : 1);
hier_insert(src_addr, dst_addr, cost);
return (TCL_OK);
}
if (strcmp(argv[1], "hier-reset") == 0) {
int i;
int src_addr[SMALL_LEN], dst_addr[SMALL_LEN];
str2address(argv, src_addr, dst_addr);
// assuming node-node addresses (instead of node-cluster or node-domain pair)
// are sent for hier_reset
// for (i=0; i < HIER_LEVEL; i++)
for (i=0; i < level_; i++)
if (src_addr[i]<=0 || dst_addr[i]<=0){
tcl.result ("negative node number");
return (TCL_ERROR);
}
hier_reset(src_addr, dst_addr);
}
if (strcmp(argv[1], "hier-lookup") == 0) {
int nh;
int res = lookup_hier((char*)argv[2], (char*)argv[3],
nh);
return res;
}
if (strcmp(argv[1], "reset") == 0) {
int src = atoi(argv[2]) + 1;
int dst = atoi(argv[3]) + 1;
if (src <= 0 || dst <= 0) {
tcl.result("negative node number");
return (TCL_ERROR);
}
reset(src, dst);
return (TCL_OK);
}
if (strcmp(argv[1], "lookup") == 0) {
int nh;
int res = lookup_flat((char*)argv[2], (char*)argv[3],
nh);
if (res == TCL_OK)
tcl.resultf("%d", nh);
return res;
}
}
return (TclObject::command(argc, argv));
}
// xxx: using references as in this result is bogus---use pointers!
int RouteLogic::lookup_flat(char* asrc, char* adst, int& result) {
Tcl& tcl = Tcl::instance();
int src = atoi(asrc) + 1;
int dst = atoi(adst) + 1;
if (route_ == 0) {
// routes are computed only after the simulator is running
// ($ns run).
tcl.result("routes not yet computed");
return (TCL_ERROR);
}
if (src >= size_ || dst >= size_) {
tcl.result("node out of range");
return (TCL_ERROR);
}
result = route_[INDEX(src, dst, size_)].next_hop - 1;
return TCL_OK;
}
// xxx: using references as in this result is bogus---use pointers!
int RouteLogic::lookup_hier(char* asrc, char* adst, int& result) {
int i;
int src[SMALL_LEN], dst[SMALL_LEN];
Tcl& tcl = Tcl::instance();
if ( hroute_ == 0) {
tcl.result("Required Hier_data not sent");
return TCL_ERROR;
}
ns_strtok(asrc, src);
ns_strtok(adst, dst);
for (i=0; i < level_; i++)
if (src[i] <= 0) {
tcl.result("negative src node number");
return TCL_ERROR;
}
if (dst[0] <= 0) {
tcl.result("negative dst domain number");
return TCL_ERROR;
}
int d = src[0];
int index = INDEX(src[0], src[1], Cmax_);
int size = cluster_size_[index];
if (hsize_[index] == 0) {
tcl.result("Routes not computed");
return TCL_ERROR;
}
if ((src[0] < D_) || (dst[0] < D_)) {
if((src[1] < C_[d]) || (dst[1] < C_[dst[0]]))
if((src[2] <= size) ||
(dst[2] <= cluster_size_[INDEX(dst[0], dst[1], Cmax_)]))
;
}
else {
tcl.result("node out of range");
return TCL_ERROR;
}
int next_hop = 0;
/* if node-domain lookup */
if (((dst[1] <= 0) && (dst[2] <= 0)) ||
(src[0] != dst[0])){
next_hop = hroute_[index][N_D_INDEX(src[2], dst[0], size, C_[d], D_)];
}
/* if node-cluster lookup */
else if ((dst[2] <= 0) || (src[1] != dst[1])) {
next_hop = hroute_[index][N_C_INDEX(src[2], dst[1], size, C_[d], D_)];
}
/* if node-node lookup */
else {
next_hop = hroute_[index][N_N_INDEX(src[2], dst[2], size, C_[d], D_)];
}
char target[SMALL_LEN];
if (next_hop > 0) {
get_address(target, next_hop, index, d, size, src);
tcl.result(target);
result= Address::instance().str2addr(target);
} else {
tcl.result("-1");
result = -1;
}
return TCL_OK;
}
RouteLogic::RouteLogic()
{
size_ = 0;
adj_ = 0;
route_ = 0;
/* additions for hierarchical routing extension */
C_ = 0;
D_ = 0;
Cmax_ = 0;
level_ = 0;
hsize_ = 0;
hadj_ = 0;
hroute_ = 0;
hconnect_ = 0;
cluster_size_ = 0;
}
RouteLogic::~RouteLogic()
{
delete[] adj_;
delete[] route_;
for (int i = 0; i < (Cmax_ * D_); i++) {
for (int j = 0; j < (Cmax_ + D_) * (cluster_size_[i]+1); j++) {
if (hconnect_[i][j] != NULL)
delete [] hconnect_[i][j];
}
delete [] hconnect_[i];
}
for (int n =0; n < (Cmax_ * D_); n++) {
if (hadj_[n] != NULL)
delete [] hadj_[n];
if (hroute_[n] != NULL)
delete [] hroute_[n];
}
delete [] C_;
delete [] hsize_;
delete [] cluster_size_;
delete hadj_;
delete hroute_;
delete hconnect_;
}
void RouteLogic::alloc(int n)
{
size_ = n;
n *= n;
adj_ = new adj_entry[n];
for (int i = 0; i < n; ++i) {
adj_[i].cost = INFINITY;
adj_[i].entry = 0;
}
}
/*
* Check that we have enough storage in the adjacency array
* to hold a node numbered "n"
*/
void RouteLogic::check(int n)
{
if (n < size_)
return;
adj_entry* old = adj_;
int osize = size_;
int m = osize;
if (m == 0)
m = 16;
while (m <= n)
m <<= 1;
alloc(m);
for (int i = 0; i < osize; ++i) {
for (int j = 0; j < osize; ++j)
adj_[INDEX(i, j, m)].cost =old[INDEX(i, j, osize)].cost;
}
size_ = m;
delete[] old;
}
void RouteLogic::insert(int src, int dst, double cost)
{
check(src);
check(dst);
adj_[INDEX(src, dst, size_)].cost = cost;
}
void RouteLogic::insert(int src, int dst, double cost, void* entry_)
{
check(src);
check(dst);
adj_[INDEX(src, dst, size_)].cost = cost;
adj_[INDEX(src, dst, size_)].entry = entry_;
}
void RouteLogic::reset(int src, int dst)
{
assert(src < size_);
assert(dst < size_);
adj_[INDEX(src, dst, size_)].cost = INFINITY;
}
void RouteLogic::compute_routes()
{
int n = size_;
int* parent = new int[n];
double* hopcnt = new double[n];
#define ADJ(i, j) adj_[INDEX(i, j, size_)].cost
#define ADJ_ENTRY(i, j) adj_[INDEX(i, j, size_)].entry
#define ROUTE(i, j) route_[INDEX(i, j, size_)].next_hop
#define ROUTE_ENTRY(i, j) route_[INDEX(i, j, size_)].entry
delete[] route_;
route_ = new route_entry[n * n];
memset((char *)route_, 0, n * n * sizeof(route_[0]));
/* do for all the sources */
int k;
for (k = 1; k < n; ++k) {
int v;
for (v = 0; v < n; v++)
parent[v] = v;
/* set the route for all neighbours first */
for (v = 1; v < n; ++v) {
if (parent[v] != k) {
hopcnt[v] = ADJ(k, v);
if (hopcnt[v] != INFINITY) {
ROUTE(k, v) = v;
ROUTE_ENTRY(k, v) = ADJ_ENTRY(k, v);
}
}
}
for (v = 1; v < n; ++v) {
/*
* w is the node that is the nearest to the subtree
* that has been routed
*/
int o = 0;
/* XXX */
hopcnt[0] = INFINITY;
int w;
for (w = 1; w < n; w++)
if (parent[w] != k && hopcnt[w] < hopcnt[o])
o = w;
parent[o] = k;
/*
* update distance counts for the nodes that are
* adjacent to o
*/
if (o == 0)
continue;
for (w = 1; w < n; w++) {
if (parent[w] != k &&
hopcnt[o] + ADJ(o, w) < hopcnt[w]) {
ROUTE(k, w) = ROUTE(k, o);
ROUTE_ENTRY(k, w) =
ROUTE_ENTRY(k, o);
hopcnt[w] = hopcnt[o] + ADJ(o, w);
}
}
}
}
/*
* The route to yourself is yourself.
*/
for (k = 1; k < n; ++k) {
ROUTE(k, k) = k;
ROUTE_ENTRY(k, k) = 0; // This should not matter
}
delete[] hopcnt;
delete[] parent;
}
/* hierarchical routing support */
/* This function creates adjacency matrix for each cluster at the lowest level of the hierarchy for every node in the cluster, for every other cluster in the domain, and every other domain. can be extended from 3-level hierarchy to n-level along similar lines*/
void RouteLogic::hier_alloc(int i)
{
hsize_[i] = cluster_size_[i]+ Cmax_+ D_ ;
hsize_[i] *= hsize_[i];
hadj_[i] = new int[hsize_[i]];
hroute_[i] = new int[hsize_[i]];
hconnect_[i] = new char*[(Cmax_ + D_) * (cluster_size_[i]+1)];
for (int n = 0; n < hsize_[i]; n++){
hadj_[i][n] = INFINITY;
hroute_[i][n] = INFINITY;
}
}
void RouteLogic::hier_check(int i)
{
if(hsize_[i] > 0)
return;
else
hier_alloc(i);
}
void RouteLogic::hier_init(void)
{
int i;
for (i = 1; i < D_; i++) {
Cmax_ = C_[i] > Cmax_ ? C_[i]: Cmax_;
}
int arr_size = Cmax_ * D_ ;
cluster_size_ = new int[arr_size];
hsize_ = new int[arr_size];
for (i = 0; i < arr_size; i++)
hsize_[i] = 0;
hadj_ = new int*[arr_size];
hroute_ = new int*[arr_size];
hconnect_ = new char**[arr_size];
}
void RouteLogic::str2address(const char*const* argv, int *src_addr, int *dst_addr)
{
char src[SMALL_LEN];
char dst[SMALL_LEN];
strcpy(src, argv[2]);
strcpy(dst, argv[3]);
ns_strtok(src, src_addr);
ns_strtok(dst, dst_addr);
}
void RouteLogic::ns_strtok(char *addr, int *addrstr)
{
int i;
char tmpstr[SMALL_LEN];
char *next, *index;
i = 0;
strcpy(tmpstr, addr);
next = tmpstr;
while(*next){
index = strstr(next, ".");
if (index != NULL){
next[index - next] = '\0';
addrstr[i] = atoi(next) + 1;
next = index + 1;
i++;
}
else {
if (*next != '\0') //damn that ending point
addrstr[i] = atoi(next) + 1;
break;
}
}
}
void RouteLogic::get_address(char *address, int next_hop, int index, int d, int size, int *src)
{
if (next_hop <= size) {
sprintf(address,"%d.%d.%d", src[0]-1, src[1]-1, next_hop-1);
}
else if ((next_hop > size) && (next_hop < (size + C_[d]))) {
int temp = next_hop - size;
int I = src[2] * (C_[d] + D_) + temp;
strcpy(address, hconnect_[index][I]);
}
else {
int temp = next_hop - size - (C_[d] - 1);
int I = src[2] * (C_[d] + D_) + (C_[d] - 1 + temp);
strcpy(address,hconnect_[index][I]);
}
}
void RouteLogic::hier_reset(int *src, int *dst)
{
int i, d, n;
d = src[0];
if (src[0] == dst[0])
if (src[1] == dst[1]) {
i = INDEX(src[0], src[1], Cmax_);
n = cluster_size_[i];
hadj_[i][N_N_INDEX(src[2], dst[2], n, C_[d], D_)] = INFINITY;
}
else {
for (int y=1; y < C_[d]; y++) {
i = INDEX(src[0], y, Cmax_);
n = cluster_size_[i];
hadj_[i][C_C_INDEX(src[1], dst[1], n, C_[d], D_)] = INFINITY;
if (y == src[1])
hadj_[i][N_C_INDEX(src[2], dst[1], n, C_[d], D_)] = INFINITY;
}
}
else {
for (int x=1; x < D_; x++)
for (int y=1; y < C_[x]; y++) {
i = INDEX(x, y, Cmax_);
n = cluster_size_[i];
hadj_[i][D_D_INDEX(src[0], dst[0], n, C_[x], D_)] = INFINITY;
if ( x == src[0] ){
hadj_[i][C_D_INDEX(src[1], dst[0], n, C_[x], D_)] = INFINITY;
if (y == src[1])
hadj_[i][N_D_INDEX(src[2], dst[0], n, C_[x], D_)] = INFINITY;
}
}
}
}
void RouteLogic::hier_insert(int *src_addr, int *dst_addr, int cost)
{
int X1 = src_addr[0];
int Y1 = src_addr[1];
int Z1 = src_addr[2];
int X2 = dst_addr[0];
int Y2 = dst_addr[1];
int Z2 = dst_addr[2];
int n, i;
if ( X1 == X2)
if (Y1 == Y2){
/*
* For the same domain & cluster
*/
i = INDEX(X1, Y1, Cmax_);
n = cluster_size_[i];
hier_check(i);
hadj_[i][N_N_INDEX(Z1, Z2, n, C_[X1], D_)] = cost;
}
else {
/*
* For the same domain but diff clusters
*/
for (int y=1; y < C_[X1]; y++) { /* insert cluster connectivity */
i = INDEX(X1, y, Cmax_);
n = cluster_size_[i];
hier_check(i);
hadj_[i][C_C_INDEX(Y1, Y2, n, C_[X1], D_)] = cost;
if (y == Y1) { /* insert node conn. */
hadj_[i][N_C_INDEX(Z1, Y2, n, C_[X1], D_)] = cost;
int I = Z1 * (C_[X1]+ D_) + Y2;
hconnect_[i][I] = new char[SMALL_LEN];
sprintf(hconnect_[i][I],"%d.%d.%d",X2-1,Y2-1,Z2-1);
}
}
}
else {
/*
* For different domains
*/
for (int x=1; x < D_; x++) { /* inset domain connectivity */
for (int y=1; y < C_[x]; y++) {
i = INDEX(x, y, Cmax_);
n = cluster_size_[i];
hier_check(i);
hadj_[i][D_D_INDEX(X1, X2, n, C_[x], D_)] = cost;
}
}
for (int y=1; y < C_[X1]; y++) { /* insert cluster connectivity */
i = INDEX(X1, y, Cmax_);
n = cluster_size_[i];
hier_check(i);
hadj_[i][C_D_INDEX(Y1, X2, n, C_[X1], D_)] = cost;
}
/* insert node connectivity */
i = INDEX(X1, Y1, Cmax_);
n = cluster_size_[i];
hadj_[i][N_D_INDEX(Z1, X2, n, C_[X1], D_)] = cost;
int I = Z1 * (C_[X1] + D_) + (C_[X1] - 1 + X2);
hconnect_[i][I] = new char[SMALL_LEN];
sprintf(hconnect_[i][I],"%d.%d.%d",X2-1,Y2-1,Z2-1);
}
}
void RouteLogic::hier_compute_routes(int i, int j)
{
int size = (cluster_size_[i] + C_[j] + D_);
int n = size ;
double* hopcnt = new double[n];
#define HADJ(i, j) adj_[INDEX(i, j, size)].cost
#define HROUTE(i, j) route_[INDEX(i, j, size)].next_hop
delete[] route_;
route_ = new route_entry[n * n];
int* parent = new int[n];
memset((char *)route_, 0, n * n * sizeof(route_[0]));
/* do for all the sources */
int k;
for (k = 1; k < n; ++k) {
int v;
for (v = 0; v < n; v++)
parent[v] = v;
/* set the route for all neighbours first */
for (v = 1; v < n; ++v) {
if (parent[v] != k) {
hopcnt[v] = HADJ(k, v);
if (hopcnt[v] != INFINITY)
HROUTE(k, v) = v;
}
}
for (v = 1; v < n; ++v) {
/*
* w is the node that is the nearest to the subtree
* that has been routed
*/
int o = 0;
/* XXX */
hopcnt[0] = INFINITY;
int w;
for (w = 1; w < n; w++)
if (parent[w] != k && hopcnt[w] < hopcnt[o])
o = w;
parent[o] = k;
/*
* update distance counts for the nodes that are
* adjacent to o
*/
if (o == 0)
continue;
for (w = 1; w < n; w++) {
if (parent[w] != k &&
hopcnt[o] + HADJ(o, w) < hopcnt[w]) {
HROUTE(k, w) = HROUTE(k, o);
hopcnt[w] = hopcnt[o] + HADJ(o, w);
}
}
}
}
/*
* The route to yourself is yourself.
*/
for (k = 1; k < n; ++k)
HROUTE(k, k) = k;
delete[] hopcnt;
delete[] parent;
}
/* function to check the adjacency matrices created */
void RouteLogic::hier_print_hadj() {
int i, j, k;
for (j=1; j < D_; j++)
for (k=1; k < C_[j]; k++) {
i = INDEX(j, k, Cmax_);
int s = (cluster_size_[i] + C_[j] + D_);
printf("ADJ MATRIX[%d] for cluster %d.%d :\n",i,j,k);
int temp = 1;
printf(" ");
while(temp < s){
printf(" %d",temp);
temp++;
}
printf("\n");
for(int n=1; n < s;n++){
printf("%d ",n);
for(int m=1;m < s;m++){
if(hadj_[i][INDEX(n,m,s)] == INFINITY)
printf("~ ");
else
printf("%d ",hadj_[i][INDEX(n,m,s)]);
}
printf("\n");
}
printf("\n\n");
}
}
void RouteLogic::hier_compute()
{
int i, j, k, m, n;
for (j=1; j < D_; j++)
for (k=1; k < C_[j]; k++) {
i = INDEX(j, k, Cmax_);
int s = (cluster_size_[i] + C_[j] + D_);
adj_ = new adj_entry[(s * s)];
memset((char *)adj_, 0, s * s * sizeof(adj_[0]));
for (n=0; n < s; n++)
for(m=0; m < s; m++)
adj_[INDEX(n, m, s)].cost = hadj_[i][INDEX(n, m, s)];
hier_compute_routes(i, j);
for (n=0; n < s; n++)
for(m=0; m < s; m++)
hroute_[i][INDEX(n, m, s)] = route_[INDEX(n, m, s)].next_hop;
delete [] adj_;
}
}
/*
* Prints routing table - debugging function
*/
void RouteLogic::hier_print_route()
{
for (int j=1; j < D_; j++)
for (int k=1; k < C_[j]; k++) {
int i = INDEX(j, k, Cmax_);
int s = (cluster_size_[i]+C_[j]+D_);
printf("ROUTE_TABLE[%d] for cluster %d.%d :\n",i,j,k);
int temp = 1;
printf(" ");
while(temp < s){
printf(" %d",temp);
temp++;
}
printf("\n");
for(int n=1; n < s; n++){
printf("%d ",n);
for(int m=1; m < s; m++)
printf("%d ",hroute_[i][INDEX(n, m, s)]);
printf("\n");
}
printf("\n\n");
}
}
static class RouteLogicAlgoClass : public TclClass {
public:
RouteLogicAlgoClass() : TclClass("RouteLogic/Algorithmic") {}
TclObject* create(int, const char*const*) {
return (new RouteLogicAlgo);
}
} class_routelogic_algo;
rtProtoDV.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* rtProtoDV.cc
*
* Copyright (C) 1997 by USC/ISI
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation, advertising
* materials, and other materials related to such distribution and use
* acknowledge that the software was developed by the University of
* Southern California, Information Sciences Institute. The name of the
* University may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (USC/ISI)";
#endif
#include "agent.h"
#include "rtProtoDV.h"
int hdr_DV::offset_;
static class rtDVHeaderClass : public PacketHeaderClass {
public:
rtDVHeaderClass() : PacketHeaderClass("PacketHeader/rtProtoDV",
sizeof(hdr_DV)) {
bind_offset(&hdr_DV::offset_);
}
} class_rtProtoDV_hdr;
static class rtProtoDVclass : public TclClass {
public:
rtProtoDVclass() : TclClass("Agent/rtProto/DV") {}
TclObject* create(int, const char*const*) {
return (new rtProtoDV);
}
} class_rtProtoDV;
int rtProtoDV::command(int argc, const char*const* argv)
{
if (strcmp(argv[1], "send-update") == 0) {
ns_addr_t dst;
dst.addr_ = atoi(argv[2]);
dst.port_ = atoi(argv[3]);
u_int32_t mtvar = atoi(argv[4]);
u_int32_t size = atoi(argv[5]);
sendpkt(dst, mtvar, size);
return TCL_OK;
}
return Agent::command(argc, argv);
}
void rtProtoDV::sendpkt(ns_addr_t dst, u_int32_t mtvar, u_int32_t size)
{
daddr() = dst.addr_;
dport() = dst.port_;
size_ = size;
Packet* p = Agent::allocpkt();
hdr_DV *rh = (hdr_DV*)p->access(off_DV_);
rh->metricsVar() = mtvar;
target_->recv(p);
}
void rtProtoDV::recv(Packet* p, Handler*)
{
hdr_DV* rh = (hdr_DV*)p->access(off_DV_);
hdr_ip* ih = (hdr_ip*)p->access(off_ip_);
Tcl::instance().evalf("%s recv-update %d %d", name(),
ih->saddr(), rh->metricsVar());
Packet::free(p);
}
rtcp.cc
/* -*- 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 MASH Research
* Group at the University of California Berkeley.
* 4. Neither the name of the University nor of the Research Group 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.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$";
#endif
#include
#include "config.h"
#include "agent.h"
#include "random.h"
#include "rtp.h"
class RTCPAgent;
class RTCP_Timer : public TimerHandler {
public:
RTCP_Timer(RTCPAgent *a) : TimerHandler() { a_ = a; }
protected:
virtual void expire(Event *e);
RTCPAgent *a_;
};
class RTCPAgent : public Agent {
public:
RTCPAgent();
virtual void timeout(int);
virtual void recv(Packet* p, Handler* h);
int command(int argc, const char*const* argv);
protected:
void start();
void stop();
void sendpkt();
int running_;
int random_;
int seqno_;
double interval_;
RTPSession* session_;
int off_rtp_;
RTCP_Timer rtcp_timer_;
};
static class RTCPAgentClass : public TclClass {
public:
RTCPAgentClass() : TclClass("Agent/RTCP") {}
TclObject* create(int, const char*const*) {
return (new RTCPAgent());
}
} class_rtcp_agent;
/* XXX Could perhaps derive this from CBR. If so, use cbr_timer_ */
RTCPAgent::RTCPAgent()
: Agent(PT_RTCP), session_(0), rtcp_timer_(this)
{
size_ = 128;
bind_time("interval_", &interval_);
bind("random_", &random_);
bind("seqno_", &seqno_);
bind("off_rtp_", &off_rtp_);
running_ = 0;
}
void RTCPAgent::start()
{
running_ = 1;
rtcp_timer_.resched(interval_);
}
void RTCPAgent::stop()
{
rtcp_timer_.cancel();
running_ = 0;
}
void RTCPAgent::recv(Packet* p, Handler*)
{
session_->recv_ctrl(p);
}
void RTCPAgent::sendpkt()
{
Packet* p = allocpkt();
hdr_rtp* rh = (hdr_rtp*)p->access(off_rtp_);
/* Fill in srcid_ and seqno */
rh->seqno() = seqno_++;
rh->srcid() = session_->srcid();
target_->recv(p);
}
void RTCPAgent::timeout(int)
{
if (running_) {
size_ = session_->build_report(0);
sendpkt();
double t = interval_;
if (random_)
/* add some zero-mean white noise */
t += interval_ * Random::uniform(-0.5, 0.5);
rtcp_timer_.resched(t);
/* XXX */
Tcl::instance().evalf("%s rtcp_timeout", session_->name());
}
}
int RTCPAgent::command(int argc, const char*const* argv)
{
if (argc == 2) {
if (strcmp(argv[1], "start") == 0) {
start();
return (TCL_OK);
}
if (strcmp(argv[1], "stop") == 0) {
stop();
return (TCL_OK);
}
if (strcmp(argv[1], "bye") == 0) {
size_ = session_->build_report(1);
sendpkt();
stop();
return (TCL_OK);
}
} else if (argc == 3) {
if (strcmp(argv[1], "session") == 0) {
session_ = (RTPSession*)TclObject::lookup(argv[2]);
return (TCL_OK);
}
}
return (Agent::command(argc, argv));
}
void RTCP_Timer::expire(Event* /*e*/) {
a_->timeout(0);
}
rtp.cc
/* -*- 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 MASH Research
* Group at the University of California Berkeley.
* 4. Neither the name of the University nor of the Research Group 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.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$";
#endif
#include
#include "config.h"
#include "agent.h"
#include "random.h"
#include "rtp.h"
int hdr_rtp::offset_;
class RTPHeaderClass : public PacketHeaderClass {
public:
RTPHeaderClass() : PacketHeaderClass("PacketHeader/RTP",
sizeof(hdr_rtp)) {
bind_offset(&hdr_rtp::offset_);
}
} class_rtphdr;
static class RTPAgentClass : public TclClass {
public:
RTPAgentClass() : TclClass("Agent/RTP") {}
TclObject* create(int, const char*const*) {
return (new RTPAgent());
}
} class_rtp_agent;
RTPAgent::RTPAgent() : Agent(PT_RTP), session_(0), lastpkttime_(-1e6),
running_(0), rtp_timer_(this)
{
bind("seqno_", &seqno_);
bind("off_rtp_", &off_rtp_);
bind_time("interval_", &interval_);
bind("packetSize_", &size_);
bind("maxpkts_", &maxpkts_);
bind("random_", &random_);
}
void RTPAgent::start()
{
running_ = 1;
sendpkt();
rtp_timer_.resched(interval_);
}
void RTPAgent::stop()
{
rtp_timer_.force_cancel();
finish();
}
void RTPAgent::sendmsg(int nbytes, const char* /*flags*/)
{
Packet *p;
int n;
if (++seqno_ < maxpkts_) {
if (size_)
n = nbytes / size_;
else
printf("Error: RTPAgent size = 0\n");
if (nbytes == -1) {
start();
return;
}
while (n-- > 0) {
p = allocpkt();
hdr_rtp* rh = (hdr_rtp*)p->access(off_rtp_);
rh->seqno() = seqno_;
target_->recv(p);
}
n = nbytes % size_;
if (n > 0) {
p = allocpkt();
hdr_rtp* rh = (hdr_rtp*)p->access(off_rtp_);
rh->seqno() = seqno_;
target_->recv(p);
}
idle();
} else {
finish();
// xxx: should we deschedule the timer here? */
};
}
void RTPAgent::timeout(int)
{
if (running_) {
sendpkt();
if (session_)
session_->localsrc_update(size_);
double t = interval_;
if (random_)
/* add some zero-mean white noise */
t += interval_ * Random::uniform(-0.5, 0.5);
rtp_timer_.resched(t);
}
}
/*
* finish() is called when we must stop (either by request or because
* we're out of packets to send.
*/
void RTPAgent::finish()
{
running_ = 0;
Tcl::instance().evalf("%s done", this->name());
}
void RTPAgent::advanceby(int delta)
{
maxpkts_ += delta;
if (seqno_ < maxpkts_ && !running_)
start();
}
void RTPAgent::recv(Packet* p, Handler*)
{
if (session_)
session_->recv(p, 0);
else
Packet::free(p);
}
int RTPAgent::command(int argc, const char*const* argv)
{
if (argc == 2) {
if (strcmp(argv[1], "rate-change") == 0) {
rate_change();
return (TCL_OK);
} else if (strcmp(argv[1], "start") == 0) {
start();
return (TCL_OK);
} else if (strcmp(argv[1], "stop") == 0) {
stop();
return (TCL_OK);
}
} else if (argc == 3) {
if (strcmp(argv[1], "session") == 0) {
session_ = (RTPSession*)TclObject::lookup(argv[2]);
return (TCL_OK);
} else if (strcmp(argv[1], "advance") == 0) {
int newseq = atoi(argv[2]);
advanceby(newseq - seqno_);
return (TCL_OK);
} else if (strcmp(argv[1], "advanceby") == 0) {
advanceby(atoi(argv[2]));
return (TCL_OK);
}
}
return (Agent::command(argc, argv));
}
/*
* We modify the rate in this way to get a faster reaction to the a rate
* change since a rate change from a very low rate to a very fast rate may
* take an undesireably long time if we have to wait for timeout at the old
* rate before we can send at the new (faster) rate.
*/
void RTPAgent::rate_change()
{
rtp_timer_.force_cancel();
double t = lastpkttime_ + interval_;
double now = Scheduler::instance().clock();
if ( t > now)
rtp_timer_.resched(t - now);
else {
sendpkt();
rtp_timer_.resched(interval_);
}
}
void RTPAgent::sendpkt()
{
Packet* p = allocpkt();
lastpkttime_ = Scheduler::instance().clock();
makepkt(p);
target_->recv(p, (Handler*)0);
}
void RTPAgent::makepkt(Packet* p)
{
hdr_rtp *rh = (hdr_rtp*)p->access(off_rtp_);
/* Fill in srcid_ and seqno */
rh->seqno() = seqno_++;
rh->srcid() = session_ ? session_->srcid() : 0;
}
void RTPTimer::expire(Event* /*e*/) {
a_->timeout(0);
}
rtqueue.cc
#include
#include
#include
#define CURRENT_TIME Scheduler::instance().clock()
#define DEBUG
/* ======================================================================
Packet Queue used by TORA and AODV.
===================================================================== */
rtqueue::rtqueue()
{
head_ = tail_ = 0;
len_ = 0;
limit_ = RTQ_MAX_LEN;
timeout_ = RTQ_TIMEOUT;
}
void
rtqueue::enque(Packet *p)
{
struct hdr_cmn *ch = HDR_CMN(p);
p->next_ = 0;
ch->ts_ = CURRENT_TIME + timeout_;
if (len_ == limit_) {
Packet *p0 = remove_head(); // decrements len_
assert(p0);
if(HDR_CMN(p0)->ts_ > CURRENT_TIME) {
drop(p0, DROP_RTR_QFULL);
}
else {
drop(p0, DROP_RTR_QTIMEOUT);
}
}
if(head_ == 0) {
head_ = tail_ = p;
}
else {
tail_->next_ = p;
tail_ = p;
}
len_++;
#ifdef DEBUG
verifyQueue();
#endif
}
Packet*
rtqueue::deque()
{
Packet *p;
/*
* Purge any packets that have timed out.
*/
purge();
p = remove_head();
#ifdef DEBUG
verifyQueue();
#endif
return p;
}
Packet*
rtqueue::deque(nsaddr_t dst)
{
Packet *p, *prev;
/*
* Purge any packets that have timed out.
*/
purge();
findPacketWithDst(dst, p, prev);
assert(p == 0 || (p == head_ && prev == 0) || (prev->next_ == p));
if(p == 0) return 0;
if (p == head_) {
p = remove_head();
}
else if (p == tail_) {
prev->next_ = 0;
tail_ = prev;
len_--;
}
else {
prev->next_ = p->next_;
len_--;
}
#ifdef DEBUG
verifyQueue();
#endif
return p;
}
char
rtqueue::find(nsaddr_t dst)
{
Packet *p, *prev;
findPacketWithDst(dst, p, prev);
if (0 == p)
return 0;
else
return 1;
}
/* ======================================================================
Private Routines
====================================================================== */
Packet*
rtqueue::remove_head()
{
Packet *p = head_;
if(head_ == tail_) {
head_ = tail_ = 0;
}
else {
head_ = head_->next_;
}
if(p) len_--;
return p;
}
void
rtqueue::purge()
{
Packet *p;
while((p = head_) && HDR_CMN(p)->ts_ < CURRENT_TIME) {
assert(p == remove_head());
drop(p, DROP_RTR_QTIMEOUT);
}
}
void
rtqueue::findPacketWithDst(nsaddr_t dst, Packet*& p, Packet*& prev)
{
p = prev = 0;
for(p = head_; p; p = p->next_) {
// if(HDR_IP(p)->dst() == dst) {
if(HDR_IP(p)->daddr() == dst) {
return;
}
prev = p;
}
if(p == 0) prev = 0; // not found
}
void
rtqueue::verifyQueue()
{
Packet *p, *prev = 0;
int cnt = 0;
for(p = head_; p; p = p->next_) {
cnt++;
prev = p;
}
assert(cnt == len_);
assert(prev == tail_);
}
rttable.cc
/* The AODV code developed by the CMU/MONARCH group was optimized
* and tuned by Samir Das (UTSA) and Mahesh Marina (UTSA). The
* work was partially done in Sun Microsystems.
*
* The original CMU copyright is below.
*/
/*
Copyright (c) 1997, 1998 Carnegie Mellon University. All Rights
Reserved.
Permission to use, copy, modify, and distribute this
software and its documentation is hereby granted (including for
commercial or for-profit use), provided that both the copyright notice
and this permission notice appear in all copies of the software,
derivative works, or modified versions, and any portions thereof, and
that both notices appear in supporting documentation, and that credit
is given to Carnegie Mellon University in all publications reporting
on direct or indirect use of this code or its derivatives.
ALL CODE, SOFTWARE, PROTOCOLS, AND ARCHITECTURES DEVELOPED BY THE CMU
MONARCH PROJECT ARE EXPERIMENTAL AND ARE KNOWN TO HAVE BUGS, SOME OF
WHICH MAY HAVE SERIOUS CONSEQUENCES. CARNEGIE MELLON PROVIDES THIS
SOFTWARE OR OTHER INTELLECTUAL PROPERTY IN ITS ``AS IS'' CONDITION,
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 CARNEGIE MELLON UNIVERSITY
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 OR
INTELLECTUAL PROPERTY, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
Carnegie Mellon encourages (but does not require) users of this
software or intellectual property to return any improvements or
extensions that they make, and to grant Carnegie Mellon the rights to
redistribute these changes without encumbrance.
*/
/* ported into VINT ns by Ya Xu, Sept. 1999 */
#include
/* =====================================================================
The Routing Table
===================================================================== */
rt_entry::rt_entry()
{
int i;
rt_dst = 0;
rt_seqno = 0;
rt_nexthop = 0;
rt_expire = 0.0;
rt_hops = INFINITY2;
rt_flags = RTF_DOWN;
rt_errors = 0;
rt_error_time = 0.0;
rt_req_timeout = 0.0;
rt_req_cnt = 0;
rt_req_last_ttl = 0;
hist_indx = 0;
for (i=0; i < MAX_HISTORY; i++) {
// rt_length[i] = 0;
rt_disc_latency[i] = 0.0;
}
error_propagate_counter = 0;
LIST_INIT(&rt_nblist);
};
rt_entry::~rt_entry()
{
Neighbor *nb;
while((nb = rt_nblist.lh_first)) {
LIST_REMOVE(nb, nb_link);
delete nb;
}
}
void
rt_entry::nb_insert(nsaddr_t id)
{
Neighbor *nb = new Neighbor(id);
assert(nb);
nb->nb_expire = 0;
LIST_INSERT_HEAD(&rt_nblist, nb, nb_link);
}
Neighbor*
rt_entry::nb_lookup(nsaddr_t id)
{
Neighbor *nb = rt_nblist.lh_first;
for(; nb; nb = nb->nb_link.le_next) {
if(nb->nb_addr == id)
break;
}
return nb;
}
/* =====================================================================
The Routing Table
===================================================================== */
rt_entry*
rttable::rt_lookup(nsaddr_t id)
{
rt_entry *rt = rthead.lh_first;
for(; rt; rt = rt->rt_link.le_next) {
if(rt->rt_dst == id)
break;
}
return rt;
}
void
rttable::rt_delete(nsaddr_t id)
{
rt_entry *rt = rt_lookup(id);
if(rt) {
LIST_REMOVE(rt, rt_link);
delete rt;
}
}
rt_entry*
rttable::rt_add(nsaddr_t id)
{
rt_entry *rt;
assert(rt_lookup(id) == 0);
rt = new rt_entry;
assert(rt);
rt->rt_dst = id;
LIST_INSERT_HEAD(&rthead, rt, rt_link);
return rt;
}
sa.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) Xerox Corporation 1997. All rights reserved.
*
* License is granted to copy, to use, and to make and to use derivative
* works for research and evaluation purposes, provided that Xerox is
* acknowledged in all documentation pertaining to any such copy or
* derivative work. Xerox grants no other licenses expressed or
* implied. The Xerox trade name should not be used in any advertising
* without its written permission.
*
* XEROX CORPORATION MAKES NO REPRESENTATIONS CONCERNING EITHER THE
* MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE
* FOR ANY PARTICULAR PURPOSE. The software is provided "as is" without
* express or implied warranty of any kind.
*
* These notices must be retained in any copies of any part of this
* software.
*/
//packets after it succeeds in a3-way handshake from the receiver
// should be connected with Agent/SignalAck class
#include "udp.h"
#include "sa.h"
#include "ip.h"
#include "random.h"
#define SAMPLERATE 8000
SA_Agent::SA_Agent() : Agent(PT_UDP), trafgen_(0), rtd_(0), callback_(0),
sa_timer_(this), nextPkttime_(-1), running_(0), seqno_(-1)
{
bind ("off_resv_",&off_resv_);
bind("off_rtp_", &off_rtp_);
bind_bw("rate_",&rate_);
bind("bucket_",&bucket_);
bind("packetSize_", &size_);
}
SA_Agent::~SA_Agent()
{
if (callback_)
delete[] callback_;
}
static class SA_AgentClass : public TclClass {
public:
SA_AgentClass() : TclClass("Agent/SA") {}
TclObject* create(int, const char*const*) {
return (new SA_Agent());
}
} class_signalsource_agent;
int SA_Agent::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
if (argc==3) {
if (strcmp(argv[1], "target") == 0) {
target_ = (NsObject*)TclObject::lookup(argv[2]);
if (target_ == 0) {
tcl.resultf("no such object %s", argv[2]);
return (TCL_ERROR);
}
ctrl_target_=target_;
return (TCL_OK);
}
else if (strcmp(argv[1],"ctrl-target")== 0) {
ctrl_target_=(NsObject*)TclObject::lookup(argv[2]);
if (ctrl_target_ == 0) {
tcl.resultf("no such object %s", argv[2]);
return (TCL_ERROR);
}
return (TCL_OK);
}
if (strcmp(argv[1], "stoponidle") == 0) {
stoponidle(argv[2]);
return(TCL_OK);
}
if (strcmp(argv[1], "attach-traffic") == 0) {
trafgen_ =(TrafficGenerator*)TclObject::lookup(argv[2]);
if (trafgen_ == 0) {
tcl.resultf("no such node %s", argv[2]);
return(TCL_ERROR);
}
return(TCL_OK);
}
}
if (argc == 2) {
if (strcmp(argv[1], "start") == 0) {
start();
return(TCL_OK);
} else if (strcmp(argv[1], "stop") == 0) {
stop();
return(TCL_OK);
}
}
return (Agent::command(argc,argv));
}
void SA_Agent::start()
{
//send the request packet
if (trafgen_) {
trafgen_->init();
//running_=1;
sendreq();
}
}
void SA_Agent::stop()
{
sendteardown();
if (running_ != 0) {
sa_timer_.cancel();
running_ =0;
}
}
void SA_Agent::sendreq()
{
Packet *p = allocpkt();
hdr_cmn* ch= (hdr_cmn*)p->access(off_cmn_);
ch->ptype()=PT_REQUEST;
ch->size()=20;
//also put in the r,b parameters for the flow in the packet
hdr_resv* rv=(hdr_resv*)p->access(off_resv_);
rv->decision() =1;
rv->rate()=rate_;
rv->bucket()=bucket_;
ctrl_target_->recv(p);
}
void SA_Agent::sendteardown()
{
Packet *p = allocpkt();
hdr_cmn* ch= (hdr_cmn*)p->access(off_cmn_);
ch->ptype()=PT_TEARDOWN;
ch->size()=20;
//also put in the r,b parameters for the flow in the packet
hdr_resv* rv=(hdr_resv*)p->access(off_resv_);
rv->decision() =1;
rv->rate()=rate_;
rv->bucket()=bucket_;
ctrl_target_->recv(p);
}
void SA_Agent::recv(Packet *p, Handler *)
{
hdr_cmn *ch= (hdr_cmn *)p->access(off_cmn_);
hdr_resv *rv=(hdr_resv *)p->access(off_resv_);
hdr_ip * iph = (hdr_ip*)p->access(off_ip_);
if ( ch->ptype() == PT_ACCEPT || ch->ptype() == PT_REJECT ) {
ch->ptype() = PT_CONFIRM;
// turn the packet around by swapping src and dst
// (address and port)
ns_addr_t tmp;
tmp = iph->src();
iph->src() = iph->dst();
iph->dst() = tmp;
ctrl_target_->recv(p);
}
// put an additional check here to see if admission was granted
if (rv->decision()) {
//printf("Flow %d accepted @ %f\n",iph->flowid(),Scheduler::instance().clock());
fflush(stdout);
double t = trafgen_->next_interval(size_);
running_=1;
sa_timer_.resched(t);
}
else {
//printf("Flow %d rejected @ %f\n",iph->flowid(),Scheduler::instance().clock());
fflush(stdout);
//Currently the flow is stopped if rejected
running_=0;
}
//make an upcall to sched a stoptime for this flow from now
Tcl::instance().evalf("%s sched-stop %d",name(),rv->decision());
}
void SA_Agent::stoponidle(const char *s)
{
callback_ = new char[strlen(s)+1];
strcpy(callback_, s);
if (trafgen_->on()) {
// Tcl::instance().evalf("puts \"%s waiting for burst at %f\"", name(), Scheduler::instance().clock());
rtd_ = 1;
}
else {
stop();
Tcl::instance().evalf("%s %s", name(), callback_);
}
}
void SA_Timer::expire(Event* /*e*/) {
a_->timeout(0);
}
void SA_Agent::timeout(int)
{
if (running_) {
/* send a packet */
sendpkt();
/* figure out when to send the next one */
nextPkttime_ = trafgen_->next_interval(size_);
/* schedule it */
sa_timer_.resched(nextPkttime_);
/* hack: if we are waiting for a current burst to end
* before stopping . . .
*/
if (rtd_) {
if (trafgen_->on() == 0) {
stop();
//Tcl::instance().evalf("puts \"%s burst over at %f\"",
// name(), Scheduler::instance().clock());
Tcl::instance().evalf("%s sched-stop %d", name(), 0);
}
}
}
}
void SA_Agent::sendpkt()
{
Packet* p = allocpkt();
hdr_rtp* rh = (hdr_rtp*)p->access(off_rtp_);
rh->seqno() = ++seqno_;
rh->flags()=0;
double local_time=Scheduler::instance().clock();
/*put in "rtp timestamps" and begining of talkspurt labels */
hdr_cmn* ch = (hdr_cmn*)p->access(off_cmn_);
ch->timestamp()=(u_int32_t)(SAMPLERATE*local_time);
ch->size()=size_;
if ((nextPkttime_ != trafgen_->interval()) || (nextPkttime_ == -1))
rh->flags() |= RTP_M;
target_->recv(p);
}
void SA_Agent::sendmsg(int nbytes, const char* /*flags*/)
{
Packet *p;
int n;
if (size_)
n = nbytes / size_;
else
printf("Error: SA_Agent size = 0\n");
if (nbytes == -1) {
start();
return;
}
while (n-- > 0) {
p = allocpkt();
hdr_rtp* rh = (hdr_rtp*)p->access(off_rtp_);
rh->seqno() = seqno_;
target_->recv(p);
}
n = nbytes % size_;
if (n > 0) {
p = allocpkt();
hdr_rtp* rh = (hdr_rtp*)p->access(off_rtp_);
rh->seqno() = seqno_;
target_->recv(p);
}
idle();
}
saack.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) Xerox Corporation 1997. All rights reserved.
*
* License is granted to copy, to use, and to make and to use derivative
* works for research and evaluation purposes, provided that Xerox is
* acknowledged in all documentation pertaining to any such copy or
* derivative work. Xerox grants no other licenses expressed or
* implied. The Xerox trade name should not be used in any advertising
* without its written permission.
*
* XEROX CORPORATION MAKES NO REPRESENTATIONS CONCERNING EITHER THE
* MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE
* FOR ANY PARTICULAR PURPOSE. The software is provided "as is" without
* express or implied warranty of any kind.
*
* These notices must be retained in any copies of any part of this
* software.
*/
//SignalAckClass
//Null receiver agent which merely acknowledges a request
#include "agent.h"
#include "ip.h"
#include "resv.h"
class SAack_Agent : public Agent {
public:
SAack_Agent();
protected:
void recv(Packet *, Handler *);
int command(int,const char* const*);
int off_resv_;
};
SAack_Agent::SAack_Agent(): Agent(PT_TCP)
{
bind("off_resv_",&off_resv_);
}
static class SAackClass : public TclClass {
public:
SAackClass() : TclClass("Agent/SAack") {}
TclObject* create(int, const char*const*) {
return (new SAack_Agent());
}
} class_saack;
void SAack_Agent::recv(Packet *p, Handler *h)
{
hdr_cmn *ch = (hdr_cmn*)p->access(off_cmn_);
if (ch->ptype() == PT_REQUEST) {
Packet *newp =allocpkt();
hdr_cmn *newch=(hdr_cmn*)newp->access(off_cmn_);
newch->size()=ch->size();
// turn the packet around by swapping src and dst
hdr_ip * iph = (hdr_ip*)p->access(off_ip_);
hdr_ip * newiph = (hdr_ip*)newp->access(off_ip_);
newiph->dst()=iph->src();
newiph->flowid()=iph->flowid();
newiph->prio()=iph->prio();
hdr_resv* rv=(hdr_resv*)p->access(off_resv_);
hdr_resv* newrv=(hdr_resv*)newp->access(off_resv_);
newrv->decision()=rv->decision();
newrv->rate()=rv->rate();
newrv->bucket()=rv->bucket();
if (rv->decision())
newch->ptype() = PT_ACCEPT;
else
newch->ptype() = PT_REJECT;
target_->recv(newp);
Packet::free(p);
return;
}
Agent::recv(p,h);
}
int SAack_Agent::command(int argc, const char*const* argv)
{
return (Agent::command(argc,argv));
}
salink.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) Xerox Corporation 1997. All rights reserved.
*
* License is granted to copy, to use, and to make and to use derivative
* works for research and evaluation purposes, provided that Xerox is
* acknowledged in all documentation pertaining to any such copy or
* derivative work. Xerox grants no other licenses expressed or
* implied. The Xerox trade name should not be used in any advertising
* without its written permission.
*
* XEROX CORPORATION MAKES NO REPRESENTATIONS CONCERNING EITHER THE
* MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE
* FOR ANY PARTICULAR PURPOSE. The software is provided "as is" without
* express or implied warranty of any kind.
*
* These notices must be retained in any copies of any part of this
* software.
*/
#include "packet.h"
#include "ip.h"
#include "resv.h"
#include "connector.h"
#include "adc.h"
#include "salink.h"
static class SALinkClass : public TclClass {
public:
SALinkClass() : TclClass("SALink") {}
TclObject* create(int, const char*const*) {
return (new SALink());
}
}class_salink;
SALink::SALink() : adc_(0), numfl_(-1), tchan_(0), onumfl_(0), last_(-1)
{
int i;
for (i=0;iSALink::recv(Packet *p, Handler *h)
{
int decide;
int j;
hdr_cmn *ch=(hdr_cmn*)p->access(off_cmn_);
hdr_ip *iph=(hdr_ip*)p->access(off_ip_);
hdr_resv *rv=(hdr_resv*)p->access(off_resv_);
//CLEAN THIS UP
int cl=(iph->flowid())?1:0;
switch(ch->ptype()) {
case PT_REQUEST:
decide=adc_->admit_flow(cl,rv->rate(),rv->bucket());
if (tchan_)
if (last_ != decide) {
int n;
char wrk[50];
double t = Scheduler::instance().clock();
sprintf(wrk, "l -t %g -s %d -d %d -S COLOR -c %s",
t, src_, dst_, decide ? "MediumBlue" : "red" );
n = strlen(wrk);
wrk[n] = '\n';
wrk[n+1] = 0;
(void)Tcl_Write(tchan_, wrk, n+1);
last_ = decide;
}
//put decide in the packet
rv->decision() &= decide;
if (decide) {
j=get_nxt();
pending_[j].flowid=iph->flowid();
//pending_[j].status=decide;
numfl_++;
}
break;
case PT_ACCEPT:
case PT_REJECT:
break;
case PT_CONFIRM:
{
j=lookup(iph->flowid());
if (j!=-1) {
if (!rv->decision()) {
//decrease the avload for this class
adc_->rej_action(cl,rv->rate(),rv->bucket());
numfl_--;
}
pending_[j].flowid=-1;
}
break;
}
case PT_TEARDOWN:
{
adc_->teardown_action(cl,rv->rate(),rv->bucket());
numfl_--;
break;
}
default:
#ifdef notdef
error("unknown signalling message type : %d",ch->ptype());
abort();
#endif
break;
}
send(p,h);
}
int SALink::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
char wrk[500];
if (argc ==3) {
if (strcmp(argv[1],"attach-adc") == 0 ) {
adc_=(ADC *)TclObject::lookup(argv[2]);
if (adc_ ==0 ) {
tcl.resultf("no such node %s", argv[2]);
return(TCL_ERROR);
}
return(TCL_OK);
}
if (strcmp(argv[1], "attach") == 0) {
int mode;
const char* id = argv[2];
tchan_ = Tcl_GetChannel(tcl.interp(), (char*)id, &mode);
if (tchan_ == 0) {
tcl.resultf("SALink: trace: can't attach %s for writing", id);
return (TCL_ERROR);
}
return (TCL_OK);
}
}
if (argc == 2) {
if (strcmp(argv[1], "add-trace") == 0) {
if (tchan_) {
sprintf(wrk, "a -t * -n %s:%d-%d -s %d",
adc_->type(), src_, dst_, src_);
int n = strlen(wrk);
wrk[n] = '\n';
wrk[n+1] = 0;
(void)Tcl_Write(tchan_, wrk, n+1);
numfl_ = 0;
}
return (TCL_OK);
}
}
return Connector::command(argc,argv);
}
int SALink::lookup(int flowid)
{
int i;
for (i=0;iSALink::get_nxt()
{
int i;
for (i=0;iSALink::trace(TracedVar* v)
{
char wrk[500];
int *p, newval;
if (strcmp(v->name(), "\"Admitted Flows\"") == 0) {
p = &onumfl_;
}
else {
fprintf(stderr, "SALink: unknown trace var %s\n", v->name());
return;
}
newval = int(*((TracedInt*)v));
if (tchan_) {
int n;
double t = Scheduler::instance().clock();
/* f -t 0.0 -s 1 -a SA -T v -n Num -v 0 -o 0 */
sprintf(wrk, "f -t %g -s %d -a %s:%d-%d -T v -n %s -v %d -o %d",
t, src_, adc_->type(), src_, dst_, v->name(), newval, *p);
n = strlen(wrk);
wrk[n] = '\n';
wrk[n+1] = 0;
(void)Tcl_Write(tchan_, wrk, n+1);
}
*p = newval;
return;
}
satgeometry.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1999 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 MASH Research
* Group at the University of California Berkeley.
* 4. Neither the name of the University nor of the Research Group 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 Tom Henderson, UCB Daedalus Research Group, June 1999
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$";
#endif
#include "satgeometry.h"
#include "satposition.h"
static class SatGeometryClass : public TclClass {
public:
SatGeometryClass() : TclClass("SatGeometry") {}
TclObject* create(int, const char*const*) {
return (new SatGeometry());
}
} class_sat_geometry;
// Returns the distance in km between points a and b
double SatGeometry::distance(coordinate a, coordinate b)
{
double a_x, a_y, a_z, b_x, b_y, b_z; // cartesian
spherical_to_cartesian(a.r, a.theta, a.phi, a_x, a_y, a_z);
spherical_to_cartesian(b.r, b.theta, b.phi, b_x, b_y, b_z);
return (Trace::round(DISTANCE(a_x, a_y, a_z, b_x, b_y, b_z), 1.0E+8));
}
void SatGeometry::spherical_to_cartesian(double R, double Theta,
double Phi, double &X, double &Y, double &Z)
{
X = R * sin(Theta) * cos (Phi);
Y = R * sin(Theta) * sin (Phi);
Z = R * cos(Theta);
}
// Propagation delay is the distance divided by the speed of light
double SatGeometry::propdelay(coordinate a, coordinate b)
{
double delay = distance(a, b)/LIGHT;
return (Trace::round(delay, 1.0E+8));
}
double SatGeometry::get_altitude(coordinate a)
{
return (a.r - EARTH_RADIUS);
}
// Returns latitude in radians, in the range from -PI/2 to PI/2
double SatGeometry::get_latitude(coordinate a)
{
return (PI/2 - a.theta);
}
// Returns (earth-centric) longitude corresponding to the position of the node
// (the input coordinate corresponds to fixed coordinate system, through
// which the Earth rotates, so we have to scale back the effects of rotation).
// The return value ranges from -PI to PI.
double SatGeometry::get_longitude(coordinate coord_)
{
double period = EARTH_PERIOD; // period of earth in seconds
// adjust longitude so that it is earth-centric (i.e., account
// for earth rotating beneath).
double earth_longitude = fmod((coord_.phi -
(fmod(NOW + SatPosition::time_advance_,period)/period) * 2*PI),
2*PI);
// Bring earth_longitude to be within (-PI, PI)
if (earth_longitude < (-1*PI))
earth_longitude = 2*PI + earth_longitude;
if (earth_longitude > PI)
earth_longitude = (-(2*PI - earth_longitude));
if (fabs(earth_longitude) < 0.0001)
return 0; // To avoid trace output of "-0.00"
else
return (earth_longitude);
}
// If the satellite is above the elevation mask of the terminal, returns
// the elevation mask in radians; otherwise, returns 0.
double SatGeometry::check_elevation(coordinate satellite,
coordinate terminal, double elev_mask_)
{
double S = satellite.r; // satellite radius
double S_2 = satellite.r * satellite.r; // satellite radius^2
double E = EARTH_RADIUS;
double E_2 = E * E;
double d, theta, alpha;
d = distance(satellite, terminal);
if (d < sqrt(S_2 - E_2)) {
// elevation angle > 0
theta = acos((E_2+S_2-(d*d))/(2*E*S));
alpha = acos(sin(theta) * S/d);
return ( (alpha > elev_mask_) ? alpha : 0);
} else
return 0;
}
// This function determines whether two satellites are too far apart
// to establish an ISL between them, due to Earth atmospheric grazing
// (or shadowing by the Earth itself). Assumes that both satellites nodes
// are at the same altitude. The line between the two satellites can be
// bisected, and a perpendicular from that point to the Earth's center will
// form a right triangle. If the length of this perpendicular is less than
// EARTH_RADIUS + ATMOS_MARGIN, the link cannot be established.
//
int SatGeometry::are_satellites_mutually_visible(coordinate first, coordinate second)
{
// if we drop a perpendicular from the ISL to the Earth's surface,
// we have a right triangle. The atmospheric margin is the minimum
// ISL grazing altitude.
double c, d, min_radius, grazing_radius;
double radius = get_radius(first); // could just use first.r here.
double distance_ = distance(first, second);
c = radius * radius;
d = (distance_/2) * (distance_/2);
grazing_radius = (EARTH_RADIUS + ATMOS_MARGIN);
min_radius = sqrt(c - d);
if (min_radius >= grazing_radius) {
return TRUE;
} else {
return FALSE;
}
}
sathandoff.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1999 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 MASH Research
* Group at the University of California Berkeley.
* 4. Neither the name of the University nor of the Research Group 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 Tom Henderson, UCB Daedalus Research Group, June 1999
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$";
#endif
#include "random.h"
#include "sathandoff.h"
#include "satlink.h"
#include "satroute.h"
#include "satposition.h"
#include "satnode.h"
#include "satgeometry.h"
#include
static class LinkHandoffMgrClass : public TclClass {
public:
LinkHandoffMgrClass() : TclClass("HandoffManager") {}
TclObject* create(int, const char*const*) {
return (new LinkHandoffMgr());
}
} class_link_handoff_manager;
static class SatLinkHandoffMgrClass : public TclClass {
public:
SatLinkHandoffMgrClass() : TclClass("HandoffManager/Sat") {}
TclObject* create(int, const char*const*) {
return (new SatLinkHandoffMgr());
}
} class_sat_link_handoff_manager;
static class TermLinkHandoffMgrClass : public TclClass {
public:
TermLinkHandoffMgrClass() : TclClass("HandoffManager/Term") {}
TclObject* create(int, const char*const*) {
return (new TermLinkHandoffMgr());
}
} class_term_link_handoff_manager;
void SatHandoffTimer::expire(Event*)
{
a_->handoff();
}
void TermHandoffTimer::expire(Event*)
{
a_->handoff();
}
//////////////////////////////////////////////////////////////////////////////
// class LinkHandoffMgr
//////////////////////////////////////////////////////////////////////////////
RNG LinkHandoffMgr::handoff_rng_;
int LinkHandoffMgr::handoff_randomization_ = 0;
LinkHandoffMgr::LinkHandoffMgr()
{
bind_bool("handoff_randomization_", &handoff_randomization_);
}
int LinkHandoffMgr::command(int argc, const char*const* argv)
{
if (argc == 2) {
} else if (argc == 3) {
if(strcmp(argv[1], "setnode") == 0) {
node_ = (Node*) TclObject::lookup(argv[2]);
if (node_ == 0)
return TCL_ERROR;
return TCL_OK;
}
}
return (TclObject::command(argc, argv));
}
// Each crossseam satellite will have two net stacks-- at most one will
// be occupied. This procedure finds an unoccupied stack on the node.
SatLinkHead* LinkHandoffMgr::get_peer_next_linkhead(SatNode* np)
{
LinkHead* lhp;
SatLinkHead* slhp;
for (lhp = np->linklisthead_.lh_first; lhp;
lhp = lhp->nextlinkhead() ) {
slhp = (SatLinkHead*) lhp;
if (slhp->type() == LINK_ISL_CROSSSEAM) {
if (!slhp->phy_tx()->channel() &&
!slhp->phy_rx()->channel() )
return slhp;
}
}
printf("Error, couldn't find an empty crossseam stack for handoff\n");
return 0;
}
// This helper function assumes that the channel to which the link interface
// is attached has one peer node (i.e., no other receive infs on channel)
SatLinkHead* LinkHandoffMgr::get_peer_linkhead(SatLinkHead* slhp)
{
SatChannel *schan_;
Phy *remote_phy_;
Node *remote_node_;
schan_ = (SatChannel*) slhp->phy_tx()->channel();
if (schan_ == 0) {
printf("Error: get_peer_linkhead called for a non-");
printf("connected link on node %d\n", slhp->node()->address());
return 0; // Link is not currently connected
}
remote_phy_ = schan_->ifhead_.lh_first;
if (remote_phy_ == 0) {
printf("Error: node %d's tx phy ", slhp->node()->address());
printf("connected to channel with no receivers\n");
return 0;
}
remote_node_ = remote_phy_->head()->node();
if (remote_phy_->nextchnl()) {
printf("Error: This ISL channel has more than one target\n");
return 0;
}
return ( (SatLinkHead*) remote_phy_->head());
}
// This helper function assumes that the channel to which the link interface
// is attached has one peer node (i.e., no other receive infs on channel)
SatNode* LinkHandoffMgr::get_peer(SatLinkHead* slhp)
{
SatChannel *schan_;
Phy *remote_phy_;
schan_ = (SatChannel*) slhp->phy_tx()->channel();
if (schan_ == 0)
return 0; // Link is not currently connected
remote_phy_ = schan_->ifhead_.lh_first;
if (remote_phy_ == 0) {
printf("Error: node %d's tx phy ", slhp->node()->address());
printf("connected to channel with no receivers\n");
return 0;
}
if (remote_phy_->nextchnl()) {
printf("Error: This ISL channel has more than one target\n");
return 0;
}
return ( (SatNode*) remote_phy_->head()->node());
}
//////////////////////////////////////////////////////////////////////////
// class TermLinkHandoffMgr
//////////////////////////////////////////////////////////////////////////
double TermLinkHandoffMgr::elevation_mask_ = 0;
int TermLinkHandoffMgr::term_handoff_int_ = 10;
TermLinkHandoffMgr::TermLinkHandoffMgr() : timer_(this)
{
bind("elevation_mask_", &elevation_mask_);
bind("term_handoff_int_", &term_handoff_int_);
}
//
// This is called each time the node checks to see if its link to a
// polar satellite needs to be handed off.
// There are two cases:
// i) current link is up; check to see if it stays up or is handed off
// ii) current link is down; check to see if it can go up
// If there are any changes, call for rerouting. Finally, restart the timer.
//
int TermLinkHandoffMgr::handoff()
{
coordinate sat_coord, earth_coord;
SatLinkHead* slhp;
SatNode *peer_; // Polar satellite at opposite end of the GSL
SatNode *best_peer_; // Best found peer for handoff
Node *nodep; // Pointer used in searching the list of nodes
PolarSatPosition *nextpos_;
int link_changes_flag_ = FALSE; // Flag indicating change took place
int restart_timer_flag_ = FALSE; // Restart timer only if polar links
double found_elev_ = 0; //``Flag'' indicates whether handoff can occur
double best_found_elev_ = 0;
double mask_ = DEG_TO_RAD(TermLinkHandoffMgr::elevation_mask_);
earth_coord = ((SatNode *)node_)->position()->coord();
// Traverse the linked list of link interfaces
for (slhp = (SatLinkHead*) node_->linklisthead_.lh_first; slhp;
slhp = (SatLinkHead*) slhp->nextlinkhead() ) {
if (slhp->type() == LINK_GSL_GEO ||
slhp->type() == LINK_GENERIC)
continue;
if (slhp->type() != LINK_GSL_POLAR) {
printf("Error: Terminal link type ");
printf("not valid %d NOW %f\n", slhp->type(), NOW);
exit(1);
}
// The link is a GSL_POLAR link-- should be one receive
// interface on it
restart_timer_flag_ = TRUE;
peer_ = get_peer(slhp);
if (peer_) {
sat_coord = peer_->position()->coord();
if (!SatGeometry::check_elevation(sat_coord,
earth_coord, mask_) && slhp->linkup_) {
slhp->linkup_ = FALSE;
link_changes_flag_ = TRUE;
// Detach receive link interface from channel
slhp->phy_rx()->removechnl();
// Set channel pointers to NULL
slhp->phy_tx()->setchnl(0);
slhp->phy_rx()->setchnl(0);
}
}
if (!slhp->linkup_) {
// If link is down, see if we can use another satellite
//
// As an optimization, first check the next satellite
// coming over the horizon. Next, consider all
// remaining satellites.
//
if (peer_) {
// Next satellite
nextpos_ = ((PolarSatPosition*)
peer_->position())->next();
if (nextpos_) {
sat_coord = nextpos_->coord();
found_elev_ = SatGeometry::check_elevation(sat_coord, earth_coord, mask_);
if (found_elev_)
peer_ = (SatNode*) nextpos_->node();
}
}
// Next, check all remaining satellites if not found
if (!found_elev_) {
for (nodep=Node::nodehead_.lh_first; nodep;
nodep = nodep->nextnode()) {
peer_ = (SatNode*) nodep;
if (peer_->position() &&
(peer_->position()->type() !=
POSITION_SAT_POLAR))
continue;
sat_coord =
peer_->position()->coord();
found_elev_ = SatGeometry::check_elevation(sat_coord, earth_coord, mask_);
if (found_elev_ > best_found_elev_) {
best_peer_ = peer_;
best_found_elev_ = found_elev_;
}
}
if (best_found_elev_) {
peer_ = best_peer_;
found_elev_ = best_found_elev_;
}
}
if (found_elev_) {
slhp->linkup_ = TRUE;
link_changes_flag_ = TRUE;
// Point slhp->phy_tx to peer_'s inlink
slhp->phy_tx()->setchnl(peer_->uplink());
// Point slhp->phy_rx to peer_'s outlink and
// add phy_rx to the channels list of phy's
slhp->phy_rx()->setchnl(peer_->downlink());
slhp->phy_rx()->insertchnl(&(peer_->downlink()->ifhead_));
}
}
}
if (link_changes_flag_) {
SatRouteObject::instance().recompute();
}
if (restart_timer_flag_) {
// If we don't have polar GSLs, don't reset the timer
if (handoff_randomization_) {
timer_.resched(term_handoff_int_ +
handoff_rng_.uniform(-1 * term_handoff_int_/2,
term_handoff_int_/2));
} else
timer_.resched(term_handoff_int_);
}
return link_changes_flag_;
}
//////////////////////////////////////////////////////////////////////////
// class SatLinkHandoffMgr
//////////////////////////////////////////////////////////////////////////
double SatLinkHandoffMgr::latitude_threshold_ = 0;
double SatLinkHandoffMgr::longitude_threshold_ = 0;
int SatLinkHandoffMgr::sat_handoff_int_ = 10;
SatLinkHandoffMgr::SatLinkHandoffMgr() : timer_(this)
{
bind("sat_handoff_int_", &sat_handoff_int_);
bind("latitude_threshold_", &latitude_threshold_);
bind("longitude_threshold_", &longitude_threshold_);
}
//
// This function is responsible for activating, deactivating, and handing off
// satellite ISLs. If the ISL is an intraplane link,
// do nothing. If the ISL is an interplane link, it will be taken down
// when _either_ of the connected satellites are above lat_threshold_
// degrees, and brought back up when _both_ satellites move below
// lat_threshold_ again. If an ISL is a cross-seam link, it must also be
// handed off periodically while the satellite is below lat_threshold_.
//
// Finally, optimizations that avoid going through the linked lists unless
// the satellite is ``close'' to lat_threshold_ are employed.
//
int SatLinkHandoffMgr::handoff()
{
SatLinkHead *slhp, *peer_slhp, *peer_next_slhp;
SatNode *local_, *peer_, *peer_next_;
PolarSatPosition *pos_, *peer_pos_, *peer_next_pos_;
double dist_to_peer, dist_to_next;
Channel *tx_channel_, *rx_channel_;
double sat_latitude_, sat_longitude_, peer_latitude_, peer_longitude_;
int link_down_flag_;
double lat_threshold_ = DEG_TO_RAD(latitude_threshold_);
double cross_long_threshold_ = DEG_TO_RAD(longitude_threshold_);
int link_changes_flag_ = FALSE; // Flag indicating change took place
coordinate local_coord_, peer_coord_;
local_ = (SatNode*) node_;
local_coord_ = local_->position()->coord();
sat_latitude_ = SatGeometry::get_latitude(local_->position()->coord());
sat_longitude_= SatGeometry::get_longitude(local_->position()->coord());
// First go through crossseam ISLs to search for handoffs
for (slhp = (SatLinkHead*) local_->linklisthead_.lh_first; slhp;
slhp = (SatLinkHead*) slhp->nextlinkhead() ) {
if (slhp->type() != LINK_ISL_CROSSSEAM)
continue;
peer_ = get_peer(slhp);
if (peer_ == 0)
continue; // this link interface is not attached
// If this is a crossseam link, first see if the link must
// be physically handed off to the next satellite.
// Handoff is needed if the satellite at the other end of
// the link is further away than the ``next'' satellite
// in the peer's orbital plane.
pos_ = (PolarSatPosition*)slhp->node()->position();
peer_slhp = get_peer_linkhead(slhp);
peer_pos_ = (PolarSatPosition*) peer_->position();
peer_coord_ = peer_pos_->coord();
if (fabs(sat_latitude_) > lat_threshold_)
link_down_flag_ = TRUE;
else
link_down_flag_ = FALSE;
if (peer_pos_->plane() < pos_->plane()) {
// Crossseam handoff is controlled by satellites
// in the plane with a lower index
break;
}
peer_next_pos_ = peer_pos_->next();
if (!peer_next_pos_) {
printf("Error: crossseam handoffs require ");
printf("setting the ``next'' field\n");
exit(1);
}
peer_next_ = (SatNode*) peer_next_pos_->node();
dist_to_peer = SatGeometry::distance(peer_coord_, local_coord_);
dist_to_next = SatGeometry::distance(peer_next_pos_->coord(),
local_coord_);
if (dist_to_next < dist_to_peer) {
// Handoff -- the "next" satellite should have a
// currently unused network stack. Find this
// unused stack and handoff the channels to it.
//
// Remove peer's tx/rx interface from channel
peer_slhp->phy_rx()->removechnl();
peer_slhp->phy_tx()->setchnl(0);
peer_slhp->phy_rx()->setchnl(0);
// Add peer_next's tx/rx interfaces to our channels
peer_next_slhp = get_peer_next_linkhead(peer_next_);
tx_channel_ = slhp->phy_tx()->channel();
rx_channel_ = slhp->phy_rx()->channel();
peer_next_slhp->phy_tx()->setchnl(rx_channel_);
peer_next_slhp->phy_rx()->setchnl(tx_channel_);
peer_next_slhp->phy_rx()->insertchnl(&(tx_channel_->ifhead_));
link_changes_flag_ = TRUE;
// Now reset the peer_ variables to point to next
peer_ = peer_next_;
peer_slhp = peer_next_slhp;
peer_coord_ = peer_->position()->coord();
}
// Next, see if the link needs to be taken down.
peer_latitude_ =
SatGeometry::get_latitude(peer_coord_);
peer_longitude_ = SatGeometry::get_longitude(peer_coord_);
if (fabs(peer_latitude_) > lat_threshold_)
link_down_flag_ = TRUE;
// If the two satellites are too close to each other in
// longitude, the link should be down
if ((fabs(peer_longitude_ - sat_longitude_) <
cross_long_threshold_) ||
fabs(peer_longitude_ - sat_longitude_) >
(2 * PI - cross_long_threshold_))
link_down_flag_ = TRUE;
// Check to see if link grazes atmosphere at an altitude
// below the atmospheric margin
link_down_flag_ |= !(SatGeometry::are_satellites_mutually_visible(peer_coord_, local_coord_));
// Evaluate whether a change in link status is needed
if ((slhp->linkup_ || peer_slhp->linkup_) && link_down_flag_) {
slhp->linkup_ = FALSE;
peer_slhp->linkup_ = FALSE;
link_changes_flag_ = TRUE;
} else if ((!slhp->linkup_ || !peer_slhp->linkup_) &&
!link_down_flag_) {
slhp->linkup_ = TRUE;
peer_slhp->linkup_ = TRUE;
link_changes_flag_ = TRUE;
}
}
// Now, work on interplane ISLs (intraplane ISLs are not handed off)
// Now search for interplane ISLs
for (slhp = (SatLinkHead*) local_->linklisthead_.lh_first; slhp;
slhp = (SatLinkHead*) slhp->nextlinkhead() ) {
if (slhp->type() != LINK_ISL_INTERPLANE)
continue;
if (fabs(sat_latitude_) > lat_threshold_)
link_down_flag_ = TRUE;
else
link_down_flag_ = FALSE;
peer_ = get_peer(slhp);
peer_slhp = get_peer_linkhead(slhp);
peer_coord_ = peer_->position()->coord();
peer_latitude_ = SatGeometry::get_latitude(peer_coord_);
if (fabs(peer_latitude_) > lat_threshold_)
link_down_flag_ = TRUE;
link_down_flag_ |= !(SatGeometry::are_satellites_mutually_visible(peer_coord_, local_coord_));
if (slhp->linkup_ && link_down_flag_) {
// Take links down if either satellite at high latitude
slhp->linkup_ = FALSE;
peer_slhp->linkup_ = FALSE;
link_changes_flag_ = TRUE;
} else if (!slhp->linkup_ && !link_down_flag_) {
slhp->linkup_ = TRUE;
peer_slhp->linkup_ = TRUE;
link_changes_flag_ = TRUE;
}
}
if (link_changes_flag_) {
SatRouteObject::instance().recompute();
}
if (handoff_randomization_) {
timer_.resched(sat_handoff_int_ +
handoff_rng_.uniform(-1 * sat_handoff_int_/2,
sat_handoff_int_/2));
} else
timer_.resched(sat_handoff_int_);
return link_changes_flag_;
}
satlink.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1999 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 MASH Research
* Group at the University of California Berkeley.
* 4. Neither the name of the University nor of the Research Group 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 Tom Henderson, UCB Daedalus Research Group, June 1999
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$";
#endif
/*
* Contains source code for:
* SatLinkHead
* SatLL
* SatMac
* SatPhy
* SatChannel
*/
#include "satlink.h"
#include "sattrace.h"
#include "satposition.h"
#include "satgeometry.h"
#include "satnode.h"
#include "errmodel.h"
/*==========================================================================*/
/*
* _SatLinkHead
*/
static class SatLinkHeadClass : public TclClass {
public:
SatLinkHeadClass() : TclClass("Connector/LinkHead/Sat") {}
TclObject* create(int, const char*const*) {
return (new SatLinkHead);
}
} class_sat_link_head;
SatLinkHead::SatLinkHead() : linkup_(1), phy_tx_(0), phy_rx_(0), mac_(0), satll_(0), queue_(0), errmodel_(0)
{
}
int SatLinkHead::command(int argc, const char*const* argv)
{
if (argc == 2) {
} else if (argc == 3) {
if (strcmp(argv[1], "set_type") == 0) {
if (strcmp(argv[2], "geo") == 0) {
type_ = LINK_GSL_GEO;
return TCL_OK;
} else if (strcmp(argv[2], "polar") == 0) {
type_ = LINK_GSL_POLAR;
return TCL_OK;
} else if (strcmp(argv[2], "gsl") == 0) {
type_ = LINK_GSL;
return TCL_OK;
} else if (strcmp(argv[2], "gsl-repeater") == 0) {
type_ = LINK_GSL_REPEATER;
return TCL_OK;
} else if (strcmp(argv[2], "interplane") == 0) {
type_ = LINK_ISL_INTERPLANE;
return TCL_OK;
} else if (strcmp(argv[2], "intraplane") == 0) {
type_ = LINK_ISL_INTRAPLANE;
return TCL_OK;
} else if (strcmp(argv[2], "crossseam") == 0) {
type_ = LINK_ISL_CROSSSEAM;
return TCL_OK;
} else {
printf("Unknown link type: %s\n", argv[2]);
exit(1);
}
}
if (strcmp(argv[1], "setll") == 0) {
satll_ = (SatLL*) TclObject::lookup(argv[2]);
if (satll_ == 0)
return TCL_ERROR;
return TCL_OK;
} else if(strcmp(argv[1], "setphytx") == 0) {
phy_tx_ = (SatPhy*) TclObject::lookup(argv[2]);
if (phy_tx_ == 0)
return TCL_ERROR;
return TCL_OK;
} else if(strcmp(argv[1], "setphyrx") == 0) {
phy_rx_ = (SatPhy*) TclObject::lookup(argv[2]);
if (phy_rx_ == 0)
return TCL_ERROR;
return TCL_OK;
} else if(strcmp(argv[1], "setmac") == 0) {
mac_ = (SatMac*) TclObject::lookup(argv[2]);
if (mac_ == 0)
return TCL_ERROR;
return TCL_OK;
} else if(strcmp(argv[1], "setqueue") == 0) {
queue_ = (Queue*) TclObject::lookup(argv[2]);
if (queue_ == 0)
return TCL_ERROR;
return TCL_OK;
} else if(strcmp(argv[1], "seterrmodel") == 0) {
errmodel_ = (ErrorModel*) TclObject::lookup(argv[2]);
if (errmodel_ == 0)
return TCL_ERROR;
return TCL_OK;
}
}
return (LinkHead::command(argc, argv));
}
/*==========================================================================*/
/*
* _SatLL
*/
static class SatLLClass : public TclClass {
public:
SatLLClass() : TclClass("LL/Sat") {}
TclObject* create(int, const char*const*) {
return (new SatLL);
}
} sat_class_ll;
void SatLL::recv(Packet* p, Handler* /*h*/)
{
hdr_cmn *ch = HDR_CMN(p);
/*
* Sanity Check
*/
assert(initialized());
// If direction = UP, then pass it up the stack
// Otherwise, set direction to DOWN and pass it down the stack
if(ch->direction() == hdr_cmn::UP) {
uptarget_ ? sendUp(p) : drop(p);
return;
}
ch->direction() = hdr_cmn::DOWN;
sendDown(p);
}
// Encode link layer sequence number, type, and mac address fields
void SatLL::sendDown(Packet* p)
{
hdr_cmn *ch = HDR_CMN(p);
hdr_ll *llh = HDR_LL(p);
char *mh = (char*)p->access(hdr_mac::offset_);
int peer_mac_;
SatChannel* satchannel_;
llh->seqno_ = ++seqno_;
llh->lltype() = LL_DATA;
// Set mac src, type, and dst
mac_->hdr_src(mh, mac_->addr());
mac_->hdr_type(mh, ETHERTYPE_IP); // We'll just use ETHERTYPE_IP
nsaddr_t dst = ch->next_hop();
// a value of -1 is IP_BROADCAST
if (dst < -1) {
printf("Error: next_hop_ field not set by routing agent\n");
exit(1);
}
switch(ch->addr_type()) {
case NS_AF_INET:
case NS_AF_NONE:
if (IP_BROADCAST == (u_int32_t) dst)
{
mac_->hdr_dst((char*) HDR_MAC(p), MAC_BROADCAST);
break;
}
/*
* Here is where arp would normally occur. In the satellite
* case, we don't arp (for now). Instead, use destination
* address to find the mac address corresponding to the
* peer connected to this channel. If someone wants to
* add arp, look at how the wireless code does it.
*/
// Cache latest value used
if (dst == arpcachedst_) {
mac_->hdr_dst((char*) HDR_MAC(p), arpcache_);
break;
}
// Search for peer's mac address (this is the pseudo-ARP)
satchannel_ = (SatChannel*) channel();
peer_mac_ = satchannel_->find_peer_mac_addr(dst);
if (peer_mac_ < 0 ) {
printf("Error: couldn't find dest mac on channel ");
printf("NOW %f\n", NOW);
exit(1);
} else {
mac_->hdr_dst((char*) HDR_MAC(p), peer_mac_);
arpcachedst_ = dst;
arpcache_ = peer_mac_;
break;
}
default:
printf("Error: addr_type not set to NS_AF_INET or NS_AF_NONE\n");
exit(1);
}
// let mac decide when to take a new packet from the queue.
Scheduler& s = Scheduler::instance();
s.schedule(downtarget_, p, delay_);
}
void SatLL::sendUp(Packet* p)
{
Scheduler& s = Scheduler::instance();
if (hdr_cmn::access(p)->error() > 0)
drop(p);
else
s.schedule(uptarget_, p, delay_);
}
// Helper function
Channel* SatLL::channel()
{
Phy* phy_ = (Phy*) mac_->downtarget();
return phy_->channel();
}
/*==========================================================================*/
/*
* _SatMac
*/
static class SatMacClass : public TclClass {
public:
SatMacClass() : TclClass("Mac/Sat") {}
TclObject* create(int, const char*const*) {
return (new SatMac);
}
} sat_class_mac;
void MacSendTimer::expire(Event*)
{
a_->send_timer();
}
void MacRecvTimer::expire(Event*)
{
a_->recv_timer();
}
int SatMac::command(int argc, const char*const* argv)
{
if(argc == 2) {
}
else if (argc == 3) {
TclObject *obj;
if( (obj = TclObject::lookup(argv[2])) == 0) {
fprintf(stderr, "%s lookup failed\n", argv[1]);
return TCL_ERROR;
}
if (strcmp(argv[1], "channel") == 0) {
//channel_ = (Channel*) obj;
return (TCL_OK);
}
}
return Mac::command(argc, argv);
}
void SatMac::sendUp(Packet* p)
{
hdr_mac* mh = HDR_MAC(p);
int dst = this->hdr_dst((char*)mh); // mac destination address
if (((u_int32_t)dst != MAC_BROADCAST) && (dst != index_)) {
drop(p);
return;
}
// First bit of packet has arrived-- wait for
// (txtime + delay_) before sending up
Scheduler::instance().schedule(uptarget_, p, delay_ + mh->txtime());
}
void SatMac::sendDown(Packet* p)
{
Scheduler& s = Scheduler::instance();
double txt;
// LINK_HDRSIZE is defined in satlink.h. This is the size of header
// information for all layers below IP. Alternatively, one could
// derive this information dynamically from packet headers.
int packetsize_ = HDR_CMN(p)->size() + LINK_HDRSIZE;
if (bandwidth_ != 0)
txt = txtime(packetsize_);
// For convenience, we encode the transmit time in the Mac header
// The packet will be held (for collision detection) for txtime
// at the receiving mac.
HDR_MAC(p)->txtime() = txt;
downtarget_->recv(p, this);
// Callback for when this packet's transmission will be done
s.schedule(&hRes_, &intr_, txt);
}
static class UnslottedAlohaMacClass : public TclClass {
public:
UnslottedAlohaMacClass() : TclClass("Mac/Sat/UnslottedAloha") {}
TclObject* create(int, const char*const*) {
return (new UnslottedAlohaMac());
}
} sat_class_unslottedalohamac;
/*==========================================================================*/
/*
* _UnslottedAlohaMac
*/
UnslottedAlohaMac::UnslottedAlohaMac() : SatMac(), tx_state_(MAC_IDLE),
rx_state_(MAC_IDLE), rtx_(0), end_of_contention_(0)
{
bind_time("mean_backoff_", &mean_backoff_);
bind("rtx_limit_", &rtx_limit_);
bind_time("send_timeout_", &send_timeout_);
}
void UnslottedAlohaMac::send_timer()
{
switch (tx_state_) {
case MAC_SEND:
// We've timed out on send-- back off
backoff();
break;
case MAC_COLL:
// Our backoff timer has expired-- resend
sendDown(snd_pkt_);
break;
default:
printf("Error: wrong tx_state in unslotted aloha: %d\n",
tx_state_);
break;
}
}
void UnslottedAlohaMac::recv_timer()
{
switch (rx_state_) {
case MAC_RECV:
// We've successfully waited out the reception
end_of_contention(rcv_pkt_);
break;
default:
printf("Error: wrong rx_state in unslotted aloha: %d\n",
rx_state_);
break;
}
}
void UnslottedAlohaMac::sendUp(Packet* p)
{
hdr_mac* mh = HDR_MAC(p);
if (rx_state_ == MAC_IDLE) {
// First bit of packet has arrived-- wait for
// txtime to make sure no collisions occur
rcv_pkt_ = p;
end_of_contention_ = NOW + mh->txtime();
rx_state_ = MAC_RECV;
recv_timer_.resched(mh->txtime());
} else {
// Collision: figure out if contention phase must be lengthened
double temp = NOW + mh->txtime();
if (temp > end_of_contention_) {
recv_timer_.resched(temp - NOW);
}
drop(p);
if (rcv_pkt_)
drop(rcv_pkt_);
rcv_pkt_ = 0;
}
}
void UnslottedAlohaMac::sendDown(Packet* p)
{
double txt;
// compute transmission delay:
int packetsize_ = HDR_CMN(p)->size() + LINK_HDRSIZE;
if (bandwidth_ != 0)
txt = txtime(packetsize_);
HDR_MAC(p)->txtime() = txt;
// Send the packet down
tx_state_ = MAC_SEND;
snd_pkt_ = p->copy(); // save a copy in case it gets retransmitted
downtarget_->recv(p, this);
// Set a timer-- if we do not hear our own transmission within this
// interval (and cancel the timer), the send_timer will expire and
// we will backoff and retransmit.
send_timer_.resched(send_timeout_ + txt);
}
// Called when contention period ends
void UnslottedAlohaMac::end_of_contention(Packet* p)
{
rx_state_ = MAC_IDLE;
if (!p)
return; // No packet to free or send up.
hdr_mac* mh = HDR_MAC(p);
int dst = this->hdr_dst((char*)mh); // mac destination address
int src = this->hdr_src((char*)mh); // mac source address
if (((u_int32_t)dst != MAC_BROADCAST) && (dst != index_) &&
(src != index_)) {
drop(p);
return;
}
if (src == index_) {
// received our own packet: free up transmit side, drop this
// packet, and perform callback to queue which is blocked
if (!callback_) {
printf("Error, queue callback_ is not valid\n");
exit(1);
}
send_timer_.force_cancel();
tx_state_ = MAC_IDLE;
rtx_ = 0;
drop(snd_pkt_); // Free the packet cached for retransmission
resume(p);
} else {
// wait for processing delay (delay_) to send packet upwards
Scheduler::instance().schedule(uptarget_, p, delay_);
}
}
void UnslottedAlohaMac::backoff(double delay)
{
double backoff_ = Random::exponential(mean_backoff_);
// if number of retransmissions is within limit, do exponential backoff
// else drop the packet and resume
if (++rtx_ <= rtx_limit_) {
tx_state_ = MAC_COLL;
delay += backoff_;
send_timer_.resched(delay);
} else {
tx_state_ = MAC_IDLE;
rtx_ = 0;
resume(snd_pkt_);
}
}
/*==========================================================================*/
/*
* _SatPhy
*/
static class SatPhyClass: public TclClass {
public:
SatPhyClass() : TclClass("Phy/Sat") {}
TclObject* create(int, const char*const*) {
return (new SatPhy);
}
} class_SatPhy;
void SatPhy::sendDown(Packet *p)
{
if (channel_)
channel_->recv(p, this);
else {
// it is possible for routing to change (and a channel to
// be disconnected) while a packet
// is moving down the stack. Therefore, just log a drop
// if there is no channel
if ( ((SatNode*) head()->node())->trace() )
((SatNode*) head()->node())->trace()->traceonly(p);
Packet::free(p);
}
}
// Note that this doesn't do that much right now. If you want to incorporate
// an error model, you could insert a "propagation" object like in the
// wireless case.
int SatPhy::sendUp(Packet * /* pkt */)
{
return TRUE;
}
int
SatPhy::command(int argc, const char*const* argv) {
if (argc == 2) {
} else if (argc == 3) {
TclObject *obj;
if( (obj = TclObject::lookup(argv[2])) == 0) {
fprintf(stderr, "%s lookup failed\n", argv[1]);
return TCL_ERROR;
}
}
return Phy::command(argc, argv);
}
static class RepeaterPhyClass: public TclClass {
public:
RepeaterPhyClass() : TclClass("Phy/Repeater") {}
TclObject* create(int, const char*const*) {
return (new RepeaterPhy);
}
} class_RepeaterPhy;
void RepeaterPhy::recv(Packet* p, Handler*)
{
struct hdr_cmn *hdr = HDR_CMN(p);
if (hdr->direction() == hdr_cmn::UP) {
// change direction and send to uptarget (which is
// really a Phy_tx that is also a RepeaterPhy)
hdr->direction() = hdr_cmn::DOWN;
uptarget_->recv(p, (Handler*) 0);
} else {
sendDown(p);
}
}
void RepeaterPhy::sendDown(Packet *p)
{
struct hdr_cmn *hdr = HDR_CMN(p);
hdr->direction() = hdr_cmn::DOWN;
if (channel_)
channel_->recv(p, this);
else {
printf("Error, no channel on repeater\n");
exit(1);
}
}
/*==========================================================================*/
/*
* _SatChannel
*/
static class SatChannelClass : public TclClass {
public:
SatChannelClass() : TclClass("Channel/Sat") {}
TclObject* create(int, const char*const*) {
return (new SatChannel);
}
} class_Sat_channel;
SatChannel::SatChannel(void) : Channel() {
}
double
SatChannel::get_pdelay(Node* tnode, Node* rnode)
{
coordinate a = ((SatNode*)tnode)->position()->coord();
coordinate b = ((SatNode*)rnode)->position()->coord();
return (SatGeometry::propdelay(a, b));
}
// This is a helper function that attaches a SatChannel to a Phy
void SatChannel::add_interface(Phy* phy_)
{
phy_->setchnl(this); // Attach phy to this channel
phy_->insertchnl(&ifhead_); // Add phy_ to list of phys on the channel
}
// Remove a phy from a channel
void SatChannel::remove_interface(Phy* phy_)
{
phy_->setchnl(NULL); // Set phy_'s channel pointer to NULL
phy_->removechnl(); // Remove phy_ to list of phys on the channel
}
// Search for destination mac address on this channel. Look through list
// of phys on the channel. If the channel connects to a geo repeater, look
// for the destination on the corresponding downlink channel.
int SatChannel::find_peer_mac_addr(int dst)
{
Phy *n;
Channel* chan_;
chan_ = this;
n = ifhead_.lh_first;
if (n->head()->type() == LINK_GSL_REPEATER) {
SatLinkHead* slh = (SatLinkHead*) n->head();
chan_ = slh->phy_tx()->channel();
}
for(n = chan_->ifhead_.lh_first; n; n = n->nextchnl() ) {
if (n->node()->address() == dst) {
return (((SatMac*) n->uptarget())->addr());
}
}
return -1;
}
satnode.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1999 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 MASH Research
* Group at the University of California Berkeley.
* 4. Neither the name of the University nor of the Research Group 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 Tom Henderson, UCB Daedalus Research Group, June 1999
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$";
#endif
#include "satnode.h"
#include "satlink.h"
#include "sattrace.h"
#include "sathandoff.h"
#include "satposition.h"
static class SatNodeClass : public TclClass {
public:
SatNodeClass() : TclClass("Node/SatNode") {}
TclObject* create(int , const char*const* ) {
return (new SatNode);
}
} class_satnode;
int SatNode::dist_routing_ = 0;
SatNode::SatNode() : ragent_(0), trace_(0), hm_(0)
{
bind_bool("dist_routing_", &dist_routing_);
}
int SatNode::command(int argc, const char*const* argv) {
Tcl& tcl = Tcl::instance();
if (argc == 2) {
if (strcmp(argv[1], "set_downlink") == 0) {
if (downlink_ != NULL) {
tcl.result(downlink_->name());
return (TCL_OK);
}
} else if (strcmp(argv[1], "set_uplink") == 0) {
if (downlink_ != NULL) {
tcl.result(uplink_->name());
return (TCL_OK);
}
} else if (strcmp(argv[1], "start_handoff") == 0) {
if (hm_)
hm_->start();
else {
printf("Error: starting non-existent ");
printf("handoff mgr\n");
exit(1);
}
return (TCL_OK);
} else if (strcmp(argv[1], "dump_sats") == 0) {
dumpSats();
return (TCL_OK);
}
}
if (argc == 3) {
if (strcmp(argv[1], "set_uplink") == 0) {
uplink_ = (SatChannel *) TclObject::lookup(argv[2]);
if (uplink_ == 0) {
tcl.resultf("no such object %s", argv[2]);
return (TCL_ERROR);
}
return (TCL_OK);
} else if (strcmp(argv[1], "set_downlink") == 0) {
downlink_ = (SatChannel *) TclObject::lookup(argv[2]);
if (downlink_ == 0) {
tcl.resultf("no such object %s", argv[2]);
return (TCL_ERROR);
}
return (TCL_OK);
} else if (strcmp(argv[1], "set_trace") == 0) {
trace_ = (SatTrace *) TclObject::lookup(argv[2]);
if (trace_ == 0) {
tcl.resultf("no such object %s", argv[2]);
return (TCL_ERROR);
}
return (TCL_OK);
} else if (strcmp(argv[1], "set_ragent") == 0) {
ragent_ = (SatRouteAgent *) TclObject::lookup(argv[2]);
if (ragent_ == 0) {
tcl.resultf("no such object %s", argv[2]);
return (TCL_ERROR);
}
return (TCL_OK);
} else if(strcmp(argv[1], "addif") == 0) {
SatPhy* n = (SatPhy*) TclObject::lookup(argv[2]);
if(n == 0)
return TCL_ERROR;
n->insertnode(&ifhead_);
n->setnode(this);
return TCL_OK;
} else if (strcmp(argv[1], "set_position") == 0) {
pos_ = (SatPosition*) TclObject::lookup(argv[2]);
if (pos_ == 0) {
tcl.resultf("no such object %s", argv[2]);
return (TCL_ERROR);
}
return (TCL_OK);
} else if (strcmp(argv[1], "set_handoff_mgr") == 0) {
hm_ = (LinkHandoffMgr*) TclObject::lookup(argv[2]);
if (hm_ == 0) {
tcl.resultf("no such object %s", argv[2]);
return (TCL_ERROR);
}
return (TCL_OK);
}
}
return (Node::command(argc, argv));
}
// debugging method for dumping out all of the satellite and ISL locations
// on demand from OTcl.
void SatNode::dumpSats()
{
SatNode *snodep, *peer_snodep;
SatPosition *sposp, *peer_sposp;
SatLinkHead *slhp;
printf("Dumping satellites at time %.2f\n", NOW);
for (snodep= (SatNode*) Node::nodehead_.lh_first; snodep;
snodep = (SatNode*) snodep->nextnode()) {
// XXX Need check to see if node is a SatNode
sposp = snodep->position();
printf("%d\t%.2f\t%.2f\n", snodep->address(),
RAD_TO_DEG(SatGeometry::get_latitude(sposp->coord())),
RAD_TO_DEG(SatGeometry::get_longitude(sposp->coord())));
}
printf("\n");
// Dump satellite links
// There is a static list of address classifiers //QQQ
printf("Links:\n");
for (snodep= (SatNode*) Node::nodehead_.lh_first; snodep;
snodep = (SatNode*) snodep->nextnode()) {
// XXX Not all links necessarily satlinks
for (slhp = (SatLinkHead*) snodep->linklisthead_.lh_first;
slhp; slhp = (SatLinkHead*) slhp->nextlinkhead() ) {
if (slhp->type() != LINK_ISL_CROSSSEAM &&
slhp->type() != LINK_ISL_INTERPLANE &&
slhp->type() != LINK_ISL_INTRAPLANE)
continue;
if (!slhp->linkup_)
continue;
// Link is up. Print out source lat point and dest
// lat point.
sposp = snodep->position();
peer_snodep = hm_->get_peer(slhp);
if (peer_snodep == 0)
continue; // this link interface is not attached
peer_sposp = peer_snodep->position();
printf("%.2f\t%.2f\t%.2f\t%.2f\n",
RAD_TO_DEG(SatGeometry::get_latitude(sposp->coord())),
RAD_TO_DEG(SatGeometry::get_longitude(sposp->coord())),
RAD_TO_DEG(SatGeometry::get_latitude(peer_sposp->coord())),
RAD_TO_DEG(SatGeometry::get_longitude(peer_sposp->coord())));
}
}
}
satposition.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1999 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 MASH Research
* Group at the University of California Berkeley.
* 4. Neither the name of the University nor of the Research Group 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 Tom Henderson, UCB Daedalus Research Group, June 1999
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$";
#endif
#include "satposition.h"
#include "satgeometry.h"
#include
#include
#include
static class TermSatPositionClass : public TclClass {
public:
TermSatPositionClass() : TclClass("Position/Sat/Term") {}
TclObject* create(int argc, const char*const* argv) {
if (argc == 5) {
float a, b;
sscanf(argv[4], "%f %f", &a, &b);
return (new TermSatPosition(a, b));
} else
return (new TermSatPosition);
}
} class_term_sat_position;
static class PolarSatPositionClass : public TclClass {
public:
PolarSatPositionClass() : TclClass("Position/Sat/Polar") {}
TclObject* create(int argc, const char*const* argv) {
if (argc == 5) {
float a = 0, b = 0, c = 0, d = 0, e = 0;
sscanf(argv[4], "%f %f %f %f %f", &a, &b, &c, &d, &e);
return (new PolarSatPosition(a, b, c, d, e));
}
else {
return (new PolarSatPosition);
}
}
} class_polarsatposition;
static class GeoSatPositionClass : public TclClass {
public:
GeoSatPositionClass() : TclClass("Position/Sat/Geo") {}
TclObject* create(int argc, const char*const* argv) {
if (argc == 5)
return (new GeoSatPosition(double(atof(argv[4]))));
else
return (new GeoSatPosition);
}
} class_geosatposition;
static class SatPositionClass : public TclClass {
public:
SatPositionClass() : TclClass("Position/Sat") {}
TclObject* create(int, const char*const*) {
printf("Error: do not instantiate Position/Sat\n");
return (0);
}
} class_satposition;
double SatPosition::time_advance_ = 0;
SatPosition::SatPosition() : node_(0)
{
bind("time_advance_", &time_advance_);
}
int SatPosition::command(int argc, const char*const* argv) {
//Tcl& tcl = Tcl::instance();
if (argc == 2) {
}
if (argc == 3) {
if(strcmp(argv[1], "setnode") == 0) {
node_ = (Node*) TclObject::lookup(argv[2]);
if (node_ == 0)
return TCL_ERROR;
return TCL_OK;
}
}
return (TclObject::command(argc, argv));
}
/////////////////////////////////////////////////////////////////////
// class TermSatPosition
/////////////////////////////////////////////////////////////////////
// Specify initial coordinates. Default coordinates place the terminal
// on the Earth's surface at 0 deg lat, 0 deg long.
TermSatPosition::TermSatPosition(double Theta, double Phi) {
initial_.r = EARTH_RADIUS;
set(Theta, Phi);
type_ = POSITION_SAT_TERM;
}
//
// Convert user specified latitude and longitude to our spherical coordinates
// Latitude is in the range (-90, 90) with neg. values -> south
// Initial_.theta is stored from 0 to PI (spherical)
// Longitude is in the range (-180, 180) with neg. values -> west
// Initial_.phi is stored from 0 to 2*PI (spherical)
//
void TermSatPosition::set(double latitude, double longitude)
{
if (latitude < -90 || latitude > 90)
fprintf(stderr, "TermSatPosition: latitude out of bounds %f\n",
latitude);
if (longitude < -180 || longitude > 180)
fprintf(stderr, "TermSatPosition: longitude out of bounds %f\n",
longitude);
initial_.theta = DEG_TO_RAD(90 - latitude);
if (longitude < 0)
initial_.phi = DEG_TO_RAD(360 + longitude);
else
initial_.phi = DEG_TO_RAD(longitude);
}
coordinate TermSatPosition::coord()
{
coordinate current;
double period = EARTH_PERIOD; // seconds
current.r = initial_.r;
current.theta = initial_.theta;
current.phi = fmod((initial_.phi +
(fmod(NOW + time_advance_,period)/period) * 2*PI), 2*PI);
#ifdef POINT_TEST
current = initial_; // debug option to stop earth's rotation
#endif
return current;
}
/////////////////////////////////////////////////////////////////////
// class PolarSatPosition
/////////////////////////////////////////////////////////////////////
PolarSatPosition::PolarSatPosition(double altitude, double Inc, double Lon,
double Alpha, double Plane) : next_(0), plane_(0) {
set(altitude, Lon, Alpha, Inc);
bind("plane_", &plane_);
if (Plane)
plane_ = int(Plane);
type_ = POSITION_SAT_POLAR;
}
//
// Since it is easier to compute instantaneous orbit position based on a
// coordinate system centered on the orbit itself, we keep initial coordinates
// specified in terms of the satellite orbit, and convert to true spherical
// coordinates in coord().
// Initial satellite position is specified as follows:
// initial_.theta specifies initial angle with respect to "ascending node"
// i.e., zero is at equator (ascending)-- this is the $alpha parameter in Otcl
// initial_.phi specifies East longitude of "ascending node"
// -- this is the $lon parameter in OTcl
// Note that with this system, only theta changes with time
//
void PolarSatPosition::set(double Altitude, double Lon, double Alpha, double Incl)
{
if (Altitude < 0) {
fprintf(stderr, "PolarSatPosition: altitude out of \
bounds: %f\n", Altitude);
exit(1);
}
initial_.r = Altitude + EARTH_RADIUS; // Altitude in km above the earth
if (Alpha < 0 || Alpha >= 360) {
fprintf(stderr, "PolarSatPosition: alpha out of bounds: %f\n",
Alpha);
exit(1);
}
initial_.theta = DEG_TO_RAD(Alpha);
if (Lon < -180 || Lon > 180) {
fprintf(stderr, "PolarSatPosition: lon out of bounds: %f\n",
Lon);
exit(1);
}
if (Lon < 0)
initial_.phi = DEG_TO_RAD(360 + Lon);
else
initial_.phi = DEG_TO_RAD(Lon);
if (Incl < 0 || Incl > 180) {
// retrograde orbits = (90 < Inclination < 180)
fprintf(stderr, "PolarSatPosition: inclination out of \
bounds: %f\n", Incl);
exit(1);
}
inclination_ = DEG_TO_RAD(Incl);
}
//
// The initial coordinate has the following properties:
// theta: 0 < theta < 2 * PI (essentially, this specifies initial position)
// phi: 0 < phi < 2 * PI (longitude of ascending node)
// Return a coordinate with the following properties (i.e. convert to a true
// spherical coordinate):
// theta: 0 < theta < PI
// phi: 0 < phi < 2 * PI
//
coordinate PolarSatPosition::coord()
{
coordinate current;
double partial; // fraction of orbit period completed
// XXX: can't use "num = pow(initial_.r,3)" here because of linux lib
double num = initial_.r * initial_.r * initial_.r;
double period = 2 * PI * sqrt(num/MU); // seconds
partial =
(fmod(NOW + time_advance_, period)/period) * 2*PI; //rad
double theta_cur, phi_cur, theta_new, phi_new;
// Compute current orbit-centric coordinates:
// theta_cur adds effects of time (orbital rotation) to initial_.theta
theta_cur = fmod(initial_.theta + partial, 2*PI);
phi_cur = initial_.phi;
// Reminder: theta_cur and phi_cur are temporal translations of
// initial parameters and are NOT true spherical coordinates.
//
// Now generate actual spherical coordinates,
// with 0 < theta_new < PI and 0 < phi_new < 360
if (inclination_ < PI) {
// asin returns value between -PI/2 and PI/2, so
// theta_new guaranteed to be between 0 and PI
theta_new = PI/2 - asin(sin(inclination_) * sin(theta_cur));
// if theta_new is between PI/2 and 3*PI/2, must correct
// for return value of atan()
if (theta_cur > PI/2 && theta_cur < 3*PI/2)
phi_new = atan(cos(inclination_) * tan(theta_cur)) +
phi_cur + PI;
else
phi_new = atan(cos(inclination_) * tan(theta_cur)) +
phi_cur;
phi_new = fmod(phi_new + 2*PI, 2*PI);
} else
printf("ERROR: inclination_ > PI\n");
current.r = initial_.r;
current.theta = theta_new;
current.phi = phi_new;
return current;
}
int PolarSatPosition::command(int argc, const char*const* argv) {
Tcl& tcl = Tcl::instance();
if (argc == 2) {
}
if (argc == 3) {
if (strcmp(argv[1], "set_next") == 0) {
next_ = (PolarSatPosition *) TclObject::lookup(argv[2]);
if (next_ == 0) {
tcl.resultf("no such object %s", argv[2]);
return (TCL_ERROR);
}
return (TCL_OK);
}
}
return (SatPosition::command(argc, argv));
}
/////////////////////////////////////////////////////////////////////
// class GeoSatPosition
/////////////////////////////////////////////////////////////////////
GeoSatPosition::GeoSatPosition(double longitude)
{
initial_.r = EARTH_RADIUS + GEO_ALTITUDE;
initial_.theta = PI/2;
set(longitude);
type_ = POSITION_SAT_GEO;
}
coordinate GeoSatPosition::coord()
{
coordinate current;
current.r = initial_.r;
current.theta = initial_.theta;
double fractional =
(fmod(NOW + time_advance_, EARTH_PERIOD)/EARTH_PERIOD) *2*PI; // rad
current.phi = fmod(initial_.phi + fractional, 2*PI);
return current;
}
//
// Longitude is in the range (0, 180) with negative values -> west
//
void GeoSatPosition::set(double longitude)
{
if (longitude < -180 || longitude > 180)
fprintf(stderr, "GeoSatPosition: longitude out of bounds %f\n",
longitude);
if (longitude < 0)
initial_.phi = DEG_TO_RAD(360 + longitude);
else
initial_.phi = DEG_TO_RAD(longitude);
}
satroute.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1999 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 MASH Research
* Group at the University of California Berkeley.
* 4. Neither the name of the University nor of the Research Group 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 Tom Henderson, UCB Daedalus Research Group, June 1999
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$";
#endif
#include "satroute.h"
#include "sattrace.h"
#include "satnode.h"
#include "satlink.h"
#include "route.h"
#include
static class SatRouteClass:public TclClass
{
public:
SatRouteClass ():TclClass ("Agent/SatRoute") { }
TclObject *create (int, const char *const *) {
return (new SatRouteAgent ());
}
} class_satroute;
SatRouteAgent::SatRouteAgent (): Agent (PT_MESSAGE), maxslot_(0), nslot_(0), slot_(0)
{
bind ("myaddr_", &myaddr_);
}
SatRouteAgent::~SatRouteAgent()
{
if (slot_)
delete [] slot_;
}
void SatRouteAgent::alloc(int slot)
{
slot_entry *old = slot_;
int n = nslot_;
if (old == 0)
nslot_ = 32;
while (nslot_ <= slot)
nslot_ <<= 1;
slot_ = new slot_entry[nslot_];
memset(slot_, 0, nslot_ * sizeof(slot_entry));
for (int i = 0; i < n; ++i) {
slot_[i].next_hop = old[i].next_hop;
slot_[i].entry = old[i].entry;
}
delete [] old;
}
void SatRouteAgent::install(int slot, int nh, NsObject* p)
{
if (slot >= nslot_)
alloc(slot);
slot_[slot].next_hop = nh;
slot_[slot].entry = p;
if (slot >= maxslot_)
maxslot_ = slot;
}
void SatRouteAgent::clear_slots()
{
if (slot_)
delete [] slot_;
slot_ = 0;
nslot_ = 0;
maxslot_ = -1;
}
int SatRouteAgent::command (int argc, const char *const *argv)
{
Tcl& tcl = Tcl::instance();
if (argc == 2) {
}
if (argc == 3) {
if (strcmp(argv[1], "set_node") == 0) {
node_ = (SatNode *) TclObject::lookup(argv[2]);
if (node_ == 0) {
tcl.resultf("no such object %s", argv[2]);
return (TCL_ERROR);
}
return (TCL_OK);
}
}
return (Agent::command (argc, argv));
}
/*
* Find a target for the received packet
*/
void SatRouteAgent::forwardPacket(Packet * p)
{
hdr_ip *iph = (hdr_ip *) p->access (off_ip_);
hdr_cmn *hdrc = HDR_CMN (p);
NsObject *link_entry_;
hdrc->direction() = hdr_cmn::DOWN; // send it down the stack
int dst = Address::instance().get_nodeaddr(iph->daddr());
// Here we need to have an accurate encoding of the next hop routing
// information
if (myaddr_ == iph->daddr()) {
printf("Error: trying to forward a packet destined to self: %d\n", myaddr_);
Packet::free(p);
}
hdrc->addr_type_ = NS_AF_INET;
hdrc->last_hop_ = myaddr_; // for tracing purposes
if (SatRouteObject::instance().data_driven_computation())
SatRouteObject::instance().recompute_node(myaddr_);
if (SatNode::dist_routing_ == 0) {
if (slot_ == 0) { // No routes to anywhere
if (node_->trace())
node_->trace()->traceonly(p);
Packet::free(p);
return;
}
link_entry_ = slot_[dst].entry;
if (link_entry_ == 0) {
if (node_->trace())
node_->trace()->traceonly(p);
Packet::free(p);
return;
}
hdrc->next_hop_ = slot_[dst].next_hop;
link_entry_->recv(p, (Handler *)0);
return;
} else {
// DISTRIBUTED ROUTING LOOKUP COULD GO HERE
printf("Error: distributed routing not available\n");
exit(1);
}
}
void SatRouteAgent::recv (Packet * p, Handler *)
{
hdr_ip *iph = (hdr_ip *) p->access (off_ip_);
hdr_cmn *cmh = (hdr_cmn *) p->access (off_cmn_);
if (iph->saddr() == myaddr_ && cmh->num_forwards() == 0) {
// Must be a packet I'm originating... add the IP header
iph->ttl_ = IP_DEF_TTL;
} else if (iph->saddr() == myaddr_) {
// I received a packet that I sent. Probably a routing loop.
Packet::free(p);
return;
} else {
// Packet I'm forwarding...
// Check the TTL. If it is zero, then discard.
if(--iph->ttl_ == 0) {
Packet::free(p);
return;
}
}
if ((iph->saddr() != myaddr_) && (iph->dport() == ROUTER_PORT)) {
// DISTRIBUTED ROUTING PROTOCOL COULD GO HERE
printf("Error: distributed routing not available\n");
exit(1);
} else {
forwardPacket(p);
}
}
//###########################################################################
static class SatRouteObjectClass:public TclClass
{
public:
SatRouteObjectClass ():TclClass ("SatRouteObject") { }
TclObject *create (int, const char *const *) {
return (new SatRouteObject ());
}
} class_satrouteobject;
SatRouteObject* SatRouteObject::instance_;
SatRouteObject::SatRouteObject() : suppress_initial_computation_(0)
{
bind_bool("metric_delay_", &metric_delay_);
bind_bool("data_driven_computation_", &data_driven_computation_);
}
int SatRouteObject::command (int argc, const char *const *argv)
{
if (instance_ == 0)
instance_ = this;
if (argc == 2) {
// While suppress_initial_computation_ may seem more
// appropriate as a bound variable, it is useful to
// implement the setting of this variable this way so that
// the ``instance_ = this'' assignment is made at the
// start of simulation.
if (strcmp(argv[1], "suppress_initial_computation") == 0) {
suppress_initial_computation_ = 1;
return (TCL_OK);
}
if (strcmp(argv[1], "compute_routes") == 0) {
recompute();
return (TCL_OK);
}
}
return (RouteLogic::command(argc, argv));
}
void SatRouteObject::recompute_node(int node)
{
compute_topology();
node_compute_routes(node);
populate_routing_tables(node);
}
void SatRouteObject::recompute()
{
// For very large topologies (e.g., Teledesic), we don't want to
// waste a lot of time computing routes at the beginning of the
// simulation. This first if() clause suppresses route computations.
if (data_driven_computation_ ||
(NOW < 0.001 && suppress_initial_computation_) )
return;
else {
compute_topology();
compute_routes(); // calls base class function
populate_routing_tables();
}
}
// Derives link adjacency information from the nodes and gives the current
// topology information to the RouteLogic.
void SatRouteObject::compute_topology()
{
Node *nodep;
Phy *phytxp, *phyrxp, *phytxp2, *phyrxp2;
SatLinkHead *slhp;
Channel *channelp, *channelp2;
int src, dst;
double delay;
reset_all();
// Compute adjacencies. Traverse linked list of nodes
for (nodep=Node::nodehead_.lh_first; nodep; nodep = nodep->nextnode()) {
// Cycle through the linked list of linkheads
for (slhp = (SatLinkHead*) nodep->linklisthead_.lh_first; slhp;
slhp = (SatLinkHead*) slhp->nextlinkhead()) {
if (slhp->type() == LINK_GSL_REPEATER)
continue;
if (!slhp->linkup_)
continue;
phytxp = (Phy *) slhp->phy_tx();
assert(phytxp);
channelp = phytxp->channel();
if (!channelp)
continue; // Not currently connected to channel
// Next, look for receive interfaces on this channel
phyrxp = channelp->ifhead_.lh_first;
for (; phyrxp; phyrxp = phyrxp->nextchnl()) {
if (phyrxp == phytxp) {
printf("Configuration error: a transmit interface \
is a channel target\n");
exit(1);
}
if (phyrxp->head()->type() == LINK_GSL_REPEATER) {
double delay_firsthop = ((SatChannel*)
channelp)->get_pdelay(phytxp->node(),
phyrxp->node());
if (!((SatLinkHead*)phyrxp->head())->linkup_)
continue;
phytxp2 = ((SatLinkHead*)phyrxp->head())->phy_tx();
channelp2 = phytxp2->channel();
if (!channelp2)
continue; // Not currently connected to channel
phyrxp2 = channelp2->ifhead_.lh_first;
for (; phyrxp2; phyrxp2 = phyrxp2->nextchnl()) {
if (phyrxp2 == phytxp2) {
printf("Config error: a transmit interface \
is a channel target\n");
exit(1);
}
// Found an adjacency relationship.
// Add this link to the RouteLogic
src = phytxp->node()->address() + 1;
dst = phyrxp2->node()->address() + 1;
if (src == dst)
continue;
if (metric_delay_)
delay = ((SatChannel*)
channelp2)->get_pdelay(phytxp2->node(),
phyrxp2->node());
else {
delay = 1;
delay_firsthop = 0;
}
insert(src, dst, delay+delay_firsthop, (void*)slhp);
}
} else {
// Found an adjacency relationship.
// Add this link to the RouteLogic
src = phytxp->node()->address() + 1;
dst = phyrxp->node()->address() + 1;
if (metric_delay_)
delay = ((SatChannel*)
channelp)->get_pdelay(phytxp->node(),
phyrxp->node());
else
delay = 1;
insert(src, dst, delay, (void*)slhp);
}
}
}
}
//dump();
}
void SatRouteObject::populate_routing_tables(int node)
{
SatNode *snodep = (SatNode*) Node::nodehead_.lh_first;
SatNode *snodep2;
int next_hop, src, dst;
NsObject *target;
for (; snodep; snodep = (SatNode*) snodep->nextnode()) {
// First, clear slots of the current routing table
if (snodep->ragent())
snodep->ragent()->clear_slots();
src = snodep->address();
if (node != -1 && node != src)
continue;
snodep2 = (SatNode*) Node::nodehead_.lh_first;
for (; snodep2; snodep2 = (SatNode*) snodep2->nextnode()) {
dst = snodep2->address();
next_hop = lookup(src, dst);
if (next_hop != -1 && src != dst) {
// Here need to insert target into slot table
target = (NsObject*) lookup_entry(src, dst);
if (target == 0) {
printf("Error, routelogic target ");
printf("not populated %f\n", NOW);
exit(1);
}
((SatNode*)snodep)->ragent()->install(dst,
next_hop, target);
}
}
}
}
int SatRouteObject::lookup(int s, int d)
{
int src = s + 1;
int dst = d + 1;
if (src >= size_ || dst >= size_) {
return (-1); // Next hop = -1
}
return (route_[INDEX(src, dst, size_)].next_hop - 1);
}
void* SatRouteObject::lookup_entry(int s, int d)
{
int src = s + 1;
int dst = d + 1;
if (src >= size_ || dst >= size_) {
return (0); // Null pointer
}
return (route_[INDEX(src, dst, size_)].entry);
}
// This method is used for debugging only
void SatRouteObject::dump()
{
int i, src, dst;
for (i = 0; i < (size_ * size_); i++) {
if (adj_[i].cost != INFINITY) {
src = i / size_ - 1;
dst = i % size_ - 1;
printf("Found a link from %d to %d with cost %f\n", src, dst, adj_[i].cost);
}
/*
if (route_[i].next_hop) {
src = i / size_ - 1;
dst = i % size_ - 1;
printf("Found a route from %d to %d through %d\n", src, dst, route_[i].next_hop - 1);
}
*/
}
}
void SatRouteObject::node_compute_routes(int node)
{
int n = size_;
int* parent = new int[n];
double* hopcnt = new double[n];
#define ADJ(i, j) adj_[INDEX(i, j, size_)].cost
#define ADJ_ENTRY(i, j) adj_[INDEX(i, j, size_)].entry
#define ROUTE(i, j) route_[INDEX(i, j, size_)].next_hop
#define ROUTE_ENTRY(i, j) route_[INDEX(i, j, size_)].entry
delete[] route_;
route_ = new route_entry[n * n];
memset((char *)route_, 0, n * n * sizeof(route_[0]));
/* compute routes only for node "node" */
int k = node + 1; // must add one to get the right offset in tables
int v;
for (v = 0; v < n; v++)
parent[v] = v;
/* set the route for all neighbours first */
for (v = 1; v < n; ++v) {
if (parent[v] != k) {
hopcnt[v] = ADJ(k, v);
if (hopcnt[v] != INFINITY) {
ROUTE(k, v) = v;
ROUTE_ENTRY(k, v) = ADJ_ENTRY(k, v);
}
}
}
for (v = 1; v < n; ++v) {
/*
* w is the node that is the nearest to the subtree
* that has been routed
*/
int o = 0;
/* XXX */
hopcnt[0] = INFINITY;
int w;
for (w = 1; w < n; w++)
if (parent[w] != k && hopcnt[w] < hopcnt[o])
o = w;
parent[o] = k;
/*
* update distance counts for the nodes that are
* adjacent to o
*/
if (o == 0)
continue;
for (w = 1; w < n; w++) {
if (parent[w] != k &&
hopcnt[o] + ADJ(o, w) < hopcnt[w]) {
ROUTE(k, w) = ROUTE(k, o);
ROUTE_ENTRY(k, w) =
ROUTE_ENTRY(k, o);
hopcnt[w] = hopcnt[o] + ADJ(o, w);
}
}
}
/*
* The route to yourself is yourself.
*/
ROUTE(k, k) = k;
ROUTE_ENTRY(k, k) = 0; // This should not matter
delete[] hopcnt;
delete[] parent;
}
sattrace.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1999 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 MASH Research
* Group at the University of California Berkeley.
* 4. Neither the name of the University nor of the Research Group 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 Tom Henderson, UCB Daedalus Research Group, June 1999
* speedup hack from Lloyd Wood, 11 October 1999 */
#ifndef lint
static const char rcsid[] =
"@(#) $Header$";
#endif
#include
#include
#include "packet.h"
#include "ip.h"
#include "tcp.h"
#include "rtp.h"
#include "srm.h"
#include "flags.h"
#include "address.h"
#include "trace.h"
#include "sattrace.h"
#include "satposition.h"
#include "satnode.h"
class SatTraceClass : public TclClass {
public:
SatTraceClass() : TclClass("Trace/Sat") { }
TclObject* create(int argc, const char*const* argv) {
if (argc >= 5) {
return (new SatTrace(*argv[4]));
}
return 0;
}
} sat_trace_class;
// XXX this should be moved from trace.cc to trace.h
char* srm_names_[] = {
SRM_NAMES
};
void SatTrace::format(int tt, int s, int d, Packet* p)
{
#ifdef OFF_HDR
hdr_cmn *th = (hdr_cmn*)p->access(off_cmn_);
hdr_ip *iph = (hdr_ip*)p->access(off_ip_);
hdr_tcp *tcph = (hdr_tcp*)p->access(off_tcp_);
hdr_rtp *rh = (hdr_rtp*)p->access(off_rtp_);
hdr_srm *sh = (hdr_srm*)p->access(off_srm_);
#else
hdr_cmn *th = hdr_cmn::access(p);
hdr_ip *iph = hdr_ip::access(p);
hdr_tcp *tcph = hdr_tcp::access(p);
hdr_rtp *rh = hdr_rtp::access(p);
hdr_srm *sh = hdr_srm::access(p);
#endif
const char* sname = "null";
int lasth, nexth, snadd;
Node* n;
packet_t t = th->ptype();
const char* name = packet_info.name(t);
/* SRM-specific */
if (strcmp(name,"SRM") == 0 || strcmp(name,"cbr") == 0 || strcmp(name,"udp") == 0) {
if ( sh->type() < 5 && sh->type() > 0 ) {
sname = srm_names_[sh->type()];
}
}
if (name == 0)
abort();
int seqno;
/* UDP's now have seqno's too */
if (t == PT_RTP || t == PT_CBR || t == PT_UDP || t == PT_EXP ||
t == PT_PARETO)
seqno = rh->seqno();
else if (t == PT_TCP || t == PT_ACK || t == PT_HTTP || t == PT_FTP ||
t == PT_TELNET)
seqno = tcph->seqno();
else
seqno = -1;
/*
* When new flags are added, make sure to change NUMFLAGS
* in trace.h
*/
char flags[NUMFLAGS+1];
for (int i = 0; i < NUMFLAGS; i++)
flags[i] = '-';
flags[NUMFLAGS] = 0;
#ifdef OFF_HDR
hdr_flags* hf = (hdr_flags*)p->access(off_flags_);
#else
hdr_flags* hf = hdr_flags::access(p);
#endif
flags[0] = hf->ecn_ ? 'C' : '-'; // Ecn Echo
flags[1] = hf->pri_ ? 'P' : '-';
flags[2] = '-';
flags[3] = hf->cong_action_ ? 'A' : '-'; // Congestion Action
flags[4] = hf->ecn_to_echo_ ? 'E' : '-'; // Congestion Experienced
flags[5] = hf->fs_ ? 'F' : '-';
flags[6] = hf->ecn_capable_ ? 'N' : '-';
#ifdef notdef
flags[1] = (iph->flags() & PF_PRI) ? 'P' : '-';
flags[2] = (iph->flags() & PF_USR1) ? '1' : '-';
flags[3] = (iph->flags() & PF_USR2) ? '2' : '-';
flags[5] = 0;
#endif
char *src_nodeaddr = Address::instance().print_nodeaddr(iph->saddr());
char *src_portaddr = Address::instance().print_portaddr(iph->sport());
char *dst_nodeaddr = Address::instance().print_nodeaddr(iph->daddr());
char *dst_portaddr = Address::instance().print_portaddr(iph->dport());
// Find position of previous hop and next hop
double s_lat = -999, s_lon = -999, d_lat = -999, d_lon = -999;
n = Node::nodehead_.lh_first;
// XXX what if n is not a SatNode?? Need a dynamic cast here, or make sure that
// only sat tracing elements go between sat nodes.
// SatNode *sn = dynamic_cast(n);
if (n) {
lasth = th->last_hop_;
nexth = th->next_hop_;
for (; n; n = n->nextnode() ) {
SatNode *sn = (SatNode*) n;
snadd = sn->address();
if (lasth == snadd) {
s_lat = RAD_TO_DEG(SatGeometry::get_latitude(sn->position()->coord()));
s_lon = RAD_TO_DEG(SatGeometry::get_longitude(sn->position()->coord()));
if (d_lat != -999)
break; // Have now found both s and d
}
if (nexth == snadd) {
d_lat = RAD_TO_DEG(SatGeometry::get_latitude(sn->position()->coord()));
d_lon = RAD_TO_DEG(SatGeometry::get_longitude(sn->position()->coord()));
if (s_lat != -999)
break; // Have now found both s and d
}
}
}
if (!show_tcphdr_) {
sprintf(wrk_, "%c %.4f %d %d %s %d %s %d %s.%s %s.%s %d %d %.2f %.2f %.2f %.2f",
tt,
round(Scheduler::instance().clock()),
lasth,
nexth,
name,
th->size(),
flags,
iph->flowid() /* was p->class_ */,
// iph->src() >> (Address::instance().NodeShift_[1]),
// iph->src() & (Address::instance().PortMask_),
// iph->dst() >> (Address::instance().NodeShift_[1]),
// iph->dst() & (Address::instance().PortMask_),
src_nodeaddr,
src_portaddr,
dst_nodeaddr,
dst_portaddr,
seqno,
th->uid(), /* was p->uid_ */
s_lat,
s_lon,
d_lat,
d_lon);
} else {
sprintf(wrk_,
"%c %.4f %d %d %s %d %s %d %s.%s %s.%s %d %d %d 0x%x %d %d %.2f %.2f %.2f %.2f",
tt,
round(Scheduler::instance().clock()),
lasth,
nexth,
name,
th->size(),
flags,
iph->flowid(), /* was p->class_ */
// iph->src() >> (Address::instance().NodeShift_[1]),
// iph->src() & (Address::instance().PortMask_),
// iph->dst() >> (Address::instance().NodeShift_[1]),
// iph->dst() & (Address::instance().PortMask_),
src_nodeaddr,
src_portaddr,
dst_nodeaddr,
dst_portaddr,
seqno,
th->uid(), /* was p->uid_ */
tcph->ackno(),
tcph->flags(),
tcph->hlen(),
tcph->sa_length(),
s_lat,
s_lon,
d_lat,
d_lon);
}
#ifdef NAM_TRACE
if (namChan_ != 0)
sprintf(nwrk_,
"%c -t %g -s %d -d %d -p %s -e %d -c %d -i %d -a %d -x {%s.%s %s.%s %d %s %s}",
tt,
Scheduler::instance().clock(),
s,
d,
name,
th->size(),
iph->flowid(),
th->uid(),
iph->flowid(),
src_nodeaddr,
src_portaddr,
dst_nodeaddr,
dst_portaddr,
seqno,flags,sname);
#endif
delete [] src_nodeaddr;
delete [] src_portaddr;
delete [] dst_nodeaddr;
delete [] dst_portaddr;
}
void SatTrace::traceonly(Packet* p)
{
format(type_, src_, dst_, p);
dump();
}
//
// we need a DequeTraceClass here because a 'h' event need to go together
// with the '-' event. It's possible to use a postprocessing script, but
// seems that's inconvient.
//
static class SatDequeTraceClass : public TclClass {
public:
SatDequeTraceClass() : TclClass("Trace/Sat/Deque") { }
TclObject* create(int args, const char*const* argv) {
if (args >= 5)
return (new SatDequeTrace(*argv[4]));
return NULL;
}
} sat_dequetrace_class;
void
SatDequeTrace::recv(Packet* p, Handler* h)
{
// write the '-' event first
format(type_, src_, dst_, p);
dump();
namdump();
#ifdef NAM_TRACE
if (namChan_ != 0) {
#ifdef OFF_HDR
hdr_cmn *th = (hdr_cmn*)p->access(off_cmn_);
hdr_ip *iph = (hdr_ip*)p->access(off_ip_);
hdr_srm *sh = (hdr_srm*)p->access(off_srm_);
#else
hdr_cmn *th = hdr_cmn::access(p);
hdr_ip *iph = hdr_ip::access(p);
hdr_srm *sh = hdr_srm::access(p);
#endif
const char* sname = "null";
packet_t t = th->ptype();
const char* name = packet_info.name(t);
if (strcmp(name,"SRM") == 0 || strcmp(name,"cbr") == 0 || strcmp(name,"udp") == 0) {
if ( sh->type() < 5 && sh->type() > 0 ) {
sname = srm_names_[sh->type()];
}
}
char *src_nodeaddr = Address::instance().print_nodeaddr(iph->saddr());
char *src_portaddr = Address::instance().print_portaddr(iph->sport());
char *dst_nodeaddr = Address::instance().print_nodeaddr(iph->daddr());
char *dst_portaddr = Address::instance().print_portaddr(iph->dport());
char flags[NUMFLAGS+1];
for (int i = 0; i < NUMFLAGS; i++)
flags[i] = '-';
flags[NUMFLAGS] = 0;
#ifdef OFF_HDR
hdr_flags* hf = (hdr_flags*)p->access(off_flags_);
#else
hdr_flags* hf = hdr_flags::access(p);
#endif
flags[0] = hf->ecn_ ? 'C' : '-'; // Ecn Echo
flags[1] = hf->pri_ ? 'P' : '-';
flags[2] = '-';
flags[3] = hf->cong_action_ ? 'A' : '-'; // Congestion Action
flags[4] = hf->ecn_to_echo_ ? 'E' : '-'; // Congestion Experienced
flags[5] = hf->fs_ ? 'F' : '-';
flags[6] = hf->ecn_capable_ ? 'N' : '-';
#ifdef notdef
flags[1] = (iph->flags() & PF_PRI) ? 'P' : '-';
flags[2] = (iph->flags() & PF_USR1) ? '1' : '-';
flags[3] = (iph->flags() & PF_USR2) ? '2' : '-';
flags[5] = 0;
#endif
sprintf(nwrk_,
"%c -t %g -s %d -d %d -p %s -e %d -c %d -i %d -a %d -x {%s.%s %s.%s %d %s %s}",
'h',
Scheduler::instance().clock(),
src_,
dst_,
name,
th->size(),
iph->flowid(),
th->uid(),
iph->flowid(),
src_nodeaddr,
src_portaddr,
dst_nodeaddr,
dst_portaddr,
-1, flags, sname);
namdump();
delete [] src_nodeaddr;
delete [] src_portaddr;
delete [] dst_nodeaddr;
delete [] dst_portaddr;
}
#endif
/* hack: if trace object not attached to anything free packet */
if (target_ == 0)
Packet::free(p);
else
send(p, h);
}
scheduler.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1994 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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*
* @(#) $Header$
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (LBL)";
#endif
#include
#include
#include "config.h"
#include "scheduler.h"
#include "packet.h"
#ifdef MEMDEBUG_SIMULATIONS
#include "mem-trace.h"
#endif
#include
Scheduler* Scheduler::instance_;
int Scheduler::uid_ = 1;
// class AtEvent : public Event {
// public:
// char* proc_;
// };
Scheduler::Scheduler() : clock_(SCHED_START), halted_(0)
{
}
/*
* Schedule an event delay time units into the future.
* The event will be dispatched to the specified handler.
* We use a relative time to avoid the problem of scheduling
* something in the past.
*/
void Scheduler::schedule(Handler* h, Event* e, double delay)
{
if (e->uid_ > 0) {
printf("Scheduler: Event UID not valid!\n\n");
abort();
}
if (delay < 0) {
// You probably don't want to do this
// (it probably represents a bug in your simulation).
fprintf(stderr, "warning: ns Scheduler::schedule: scheduling event\n\twith negative delay (%f) at time %f.\n", delay, clock_);
}
if (uid_ < 0 || uid_ >= INT_MAX) {
fprintf(stderr, "Scheduler: UID space exhausted!\n");
abort();
}
e->uid_ = uid_++;
e->handler_ = h;
double t = clock_ + delay;
e->time_ = t;
insert(e);
}
void
Scheduler::run()
{
instance_ = this;
Event *p;
while ((p = deque()) && !halted_) {
dispatch(p);
}
}
/*
* dispatch a single simulator event by setting the system
* virtul clock to the event's timestamp and calling its handler.
* Note that the event may have side effects of placing other items
* in the scheduling queue
*/
void
Scheduler::dispatch(Event* p, double t)
{
if (t < clock_) {
fprintf(stderr, "ns: scheduler going backwards in time from %f to %f.\n", clock_, t);
}
clock_ = t;
p->uid_ = -p->uid_; // being dispatched
p->handler_->handle(p); // dispatch
}
void
Scheduler::dispatch(Event* p)
{
dispatch(p, p->time_);
}
class AtEvent : public Event {
public:
AtEvent() : proc_(0) {
}
~AtEvent() {
if (proc_) delete [] proc_;
}
char* proc_;
};
class AtHandler : public Handler {
public:
void handle(Event* event);
} at_handler;
void AtHandler::handle(Event* e)
{
AtEvent* at = (AtEvent*)e;
Tcl::instance().eval(at->proc_);
delete at;
}
void
Scheduler::reset()
{
clock_ = SCHED_START;
}
int Scheduler::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
if (instance_ == 0)
instance_ = this;
if (argc == 2) {
if (strcmp(argv[1], "run") == 0) {
/* set global to 0 before calling object reset methods */
reset(); // sets clock to zero
run();
return (TCL_OK);
} else if (strcmp(argv[1], "now") == 0) {
sprintf(tcl.buffer(), "%.17g", clock());
tcl.result(tcl.buffer());
return (TCL_OK);
} else if (strcmp(argv[1], "resume") == 0) {
halted_ = 0;
run();
return (TCL_OK);
} else if (strcmp(argv[1], "halt") == 0) {
halted_ = 1;
return (TCL_OK);
} else if (strcmp(argv[1], "clearMemTrace") == 0) {
#ifdef MEMDEBUG_SIMULATIONS
extern MemTrace *globalMemTrace;
if (globalMemTrace)
globalMemTrace->diff("Sim.");
#endif
return (TCL_OK);
} else if (strcmp(argv[1], "is-running") == 0) {
sprintf(tcl.buffer(), "%d", !halted_);
return (TCL_OK);
} else if (strcmp(argv[1], "dumpq") == 0) {
if (!halted_) {
fprintf(stderr, "Scheduler: dumpq only allowed while halted\n");
tcl.result("0");
return (TCL_ERROR);
}
dumpq();
return (TCL_OK);
}
} else if (argc == 3) {
if (strcmp(argv[1], "at") == 0 ||
strcmp(argv[1], "cancel") == 0) {
Event* p = lookup(atoi(argv[2]));
if (p != 0) {
/*XXX make sure it really is an atevent*/
cancel(p);
AtEvent* ae = (AtEvent*)p;
delete ae;
}
} else if (strcmp(argv[1], "at-now") == 0) {
const char* proc = argv[2];
// "at [$ns now]" may not work because of tcl's
// string number resolution
AtEvent* e = new AtEvent;
int n = strlen(proc);
e->proc_ = new char[n + 1];
strcpy(e->proc_, proc);
schedule(&at_handler, e, 0);
sprintf(tcl.buffer(), "%u", e->uid_);
tcl.result(tcl.buffer());
}
return (TCL_OK);
} else if (argc == 4) {
if (strcmp(argv[1], "at") == 0) {
/* t < 0 means relative time: delay = -t */
double delay, t = atof(argv[2]);
const char* proc = argv[3];
AtEvent* e = new AtEvent;
int n = strlen(proc);
e->proc_ = new char[n + 1];
strcpy(e->proc_, proc);
delay = (t < 0) ? -t : t - clock();
if (delay < 0) {
tcl.result("can't schedule command in past");
return (TCL_ERROR);
}
schedule(&at_handler, e, delay);
sprintf(tcl.buffer(), "%u", e->uid_);
tcl.result(tcl.buffer());
return (TCL_OK);
}
}
return (TclObject::command(argc, argv));
}
void
Scheduler::dumpq()
{
Event *p;
printf("Contents of scheduler queue (events) [cur time: %f]---\n",
clock());
while ((p = deque()) != NULL) {
printf("t:%f uid: %d handler: %p\n",
p->time_, p->uid_, p->handler_);
}
}
static class ListSchedulerClass : public TclClass {
public:
ListSchedulerClass() : TclClass("Scheduler/List") {}
TclObject* create(int /* argc */, const char*const* /* argv */) {
return (new ListScheduler);
}
} class_list_sched;
void ListScheduler::insert(Event* e)
{
double t = e->time_;
Event** p;
for (p = &queue_; *p != 0; p = &(*p)->next_)
if (t < (*p)->time_)
break;
e->next_ = *p;
*p = e;
}
/*
* Cancel an event. It is an error to call this routine
* when the event is not actually in the queue. The caller
* must free the event if necessary; this routine only removes
* it from the scheduler queue.
*/
void ListScheduler::cancel(Event* e)
{
Event** p;
if (e->uid_ <= 0) // event not in queue
return;
for (p = &queue_; *p != e; p = &(*p)->next_)
if (*p == 0)
abort();
*p = (*p)->next_;
e->uid_ = - e->uid_;
}
Event* ListScheduler::lookup(int uid)
{
Event* e;
for (e = queue_; e != 0; e = e->next_)
if (e->uid_ == uid)
break;
return (e);
}
Event*
ListScheduler::deque()
{
Event* e = queue_;
if (e)
queue_ = e->next_;
return (e);
}
#include "heap.h"
Heap::Heap(int size)
: h_s_key(0), h_size(0), h_maxsize(size), h_iter(0)
{
h_elems = new Heap_elem[h_maxsize];
memset(h_elems, 0, h_maxsize*sizeof(Heap_elem));
//for (unsigned int i = 0; i < h_maxsize; i++)
// h_elems[i].he_elem = 0;
}
Heap::~Heap()
{
delete [] h_elems;
}
/*
* int heap_member(Heap *h, void *elem): O(n) algorithm.
*
* Returns index(elem \in h->he_elems[]) + 1,
* if elem \in h->he_elems[],
* 0, otherwise.
* External callers should just test for zero, or non-zero.
* heap_delete() uses this routine to find an element in the heap.
*/
int
Heap::heap_member(void* elem)
{
unsigned int i;
Heap::Heap_elem* he;
for (i = 0, he = h_elems; i < h_size; i++, he++)
if (he->he_elem == elem)
return ++i;
return 0;
}
/*
* int heap_delete(Heap *h, void *elem): O(n) algorithm
*
* Returns 1 for success, 0 otherwise.
*
* find elem in h->h_elems[] using heap_member()
*
* To actually remove the element from the tree, first swap it to the
* root (similar to the procedure applied when inserting a new
* element, but no key comparisons--just get it to the root).
*
* Then call heap_extract_min() to remove it & fix the tree.
* This process is not the most efficient, but we do not
* particularily care about how fast heap_delete() is.
* Besides, heap_member() is already O(n),
* and is the dominating cost.
*
* Actually remove the element by calling heap_extract_min().
* The key that is now at the root is not necessarily the
* minimum, but heap_extract_min() does not care--it just
* removes the root.
*/
int
Heap::heap_delete(void* elem)
{
int i;
if ((i = heap_member(elem)) == 0)
return 0;
for (--i; i; i = parent(i)) {
swap(i, parent(i));
}
(void) heap_extract_min();
return 1;
}
/*
* void heap_insert(Heap *h, heap_key_t *key, void *elem)
*
* Insert into heap h.
* Adjust heap_size if we hit the limit.
*
* i := heap_size(h)
* heap_size := heap_size + 1
* while (i > 0 and key < h[Parent(i)])
* do h[i] := h[Parent(i)]
* i := Parent(i)
* h[i] := key
*/
void
Heap::heap_insert(heap_key_t key, void* elem)
{
unsigned int i, par;
if (h_maxsize == h_size) { /* Adjust heap_size */
unsigned int osize = h_maxsize;
Heap::Heap_elem *he_old = h_elems;
h_maxsize *= 2;
h_elems = new Heap::Heap_elem[h_maxsize];
memcpy(h_elems, he_old, osize*sizeof(Heap::Heap_elem));
//for (i = 0; i < osize; i++)
// h_elems[i] = he_old[i];
//delete he_old;
}
i = h_size++;
par = parent(i);
while ((i > 0) &&
(KEY_LESS_THAN(key, h_s_key,
h_elems[par].he_key, h_elems[par].he_s_key))) {
h_elems[i] = h_elems[par];
i = par;
par = parent(i);
}
h_elems[i].he_key = key;
h_elems[i].he_s_key= h_s_key++;
h_elems[i].he_elem = elem;
return;
};
/*
* void *heap_extract_min(Heap *h)
*
* Returns the smallest element in the heap, if it exists.
* NULL otherwise.
*
* if heap_size[h] == 0
* return NULL
* min := h[0]
* heap_size[h] := heap_size[h] - 1 # C array indices start at 0
* h[0] := h[heap_size[h]]
* Heapify:
* i := 0
* while (i < heap_size[h])
* do l = HEAP_LEFT(i)
* r = HEAP_RIGHT(i)
* if (r < heap_size[h])
* # right child exists =>
* # left child exists
* then if (h[l] < h[r])
* then try := l
* else try := r
* else
* if (l < heap_size[h])
* then try := l
* else try := i
* if (h[try] < h[i])
* then HEAP_SWAP(h[i], h[try])
* i := try
* else break
* done
*/
void*
Heap::heap_extract_min()
{
void* min; /* return value */
unsigned int i;
unsigned int l, r, x;
if (h_size == 0)
return 0;
min = h_elems[0].he_elem;
h_elems[0] = h_elems[--h_size];
// Heapify:
i = 0;
while (i < h_size) {
l = left(i);
r = right(i);
if (r < h_size) {
if (KEY_LESS_THAN(h_elems[l].he_key, h_elems[l].he_s_key,
h_elems[r].he_key, h_elems[r].he_s_key))
x= l;
else
x= r;
} else
x = (l < h_size ? l : i);
if ((x != i) &&
(KEY_LESS_THAN(h_elems[x].he_key, h_elems[x].he_s_key,
h_elems[i].he_key, h_elems[i].he_s_key))) {
swap(i, x);
i = x;
} else {
break;
}
}
return min;
}
static class HeapSchedulerClass : public TclClass {
public:
HeapSchedulerClass() : TclClass("Scheduler/Heap") {}
TclObject* create(int /* argc */, const char*const* /* argv */) {
return (new HeapScheduler);
}
} class_heap_sched;
Event* HeapScheduler::lookup(int uid)
{
Event* e;
for (e = (Event*) hp_->heap_iter_init(); e;
e = (Event*) hp_->heap_iter())
if (e->uid_ == uid)
break;
return e;
}
Event*
HeapScheduler::deque()
{
return ((Event*) hp_->heap_extract_min());
}
/*
* Calendar queue scheduler contributed by
* David Wetherall
* March 18, 1997.
*
* A calendar queue basically hashes events into buckets based on
* arrival time.
*
* See R.Brown. "Calendar queues: A fast O(1) priority queue implementation
* for the simulation event set problem."
* Comm. of ACM, 31(10):1220-1227, Oct 1988
*/
static class CalendarSchedulerClass : public TclClass {
public:
CalendarSchedulerClass() : TclClass("Scheduler/Calendar") {}
TclObject* create(int /* argc */, const char*const* /* argv */) {
return (new CalendarScheduler);
}
} class_calendar_sched;
CalendarScheduler::CalendarScheduler() {
reinit(2, 1.0, 0.0);
resizeenabled_ = 1;
max_ = 0.0;
}
CalendarScheduler::~CalendarScheduler() {
delete [] buckets_;
qsize_ = 0;
}
void CalendarScheduler::insert(Event* e)
{
/* (e->time_ * oneonwidth) needs to be less than the
* largest integer on the machine for the mapping
* of time to bucket to work properly. if it is not
* we need to re-calculate the width.
*/
if (e->time_ > max_) {
max_ = e->time_;
if (e->time_ * oneonwidth_ > ULONG_MAX) {
resize(nbuckets_);
}
}
// bucket number and address
int i = (int)(((long)(e->time_ * oneonwidth_)) & buckbits_);
Event** p = buckets_ + i;
// insert event in stable time sorted order
while ((*p != NULL) && (e->time_ >= (*p)->time_))
p = &(*p)->next_;
e->next_ = *p;
*p = e;
if (++qsize_ > top_threshold_)
resize(nbuckets_<<1);
}
Event*
CalendarScheduler::deque()
{
if (qsize_ == 0)
return NULL;
for (;;) {
int i = lastbucket_;
// check for an event this `year'
do {
Event* e = buckets_[i];
if ((e != NULL) && (e->time_ < buckettop_)) {
buckets_[i] = e->next_;
lastbucket_ = i;
last_clock_ = e->time_;
if (--qsize_ < bot_threshold_)
resize(nbuckets_>>1);
return e;
} else {
if (++i == nbuckets_) {
i = 0;
/* Brad Karp, karp@eecs.harvard.edu:
at the end of each year, eliminate roundoff
error in buckettop_ */
buckettop_ = prevtop_ + nbuckets_ * width_;
prevtop_ = buckettop_;
} else {
buckettop_+= width_;
}
}
} while (i != lastbucket_);
// or direct search for the minimum event
int pos = 0;
Event* min;
do {
min = buckets_[pos++];
} while (min == NULL);
pos--;
int k;
for (k = pos+1; k < nbuckets_; k++) {
Event* e = buckets_[k];
if ((e != NULL) && (e->time_ < min->time_)) {
min = e; pos = k;
}
}
// adjust year and resume
lastbucket_ = pos;
last_clock_ = min->time_;
unsigned long n = (unsigned long)(min->time_ * oneonwidth_);
buckettop_ = width_*(n+1.5);
prevtop_ = buckettop_ - lastbucket_ * width_;
}
//return deque();
}
void CalendarScheduler::reinit(int nbuck, double bwidth, double start)
{
buckets_ = new Event*[nbuck];
memset(buckets_, 0, sizeof(Event*)*nbuck);
width_ = bwidth;
oneonwidth_ = 1.0 / width_;
nbuckets_ = nbuck;
buckbits_ = nbuck-1;
qsize_ = 0;
last_clock_ = start;
long n = (long)(start * oneonwidth_);
lastbucket_ = n & buckbits_;
buckettop_ = width_*(n+1.5);
prevtop_ = buckettop_ - lastbucket_ * width_;
bot_threshold_ = (nbuck >> 1) - 2;
top_threshold_ = (nbuck << 1);
}
void CalendarScheduler::resize(int newsize)
{
if (!resizeenabled_) return;
double bwidth = newwidth();
Event** oldb = buckets_;
int oldn = nbuckets_;
// copy events to new buckets
reinit(newsize, bwidth, clock_);
int i;
for (i = oldn-1; i >= 0; i--) {
Event* e = oldb[i];
while (e != NULL) {
Event* en = e->next_;
insert(e);
e = en;
}
}
delete [] oldb;
}
#define MIN_WIDTH (1.0e-6)
#define MAX_HOLD 25
double
CalendarScheduler::newwidth()
{
static Event* hold[MAX_HOLD];
int nsamples;
if (qsize_ < 2) return 1.0;
if (qsize_ < 5) nsamples = qsize_;
else nsamples = 5 + qsize_ / 10;
if (nsamples > MAX_HOLD) nsamples = MAX_HOLD;
// dequue and requeue sample events to gauge width
double olp = clock_;
double olt = buckettop_;
int olb = lastbucket_;
double olbt = prevtop_;
resizeenabled_ = 0;
for (int i = 0; i < nsamples; i++) hold[i] = deque();
// insert in the inverse order and using insert2 to take care of same-time events.
for (int j = nsamples-1; j >= 0; j--) insert2(hold[j]);
resizeenabled_ = 1;
clock_ = olp;
buckettop_ = olt;
prevtop_ = olbt;
lastbucket_ = olb;
// try to work out average cluster separation
double asep = (hold[nsamples-1]->time_ - hold[0]->time_) / (nsamples-1);
double asep2 = 0.0;
double min = (clock_ + 1.0) * MIN_WIDTH;
int count = 0;
for (int k = 1; k < nsamples; k++) {
double diff = hold[k]->time_ - hold[k-1]->time_;
if (diff < 2.0*asep) { asep2 += diff; count++; }
}
// but don't let things get too small for numerical stability
double nw = count ? 3.0*(asep2/count) : asep;
if (nw < min) nw = min;
/* need to make sure that time_/width_ can be represented as
* an int. see the comment at the start of insert().
*/
if (max_/nw > ULONG_MAX) {
nw = max_/ULONG_MAX;
}
return nw;
}
/*
* Cancel an event. It is an error to call this routine
* when the event is not actually in the queue. The caller
* must free the event if necessary; this routine only removes
* it from the scheduler queue.
*
* xxx: we may cancel the last event and invalidate the value of max_
* xxx: thus using wider buckets than really necessary
*/
void CalendarScheduler::cancel(Event* e)
{
int i = (int)(((long)(e->time_ * oneonwidth_)) & buckbits_);
if (e->uid_ <= 0) // event not in queue
return;
for (Event** p = buckets_ + i; (*p) != NULL; p = &(*p)->next_)
if ((*p) == e) {
(*p) = (*p)->next_;
e->uid_ = - e->uid_;
qsize_--;
return;
}
abort();
}
Event* CalendarScheduler::lookup(int uid)
{
for (int i = 0; i < nbuckets_; i++)
for (Event* p = buckets_[i]; p != NULL; p = p->next_)
if (p->uid_== uid) return p;
return NULL;
}
void CalendarScheduler::insert2(Event* e)
{
// Same as insert, but for inserts e *before* any same-time-events, if
// there should be any. Since it is used only by CalendarScheduler::newwidth(),
// some important checks present in insert() need not be performed.
// bucket number and address
int i = (int)(((long)(e->time_ * oneonwidth_)) & buckbits_);
Event** p = buckets_ + i;
// insert event in stable time sorted order
while ((*p != NULL) && (e->time_ > (*p)->time_)) // > instead of >=!
p = &(*p)->next_;
e->next_ = *p;
*p = e;
++qsize_;
}
#ifndef WIN32
#include
#endif
/*
* Really should instance the list/calendar/heap discipline
* inside a RealTimeScheduler or VirtualTimeScheduler
*/
#ifdef notyet
class RealTimeScheduler : public CalendarScheduler {
#endif
class RealTimeScheduler : public ListScheduler {
public:
RealTimeScheduler();
virtual void run();
double start() const { return start_; }
virtual void reset();
protected:
void sync() { clock_ = tod(); }
int rwait(double); // sleep
double tod();
double slop_; // allowed drift between real-time and virt time
double start_; // starting time
};
static class RealTimeSchedulerClass : public TclClass {
public:
RealTimeSchedulerClass() : TclClass("Scheduler/RealTime") {}
TclObject* create(int /* argc */, const char*const* /* argv */) {
return (new RealTimeScheduler);
}
} class_realtime_sched;
RealTimeScheduler::RealTimeScheduler() : start_(0.0)
{
bind("maxslop_", &slop_);
}
double
RealTimeScheduler::tod()
{
timeval tv;
gettimeofday(&tv, 0);
double s = tv.tv_sec;
s += (1e-6 * tv.tv_usec);
return (s - start_);
}
// XXX not used?
// static void nullTimer(ClientData)
// {
// }
void
RealTimeScheduler::reset()
{
clock_ = SCHED_START;
start_ = tod();
}
void RealTimeScheduler::run()
{
Event *p;
double now;
/*XXX*/
instance_ = this;
while (!halted_) {
now = tod();
//if ((clock_ - now) > slop_) {
if ((now - clock_) > slop_) {
fprintf(stderr,
"RealTimeScheduler: warning: slop %f exceeded limit %f [now:%f, clock_:%f\n",
now - clock_, slop_, now, clock_);
}
//
// first handle any "old events"
//
while ((p = deque()) != NULL && (p->time_ <= now)) {
dispatch(p);
}
//
// now handle a "future event", if there is one
//
if (p != NULL) {
int rval = rwait(p->time_);
if (rval < 0) {
fprintf(stderr, "RTScheduler: wait problem\n");
abort();
} else if (rval == 0) {
//
// proper time to dispatch sim event... do so
//
dispatch(p, clock_);
} else {
//
// there was a simulator event which fired, and
// may have added something to the queue, which
// could cause our event p to not be the next,
// so put p back into the event queue and cont
//
insert(p);
}
continue;
}
//
// no sim events to handle at all, check with tcl
//
sync();
Tcl_DoOneEvent(TCL_DONT_WAIT);
}
return; // we reach here only if halted
}
/*
* wait until the specified amount has elapsed, or a tcl event has happened,
* whichever comes first. Return 1 if a tcl event happened, 0 if the
* deadline has been reached, or -1 on error (shouldn't happen).
*/
int
RealTimeScheduler::rwait(double deadline)
{
while (1) {
sync();
if (Tcl_DoOneEvent(TCL_DONT_WAIT) == 1)
return (1);
if (deadline <= tod())
return 0;
}
return -1;
}
scoreboard.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1996 The 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 Network Research
* Group at Lawrence Berkeley National Laboratory.
* 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.
*/
/* 9/96 Pittsburgh Supercomputing Center
* UpdateScoreBoard, CheckSndNxt, MarkRetran modified for fack
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (LBL)";
#endif
/* A quick hack version of the scoreboard */
#include
#include
#include
#include
#include "packet.h"
#include "scoreboard.h"
#include "tcp.h"
#define ASSERT(x) if (!(x)) {printf ("Assert SB failed\n"); exit(1);}
#define ASSERT1(x) if (!(x)) {printf ("Assert1 SB (length)\n"); exit(1);}
#define SBNI SBN[i%SBSIZE]
// last_ack = TCP last ack
int ScoreBoard::UpdateScoreBoard (int last_ack, hdr_tcp* tcph)
{
int i, sack_index, sack_left, sack_right;
int retran_decr = 0;
// If there is no scoreboard, create one.
if (length_ == 0) {
i = last_ack+1;
SBNI.seq_no_ = i;
SBNI.ack_flag_ = 0;
SBNI.sack_flag_ = 0;
SBNI.retran_ = 0;
SBNI.snd_nxt_ = 0;
first_ = i%SBSIZE;
length_++;
if (length_ >= SBSIZE) {
printf ("Error, scoreboard too large (increase SBSIZE for more space)\n");
exit(1);
}
}
for (sack_index=0; sack_index < tcph->sa_length(); sack_index++) {
sack_left = tcph->sa_left(sack_index);
sack_right = tcph->sa_right(sack_index);
// Create new entries off the right side.
if (sack_right > SBN[(first_+length_+SBSIZE-1)%SBSIZE].seq_no_) {
// Create new entries
for (i = SBN[(first_+length_+SBSIZE-1)%SBSIZE].seq_no_+1; i= SBSIZE) {
printf ("Error, scoreboard too large (increase SBSIZE for more space)\n");
exit(1);
}
}
}
// Advance the left edge of the block.
if (SBN[first_].seq_no_ <= last_ack) {
for (i=SBN[(first_)%SBSIZE].seq_no_; i<=last_ack; i++) {
// Advance the ACK
if (SBNI.seq_no_ <= last_ack) {
ASSERT(first_ == i%SBSIZE);
first_ = (first_+1)%SBSIZE;
length_--;
ASSERT1(length_ >= 0);
SBNI.ack_flag_ = 1;
SBNI.sack_flag_ = 1;
if (SBNI.retran_) {
SBNI.retran_ = 0;
SBNI.snd_nxt_ = 0;
retran_decr++;
}
if (length_==0)
break;
}
}
}
for (i=SBN[(first_)%SBSIZE].seq_no_; i= sack_left && SBNI.seq_no_ < sack_right) {
if (! SBNI.sack_flag_) {
SBNI.sack_flag_ = 1;
}
if (SBNI.retran_) {
SBNI.retran_ = 0;
retran_decr++;
}
}
}
}
return (retran_decr);
}
int ScoreBoard::CheckSndNxt (hdr_tcp* tcph)
{
int i, sack_index, sack_left, sack_right;
int force_timeout = 0;
for (sack_index=0; sack_index < tcph->sa_length(); sack_index++) {
sack_left = tcph->sa_left(sack_index);
sack_right = tcph->sa_right(sack_index);
for (i=SBN[(first_)%SBSIZE].seq_no_; iScoreBoard::ClearScoreBoard()
{
length_ = 0;
}
/*
* GetNextRetran() returns "-1" if there is no packet that is
* not acked and not sacked and not retransmitted.
*/
int ScoreBoard::GetNextRetran() // Returns sequence number of next pkt...
{
int i;
if (length_) {
for (i=SBN[(first_)%SBSIZE].seq_no_;
iScoreBoard::MarkRetran (int retran_seqno, int snd_nxt)
{
SBN[retran_seqno%SBSIZE].retran_ = 1;
SBN[retran_seqno%SBSIZE].snd_nxt_ = snd_nxt;
}
void ScoreBoard::MarkRetran (int retran_seqno)
{
SBN[retran_seqno%SBSIZE].retran_ = 1;
}
semantic-packetqueue.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1997 The 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 at Berkeley.
* 4. Neither the name of the University nor of the Research Group 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.
*
* semantic-packetqueue.cc: contributed by the Daedalus Research Group,
* UC Berkeley (http://daedalus.cs.berkeley.edu).
*/
#include "ip.h"
#include "tcp.h"
#include "template.h"
#include "semantic-packetqueue.h"
#include "ack-recons.h"
static class SemanticPacketQueueClass : public TclClass {
public:
SemanticPacketQueueClass() : TclClass("PacketQueue/Semantic") {}
TclObject* create(int , const char*const*) {
return (new SemanticPacketQueue());
}
} class_semanticpacketqueue;
SemanticPacketQueue::SemanticPacketQueue() : ack_count(0), data_count(0),
acks_to_send(0), marked_count_(0), unmarked_count_(0)
{
bind("off_cmn_", &off_cmn_);
bind("off_flags_", &off_flags_);
bind("off_ip_", &off_ip_);
bind("off_tcp_", &off_tcp_);
bind("off_flags_", &off_flags_);
bind_bool("acksfirst_", &acksfirst_);
bind_bool("filteracks_", &filteracks_);
bind_bool("reconsAcks_", &reconsAcks_);
bind_bool("replace_head_", &replace_head_);
bind_bool("priority_drop_", &priority_drop_);
bind_bool("random_drop_", &random_drop_);
bind_bool("random_ecn_", &random_ecn_);
}
int
SemanticPacketQueue::command(int argc, const char*const* argv)
{
if (argc == 3) {
if (strcmp(argv[1], "ackrecons") == 0) {
if ((reconsCtrl_ = (AckReconsController *)
TclObject::lookup(argv[2]))) {
reconsCtrl_->spq_ = this;
reconsAcks_ = 1;
}
}
return (TCL_OK);
}
return (TclObject::command(argc, argv));
}
/*
* Deque TCP acks before any other type of packet.
*/
Packet*
SemanticPacketQueue::deque_acksfirst() {
Packet* p = head_;
Packet* pp = NULL;
packet_t type;
if (ack_count > 0) {
while (p) {
type = ((hdr_cmn*)p->access(off_cmn_))->ptype_;
if (type == PT_ACK)
break;
pp = p;
p = p->next_;
}
if (!p)
fprintf(stderr, "In deque_acksfirst(): ack_count: %d but no acks in queue, length = %d\n", ack_count, length());
PacketQueue::remove(p, pp);
} else {
p = PacketQueue::deque();
}
return p;
}
/*
* Purge the queue of acks that are older (i.e., have a smaller sequence
* number) than the most recent ack. If replace_head is set, the most recent
* ack (pointed to by pkt) takes the place of the oldest ack that is purged.
* Otherwise, it remains at the tail of the queue. pkt must be an ACK -- this
* is checked by the caller.
*/
void
SemanticPacketQueue::filterAcks(Packet *pkt, int replace_head)
{
int done_replacement = 0;
Packet *p, *pp, *new_p;
hdr_tcp *tcph = (hdr_tcp*) pkt->access(off_tcp_);
int &ack = tcph->seqno();
hdr_ip *iph = (hdr_ip*) pkt->access(off_ip_);
for (p = head(), pp = p; p != 0; ) {
/*
* Check if packet in the queue belongs to the
* same connection as the most recent ack
*/
if (compareFlows((hdr_ip*) p->access(off_ip_), iph)) {
/* check if queued packet is an ack */
if (((hdr_cmn*)p->access(off_cmn_))->ptype_==PT_ACK) {
hdr_tcp *th = (hdr_tcp*) p->access(off_tcp_);
/* is this ack older than the current one? */
if ((th->seqno() < ack) ||
(replace_head && th->seqno() == ack)) {
/*
* If we haven't yet replaced the ack
* closest to the head with the most
* recent ack, do so now.
*/
if (replace_head && pkt != p &&
!done_replacement) {
PacketQueue::remove(pkt);
ack_count--; /* XXX */
pkt->next_ = p;
if (pp)
pp->next_ = pkt;
pp = pkt;
done_replacement = 1;
continue;
} else if (done_replacement||pkt != p){
new_p = p->next_;
/*
* If p is in scheduler queue,
* cancel the event. Also,
* print out a warning because
* this should never happen.
*/
Scheduler &s = Scheduler::instance();
if (s.lookup(p->uid_)) {
s.cancel(p);
fprintf(stderr, "Warning: In filterAcks(): packet being dropped from queue is in scheduler queue\n");
}
PacketQueue::remove(p, pp);
/* XXX should drop, but we
don't have access to q */
Packet::free(p);
ack_count--;
p = new_p;
continue;
}
if (ack_count <= 0)
fprintf(stderr,
"oops! ackcount %d\n",
ack_count);
}
}
}
pp = p;
p = p->next_;
}
}
/* check if packet is marked */
int
SemanticPacketQueue::isMarked(Packet *p)
{
return(((hdr_flags*)p->access(off_flags_))->fs_);
}
/* pick out the index'th of the appropriate kind (marked/unmarked) depending on markedFlag */
Packet*
SemanticPacketQueue::lookup(int index, int markedFlag)
{
if (index < 0) {
fprintf(stderr, "In SemanticPacketQueue::lookup(): index = %d\n", index);
return (NULL);
}
for (Packet* p = head_; p != 0; p = p->next_) {
if (isMarked(p) == markedFlag)
if (--index < 0)
return (p);
}
return (NULL);
}
/*
* If random_ecn_ is set, pick out the packet for ECN at random from among the
* packets in the queue and the packet that just arrived ('pkt'). Otherwise, just
* pick the packet that just arrived.
*/
Packet*
SemanticPacketQueue::pickPacketForECN(Packet* pkt)
{
Packet *victim;
int victimIndex;
if (random_ecn_) {
victimIndex = Random::integer(length()+1);
if (victimIndex == length())
victim = pkt;
else
victim = PacketQueue::lookup(victimIndex);
}
else
victim = pkt;
return (victim);
}
/*
* If priority_drop_ is set, drop marked packets before unmarked ones.
* If in addition or separately random_drop_ is set, use randomization in
* picking out the victim. XXX not used at present
*/
Packet*
SemanticPacketQueue::pickPacketToDrop()
{
Packet *victim;
int victimIndex, victimMarked;
if (!priority_drop_) {
if (random_drop_)
victim=PacketQueue::lookup(Random::integer(length()));
else
victim = PacketQueue::lookup(length() - 1);
} else {
/* if there are marked (low priority) packets */
if (marked_count_) {
victimMarked = 1;
if (!random_drop_)
victimIndex = marked_count_ - 1;
else
victimIndex = Random::integer(marked_count_);
}
else {
victimMarked = 0;
if (!random_drop_)
victimIndex = unmarked_count_ - 1;
else
victimIndex = Random::integer(unmarked_count_);
}
victim = lookup(victimIndex, victimMarked);
}
return (victim);
}
void
SemanticPacketQueue::enque(Packet *pkt)
{
if (reconsAcks_&&(((hdr_cmn*)pkt->access(off_cmn_))->ptype_==PT_ACK)) {
reconsCtrl_->recv(pkt);
return;
}
if (((hdr_cmn*)pkt->access(off_cmn_))->ptype_ == PT_ACK)
ack_count++;
else
data_count++;
if (isMarked(pkt))
marked_count_++;
else
unmarked_count_++;
PacketQueue::enque(pkt); /* actually enqueue the packet */
if (filteracks_ && (((hdr_cmn*)pkt->access(off_cmn_))->ptype_==PT_ACK))
filterAcks(pkt, replace_head_);
}
Packet *
SemanticPacketQueue::deque()
{
Packet *pkt;
if (acksfirst_)
pkt = deque_acksfirst();
else
pkt = PacketQueue::deque();
if (pkt) {
if (((hdr_cmn*)pkt->access(off_cmn_))->ptype_ == PT_ACK)
ack_count--;
else
data_count--;
if (isMarked(pkt))
marked_count_--;
else
unmarked_count_--;
}
return pkt;
}
void
SemanticPacketQueue::remove(Packet *pkt)
{
PacketQueue::remove(pkt);
if (pkt) {
if (((hdr_cmn*)pkt->access(off_cmn_))->ptype_ == PT_ACK)
ack_count--;
else
data_count--;
if (isMarked(pkt))
marked_count_--;
else
unmarked_count_--;
}
}
semantic-red.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* A RED queue that allows certain operations to be done in a way
* that depends on higher-layer semantics. The only such operation
* at present is pickPacketToDrop(), which invokes the corresponding
* member function in SemanticPacketQueue.
*/
#include "red.h"
#include "semantic-packetqueue.h"
class SemanticREDQueue : public REDQueue {
public:
SemanticREDQueue() : REDQueue() {}
Packet* pickPacketToDrop() {
return(((SemanticPacketQueue*) pq_)->pickPacketToDrop());
}
Packet* pickPacketForECN(Packet *pkt) {
return(((SemanticPacketQueue*) pq_)->pickPacketForECN(pkt));
}
};
static class SemanticREDClass : public TclClass {
public:
SemanticREDClass() : TclClass("Queue/RED/Semantic") {}
TclObject* create(int, const char*const*) {
return (new SemanticREDQueue);
}
} class_semantic_red;
session-rtp.cc
/* -*- 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 MASH Research
* Group at the University of California Berkeley.
* 4. Neither the name of the University nor of the Research Group 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.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$";
#endif
#include
#include "packet.h"
#include "ip.h"
#include "rtp.h"
static class RTPSourceClass : public TclClass {
public:
RTPSourceClass() : TclClass("RTPSource") {}
TclObject* create(int argc, const char*const* argv) {
if (argc >= 5)
return (new RTPSource(atoi(argv[4])));
return 0;
}
} class_rtp_source;
static class RTPSessionClass : public TclClass {
public:
RTPSessionClass() : TclClass("Session/RTP") {}
TclObject* create(int, const char*const*) {
return (new RTPSession());
}
} class_rtp_session;
RTPSession::RTPSession()
: allsrcs_(0), localsrc_(0), last_np_(0)
{
bind("off_rtp_", &off_rtp_);
}
RTPSession::~RTPSession()
{
while (allsrcs_ != 0) {
RTPSource* p = allsrcs_;
allsrcs_ = allsrcs_->next;
delete p;
}
delete localsrc_;
}
void RTPSession::localsrc_update(int)
{
localsrc_->np(1);
}
#define RTCP_HDRSIZE 8
#define RTCP_SR_SIZE 20
#define RTCP_RR_SIZE 48
int RTPSession::build_report(int bye)
{
int nsrc = 0;
int nrr = 0;
int len = RTCP_HDRSIZE;
int we_sent = 0;
if (localsrc_->np() != last_np_) {
last_np_ = localsrc_->np();
we_sent = 1;
len += RTCP_SR_SIZE;
}
for (RTPSource* sp = allsrcs_; sp != 0; sp = sp->next) {
++nsrc;
int received = sp->np() - sp->snp();
if (received == 0) {
continue;
}
sp->snp(sp->np());
len += RTCP_RR_SIZE;
if (++nrr >= 31)
break;
}
if (bye)
len += build_bye();
else
len += build_sdes();
Tcl::instance().evalf("%s adapt-timer %d %d %d", name(),
nsrc, nrr, we_sent);
Tcl::instance().evalf("%s sample-size %d", name(), len);
return (len);
}
int RTPSession::build_bye()
{
return (8);
}
int RTPSession::build_sdes()
{
/* XXX We'll get to this later... */
return (20);
}
void RTPSession::recv(Packet* p, Handler*)
{
hdr_rtp *rh = (hdr_rtp*)p->access(off_rtp_);
u_int32_t srcid = rh->srcid();
RTPSource* s = lookup(srcid);
if (s == 0) {
Tcl& tcl = Tcl::instance();
tcl.evalf("%s new-source %d", name(), srcid);
s = (RTPSource*)TclObject::lookup(tcl.result());
}
s->np(1);
s->ehsr(rh->seqno());
Packet::free(p);
}
void RTPSession::recv_ctrl(Packet* p)
{
hdr_cmn* ch = (hdr_cmn*)p->access(off_cmn_);
Tcl::instance().evalf("%s sample-size %d", name(), ch->size());
Packet::free(p);
}
/* XXX Should hash this... */
RTPSource* RTPSession::lookup(u_int32_t srcid)
{
RTPSource *p;
for (p = allsrcs_; p != 0; p = p->next)
if (p->srcid() == srcid)
return (p);
return (0);
}
void RTPSession::enter(RTPSource* s)
{
s->next = allsrcs_;
allsrcs_ = s;
}
int RTPSession::command(int argc, const char*const* argv)
{
if (argc == 3) {
if (strcmp(argv[1], "enter") == 0) {
RTPSource* s = (RTPSource*)TclObject::lookup(argv[2]);
enter(s);
return (TCL_OK);
}
if (strcmp(argv[1], "localsrc") == 0) {
localsrc_ = (RTPSource*)TclObject::lookup(argv[2]);
enter(localsrc_);
return (TCL_OK);
}
}
return (TclObject::command(argc, argv));
}
RTPSource::RTPSource(u_int32_t srcid)
: next(0), np_(0), snp_(0), ehsr_(-1)
{
bind("srcid_", (int*)&srcid_);
srcid_ = srcid;
}
sessionhelper.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* sessionhelper.cc
* Copyright (C) 1997 by USC/ISI
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation, advertising
* materials, and other materials related to such distribution and use
* acknowledge that the software was developed by the University of
* Southern California, Information Sciences Institute. The name of the
* University may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* Contributed by Polly Huang (USC/ISI), http://www-scf.usc.edu/~bhuang
*
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (USC/ISI)";
#endif
#include "config.h"
#include "tclcl.h"
#include "ip.h"
#include "packet.h"
#include "connector.h"
#include "errmodel.h"
//Definitions for special reference count events
class RcEvent : public Event {
public:
Packet* packet_;
Handler* real_handler_;
};
class RcHandler : public Handler {
public:
void handle(Event* event);
} rc_handler;
void RcHandler::handle(Event* e)
{
RcEvent* rc = (RcEvent*)e;
rc->real_handler_->handle(rc->packet_);
delete rc;
}
struct dstobj {
double bw;
double delay;
double prev_arrival;
int ttl;
int dropped;
nsaddr_t addr;
NsObject *obj;
dstobj *next;
};
struct rcv_depobj {
dstobj *obj;
rcv_depobj *next;
};
struct loss_depobj {
ErrorModel *obj;
loss_depobj *loss_dep;
rcv_depobj *rcv_dep;
loss_depobj *next;
};
class SessionHelper : public Connector {
public:
SessionHelper();
int command(int, const char*const*);
void recv(Packet*, Handler*);
protected:
void get_dropped(loss_depobj*, Packet*);
void mark_dropped(loss_depobj*);
void clear_dropped();
dstobj* find_dstobj(NsObject*);
void delete_dstobj(NsObject*);
loss_depobj* find_loss_depobj(ErrorModel*);
void show_dstobj();
void show_loss_depobj(loss_depobj*);
nsaddr_t src_;
dstobj *dstobj_;
loss_depobj *loss_dependency_;
int off_ip_;
int ndst_;
int rc_; //enable reference count
};
static class SessionHelperClass : public TclClass {
public:
SessionHelperClass() : TclClass("SessionHelper") {}
TclObject* create(int, const char*const*) {
return (new SessionHelper());
}
} class_sessionhelper;
SessionHelper::SessionHelper() : dstobj_(0), ndst_(0), rc_(0)
{
bind("off_ip_", &off_ip_);
bind("rc_", &rc_);
loss_dependency_ = new loss_depobj;
loss_dependency_->obj = 0;
loss_dependency_->loss_dep = 0;
loss_dependency_->rcv_dep = 0;
loss_dependency_->next = 0;
}
void SessionHelper::recv(Packet* pkt, Handler*)
{
dstobj *tmpdst = dstobj_;
Scheduler& s = Scheduler::instance();
hdr_cmn* th = (hdr_cmn*)pkt->access(off_cmn_);
hdr_ip* iph = (hdr_ip*)pkt->access(off_ip_);
double tmp_arrival;
//printf ("src %d, size %d, iface %d\n", src_, th->size(), th->iface());
clear_dropped();
get_dropped(loss_dependency_->loss_dep, pkt);
if (rc_) {
th->ref_count() = ndst_;
}
while (tmpdst != 0) {
if (!(tmpdst->dropped)) {
int ttl = iph->ttl() - tmpdst->ttl;
if (ttl > 0) {
if (tmpdst->bw == 0) {
tmp_arrival = tmpdst->delay;
} else {
tmp_arrival = th->size()*8/tmpdst->bw + tmpdst->delay;
}
if (tmpdst->prev_arrival >= tmp_arrival) {
tmp_arrival = tmpdst->prev_arrival + 0.000001;
/* Assume 1 ns process delay; just to maintain the causality */
}
tmpdst->prev_arrival = tmp_arrival;
if (rc_) {
// reference count
//s.rc_schedule(tmpdst->obj, pkt, tmp_arrival);
RcEvent* rc = new RcEvent;
rc->packet_ = pkt;
rc->real_handler_ = tmpdst->obj;
s.schedule(&rc_handler, rc, tmp_arrival);
} else {
Packet* tmppkt = pkt->copy();
hdr_ip* tmpiph = (hdr_ip*)tmppkt->access(off_ip_);
tmpiph->ttl() = ttl;
s.schedule(tmpdst->obj, tmppkt, tmp_arrival);
}
} else {
if (rc_) th->ref_count() -= 1;
}
} else {
if (rc_) th->ref_count() -= 1;
}
tmpdst = tmpdst->next;
}
Packet::free(pkt);
}
void SessionHelper::get_dropped(loss_depobj* loss_dep, Packet* pkt)
{
if (loss_dep != 0)
if (loss_dep->obj != 0) {
if (loss_dep->obj->corrupt(pkt)) {
mark_dropped(loss_dep);
} else {
get_dropped(loss_dep->loss_dep, pkt);
}
get_dropped(loss_dep->next, pkt);
}
}
void SessionHelper::mark_dropped(loss_depobj* loss_dep)
{
if (loss_dep != 0) {
rcv_depobj *tmprcv_dep = loss_dep->rcv_dep;
loss_depobj *tmploss_dep = loss_dep->loss_dep;
while (tmprcv_dep != 0) {
tmprcv_dep->obj->dropped = 1;
tmprcv_dep = tmprcv_dep->next;
}
while (tmploss_dep != 0) {
mark_dropped(tmploss_dep);
tmploss_dep = tmploss_dep->next;
}
}
}
void SessionHelper::clear_dropped()
{
dstobj *tmpdst = dstobj_;
while (tmpdst != 0) {
tmpdst->dropped = 0;
tmpdst = tmpdst->next;
}
}
dstobj* SessionHelper::find_dstobj(NsObject* obj) {
dstobj *tmpdst = dstobj_;
while (tmpdst != 0) {
if (tmpdst->obj == obj) return (tmpdst);
tmpdst = tmpdst->next;
}
return 0;
}
loss_depobj* SessionHelper::find_loss_depobj(ErrorModel* err) {
struct stackobj {
loss_depobj *loss_obj;
stackobj *next;
};
if (!loss_dependency_) return 0;
stackobj *top = new stackobj;
top->loss_obj = loss_dependency_;
top->next = 0;
while (top != 0) {
if (top->loss_obj->obj == err) {
loss_depobj *tmp_loss_obj = top->loss_obj;
while (top != 0) {
stackobj *befreed = top;
top = top->next;
free(befreed);
}
return (tmp_loss_obj);
}
loss_depobj *tmploss = top->loss_obj->loss_dep;
stackobj *befreed = top;
top = top->next;
free(befreed);
while (tmploss != 0) {
stackobj *new_element = new stackobj;
new_element->loss_obj = tmploss;
new_element->next = top;
top = new_element;
tmploss = tmploss->next;
}
}
return 0;
}
void SessionHelper::show_dstobj() {
dstobj *tmpdst = dstobj_;
while (tmpdst != 0) {
printf("bw:%.2f, delay:%.2f, ttl:%d, dropped:%d, addr:%d, obj:%s\n", tmpdst->bw, tmpdst->delay, tmpdst->ttl, tmpdst->dropped, tmpdst->addr, tmpdst->obj->name());
tmpdst = tmpdst->next;
}
}
void SessionHelper::delete_dstobj(NsObject *obj) {
dstobj *tmpdst = dstobj_;
dstobj *tmpprev = 0;
while (tmpdst != 0) {
if (tmpdst->obj == obj) {
if (tmpprev == 0) dstobj_ = tmpdst->next;
else tmpprev->next = tmpdst->next;
free(tmpdst);
return;
}
tmpprev = tmpdst;
tmpdst = tmpdst->next;
}
}
void SessionHelper::show_loss_depobj(loss_depobj *loss_obj) {
loss_depobj *tmploss = loss_obj->loss_dep;
rcv_depobj *tmprcv = loss_obj->rcv_dep;
while (tmprcv != 0) {
printf("%d ", tmprcv->obj->addr);
tmprcv = tmprcv->next;
}
while (tmploss != 0) {
printf("(%s: ", tmploss->obj->name());
show_loss_depobj(tmploss);
tmploss = tmploss->next;
}
if (loss_obj == loss_dependency_) {
printf("\n");
} else {
printf(")");
}
}
int SessionHelper::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
if (argc == 2) {
if (strcmp(argv[1], "list-mbr") == 0) {
dstobj *tmp = dstobj_;
while (tmp != 0) {
tcl.resultf("%s %s", tcl.result(),
tmp->obj->name());
tmp = tmp->next;
}
return (TCL_OK);
}
if (strcmp(argv[1], "show-loss-depobj") == 0) {
show_loss_depobj(loss_dependency_);
return (TCL_OK);
}
if (strcmp(argv[1], "show-dstobj") == 0) {
show_dstobj();
return (TCL_OK);
}
} else if (argc == 3) {
if (strcmp(argv[1], "set-node") == 0) {
int src = atoi(argv[2]);
src_ = src;
//printf("set node %d\n", src_);
return (TCL_OK);
}
if (strcmp(argv[1], "update-loss-top") == 0) {
loss_depobj *tmploss = (loss_depobj*)(atoi(argv[2]));
tmploss->next = loss_dependency_->loss_dep;
loss_dependency_->loss_dep = tmploss;
return (TCL_OK);
}
} else if (argc == 4) {
if (strcmp(argv[1], "update-loss-rcv") == 0) {
ErrorModel *tmperr = (ErrorModel*)TclObject::lookup(argv[2]);
NsObject *tmpobj = (NsObject*)TclObject::lookup(argv[3]);
//printf("errmodel %s, agent %s\n", tmperr->name(), tmpobj->name());
loss_depobj *tmploss = find_loss_depobj(tmperr);
//printf ("%d, loss_dependency_ %d\n", tmploss, loss_dependency_);
if (!tmploss) {
tmploss = new loss_depobj;
tmploss->obj = tmperr;
tmploss->next = 0;
tmploss->rcv_dep = 0;
tmploss->loss_dep = 0;
tcl.resultf("%d", tmploss);
} else {
tcl.result("0");
}
rcv_depobj *tmprcv = new rcv_depobj;
tmprcv->obj = find_dstobj(tmpobj);
tmprcv->next = tmploss->rcv_dep;
tmploss->rcv_dep = tmprcv;
return (TCL_OK);
}
if (strcmp(argv[1], "update-loss-loss") == 0) {
ErrorModel *tmperrparent = (ErrorModel*)TclObject::lookup(argv[2]);
loss_depobj *tmplossparent = find_loss_depobj(tmperrparent);
loss_depobj *tmplosschild = (loss_depobj*)(atoi(argv[3]));
if (!tmplossparent) {
tmplossparent = new loss_depobj;
tmplossparent->obj = tmperrparent;
tmplossparent->next = 0;
tmplossparent->loss_dep = 0;
tmplossparent->rcv_dep = 0;
tcl.resultf("%d", tmplossparent);
} else {
tcl.result("0");
}
tmplosschild->next = tmplossparent->loss_dep;
tmplossparent->loss_dep = tmplosschild;
return (TCL_OK);
}
if (strcmp(argv[1], "delete-dst") == 0) {
int tmpaddr = atoi(argv[2]);
//NsObject *tmpobj = (NsObject*)TclObject::lookup(argv[3]);
printf ("addr = %d\n", tmpaddr);
return (TCL_OK);
}
} else if (argc == 7) {
if (strcmp(argv[1], "add-dst") == 0) {
dstobj *tmp = new dstobj;
tmp->bw = atof(argv[2]);
tmp->delay = atof(argv[3]);
tmp->prev_arrival = 0;
tmp->ttl = atoi(argv[4]);
tmp->addr = atoi(argv[5]);
tmp->obj = (NsObject*)TclObject::lookup(argv[6]);
//printf ("addr = %d, argv3 = %s, obj = %d, ttl=%d\n", tmp->addr, argv[3], tmp->obj, tmp->ttl);
tmp->next = dstobj_;
dstobj_ = tmp;
ndst_ += 1;
return (TCL_OK);
}
}
return (Connector::command(argc, argv));
}
sfq.cc
/* -*- 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 MASH Research
* Group at the University of California Berkeley.
* 4. Neither the name of the University nor of the Research Group 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.
*
* This file contributed by Curtis Villamizar < curtis@ans.net >, Feb 1997.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (ANS)";
#endif
#include
#include "config.h"
#include "queue.h"
class PacketSFQ; // one queue
class SFQ; // a set of SFQ queues
class PacketSFQ : public PacketQueue {
PacketSFQ() : pkts(0), prev(0), next(0) {}
friend SFQ;
protected:
void sfqdebug();
int pkts;
PacketSFQ *prev;
PacketSFQ *next;
inline PacketSFQ * activate(PacketSFQ *head) {
if (head) {
this->prev = head->prev;
this->next = head;
head->prev->next = this;
head->prev = this;
return head;
}
this->prev = this;
this->next = this;
return this;
}
inline PacketSFQ * idle(PacketSFQ *head) {
if (head == this) {
if (this->next == this)
return 0;
this->next->prev = this->prev;
this->prev->next = this->next;
return this->next;
}
return head;
}
};
class SFQ : public Queue {
public:
SFQ();
virtual int command(int argc, const char*const* argv);
Packet *deque(void);
void enque(Packet *pkt);
protected:
int maxqueue_; // max queue size in packets
int buckets_; // number of queues
PacketSFQ *bucket;
void initsfq();
void clear();
int hash(Packet *);
PacketSFQ *active;
int occupied;
int fairshare;
int off_ip_;
};
static class SFQClass : public TclClass {
public:
SFQClass() : TclClass("Queue/SFQ") {}
TclObject* create(int, const char*const*) {
return (new SFQ);
}
} class_sfq;
SFQ::SFQ()
{
maxqueue_ = 40;
buckets_ = 16;
bucket = 0;
active = 0;
bind("maxqueue_", &maxqueue_);
bind("buckets_", &buckets_);
bind("off_ip_", &off_ip_);
}
void SFQ::clear()
{
PacketSFQ *q = bucket;
int i = buckets_;
if (!q)
return;
while (i) {
if (q->pkts) {
fprintf(stderr, "SFQ changed while queue occupied\n");
exit(1);
}
++q;
}
delete[](bucket);
bucket = 0;
}
void SFQ::initsfq()
{
bucket = new PacketSFQ[buckets_];
active = 0;
occupied = 0;
fairshare = maxqueue_ / buckets_;
// fprintf(stderr, "SFQ initsfq: %d %d\n", maxqueue_, buckets_);
}
/*
* This implements the following tcl commands:
* $sfq limit $size
* $sfq buckets $num
*/
int SFQ::command(int argc, const char*const* argv)
{
if (argc == 3) {
if (strcmp(argv[1], "limit") == 0) {
maxqueue_ = atoi(argv[2]);
fairshare = maxqueue_ / buckets_;
return (TCL_OK);
}
if (strcmp(argv[1], "buckets") == 0) {
clear();
buckets_ = atoi(argv[2]);
return (TCL_OK);
}
}
return (Queue::command(argc, argv));
}
void PacketSFQ::sfqdebug()
{
PacketSFQ *q = this;
fprintf(stderr, "sfq: ");
while (q) {
fprintf(stderr, " 0x%p(%d)", q, q->pkts);
q = q->next;
if (q == this)
break;
}
fprintf(stderr, "\n");
}
Packet* SFQ::deque(void)
{
Packet* pkt;
if (!bucket)
initsfq();
if (!active) {
// fprintf(stderr, " dequeue: empty\n");
return (0);
}
--active->pkts;
--occupied;
pkt = active->deque();
// fprintf(stderr, "dequeue 0x%x(%d): 0x%x\n",
// (int)active, active->pkts, (int)pkt);
// active->sfqdebug();
if (active->pkts == 0)
active = active->idle(active);
else
active = active->next;
return pkt;
}
void SFQ::enque(Packet* pkt)
{
int which;
PacketSFQ *q;
int used, left;
if (!bucket)
initsfq();
which = hash(pkt) % buckets_;
q = &bucket[which];
// log_packet_arrival(pkt);
used = q->pkts;
left = maxqueue_ - occupied;
// note: if maxqueue_ is changed while running left can be < 0
if ((used >= (left >> 1))
|| (left < buckets_ && used > fairshare)
|| (left <= 0)) {
// log_packet_drop(pkt);
drop(pkt);
// fprintf(stderr, " drop: 0x%x\n", (int)pkt);
return;
}
q->enque(pkt);
++occupied;
++q->pkts;
if (q->pkts == 1)
active = q->activate(active);
// fprintf(stderr, " enqueue(%d=%d): 0x%x\n", which, q->pkts, (int)pkt);
// active->sfqdebug();
}
int SFQ::hash(Packet* pkt)
{
hdr_ip* iph = (hdr_ip*)pkt->access(off_ip_);
int i = (int)iph->saddr();
int j = (int)iph->daddr();
int k = i + j;
return (k + (k >> 8) + ~(k >> 4)) % ((2<<19)-1); // modulo a large prime
}
simple-intserv-sched.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) Xerox Corporation 1997. All rights reserved.
*
* License is granted to copy, to use, and to make and to use derivative
* works for research and evaluation purposes, provided that Xerox is
* acknowledged in all documentation pertaining to any such copy or
* derivative work. Xerox grants no other licenses expressed or
* implied. The Xerox trade name should not be used in any advertising
* without its written permission.
*
* XEROX CORPORATION MAKES NO REPRESENTATIONS CONCERNING EITHER THE
* MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE
* FOR ANY PARTICULAR PURPOSE. The software is provided "as is" without
* express or implied warranty of any kind.
*
* These notices must be retained in any copies of any part of this
* software.
*/
/*
* Copyright (c) 1994 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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (LBL)";
#endif
//Simple scheduler with 2 service priority levels and protects signalling
//ctrl packets
#include "config.h"
#include "queue.h"
#define CLASSES 2
class SimpleIntServ : public Queue {
public:
SimpleIntServ() {
int i;
char buf[10];
for (i=0;iSimpleIntServ::enque(Packet* p)
{
hdr_ip* iph=(hdr_ip*)p->access(off_ip_);
int cl=(iph->flowid()) ? 1:0;
if (q_[cl]->length() >= (qlimit_[cl]-1)) {
hdr_cmn* ch=(hdr_cmn*)p->access(off_cmn_);
packet_t ptype = ch->ptype();
if ( (ptype != PT_REQUEST) && (ptype != PT_REJECT) && (ptype != PT_ACCEPT) && (ptype != PT_CONFIRM) && (ptype != PT_TEARDOWN) ) {
drop(p);
}
else {
q_[cl]->enque(p);
}
}
else {
q_[cl]->enque(p);
}
}
Packet *SimpleIntServ::deque()
{
int i;
for (i=CLASSES-1;i>=0;i--)
if (q_[i]->length())
return q_[i]->deque();
return 0;
}
snoop.cc
/* -*- 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 Research Group 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.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (UCB)";
#endif
#include "snoop.h"
int hdr_snoop::offset_;
class SnoopHeaderClass : public PacketHeaderClass {
public:
SnoopHeaderClass() : PacketHeaderClass("PacketHeader/Snoop",
sizeof(hdr_snoop)) {
bind_offset(&hdr_snoop::offset_);
}
} class_snoophdr;
static class LLSnoopClass : public TclClass {
public:
LLSnoopClass() : TclClass("LL/LLSnoop") {}
TclObject* create(int, const char*const*) {
return (new LLSnoop());
}
} llsnoop_class;
static class SnoopClass : public TclClass {
public:
SnoopClass() : TclClass("Snoop") {}
TclObject* create(int, const char*const*) {
return (new Snoop());
}
} snoop_class;
Snoop::Snoop() : NsObject(),
fstate_(0), lastSeen_(-1), lastAck_(-1),
expNextAck_(0), expDupacks_(0), bufhead_(0),
toutPending_(0), buftail_(0),
wl_state_(SNOOP_WLEMPTY), wl_lastSeen_(-1), wl_lastAck_(-1),
wl_bufhead_(0), wl_buftail_(0)
{
bind("snoopDisable_", &snoopDisable_);
bind_time("srtt_", &srtt_);
bind_time("rttvar_", &rttvar_);
bind("maxbufs_", &maxbufs_);
bind("snoopTick_", &snoopTick_);
bind("g_", &g_);
bind("tailTime_", &tailTime_);
bind("rxmitStatus_", &rxmitStatus_);
bind("lru_", &lru_);
rxmitHandler_ = new SnoopRxmitHandler(this);
int i;
for (i = 0; i < SNOOP_MAXWIND; i++) /* data from wired->wireless */
pkts_[i] = 0;
for (i = 0; i < SNOOP_WLSEQS; i++) {/* data from wireless->wired */
wlseqs_[i] = (hdr_seq *) malloc(sizeof(hdr_seq));
wlseqs_[i]->seq = wlseqs_[i]->num = 0;
}
if (maxbufs_ == 0)
maxbufs_ = SNOOP_MAXWIND;
}
void
Snoop::reset()
{
// printf("%x resetting\n", this);
fstate_ = 0;
lastSeen_ = -1;
lastAck_ = -1;
expNextAck_ = 0;
expDupacks_ = 0;
bufhead_ = buftail_ = 0;
if (toutPending_)
Scheduler::instance().cancel(toutPending_);
toutPending_ = 0;
for (int i = 0; i < SNOOP_MAXWIND; i++) {
if (pkts_[i]) {
Packet::free(pkts_[i]);
pkts_[i] = 0;
}
}
}
void
Snoop::wlreset()
{
wl_state_ = SNOOP_WLEMPTY;
wl_bufhead_ = wl_buftail_ = 0;
for (int i = 0; i < SNOOP_WLSEQS; i++) {
wlseqs_[i]->seq = wlseqs_[i]->num = 0;
}
}
int
Snoop::command(int argc, const char*const* argv)
{
//Tcl& tcl = Tcl::instance();
if (argc == 3) {
if (strcmp(argv[1], "llsnoop") == 0) {
parent_ = (LLSnoop *) TclObject::lookup(argv[2]);
if (parent_)
recvtarget_ = parent_->uptarget();
return (TCL_OK);
}
if (strcmp(argv[1], "check-rxmit") == 0) {
if (empty_()) {
rxmitStatus_ = SNOOP_PROPAGATE;
return (TCL_OK);
}
Packet *p = pkts_[buftail_];
hdr_snoop *sh = hdr_snoop::access(p);
if (sh->sndTime()!=-1 && sh->sndTime()numRxmit() == 0)
/* candidate for retransmission */
rxmitStatus_ = snoop_rxmit(p);
else
rxmitStatus_ = SNOOP_PROPAGATE;
return (TCL_OK);
}
}
return NsObject::command(argc, argv);
}
void LLSnoop::recv(Packet *p, Handler *h)
{
Tcl &tcl = Tcl::instance();
hdr_ip *iph = hdr_ip::access(p);
/* get-snoop creates a snoop object if none currently exists */
if (h == 0)
/* In ns, addresses have ports embedded in them. */
tcl.evalf("%s get-snoop %d %d", name(), iph->daddr(),
iph->saddr());
else
tcl.evalf("%s get-snoop %d %d", name(), iph->saddr(),
iph->daddr());
Snoop *snoop = (Snoop *) TclObject::lookup(tcl.result());
snoop->recv(p, h);
if (integrate_)
tcl.evalf("%s integrate %d %d", name(), iph->saddr(),
iph->daddr());
if (h) /* resume higher layer (queue) */
Scheduler::instance().schedule(h, &intr_, 0.000001);
return;
}
/*
* Receive a packet from higher layer or from the network.
* Call snoop_data() if TCP packet and forward it on if it's an ack.
*/
void
Snoop::recv(Packet* p, Handler* h)
{
if (h == 0) { // from MAC classifier
handle((Event *) p);
return;
}
packet_t type = hdr_cmn::access(p)->ptype();
/* Put packet (if not ack) in cache after checking, and send it on */
if (type == PT_TCP)
snoop_data(p);
else if (type == PT_ACK)
snoop_wired_ack(p);
parent_->sendDown(p); /* vector to LLSnoop's sendto() */
}
/*
* Handle a packet received from peer across wireless link. Check first
* for packet errors, then call snoop_ack() or pass it up as necessary.
*/
void
Snoop::handle(Event *e)
{
Packet *p = (Packet *) e;
packet_t type = hdr_cmn::access(p)->ptype();
//int seq = hdr_tcp::access(p)->seqno();
int prop = SNOOP_PROPAGATE; // by default; propagate ack or packet
Scheduler& s = Scheduler::instance();
//hdr_ll *llh = hdr_ll::access(p);
if (hdr_cmn::access(p)->error()) {
parent_->drop(p); // drop packet if it's been corrupted
return;
}
if (type == PT_ACK)
prop = snoop_ack(p);
else if (type == PT_TCP) /* XXX what about TELNET? */
snoop_wless_data(p);
if (prop == SNOOP_PROPAGATE)
s.schedule(recvtarget_, e, parent_->delay());
else { // suppress ack
/* printf("---- %f suppressing ack %d\n", s.clock(), seq);*/
Packet::free(p);
}
}
/*
* Data packet processing. p is guaranteed to be of type PT_TCP when
* this function is called.
*/
void
Snoop::snoop_data(Packet *p)
{
Scheduler &s = Scheduler::instance();
int seq = hdr_tcp::access(p)->seqno();
int resetPending = 0;
// printf("%x snoop_data: %f sending packet %d\n", this, s.clock(), seq);
if (fstate_ & SNOOP_ALIVE && seq == 0)
reset();
fstate_ |= SNOOP_ALIVE;
if ((fstate_ & SNOOP_FULL) && !lru_) {
// printf("snoop full, fwd'ing\n t %d h %d", buftail_, bufhead_);
if (seq > lastSeen_)
lastSeen_ = seq;
return;
}
/*
* Only if the ifq is NOT full do we insert, since otherwise we want
* congestion control to kick in.
*/
if (parent_->ifq()->length() < parent_->ifq()->limit()-1)
resetPending = snoop_insert(p);
if (toutPending_ && resetPending == SNOOP_TAIL) {
s.cancel(toutPending_);
toutPending_ = 0;
}
if (!toutPending_ && !empty_()) {
toutPending_ = (Event *) (pkts_[buftail_]);
s.schedule(rxmitHandler_, toutPending_, timeout());
}
return;
}
/*
* snoop_insert() does all the hard work for snoop_data(). It traverses the
* snoop cache and looks for the right place to insert this packet (or
* determines if its already been cached). It then decides whether
* this is a packet in the normal increasing sequence, whether it
* is a sender-rexmitted-but-lost-due-to-congestion (or network
* out-of-order) packet, or if it is a sender-rexmitted packet that
* was buffered by us before.
*/
int
Snoop::snoop_insert(Packet *p)
{
int i, seq = hdr_tcp::access(p)->seqno(), retval=0;
if (seq <= lastAck_)
return retval;
if (fstate_ & SNOOP_FULL) {
/* free tail and go on */
printf("snoop full, making room\n");
Packet::free(pkts_[buftail_]);
pkts_[buftail_] = 0;
buftail_ = next(buftail_);
fstate_ |= ~SNOOP_FULL;
}
if (seq > lastSeen_ || pkts_[buftail_] == 0) { // in-seq or empty cache
i = bufhead_;
bufhead_ = next(bufhead_);
} else if (seq < hdr_snoop::access(pkts_[buftail_])->seqno()) {
buftail_ = prev(buftail_);
i = buftail_;
} else {
for (i = buftail_; i != bufhead_; i = next(i)) {
hdr_snoop *sh = hdr_snoop::access(pkts_[i]);
if (sh->seqno() == seq) {
sh->numRxmit() = 0;
sh->senderRxmit() = 1;
sh->sndTime() = Scheduler::instance().clock();
return SNOOP_TAIL;
} else if (sh->seqno() > seq) {
Packet *temp = pkts_[prev(buftail_)];
for (int j = buftail_; j != i; j = next(j))
pkts_[prev(j)] = pkts_[j];
i = prev(i);
pkts_[i] = temp;
buftail_ = prev(buftail_);
break;
}
}
if (i == bufhead_)
bufhead_ = next(bufhead_);
}
savepkt_(p, seq, i);
if (bufhead_ == buftail_)
fstate_ |= SNOOP_FULL;
/*
* If we have one of the following packets:
* 1. a network-out-of-order packet, or
* 2. a fast rxmit packet, or 3. a sender retransmission
* AND it hasn't already been buffered,
* then seq will be < lastSeen_.
* We mark this packet as having been due to a sender rexmit
* and use this information in snoop_ack(). We let the dupacks
* for this packet go through according to expDupacks_.
*/
if (seq < lastSeen_) { /* not in-order -- XXX should it be <= ? */
if (buftail_ == i) {
hdr_snoop *sh = hdr_snoop::access(pkts_[i]);
sh->senderRxmit() = 1;
sh->numRxmit() = 0;
}
expNextAck_ = buftail_;
retval = SNOOP_TAIL;
} else
lastSeen_ = seq;
return retval;
}
void
Snoop::savepkt_(Packet *p, int seq, int i)
{
pkts_[i] = p->copy();
Packet *pkt = pkts_[i];
hdr_snoop *sh = hdr_snoop::access(pkt);
sh->seqno() = seq;
sh->numRxmit() = 0;
sh->senderRxmit() = 0;
sh->sndTime() = Scheduler::instance().clock();
}
/*
* Ack processing in snoop protocol. We know for sure that this is an ack.
* Return SNOOP_SUPPRESS if ack is to be suppressed and SNOOP_PROPAGATE o.w.
*/
int
Snoop::snoop_ack(Packet *p)
{
Packet *pkt;
int ack = hdr_tcp::access(p)->seqno();
/*
* There are 3 cases:
* 1. lastAck_ > ack. In this case what has happened is
* that the acks have come out of order, so we don't
* do any local processing but forward it on.
* 2. lastAck_ == ack. This is a duplicate ack. If we have
* the packet we resend it, and drop the dupack.
* Otherwise we never got it from the fixed host, so we
* need to let the dupack get through.
* Set expDupacks_ to number of packets already sent
* This is the number of dup acks to ignore.
* 3. lastAck_ < ack. Set lastAck_ = ack, and update
* the head of the buffer queue. Also clean up ack'd packets.
*/
if (fstate_ & SNOOP_CLOSED || lastAck_ > ack)
return SNOOP_PROPAGATE; // send ack onward
if (lastAck_ == ack) {
/* A duplicate ack; pure window updates don't occur in ns. */
pkt = pkts_[buftail_];
if (pkt == 0)
return SNOOP_PROPAGATE;
hdr_snoop *sh = hdr_snoop::access(pkt);
if (pkt == 0 || sh->seqno() > ack + 1)
/* don't have packet, letting thru' */
return SNOOP_PROPAGATE;
/*
* We have the packet: one of 3 possibilities:
* 1. We are not expecting any dupacks (expDupacks_ == 0)
* 2. We are expecting dupacks (expDupacks_ > 0)
* 3. We are in an inconsistent state (expDupacks_ == -1)
*/
if (expDupacks_ == 0) { // not expecting it
#define RTX_THRESH 1
static int thresh = 0;
if (thresh++ < RTX_THRESH) {
/* no action if under RTX_THRESH */
return SNOOP_PROPAGATE;
}
thresh = 0;
if (sh->senderRxmit())
return SNOOP_PROPAGATE;
/*
* Otherwise, not triggered by sender. If this is
* the first dupack recd., we must determine how many
* dupacks will arrive that must be ignored, and also
* rexmit the desired packet. Note that expDupacks_
* will be -1 if we miscount for some reason.
*/
expDupacks_ = bufhead_ - expNextAck_;
if (expDupacks_ < 0)
expDupacks_ += SNOOP_MAXWIND;
expDupacks_ -= RTX_THRESH + 1;
expNextAck_ = next(buftail_);
if (sh->numRxmit() == 0)
return snoop_rxmit(pkt);
} else if (expDupacks_ > 0) {
expDupacks_--;
return SNOOP_SUPPRESS;
} else if (expDupacks_ == -1) {
if (sh->numRxmit() < 2) {
return snoop_rxmit(pkt);
}
} else // let sender deal with it
return SNOOP_PROPAGATE;
} else { // a new ack
fstate_ &= ~SNOOP_NOACK; // have seen at least 1 new ack
/* free buffers */
double sndTime = snoop_cleanbufs_(ack);
if (sndTime != -1)
snoop_rtt(sndTime);
expDupacks_ = 0;
expNextAck_ = buftail_;
lastAck_ = ack;
}
return SNOOP_PROPAGATE;
}
/*
* Handle data packets that arrive from a wireless link, and we're not
* the end recipient. See if there are any holes in the transmission, and
* if there are, mark them as candidates for wireless loss. Then, when
* (dup)acks troop back for this loss, set the ELN bit in their header, to
* help the sender (or a snoop agent downstream) retransmit.
*/
void
Snoop::snoop_wless_data(Packet *p)
{
hdr_tcp *th = hdr_tcp::access(p);
int i, seq = th->seqno();
if (wl_state_ & SNOOP_WLALIVE && seq == 0)
wlreset();
wl_state_ |= SNOOP_WLALIVE;
if (wl_state_ & SNOOP_WLEMPTY && seq >= wl_lastAck_) {
wlseqs_[wl_bufhead_]->seq = seq;
wlseqs_[wl_bufhead_]->num = 1;
wl_buftail_ = wl_bufhead_;
wl_bufhead_ = wl_next(wl_bufhead_);
wl_lastSeen_ = seq;
wl_state_ &= ~SNOOP_WLEMPTY;
return;
}
/* WL data list definitely not empty at this point. */
if (seq >= wl_lastSeen_) {
wl_lastSeen_ = seq;
i = wl_prev(wl_bufhead_);
if (wlseqs_[i]->seq + wlseqs_[i]->num == seq) {
wlseqs_[i]->num++;
return;
}
i = wl_bufhead_;
wl_bufhead_ = wl_next(wl_bufhead_);
} else if (seq == wlseqs_[i = wl_buftail_]->seq - 1) {
} else
return;
wlseqs_[i]->seq = seq;
wlseqs_[i]->num++;
/* Ignore network out-of-ordering and retransmissions for now */
return;
}
/*
* Ack from wired side (for sender on "other" side of wireless link.
*/
void
Snoop::snoop_wired_ack(Packet *p)
{
hdr_tcp *th = hdr_tcp::access(p);
int ack = th->seqno();
int i;
if (ack == wl_lastAck_ && snoop_wlessloss(ack)) {
hdr_flags::access(p)->eln_ = 1;
} else if (ack > wl_lastAck_) {
/* update info about unack'd data */
for (i = wl_buftail_; i != wl_bufhead_; i = wl_next(i)) {
hdr_seq *t = wlseqs_[i];
if (t->seq + t->num - 1 <= ack) {
t->seq = t->num = 0;
} else if (ack < t->seq) {
break;
} else if (ack < t->seq + t->num - 1) {
/* ack for part of a block */
t->num -= ack - t->seq +1;
t->seq = ack + 1;
break;
}
}
wl_buftail_ = i;
if (wl_buftail_ == wl_bufhead_)
wl_state_ |= SNOOP_WLEMPTY;
wl_lastAck_ = ack;
/* Even a new ack could cause an ELN to be set. */
if (wl_bufhead_ != wl_buftail_ && snoop_wlessloss(ack))
hdr_flags::access(p)->eln_ = 1;
}
}
/*
* Return 1 if we think this packet loss was not congestion-related, and
* 0 otherwise. This function simply implements the lookup into the table
* that maintains this info; most of the hard work is done in
* snoop_wless_data() and snoop_wired_ack().
*/
int
Snoop::snoop_wlessloss(int ack)
{
if ((wl_bufhead_ == wl_buftail_) || wlseqs_[wl_buftail_]->seq > ack+1)
return 1;
return 0;
}
/*
* clean snoop cache of packets that have been acked.
*/
double
Snoop::snoop_cleanbufs_(int ack)
{
Scheduler &s = Scheduler::instance();
double sndTime = -1;
if (toutPending_)
s.cancel(toutPending_);
toutPending_ = 0;
if (empty_())
return sndTime;
int i = buftail_;
do {
hdr_snoop *sh = hdr_snoop::access(pkts_[i]);
int seq = hdr_tcp::access(pkts_[i])->seqno();
if (seq <= ack) {
sndTime = sh->sndTime();
Packet::free(pkts_[i]);
pkts_[i] = 0;
fstate_ &= ~SNOOP_FULL; /* XXX redundant? */
} else if (seq > ack)
break;
i = next(i);
} while (i != bufhead_);
if ((i != buftail_) || (bufhead_ != buftail_)) {
fstate_ &= ~SNOOP_FULL;
buftail_ = i;
}
if (!empty_()) {
toutPending_ = (Event *) (pkts_[buftail_]);
s.schedule(rxmitHandler_, toutPending_, timeout());
hdr_snoop *sh = hdr_snoop::access(pkts_[buftail_]);
tailTime_ = sh->sndTime();
}
return sndTime;
}
/*
* Calculate smoothed rtt estimate and linear deviation.
*/
void
Snoop::snoop_rtt(double sndTime)
{
double rtt = Scheduler::instance().clock() - sndTime;
if (parent_->integrate()) {
parent_->snoop_rtt(sndTime);
return;
}
if (rtt > 0) {
srtt_ = g_*srtt_ + (1-g_)*rtt;
double delta = rtt - srtt_;
if (delta < 0)
delta = -delta;
if (rttvar_ != 0)
rttvar_ = g_*delta + (1-g_)*rttvar_;
else
rttvar_ = delta;
}
}
/*
* Calculate smoothed rtt estimate and linear deviation.
*/
void
LLSnoop::snoop_rtt(double sndTime)
{
double rtt = Scheduler::instance().clock() - sndTime;
if (rtt > 0) {
srtt_ = g_*srtt_ + (1-g_)*rtt;
double delta = rtt - srtt_;
if (delta < 0)
delta = -delta;
if (rttvar_ != 0)
rttvar_ = g_*delta + (1-g_)*rttvar_;
else
rttvar_ = delta;
}
}
/*
* Returns 1 if recent queue length is <= half the maximum and 0 otherwise.
*/
int
Snoop::snoop_qlong()
{
/* For now only instantaneous lengths */
if (parent_->ifq()->length() <= 3*parent_->ifq()->limit()/4)
return 1;
return 0;
}
/*
* Ideally, would like to schedule snoop retransmissions at higher priority.
*/
int
Snoop::snoop_rxmit(Packet *pkt)
{
Scheduler& s = Scheduler::instance();
if (pkt != 0) {
hdr_snoop *sh = hdr_snoop::access(pkt);
if (sh->numRxmit() < SNOOP_MAX_RXMIT && snoop_qlong()) {
/* && sh->seqno() == lastAck_+1) */
#if 0
printf("%f Rxmitting packet %d\n", s.clock(),
hdr_tcp::access(pkt)->seqno());
#endif
sh->sndTime() = s.clock();
sh->numRxmit() = sh->numRxmit() + 1;
Packet *p = pkt->copy();
parent_->sendDown(p);
} else
return SNOOP_PROPAGATE;
}
/* Reset timeout for later time. */
if (toutPending_)
s.cancel(toutPending_);
toutPending_ = (Event *)pkt;
s.schedule(rxmitHandler_, toutPending_, timeout());
return SNOOP_SUPPRESS;
}
void
Snoop::snoop_cleanup()
{
}
void
SnoopRxmitHandler::handle(Event *)
{
Packet *p = snoop_->pkts_[snoop_->buftail_];
snoop_->toutPending_ = 0;
if (p == 0)
return;
hdr_snoop *sh = hdr_snoop::access(p);
if (sh->seqno() != snoop_->lastAck_ + 1)
return;
if ((snoop_->bufhead_ != snoop_->buftail_) ||
(snoop_->fstate_ & SNOOP_FULL)) {
/* printf("%f timeout\n", Scheduler::instance().clock());*/
if (snoop_->snoop_rxmit(p) == SNOOP_SUPPRESS)
snoop_->expNextAck_ = snoop_->next(snoop_->buftail_);
}
}
srm-ssm.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
//
// Copyright (c) 1997 by the University of Southern California
// All rights reserved.
//
// Permission to use, copy, modify, and distribute this software and its
// documentation in source and binary forms for non-commercial purposes
// and without fee is hereby granted, provided that the above copyright
// notice appear in all copies and that both the copyright notice and
// this permission notice appear in supporting documentation. and that
// any documentation, advertising materials, and other materials related
// to such distribution and use acknowledge that the software was
// developed by the University of Southern California, Information
// Sciences Institute. The name of the University may not be used to
// endorse or promote products derived from this software without
// specific prior written permission.
//
// THE UNIVERSITY OF SOUTHERN CALIFORNIA makes no representations about
// the suitability of this software for any purpose. THIS SOFTWARE IS
// PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
// INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
//
// Other copyrights might apply to parts of this software and are so
// noted when applicable.
//
// Maintainer:
// Version Date: Tue Jul 22 15:41:16 PDT 1997
// The code implements scalable session message. See
// http://catarina.usc.edu/estrin/papers/infocom98/ssession.ps
//
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (USC/ISI)";
#endif
#include
#include
#include
#include "config.h"
#include "tclcl.h"
#include "agent.h"
#include "packet.h"
#include "ip.h"
#include "srm.h"
#include "srm-ssm.h"
#include "trace.h"
int hdr_srm_ext::offset_;
static class SRMEXTHeaderClass : public PacketHeaderClass {
public:
SRMEXTHeaderClass() : PacketHeaderClass("PacketHeader/SRMEXT",
sizeof(hdr_srm_ext)) {
bind_offset(&hdr_srm_ext::offset_);
}
} class_srmexthdr;
static class SSMSRMAgentClass : public TclClass {
public:
SSMSRMAgentClass() : TclClass("Agent/SRM/SSM") {}
TclObject* create(int, const char*const*) {
return (new SSMSRMAgent());
}
} class_srm_ssm_agent;
SSMSRMAgent::SSMSRMAgent()
: SRMAgent(), glb_sessCtr_(-1), loc_sessCtr_(-1), rep_sessCtr_(-1)
{
bind("group_scope_",&groupScope_);
bind("local_scope_",&localScope_);
bind("scope_flag_",&scopeFlag_);
bind("rep_id_", &repid_);
bind("off_srm_ext_", &off_srm_ext_);
}
int SSMSRMAgent::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
if (strcmp(argv[1], "send") == 0) {
if (strcmp(argv[2], "session") == 0) {
send_sess();
return TCL_OK;
}
if (strcmp(argv[2], "request") == 0) {
int round = atoi(argv[3]);
int sender = atoi(argv[4]);
int msgid = atoi(argv[5]);
send_ctrl(SRM_RQST, round, sender, msgid, 0);
return TCL_OK;
}
if (strcmp(argv[2], "repair") == 0) {
int round = atoi(argv[3]);
int sender = atoi(argv[4]);
int msgid = atoi(argv[5]);
send_ctrl(SRM_REPR, round, sender, msgid, packetSize_);
return TCL_OK;
}
tcl.resultf("%s: invalid send request %s", name_, argv[2]);
return TCL_ERROR;
/*
#if 0
fprintf(stdout,"%s: send request %s passed to srm_agent",
name_, argv[2]);
#endif
return SRMAgent::command(argc, argv);
*/
}
if (argc == 2) {
if (strcmp(argv[1], "start") == 0) {
sip_->sender_ = addr();
sip_->distance_ = 0.0;
/* sip_->repid_ = addr_;
sip_->scopeFlag_ = SRM_GLOBAL;
repid_ = addr_;
scopeFlag_ = SRM_GLOBAL;
*/
groupScope_ = 32;
senderFlag_ = 0;
printf("%s is %d and rep-status %d\n", name_, addr(), scopeFlag_);
return TCL_OK;
}
if (strcmp(argv[1], "ch-rep") == 0) {
if(scopeFlag_ == SRM_GLOBAL) {
sip_->repid_ = repid_ = addr();
sip_->scopeFlag_ = SRM_GLOBAL;
} else {
sip_->repid_ = repid_;
sip_->scopeFlag_ = SRM_LOCAL;
}
return TCL_OK;
}
if (strcmp(argv[1], "distances?") == 0) {
if (sip_->sender_ < 0) { // i.e. this agent is not
tcl.result(""); // yet active.
return TCL_OK;
}
for (SRMinfo* sp = sip_; sp; sp = sp->next_) {
if((sp->distanceFlag_ == REP_DISTANCE) ||
(sp->distanceFlag_ == SELF_DISTANCE)) {
tcl.resultf("%s %d %f", tcl.result(),
sp->sender_, sp->distance_);
} else { /* Return reps distance */
SRMinfo* rsp = get_state(sp->repid_);
tcl.resultf("%s %d %f", tcl.result(),
sp->sender_, rsp->distance_);
}
}
return TCL_OK;
}
}
if (argc == 3) {
if (strcmp(argv[1], "distance?") == 0) {
int sender = atoi(argv[2]);
SRMinfo* sp = get_state(sender);
if((sp->distanceFlag_ == REP_DISTANCE) ||
(sp->distanceFlag_ == SELF_DISTANCE)) {
tcl.resultf("%f", sp->distance_);
} else { /* Return reps distance */
SRMinfo* rsp = get_state(sp->repid_);
tcl.resultf("%f", rsp->distance_);
}
return TCL_OK;
}
}
return SRMAgent::command(argc, argv);
}
void SSMSRMAgent::recv(Packet* p, Handler* h)
{
hdr_ip* ih = (hdr_ip*) p->access(off_ip_);
hdr_srm* sh = (hdr_srm*) p->access(off_srm_);
hdr_srm_ext* seh = (hdr_srm_ext*) p->access(off_srm_ext_);
if (ih->daddr() == 0) {
// Packet from local agent. Add srm headers, set dst, and fwd
sh->type() = SRM_DATA;
sh->sender() = addr();
sh->seqnum() = ++dataCtr_;
seh->repid() = repid_;
ih->dst() = dst_;
ih->src() = here_;
target_->recv(p, h);
} else {
#if 0
static char *foo[] = {"NONE", "DATA", "SESS", "RQST", "REPR"};
fprintf(stdout, "%7.4f %s %d recvd SRM_%s <%d, %d> from %d\n",
Scheduler::instance().clock(), name_, addr_, foo[sh->type()],
sh->sender(), sh->seqnum(), ih->src());
fflush(stdout);
#endif
switch (sh->type()) {
case SRM_DATA:
recv_data(sh->sender(), sh->seqnum(), seh->repid(), p->accessdata());
Packet::free(p);
break;
case SRM_RQST:
recv_rqst(ih->saddr(), sh->round(), sh->sender(), sh->seqnum(),
seh->repid());
Packet::free(p);
break;
case SRM_REPR:
recv_repr(sh->round(), sh->sender(), sh->seqnum(), p->accessdata());
Packet::free(p);
break;
case SRM_SESS:
// This seqnum() is the session sequence number,
// not the data packet sequence numbers seen before.
// Send the whole pkt for ttl etc..
recv_sess(sh->seqnum(), (int*) p->accessdata(), p);
break;
}
}
}
void SSMSRMAgent::recv_data(int sender, int id, int repid, u_char* data)
{
SRMinfo* sp = get_state(sender);
/* Just store the repid and call srmagent recv_data */
sp->repid_ = repid;
SRMAgent::recv_data(sender,id,data);
}
void SSMSRMAgent::send_ctrl(int type, int round, int sender, int msgid, int size)
{
Packet* p = Agent::allocpkt();
hdr_srm* sh = (hdr_srm*) p->access(off_srm_);
hdr_srm_ext* seh = (hdr_srm_ext*) p->access(off_srm_ext_);
sh->type() = type;
sh->sender() = sender;
sh->seqnum() = msgid;
sh->round() = round;
seh->repid() = repid_; /* For ctrl messages this is your own repid */
hdr_cmn* ch = (hdr_cmn*) p->access(off_cmn_);
ch->size() = sizeof(hdr_srm) + size;
target_->recv(p, (Handler*)NULL);
}
void SSMSRMAgent::recv_rqst(int requestor, int round, int sender,
int msgid, int repid)
{
//Tcl& tcl = Tcl::instance();
SRMinfo* rsp = get_state(requestor);
rsp->repid_ = repid;
SRMAgent::recv_rqst(requestor,round, sender,msgid);
}
void SSMSRMAgent::send_sess()
{
if (scopeFlag_ == SRM_GLOBAL) {
send_glb_sess();
send_rep_sess();
} else {
send_loc_sess();
}
// timeout_info();
}
#define SESSINFO_SIZE 5
#define SESS_CONST 2
void SSMSRMAgent::send_glb_sess()
{
int size = (SESS_CONST + groupSize_ * SESSINFO_SIZE) * sizeof(int);
/* Currently do extra allocation, later change */
int num_entries;
Packet* p = Agent::allocpkt(size);
hdr_srm* sh = (hdr_srm*) p->access(off_srm_);
hdr_srm_ext* seh = (hdr_srm_ext*) p->access(off_srm_ext_);
#if 0
printf("sending global session message\n");
#endif
sh->type() = SRM_SESS;
sh->sender() = addr();
sh->seqnum() = ++glb_sessCtr_;
seh->repid() = repid_;
int* data = (int*) p->accessdata();
*data++ = groupSize_;
*data++ = SRM_GLOBAL;
num_entries = 0;
for (SRMinfo* sp = sip_; sp; sp = sp->next_) {
/* Global Session Message has information about Senders/reps */
if ((sp->senderFlag_ ||
(sp->scopeFlag_ == SRM_GLOBAL) ||
(sp->sender_ == addr()))
&& (is_active(sp))) {
*data++ = sp->sender_;
*data++ = sp->ldata_;
*data++ = sp->recvTime_;
*data++ = sp->sendTime_;
*data++ = sp->repid_;
num_entries++;
}
}
data = (int*) p->accessdata();
data[0] = num_entries;
data[1] = SRM_GLOBAL;
size = (SESS_CONST + num_entries * SESSINFO_SIZE) * sizeof(int);
data[5] = (int) (Scheduler::instance().clock()*1000);
hdr_cmn* ch = (hdr_cmn*) p->access(off_cmn_);
ch->size() += size+ sizeof(hdr_srm); /* Add size of srm_hdr_ext */
hdr_ip* ih = (hdr_ip*) p->access(off_ip_);
ih->ttl() = groupScope_;
// Currently put this to distinguish various session messages
ih->flowid() = SRM_GLOBAL;
seh->ottl() = groupScope_;
target_->recv(p, (Handler*)NULL);
}
void SSMSRMAgent::send_loc_sess()
{
int size = (SESS_CONST + groupSize_ * SESSINFO_SIZE) * sizeof(int);
/* Currently do extra allocation, later change */
int num_entries;
Packet* p = Agent::allocpkt(size);
hdr_srm* sh = (hdr_srm*) p->access(off_srm_);
hdr_srm_ext* seh = (hdr_srm_ext*) p->access(off_srm_ext_);
sh->type() = SRM_SESS;
sh->sender() = addr();
sh->seqnum() = ++loc_sessCtr_;
seh->repid() = repid_;
#if 0
printf("sending local session message\n");
#endif
int* data = (int*) p->accessdata();
//int* tmp_data = (int*) p->accessdata();
*data++ = groupSize_;
*data++ = SRM_LOCAL;
num_entries = 0;
for (SRMinfo* sp = sip_; sp; sp = sp->next_) {
/* Local Session Message has information
about Senders/other locals */
if ((sp->senderFlag_ ||
(sp->scopeFlag_ == SRM_LOCAL) ||
(sp->distanceFlag_ = SELF_DISTANCE) ||
/* For the reps that I am hearing from */
(sp->sender_ == addr()) ||
// just in case, I have not set the flags properly,
// one entry has to be there
(repid_ == sp->sender_))
&& (is_active(sp))) {
*data++ = sp->sender_;
*data++ = sp->ldata_;
*data++ = sp->recvTime_;
*data++ = sp->sendTime_;
*data++ = sp->repid_;
num_entries++;
}
}
data = (int*) p->accessdata();
data[0] = num_entries;
data[1] = SRM_LOCAL;
size = (SESS_CONST + num_entries * SESSINFO_SIZE) * sizeof(int);
data[5] = (int) (Scheduler::instance().clock()*1000);
hdr_cmn* ch = (hdr_cmn*) p->access(off_cmn_);
ch->size() += size+ sizeof(hdr_srm);
hdr_ip* ih = (hdr_ip*) p->access(off_ip_);
ih->ttl() = localScope_;
// Currently put this to distinguish various session messages
ih->flowid() = SRM_LOCAL;
seh->ottl() = localScope_;
target_->recv(p, (Handler*)NULL);
}
void SSMSRMAgent::send_rep_sess()
{
int size = (SESS_CONST + groupSize_ * SESSINFO_SIZE) * sizeof(int);
/* Currently do extra allocation, later change */
int num_entries, num_local_members;
Packet* p = Agent::allocpkt(size);
hdr_srm* sh = (hdr_srm*) p->access(off_srm_);
hdr_srm_ext* seh = (hdr_srm_ext*) p->access(off_srm_ext_);
sh->type() = SRM_SESS;
sh->sender() = addr();
sh->seqnum() = ++rep_sessCtr_;
seh->repid() = repid_;
#if 0
printf("sending rep_info session message\n");
#endif
int* data = (int*) p->accessdata();
*data++ = groupSize_;
*data++ = SRM_RINFO;
num_entries = 0;
num_local_members = 0;
for (SRMinfo* sp = sip_; sp; sp = sp->next_) {
if (sp->activeFlag_ == ACTIVE) {
/* Rep info has distance to others reps and
timestamps for everyone */
*data++ = sp->sender_;
*data++ = sp->ldata_;
if (sp->scopeFlag_ == SRM_GLOBAL) {
*data++ = (int) (sp->distance_*1000);
data++;
} else {
// Put a check here for only people I have heard from.??
*data++ = sp->recvTime_;
*data++ = sp->sendTime_;
num_local_members++;
}
*data++ = sp->repid_;
num_entries++;
}
}
if (num_local_members <= 0) {
Packet::free(p);
return;
}
data = (int*) p->accessdata();
data[0] = num_entries;
data[1] = SRM_RINFO;
size = (SESS_CONST + num_entries * SESSINFO_SIZE) * sizeof(int);
data[5] = (int) (Scheduler::instance().clock()*1000);
hdr_cmn* ch = (hdr_cmn*) p->access(off_cmn_);
ch->size() += size+ sizeof(hdr_srm);
hdr_ip* ih = (hdr_ip*) p->access(off_ip_);
ih->ttl() = localScope_;
// Currently put this to distinguish various session messages
ih->flowid() = SRM_RINFO;
seh->ottl() = localScope_;
target_->recv(p, (Handler*)NULL);
}
#define GET_SESSION_INFO \
sender = *data++; \
dataCnt = *data++; \
rtime = *data++; \
stime = *data++; \
repid = *data++; \
// printf("s:%d, d:%d, rt:%d, st:%d, rep:%d\n",sender,
// dataCnt,rtime,stime,repid)
void SSMSRMAgent::recv_sess(int sessCtr, int* data, Packet* p)
{
int type = data[1];
switch (type) {
case SRM_GLOBAL :
recv_glb_sess(sessCtr,data,p);
break;
case SRM_LOCAL :
recv_loc_sess(sessCtr,data,p);
break;
case SRM_RINFO :
if (scopeFlag_ == SRM_GLOBAL) return;
recv_rep_sess(sessCtr,data,p);
break;
}
Packet::free(p);
}
void SSMSRMAgent::recv_glb_sess(int sessCtr, int* data, Packet* p)
{
Tcl& tcl = Tcl::instance();
SRMinfo* sp;
int ttl;
hdr_ip* ih = (hdr_ip*) p->access(off_ip_);
//hdr_srm* sh = (hdr_srm*) p->access(off_srm_);
hdr_srm_ext* seh = (hdr_srm_ext*) p->access(off_srm_ext_);
ttl = seh->ottl() - ih->ttl();
int sender, dataCnt, rtime, stime,repid;
int now, sentAt, sentBy;
int cnt = *data++;
//int type = *data++;
int i;
// data = data + SESS_CONST;
/* As as included type of session message also */
/* The first block contains the sender's own state */
GET_SESSION_INFO;
if (sender == addr())
// sender's own session message
return;
if (seh->repid() != repid) {
fprintf(stdout,"%f Recvd a glb-sess with diff header(%d) != inside(%d)\n",
Scheduler::instance().clock(),seh->repid(),repid);
/* abort(); */
return;
}
if (sender != repid) {
fprintf(stdout,"%f Recvd a glb-sess with repid(%d) != address(%d)\n",
Scheduler::instance().clock(),repid,sender);
/* abort(); */
return;
}
sp = get_state(sender);
if (sp->lglbsess_ > sessCtr) // older session message recd.
return;
#if 0
fprintf(stdout,"%s recv-gsess from %d\n",name_,sender);
#endif
tcl.evalf("%s recv-gsess %d %d", name_, sender, ttl);
if (sp->scopeFlag_ != SRM_GLOBAL) {
sp->scopeFlag_ = SRM_GLOBAL;
}
sp->repid_ = repid;
now = (int) (Scheduler::instance().clock() * 1000);
sentBy = sender; // to later compute rtt
sentAt = stime;
sp->lglbsess_ = sessCtr;
sp->recvTime_ = now;
sp->sendTime_ = stime;
for (i = sp->ldata_ + 1; i <= dataCnt; i++)
if (! sp->ifReceived(i))
tcl.evalf("%s request %d %d", name_, sender, i, sp->repid_);
if (sp->ldata_ < dataCnt)
sp->ldata_ = dataCnt;
for (i = 1; i < cnt; i++) {
GET_SESSION_INFO;
if (sender == addr() && now) {
int rtt = (now - sentAt) + (rtime - stime);
sp = get_state(sentBy);
sp->distance_ = (double) rtt / 2 / 1000;
sp->distanceFlag_ = SELF_DISTANCE;
#if 0
fprintf(stderr,
"%7.4f %s compute distance to %d: %f\n",
Scheduler::instance().clock(), name_,
sentBy, sp->distance_);
#endif
continue;
}
sp = get_state(sender);
for (int j = sp->ldata_ + 1; j <= dataCnt; j++)
if (! sp->ifReceived(j))
tcl.evalf("%s request %d %d", name_, sender, j, sp->repid_);
if (sp->ldata_ < dataCnt)
sp->ldata_ = dataCnt;
}
}
void SSMSRMAgent::recv_loc_sess(int sessCtr, int* data, Packet* p)
{
Tcl& tcl = Tcl::instance();
SRMinfo* sp;
int ttl;
hdr_ip* ih = (hdr_ip*) p->access(off_ip_);
//hdr_srm* sh = (hdr_srm*) p->access(off_srm_);
hdr_srm_ext* seh = (hdr_srm_ext*) p->access(off_srm_ext_);
ttl = seh->ottl() - ih->ttl();
int sender, dataCnt, rtime, stime,repid;
int now, sentAt, sentBy;
int cnt = *data++;
/*int type = * */data++;
int i;
// data = data + SESS_CONST;
/* As as included type of session message also */
/* The first block contains the sender's own state */
GET_SESSION_INFO;
if (sender == addr()) // sender's own session message
return;
sp = get_state(sender);
if (sp->llocsess_ > sessCtr) // older session message recd.
return;
if (sp->scopeFlag_ != SRM_LOCAL) {
sp->scopeFlag_ = SRM_LOCAL;
// Also put a check if this is my child
}
sp->repid_ = repid;
#if 0
fprintf(stdout,"%s recv-lsess from %d\n",name_,sender);
#endif
tcl.evalf("%s recv-lsess %d %d %d", name_, sender, repid, ttl);
now = (int) (Scheduler::instance().clock() * 1000);
sentBy = sender; // to later compute rtt
sentAt = stime;
sp->llocsess_ = sessCtr;
sp->recvTime_ = now;
sp->sendTime_ = stime;
for (i = sp->ldata_ + 1; i <= dataCnt; i++)
if (! sp->ifReceived(i))
tcl.evalf("%s request %d %d", name_, sender, i, sp->repid_);
if (sp->ldata_ < dataCnt)
sp->ldata_ = dataCnt;
for (i = 1; i < cnt; i++) {
GET_SESSION_INFO;
if (sender == addr() && now) {
int rtt = (now - sentAt) + (rtime - stime);
sp = get_state(sentBy);
sp->distance_ = (double) rtt / 2 / 1000;
sp->distanceFlag_ = SELF_DISTANCE;
#if 0
fprintf(stderr,
"%7.4f %s compute distance to %d: %f\n",
Scheduler::instance().clock(), name_,
sentBy, sp->distance_);
#endif
continue;
}
sp = get_state(sender);
for (int j = sp->ldata_ + 1; j <= dataCnt; j++)
if (! sp->ifReceived(j))
tcl.evalf("%s request %d %d", name_, sender, j, sp->repid_);
if (sp->ldata_ < dataCnt)
sp->ldata_ = dataCnt;
}
}
// For the global members the repid == addr
void SSMSRMAgent::recv_rep_sess(int sessCtr, int* data, Packet*)
{
Tcl& tcl = Tcl::instance();
SRMinfo* sp;
int sender, dataCnt, rtime, stime,repid;
int now, sentAt, sentBy;
int cnt = *data++;
/*int type = **/data++;
int i;
//data = data + SESS_CONST;
/* As as included type of session message also */
/* The first block contains the sender's own state */
GET_SESSION_INFO;
if (sender == addr()) // sender's own session message
return;
if (sender != repid_) // not from my rep
return;
if (sender != repid) {
fprintf(stdout,"Recvd a rep-sess with repid(%d) != address(%d)\n",
repid,sender);
abort();
}
sp = get_state(sender);
if (sp->lrepsess_ > sessCtr) // older session message recd.
return;
if (sp->scopeFlag_ != SRM_GLOBAL) // Should I change the repid also??
sp->scopeFlag_ = SRM_GLOBAL;
now = (int) (Scheduler::instance().clock() * 1000);
sentBy = sender; // to later compute rtt
sentAt = stime;
sp->lrepsess_ = sessCtr;
sp->recvTime_ = now;
sp->sendTime_ = stime;
for (i = sp->ldata_ + 1; i <= dataCnt; i++)
if (! sp->ifReceived(i))
tcl.evalf("%s request %d %d", name_, sender, i, sp->repid_);
if (sp->ldata_ < dataCnt)
sp->ldata_ = dataCnt;
for (i = 1; i < cnt; i++) {
GET_SESSION_INFO;
if (sender == addr() && now) {
int rtt = (now - sentAt) + (rtime - stime);
sp = get_state(sentBy);
sp->distance_ = (double) rtt / 2 / 1000;
sp->distanceFlag_ = SELF_DISTANCE;
#if 0
fprintf(stderr,
"%7.4f %s compute distance to %d: %f\n",
Scheduler::instance().clock(), name_,
sentBy, sp->distance_);
#endif
continue;
}
if ((sender == repid) && (sender != sentBy)) {
sp = get_state(sender);
if (!(is_active(sp) && (sp->distanceFlag_ == SELF_DISTANCE))) {
sp->distance_ = (double) rtime/1000;
/* As for global members this is distance */
sp->distanceFlag_ = REP_DISTANCE;
/* ?? What if I am hearing from this guy already */
}
}
sp = get_state(sender);
for (int j = sp->ldata_ + 1; j <= dataCnt; j++)
if (! sp->ifReceived(j))
tcl.evalf("%s request %d %d", name_, sender, j, sp->repid_);
if (sp->ldata_ < dataCnt)
sp->ldata_ = dataCnt;
}
}
#define sessionDelay 1000
void SSMSRMAgent::timeout_info()
{
int now;
now = (int) (Scheduler::instance().clock() * 1000);
for (SRMinfo* sp = sip_->next_; sp; sp = sp->next_) {
if ((now - sp->recvTime_) >= 3*sessionDelay) {
sp->activeFlag_ = INACTIVE;
groupSize_--;
}
}
}
int SSMSRMAgent::is_active(SRMinfo *sp)
{
int now;
now = (int) (Scheduler::instance().clock() * 1000);
if ((sp->sender_ != addr()) && ((now - sp->recvTime_) >= 3*sessionDelay)) {
return 0;
} else {
return 1;
}
}
srm-topo.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) Xerox Corporation 1997. All rights reserved.
*
* License is granted to copy, to use, and to make and to use derivative
* works for research and evaluation purposes, provided that Xerox is
* acknowledged in all documentation pertaining to any such copy or derivative
* work. Xerox grants no other licenses expressed or implied. The Xerox trade
* name should not be used in any advertising without its written permission.
*
* XEROX CORPORATION MAKES NO REPRESENTATIONS CONCERNING EITHER THE
* MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE
* FOR ANY PARTICULAR PURPOSE. The software is provided "as is" without
* express or implied warranty of any kind.
*
* These notices must be retained in any copies of any part of this software.
*
* This file contributed by Suchitra Raman , June 1997.
*/
#include
#include
#include "config.h"
#include "tclcl.h"
#include "srm-topo.h"
#include "scheduler.h"
#define SRM_DEBUG
Topology::Topology(int nn, int src) : idx_(nn), src_(src)
{
node_ = new SrmNode[nn];
for (int i = 0; i < nn; i ++)
node_[i].id(i);
bind("src_", &src_);
bind("delay_", &delay_);
bind("D_", &D_);
bind("frac_", &frac_);
/* D_ (ms) is the delay of node 1 from node 0 */
bind("det_", &det_);
bind("rtt_est_", &rtt_est_);
bind("rand_", &rand_);
}
Topology::~Topology() {
delete [] node_;
}
int Topology::command(int argc, const char*const* argv)
{
//Tcl& tcl = Tcl::instance();
if (argc == 3) {
if (strcmp(argv[1], "flood") == 0) {
flood(0, atoi(argv[2]));
return (TCL_OK);
}
}
return (TclObject::command(argc, argv));
}
SrmNode* Topology::node(int nn)
{
if (nn < 0 || nn >= idx_) return 0;
return &(node_[nn]);
}
static class LineClass : public TclClass {
public:
LineClass() : TclClass("Topology/Line") {}
TclObject* create(int, const char*const* argv) {
int nn = atoi(argv[4]);
return (new Line(nn, 0));
}
} class_line_topo;
double Line::backoff(int dst)
{
double rtt;
if (topology->rtt_estimated() == 1)
rtt = delay(dst, 0);
else rtt = D_ * frac_;
double b;
double sqroot = sqrt(rtt);
double r = Random::uniform(0.0, 1.0);
switch(c2func_) {
case LOG :
b = rtt * (det_ + rand_ * r * log(rtt)/log(D_));
break;
case SQRT :
b = rtt * (det_ + rand_ * r * sqroot/sqrt(D_));
break;
case LINEAR :
b = rtt * (det_ + rand_ * r * rtt/D_);
break;
case CONSTANT :
b = rtt * (det_ + rand_ * r * 1.);
break;
}
return b;
}
#ifdef SRM_BIMODAL
double Line::backoff(int dst)
{
double rtt;
if (topology->rtt_estimated() == 1)
rtt = delay(dst, 0);
else rtt = D_ * frac_;
double rbackoff;
double p = Random::uniform(0.0, 1.0);
int bin = 0;
int copies = c_;
int size = topology->idx();
if (p <= copies * 1./size) {
rbackoff = Random::uniform(0.0, alpha_);
bin = 0;
} else {
rbackoff = Random::uniform(beta_, 1.0 + beta_);
bin = 1;
}
return (rtt * (det_ + rand_ * rbackoff));
}
#endif
Interface_List* Line::oif(int node, int iif=SRM_NOIF)
{
//int oif;
Interface_List *ilist = new Interface_List;
/* If there are no more nodes downstream, return -1 */
if ((iif <= node && node >= idx_ - 1) || (iif > node && node == 0))
return 0;
if (iif == SRM_NOIF)
{
ilist->append(node + 1);
ilist->append(node - 1);
} else if (iif <= node)
ilist->append(node + 1);
else
ilist->append(node - 1);
return (ilist);
}
double Line::delay(int src, int dst)
{
if (src == 0 || dst == 0)
return D_ + delay_ * abs(dst - src - 1);
else
return delay_ * abs(dst - src);
}
double Star::delay(int src, int dst)
{
if (src == 0 || dst == 0)
return D_;
else
return delay_;
}
/*
* Very simple now, can only send to direct ancestor/descendant
*/
double BTree::delay(int src, int dst)
{
double src_ht = 0;
double dst_ht = 0;
if (src > 0)
src_ht = 1 + floor(log(src)/log(2));
if (dst > 0)
dst_ht = 1 + floor(log(dst)/log(2));
if (src == 0 || dst == 0)
return D_ + delay_ * abs(int (dst_ht - src_ht - 1));
else
return delay_ * abs(int (dst_ht - src_ht));
}
/*
* There is always an outbound interface. We should not
* start a flood() from a leaf node.
* Change it for other topologies.
*/
void Line::flood(int start, int seqno)
{
SRM_Event *se = new SRM_Event(seqno, SRM_DATA, start);
node_[start].send(se);
}
/*
* BTree -
*/
static class BTreeClass : public TclClass {
public:
BTreeClass() : TclClass("Topology/BTree") {}
TclObject* create(int, const char*const* argv) {
int nn = atoi(argv[4]);
return (new BTree(nn, 0));
}
} class_btree_topo;
/*
* The ranges of the different distributions
* must be normalized to 1.
*/
double BTree::backoff(int id)
{
double rtt;
if (topology->rtt_estimated())
rtt = delay(id, 0);
else rtt = D_ * frac_;
double r = Random::uniform(0.0, 1.0);
double b;
double sqroot = sqrt(rtt);
double D = topology->D();
switch(c2func_) {
case LOG :
b = rtt * (det_ + rand_ * r * log(rtt)/log(D));
break;
case SQRT :
b = rtt * (det_ + rand_ * r * sqroot/sqrt(D));
break;
case LINEAR :
b = rtt * (det_ + rand_ * r * rtt/D);
break;
case CONSTANT :
b = rtt * (det_ + rand_ * r * 1.);
break;
}
return b;
}
Interface_List* BTree::oif(int node, int iif=SRM_NOIF)
{
//int oif;
Interface_List *ilist = new Interface_List;
/*
* If the packet comes from the source,
* there is only 1 outgoing link.
* We make the assumption that source = 0 always (YUCK!)
*/
if (iif == SRM_NOIF) {
if (node == 0) {
ilist->append(1);
}
else {
ilist->append(2 * node + 1);
ilist->append(2 * node);
int k = (int)floor(.5 * node);
ilist->append(k);
}
} else if (iif <= node) {
ilist->append(2 * node + 1);
ilist->append(2 * node);
}
// Packet came along 2n + 1 or 2n + 2
else if (iif == 2 * node + 1) {
if (node == 0)
return ilist;
ilist->append(2 * node);
ilist->append((int) floor(.5 * node));
} else if (iif == 2 * node) {
ilist->append(2 * node + 1);
ilist->append((int)floor(.5 * node));
}
return (ilist);
}
/*
* Star -
*/
static class StarClass : public TclClass {
public:
StarClass() : TclClass("Topology/Star") {}
TclObject* create(int, const char*const* argv) {
int nn = atoi(argv[4]);
return (new Star(nn, 0));
}
} class_star_topo;
/*
* SrmNode -
*/
void SrmNode::dump_packet(SRM_Event *e)
{
#ifdef SRM_DEBUG
tprintf(("(type %d) (in %d) -- @ %d --> \n", e->type(), e->iif(), id_));
#endif
}
/* This forwards an event by making multiple copies of the
* event 'e'. Does NOT free event 'e'.
*/
void SrmNode::send(SRM_Event *e)
{
/*
* Copy the packet and send it over to
* all the outbound interfaces
*/
int nn;
Scheduler& s = Scheduler::instance();
SRM_Event *copy;
Interface *p;
Interface_List *ilist = topology->oif(id_, e->iif());
if (e->iif() < 0)
e->iif(id_);
if (ilist) {
for (p=ilist->head_; p; p=p->next_) {
nn = p->in_;
int t = e->type();
//int i = id_;
int snum = e->seqno();
SrmNode *next = topology->node(nn);
if (next) {
copy = new SRM_Event(snum, t, id_);
s.schedule(next, copy,
topology->delay(id_, nn));
}
}
}
delete ilist;
delete e;
}
/*
* Demux the two types of events depending on
* the type field.
*/
void SrmNode::handle(Event* event)
{
SRM_Event *srm_event = (SRM_Event *) event;
int type = srm_event->type();
int seqno = srm_event->seqno();
//int iif = srm_event->iif();
//Scheduler& s = Scheduler::instance();
switch (type) {
case SRM_DATA :
if (seqno != expected_)
sched_nack(seqno);
expected_ = seqno;
break;
case SRM_PENDING_RREQ :
tprintf(("Fired RREQ (node %d)\n", id_));
remove(seqno, SRM_NO_SUPPRESS);
srm_event->type(SRM_RREQ);
break;
case SRM_RREQ :
remove(seqno, SRM_SUPPRESS);
break;
default :
tprintf(("panic: node(%d) Unexpected type %d\n", id_, type));
return;
}
#ifdef SRM_STAR
if (type == SRM_RREQ || type == SRM_DATA) {
delete srm_event;
return;
}
#endif
send(srm_event);
return;
}
/*
* XXX Should take two sequence numbers. The iif field is a dummy. We should be
* careful not to use it for NACKs
*/
void SrmNode::sched_nack(int seqno)
{
double backoff, time;
Scheduler& s = Scheduler::instance();
SRM_Event *event = new SRM_Event(seqno,
SRM_PENDING_RREQ, SRM_NOIF);
append(event);
backoff = topology->backoff(id_);
time = backoff + s.clock();
// tprintf(("node(%d) schd rrq after %f s\n", id_, backoff));
s.schedule(this, event, backoff);
}
void SrmNode::append(SRM_Event *e)
{
SRM_Request *req = new SRM_Request(e);
req->next_ = pending_;
pending_ = req;
}
/* If an event identical to e exists, cancel it */
void SrmNode::remove(int seqno, int flag)
{
SRM_Request *curr, *prev;
SRM_Event *ev;
if (!pending_)
return;
for (curr=pending_, prev=0; curr; curr=curr->next_)
{
ev = curr->event_;
if (ev->seqno() == seqno) {
if (!prev)
pending_ = curr->next_;
else {
prev->next_ = curr->next_;
prev = curr;
}
if (flag == SRM_SUPPRESS) {
curr->cancel_timer();
}
delete curr;
}
}
}
SRM_Event::SRM_Event(SRM_Event *e)
{
seqno_ = e->seqno();
type_ = e->type();
iif_ = e->iif();
}
SRM_Request::~SRM_Request() {}
void SRM_Request::cancel_timer()
{
if (event_) {
Scheduler& s = Scheduler::instance();
s.cancel(event_);
}
}
void Interface_List::append(int in)
{
Interface *i = new Interface(in);
i->next_ = head_;
head_ = i;
}
Interface_List::~Interface_List()
{
Interface *p, *next;
for (p=head_; p; p=next)
{
next = p->next_;
delete p;
}
}
void BTree::flood(int start, int seqno)
{
SRM_Event *se = new SRM_Event(seqno, SRM_DATA, SRM_NOIF);
node_[start].send(se);
}
void Star::flood(int start, int seqno)
{
SRM_Event *se = new SRM_Event(seqno, SRM_DATA, start);
node_[start].send(se);
}
Interface_List* Star::oif(int node, int iif=SRM_NOIF)
{
int i;
Interface_List *ilist = new Interface_List;
/* Make a list with every node except myself */
for (i = 0; i < topology->idx(); i ++)
{
if (i != node && i != iif) {
ilist->append(i);
}
}
return (ilist);
}
/*
* Distance of source to cluster = D_
*/
double Star::backoff(int id)
{
double rtt = topology->delay(id, 0);
double rbackoff;
double p = Random::uniform(0.0, 1.0);
static int bin = 0;
int copies = c_;
int size = topology->idx();
if (p <= copies * 1./size) {
rbackoff = Random::uniform(0.0, alpha_);
bin ++;
} else {
rbackoff = Random::uniform(beta_, 1.0 + beta_);
}
return (rtt * (det_ + rand_ * rbackoff));
}
#ifdef SRM_BIMODAL
double BTree::backoff(int dst)
{
double height = floor(log(dst)/log(2));
double D = topology->D();
double rtt = delay_ * height + D;
double p = Random::uniform(0.0, 1.0);
double rbackoff;
static int bin = 0;
int copies = c_;
int size = topology->idx();
if (p <= copies * 1./size) {
rbackoff = Random::uniform(0.0, alpha_);
bin ++;
} else {
rbackoff = Random::uniform(1.0 + alpha_, 2.0);
}
return (rtt * (det_ + rand_ * rbackoff));
}
#endif
srm.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
//
// Copyright (c) 1997 by the University of Southern California
// All rights reserved.
//
// Permission to use, copy, modify, and distribute this software and its
// documentation in source and binary forms for non-commercial purposes
// and without fee is hereby granted, provided that the above copyright
// notice appear in all copies and that both the copyright notice and
// this permission notice appear in supporting documentation. and that
// any documentation, advertising materials, and other materials related
// to such distribution and use acknowledge that the software was
// developed by the University of Southern California, Information
// Sciences Institute. The name of the University may not be used to
// endorse or promote products derived from this software without
// specific prior written permission.
//
// THE UNIVERSITY OF SOUTHERN CALIFORNIA makes no representations about
// the suitability of this software for any purpose. THIS SOFTWARE IS
// PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
// INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
//
// Other copyrights might apply to parts of this software and are so
// noted when applicable.
//
// Maintainer: Kannan Varadhan
// Version Date: Tue Jul 22 15:41:16 PDT 1997
//
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (USC/ISI)";
#endif
#include
#include
#include "config.h"
#include "agent.h"
#include "ip.h"
#include "srm.h"
#include "trace.h"
#include "rtp.h"
int hdr_srm::offset_;
int hdr_asrm::offset_;
static class SRMHeaderClass : public PacketHeaderClass {
public:
SRMHeaderClass() : PacketHeaderClass("PacketHeader/SRM",
sizeof(hdr_srm)) {
bind_offset(&hdr_srm::offset_);
}
} class_srmhdr;
static class ASRMHeaderClass : public PacketHeaderClass {
public:
ASRMHeaderClass() : PacketHeaderClass("PacketHeader/aSRM",
sizeof(hdr_asrm)) {
bind_offset(&hdr_asrm::offset_);
}
} class_adaptive_srmhdr;
static class SRMAgentClass : public TclClass {
public:
SRMAgentClass() : TclClass("Agent/SRM") {}
TclObject* create(int, const char*const*) {
return (new SRMAgent());
}
} class_srm_agent;
static class ASRMAgentClass : public TclClass {
public:
ASRMAgentClass() : TclClass("Agent/SRM/Adaptive") {}
TclObject* create(int, const char*const*) {
return (new ASRMAgent());
}
} class_adaptive_srm_agent;
SRMAgent::SRMAgent()
: Agent(PT_SRM), dataCtr_(-1), sessCtr_(-1), siphash_(0), seqno_(-1),
app_type_(PT_NTYPE)
{
sip_ = new SRMinfo(-1);
bind("off_srm_", &off_srm_);
bind("off_cmn_", &off_cmn_);
bind("off_rtp_", &off_rtp_);
bind("packetSize_", &packetSize_);
bind("groupSize_", &groupSize_);
bind("app_fid_", &app_fid_);
}
SRMAgent::~SRMAgent()
{
cleanup();
}
int SRMAgent::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
if (strcmp(argv[1], "send") == 0) {
if (strcmp(argv[2], "session") == 0) {
send_sess();
return TCL_OK;
}
if (strcmp(argv[2], "request") == 0) {
int round = atoi(argv[3]);
int sender = atoi(argv[4]);
int msgid = atoi(argv[5]);
send_ctrl(SRM_RQST, round, sender, msgid, 0);
return TCL_OK;
}
if (strcmp(argv[2], "repair") == 0) {
int round = atoi(argv[3]);
int sender = atoi(argv[4]);
int msgid = atoi(argv[5]);
send_ctrl(SRM_REPR, round, sender, msgid, packetSize_);
return TCL_OK;
}
tcl.resultf("%s: invalid send request %s", name_, argv[2]);
return TCL_ERROR;
}
if (argc == 2) {
if (strcmp(argv[1], "distances?") == 0) {
tcl.result("");
if (sip_->sender_ >= 0) { // i.e. this agent is active
for (SRMinfo* sp = sip_; sp; sp = sp->next_) {
tcl.resultf("%s %d %f", tcl.result(),
sp->sender_,
sp->distance_);
}
}
return TCL_OK;
}
if (strcmp(argv[1], "start") == 0) {
start();
return TCL_OK;
}
}
if (argc == 3) {
if (strcmp(argv[1], "distance?") == 0) {
int sender = atoi(argv[2]);
SRMinfo* sp = get_state(sender);
tcl.resultf("%lf", sp->distance_);
return TCL_OK;
}
}
return Agent::command(argc, argv);
}
void SRMAgent::recv(Packet* p, Handler* h)
{
hdr_ip* ih = (hdr_ip*) p->access(off_ip_);
hdr_srm* sh = (hdr_srm*) p->access(off_srm_);
if (ih->daddr() == -1) {
// Packet from local agent. Add srm headers, set dst, and fwd
sh->type() = SRM_DATA;
sh->sender() = addr();
sh->seqnum() = ++dataCtr_;
addExtendedHeaders(p);
ih->dst() = dst_;
target_->recv(p, h);
} else {
#if 0
static char *foo[] = {"NONE", "DATA", "SESS", "RQST", "REPR"};
fprintf(stderr, "%7.4f %s %d recvd SRM_%s <%d, %d> from %d\n",
Scheduler::instance().clock(), name_, addr_,
foo[sh->type()],
sh->sender(), sh->seqnum(), ih->src());
#endif
parseExtendedHeaders(p);
switch (sh->type()) {
case SRM_DATA:
recv_data(sh->sender(), sh->seqnum(), p->accessdata());
break;
case SRM_RQST:
recv_rqst(ih->saddr(),
sh->round(), sh->sender(), sh->seqnum());
break;
case SRM_REPR:
recv_repr(sh->round(), sh->sender(), sh->seqnum(),
p->accessdata());
break;
case SRM_SESS:
// This seqnum() is the session sequence number,
// not the data packet sequence numbers seen before.
recv_sess(p, sh->seqnum(), (int*) p->accessdata());
break;
}
Packet::free(p);
}
}
void SRMAgent::sendmsg(int nbytes, const char* /*flags*/)
{
if (nbytes == -1) {
printf("Error: sendmsg() for SRM should not be -1\n");
return;
}
// The traffic generator may have reset our payload type when it
// initialized. If so, save the current payload type as app_type_,
// and set type_ to PT_SRM. Use app_type_ for all app. packets
//
if (type_ != PT_SRM) {
app_type_ = type_;
type_ = PT_SRM;
}
size_ = nbytes;
Packet *p;
p = allocpkt();
hdr_ip* ih = (hdr_ip*) p->access(off_ip_);
hdr_srm* sh = (hdr_srm*) p->access(off_srm_);
hdr_rtp* rh = (hdr_rtp*)p->access(off_rtp_);
hdr_cmn* ch = (hdr_cmn*)p->access(off_cmn_);
//hdr_cmn* ch = hdr_cmn::access(p);
ch->ptype() = app_type_;
ch->size() = size_;
ih->flowid() = app_fid_;
rh->seqno() = ++seqno_;
// Add srm headers, set dst, and fwd
sh->type() = SRM_DATA;
sh->sender() = addr();
sh->seqnum() = ++dataCtr_;
addExtendedHeaders(p);
ih->dst() = dst_;
target_->recv(p);
}
void SRMAgent::send_ctrl(int type, int round, int sender, int msgid, int size)
{
Packet* p = Agent::allocpkt();
hdr_srm* sh = (hdr_srm*) p->access(off_srm_);
sh->type() = type;
sh->sender() = sender;
sh->seqnum() = msgid;
sh->round() = round;
addExtendedHeaders(p);
hdr_cmn* ch = (hdr_cmn*) p->access(off_cmn_);
ch->size() = sizeof(hdr_srm) + size;
target_->recv(p);
}
void SRMAgent::recv_data(int sender, int msgid, u_char*)
{
Tcl& tcl = Tcl::instance();
SRMinfo* sp = get_state(sender);
if (msgid > sp->ldata_) {
(void) request(sp, msgid - 1);
sp->setReceived(msgid);
sp->ldata_ = msgid;
} else {
tcl.evalf("%s recv data %d %d", name_, sender, msgid);
}
}
void SRMAgent::recv_rqst(int requestor, int round, int sender, int msgid)
{
Tcl& tcl = Tcl::instance();
SRMinfo* sp = get_state(sender);
if (msgid > sp->ldata_) {
(void) request(sp, msgid); // request upto msgid
sp->ldata_ = msgid;
} else {
tcl.evalf("%s recv request %d %d %d %d", name_,
requestor, round, sender, msgid);
}
}
void SRMAgent::recv_repr(int round, int sender, int msgid, u_char*)
{
Tcl& tcl = Tcl::instance();
SRMinfo* sp = get_state(sender);
if (msgid > sp->ldata_) {
(void) request(sp, msgid - 1); // request upto msgid - 1
sp->setReceived(msgid);
sp->ldata_ = msgid;
} else {
tcl.evalf("%s recv repair %d %d %d", name_,
round, sender, msgid);
}
// Notice that we currently make no provisions for a listener
// agent to receive the data.
}
void SRMAgent::send_sess()
{
int size = (1 + groupSize_ * 4) * sizeof(int);
Packet* p = Agent::allocpkt(size);
hdr_srm* sh = (hdr_srm*) p->access(off_srm_);
sh->type() = SRM_SESS;
sh->sender() = addr();
sh->seqnum() = ++sessCtr_;
addExtendedHeaders(p);
int* data = (int*) p->accessdata();
*data++ = groupSize_;
for (SRMinfo* sp = sip_; sp; sp = sp->next_) {
*data++ = sp->sender_;
*data++ = sp->ldata_;
*data++ = sp->recvTime_;
*data++ = sp->sendTime_;
}
data = (int*) p->accessdata();
data[4] = (int) (Scheduler::instance().clock()*1000);
hdr_cmn* ch = (hdr_cmn*) p->access(off_cmn_);
ch->size() = size+ sizeof(hdr_srm);
target_->recv(p, (Handler*)NULL);
}
#define GET_SESSION_INFO \
sender = *data++; \
dataCnt = *data++; \
rtime = *data++; \
stime = *data++
void SRMAgent::recv_sess(Packet*, int sessCtr, int* data)
{
SRMinfo* sp;
int sender, dataCnt, rtime, stime;
int now, sentAt, sentBy;
int cnt = *data++;
int i;
/* The first block contains the sender's own state */
GET_SESSION_INFO;
if (sender == addr()) // sender's own session message
return;
sp = get_state(sender);
if (sp->lsess_ > sessCtr) // older session message recd.
return;
now = (int) (Scheduler::instance().clock() * 1000);
sentBy = sender; // to later compute rtt
sentAt = stime;
sp->lsess_ = sessCtr;
sp->recvTime_ = now;
sp->sendTime_ = stime;
(void) request(sp, dataCnt);
if (sp->ldata_ < dataCnt)
sp->ldata_ = dataCnt;
for (i = 1; i < cnt; i++) {
GET_SESSION_INFO;
if (sender == addr() && now) {
//
// This session message from sender sentBy:
// vvvvv
// now <=======+ sentAt
// | |
// stime +=======> rtime
// ^^^^^
// Earlier session message sent by ``this'' agent
//
int rtt = (now - sentAt) + (rtime - stime);
sp = get_state(sentBy);
sp->distance_ = (double) rtt / 2 / 1000;
#if 0
fprintf(stderr,
"%7.4f %s compute distance to %d: %f\n",
Scheduler::instance().clock(), name_,
sentBy, sp->distance_);
#endif
continue;
}
sp = get_state(sender);
(void) request(sp, dataCnt);
if (sp->ldata_ < dataCnt)
sp->ldata_ = dataCnt;
}
}
tbf.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) Xerox Corporation 1997. All rights reserved.
*
* License is granted to copy, to use, and to make and to use derivative
* works for research and evaluation purposes, provided that Xerox is
* acknowledged in all documentation pertaining to any such copy or
* derivative work. Xerox grants no other licenses expressed or
* implied. The Xerox trade name should not be used in any advertising
* without its written permission.
*
* XEROX CORPORATION MAKES NO REPRESENTATIONS CONCERNING EITHER THE
* MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE
* FOR ANY PARTICULAR PURPOSE. The software is provided "as is" without
* express or implied warranty of any kind.
*
* These notices must be retained in any copies of any part of this
* software.
*/
/* Token Bucket filter which has 3 parameters :
a. Token Generation rate
b. Token bucket depth
c. Max. Queue Length (a finite length would allow this to be used as policer as packets are dropped after queue gets full)
*/
#include "connector.h"
#include "packet.h"
#include "queue.h"
#include "tbf.h"
TBF::TBF() :tokens_(0),tbf_timer_(this), init_(1)
{
q_=new PacketQueue();
bind_bw("rate_",&rate_);
bind("bucket_",&bucket_);
bind("qlen_",&qlen_);
}
TBF::~TBF()
{
if (q_->length() != 0) {
//Clear all pending timers
tbf_timer_.cancel();
//Free up the packetqueue
for (Packet *p=q_->head();p!=0;p=p->next_)
Packet::free(p);
}
delete q_;
}
void TBF::recv(Packet *p, Handler *)
{
//start with a full bucket
if (init_) {
tokens_=bucket_;
lastupdatetime_ = Scheduler::instance().clock();
init_=0;
}
hdr_cmn *ch=(hdr_cmn *)p->access(off_cmn_);
//enque packets appropriately if a non-zero q already exists
if (q_->length() !=0) {
if (q_->length() < qlen_) {
q_->enque(p);
return;
}
drop(p);
return;
}
double tok;
tok = getupdatedtokens();
int pktsize = ch->size()<<3;
if (tokens_ >=pktsize) {
target_->recv(p);
tokens_-=pktsize;
}
else {
if (qlen_!=0) {
q_->enque(p);
tbf_timer_.resched((pktsize-tokens_)/rate_);
}
else {
drop(p);
}
}
}
double TBF::getupdatedtokens(void)
{
double now=Scheduler::instance().clock();
tokens_ += (now-lastupdatetime_)*rate_;
if (tokens_ > bucket_)
tokens_=bucket_;
lastupdatetime_ = Scheduler::instance().clock();
return tokens_;
}
void TBF::timeout(int)
{
if (q_->length() == 0) {
fprintf (stderr,"ERROR in tbf\n");
abort();
}
Packet *p=q_->deque();
double tok;
tok = getupdatedtokens();
hdr_cmn *ch=(hdr_cmn *)p->access(off_cmn_);
int pktsize = ch->size()<<3;
//We simply send the packet here without checking if we have enough tokens
//because the timer is supposed to fire at the right time
target_->recv(p);
tokens_-=pktsize;
if (q_->length() !=0 ) {
p=q_->head();
hdr_cmn *ch=(hdr_cmn *)p->access(off_cmn_);
pktsize = ch->size()<<3;
tbf_timer_.resched((pktsize-tokens_)/rate_);
}
}
void TBF_Timer::expire(Event* /*e*/)
{
tbf_->timeout(0);
}
static class TBFClass : public TclClass {
public:
TBFClass() : TclClass ("TBF") {}
TclObject* create(int,const char*const*) {
return (new TBF());
}
}class_tbf;
tclAppInit.cc
/*
* tclAppInit.c --
*
* Provides a default version of the main program and Tcl_AppInit
* procedure for Tcl applications (without Tk).
*
* Copyright (c) 1993 The Regents of the University of California.
* Copyright (c) 1994-1995 Sun Microsystems, Inc.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
* SCCS: @(#) tclAppInit.c 1.17 96/03/26 12:45:29
*/
#include "config.h"
extern void init_misc(void);
extern EmbeddedTcl et_ns_lib;
extern EmbeddedTcl et_ns_ptypes;
/* MSVC requires this global var declaration to be outside of 'extern "C"' */
#ifdef MEMDEBUG_SIMULATIONS
#include "mem-trace.h"
MemTrace *globalMemTrace;
#endif
extern "C" {
/*
* The following variable is a special hack that is needed in order for
* Sun shared libraries to be used for Tcl.
*/
#ifdef TCL_TEST
EXTERN int Tcltest_Init _ANSI_ARGS_((Tcl_Interp *interp));
#endif /* TCL_TEST */
/*
*----------------------------------------------------------------------
*
* main --
*
* This is the main program for the application.
*
* Results:
* None: Tcl_Main never returns here, so this procedure never
* returns either.
*
* Side effects:
* Whatever the application does.
*
*----------------------------------------------------------------------
*/
int
main(int argc, char **argv)
{
Tcl_Main(argc, argv, Tcl_AppInit);
return 0; /* Needed only to prevent compiler warning. */
}
/*
*----------------------------------------------------------------------
*
* Tcl_AppInit --
*
* This procedure performs application-specific initialization.
* Most applications, especially those that incorporate additional
* packages, will have their own version of this procedure.
*
* Results:
* Returns a standard Tcl completion code, and leaves an error
* message in interp->result if an error occurs.
*
* Side effects:
* Depends on the startup script.
*
*----------------------------------------------------------------------
*/
int
Tcl_AppInit(Tcl_Interp *interp)
{
#ifdef MEMDEBUG_SIMULATIONS
extern MemTrace *globalMemTrace;
globalMemTrace = new MemTrace;
#endif
if (Tcl_Init(interp) == TCL_ERROR ||
Otcl_Init(interp) == TCL_ERROR)
return TCL_ERROR;
#ifdef HAVE_LIBTCLDBG
extern int Tcldbg_Init(Tcl_Interp *); // hackorama
if (Tcldbg_Init(interp) == TCL_ERROR) {
return TCL_ERROR;
}
#endif
Tcl_SetVar(interp, "tcl_rcFileName", "~/.ns.tcl", TCL_GLOBAL_ONLY);
Tcl::init(interp, "ns");
et_ns_ptypes.load();
et_ns_lib.load();
init_misc();
#ifdef TCL_TEST
if (Tcltest_Init(interp) == TCL_ERROR) {
return TCL_ERROR;
}
Tcl_StaticPackage(interp, "Tcltest", Tcltest_Init,
(Tcl_PackageInitProc *) NULL);
#endif /* TCL_TEST */
return TCL_OK;
}
#ifndef WIN32
void
abort()
{
Tcl& tcl = Tcl::instance();
tcl.evalc("[Simulator instance] flush-trace");
#ifdef abort
#undef abort
abort();
#else
exit(1);
#endif /*abort*/
/*NOTREACHED*/
}
#endif
}
tcp-abs.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/* tcp-abs.cc
* Copyright (C) 1999 by USC/ISI
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation, advertising
* materials, and other materials related to such distribution and use
* acknowledge that the software was developed by the University of
* Southern California, Information Sciences Institute. The name of the
* University may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* Contributed by Polly Huang (USC/ISI), http://www-scf.usc.edu/~bhuang
* @(#) $Header$ (LBL)";
*/
#include "ip.h"
#include "tcp.h"
#include "tcp-abs.h"
//AbsTcp
AbsTcpAgent::AbsTcpAgent() : Agent(PT_TCP), rtt_(0), current_(NULL),offset_(0), seqno_lb_(-1), connection_size_(0), timer_(this), rescheduled_(0)
{
size_ = 1000;
}
void AbsTcpAgent::timeout()
{
if (rescheduled_ == 0 && current_->transition_[offset_]!= current_->transition_[0]) {
set_timer(2*rtt_);
rescheduled_ = 1;
} else {
rescheduled_ = 0;
seqno_lb_ += current_->batch_size_;
if (current_->drop_[offset_] == NULL) {
printf("Error: This fsm can't handle multi losses per connection\n");
exit(0);
}
current_ = current_->drop_[offset_];
send_batch();
}
}
void AbsTcpAgent::sendmsg(int pktcnt)
{
connection_size_ = pktcnt;
start();
}
void AbsTcpAgent::advanceby(int pktcnt)
{
connection_size_ = pktcnt;
start();
}
void AbsTcpAgent::start()
{
//printf("starting fsm tcp, %d\n", connection_size_);
send_batch();
}
void AbsTcpAgent::send_batch()
{
int seqno = seqno_lb_;
offset_ = 0;
//printf("sending batch, %d\n", current_->batch_size_);
for (int i=0; ibatch_size_ && seqno < connection_size_-1; i++) {
seqno++;
output(seqno);
}
if (seqno == connection_size_-1) {
finish();
}
else if (seqno < connection_size_-1) {
if (current_->drop_[offset_] == NULL) {
printf("Error: current fsm can't handle this tcp connection flow id %d (possibly too long)\n", fid_);
exit(0);
}
//printf("start timer %d\n", current_->transition_[offset_]);
if (current_->transition_[offset_] == 0) {
current_ = current_->drop_[offset_];
send_batch();
} else if (current_->transition_[offset_] == RTT) {
set_timer(rtt_);
} else if (current_->transition_[offset_] == TIMEOUT) {
set_timer(rtt_ * 3);
} else {
printf("Error: weird transition timer\n");
exit(0);
}
} else {
printf("Error: sending more than %d packets\n", connection_size_);
exit(0);
}
}
void AbsTcpAgent::drop(int seqno)
{
//printf("dropped: %d\n", seqno);
if (offset_ != 0) {
printf("Error: Sorry, can't handle multiple drops per batch\n");
exit(0);
}
offset_ = seqno - seqno_lb_;
connection_size_++;
}
void AbsTcpAgent::finish()
{
//printf("finish: sent %d\n", seqno_lb_+1);
cancel_timer();
}
void AbsTcpAgent::output(int seqno)
{
Packet* p = allocpkt();
hdr_tcp *tcph = hdr_tcp::access(p);
tcph->seqno() = seqno;
send(p, 0);
}
void AbsTcpAgent::recv(Packet* pkt, Handler*)
{
Packet::free(pkt);
}
int AbsTcpAgent::command(int argc, const char*const* argv)
{
if (argc == 3 ) {
if (strcmp(argv[1], "rtt") == 0) {
rtt_ = atof(argv[2]);
//printf("rtt %f\n", rtt_);
return (TCL_OK);
}
if (strcmp(argv[1], "advance") == 0) {
advanceby(atoi(argv[2]));
return (TCL_OK);
}
if (strcmp(argv[1], "advanceby") == 0) {
advanceby(atoi(argv[2]));
return (TCL_OK);
}
if(strcmp(argv[1], "print-stats") == 0) {
// xxx: works best if invoked on a new fsm
// (otherwise you don't get the whole thing).
int n = atoi(argv[2]);
if (n < 0 || n >= 17)
return TCL_ERROR;
FSM::print_FSM_stats(current_, n);
return (TCL_OK);
};
} else if (argc == 2) {
if (strcmp(argv[1], "print") == 0) {
// xxx: works best if invoked on a new fsm
// (otherwise you don't get the whole thing).
FSM::print_FSM(current_);
return (TCL_OK);
};
};
return (Agent::command(argc, argv));
}
void AbsTcpTimer::expire(Event*)
{
a_->timeout();
}
//AbsTCP/TahoeAck
static class AbsTcpTahoeAckClass : public TclClass {
public:
AbsTcpTahoeAckClass() : TclClass("Agent/AbsTCP/TahoeAck") {}
TclObject* create(int, const char*const*) {
return (new AbsTcpTahoeAckAgent());
}
} class_abstcptahoeack;
AbsTcpTahoeAckAgent::AbsTcpTahoeAckAgent() : AbsTcpAgent()
{
size_ = 1000;
current_ = TahoeAckFSM::instance().start_state();
DropTargetAgent::instance().insert_tcp(this);
}
//AbsTCP/RenoAck
static class AbsTcpRenoAckClass : public TclClass {
public:
AbsTcpRenoAckClass() : TclClass("Agent/AbsTCP/RenoAck") {}
TclObject* create(int, const char*const*) {
return (new AbsTcpRenoAckAgent());
}
} class_abstcprenoack;
AbsTcpRenoAckAgent::AbsTcpRenoAckAgent() : AbsTcpAgent()
{
size_ = 1000;
current_ = RenoAckFSM::instance().start_state();
DropTargetAgent::instance().insert_tcp(this);
}
//AbsTCP/TahoeDelAck
static class AbsTcpTahoeDelAckClass : public TclClass {
public:
AbsTcpTahoeDelAckClass() : TclClass("Agent/AbsTCP/TahoeDelAck") {}
TclObject* create(int, const char*const*) {
return (new AbsTcpTahoeDelAckAgent());
}
} class_abstcptahoedelack;
AbsTcpTahoeDelAckAgent::AbsTcpTahoeDelAckAgent() : AbsTcpAgent()
{
size_ = 1000;
current_ = TahoeDelAckFSM::instance().start_state();
DropTargetAgent::instance().insert_tcp(this);
}
//AbsTCP/RenoDelAck
static class AbsTcpRenoDelAckClass : public TclClass {
public:
AbsTcpRenoDelAckClass() : TclClass("Agent/AbsTCP/RenoDelAck") {}
TclObject* create(int, const char*const*) {
return (new AbsTcpRenoDelAckAgent());
}
} class_abstcprenodelack;
AbsTcpRenoDelAckAgent::AbsTcpRenoDelAckAgent() : AbsTcpAgent()
{
size_ = 1000;
current_ = RenoDelAckFSM::instance().start_state();
DropTargetAgent::instance().insert_tcp(this);
}
//AbsTcpSink
static class AbsTcpSinkClass : public TclClass {
public:
AbsTcpSinkClass() : TclClass("Agent/AbsTCPSink") {}
TclObject* create(int, const char*const*) {
return (new AbsTcpSink());
}
} class_abstcpsink;
AbsTcpSink::AbsTcpSink() : Agent(PT_ACK)
{
size_ = 40;
}
void AbsTcpSink::recv(Packet* pkt, Handler*)
{
Packet* p = allocpkt();
send(p, 0);
Packet::free(pkt);
}
static class AbsDelAckSinkClass : public TclClass {
public:
AbsDelAckSinkClass() : TclClass("Agent/AbsTCPSink/DelAck") {}
TclObject* create(int, const char*const*) {
return (new AbsDelAckSink());
}
} class_absdelacksink;
AbsDelAckSink::AbsDelAckSink() : AbsTcpSink(), delay_timer_(this)
{
size_ = 40;
interval_ = 0.1;
}
void AbsDelAckSink::recv(Packet* pkt, Handler*)
{
if (delay_timer_.status() != TIMER_PENDING) {
delay_timer_.resched(interval_);
} else {
delay_timer_.cancel();
Packet* p = allocpkt();
send(p, 0);
}
Packet::free(pkt);
}
void AbsDelAckSink::timeout()
{
/*
* The timer expired so we ACK the last packet seen.
* (shouldn't this check for a particular time out#? -kf)
*/
Packet* p = allocpkt();
send(p, 0);
}
void AbsDelayTimer::expire(Event */*e*/) {
a_->timeout();
}
//Special drop target agent
DropTargetAgent* DropTargetAgent::instance_;
static class DropTargetClass : public TclClass {
public:
DropTargetClass() : TclClass("DropTargetAgent") {}
TclObject* create(int, const char*const*) {
return (new DropTargetAgent());
}
} class_droptarget;
DropTargetAgent::DropTargetAgent(): Connector(), dropper_list_(NULL)
{
instance_ = this;
}
void DropTargetAgent::recv(Packet* pkt, Handler*)
{
Dropper* tmp = dropper_list_;
hdr_tcp *tcph = hdr_tcp::access(pkt);
hdr_ip *iph = hdr_ip::access(pkt);
//printf("flow %d dropping seqno %d\n", iph->flowid(),tcph->seqno());
while(tmp != NULL) {
if(tmp->agent_->flowid() == iph->flowid())
tmp->agent_->drop(tcph->seqno());
tmp = tmp->next_;
}
Packet::free(pkt);
}
void DropTargetAgent::insert_tcp(AbsTcpAgent* tcp)
{
Dropper* dppr = new Dropper;
dppr->agent_=tcp;
dppr->next_ = dropper_list_;
dropper_list_ = dppr;
}
tcp-asym-fs.cc
/* -*- 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, U.C.Berkeley
* http://daedalus.cs.berkeley.edu
*/
/*
* tcp-asym-fs: TCP mods for asymmetric networks (tcp-asym) with fast start
* (tcp-fs).
*
* Contributed by Venkat Padmanabhan (padmanab@cs.berkeley.edu),
* Daedalus Research Group, U.C.Berkeley
*/
#include "tcp-asym.h"
#include "tcp-fs.h"
/* TCP-FS with NewReno */
class NewRenoTcpAsymFsAgent : public NewRenoTcpAsymAgent, public NewRenoTcpFsAgent {
public:
NewRenoTcpAsymFsAgent() : NewRenoTcpAsymAgent(), NewRenoTcpFsAgent() {count_bytes_acked_= 1;}
/* helper functions */
virtual void output_helper(Packet* pkt) {NewRenoTcpAsymAgent::output_helper(pkt); NewRenoTcpFsAgent::output_helper(pkt);}
virtual void recv_helper(Packet* pkt) {NewRenoTcpAsymAgent::recv_helper(pkt); NewRenoTcpFsAgent::recv_helper(pkt);}
virtual void send_helper(int maxburst) {NewRenoTcpFsAgent::send_helper(maxburst);}
virtual void send_idle_helper() {NewRenoTcpFsAgent::send_idle_helper();}
virtual void recv_newack_helper(Packet* pkt) {printf("count_bytes_acked_=%d\n", count_bytes_acked_); NewRenoTcpFsAgent::recv_newack_helper(pkt); NewRenoTcpAsymAgent::t_exact_srtt_ = NewRenoTcpFsAgent::t_exact_srtt_;}
virtual void partialnewack_helper(Packet* pkt) {NewRenoTcpFsAgent::partialnewack_helper(pkt);}
virtual void set_rtx_timer() {NewRenoTcpFsAgent::set_rtx_timer();}
virtual void timeout_nonrtx(int tno) {NewRenoTcpFsAgent::timeout_nonrtx(tno);}
virtual void timeout_nonrtx_helper(int tno) {NewRenoTcpFsAgent::timeout_nonrtx_helper(tno);}
};
static class NewRenoTcpAsymFsClass : public TclClass {
public:
NewRenoTcpAsymFsClass() : TclClass("Agent/TCP/Newreno/Asym/FS") {}
TclObject* create(int, const char*const*) {
return (new NewRenoTcpAsymFsAgent());
}
} class_newrenotcpasymfs;
tcp-asym-sink.cc
/* -*- 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, U.C.Berkeley
* http://daedalus.cs.berkeley.edu
*
* @(#) $Header:
*/
/*
* tcp-asym includes modifications to several flavors of TCP to enhance
* performance over asymmetric networks, where the ack channel is
* constrained. Types of asymmetry we have studied and used these mods
* include bandwidth asymmetry and latency asymmetry (where variable
* latencies cause problems to TCP, e.g., in packet radio networks.
* The receiver-side code in this file is derived from the regular
* TCP sink code. The main additional functionality is that the sink responds
* to ECN by performing ack congestion control, i.e. it multiplicatively backs
* off the frequency with which it sends acks (up to a limit). For each
* subsequent round-trip period during which it does not receive an ECN,
* it gradually increases the frequency of acks (up to a maximum of 1
* per data packet).
*
* For questions/comments, please contact:
* Venkata N. Padmanabhan (padmanab@cs.berkeley.edu)
* http://www.cs.berkeley.edu/~padmanab
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (UCB)";
#endif
#include "template.h"
#include "flags.h"
#include "tcp-sink.h"
#include "tcp-asym.h"
class TcpAsymSink : public DelAckSink {
public:
TcpAsymSink(Acker*);
virtual void recv(Packet* pkt, Handler* h);
virtual void timeout(int tno);
protected:
virtual void add_to_ack(Packet* pkt);
int delackcount_; /* the number of consecutive packets that have
not been acked yet */
int maxdelack_; /* the maximum extent to which acks can be
delayed */
int delackfactor_; /* the dynamically varying limit on the extent
to which acks can be delayed */
int delacklim_; /* limit on the extent of del ack based on the
sender's window */
double ts_ecn_; /* the time when an ECN was received last */
double ts_decrease_; /* the time when delackfactor_ was decreased last */
double highest_ts_echo_;/* the highest timestamp echoed by the peer */
};
static class TcpAsymSinkClass : public TclClass {
public:
TcpAsymSinkClass() : TclClass("Agent/TCPSink/Asym") {}
TclObject* create(int, const char*const*) {
return (new TcpAsymSink(new Acker));
}
} class_tcpasymsink;
TcpAsymSink::TcpAsymSink(Acker* acker) : DelAckSink(acker), delackcount_(0),
delackfactor_(1), delacklim_(0), ts_ecn_(0), ts_decrease_(0)
{
bind("maxdelack_", &maxdelack_);
}
/* Add fields to the ack. Not needed? */
void TcpAsymSink::add_to_ack(Packet* pkt)
{
hdr_tcpasym *tha = hdr_tcpasym::access(pkt);
tha->ackcount() = delackcount_;
}
void TcpAsymSink::recv(Packet* pkt, Handler*)
{
int olddelackfactor = delackfactor_;
int olddelacklim = delacklim_;
int max_sender_can_send = 0;
hdr_flags *fh = hdr_flags::access(pkt);
hdr_tcp *th = hdr_tcp::access(pkt);
hdr_tcpasym *tha = hdr_tcpasym::access(pkt);
double now = Scheduler::instance().clock();
int numBytes = hdr_cmn::access(pkt)->size();
acker_->update_ts(th->seqno(),th->ts());
acker_->update(th->seqno(), numBytes);
#if 0 // johnh
int numToDeliver;
/* XXX if the #if 0 is removed, delete the call to acker_->update() above */
numToDeliver = acker_->update(th->seqno(), numBytes);
if (numToDeliver)
recvBytes(numToDeliver);
#endif /* 0 */
/* determine the highest timestamp the sender has echoed */
highest_ts_echo_ = max(highest_ts_echo_, th->ts_echo());
/*
* if we receive an ECN and haven't received one in the past
* round-trip, double delackfactor_ (and consequently halve
* the frequency of acks) subject to a maximum
*/
if (fh->ecnecho() && highest_ts_echo_ >= ts_ecn_) {
delackfactor_ = min(2*delackfactor_, maxdelack_);
ts_ecn_ = now;
}
/*
* else if we haven't received an ECN in the past round trip and
* haven't (linearly) decreased delackfactor_ in the past round
* trip, we decrease delackfactor_ by 1 (and consequently increase
* the frequency of acks) subject to a minimum
*/
else if (highest_ts_echo_ >= ts_ecn_ && highest_ts_echo_ >= ts_decrease_) {
delackfactor_ = max(delackfactor_ - 1, 1);
ts_decrease_ = now;
}
/*
* if this is the next packet in sequence, we can consider delaying the ack.
* Set delacklim_ based on how much data the sender can send if we don't
* send back any more acks. The idea is to avoid stalling the sender because
* of a lack of acks.
*/
if (th->seqno() == acker_->Seqno()) {
max_sender_can_send = (int) min(tha->win()+acker_->Seqno()-tha->highest_ack(), tha->max_left_to_send());
/* XXXX we use a safety factor 2 */
delacklim_ = min(maxdelack_, max_sender_can_send/2);
}
else
delacklim_ = 0;
if (delackfactor_ < delacklim_)
delacklim_ = delackfactor_;
/*
* Log values of variables of interest. Since this is the only place
* where this is done, we decided against using a more general method
* as used for logging TCP sender state variables.
*/
if (channel_ && (olddelackfactor != delackfactor_ || olddelacklim != delacklim_)) {
char wrk[500];
int n;
/* we print src and dst in reverse order to conform to sender side */
sprintf(wrk, "time: %-6.3f saddr: %-2d sport: %-2d daddr:"
" %-2d dport: %-2d dafactor: %2d dalim: %2d max_scs:"
" %4d win: %4d\n", now, addr(), port(),
daddr(), dport(), delackfactor_,
delacklim_,max_sender_can_send, tha->win());
n = strlen(wrk);
wrk[n] = '\n';
wrk[n+1] = 0;
(void)Tcl_Write(channel_, wrk, n+1);
wrk[n] = 0;
}
delackcount_++;
/* check if we have waited long enough that we should send an ack */
if (delackcount_ < delacklim_) { /* it is not yet time to send an ack */
/* if the delayed ack timer is not set, set it now */
if (!(delay_timer_.status() == TIMER_PENDING)) {
save_ = pkt;
delay_timer_.resched(interval_);
}
else {
hdr_tcp *sth = hdr_tcp::access(save_);
/* save the pkt with the more recent timestamp */
if (th->ts() > sth->ts()) {
Packet::free(save_);
save_ = pkt;
}
}
return;
}
else { /* send back an ack now */
if (delay_timer_.status() == TIMER_PENDING) {
delay_timer_.cancel();
Packet::free(save_);
save_ = 0;
}
hdr_flags* hf = hdr_flags::access(pkt);
hf->ect() = 1;
ack(pkt);
delackcount_ = 0;
Packet::free(pkt);
}
}
void TcpAsymSink::timeout(int /*tno*/)
{
/*
* The timer expired so we ACK the last packet seen.
*/
Packet* pkt = save_;
delackcount_ = 0;
ack(pkt);
save_ = 0;
Packet::free(pkt);
}
tcp-asym.cc
/* -*- 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, U.C.Berkeley
* http://daedalus.cs.berkeley.edu
*
* @(#) $Header:
*/
/*
* tcp-asym includes modifications to several flavors of TCP to enhance
* performance over asymmetric networks, where the ack channel is
* constrained. Types of asymmetry we have studied and used these mods
* include bandwidth asymmetry and latency asymmetry (where variable
* latencies cause problems to TCP, e.g., in packet radio networks.
* The sender-side modifications in this file are structured as helper
* functions that are invoked from various points in the TCP code. The main
* additional functionality is (a) the sender increases its congestion window
* in proportion to the amount of data acked rather than the number of acks
* received, (b) it breaks up potentially large bursts into smaller ones
* when acks are few and far between by rate-based pacing, and
* (c) it copies the ecn_to_echo_ bit from acks into subsequent data packets,
* using which the sink can perform ack congestion control (tcp-asym-sink.cc).
* The tcp-asym source is usually used in conjunction with a tcp-asym sink, or
* with a router/end-host implementing ack filtering (semantic-packetqueue.cc).
*
* For questions/comments, please contact:
* Venkata N. Padmanabhan (padmanab@cs.berkeley.edu)
* http://www.cs.berkeley.edu/~padmanab
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (UCB)";
#endif
#include "tcp-asym.h"
int hdr_tcpasym::offset_;
static class TCPAHeaderClass : public PacketHeaderClass {
public:
TCPAHeaderClass() : PacketHeaderClass("PacketHeader/TCPA",
sizeof(hdr_tcpasym)) {
bind_offset(&hdr_tcpasym::offset_);
}
} class_tcpahdr;
static class TcpAsymClass : public TclClass {
public:
TcpAsymClass() : TclClass("Agent/TCP/Asym") {}
TclObject* create(int, const char*const*) {
return (new TcpAsymAgent());
}
} class_tcpasym;
TcpAsymAgent::TcpAsymAgent() : TcpAgent(), ecn_to_echo_(0), t_exact_srtt_(0)
{
/* bind("exact_srtt_", &t_exact_srtt_);*/
bind("g_", &g_);
/* bind("avg_win_", &avg_win_);*/
/* bind("damp_", &damp_);*/
}
static class TcpRenoAsymClass : public TclClass {
public:
TcpRenoAsymClass() : TclClass("Agent/TCP/Reno/Asym") {}
TclObject* create(int, const char*const*) {
return (new TcpRenoAsymAgent());
}
} class_tcprenoasym;
static class NewRenoTcpAsymClass : public TclClass {
public:
NewRenoTcpAsymClass() : TclClass("Agent/TCP/Newreno/Asym") {}
TclObject* create(int, const char*const*) {
return (new NewRenoTcpAsymAgent());
}
} class_newrenotcpasym;
/* The helper functions */
/* fill in the TCP asym header before packet is sent out */
void TcpAsymAgent::output_helper(Packet* p)
{
hdr_tcpasym *tcpha = hdr_tcpasym::access(p);
hdr_flags *flagsh = hdr_flags::access(p);
tcpha->win() = min(highest_ack_+window(), curseq_) - t_seqno_;
tcpha->highest_ack() = highest_ack_;
tcpha->max_left_to_send() = curseq_ - highest_ack_; /* XXXX not needed? */
flagsh->ecnecho() = ecn_to_echo_;
ecn_to_echo_ = 0;
}
/* schedule the next burst of data (of size at most maxburst) */
void TcpAsymAgent::send_helper(int maxburst)
{
/*
* If there is still data to be sent and there is space in the
* window, set a timer to schedule the next burst. Note that
* we wouldn't get here if TCP_TIMER_BURSTSEND were pending,
* so we do not need an explicit check here.
*/
if (t_seqno_ <= highest_ack_ + window() && t_seqno_ < curseq_) {
burstsnd_timer_.resched(t_exact_srtt_*maxburst/window());
}
}
/* check if the received ack has an ECN that needs to be echoed back to the sink */
void TcpAsymAgent::recv_helper(Packet *pkt)
{
if (hdr_flags::access(pkt)->ce())
ecn_to_echo_ = 1;
}
/* open cwnd in proportion to the amount of data acked */
void TcpAsymAgent::recv_newack_helper(Packet *pkt)
{
int i;
hdr_tcp *tcph = hdr_tcp::access(pkt);
int ackcount = tcph->seqno() - last_ack_;
double tao = Scheduler::instance().clock() - tcph->ts_echo();
newack(pkt);
/* update our fine-grained estimate of the smoothed RTT */
if (t_exact_srtt_ != 0)
t_exact_srtt_ = g_*tao + (1-g_)*t_exact_srtt_;
else
t_exact_srtt_ = tao;
/* avg_win_ = g_*window() + (1-g_)*avg_win_;*/
/* grow cwnd */
for (i=0; i= curseq_-1) && !closed_) {
closed_ = 1;
finish();
}
}
/* Print out if tcp-asym-specific variables have been modified */
void TcpAsymAgent::traceVar(TracedVar* v) {
Scheduler& s = Scheduler::instance();
char wrk[500];
double curtime = &s ? s.clock() : 0;
if (!strcmp(v->name(), "exact_srtt_"))
sprintf(wrk,"%-8.5f %-2d %-2d %-2d %-2d %s %-6.3f",
curtime, addr(), port(), daddr(), dport(),
v->name(), double(*((TracedDouble*) v)));
else if (!strcmp(v->name(), "avg_win_"))
sprintf(wrk,"%-8.5f %-2d %-2d %-2d %-2d %s %-6.3f",
curtime, addr(), port(), daddr(), dport(),
v->name(), double(*((TracedDouble*) v)));
else {
TcpAgent::traceVar(v);
return;
}
int n = strlen(wrk);
wrk[n] = '\n';
wrk[n+1] = 0;
if (channel_)
(void)Tcl_Write(channel_, wrk, n+1);
wrk[n] = 0;
return;
}
/* Print out all the traced variables whenever any one is changed */
void
TcpAsymAgent::traceAll() {
double curtime;
Scheduler& s = Scheduler::instance();
char wrk[500];
int n;
TcpAgent::traceAll();
curtime = &s ? s.clock() : 0;
sprintf(wrk, "time: %-8.5f saddr: %-2d sport: %-2d daddr: %-2d dport:"
" %-2d exact_srtt %d", curtime, addr(), port(),
daddr(), dport(), (int(t_exact_srtt_)));
n = strlen(wrk);
wrk[n] = '\n';
wrk[n+1] = 0;
if (channel_)
(void)Tcl_Write(channel_, wrk, n+1);
wrk[n] = 0;
return;
}
tcp-fack.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1990, 1997 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Lawrence Berkeley Laboratory,
* Berkeley, CA. The name of the University may not be used to
* endorse or promote products derived from this software without
* specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (PSC)";
#endif
#include
#include
#include
#include "ip.h"
#include "tcp.h"
#include "flags.h"
#include "scoreboard.h"
#include "random.h"
#include "tcp-fack.h"
#include "template.h"
static class FackTcpClass : public TclClass {
public:
FackTcpClass() : TclClass("Agent/TCP/Fack") {}
TclObject* create(int, const char*const*) {
return (new FackTcpAgent());
}
} class_fack;
FackTcpAgent::FackTcpAgent() : timeout_(FALSE), wintrim_(0),
wintrimmult_(.5), rampdown_(0), fack_(-1), retran_data_(0),
ss_div4_(0) // What about fastrecov_ and scb_
{
bind_bool("ss-div4_", &ss_div4_);
bind_bool("rampdown_", &rampdown_);
}
int FackTcpAgent::window()
{
int win;
win = int((cwnd_ < wnd_ ? (double) cwnd_ : (double) wnd_) + wintrim_);
return (win);
}
void FackTcpAgent::reset ()
{
scb_.ClearScoreBoard();
TcpAgent::reset ();
}
/*
* Process a dupack.
*/
void FackTcpAgent::oldack(Packet* pkt)
{
hdr_tcp *tcph = (hdr_tcp*)pkt->access(off_tcp_);
last_ack_ = tcph->seqno();
highest_ack_ = last_ack_;
fack_ = max(fack_,highest_ack_);
/*
* There are conditions under which certain versions of TCP (e.g., tcp-fs)
* retract maxseq_. The following line of code helps in those cases. For
* versions of TCP, it is a NOP.
*/
maxseq_ = max(maxseq_, highest_ack_);
if (t_seqno_ < last_ack_ + 1)
t_seqno_ = last_ack_ + 1;
newtimer(pkt);
if (rtt_active_ && tcph->seqno() >= rtt_seq_) {
rtt_active_ = 0;
t_backoff_ = 1;
}
/* with timestamp option */
double tao = Scheduler::instance().clock() - tcph->ts_echo();
rtt_update(tao);
/* update average window */
awnd_ *= 1.0 - wnd_th_;
awnd_ += wnd_th_ * cwnd_;
/* if the connection is done, call finish() */
if ((highest_ack_ >= curseq_-1) && !closed_) {
closed_ = 1;
finish();
}
}
int FackTcpAgent::maxsack(Packet *pkt)
{
hdr_tcp *tcph = (hdr_tcp*)pkt->access(off_tcp_);
int maxsack=-1, sack_index;
for (sack_index=0; sack_index < tcph->sa_length(); sack_index++) {
if (tcph->sa_right(sack_index) > maxsack)
maxsack = tcph->sa_right(sack_index);
}
return (maxsack-1);
}
void FackTcpAgent::recv_newack_helper(Packet *pkt) {
newack(pkt);
opencwnd();
/* if the connection is done, call finish() */
if ((highest_ack_ >= curseq_-1) && !closed_) {
closed_ = 1;
finish();
}
}
void FackTcpAgent::recv(Packet *pkt, Handler*)
{
hdr_tcp *tcph = (hdr_tcp*)pkt->access(off_tcp_);
int ms;
#ifdef notdef
if (pkt->type_ != PT_ACK) {
Tcl::instance().evalf("%s error \"received non-ack\"",
name());
Packet::free(pkt);
return;
}
#endif
ts_peer_ = tcph->ts();
if (((hdr_flags*)pkt->access(off_flags_))->ecnecho() && ecn_)
ecn(tcph->seqno());
recv_helper(pkt);
if (!fastrecov_) { // Not in fast recovery
if ((int)tcph->seqno() > last_ack_ && tcph->sa_length() == 0) {
/*
* regular ACK not in fast recovery... normal
*/
recv_newack_helper(pkt);
fack_ = last_ack_;
timeout_ = FALSE;
scb_.ClearScoreBoard();
retran_data_ = 0;
wintrim_ = 0;
} else if ((int)tcph->seqno() < last_ack_) {
// Do nothing; ack may have been misordered
} else {
retran_data_ -= scb_.UpdateScoreBoard (highest_ack_, tcph);
oldack(pkt);
ms = maxsack(pkt);
if (ms > fack_)
fack_ = ms;
if (fack_ >= t_seqno_)
t_seqno_ = fack_ + 1;
dupacks_ = (fack_ - last_ack_) - 1;
/*
* a duplicate ACK
*/
if (dupacks_ >= NUMDUPACKS) {
/*
* Assume we dropped just one packet.
* Retransmit last ack + 1
* and try to resume the sequence.
*/
recover_ = t_seqno_;
last_cwnd_action_ = CWND_ACTION_DUPACK;
if ((ss_div4_ == 1) && (cwnd_ <= ssthresh_ + .5)) {
cwnd_ /= 2;
wintrimmult_ = .75;
} else {
wintrimmult_ = .5;
}
slowdown(CLOSE_SSTHRESH_HALF|CLOSE_CWND_RESTART);
if (rampdown_) {
wintrim_ = (t_seqno_ - fack_ - 1) * wintrimmult_;
}
reset_rtx_timer(1,0);
fastrecov_ = TRUE;
scb_.MarkRetran (last_ack_+1, t_seqno_);
retran_data_++;
output(last_ack_ + 1, TCP_REASON_DUPACK);
}
}
if (dupacks_ == 0)
send_much(FALSE, 0, maxburst_);
} else {
// we are in fast recovery
oldack(pkt);
ms = maxsack(pkt);
if (ms > fack_) {
if (rampdown_) {
wintrim_ -= ((float)ms - (float)fack_)* wintrimmult_;
if (wintrim_< 0)
wintrim_ = 0;
}
fack_ = ms;
}
if (fack_ >= t_seqno_)
t_seqno_ = fack_ + 1;
retran_data_ -= scb_.UpdateScoreBoard (highest_ack_, tcph);
// If the retransmission was lost again, timeout_ forced to TRUE
// if timeout_ TRUE, this shuts off send()
timeout_ |= scb_.CheckSndNxt (tcph);
opencwnd();
if (retran_data_ < 0) {
printf("Error, retran_data_ < 0");
}
if ((int)tcph->sa_length() == 0 && (last_ack_ >= recover_)) {
// No SACK blocks indicates fast recovery is over
fastrecov_ = FALSE;
timeout_ = FALSE;
scb_.ClearScoreBoard();
retran_data_ = 0;
wintrim_ = 0;
dupacks_ = 0;
}
send_much(FALSE, 0, maxburst_);
}
Packet::free(pkt);
#ifdef notyet
if (trace_)
plot();
#endif
}
void FackTcpAgent::timeout(int tno)
{
if (tno == TCP_TIMER_RTX) {
if (highest_ack_ == maxseq_ && !slow_start_restart_) {
/*
* TCP option:
* If no outstanding data, then don't do anything.
*/
return;
};
// Do not clear fastrecov_ or alter recover_ variable
timeout_ = FALSE;
if (highest_ack_ > last_ack_)
last_ack_ = highest_ack_;
#ifdef DEBUGSACK1A
printf ("timeout. highest_ack: %d seqno: %d\n",
highest_ack_, t_seqno_);
#endif
retran_data_ = 0;
last_cwnd_action_ = CWND_ACTION_TIMEOUT;
/* if there is no outstanding data, don't cut down ssthresh_ */
if (highest_ack_ == maxseq_ && restart_bugfix_)
slowdown(CLOSE_CWND_INIT);
else {
// close down to 1 segment
slowdown(CLOSE_SSTHRESH_HALF|CLOSE_CWND_RESTART);
}
scb_.ClearScoreBoard();
/* if there is no outstanding data, don't back off rtx timer */
if (highest_ack_ == maxseq_)
reset_rtx_timer(TCP_REASON_TIMEOUT,0);
else
reset_rtx_timer(TCP_REASON_TIMEOUT,1);
fack_ = last_ack_;
t_seqno_ = last_ack_ + 1;
send_much(0, TCP_REASON_TIMEOUT);
} else {
TcpAgent::timeout(tno);
}
}
void FackTcpAgent::send_much(int force, int reason, int maxburst)
{
register int found, npacket = 0;
send_idle_helper();
int win = window();
int xmit_seqno;
if (!force && delsnd_timer_.status() == TIMER_PENDING)
return;
/*
* If TCP_TIMER_BURSTSND is pending, cancel it. The timer is
* set again, if necessary, after the maxburst pakts have been
* sent out.
*/
if (burstsnd_timer_.status() == TIMER_PENDING)
burstsnd_timer_.cancel();
found = 1;
/*
* as long as the pipe is open and there is app data to send...
*/
while (( t_seqno_ <= fack_ + win - retran_data_) && (!timeout_)) {
if (overhead_ == 0 || force) {
found = 0;
xmit_seqno = scb_.GetNextRetran ();
#ifdef DEBUGSACK1A
printf("highest_ack: %d xmit_seqno: %d timeout: %d seqno: %d fack: % d win: %d retran_data: %d\n",
highest_ack_, xmit_seqno, timeout_, t_seqno_, fack_, win, retran_data_);
#endif
if (xmit_seqno == -1) { // no retransmissions to send
/*
* if there is no more application data to send,
* do nothing
*/
if (t_seqno_ >= curseq_)
return;
found = 1;
xmit_seqno = t_seqno_++;
#ifdef DEBUGSACK1A
printf("sending %d fastrecovery: %d win %d\n",
xmit_seqno, fastrecov_, win);
#endif
} else {
found = 1;
scb_.MarkRetran (xmit_seqno, t_seqno_);
retran_data_++;
win = window();
}
if (found) {
output(xmit_seqno, reason);
if (t_seqno_ <= xmit_seqno) {
printf("Hit a strange case 2.\n");
t_seqno_ = xmit_seqno + 1;
}
npacket++;
}
} else if (!(delsnd_timer_.status() == TIMER_PENDING)) {
/*
* Set a delayed send timeout.
*/
delsnd_timer_.resched(Random::uniform(overhead_));
return;
}
if (maxburst && npacket == maxburst)
break;
} /* while */
/* call helper function */
send_helper(maxburst);
}
/*
* open up the congestion window -- Hack hack hack
*/
void FackTcpAgent::opencwnd()
{
TcpAgent::opencwnd();
// if maxcwnd_ is set (nonzero), make it the cwnd limit
if (maxcwnd_ && (int (cwnd_) > maxcwnd_))
cwnd_ = maxcwnd_;
}
void FackTcpAgent::plot()
{
#ifdef notyet
double t = Scheduler::instance().clock();
sprintf(trace_->buffer(), "t %g %d rtt %g\n",
t, class_, t_rtt_ * tcp_tick_);
trace_->dump();
sprintf(trace_->buffer(), "t %g %d dev %g\n",
t, class_, t_rttvar_ * tcp_tick_);
trace_->dump();
sprintf(trace_->buffer(), "t %g %d win %f\n", t, class_, cwnd_);
trace_->dump();
sprintf(trace_->buffer(), "t %g %d bck %d\n", t, class_, t_backoff_);
trace_->dump();
#endif
}
tcp-fs.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* tcp-fs: TCP with "fast start", a procedure for avoiding the penalty of
* slow start when a connection resumes after a pause. The connection tries
* to reuse values old values of variables such as cwnd_, ssthresh_, srtt_,
* etc., with suitable modifications. The same procedure as used in tcp-asym
* is used to clock out packets until ack clocking kicks in. A connection
* doing fast start protects itself and other connections in the network against
* the adverse consequences of stale information by (a) marking all packets sent
* during fast start as low priority for the purposes of a priority-drop router,
* and (b) quickly detecting the loss of several fast start packets and falling
* back to a regular slow start, almost as if a fast start had never been attempted
* in the first place.
*
* Contributed by Venkat Padmanabhan (padmanab@cs.berkeley.edu),
* Daedalus Research Group, U.C.Berkeley
*/
#include "tcp-fs.h"
void ResetTimer::expire(Event *) {
a_->timeout(TCP_TIMER_RESET);
}
static class TcpFsClass : public TclClass {
public:
TcpFsClass() : TclClass("Agent/TCP/FS") {}
TclObject* create(int, const char*const*) {
return (new TcpFsAgent());
}
} class_tcpfs;
static class RenoTcpFsClass : public TclClass {
public:
RenoTcpFsClass() : TclClass("Agent/TCP/Reno/FS") {}
TclObject* create(int, const char*const*) {
return (new RenoTcpFsAgent());
}
} class_renotcpfs;
static class NewRenoTcpFsClass : public TclClass {
public:
NewRenoTcpFsClass() : TclClass("Agent/TCP/Newreno/FS") {}
TclObject* create(int, const char*const*) {
return (new NewRenoTcpFsAgent());
}
} class_newrenotcpfs;
#ifdef USE_FACK
static class FackTcpFsClass : public TclClass {
public:
FackTcpFsClass() : TclClass("Agent/TCP/Fack/FS") {}
TclObject* create(int, const char*const*) {
return (new FackTcpFsAgent());
}
} class_facktcpfs;
#endif
/* mark packets sent as part of fast start */
void
TcpFsAgent::output_helper(Packet *pkt)
{
hdr_tcp *tcph = (hdr_tcp*)pkt->access(off_tcp_);
double now = Scheduler::instance().clock();
double idle_time = now - last_recv_time_;
double timeout = ((t_srtt_ >> 3) + t_rttvar_) * tcp_tick_ ;
maxseq_ = max(maxseq_, highest_ack_);
/*
* if the connection has been idle (with no outstanding data) for long
* enough, we enter the fast start phase. We compute the start and end
* sequence numbers that define the fast start phase. Note that the
* first packet to be sent next is not included because it would have
* been sent even with regular slow start.
*/
if ((idle_time > timeout) && (maxseq_ == highest_ack_) && (cwnd_ > 1)){
/*
* We set cwnd_ to a "safe" value: cwnd_/2 if the connection
* was in slow start before the start of the idle period and
* cwnd_-1 if it was in congestion avoidance phase.
*/
/* if (cwnd_ < ssthresh_+1)*/
if (cwnd_ < ssthresh_)
cwnd_ = int(cwnd_/2);
else
cwnd_ -= 1;
/* set the start and end seq. nos. for the fast start phase */
fs_startseq_ = highest_ack_+2;
fs_endseq_ = highest_ack_+window()+1;
fs_mode_ = 1;
}
/* initially set fs_ flag to 0 */
((hdr_flags*)pkt->access(off_flags_))->fs_ = 0;
/* check if packet belongs to the fast start phase. */
if (tcph->seqno() >= fs_startseq_ && tcph->seqno() < fs_endseq_ && fs_mode_) {
/* if not a retransmission, mark the packet */
if (tcph->seqno() > maxseq_) {
((hdr_flags*)pkt->access(off_flags_))->fs_ = 1;
}
}
}
/* update last_output_time_ */
void
TcpFsAgent::recv_helper(Packet *)
{
double now = Scheduler::instance().clock();
last_recv_time_ = now;
}
/* schedule the next burst of data (of size at most maxburst) */
void
TcpFsAgent::send_helper(int maxburst)
{
/*
* If there is still data to be sent and there is space in the
* window, set a timer to schedule the next burst. Note that
* we wouldn't get here if TCP_TIMER_BURSTSEND were pending,
* so we do not need an explicit check here.
*/
if (t_seqno_ <= highest_ack_ + window() && t_seqno_ < curseq_) {
burstsnd_timer_.resched(t_exact_srtt_*maxburst/window());
}
}
#ifdef USE_FACK
/* schedule the next burst of data (of size at most maxburst) */
void
FackTcpFsAgent::send_helper(int maxburst)
{
/*
* If there is still data to be sent and there is space in the
* window, set a timer to schedule the next burst. Note that
* we wouldn't get here if TCP_TIMER_BURSTSEND were pending,
* so we do not need an explicit check here.
*/
if ((t_seqno_ <= fack_ + window() - retran_data_) && (!timeout_) && (t_seqno_ < curseq_)) {
burstsnd_timer_.resched(t_exact_srtt_*maxburst/window());
}
}
#endif
/* do appropriate processing depending on the length of idle time */
void
TcpFsAgent::send_idle_helper()
{
// Commented out because they are not used
// XXX What processing belong here??? - haoboy
//double now = Scheduler::instance().clock();
//double idle_time = now - last_recv_time_;
}
/* update srtt estimate */
void
TcpFsAgent::recv_newack_helper(Packet *pkt)
{
hdr_tcp *tcph = (hdr_tcp*)pkt->access(off_tcp_);
double tao = Scheduler::instance().clock() - tcph->ts_echo();
double g = 1/8; /* gain used for smoothing rtt */
double h = 1/4; /* gain used for smoothing rttvar */
double delta;
int ackcount, i;
/*
* If we are counting the actual amount of data acked, ackcount >= 1.
* Otherwise, ackcount=1 just as in standard TCP.
*/
if (count_bytes_acked_)
ackcount = tcph->seqno() - last_ack_;
else
ackcount = 1;
newack(pkt);
maxseq_ = max(maxseq_, highest_ack_);
if (t_exact_srtt_ != 0) {
delta = tao - t_exact_srtt_;
if (delta < 0)
delta = -delta;
/* update the fine-grained estimate of the smoothed RTT */
if (t_exact_srtt_ != 0)
t_exact_srtt_ = g*tao + (1-g)*t_exact_srtt_;
else
t_exact_srtt_ = tao;
/* update the fine-grained estimate of mean deviation in RTT */
delta -= t_exact_rttvar_;
t_exact_rttvar_ += h*delta;
}
else {
t_exact_srtt_ = tao;
t_exact_rttvar_ = tao/2;
}
/* grow cwnd. ackcount > 1 indicates that actual ack counting is enabled */
for (i=0; i= fs_endseq_-1))
fs_mode_ = 0;
/* if the connection is done, call finish() */
if ((highest_ack_ >= curseq_-1) && !closed_) {
closed_ = 1;
finish();
}
}
void
NewRenoTcpFsAgent::partialnewack_helper(Packet* pkt)
{
partialnewack(pkt);
/* Do this because we may have retracted maxseq_ */
maxseq_ = max(maxseq_, highest_ack_);
if (fs_mode_ && fast_loss_recov_) {
/*
* A partial new ack implies that more than one packet has been lost
* in the window. Rather than recover one loss per RTT, we get out of
* fast start mode and do a slow start (no rtx timeout, though).
*/
timeout_nonrtx(TCP_TIMER_RESET);
}
else {
output(last_ack_ + 1, 0);
}
}
void
TcpFsAgent::set_rtx_timer()
{
if (rtx_timer_.status() == TIMER_PENDING)
rtx_timer_.cancel();
if (reset_timer_.status() == TIMER_PENDING)
reset_timer_.cancel();
if (fs_mode_ && fast_loss_recov_ && fast_reset_timer_)
reset_timer_.resched(rtt_exact_timeout());
else if (fs_mode_ && fast_loss_recov_)
reset_timer_.resched(rtt_timeout());
else
rtx_timer_.resched(rtt_timeout());
}
void
TcpFsAgent::cancel_rtx_timer()
{
rtx_timer_.force_cancel();
reset_timer_.force_cancel();
}
void
TcpFsAgent::cancel_timers()
{
rtx_timer_.force_cancel();
reset_timer_.force_cancel();
burstsnd_timer_.force_cancel();
delsnd_timer_.force_cancel();
}
void
TcpFsAgent::timeout_nonrtx(int tno)
{
if (tno == TCP_TIMER_RESET) {
fs_mode_ = 0; /* out of fast start mode */
dupacks_ = 0; /* just to be safe */
if (highest_ack_ == maxseq_ && !slow_start_restart_) {
/*
* TCP option:
* If no outstanding data, then don't do anything.
*/
return;
};
/*
* If the pkt sent just before the fast start phase has not
* gotten through, treat this as a regular rtx timeout.
*/
if (highest_ack_ < fs_startseq_-1) {
maxseq_ = fs_startseq_ - 1;
recover_ = maxseq_;
timeout(TCP_TIMER_RTX);
}
/* otherwise decrease window size to 1 but don't back off rtx timer */
else {
if (highest_ack_ > last_ack_)
last_ack_ = highest_ack_;
maxseq_ = last_ack_;
recover_ = maxseq_;
last_cwnd_action_ = CWND_ACTION_TIMEOUT;
slowdown(CLOSE_CWND_INIT);
timeout_nonrtx_helper(tno);
}
}
else {
TcpAgent::timeout_nonrtx(tno);
}
}
void
TcpFsAgent::timeout_nonrtx_helper(int tno)
{
if (tno == TCP_TIMER_RESET) {
reset_rtx_timer(0,0);
send_much(0, TCP_REASON_TIMEOUT, maxburst_);
}
}
void
RenoTcpFsAgent::timeout_nonrtx_helper(int tno)
{
if (tno == TCP_TIMER_RESET) {
dupwnd_ = 0;
dupacks_ = 0;
TcpFsAgent::timeout_nonrtx_helper(tno);
}
}
void
NewRenoTcpFsAgent::timeout_nonrtx_helper(int tno)
{
if (tno == TCP_TIMER_RESET) {
dupwnd_ = 0;
dupacks_ = 0;
TcpFsAgent::timeout_nonrtx_helper(tno);
}
}
#ifdef USE_FACK
void
FackTcpFsAgent::timeout_nonrtx_helper(int tno)
{
if (tno == TCP_TIMER_RESET) {
timeout_ = FALSE;
retran_data_ = 0;
fack_ = last_ack_;
t_seqno_ = last_ack_ + 1;
reset_rtx_timer(TCP_REASON_TIMEOUT, 0);
send_much(0, TCP_REASON_TIMEOUT);
}
}
#endif
tcp-full.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1997, 1998 The 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 Network Research
* Group at Lawrence Berkeley National Laboratory.
* 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.
*/
/*
* This code below was motivated in part by code contributed by
* Kathie Nichols (nichols@baynetworks.com). The code below is based primarily
* on the 4.4BSD TCP implementation. -KF [kfall@ee.lbl.gov]
*
* Kathie Nichols and Van Jacobson have contributed significant bug fixes,
* especially with respect to the the handling of sequence numbers during
* connection establishment/clearin. Additional fixes have followed
* theirs.
*
* Fixes for gensack() and ReassemblyQueue::add() contributed by Richard
* Mortier
*
* Some warnings and comments:
* this version of TCP will not work correctly if the sequence number
* goes above 2147483648 due to sequence number wrap
*
* this version of TCP by default sends data at the beginning of a
* connection in the "typical" way... That is,
* A ------> SYN ------> B
* A <----- SYN+ACK ---- B
* A ------> ACK ------> B
* A ------> data -----> B
*
* there is no dynamic receiver's advertised window. The advertised
* window is simulated by simply telling the sender a bound on the window
* size (wnd_).
*
* in real TCP, a user process performing a read (via PRU_RCVD)
* calls tcp_output each time to (possibly) send a window
* update. Here we don't have a user process, so we simulate
* a user process always ready to consume all the receive buffer
*
* Notes:
* wnd_, wnd_init_, cwnd_, ssthresh_ are in segment units
* sequence and ack numbers are in byte units
*
* Futures:
* there are different existing TCPs with respect to how
* ack's are handled on connection startup. Some delay
* the ack for the first segment, which can cause connections
* to take longer to start up than if we be sure to ack it quickly.
*
* SACK
*
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (LBL)";
#endif
#include "ip.h"
#include "tcp-full.h"
#include "flags.h"
#include "random.h"
#include "template.h"
#define TRUE 1
#define FALSE 0
#define MIN(x,y) (((x)<(y))?(x):(y))
#define MAX(x,y) (((x)>(y))?(x):(y))
static class FullTcpClass : public TclClass {
public:
FullTcpClass() : TclClass("Agent/TCP/FullTcp") {}
TclObject* create(int, const char*const*) {
return (new FullTcpAgent());
}
} class_full;
static class TahoeFullTcpClass : public TclClass {
public:
TahoeFullTcpClass() : TclClass("Agent/TCP/FullTcp/Tahoe") {}
TclObject* create(int, const char*const*) {
// tcl lib code
// sets reno_fastrecov_ to false
return (new TahoeFullTcpAgent());
}
} class_tahoe_full;
static class NewRenoFullTcpClass : public TclClass {
public:
NewRenoFullTcpClass() : TclClass("Agent/TCP/FullTcp/Newreno") {}
TclObject* create(int, const char*const*) {
// tcl lib code
// sets deflate_on_pack_ to false
return (new NewRenoFullTcpAgent());
}
} class_newreno_full;
static class SackFullTcpClass : public TclClass {
public:
SackFullTcpClass() : TclClass("Agent/TCP/FullTcp/Sack") {}
TclObject* create(int, const char*const*) {
return (new SackFullTcpAgent());
}
} class_sack_full;
/*
* Tcl bound variables:
* segsperack: for delayed ACKs, how many to wait before ACKing
* segsize: segment size to use when sending
*/
FullTcpAgent::FullTcpAgent() :
closed_(0), pipe_(0), fastrecov_(FALSE),
last_send_time_(-1.0), infinite_send_(0), irs_(-1),
delack_timer_(this), flags_(0),
state_(TCPS_CLOSED), ect_(FALSE), recent_ce_(FALSE),
last_state_(TCPS_CLOSED), rq_(rcv_nxt_), last_ack_sent_(-1)
{
}
void
FullTcpAgent::delay_bind_init_all()
{
delay_bind_init_one("segsperack_");
delay_bind_init_one("segsize_");
delay_bind_init_one("tcprexmtthresh_");
delay_bind_init_one("iss_");
delay_bind_init_one("nodelay_");
delay_bind_init_one("data_on_syn_");
delay_bind_init_one("dupseg_fix_");
delay_bind_init_one("dupack_reset_");
delay_bind_init_one("close_on_empty_");
delay_bind_init_one("interval_");
delay_bind_init_one("ts_option_size_");
delay_bind_init_one("reno_fastrecov_");
delay_bind_init_one("pipectrl_");
delay_bind_init_one("open_cwnd_on_pack_");
delay_bind_init_one("halfclose_");
TcpAgent::delay_bind_init_all();
reset();
}
int
FullTcpAgent::delay_bind_dispatch(const char *varName, const char *localName, TclObject *tracer)
{
if (delay_bind(varName, localName, "segsperack_", &segs_per_ack_, tracer)) return TCL_OK;
if (delay_bind(varName, localName, "segsize_", &maxseg_, tracer)) return TCL_OK;
if (delay_bind(varName, localName, "tcprexmtthresh_", &tcprexmtthresh_, tracer)) return TCL_OK;
if (delay_bind(varName, localName, "iss_", &iss_, tracer)) return TCL_OK;
if (delay_bind_bool(varName, localName, "nodelay_", &nodelay_, tracer)) return TCL_OK;
if (delay_bind_bool(varName, localName, "data_on_syn_", &data_on_syn_, tracer)) return TCL_OK;
if (delay_bind_bool(varName, localName, "dupseg_fix_", &dupseg_fix_, tracer)) return TCL_OK;
if (delay_bind_bool(varName, localName, "dupack_reset_", &dupack_reset_, tracer)) return TCL_OK;
if (delay_bind_bool(varName, localName, "close_on_empty_", &close_on_empty_, tracer)) return TCL_OK;
if (delay_bind_time(varName, localName, "interval_", &delack_interval_, tracer)) return TCL_OK;
if (delay_bind(varName, localName, "ts_option_size_", &ts_option_size_, tracer)) return TCL_OK;
if (delay_bind_bool(varName, localName, "reno_fastrecov_", &reno_fastrecov_, tracer)) return TCL_OK;
if (delay_bind_bool(varName, localName, "pipectrl_", &pipectrl_, tracer)) return TCL_OK;
if (delay_bind_bool(varName, localName, "open_cwnd_on_pack_", &open_cwnd_on_pack_, tracer)) return TCL_OK;
if (delay_bind_bool(varName, localName, "halfclose_", &halfclose_, tracer)) return TCL_OK;
return TcpAgent::delay_bind_dispatch(varName, localName, tracer);
}
/*
* reset to starting point, don't set state_ here,
* because our starting point might be LISTEN rather
* than CLOSED if we're a passive opener
*/
void
FullTcpAgent::reset()
{
cancel_timers(); // cancel timers first
TcpAgent::reset(); // resets most variables
rq_.clear();
rtt_init();
last_ack_sent_ = -1;
rcv_nxt_ = -1;
pipe_ = 0;
flags_ = 0;
t_seqno_ = iss_;
maxseq_ = -1;
irs_ = -1;
last_send_time_ = -1.0;
if (ts_option_)
recent_ = recent_age_ = 0.0;
else
recent_ = recent_age_ = -1.0;
fastrecov_ = FALSE;
}
/*
* pack() -- is the ACK a partial ACK (not past recover_)
*/
int
FullTcpAgent::pack(Packet *pkt)
{
hdr_tcp *tcph = (hdr_tcp*)pkt->access(off_tcp_);
return (tcph->ackno() >= highest_ack_ &&
tcph->ackno() < recover_);
}
/*
* baseline reno TCP exist fast recovery on a partial ACK
*/
void
FullTcpAgent::pack_action(Packet*)
{
if (reno_fastrecov_ && fastrecov_ && cwnd_ > ssthresh_) {
cwnd_ = ssthresh_; // retract window if inflated
}
fastrecov_ = FALSE;
dupacks_ = 0;
}
/*
* ack_action
*/
void
FullTcpAgent::ack_action(Packet* p)
{
FullTcpAgent::pack_action(p);
}
/*
* headersize:
* how big is an IP+TCP header in bytes; include options such as ts
*/
int
FullTcpAgent::headersize()
{
int total = tcpip_base_hdr_size_;
if (total < 1) {
fprintf(stderr,
"TCP-FULL(%s): warning: tcpip hdr size is only %d bytes\n",
name(), tcpip_base_hdr_size_);
}
if (ts_option_)
total += ts_option_size_;
return (total);
}
/*
* free resources: any pending timers + reassembly queue
*/
FullTcpAgent::~FullTcpAgent()
{
cancel_timers(); // cancel all pending timers
rq_.clear(); // free mem in reassembly queue
}
/*
* the 'advance' interface to the regular tcp is in packet
* units. Here we scale this to bytes for full tcp.
*
* 'advance' is normally called by an "application" (i.e. data source)
* to signal that there is something to send
*
* 'curseq_' is the sequence number of the last byte provided
* by the application. In the case where no data has been supplied
* by the application, curseq_ is the iss_.
*/
void
FullTcpAgent::advanceby(int np)
{
// XXX hack:
// because np is in packets and a data source
// may pass a *huge* number as a way to tell us
// to go forever, just look for the huge number
// and if it's there, pre-divide it
if (np >= 0x10000000)
np /= maxseg_;
advance_bytes(np * maxseg_);
return;
}
/*
* the byte-oriented interface: advance_bytes(int nbytes)
*/
void
FullTcpAgent::advance_bytes(int nb)
{
//
// state-specific operations:
// if CLOSED, reset and try a new active open/connect
// if ESTABLISHED, just try to send more
// if above ESTABLISHED, we are closing, so don't allow
// if anything else (establishing), do nothing here
//
if (state_ > TCPS_ESTABLISHED) {
fprintf(stderr,
"%f: FullTcpAgent::advance(%s): cannot advance while in state %d\n",
now(), name(), state_);
return;
} else if (state_ == TCPS_CLOSED) {
reset();
curseq_ = iss_ + nb;
connect(); // initiate new connection
} else if (state_ == TCPS_ESTABLISHED) {
closed_ = 0;
if (curseq_ < iss_)
curseq_ = iss_;
curseq_ += nb;
send_much(0, REASON_NORMAL, maxburst_);
}
return;
}
/*
* If MSG_EOF is set, by setting close_on_empty_ to TRUE, we ensure that
* a FIN will be sent when the send buffer emptys.
*
* When (in the future) FullTcpAgent implements T/TCP, avoidance of 3-way
* handshake can be handled in this function.
*/
void FullTcpAgent::sendmsg(int nbytes, const char *flags)
{
if (flags && strcmp(flags, "MSG_EOF") == 0)
close_on_empty_ = TRUE;
if (nbytes == -1) {
infinite_send_ = TRUE;
advance_bytes(0);
} else
advance_bytes(nbytes);
}
/*
* flags that are completely dependent on the tcp state
* (in real TCP, see tcp_fsm.h, the "tcp_outflags" array)
*/
int FullTcpAgent::outflags()
{
int flags = 0;
if ((state_ != TCPS_LISTEN) && (state_ != TCPS_SYN_SENT))
flags |= TH_ACK;
if ((state_ == TCPS_SYN_SENT) || (state_ == TCPS_SYN_RECEIVED))
flags |= TH_SYN;
if ((state_ == TCPS_FIN_WAIT_1) || (state_ == TCPS_LAST_ACK))
flags |= TH_FIN;
return (flags);
}
/*
* utility function to set rcv_next_ during inital exchange of seq #s
*/
int FullTcpAgent::rcvseqinit(int seq, int dlen)
{
return (seq + dlen + 1);
}
int
FullTcpAgent::build_options(hdr_tcp* tcph)
{
int total = 0;
if (ts_option_) {
tcph->ts() = now();
tcph->ts_echo() = recent_;
total += ts_option_size_;
} else {
tcph->ts() = tcph->ts_echo() = -1.0;
}
return (total);
}
/*
* sendpacket:
* allocate a packet, fill in header fields, and send
* also keeps stats on # of data pkts, acks, re-xmits, etc
*
* fill in packet fields. Agent::allocpkt() fills
* in most of the network layer fields for us.
* So fill in tcp hdr and adjust the packet size.
*
* Also, return the size of the tcp header.
*/
void
FullTcpAgent::sendpacket(int seqno, int ackno, int pflags, int datalen, int reason)
{
Packet* p = allocpkt();
hdr_tcp *tcph = (hdr_tcp*)p->access(off_tcp_);
/* build basic header w/options */
tcph->seqno() = seqno;
tcph->ackno() = ackno;
tcph->flags() = pflags;
tcph->reason() |= reason; // make tcph->reason look like ns1 pkt->flags?
tcph->sa_length() = 0; // may be increased by build_options()
tcph->hlen() = tcpip_base_hdr_size_;
tcph->hlen() += build_options(tcph);
/* ECT, ECN, and congestion action */
hdr_flags *fh = (hdr_flags *)p->access(off_flags_);
fh->ect() = ect_; // on after mutual agreement on ECT
if (ecn_ && !ect_) // initializing; ect = 0, ecnecho = 1
fh->ecnecho() = 1;
else {
fh->ecnecho() = recent_ce_;
}
fh->cong_action() = cong_action_;
/* actual size is data length plus header length */
hdr_cmn *ch = (hdr_cmn*)p->access(off_cmn_);
ch->size() = datalen + tcph->hlen();
if (datalen <= 0)
++nackpack_;
else {
++ndatapack_;
ndatabytes_ += datalen;
last_send_time_ = now(); // time of last data
}
if (reason == REASON_TIMEOUT || reason == REASON_DUPACK) {
++nrexmitpack_;
nrexmitbytes_ += datalen;
}
last_ack_sent_ = ackno;
send(p, 0);
}
void FullTcpAgent::cancel_timers()
{
TcpAgent::cancel_timers();
delack_timer_.force_cancel();
}
/*
* see if we should send a segment, and if so, send it
* (may be ACK or data)
*
* simulator var, desc (name in real TCP)
* --------------------------------------
* maxseq_, largest seq# we've sent plus one (snd_max)
* flags_, flags regarding our internal state (t_state)
* pflags, a local used to build up the tcp header flags (flags)
* curseq_, is the highest sequence number given to us by "application"
* highest_ack_, the highest ACK we've seen for our data (snd_una-1)
* seqno, the next seq# we're going to send (snd_nxt), this will
* update t_seqno_ (the last thing we sent)
*/
void FullTcpAgent::output(int seqno, int reason)
{
int is_retransmit = (seqno < maxseq_);
int quiet = (highest_ack_ == maxseq_);
int pflags = outflags();
int syn = (seqno == iss_);
int emptying_buffer = FALSE;
int buffered_bytes = (infinite_send_) ? TCP_MAXSEQ :
curseq_ - highest_ack_ + 1;
int win = window() * maxseg_; // window (in bytes)
int off = seqno - highest_ack_; // offset of seg in window
int datalen;
// be careful if we have not received any ACK yet
if (highest_ack_ < 0) {
if (!infinite_send_)
buffered_bytes = curseq_ - iss_;;
off = seqno - iss_;
}
if (syn && !data_on_syn_)
datalen = 0;
else if (pipectrl_)
datalen = buffered_bytes - off;
else
datalen = min(buffered_bytes, win) - off;
//
// in real TCP datalen (len) could be < 0 if there was window
// shrinkage, or if a FIN has been sent and neither ACKd nor
// retransmitted. Only this 2nd case concerns us here...
//
if (datalen < 0) {
datalen = 0;
} else if (datalen > maxseg_) {
datalen = maxseg_;
}
//
// this is an option that causes us to slow-start if we've
// been idle for a "long" time, where long means a rto or longer
// the slow-start is a sort that does not set ssthresh
//
if (slow_start_restart_ && quiet && datalen > 0) {
if (idle_restart()) {
slowdown(CLOSE_CWND_INIT);
}
}
//
// see if sending this packet will empty the send buffer
// a dataless SYN packet counts also
//
if (!infinite_send_ && ((seqno + datalen) > curseq_ ||
(syn && datalen == 0))) {
emptying_buffer = TRUE;
//
// if not a retransmission, notify application that
// everything has been sent out at least once.
//
if (!syn) {
idle();
if (close_on_empty_ && quiet) {
flags_ |= TF_NEEDCLOSE;
}
}
pflags |= TH_PUSH;
//
// if close_on_empty set, we are finished
// with this connection; close it
//
} else {
/* not emptying buffer, so can't be FIN */
pflags &= ~TH_FIN;
}
if (infinite_send_ && (syn && datalen == 0))
pflags |= TH_PUSH; // set PUSH for dataless SYN
/* sender SWS avoidance (Nagle) */
if (datalen > 0) {
// if full-sized segment, ok
if (datalen == maxseg_)
goto send;
// if Nagle disabled and buffer clearing, ok
if ((quiet || nodelay_) && emptying_buffer)
goto send;
// if a retransmission
if (is_retransmit)
goto send;
// if big "enough", ok...
// (this is not a likely case, and would
// only happen for tiny windows)
if (datalen >= ((wnd_ * maxseg_) / 2.0))
goto send;
}
if (need_send())
goto send;
/*
* send now if a control packet or we owe peer an ACK
* TF_ACKNOW can be set during connection establishment and
* to generate acks for out-of-order data
*/
if ((flags_ & (TF_ACKNOW|TF_NEEDCLOSE)) ||
(pflags & (TH_SYN|TH_FIN))) {
goto send;
}
/*
* No reason to send a segment, just return.
*/
return;
send:
syn = (pflags & TH_SYN) ? 1 : 0;
int fin = (pflags & TH_FIN) ? 1 : 0;
sendpacket(seqno, rcv_nxt_, pflags, datalen, reason);
/*
* Data sent (as far as we can tell).
* Any pending ACK has now been sent.
*/
flags_ &= ~(TF_ACKNOW|TF_DELACK);
/*
* if we have reacted to congestion recently, the
* slowdown() procedure will have set cong_action_ and
* sendpacket will have copied that to the outgoing pkt
* CACT field. If that packet contains data, then
* it will be reliably delivered, so we are free to turn off the
* cong_action_ state now If only a pure ACK, we keep the state
* around until we actually send a segment
*/
int reliable = datalen + syn + fin; // seq #'s reliably sent
if (cong_action_ && reliable > 0)
cong_action_ = FALSE;
/*
* SYNs and FINs each use up one sequence number, but
* there's no reason to advance t_seqno_ by one for a FIN
*/
if (!fin && seqno == t_seqno_) {
t_seqno_ += reliable;
}
// highest: greatest sequence number sent + 1
// and adjusted for SYNs and FINs which use up one number
int highest = seqno + reliable;
if (highest > maxseq_) {
maxseq_ = highest;
//
// if we are using conventional RTT estimation,
// establish timing on this segment
//
if (!ts_option_ && rtt_active_ == FALSE) {
rtt_active_ = TRUE; // set timer
rtt_seq_ = seqno; // timed seq #
rtt_ts_ = now(); // when set
}
}
//if (fin)
//printf("%f %s: sent FIN, seq: %d, maxseq now: %d\n",
//now(), name(), seqno, int(maxseq_));
/*
* Set retransmit timer if not currently set,
* and not doing an ack or a keep-alive probe.
* Initial value for retransmit timer is smoothed
* round-trip time + 2 * round-trip time variance.
* Future values are rtt + 4 * rttvar.
*/
if (rtx_timer_.status() != TIMER_PENDING && reliable) {
set_rtx_timer(); // no timer pending, schedule one
}
if (flags_ & TF_NEEDCLOSE) {
flags_ &= ~TF_NEEDCLOSE;
if (state_ <= TCPS_ESTABLISHED && state_ != TCPS_CLOSED)
usrclosed();
}
return;
}
/*
* Try to send as much data as the window will allow. The link layer will
* do the buffering; we ask the application layer for the size of the packets.
*/
void FullTcpAgent::send_much(int force, int reason, int maxburst)
{
/*
* highest_ack is essentially "snd_una" in real TCP
*
* loop while we are in-window (seqno <= (highest_ack + win))
* and there is something to send (t_seqno_ < curseq_+iss_)
*/
int win = window() * maxseg_; // window() in pkts
int npackets = 0;
int topwin = curseq_; // 1 seq number past the last byte we can send
if ((topwin > highest_ack_ + win) || infinite_send_)
topwin = highest_ack_ + win;
if (!force && (delsnd_timer_.status() == TIMER_PENDING))
return;
/*
* note that if output() doesn't actually send anything, we can
* loop forever here
*/
while (force ||
(pipectrl_ ? (pipe_ < window()) : (t_seqno_ < topwin))) {
if (overhead_ != 0 &&
(delsnd_timer_.status() != TIMER_PENDING)) {
delsnd_timer_.resched(Random::uniform(overhead_));
return;
}
/*
* note that if output decides to not actually send
* (e.g. because of Nagle), then if we don't break out
* of this loop, we can loop forever at the same
* simulated time instant
*/
int save = t_seqno_; // scrawl away current t_seqno
output(t_seqno_, reason); // updates t_seqno for us
if (t_seqno_ == save) { // progress?
break;
}
++pipe_;
force = 0;
if ((outflags() & (TH_SYN|TH_FIN)) ||
(maxburst && ++npackets >= maxburst))
break;
}
return;
}
/*
* This function is invoked when the connection is done. It in turn
* invokes the Tcl finish procedure that was registered with TCP.
* This function mimics tcp_close()
*/
void FullTcpAgent::finish()
{
if (closed_)
return;
closed_ = 1;
rq_.clear();
cancel_timers();
Tcl::instance().evalf("%s done", this->name());
}
/*
* Process an ACK
* this version of the routine doesn't necessarily
* require the ack to be one which advances the ack number
*
* if this ACKs a rtt estimate
* indicate we are not timing
* reset the exponential timer backoff (gamma)
* update rtt estimate
* cancel retrans timer if everything is sent and ACK'd, else set it
* advance the ack number if appropriate
* update segment to send next if appropriate
*/
void FullTcpAgent::newack(Packet* pkt)
{
hdr_tcp *tcph = (hdr_tcp*)pkt->access(off_tcp_);
register int ackno = tcph->ackno();
int progress = (ackno > highest_ack_);
//if (state_ > TCPS_ESTABLISHED)
//printf("%f %s: newack: %d, state %d, maxseq:%d\n", now(), name(),
//ackno, int(state_), int(maxseq_));
if (ackno == maxseq_) {
cancel_rtx_timer(); // all data ACKd
} else if (progress) {
set_rtx_timer();
}
// advance the ack number if this is for new data
if (progress)
highest_ack_ = ackno;
// if we have suffered a retransmit timeout, t_seqno_
// will have been reset to highest_ ack. If the
// receiver has cached some data above t_seqno_, the
// new-ack value could (should) jump forward. We must
// update t_seqno_ here, otherwise we would be doing
// go-back-n.
if (t_seqno_ < highest_ack_)
t_seqno_ = highest_ack_; // seq# to send next
/*
* Update RTT only if it's OK to do so from info in the flags header.
* This is needed for protocols in which intermediate agents
* in the network intersperse acks (e.g., ack-reconstructors) for
* various reasons (without violating e2e semantics).
*/
hdr_flags *fh = (hdr_flags *)pkt->access(off_flags_);
if (!fh->no_ts_) {
if (ts_option_) {
recent_age_ = now();
recent_ = tcph->ts();
rtt_update(now() - tcph->ts_echo());
} else if (rtt_active_ && ackno > rtt_seq_) {
// got an RTT sample, record it
t_backoff_ = 1;
rtt_active_ = FALSE;
rtt_update(now() - rtt_ts_);
}
}
return;
}
/*
* this is the simulated form of the header prediction
* predicate. While not really necessary for a simulation, it
* follows the code base more closely and can sometimes help to reveal
* odd behavior caused by the implementation structure..
*
* Here's the comment from the real TCP:
*
* Header prediction: check for the two common cases
* of a uni-directional data xfer. If the packet has
* no control flags, is in-sequence, the window didn't
* change and we're not retransmitting, it's a
* candidate. If the length is zero and the ack moved
* forward, we're the sender side of the xfer. Just
* free the data acked & wake any higher level process
* that was blocked waiting for space. If the length
* is non-zero and the ack didn't move, we're the
* receiver side. If we're getting packets in-order
* (the reassembly queue is empty), add the data to
* the socket buffer and note that we need a delayed ack.
* Make sure that the hidden state-flags are also off.
* Since we check for TCPS_ESTABLISHED above, it can only
* be TF_NEEDSYN.
*/
int
FullTcpAgent::predict_ok(Packet* pkt)
{
hdr_tcp *tcph = (hdr_tcp*)pkt->access(off_tcp_);
hdr_flags *fh = (hdr_flags *)pkt->access(off_flags_);
/* not the fastest way to do this, but perhaps clearest */
int p1 = (state_ == TCPS_ESTABLISHED); // ready
int p2 = ((tcph->flags() & (TH_SYN|TH_FIN|TH_ACK)) == TH_ACK); // ACK
int p3 = ((flags_ & TF_NEEDFIN) == 0); // don't need fin
int p4 = (!ts_option_ || fh->no_ts_ || (tcph->ts() >= recent_)); // tsok
int p5 = (tcph->seqno() == rcv_nxt_); // in-order data
int p6 = (t_seqno_ == maxseq_); // not re-xmit
int p7 = (!ecn_ || fh->ecnecho() == 0); // no ECN
return (p1 && p2 && p3 && p4 && p5 && p6 && p7);
}
/*
* fast_retransmit using the given seqno
* perform a fast retransmit
* kludge t_seqno_ (snd_nxt) so we do the
* retransmit then continue from where we were
* Also, stash our current location in recover_
*/
void FullTcpAgent::fast_retransmit(int seq)
{
int onxt = t_seqno_; // output() changes t_seqno_
recover_ = maxseq_; // keep a copy of highest sent
last_cwnd_action_ = CWND_ACTION_DUPACK;
output(seq, REASON_DUPACK); // send one pkt
t_seqno_ = onxt;
}
/*
* real tcp determines if the remote
* side should receive a window update/ACK from us, and often
* results in sending an update every 2 segments, thereby
* giving the familiar 2-packets-per-ack behavior of TCP.
* Here, we don't advertise any windows, so we just see if
* there's at least 'segs_per_ack_' pkts not yet acked
*/
int FullTcpAgent::need_send()
{
if (flags_ & TF_ACKNOW)
return TRUE;
return ((rcv_nxt_ - last_ack_sent_) >= (segs_per_ack_ * maxseg_));
}
/*
* determine whether enough time has elapsed in order to
* conclude a "restart" is necessary (e.g. a slow-start)
*
* for now, keep track of this similarly to how rtt_update() does
*/
int
FullTcpAgent::idle_restart()
{
if (last_send_time_ < 0.0) {
// last_send_time_ isn't set up yet, we shouldn't
// do the idle_restart
return (0);
}
double tao = now() - last_send_time_;
if (!ts_option_) {
double tickoff = fmod(last_send_time_ + boot_time_,
tcp_tick_);
tao = int((tao + tickoff) / tcp_tick_) * tcp_tick_;
}
return (tao > t_rtxcur_); // verify this CHECKME
}
/*
* tcp-full's version of set_initial_window()... over-rides
* the one in tcp.cc
*/
void
FullTcpAgent::set_initial_window() {
if (delay_growth_)
cwnd_ = wnd_init_;
else
cwnd_ = initial_window();
}
/*
* main reception path -
* called from the agent that handles the data path below in its muxing mode
* advance() is called when connection is established with size sent from
* user/application agent
*/
void FullTcpAgent::recv(Packet *pkt, Handler*)
{
hdr_tcp *tcph = (hdr_tcp*)pkt->access(off_tcp_);
hdr_cmn *th = (hdr_cmn*)pkt->access(off_cmn_);
hdr_flags *fh = (hdr_flags *)pkt->access(off_flags_);
int needoutput = FALSE;
int ourfinisacked = FALSE;
int dupseg = FALSE;
int todrop = 0;
last_state_ = state_;
int datalen = th->size() - tcph->hlen(); // # payload bytes
int ackno = tcph->ackno(); // ack # from packet
int tiflags = tcph->flags() ; // tcp flags from packet
if (state_ == TCPS_CLOSED)
goto drop;
/*
* Process options if not in LISTEN state,
* else do it below
*/
if (state_ != TCPS_LISTEN)
dooptions(pkt);
//
// if we are using delayed-ACK timers and
// no delayed-ACK timer is set, set one.
// They are set to fire every 'interval_' secs, starting
// at time t0 = (0.0 + k * interval_) for some k such
// that t0 > now
//
if (delack_interval_ > 0.0 &&
(delack_timer_.status() != TIMER_PENDING)) {
int last = int(now() / delack_interval_);
delack_timer_.resched(delack_interval_ * (last + 1.0) - now());
}
//
// sanity check for ECN: shouldn't be seeing a CE bit if
// ECT wasn't set on the packet first. If we see this, we
// probably have a misbehaving router...
//
if (fh->ce() && !fh->ect()) {
fprintf(stderr,
"%f: FullTcpAgent::recv(%s): warning: CE bit on, but ECT false!\n",
now(), name());
}
if (predict_ok(pkt)) {
/*
* If last ACK falls within this segment's sequence numbers,
* record the timestamp.
* See RFC1323 (now RFC1323 bis)
*/
if (ts_option_ && !fh->no_ts_ &&
tcph->seqno() <= last_ack_sent_) {
/*
* this is the case where the ts value is newer than
* the last one we've seen, and the seq # is the one
* we expect [seqno == last_ack_sent_] or older
*/
recent_age_ = now();
recent_ = tcph->ts();
}
//
// generate a stream of ecnecho bits until we see a true
// cong_action bit
//
if (ecn_) {
if (fh->ce() && fh->ect())
recent_ce_ = TRUE;
else if (fh->cong_action())
recent_ce_ = FALSE;
}
if (datalen == 0) {
// check for a received pure ACK in the correct range..
// also checks to see if we are wnd_ limited
// (we don't change cwnd at all below), plus
// not being in fast recovery and not a partial ack.
// If we are in fast
// recovery, go below so we can remember to deflate
// the window if we need to
if (ackno > highest_ack_ && ackno < maxseq_ &&
cwnd_ >= wnd_ && !fastrecov_) {
newack(pkt); // update timers, highest_ack_
/* no adjustment of cwnd here */
if (curseq_ >= highest_ack_ || infinite_send_)
send_much(0, REASON_NORMAL, maxburst_);
Packet::free(pkt);
return;
}
} else if (ackno == highest_ack_ && rq_.empty()) {
// check for pure incoming segment
// the next data segment we're awaiting, and
// that there's nothing sitting in the reassem-
// bly queue
// give to "application" here
// note: DELACK is inspected only by
// tcp_fasttimo() in real tcp. Every 200 ms
// this routine scans all tcpcb's looking for
// DELACK segments and when it finds them
// changes DELACK to ACKNOW and calls tcp_output()
rcv_nxt_ += datalen;
flags_ |= TF_DELACK;
recvBytes(datalen); // notify application of "delivery"
//
// special code here to simulate the operation
// of a receiver who always consumes data,
// resulting in a call to tcp_output
Packet::free(pkt);
if (need_send())
send_much(1, REASON_NORMAL, maxburst_);
return;
}
}
// header predication failed (or pure ACK out of valid range)...
// do slow path processing
switch (state_) {
/*
* If the segment contains an ACK then it is bad and do reset.
* If it does not contain a SYN then it is not interesting; drop it.
* Otherwise initialize tp->rcv_nxt, and tp->irs, iss is already
* selected, and send a segment:
*
* Initialize tp->snd_nxt to tp->iss.
* Enter SYN_RECEIVED state, and process any other fields of this
* segment in this state.
*/
case TCPS_LISTEN: /* awaiting peer's SYN */
if (tiflags & TH_ACK) {
goto dropwithreset;
}
if ((tiflags & TH_SYN) == 0) {
goto drop;
}
/*
* must by a SYN (no ACK) at this point...
* in real tcp we would bump the iss counter here also
*/
dooptions(pkt);
irs_ = tcph->seqno();
t_seqno_ = iss_; /* tcp_sendseqinit() macro in real tcp */
rcv_nxt_ = rcvseqinit(irs_, datalen);
flags_ |= TF_ACKNOW;
if (ecn_ && fh->ecnecho()) {
ect_ = TRUE;
}
newstate(TCPS_SYN_RECEIVED);
goto trimthenstep6;
/*
* If the state is SYN_SENT:
* if seg contains an ACK, but not for our SYN, drop the input.
* if seg does not contain SYN, then drop it.
* Otherwise this is an acceptable SYN segment
* initialize tp->rcv_nxt and tp->irs
* if seg contains ack then advance tp->snd_una
* if SYN has been acked change to ESTABLISHED else SYN_RCVD state
* arrange for segment to be acked (eventually)
* continue processing rest of data/controls, beginning with URG
*/
case TCPS_SYN_SENT: /* we sent SYN, expecting SYN+ACK (or SYN) */
/* drop if it's a SYN+ACK and the ack field is bad */
if ((tiflags & TH_ACK) &&
((ackno <= iss_) || (ackno > maxseq_))) {
// not an ACK for our SYN, discard
fprintf(stderr,
"%f: FullTcpAgent::recv(%s): bad ACK (%d) for our SYN(%d)\n",
now(), name(), int(ackno), int(maxseq_));
goto dropwithreset;
}
if ((tiflags & TH_SYN) == 0) {
fprintf(stderr,
"%f: FullTcpAgent::recv(%s): no SYN for our SYN(%d)\n",
now(), name(), int(maxseq_));
goto drop;
}
/* looks like an ok SYN or SYN+ACK */
#ifdef notdef
cancel_rtx_timer(); // cancel timer on our 1st SYN [does this belong!?]
#endif
irs_ = tcph->seqno(); // get initial recv'd seq #
rcv_nxt_ = rcvseqinit(irs_, datalen);
/*
* we are seeing either a SYN or SYN+ACK. For pure SYN,
* ecnecho tells us our peer is ecn-capable. For SYN+ACK,
* it's acking our SYN, so it already knows we're ecn capable,
* so it can just turn on ect
*/
if (ecn_ && (fh->ecnecho() || fh->ect()))
ect_ = TRUE;
if (tiflags & TH_ACK) {
// SYN+ACK (our SYN was acked)
// CHECKME
highest_ack_ = ackno;
cwnd_ = initial_window();
#ifdef notdef
/*
* if we didn't have to retransmit the SYN,
* use its rtt as our initial srtt & rtt var.
*/
if (t_rtt_) {
double tao = now() - tcph->ts();
rtt_update(tao);
}
#endif
/*
* if there's data, delay ACK; if there's also a FIN
* ACKNOW will be turned on later.
*/
if (datalen > 0) {
flags_ |= TF_DELACK; // data there: wait
} else {
flags_ |= TF_ACKNOW; // ACK peer's SYN
}
/*
* Received in SYN_SENT[*] state.
* Transitions:
* SYN_SENT --> ESTABLISHED
* SYN_SENT* --> FIN_WAIT_1
*/
if (flags_ & TF_NEEDFIN) {
newstate(TCPS_FIN_WAIT_1);
flags_ &= ~TF_NEEDFIN;
tiflags &= ~TH_SYN;
} else {
newstate(TCPS_ESTABLISHED);
}
// special to ns:
// generate pure ACK here.
// this simulates the ordinary connection establishment
// where the ACK of the peer's SYN+ACK contains
// no data. This is typically caused by the way
// the connect() socket call works in which the
// entire 3-way handshake occurs prior to the app
// being able to issue a write() [which actually
// causes the segment to be sent].
sendpacket(t_seqno_, rcv_nxt_, TH_ACK, 0, 0);
} else {
// SYN (no ACK) (simultaneous active opens)
flags_ |= TF_ACKNOW;
cancel_rtx_timer();
newstate(TCPS_SYN_RECEIVED);
/*
* decrement t_seqno_: we are sending a
* 2nd SYN (this time in the form of a
* SYN+ACK, so t_seqno_ will have been
* advanced to 2... reduce this
*/
t_seqno_--; // CHECKME
}
trimthenstep6:
/*
* advance the seq# to correspond to first data byte
*/
tcph->seqno()++;
if (tiflags & TH_ACK)
goto process_ACK;
goto step6;
case TCPS_LAST_ACK:
case TCPS_CLOSING:
break;
} /* end switch(state_) */
/*
* States other than LISTEN or SYN_SENT.
* First check timestamp, if present.
* Then check that at least some bytes of segment are within
* receive window. If segment begins before rcv_nxt,
* drop leading data (and SYN); if nothing left, just ack.
*
* RFC 1323 PAWS: If we have a timestamp reply on this segment
* and it's less than ts_recent, drop it.
*/
if (ts_option_ && !fh->no_ts_ && recent_ && tcph->ts() < recent_) {
if ((now() - recent_age_) > TCP_PAWS_IDLE) {
/*
* this is basically impossible in the simulator,
* but here it is...
*/
/*
* Invalidate ts_recent. If this segment updates
* ts_recent, the age will be reset later and ts_recent
* will get a valid value. If it does not, setting
* ts_recent to zero will at least satisfy the
* requirement that zero be placed in the timestamp
* echo reply when ts_recent isn't valid. The
* age isn't reset until we get a valid ts_recent
* because we don't want out-of-order segments to be
* dropped when ts_recent is old.
*/
recent_ = 0.0;
} else {
goto dropafterack;
}
}
// check for redundant data at head/tail of segment
// note that the 4.4bsd [Net/3] code has
// a bug here which can cause us to ignore the
// perfectly good ACKs on duplicate segments. The
// fix is described in (Stevens, Vol2, p. 959-960).
// This code is based on that correction.
//
// In addition, it has a modification so that duplicate segments
// with dup acks don't trigger a fast retransmit when dupseg_fix_
// is enabled.
//
// Yet one more modification: make sure that if the received
// segment had datalen=0 and wasn't a SYN or FIN that
// we don't turn on the ACKNOW status bit. If we were to
// allow ACKNO to be turned on, normal pure ACKs that happen
// to have seq #s below rcv_nxt can trigger an ACK war by
// forcing us to ACK the pure ACKs
//
todrop = rcv_nxt_ - tcph->seqno(); // how much overlap?
if (todrop > 0 && ((tiflags & (TH_SYN|TH_FIN)) || datalen > 0)) {
if (tiflags & TH_SYN) {
tiflags &= ~TH_SYN;
tcph->seqno()++;
th->size()--; // XXX Must decrease packet size too!!
todrop--;
}
//
// see Stevens, vol 2, p. 960 for this check;
// this check is to see if we are dropping
// more than this segment (i.e. the whole pkt + a FIN),
// or just the whole packet (no FIN)
//
if (todrop > datalen ||
(todrop == datalen && (tiflags & TH_FIN) == 0)) {
/*
* Any valid FIN must be to the left of the window.
* At this point the FIN must be a duplicate or out
* of sequence; drop it.
*/
tiflags &= ~TH_FIN;
/*
* Send an ACK to resynchronize and drop any data.
* But keep on processing for RST or ACK.
*/
flags_ |= TF_ACKNOW;
todrop = datalen;
dupseg = TRUE;
}
tcph->seqno() += todrop;
th->size() -= todrop; // XXX Must decrease size too!!
datalen -= todrop;
}
/*
* If last ACK falls within this segment's sequence numbers,
* record the timestamp.
* See RFC1323 (now RFC1323 bis)
*/
if (ts_option_ && !fh->no_ts_ && tcph->seqno() <= last_ack_sent_) {
/*
* this is the case where the ts value is newer than
* the last one we've seen, and the seq # is the one we expect
* [seqno == last_ack_sent_] or older
*/
recent_age_ = now();
recent_ = tcph->ts();
}
if (tiflags & TH_SYN) {
fprintf(stderr,
"%f: FullTcpAgent::recv(%s) received unexpected SYN (state:%d)\n",
now(), name(), state_);
goto dropwithreset;
}
// K: added TH_SYN, but it is already known here!
if ((tiflags & TH_ACK) == 0) {
fprintf(stderr, "%f: FullTcpAgent::recv(%s) got packet lacking ACK (seq %d)\n",
now(), name(), tcph->seqno());
goto drop;
}
/*
* Ack processing.
*/
switch (state_) {
case TCPS_SYN_RECEIVED: /* want ACK for our SYN+ACK */
if (ackno < highest_ack_ || ackno > maxseq_) {
// not in useful range
goto dropwithreset;
}
/*
* Make transitions:
* SYN-RECEIVED -> ESTABLISHED
* SYN-RECEIVED* -> FIN-WAIT-1
*/
if (flags_ & TF_NEEDFIN) {
newstate(TCPS_FIN_WAIT_1);
flags_ &= ~TF_NEEDFIN;
} else {
newstate(TCPS_ESTABLISHED);
}
cwnd_ = initial_window();
/* fall into ... */
/*
* In ESTABLISHED state: drop duplicate ACKs; ACK out of range
* ACKs. If the ack is in the range
* tp->snd_una < ti->ti_ack <= tp->snd_max
* then advance tp->snd_una to ti->ti_ack and drop
* data from the retransmission queue.
*
* note that state TIME_WAIT isn't used
* in the simulator
*/
case TCPS_ESTABLISHED:
case TCPS_FIN_WAIT_1:
case TCPS_FIN_WAIT_2:
case TCPS_CLOSE_WAIT:
case TCPS_CLOSING:
case TCPS_LAST_ACK:
//
// look for ECNs in ACKs, react as necessary
//
if (fh->ecnecho() && (!ecn_ || !ect_)) {
fprintf(stderr,
"%f: FullTcp(%s): warning, recvd ecnecho but I am not ECN capable!\n",
now(), name());
}
//
// generate a stream of ecnecho bits until we see a true
// cong_action bit
//
if (ecn_) {
if (fh->ce() && fh->ect())
recent_ce_ = TRUE;
else if (fh->cong_action())
recent_ce_ = FALSE;
}
// look for dup ACKs (dup ack numbers, no data)
//
// do fast retransmit/recovery if at/past thresh
if (ackno <= highest_ack_) {
// an ACK which doesn't advance highest_ack_
if (datalen == 0 && (!dupseg_fix_ || !dupseg)) {
--pipe_; // ACK indicates pkt cached @ receiver
/*
* If we have outstanding data
* this is a completely
* duplicate ack,
* the ack is the biggest we've
* seen and we've seen exactly our rexmt
* threshhold of them, assume a packet
* has been dropped and retransmit it.
*
* We know we're losing at the current
* window size so do congestion avoidance.
*
* Dup acks mean that packets have left the
* network (they're now cached at the receiver)
* so bump cwnd by the amount in the receiver
* to keep a constant cwnd packets in the
* network.
*/
if ((rtx_timer_.status() != TIMER_PENDING) ||
ackno != highest_ack_) {
// not timed, or re-ordered ACK
dupacks_ = 0;
} else if (++dupacks_ == tcprexmtthresh_) {
fastrecov_ = TRUE;
/* re-sync the pipe_ estimate */
pipe_ = maxseq_ - highest_ack_;
pipe_ /= maxseg_;
pipe_ -= (dupacks_ + 1);
pipe_ = int(cwnd_) - dupacks_ - 1;
dupack_action(); // maybe fast rexmt
goto drop;
} else if (dupacks_ > tcprexmtthresh_) {
if (reno_fastrecov_) {
// we just measure cwnd in
// packets, so don't scale by
// maxseg_ as real
// tcp does
cwnd_++;
}
send_much(0, REASON_NORMAL, maxburst_);
goto drop;
}
} else {
// non zero-length [dataful] segment
// with a dup ack (normal for dataful segs)
// (or window changed in real TCP).
if (dupack_reset_) {
dupacks_ = 0;
fastrecov_ = FALSE;
}
}
break; /* take us to "step6" */
} /* end of dup acks */
/*
* we've finished the fast retransmit/recovery period
* (i.e. received an ACK which advances highest_ack_)
* The ACK may be "good" or "partial"
*/
process_ACK:
if (ackno > maxseq_) {
// ack more than we sent(!?)
fprintf(stderr,
"%f: FullTcpAgent::recv(%s) too-big ACK (ack: %d, maxseq:%d)\n",
now(), name(), int(ackno), int(maxseq_));
goto dropafterack;
}
/*
* If we have a timestamp reply, update smoothed
* round trip time. If no timestamp is present but
* transmit timer is running and timed sequence
* number was acked, update smoothed round trip time.
* Since we now have an rtt measurement, cancel the
* timer backoff (cf., Phil Karn's retransmit alg.).
* Recompute the initial retransmit timer.
*
* If all outstanding data is acked, stop retransmit
* If there is more data to be acked, restart retransmit
* timer, using current (possibly backed-off) value.
*/
newack(pkt); // handle timers, update highest_ack_
--pipe_;
/*
* if this is a partial ACK, invoke whatever we should
*/
int partial = pack(pkt);
if (partial)
pack_action(pkt);
else
ack_action(pkt);
/*
* if this is an ACK with an ECN indication, handle this
*/
if (fh->ecnecho())
ecn(highest_ack_); // updated by newack(), above
// CHECKME: handling of rtx timer
if (ackno == maxseq_) {
needoutput = TRUE;
}
/*
* If no data (only SYN) was ACK'd,
* skip rest of ACK processing.
*/
if (ackno == (highest_ack_ + 1))
goto step6;
// if we are delaying initial cwnd growth (probably due to
// large initial windows), then only open cwnd if data has
// been received
/*
* When new data is acked, open the congestion window.
* If the window gives us less than ssthresh packets
* in flight, open exponentially (maxseg per packet).
* Otherwise open about linearly: maxseg per window
* (maxseg^2 / cwnd per packet).
*/
if ((!delay_growth_ || (rcv_nxt_ > 0)) &&
last_state_ == TCPS_ESTABLISHED) {
if (!partial || open_cwnd_on_pack_)
opencwnd();
}
// K: added state check in equal but diff way
if ((state_ >= TCPS_FIN_WAIT_1) && (ackno == maxseq_)) {
ourfinisacked = TRUE;
} else {
ourfinisacked = FALSE;
}
// additional processing when we're in special states
switch (state_) {
/*
* In FIN_WAIT_1 STATE in addition to the processing
* for the ESTABLISHED state if our FIN is now acknowledged
* then enter FIN_WAIT_2.
*/
case TCPS_FIN_WAIT_1: /* doing active close */
if (ourfinisacked) {
// got the ACK, now await incoming FIN
newstate(TCPS_FIN_WAIT_2);
cancel_timers();
needoutput = FALSE;
}
break;
/*
* In CLOSING STATE in addition to the processing for
* the ESTABLISHED state if the ACK acknowledges our FIN
* then enter the TIME-WAIT state, otherwise ignore
* the segment.
*/
case TCPS_CLOSING: /* simultaneous active close */;
if (ourfinisacked) {
newstate(TCPS_CLOSED);
cancel_timers();
}
break;
/*
* In LAST_ACK, we may still be waiting for data to drain
* and/or to be acked, as well as for the ack of our FIN.
* If our FIN is now acknowledged,
* enter the closed state and return.
*/
case TCPS_LAST_ACK: /* passive close */
// K: added state change here
if (ourfinisacked) {
newstate(TCPS_CLOSED);
#ifdef notdef
cancel_timers(); // DOES THIS BELONG HERE?, probably (see tcp_cose
#endif
finish();
goto drop;
} else {
// should be a FIN we've seen
hdr_ip* iph = (hdr_ip*)pkt->access(off_ip_);
fprintf(stderr,
"%f: %d.%d>%d.%d FullTcpAgent::recv(%s) received non-ACK (state:%d)\n",
now(),
iph->saddr(), iph->sport(),
iph->daddr(), iph->dport(),
name(), state_);
}
break;
/* no case for TIME_WAIT in simulator */
} // inner switch
} // outer switch
step6:
/* real TCP handles window updates and URG data here */
/* dodata: this label is in the "real" code.. here only for reference */
/*
* DATA processing
*/
if ((datalen > 0 || (tiflags & TH_FIN)) &&
TCPS_HAVERCVDFIN(state_) == 0) {
if (tcph->seqno() == rcv_nxt_ && rq_.empty()) {
// see the "TCP_REASS" macro for this code:
// got the in-order packet we were looking
// for, nobody is in the reassembly queue,
// so this is the common case...
// note: in "real" TCP we must also be in
// ESTABLISHED state to come here, because
// data arriving before ESTABLISHED is
// queued in the reassembly queue. Since we
// don't really have a process anyhow, just
// accept the data here as-is (i.e. don't
// require being in ESTABLISHED state)
flags_ |= TF_DELACK;
rcv_nxt_ += datalen;
if (datalen)
recvBytes(datalen); // notify app. of "delivery"
tiflags = tcph->flags() & TH_FIN;
// give to "application" here
needoutput = need_send();
} else {
// see the "tcp_reass" function:
// not the one we want next (or it
// is but there's stuff on the reass queue);
// do whatever we need to do for out-of-order
// segments or hole-fills. Also,
// send an ACK to the other side right now.
// K: some changes here, figure out
int rcv_nxt_old_ = rcv_nxt_; // notify app. if changes
tiflags = rq_.add(pkt);
if (rcv_nxt_ > rcv_nxt_old_)
recvBytes(rcv_nxt_ - rcv_nxt_old_);
if (tiflags & TH_PUSH) {
// K: APPLICATION recv
needoutput = need_send();
} else {
flags_ |= TF_ACKNOW;
}
}
} else {
/*
* we're closing down or this is a pure ACK that
* wasn't handled by the header prediction part above
* (e.g. because cwnd < wnd)
*/
// K: this is deleted
tiflags &= ~TH_FIN;
}
/*
* if FIN is received, ACK the FIN
* (let user know if we could do so)
*/
if (tiflags & TH_FIN) {
if (TCPS_HAVERCVDFIN(state_) == 0) {
flags_ |= TF_ACKNOW;
rcv_nxt_++;
}
switch (state_) {
/*
* In SYN_RECEIVED and ESTABLISHED STATES
* enter the CLOSE_WAIT state.
* (passive close)
*/
case TCPS_SYN_RECEIVED:
case TCPS_ESTABLISHED:
newstate(TCPS_CLOSE_WAIT);
break;
/*
* If still in FIN_WAIT_1 STATE FIN has not been acked so
* enter the CLOSING state.
* (simultaneous close)
*/
case TCPS_FIN_WAIT_1:
newstate(TCPS_CLOSING);
break;
/*
* In FIN_WAIT_2 state enter the TIME_WAIT state,
* starting the time-wait timer, turning off the other
* standard timers.
* (in the simulator, just go to CLOSED)
* (completion of active close)
*/
case TCPS_FIN_WAIT_2:
newstate(TCPS_CLOSED);
cancel_timers();
break;
}
} /* end of if FIN bit on */
if (needoutput || (flags_ & TF_ACKNOW))
send_much(1, REASON_NORMAL, maxburst_);
else if (curseq_ >= highest_ack_ || infinite_send_)
send_much(0, REASON_NORMAL, maxburst_);
// K: which state to return to when nothing left?
if (!halfclose_ && state_ == TCPS_CLOSE_WAIT && highest_ack_ == maxseq_)
usrclosed();
Packet::free(pkt);
// haoboy: Is here the place for done{} of active close?
// It cannot be put in the switch above because we might need to do
// send_much() (an ACK)
Tcl::instance().evalf("%s done", this->name());
return;
dropafterack:
flags_ |= TF_ACKNOW;
send_much(1, REASON_NORMAL, maxburst_);
goto drop;
dropwithreset:
/* we should be sending an RST here, but can't in simulator */
if (tiflags & TH_ACK) {
sendpacket(ackno, 0, 0x0, 0, REASON_NORMAL);
} else {
int ack = tcph->seqno() + datalen;
if (tiflags & TH_SYN)
ack--;
sendpacket(0, ack, TH_ACK, 0, REASON_NORMAL);
}
drop:
Packet::free(pkt);
return;
}
/*
* Dupack-action: what to do on a DUP ACK. After the initial check
* of 'recover' below, this function implements the following truth
* table:
*
* bugfix ecn last-cwnd == ecn action
*
* 0 0 0 full_reno_action
* 0 0 1 full_reno_action [impossible]
* 0 1 0 full_reno_action
* 0 1 1 1/2 window, return
* 1 0 0 nothing
* 1 0 1 nothing [impossible]
* 1 1 0 nothing
* 1 1 1 1/2 window, return
*/
void
FullTcpAgent::dupack_action()
{
int recovered = (highest_ack_ > recover_);
//int recovered = !fastrecov_;
if (recovered || (!bug_fix_ && !ecn_) ||
last_cwnd_action_ == CWND_ACTION_DUPACK) {
goto full_reno_action;
}
if (ecn_ && last_cwnd_action_ == CWND_ACTION_ECN) {
slowdown(CLOSE_CWND_HALF);
cancel_rtx_timer();
rtt_active_ = FALSE;
fast_retransmit(highest_ack_);
return;
}
if (bug_fix_) {
/*
* The line below, for "bug_fix_" true, avoids
* problems with multiple fast retransmits in one
* window of data.
*/
return;
}
full_reno_action:
slowdown(CLOSE_SSTHRESH_HALF|CLOSE_CWND_HALF);
cancel_rtx_timer();
rtt_active_ = FALSE;
fast_retransmit(highest_ack_);
// we measure cwnd in packets,
// so don't scale by maxseg_
// as real TCP does
cwnd_ = ssthresh_ + dupacks_;
return;
}
void
FullTcpAgent::timeout_action()
{
recover_ = maxseq_;
last_cwnd_action_ = CWND_ACTION_TIMEOUT;
slowdown(CLOSE_SSTHRESH_HALF|CLOSE_CWND_RESTART);
reset_rtx_timer(1);
t_seqno_ = (highest_ack_ < 0) ? iss_ : int(highest_ack_);
fastrecov_ = FALSE;
dupacks_ = 0;
}
//
// reset_rtx_timer: called during a retransmission timeout
// to perform exponential backoff. Also, note that because
// we have performed a retransmission, our rtt timer is now
// invalidated (indicate this by setting rtt_active_ false)
//
void FullTcpAgent::reset_rtx_timer(int /* mild */)
{
// cancel old timer, set a new one
rtt_backoff(); // double current timeout
set_rtx_timer(); // set new timer
rtt_active_ = FALSE; // no timing during this window
}
/*
* do an active open
* (in real TCP, see tcp_usrreq, case PRU_CONNECT)
*/
void FullTcpAgent::connect()
{
newstate(TCPS_SYN_SENT); // sending a SYN now
output(iss_, REASON_NORMAL);
return;
}
/*
* be a passive opener
* (in real TCP, see tcp_usrreq, case PRU_LISTEN)
* (for simulation, make this peer's ptype ACKs)
*/
void FullTcpAgent::listen()
{
newstate(TCPS_LISTEN);
type_ = PT_ACK; // instead of PT_TCP
}
/*
* called when user/application performs 'close'
*/
void FullTcpAgent::usrclosed()
{
//curseq_ = t_seqno_ - 1; // truncate buffer
curseq_ = maxseq_ - 1;
infinite_send_ = 0;
switch (state_) {
case TCPS_CLOSED:
case TCPS_LISTEN:
cancel_timers();
newstate(TCPS_CLOSED);
break;
case TCPS_SYN_SENT:
newstate(TCPS_CLOSED);
/* fall through */
case TCPS_LAST_ACK:
flags_ |= TF_NEEDFIN;
send_much(1, REASON_NORMAL, maxburst_);
break;
case TCPS_SYN_RECEIVED:
case TCPS_ESTABLISHED:
newstate(TCPS_FIN_WAIT_1);
flags_ |= TF_NEEDFIN;
send_much(1, REASON_NORMAL, maxburst_);
break;
case TCPS_CLOSE_WAIT:
newstate(TCPS_LAST_ACK);
flags_ |= TF_NEEDFIN;
send_much(1, REASON_NORMAL, maxburst_);
break;
case TCPS_FIN_WAIT_1:
case TCPS_FIN_WAIT_2:
case TCPS_CLOSING:
/* usr asked for a close more than once [?] */
fprintf(stderr,
"%f FullTcpAgent(%s): app close in bad state %d\n",
now(), name(), state_);
break;
default:
fprintf(stderr,
"%f FullTcpAgent(%s): app close in unknown state %d\n",
now(), name(), state_);
}
return;
}
int FullTcpAgent::command(int argc, const char*const* argv)
{
// would like to have some "connect" primitive
// here, but the problem is that we get called before
// the simulation is running and we want to send a SYN.
// Because no routing exists yet, this fails.
// Instead, see code in advance() above.
//
// listen can happen any time because it just changes state_
//
// close is designed to happen at some point after the
// simulation is running (using an ns 'at' command)
if (argc == 2) {
if (strcmp(argv[1], "listen") == 0) {
// just a state transition
listen();
return (TCL_OK);
}
if (strcmp(argv[1], "close") == 0) {
usrclosed();
return (TCL_OK);
}
}
if (argc == 3) {
if (strcmp(argv[1], "advance") == 0) {
advanceby(atoi(argv[2]));
return (TCL_OK);
}
if (strcmp(argv[1], "advanceby") == 0) {
advanceby(atoi(argv[2]));
return (TCL_OK);
}
if (strcmp(argv[1], "advance-bytes") == 0) {
advance_bytes(atoi(argv[2]));
return (TCL_OK);
}
}
return (TcpAgent::command(argc, argv));
}
/*
* constructure for reassembly queue-- give it a ref
* to the containing tcp's rcv_nxt_ field, plus
* allow it to find off_tcp_ and off_cmn_
*/
ReassemblyQueue::ReassemblyQueue(int& nxt) :
head_(NULL), tail_(NULL), ptr_(NULL), rcv_nxt_(nxt)
{
bind("off_tcp_", &off_tcp_);
bind("off_cmn_", &off_cmn_);
}
/*
* clear out reassembly queue
*/
void ReassemblyQueue::clear()
{
seginfo* p= head_;
while(head_) {
p= head_;
head_= head_->next_;
delete(p);
}
ptr_ = head_ = tail_ = NULL;
return;
}
/*
* gensack() -- generate 'maxsblock' sack blocks (start/end seq pairs)
* at specified address
*/
int
ReassemblyQueue::gensack(int *sacks, int maxsblock)
{
seginfo *p, *q;
if (head_ == NULL)
return (0); // nothing there
//
// look for the SACK block containing the
// most recently received segment. Update the timestamps
// to match the most recent of the contig block
//
// Hmmm. Leave max. time (ie. time of most recent segment in
// block) in the time_ field of the last segment in the block
p = head_;
while (p != NULL) {
q = p;
while (p->next_ &&
(p->next_->startseq_ == p->endseq_)) {
// RMM fix
//p->time_ = MAX(p->time_, p->next_->time_);
p->next_->time_ = MAX(p->time_, p->next_->time_);
p = p->next_;
}
// RMM fix
//p->time_ = MAX(q->time_, p->time_);
p = p->next_;
}
//
// look for maxsblock most recent blocks
//
// RMM fix
// Start at tail & work back; common case: most recent n are
// at end of list... Put first n (counting from tail_) on
// return list.
// num_sack_blocks is how many blocks have been found & is returned
int *sacks_base_p = sacks;
register int num_sack_blocks=0;
#ifdef WIN32
// MSVC does not support variable-sized array definition :(
double* sacks_times = new double[maxsblock];
#else
double sacks_times[maxsblock];
#endif
p = q = tail_;
while (p && num_sack_blocks < maxsblock) {
while (p->prev_ &&
(p->prev_->endseq_ == p->startseq_))
{
p = p->prev_;
}
// stash the start & end of the block
sacks[2*num_sack_blocks + 0] = p->startseq_;
sacks[2*num_sack_blocks + 1] = q->endseq_;
// stash the time corresp. to the SACK block
// entered in list
sacks_times[num_sack_blocks] = q->time_;
q = p = p->prev_;
num_sack_blocks++;
}
// p and q point to the end of the first remaining block; one
// should really be storing sacks[] in time order, so as to
// make replacing least recent with more recent easy, but I
// will cop out for now, and simply examine the entire list
// for each element remaining -- maxsblocks defaults to 3 (and
// is, I think, always <5) so this isn't too great a
// penalty... (he hopes)
// replacement policy is: go through sacks[] and replace
// 2-tuple with values from current list element (enumerated
// only once) iff (last PDU) time for SACK block corresp. to
// 2-tuple is >= (more recent than) time for block in list...
while (p) {
// set up prev. SACK block for consideration...
while (p->prev_ && (p->prev_->endseq_ == p->startseq_)) {
p = p->prev_;
}
// now have previous SACK block in [p, q]...
// NB. dodgy ptr. comparison/arith. below?
int *ip, *tip=NULL;
double *dp, *tdp=NULL;
for (ip=sacks_base_p, dp=sacks_times;
iptime_ >= *dp) {
if (!tip && !tdp) {
// first candidate => accept
tip = ip;
tdp = dp;
} else {
// not first candidate => must
// be older than prior
// candidate
if (*dp < *tdp) {
tip = ip;
tdp = dp;
}
}
}
}
if (tdp && tip) {
*tdp = q->time_;
tip[0] = p->startseq_;
tip[1] = q->endseq_;
}
p = q = p->prev_;
}
#ifdef WIN32
delete sacks_times;
#endif
return (num_sack_blocks);
#if 0
double max;
register int i;
seginfo *maxp, *maxq;
for (i = 0; i < maxsblock; ++i) {
p = head_;
max = 0.0;
maxp = maxq = NULL;
// find the max, q->ptr to left of max, p->ptr to right
while (p != NULL) {
q = p;
while (p->next_ &&
(p->next_->startseq_ == p->endseq_)) {
p = p->next_;
}
if ((p->time_ > 0.0) && p->time_ > max) {
maxp = p;
maxq = q;
}
p = p->next_;
}
// if we found a max, scrawl it away
if (maxp != NULL) {
*sacks++ = maxq->startseq_;
*sacks++ = maxp->endseq_;
// invert timestamp on max's
while (maxq != maxp) {
maxq->time_ = -maxq->time_;
maxq = maxq->next_;
}
maxp->time_ = -maxp->time_;
} else
break;
}
// now clean up all negatives
p = head_;
while (p != NULL) {
if (p->time_ < 0.0)
p->time_ = -p->time_;
p = p->next_;
}
return (i);
#endif
}
/*
* nextblk -- keep state, walk through
*/
int
ReassemblyQueue::nextblk(int* sacks)
{
register seginfo* p = ptr_;
if (p != NULL) {
*sacks++ = p->startseq_;
while (p->next_ && (p->next_->startseq_ == p->endseq_)) {
p = p->next_;
}
*sacks = p->endseq_;
} else {
*sacks++ = -1;
*sacks = -1;
return (0);
}
ptr_ = p->next_;
return (1);
}
/*
* dumplist -- print out list (for debugging)
*/
void
ReassemblyQueue::dumplist()
{
register seginfo* p = head_;
if (!head_) {
printf("DUMPLIST: head_ is NULL\n");
}
while (p != NULL) {
if (p == ptr_) {
printf("[->%d, %d<-]",
p->startseq_, p->endseq_);
} else {
printf("[%d, %d]",
p->startseq_, p->endseq_);
}
p = p->next_;
}
printf("\n");
}
/*
* sync the nextblk pointer to the head of the list
*/
void
ReassemblyQueue::sync()
{
ptr_ = head_;
}
/*
* add a packet to the reassembly queue..
* will update FullTcpAgent::rcv_nxt_ by way of the
* ReassemblyQueue::rcv_nxt_ integer reference (an alias)
*/
int ReassemblyQueue::add(Packet* pkt)
{
hdr_tcp *tcph = (hdr_tcp*)pkt->access(off_tcp_);
hdr_cmn *th = (hdr_cmn*)pkt->access(off_cmn_);
int start = tcph->seqno();
int end = start + th->size() - tcph->hlen();
int tiflags = tcph->flags();
return (add(start, end, tiflags));
}
/*
* add start/end seq to reassembly queue
* start specifies starting seq# for segment, end specifies
* last seq# number in the segment plus one
*/
int ReassemblyQueue::add(int start, int end, int tiflags)
{
seginfo *q, *p, *n, *r;
double now = Scheduler::instance().clock();
if (head_ == NULL) {
// nobody there, just insert
tail_ = head_ = new seginfo;
head_->prev_ = head_->next_ = NULL;
head_->startseq_ = start;
head_->endseq_ = end;
head_->flags_ = tiflags;
head_->time_ = now;
} else {
if (tail_->endseq_ <= start) {
// common case of end of reass queue
p = tail_;
goto endfast;
}
// Look for the first segment AFTER this one
for (q = head_; q && (end > q->startseq_); q = q->next_)
;
// set p to the segment before this one
if (q == NULL)
p = tail_;
else
p = q->prev_;
// Put this one after p, regardless whether any segments
// should be deleted
if (p == NULL || (start >= p->endseq_)) {
// no overlap
endfast:
n = new seginfo;
n->startseq_ = start;
n->endseq_ = end;
n->flags_ = tiflags;
n->time_ = now;
if (p == NULL) {
// insert at head
n->next_ = head_;
n->prev_ = NULL;
head_->prev_ = n;
head_ = n;
} else {
// insert in the middle or end
n->next_ = p->next_;
p->next_ = n;
n->prev_ = p;
if (p == tail_)
tail_ = n;
}
} else {
// start or end overlaps p, so patch
// up p; will catch other overlaps below
p->startseq_ = MIN(p->startseq_, start);
p->endseq_ = MAX(p->endseq_, end);
p->flags_ |= tiflags;
n = p;
}
// Look for the first segment BEFORE p
for (q = head_; q && (n->startseq_ > q->endseq_); q = q->next_)
;
while (q != n) {
if (n->startseq_ > q->startseq_)
// q is partially included in n, merge it
n->startseq_ = q->startseq_;
// Delete q, which is already covered by n.
r = q;
q = q->next_;
if (r->prev_)
r->prev_->next_ = r->next_;
else
head_ = r->next_;
if (r->next_)
r->next_->prev_ = r->prev_;
else
// This should not happen!!
abort();
if (r == ptr_)
ptr_ = NULL;
delete r;
}
}
//
// look for a sequence of in-order segments and
// set rcv_nxt if we can
//
if (head_->startseq_ > rcv_nxt_) {
return 0; // still awaiting a hole-fill
}
tiflags = 0;
p = head_;
while (p != NULL) {
// update rcv_nxt_ to highest in-seq thing
// and delete the entry from the reass queue
// note that endseq is last contained seq# + 1
rcv_nxt_ = p->endseq_;
tiflags |= p->flags_;
q = p;
if (q->prev_)
q->prev_->next_ = q->next_;
else
head_ = q->next_;
if (q->next_)
q->next_->prev_ = q->prev_;
else
tail_ = q->prev_;
if (q == ptr_)
ptr_ = NULL;
if (q->next_ && (q->endseq_ < q->next_->startseq_)) {
delete q;
break; // only the in-seq stuff
}
// continue cleaning out in-seq stuff
p = p->next_;
delete q;
}
return (tiflags);
}
/*
* deal with timers going off.
* 2 types for now:
* retransmission timer (rtx_timer_)
* delayed ack timer (delack_timer_)
* delayed send (randomization) timer (delsnd_timer_)
*
* real TCP initializes the RTO as 6 sec
* (A + 2D, where A=0, D=3), [Stevens p. 305]
* and thereafter uses
* (A + 4D, where A and D are dynamic estimates)
*
* note that in the simulator t_srtt_, t_rttvar_ and t_rtt_
* are all measured in 'tcp_tick_'-second units
*/
void FullTcpAgent::timeout(int tno)
{
/*
* shouldn't be getting timeouts here
*/
if (state_ == TCPS_CLOSED || state_ == TCPS_LISTEN) {
fprintf(stderr, "%f: (%s) unexpected timeout %d in state %d\n",
now(), name(), tno, state_);
return;
}
if (tno == TCP_TIMER_RTX) {
/* retransmit timer */
++nrexmit_;
timeout_action();
send_much(1, PF_TIMEOUT, maxburst_);
} else if (tno == TCP_TIMER_DELSND) {
/*
* delayed-send timer, with random overhead
* to avoid phase effects
*/
send_much(1, PF_TIMEOUT, maxburst_);
} else if (tno == TCP_TIMER_DELACK) {
if (flags_ & TF_DELACK) {
flags_ &= ~TF_DELACK;
flags_ |= TF_ACKNOW;
send_much(1, REASON_NORMAL, 0);
}
delack_timer_.resched(delack_interval_);
} else {
fprintf(stderr, "%f: (%s) UNKNOWN TIMEOUT %d\n",
now(), name(), tno);
}
}
void
FullTcpAgent::dooptions(Packet* pkt)
{
// interesting options: timestamps (here),
// CC, CCNEW, CCECHO (future work perhaps)
hdr_flags *fh = (hdr_flags *)pkt->access(off_flags_);
hdr_tcp *tcph = (hdr_tcp*)pkt->access(off_tcp_);
if (ts_option_ && !fh->no_ts_) {
if (tcph->ts() < 0.0) {
fprintf(stderr,
"%f: FullTcpAgent(%s) warning: ts_option enabled in this TCP, but appears to be disabled in peer\n",
now(), name());
} else if (tcph->flags() & TH_SYN) {
flags_ |= TF_RCVD_TSTMP;
recent_ = tcph->ts();
recent_age_ = now();
}
}
}
void FullTcpAgent::newstate(int ns)
{
//printf("%f %s: newstate (%d)->(%d)\n",
//now(), name(), state_, ns);
state_ = ns;
}
void DelAckTimer::expire(Event *) {
a_->timeout(TCP_TIMER_DELACK);
}
/*
* for TCP Tahoe, we force a slow-start as the dup ack
* action. Also, no window inflation due to multiple dup
* acks. The latter is arranged by setting reno_fastrecov_
* false [which is performed by the Tcl constructor for Tahoe in
* ns-agent.tcl].
*/
/*
* Tahoe
* Dupack-action: what to do on a DUP ACK. After the initial check
* of 'recover' below, this function implements the following truth
* table:
*
* bugfix ecn last-cwnd == ecn action
*
* 0 0 0 full_tahoe_action
* 0 0 1 full_tahoe_action [impossible]
* 0 1 0 full_tahoe_action
* 0 1 1 1/2 window, return
* 1 0 0 nothing
* 1 0 1 nothing [impossible]
* 1 1 0 nothing
* 1 1 1 1/2 window, return
*/
void
TahoeFullTcpAgent::dupack_action()
{
int recovered = (highest_ack_ > recover_);
if (recovered || (!bug_fix_ && !ecn_) ||
last_cwnd_action_ == CWND_ACTION_DUPACK) {
goto full_tahoe_action;
}
if (ecn_ && last_cwnd_action_ == CWND_ACTION_ECN) {
last_cwnd_action_ = CWND_ACTION_DUPACK;
slowdown(CLOSE_CWND_ONE);
set_rtx_timer();
rtt_active_ = FALSE;
t_seqno_ = highest_ack_; // slow-start
//send_much(0, REASON_NORMAL, 0);
return;
}
if (bug_fix_) {
/*
* The line below, for "bug_fix_" true, avoids
* problems with multiple fast retransmits in one
* window of data.
*/
return;
}
full_tahoe_action:
recover_ = maxseq_;
last_cwnd_action_ = CWND_ACTION_DUPACK;
slowdown(CLOSE_SSTHRESH_HALF|CLOSE_CWND_ONE); // cwnd->1
set_rtx_timer();
rtt_active_ = FALSE;
t_seqno_ = highest_ack_; // slow-start
send_much(0, REASON_NORMAL, 0);
return;
}
/*
* for NewReno, a partial ACK does not exit fast recovery,
* and does not reset the dup ACK counter (which might trigger fast
* retransmits we don't want). In addition, the number of packets
* sent in response to an ACK is limited to recov_maxburst_ during
* recovery periods.
*/
NewRenoFullTcpAgent::NewRenoFullTcpAgent() : save_maxburst_(-1)
{
bind("recov_maxburst_", &recov_maxburst_);
}
void
NewRenoFullTcpAgent::pack_action(Packet*)
{
fast_retransmit(highest_ack_);
cwnd_ = ssthresh_;
if (save_maxburst_ < 0) {
save_maxburst_ = maxburst_;
maxburst_ = recov_maxburst_;
}
}
void
NewRenoFullTcpAgent::ack_action(Packet* p)
{
if (save_maxburst_ >= 0) {
maxburst_ = save_maxburst_;
save_maxburst_ = -1;
}
FullTcpAgent::ack_action(p);
}
/*
*
* ****** SACK ******
*
* for Sack, do the following
*/
SackFullTcpAgent::SackFullTcpAgent() : sack_nxt_(-1), sq_(sack_min_)
{
bind("sack_option_size_", &sack_option_size_);
bind("sack_block_size_", &sack_block_size_);
bind("max_sack_blocks_", &max_sack_blocks_);
}
SackFullTcpAgent::~SackFullTcpAgent()
{
sq_.clear();
}
void
SackFullTcpAgent::reset()
{
sq_.clear();
sack_max_ = sack_min_ = sack_nxt_ = -1;
reno_fastrecov_ = FALSE; // always F for sack
pipectrl_ = FALSE; // start in window mode
FullTcpAgent::reset();
}
/*
* override FullTcpAgent::recv() method to parse sack info
* when it arrives. Then call standard recv() method.
*/
void
SackFullTcpAgent::recv(Packet* pkt, Handler* h)
{
hdr_tcp* tcph = (hdr_tcp*)pkt->access(off_tcp_);
int ackno = tcph->ackno();
if (state_ == TCPS_ESTABLISHED &&
(ackno > iss_ && ackno <= maxseq_)) {
int slen = tcph->sa_length();
int i;
for (i = 0; i < slen; ++i) {
if (sack_max_ < tcph->sa_right(i))
sack_max_ = tcph->sa_right(i);
sq_.add(tcph->sa_left(i), tcph->sa_right(i), 0);
}
}
FullTcpAgent::recv(pkt, h);
}
int
SackFullTcpAgent::hdrsize(int nsackblocks)
{
int total = FullTcpAgent::headersize();
if (nsackblocks > 0) {
total += ((nsackblocks * sack_block_size_)
+ sack_option_size_);
}
return (total);
}
void
SackFullTcpAgent::ack_action(Packet* p)
{
FullTcpAgent::ack_action(p);
if (!sq_.empty() && sack_max_ <= highest_ack_) {
sq_.clear();
}
if (sack_nxt_ < highest_ack_)
sack_nxt_ = highest_ack_;
pipectrl_ = FALSE;
}
void
SackFullTcpAgent::dupack_action()
{
int recovered = (highest_ack_ > recover_);
if (recovered || (!bug_fix_ && !ecn_) ||
last_cwnd_action_ == CWND_ACTION_DUPACK) {
goto full_sack_action;
}
if (ecn_ && last_cwnd_action_ == CWND_ACTION_ECN) {
pipectrl_ = TRUE;
slowdown(CLOSE_CWND_HALF);
cancel_rtx_timer();
rtt_active_ = FALSE;
recover_ = maxseq_;
send_much(1, REASON_DUPACK, maxburst_);
return;
}
if (bug_fix_) {
/*
* The line below, for "bug_fix_" true, avoids
* problems with multiple fast retransmits in one
* window of data.
*/
return;
}
full_sack_action:
slowdown(CLOSE_SSTHRESH_HALF|CLOSE_CWND_HALF);
cancel_rtx_timer();
rtt_active_ = FALSE;
pipectrl_ = TRUE;
recover_ = maxseq_;
send_much(1, REASON_DUPACK, maxburst_);
return;
}
void
SackFullTcpAgent::pack_action(Packet*)
{
--pipe_;
if (sack_nxt_ < highest_ack_)
sack_nxt_ = highest_ack_;
}
void
SackFullTcpAgent::send_much(int force, int reason, int maxburst)
{
if (fastrecov_)
send_holes(force, maxburst);
FullTcpAgent::send_much(force, reason, maxburst);
}
int
SackFullTcpAgent::build_options(hdr_tcp* tcph)
{
int total = FullTcpAgent::build_options(tcph);
if (!rq_.empty()) {
int nblk = rq_.gensack(&tcph->sa_left(0), max_sack_blocks_);
tcph->sa_length() = nblk;
total += (nblk * sack_block_size_) + sack_option_size_;
} else {
tcph->sa_length() = 0;
}
return (total);
}
void
SackFullTcpAgent::send_holes(int force, int maxburst)
{
int npack = 0;
int save_tseq = t_seqno_;
t_seqno_ = sack_nxt_;
if (sq_.empty()) {
// no holes, just return
return;
}
int nxtblk[2]; // left, right of 1 sack block
sq_.sync(); // reset to beginning of sack list
// skip over old blocks
while (sq_.nextblk(nxtblk)) {
if (t_seqno_ <= nxtblk[1])
break;
}
while (force || (pipe_ < window())) {
force = 0;
// don't sent off the top
if (t_seqno_ > recover_ || t_seqno_ >= sack_max_)
break;
// skip this one if the receiver has it already
if (t_seqno_ >= nxtblk[0] && t_seqno_ <= nxtblk[1]) {
t_seqno_ = nxtblk[1];
if (sq_.nextblk(nxtblk))
continue;
break; // no more blocks, finish up
}
// try to send something, check to see if we did
int save = t_seqno_;
output(t_seqno_, REASON_SACK);
if (t_seqno_ == save) {
break;
}
++pipe_; // sent something
if ((maxburst && ++npack >= maxburst) ||
(outflags() & (TH_SYN|TH_FIN)))
break;
}
sack_nxt_ = t_seqno_; // update next hole fill
t_seqno_ = save_tseq; // restore t_seqno_
return;
}
void
SackFullTcpAgent::timeout_action()
{
FullTcpAgent::timeout_action();
sq_.clear();
sack_min_ = highest_ack_;
sack_nxt_ = sack_max_ = -1;
}
tcp-int.cc
/* -*- 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 Research Group 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, U.C.Berkeley
* http://daedalus.cs.berkeley.edu
*/
/*
* We separate TCP functionality into two parts: that having to do with
* providing a reliable, ordered byte-stream service, and that having to do with
* congestion control and loss recovery. The former is done on a per-connection
* basis and is implemented as part of IntTcpAgent ("integrated TCP"). The
* latter is done in an integrated fashion across multiple TCP connections, and
* is implemented as part of TcpSessionAgent ("TCP session"). TcpSessionAgent is
* derived from CorresHost ("correspondent host"), which keeps track of the
* state of all TCP (TCP/Int) connections to a host that it is corresponding
* with.
*
* The motivation for this separation of functionality is to make an ensemble of
* connection more well-behaved than a set of independent TCP connections.
* The packet loss rate is cut down and the chances of losses being recovered
* via data-driven techniques (rather than via timeouts) is improved. At the
* same time, we do not introduce any unnecessary coupling between the
* logically-independent byte-streams that the set of connections represents.
* This is in contrast to the coupling that is inherent in the multiplexing at
* the application layer of multiple byte-streams onto a single TCP connection.
*/
#include
#include
#include
#include "packet.h"
#include "ip.h"
#include "tcp.h"
#include "flags.h"
#include "nilist.h"
#include "tcp-int.h"
#include "chost.h"
#include "tcp-session.h"
#include "random.h"
Islist TcpSessionAgent::sessionList_;
static class IntTcpClass : public TclClass {
public:
IntTcpClass() : TclClass("Agent/TCP/Int") {}
TclObject* create(int, const char*const*) {
return (new IntTcpAgent());
}
} class_tcp_int;
IntTcpAgent::IntTcpAgent() : TcpAgent(), slink(),
session_(0), closecwTS_(0), lastTS_(-1), count_(0),
wt_(1), wndIncSeqno_(0), num_thresh_dupack_segs_(0)
{
bind("rightEdge_", &rightEdge_);
bind("uniqTS_", &uniqTS_);
bind("winInc_", &winInc_);
bind("winMult_", &winMult_);
}
int
IntTcpAgent::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
if (argc == 3) {
if (!strcmp(argv[1], "setwt")) {
if (!session_)
createTcpSession();
session_->set_weight(this,atoi(argv[2]));
return (TCL_OK);
}
}
else if (argc == 2) {
if (!strcmp(argv[1], "session")) {
if (!session_)
createTcpSession();
tcl.resultf("%s", session_->name());
return (TCL_OK);
}
}
return (TcpAgent::command(argc,argv));
}
/*
* Update the ack information and reset the timer. RTT update is done in
* TcpSessionAgent.
*/
void
IntTcpAgent::newack(Packet* pkt)
{
hdr_tcp *tcph = (hdr_tcp*)pkt->access(off_tcp_);
last_ack_ = tcph->seqno();
highest_ack_ = last_ack_;
if (t_seqno_ < last_ack_ + 1)
t_seqno_ = last_ack_ + 1;
/* if the connection is done, call finish() */
if ((highest_ack_ >= curseq_-1) && !closed_) {
closed_ = 1;
finish();
}
}
void
IntTcpAgent::recv(Packet *pkt, Handler *)
{
hdr_tcp *tcph = (hdr_tcp*)pkt->access(off_tcp_);
int amt_data_acked = 0;
if (tcph->seqno() > last_ack_) {
amt_data_acked = tcph->seqno() - last_ack_;
newack(pkt);
}
session_->recv(this, pkt, amt_data_acked);
}
void
IntTcpAgent::createTcpSession()
{
Tcl& tcl = Tcl::instance();
tcl.evalf("%s set node_", name());
tcl.evalf("%s createTcpSession %d", tcl.result(), daddr());
Islist_iter session_iter(TcpSessionAgent::sessionList_);
TcpSessionAgent *cur;
while ((cur = session_iter()) != NULL) {
if (cur->addr()/256 == addr() && cur->daddr() == daddr()) {
session_ = cur;
break;
}
}
if (!session_) {
printf("In IntTcpAgent::createTcpSession(): failed\n");
abort();
}
session_->add_agent(this, size_, winMult_, winInc_, ssthresh_);
}
void
IntTcpAgent::output(int seqno, int reason)
{
Packet *pkt = allocpkt();
hdr_tcp *tcph = (hdr_tcp *) pkt->access(off_tcp_);
tcph->seqno() = seqno;
tcph->ts() = Scheduler::instance().clock();
tcph->ts_echo() = ts_peer_;
tcph->reason() = reason;
session_->setflags(pkt);
int bytes = ((hdr_cmn*)pkt->access(off_cmn_))->size();
/* call helper function to fill in additional fields */
output_helper(pkt);
++ndatapack_;
ndatabytes_ += bytes;
send(pkt, 0);
if (seqno == curseq_ && seqno > maxseq_)
idle(); // Tell application I have sent everything so far
if (seqno > maxseq_) {
maxseq_ = seqno;
}
else {
++nrexmitpack_;
nrexmitbytes_ += bytes;
}
if (wndIncSeqno_ == 0)
wndIncSeqno_ = maxseq_;
}
/*
* Unlike in other flavors of TCP, IntTcpAgent does not decide when to send
* packets and how many of them to send. That decision is made by
* TcpSessionAgent. So send_much() does little more than defer to
* TcpSessionAgent.
*/
void
IntTcpAgent::send_much(int force, int reason, int /*maxburst*/)
{
if (!session_)
createTcpSession();
if (!force && delsnd_timer_.status() == TIMER_PENDING)
return;
if (overhead_ && !force) {
delsnd_timer_.resched(Random::uniform(overhead_));
return;
}
session_->send_much(this, force,reason);
}
/*
* Send one new packet.
*/
void
IntTcpAgent::send_one(int sessionSeqno)
{
int dst_addr = daddr();
int dst_port = dport();
int sport = port();
if (!session_)
createTcpSession();
/*
* XXX We assume that the session layer has already made sure that
* we have data to send.
*/
output(t_seqno_++);
session_->add_pkts(size_, t_seqno_ - 1, sessionSeqno,
dst_addr, dst_port, sport, lastTS_, this);
}
/*
* open up the congestion window
*/
void
IntTcpAgent::opencwnd()
{
session_->opencwnd(size_, this);
}
/*
* close down the congestion window
*/
void
IntTcpAgent::closecwnd(int how)
{
session_->closecwnd(how, this);
}
Segment *
IntTcpAgent::rxmit_last(int reason, int seqno, int sessionSeqno, double /*ts*/)
{
session_->agent_rcov(this);
/*
* XXX kludge -- IntTcpAgent is not supposed to deal with
* rtx timer
*/
session_->reset_rtx_timer(1,0);
output(seqno, reason);
daddr_ = daddr();
dport_ = dport();
sport_ = port();
return (session_->add_pkts(size_, seqno, sessionSeqno, daddr_,
dport_, sport_, lastTS_, this));
return NULL;
}
unsigned long output_helper_count=0;
double last_clock=0;
void
IntTcpAgent::output_helper(Packet *p)
{
double now = Scheduler::instance().clock();
output_helper_count++;
last_clock = now;
hdr_tcp *tcph = (hdr_tcp*)p->access(off_tcp_);
/* This is to make sure that we get unique times for each xmission */
while (uniqTS_ && now <= lastTS_) {
now += 0.000001; // something arbitrarily small
}
lastTS_ = now;
tcph->ts() = now;
/* if this is a fast start pkt and not a retransmission, mark it */
if (session_->fs_pkt() && tcph->seqno() > maxseq_)
((hdr_flags*)p->access(off_flags_))->fs_ = 1;
return;
}
int
IntTcpAgent::data_left_to_send() {
return (curseq_ > t_seqno_);
}
tcp-newreno.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1990, 1997 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Lawrence Berkeley Laboratory,
* Berkeley, CA. The name of the University may not be used to
* endorse or promote products derived from this software without
* specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (LBL)";
#endif
//
// newreno-tcp: a revised reno TCP source, without sack
//
#include
#include
#include
#include "packet.h"
#include "ip.h"
#include "tcp.h"
#include "flags.h"
static class NewRenoTcpClass : public TclClass {
public:
NewRenoTcpClass() : TclClass("Agent/TCP/Newreno") {}
TclObject* create(int, const char*const*) {
return (new NewRenoTcpAgent());
}
} class_newreno;
NewRenoTcpAgent::NewRenoTcpAgent() : newreno_changes_(0),
newreno_changes1_(0), acked_(0), firstpartial_(0),
partial_window_deflation_(0), exit_recovery_fix_(0)
{
bind("newreno_changes_", &newreno_changes_);
bind("newreno_changes1_", &newreno_changes1_);
bind("exit_recovery_fix_", &exit_recovery_fix_);
bind("partial_window_deflation_", &partial_window_deflation_);
}
/*
* Process a packet that acks previously unacknowleges data, but
* does not take us out of Fast Retransmit.
*/
void NewRenoTcpAgent::partialnewack(Packet* pkt)
{
hdr_tcp *tcph = (hdr_tcp*)pkt->access(off_tcp_);
#ifdef notyet
if (pkt->seqno_ == stp->maxpkts && stp->maxpkts > 0)
stp->endtime = (float) realtime();
#endif
if (partial_window_deflation_) {
// Do partial window deflation before resetting last_ack_
unsigned int deflate = 0; // Should initialize it?? - haoboy
if (tcph->seqno() > last_ack_) // assertion
deflate = tcph->seqno() - last_ack_;
else
printf("False call to partialnewack: deflate %u \
last_ack_ %d\n", deflate, last_ack_);
if (dupwnd_ > deflate)
dupwnd_ -= (deflate - 1);
else {
cwnd_ -= (deflate - dupwnd_);
// Leave dupwnd_ > 0 to flag "fast recovery" phase
dupwnd_ = 1;
}
}
last_ack_ = tcph->seqno();
highest_ack_ = last_ack_;
if (t_seqno_ < last_ack_ + 1)
t_seqno_ = last_ack_ + 1;
if (rtt_active_ && tcph->seqno() >= rtt_seq_) {
rtt_active_ = 0;
t_backoff_ = 1;
}
}
void NewRenoTcpAgent::partialnewack_helper(Packet* pkt)
{
if (!newreno_changes1_ || firstpartial_ == 0) {
firstpartial_ = 1;
/* For newreno_changes1_,
* only reset the retransmit timer for the first
* partial ACK, so that, in the worst case, we
* don't have to wait for one packet retransmitted
* per RTT.
*/
newtimer(pkt);
}
partialnewack(pkt);
output(last_ack_ + 1, 0);
}
int
NewRenoTcpAgent::allow_fast_retransmit(int /* last_cwnd_action_*/)
{
return 0;
}
void
NewRenoTcpAgent::dupack_action()
{
int recovered = (highest_ack_ > recover_);
int allowFastRetransmit = allow_fast_retransmit(last_cwnd_action_);
if (recovered || (!bug_fix_ && !ecn_) || allowFastRetransmit) {
goto reno_action;
}
if (ecn_ && last_cwnd_action_ == CWND_ACTION_ECN) {
last_cwnd_action_ = CWND_ACTION_DUPACK;
/*
* What if there is a DUPACK action followed closely by ECN
* followed closely by a DUPACK action?
* The optimal thing to do would be to remember all
* congestion actions from the most recent window
* of data. Otherwise "bugfix" might not prevent
* all unnecessary Fast Retransmits.
*/
reset_rtx_timer(1,0);
output(last_ack_ + 1, TCP_REASON_DUPACK);
return;
}
if (bug_fix_) {
/*
* The line below, for "bug_fix_" true, avoids
* problems with multiple fast retransmits in one
* window of data.
*/
return;
}
reno_action:
recover_ = maxseq_;
last_cwnd_action_ = CWND_ACTION_DUPACK;
slowdown(CLOSE_SSTHRESH_HALF|CLOSE_CWND_HALF);
reset_rtx_timer(1,0);
output(last_ack_ + 1, TCP_REASON_DUPACK); // from top
return;
}
void NewRenoTcpAgent::recv(Packet *pkt, Handler*)
{
hdr_tcp *tcph = (hdr_tcp*)pkt->access(off_tcp_);
/* Use first packet to calculate the RTT --contributed by Allman */
if (++acked_ == 1)
basertt_ = Scheduler::instance().clock() - firstsent_;
/* Estimate ssthresh based on the calculated RTT and the estimated
bandwidth (using ACKs 2 and 3). */
else if (acked_ == 2)
ack2_ = Scheduler::instance().clock();
else if (acked_ == 3) {
ack3_ = Scheduler::instance().clock();
new_ssthresh_ = int((basertt_ * (size_ / (ack3_ - ack2_))) / size_);
if (newreno_changes_ > 0 && new_ssthresh_ < ssthresh_)
ssthresh_ = new_ssthresh_;
}
#ifdef notdef
if (pkt->type_ != PT_ACK) {
fprintf(stderr,
"ns: confiuration error: tcp received non-ack\n");
exit(1);
}
#endif
++nackpack_;
ts_peer_ = tcph->ts();
if (((hdr_flags*)pkt->access(off_flags_))->ecnecho() && ecn_)
ecn(tcph->seqno());
recv_helper(pkt);
if (tcph->seqno() > last_ack_) {
if (tcph->seqno() >= recover_
|| (last_cwnd_action_ != CWND_ACTION_DUPACK &&
tcph->seqno() > last_ack_)) {
if (dupwnd_ > 0 && exit_recovery_fix_) {
int outstanding = maxseq_ - tcph->seqno() + 1;
if (ssthresh_ < outstanding)
cwnd_ = ssthresh_;
else
cwnd_ = outstanding;
}
dupwnd_ = 0;
firstpartial_ = 0;
recv_newack_helper(pkt);
if (last_ack_ == 0 && delay_growth_) {
cwnd_ = initial_window();
}
} else {
/* received new ack for a packet sent during Fast
* Recovery, but sender stays in Fast Recovery */
if (partial_window_deflation_ == 0)
dupwnd_ = 0;
partialnewack_helper(pkt);
}
} else if (tcph->seqno() == last_ack_) {
if (((hdr_flags*)pkt->access(off_flags_))->eln_ && eln_) {
tcp_eln(pkt);
return;
}
if (++dupacks_ == NUMDUPACKS) {
dupack_action();
dupwnd_ = NUMDUPACKS;
} else if (dupacks_ > NUMDUPACKS) {
++dupwnd_; // fast recovery
/* For every two duplicate ACKs we receive (in the
* "fast retransmit phase"), send one entirely new
* data packet "to keep the flywheel going". --Allman
*/
if (newreno_changes_ > 0 && (dupacks_ % 2) == 1)
output (t_seqno_++,0);
} else if (dupacks_ < NUMDUPACKS && singledup_ ) {
send_one();
}
}
Packet::free(pkt);
#ifdef notyet
if (trace_)
plot();
#endif
/*
* Try to send more data
*/
if (dupacks_ == 0)
/*
* Maxburst is really only needed for the first
* window of data on exiting Fast Recovery.
*/
send_much(0, 0, maxburst_);
else if (dupacks_ > NUMDUPACKS - 1 && newreno_changes_ == 0)
send_much(0, 0, 2);
}
tcp-rbp.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1997 University of Southern California.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation, advertising
* materials, and other materials related to such distribution and use
* acknowledge that the software was developed by the University of
* Southern California, Information Sciences Institute. The name of the
* University may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
**
*
* Tcp-vegas with Rate-based pacing by John Heidemann
* and Vikram Visweswaraiah .
* The original SunOS implementation was by Vikram Visweswaraiah
* and Ashish Savla .
*
* Rate-based pacing is an experimental addition to TCP
* to address the slow-start restart problem.
* See
* for details.
*
* A paper analysing RBP performance is in progress (as of 19-Jun-97).
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (NCSU/IBM)";
#endif
#include
#include
#include
#include "ip.h"
#include "tcp.h"
#include "flags.h"
#ifndef MIN
#define MIN(x, y) ((x)<(y) ? (x) : (y))
#endif /* ! MIN */
#if 0
#define RBP_DEBUG_PRINTF(x) printf x
#else /* ! 0 */
#define RBP_DEBUG_PRINTF(x)
#endif /* 0 */
#define RBP_MIN_SEGMENTS 2
class RBPVegasTcpAgent;
class RBPVegasPaceTimer : public TimerHandler {
public:
RBPVegasPaceTimer(RBPVegasTcpAgent *a) : TimerHandler() { a_ = a; }
protected:
virtual void expire(Event *e);
RBPVegasTcpAgent *a_;
};
// Hmmm... ``a is a'' in the construction of the RBPVegasPaceTimer edifice :->
class RBPVegasTcpAgent : public virtual VegasTcpAgent {
friend RBPVegasPaceTimer;
public:
RBPVegasTcpAgent();
virtual void recv(Packet *pkt, Handler *);
virtual void timeout(int tno);
virtual void send_much(int force, int reason, int maxburst);
double rbp_scale_; // conversion from actual -> rbp send rates
enum rbp_rate_algorithms { RBP_NO_ALGORITHM, RBP_VEGAS_RATE_ALGORITHM, RBP_CWND_ALGORITHM };
int rbp_rate_algorithm_;
protected:
void paced_send_one();
int able_to_rbp_send_one();
// stats on what we did
int rbp_segs_actually_paced_;
enum rbp_modes { RBP_GOING, RBP_POSSIBLE, RBP_OFF };
enum rbp_modes rbp_mode_;
double rbp_inter_pace_delay_;
RBPVegasPaceTimer pace_timer_;
};
static class RBPVegasTcpClass : public TclClass {
public:
RBPVegasTcpClass() : TclClass("Agent/TCP/Vegas/RBP") {}
TclObject* create(int, const char*const*) {
return (new RBPVegasTcpAgent());
}
} class_vegas_rbp;
void RBPVegasPaceTimer::expire(Event *) { a_->paced_send_one(); }
RBPVegasTcpAgent::RBPVegasTcpAgent() : VegasTcpAgent(),
rbp_mode_(RBP_OFF),
pace_timer_(this)
{
bind("rbp_scale_", &rbp_scale_);
bind("rbp_rate_algorithm_", &rbp_rate_algorithm_);
bind("rbp_segs_actually_paced_", &rbp_segs_actually_paced_);
bind("rbp_inter_pace_delay_", &rbp_inter_pace_delay_);
}
void
RBPVegasTcpAgent::recv(Packet *pkt, Handler *hand)
{
if (rbp_mode_ != RBP_OFF) {
// reciept of anything disables rbp
rbp_mode_ = RBP_OFF;
// Vegas takes care of cwnd.
};
VegasTcpAgent::recv(pkt, hand);
}
void
RBPVegasTcpAgent::timeout(int tno)
{
if (tno == TCP_TIMER_RTX) {
if (highest_ack_ == maxseq_) {
// Idle for a while => RBP next time.
rbp_mode_ = RBP_POSSIBLE;
return;
};
};
VegasTcpAgent::timeout(tno);
}
void
RBPVegasTcpAgent::send_much(int force, int reason, int maxburst)
{
if (rbp_mode_ == RBP_POSSIBLE && able_to_rbp_send_one()) {
// start paced mode
rbp_mode_ = RBP_GOING;
rbp_segs_actually_paced_ = 0;
double rbwin_vegas;
switch (rbp_rate_algorithm_) {
case RBP_VEGAS_RATE_ALGORITHM:
// Try to follow tcp_output.c here
// Calculate the vegas window as its reported rate
// times the rtt.
rbwin_vegas = v_actual_ * v_rtt_;
RBP_DEBUG_PRINTF(("-----------------\n"));
RBP_DEBUG_PRINTF(("rbwin_vegas = %g\nv_actual = %g\nv_rtt =%g\nbase_rtt=%g\n",
rbwin_vegas, v_actual_, v_rtt_, v_baseRTT_));
// Smooth the vegas window
rbwin_vegas *= rbp_scale_;
break;
case RBP_CWND_ALGORITHM:
// Pace out scaled cwnd.
rbwin_vegas = cwnd_ * rbp_scale_;
break;
default:
abort();
};
rbwin_vegas = int(rbwin_vegas + 0.5); // round
// Always pace at least RBP_MIN_SEGMENTS
if (rbwin_vegas <= RBP_MIN_SEGMENTS) {
rbwin_vegas = RBP_MIN_SEGMENTS;
};
// Conservatively set the congestion window to min of
// congestion window and the smoothed rbwin_vegas
RBP_DEBUG_PRINTF(("cwnd before check = %g\n", double(cwnd_)));
cwnd_ = MIN(cwnd_,(TracedDouble) rbwin_vegas);
RBP_DEBUG_PRINTF(("cwnd after check = %g\n", double(cwnd_)));
RBP_DEBUG_PRINTF(("recv win = %g\n", wnd_));
// RBP timer calculations must be based on the actual
// window which is the min of the receiver's
// advertised window and the congestion window.
// TcpAgent::window() does this job.
// What this means is we expect to send window() pkts
// in v_rtt_ time.
rbp_inter_pace_delay_ = (v_rtt_)/(window() * 1.0);
RBP_DEBUG_PRINTF(("window is %d\n", window()));
RBP_DEBUG_PRINTF(("ipt = %g\n", rbp_inter_pace_delay_));
paced_send_one();
} else {
VegasTcpAgent::send_much(force,reason, maxburst);
};
}
void
RBPVegasTcpAgent::paced_send_one()
{
if (rbp_mode_ == RBP_GOING && able_to_rbp_send_one()) {
RBP_DEBUG_PRINTF(("Sending one rbp packet\n"));
// send one packet
output(t_seqno_++, TCP_REASON_RBP);
rbp_segs_actually_paced_++;
// schedule next pkt
pace_timer_.resched(rbp_inter_pace_delay_);
};
}
int
RBPVegasTcpAgent::able_to_rbp_send_one()
{
return t_seqno_ < curseq_ && t_seqno_ <= highest_ack_ + window();
}
/***********************************************************************
*
* The reno-based version
*
*/
class RBPRenoTcpAgent;
class RBPRenoPaceTimer : public TimerHandler {
public:
RBPRenoPaceTimer(RBPRenoTcpAgent *a) : TimerHandler() { a_ = a; }
protected:
virtual void expire(Event *e);
RBPRenoTcpAgent *a_;
};
// Hmmm... ``a is a'' in the construction of the RBPRenoPaceTimer edifice :->
class RBPRenoTcpAgent : public virtual RenoTcpAgent {
friend RBPRenoPaceTimer;
public:
RBPRenoTcpAgent();
virtual void recv(Packet *pkt, Handler *);
virtual void timeout(int tno);
virtual void send_much(int force, int reason, int maxburst);
double rbp_scale_; // conversion from actual -> rbp send rates
// enum rbp_rate_algorithms { RBP_NO_ALGORITHM, RBP_VEGAS_RATE_ALGORITHM, RBP_CWND_ALGORITHM };
// int rbp_rate_algorithm_;
protected:
void paced_send_one();
int able_to_rbp_send_one();
// stats on what we did
int rbp_segs_actually_paced_;
enum rbp_modes { RBP_GOING, RBP_POSSIBLE, RBP_OFF };
enum rbp_modes rbp_mode_;
double rbp_inter_pace_delay_;
RBPRenoPaceTimer pace_timer_;
};
static class RBPRenoTcpClass : public TclClass {
public:
RBPRenoTcpClass() : TclClass("Agent/TCP/Reno/RBP") {}
TclObject* create(int, const char*const*) {
return (new RBPRenoTcpAgent());
}
} class_reno_rbp;
void RBPRenoPaceTimer::expire(Event *) { a_->paced_send_one(); }
RBPRenoTcpAgent::RBPRenoTcpAgent() : TcpAgent(),
rbp_mode_(RBP_OFF),
pace_timer_(this)
{
bind("rbp_scale_", &rbp_scale_);
// algorithm is not used in Reno
// bind("rbp_rate_algorithm_", &rbp_rate_algorithm_);
bind("rbp_segs_actually_paced_", &rbp_segs_actually_paced_);
bind("rbp_inter_pace_delay_", &rbp_inter_pace_delay_);
}
void
RBPRenoTcpAgent::recv(Packet *pkt, Handler *hand)
{
if (rbp_mode_ != RBP_OFF) {
// reciept of anything disables rbp
rbp_mode_ = RBP_OFF;
// reset cwnd such that we're now ack clocked.
hdr_tcp *tcph = (hdr_tcp*)pkt->access(off_tcp_);
if (tcph->seqno() > last_ack_) {
/* reno does not do rate adjustments as Vegas;
* normally, one wouldn't do any adjustments to
* cwnd and allow the sliding window to do its job
* But, if cwnd >> amt_paced, then there's a
* bunch of data that can be sent asap, plus the
* two (typically, due to delacks) that get opened
* up due to the first ack. This would lead to
* a burst, defeating the purpose of pacing.
* Ideally, one would want cwnd = amt_paced
* ALWAYS. Since this doesn't necessarily happen,
* `cap' cwnd to the amt paced and THEN let
* sliding windows take over. Note that this
* mechanism will typically result in 3 segs
* being sent out when the first ack is received.
*/
cwnd_ = maxseq_ - last_ack_;
RBP_DEBUG_PRINTF(("\ncwnd-after-first-ack=%g\n", (double)cwnd_));
};
};
RenoTcpAgent::recv(pkt, hand);
}
void
RBPRenoTcpAgent::timeout(int tno)
{
if (tno == TCP_TIMER_RTX) {
if (highest_ack_ == maxseq_) {
// Idle for a while => RBP next time.
rbp_mode_ = RBP_POSSIBLE;
return;
};
};
RenoTcpAgent::timeout(tno);
}
void
RBPRenoTcpAgent::send_much(int force, int reason, int maxburst)
{
if (rbp_mode_ == RBP_POSSIBLE && able_to_rbp_send_one()) {
// start paced mode
rbp_mode_ = RBP_GOING;
rbp_segs_actually_paced_ = 0;
// Pace out scaled cwnd.
double rbwin_reno;
rbwin_reno = cwnd_ * rbp_scale_;
rbwin_reno = int(rbwin_reno + 0.5); // round
// Always pace at least RBP_MIN_SEGMENTS
if (rbwin_reno <= RBP_MIN_SEGMENTS) {
rbwin_reno = RBP_MIN_SEGMENTS;
};
// Conservatively set the congestion window to min of
// congestion window and the smoothed rbwin_reno
RBP_DEBUG_PRINTF(("cwnd before check = %g\n", double(cwnd_)));
cwnd_ = MIN(cwnd_,(TracedDouble) rbwin_reno);
RBP_DEBUG_PRINTF(("cwnd after check = %g\n", double(cwnd_)));
RBP_DEBUG_PRINTF(("recv win = %g\n", wnd_));
// RBP timer calculations must be based on the actual
// window which is the min of the receiver's
// advertised window and the congestion window.
// TcpAgent::window() does this job.
// What this means is we expect to send window() pkts
// in v_srtt_ time.
static double srtt_scale = 0.0;
if (srtt_scale == 0.0) { // yuck yuck yuck!
srtt_scale = 1.0; // why are we doing fixed point?
int i;
for (i = T_SRTT_BITS; i > 0; i--) {
srtt_scale /= 2.0;
};
}
rbp_inter_pace_delay_ = (t_srtt_ * srtt_scale * tcp_tick_) / (window() * 1.0);
RBP_DEBUG_PRINTF(("window is %d\n", window()));
RBP_DEBUG_PRINTF(("ipt = %g\n", rbp_inter_pace_delay_));
paced_send_one();
} else {
RenoTcpAgent::send_much(force,reason, maxburst);
};
}
void
RBPRenoTcpAgent::paced_send_one()
{
if (rbp_mode_ == RBP_GOING && able_to_rbp_send_one()) {
RBP_DEBUG_PRINTF(("Sending one rbp packet\n"));
// send one packet
output(t_seqno_++, TCP_REASON_RBP);
rbp_segs_actually_paced_++;
// schedule next pkt
pace_timer_.resched(rbp_inter_pace_delay_);
};
}
int
RBPRenoTcpAgent::able_to_rbp_send_one()
{
return t_seqno_ < curseq_ && t_seqno_ <= highest_ack_ + window();
}
tcp-reno.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1990, 1997 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Lawrence Berkeley Laboratory,
* Berkeley, CA. The name of the University may not be used to
* endorse or promote products derived from this software without
* specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (LBL)";
#endif
#include
#include
#include
#include "ip.h"
#include "tcp.h"
#include "flags.h"
static class RenoTcpClass : public TclClass {
public:
RenoTcpClass() : TclClass("Agent/TCP/Reno") {}
TclObject* create(int, const char*const*) {
return (new RenoTcpAgent());
}
} class_reno;
int RenoTcpAgent::window()
{
//
// reno: inflate the window by dupwnd_
// dupwnd_ will be non-zero during fast recovery,
// at which time it contains the number of dup acks
//
int win = int(cwnd_) + dupwnd_;
if (win > int(wnd_))
win = int(wnd_);
return (win);
}
RenoTcpAgent::RenoTcpAgent() : TcpAgent(), dupwnd_(0)
{
}
void RenoTcpAgent::recv(Packet *pkt, Handler*)
{
hdr_tcp *tcph = (hdr_tcp*)pkt->access(off_tcp_);
#ifdef notdef
if (pkt->type_ != PT_ACK) {
fprintf(stderr,
"ns: confiuration error: tcp received non-ack\n");
exit(1);
}
#endif
++nackpack_;
ts_peer_ = tcph->ts();
if (((hdr_flags*)pkt->access(off_flags_))->ecnecho() && ecn_)
ecn(tcph->seqno());
recv_helper(pkt);
if (tcph->seqno() > last_ack_) {
dupwnd_ = 0;
recv_newack_helper(pkt);
if (last_ack_ == 0 && delay_growth_) {
cwnd_ = initial_window();
}
} else if (tcph->seqno() == last_ack_) {
if (((hdr_flags*)pkt->access(off_flags_))->eln_ && eln_) {
tcp_eln(pkt);
return;
}
if (++dupacks_ == NUMDUPACKS) {
dupack_action();
dupwnd_ = NUMDUPACKS;
} else if (dupacks_ > NUMDUPACKS) {
++dupwnd_; // fast recovery
} else if (dupacks_ < NUMDUPACKS && singledup_ ) {
send_one();
}
}
Packet::free(pkt);
#ifdef notyet
if (trace_)
plot();
#endif
/*
* Try to send more data
*/
if (dupacks_ == 0 || dupacks_ > NUMDUPACKS - 1)
send_much(0, 0, maxburst_);
}
int
RenoTcpAgent::allow_fast_retransmit(int last_cwnd_action_)
{
return (last_cwnd_action_ == CWND_ACTION_DUPACK);
}
/*
* Dupack-action: what to do on a DUP ACK. After the initial check
* of 'recover' below, this function implements the following truth
* table:
*
* bugfix ecn last-cwnd == ecn action
*
* 0 0 0 reno_action
* 0 0 1 reno_action [impossible]
* 0 1 0 reno_action
* 0 1 1 retransmit, return
* 1 0 0 nothing
* 1 0 1 nothing [impossible]
* 1 1 0 nothing
* 1 1 1 retransmit, return
*/
void
RenoTcpAgent::dupack_action()
{
int recovered = (highest_ack_ > recover_);
int allowFastRetransmit = allow_fast_retransmit(last_cwnd_action_);
if (recovered || (!bug_fix_ && !ecn_) || allowFastRetransmit) {
goto reno_action;
}
if (ecn_ && last_cwnd_action_ == CWND_ACTION_ECN) {
last_cwnd_action_ = CWND_ACTION_DUPACK;
/*
* What if there is a DUPACK action followed closely by ECN
* followed closely by a DUPACK action?
* The optimal thing to do would be to remember all
* congestion actions from the most recent window
* of data. Otherwise "bugfix" might not prevent
* all unnecessary Fast Retransmits.
*/
reset_rtx_timer(1,0);
output(last_ack_ + 1, TCP_REASON_DUPACK);
return;
}
if (bug_fix_) {
/*
* The line below, for "bug_fix_" true, avoids
* problems with multiple fast retransmits in one
* window of data.
*/
return;
}
reno_action:
recover_ = maxseq_;
last_cwnd_action_ = CWND_ACTION_DUPACK;
slowdown(CLOSE_SSTHRESH_HALF|CLOSE_CWND_HALF);
reset_rtx_timer(1,0);
output(last_ack_ + 1, TCP_REASON_DUPACK); // from top
return;
}
void RenoTcpAgent::timeout(int tno)
{
if (tno == TCP_TIMER_RTX) {
dupwnd_ = 0;
dupacks_ = 0;
if (bug_fix_) recover_ = maxseq_;
TcpAgent::timeout(tno);
} else {
timeout_nonrtx(tno);
}
}
tcp-rfc793edu.cc
/*
* Copyright (c) 1999 International Computer Science Institute
* 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 ACIRI, the AT&T
* Center for Internet Research at ICSI (the International Computer
* Science Institute).
* 4. Neither the name of ACIRI nor of ICSI may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY ICSI 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 ICSI 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.
*/
#include
#include
#include
#include
#include "ip.h"
#include "tcp-rfc793edu.h"
#include "flags.h"
// Original code contributed by Fernando Cela Diaz,
// .
// For more information, see the following:
// URL "http://www.ce.chalmers.se/~fcela/tcp-tour.html"
static class RFC793eduTcpClass : public TclClass {
public:
RFC793eduTcpClass() : TclClass("Agent/TCP/RFC793edu") {}
TclObject* create(int, const char*const*) {
return (new RFC793eduTcpAgent());
}
} class_rfc793edu;
RFC793eduTcpAgent::RFC793eduTcpAgent() : TcpAgent()
{
}
void
RFC793eduTcpAgent::delay_bind_init_all()
{
delay_bind_init_one("add793expbackoff_");
delay_bind_init_one("add793jacobsonrtt_");
delay_bind_init_one("add793fastrtx_");
delay_bind_init_one("add793slowstart_");
delay_bind_init_one("add793additiveinc_");
delay_bind_init_one("add793karnrtt_");
delay_bind_init_one("add793exponinc_");
delay_bind_init_one("rto_");
TcpAgent::delay_bind_init_all();
reset();
}
int
RFC793eduTcpAgent::delay_bind_dispatch(const char *varName,
const char *localName,TclObject *tracer)
{
if (delay_bind_bool(varName, localName, "add793expbackoff_",
&add793expbackoff_, tracer))
return TCL_OK;
if (delay_bind_bool(varName, localName, "add793jacobsonrtt_",
&add793jacobsonrtt_, tracer))
return TCL_OK;
if (delay_bind_bool(varName, localName, "add793fastrtx_",
&add793fastrtx_, tracer))
return TCL_OK;
if (delay_bind_bool(varName, localName, "add793slowstart_",
&add793slowstart_, tracer))
return TCL_OK;
if (delay_bind_bool(varName, localName, "add793slowstart_",
&add793slowstart_, tracer))
return TCL_OK;
if (delay_bind_bool(varName, localName, "add793additiveinc_",
&add793additiveinc_, tracer))
return TCL_OK;
if (delay_bind_bool(varName, localName, "add793karnrtt_",
&add793karnrtt_, tracer))
return TCL_OK;
if (delay_bind_bool(varName, localName, "add793exponinc_",
&add793exponinc_, tracer))
return TCL_OK;
if (delay_bind(varName, localName, "rto_",
&rto_, tracer))
return TCL_OK;
return TcpAgent::delay_bind_dispatch(varName, localName, tracer);
}
void RFC793eduTcpAgent::reset()
{
//Reset here protected vars.
rto_ = rtxcur_init_ / tcp_tick_;
TcpAgent::reset();
}
void RFC793eduTcpAgent::rtt_update(double tao)
{
double now = Scheduler::instance().clock();
if (ts_option_)
t_rtt_ = int(tao /tcp_tick_ + 0.5);
else {
double sendtime = now - tao;
sendtime += boot_time_;
double tickoff = fmod(sendtime, tcp_tick_);
t_rtt_ = int((tao + tickoff) / tcp_tick_);
}
if (t_rtt_ < 1)
t_rtt_ = 1;
// Jacobson/Karels RTT estimation as implemented in tcp.cc
//
// Diference = SampleRTT - EstimatedRTT
// EstimatedRTT = EstimatedRTT + (delta * Difference)
// Deviation = Deviation + delta * (|Difference| - Deviation)
// TimeOut = EstimatedRTT + 4 * Deviation
// srtt has 3 bits to the right of the binary point
// rttvar has 2
if (t_srtt_ != 0) {
register short delta;
delta = t_rtt_ - (t_srtt_ >> T_SRTT_BITS); // d = (m - a0)
if ((t_srtt_ += delta) <= 0) // a1 = 7/8 a0 + 1/8 m
t_srtt_ = 1;
if (delta < 0)
delta = -delta;
delta -= (t_rttvar_ >> T_RTTVAR_BITS);
if ((t_rttvar_ += delta) <= 0) // var1 = 3/4 var0 + 1/4 |d|
t_rttvar_ = 1;
} else {
t_srtt_ = t_rtt_ << T_SRTT_BITS; // srtt = rtt
t_rttvar_ = t_rtt_ << (T_RTTVAR_BITS-1); // rttvar = rtt / 2
}
if (add793jacobsonrtt_)
{
//
// Current retransmit value is
// (unscaled) smoothed round trip estimate
// plus 2^rttvar_exp_ times (unscaled) rttvar.
//
rto_ = (((t_rttvar_ << (rttvar_exp_ + (T_SRTT_BITS - T_RTTVAR_BITS))) + t_srtt_) >> T_SRTT_BITS );
}
else {
// RFC 793
rto_ = (t_srtt_ >> ( T_SRTT_BITS - 1) );
}
t_rtxcur_ = rto_ * tcp_tick_;
return;
}
void RFC793eduTcpAgent::rtt_backoff()
{
if (add793expbackoff_) {
// Standard Tahoe Code
if (t_backoff_ < 64)
t_backoff_ <<= 1;
if (t_backoff_ > 8) {
/*
* If backed off this far, clobber the srtt
* value, storing it in the mean deviation
* instead.
*/
t_rttvar_ += (t_srtt_ >> T_SRTT_BITS);
t_srtt_ = 0;
}
}
// safe paranoia
else
t_backoff_ = 1;
}
void RFC793eduTcpAgent::recv(Packet *pkt, Handler*)
{
hdr_tcp *tcph = hdr_tcp::access(pkt);
#ifdef notdef
if (pkt->type_ != PT_ACK) {
Tcl::instance().evalf("%s error \"received non-ack\"",
name());
Packet::free(pkt);
return;
}
#endif
++nackpack_;
ts_peer_ = tcph->ts();
int ecnecho = hdr_flags::access(pkt)->ecnecho();
if (ecnecho && ecn_)
ecn(tcph->seqno());
recv_helper(pkt);
/* grow cwnd and check if the connection is done */
if (tcph->seqno() > last_ack_) {
recv_newack_helper(pkt);
if (last_ack_ == 0 && delay_growth_) {
cwnd_ = initial_window();
}
} else if (tcph->seqno() == last_ack_) {
if (hdr_flags::access(pkt)->eln_ && eln_) {
tcp_eln(pkt);
return;
}
// fast retransmission
if ( (++dupacks_ == NUMDUPACKS) && add793fastrtx_ ) {
dupack_action();
}
}
Packet::free(pkt);
/*
* Try to send more data.
*/
send_much(0, 0, maxburst_);
}
void RFC793eduTcpAgent::opencwnd()
{
if ( !(add793slowstart_ || add793exponinc_ || add793additiveinc_)) {
cwnd_ = wnd_;
} else if (((cwnd_ < ssthresh_) && (!add793additiveinc_))
|| add793exponinc_)
{
/* slow-start (exponential) */
if (add793exponinc_ && (cwnd_ < ssthresh_))
cwnd_ = ssthresh_;
else cwnd_ += 1;
} else {
/* linear */
double f;
if (add793additiveinc_ && (cwnd_ < ssthresh_))
cwnd_ = ssthresh_;
else {
switch (wnd_option_) {
case 0:
if (++count_ >= cwnd_) {
count_ = 0;
++cwnd_;
}
break;
case 1:
/* This is the standard algorithm. */
cwnd_ += 1 / cwnd_;
break;
case 2:
/* These are window increase algorithms
* for experimental purposes only. */
f = (t_srtt_ >> T_SRTT_BITS) * tcp_tick_;
f *= f;
f *= wnd_const_;
f += fcnt_;
if (f > cwnd_) {
fcnt_ = 0;
++cwnd_;
} else
fcnt_ = f;
break;
case 3:
f = awnd_;
f *= f;
f *= wnd_const_;
f += fcnt_;
if (f > cwnd_) {
fcnt_ = 0;
++cwnd_;
} else
fcnt_ = f;
break;
case 4:
f = awnd_;
f *= wnd_const_;
f += fcnt_;
if (f > cwnd_) {
fcnt_ = 0;
++cwnd_;
} else
fcnt_ = f;
break;
case 5:
f = (t_srtt_ >> T_SRTT_BITS) * tcp_tick_;
f *= wnd_const_;
f += fcnt_;
if (f > cwnd_) {
fcnt_ = 0;
++cwnd_;
} else
fcnt_ = f;
break;
default:
#ifdef notdef
/*XXX*/
error("illegal window option %d", wnd_option_);
#endif
abort();
}
}
}
// if maxcwnd_ is set (nonzero), make it the cwnd limit
if (maxcwnd_ && (int(cwnd_) > maxcwnd_))
cwnd_ = maxcwnd_;
return;
}
void RFC793eduTcpAgent::output(int seqno, int reason)
{
int force_set_rtx_timer = 0;
Packet* p = allocpkt();
hdr_tcp *tcph = hdr_tcp::access(p);
hdr_flags* hf = hdr_flags::access(p);
tcph->seqno() = seqno;
tcph->ts() = Scheduler::instance().clock();
tcph->ts_echo() = ts_peer_;
tcph->reason() = reason;
if (ecn_) {
hf->ect() = 1; // ECN-capable transport
}
if (cong_action_) {
hf->cong_action() = TRUE; // Congestion action.
cong_action_ = FALSE;
}
/* Check if this is the initial SYN packet. */
if (seqno == 0) {
if (syn_) {
hdr_cmn::access(p)->size() = tcpip_base_hdr_size_;
}
if (ecn_) {
hf->ecnecho() = 1;
// hf->cong_action() = 1;
hf->ect() = 0;
}
}
int bytes = hdr_cmn::access(p)->size();
/* if no outstanding data, be sure to set rtx timer again */
if (highest_ack_ == maxseq_)
force_set_rtx_timer = 1;
/* call helper function to fill in additional fields */
output_helper(p);
++ndatapack_;
ndatabytes_ += bytes;
send(p, 0);
if (seqno == curseq_ && seqno > maxseq_)
idle(); // Tell application I have sent everything so far
if (seqno > maxseq_) {
maxseq_ = seqno;
if (!rtt_active_) {
rtt_active_ = 1;
if (seqno > rtt_seq_) {
rtt_seq_ = seqno;
rtt_ts_ = Scheduler::instance().clock();
}
}
} else {
if (!add793karnrtt_) {
rtt_active_ = 1;
rtt_seq_ = seqno;
rtt_ts_ = Scheduler::instance().clock();
}
++nrexmitpack_;
nrexmitbytes_ += bytes;
}
if (!(rtx_timer_.status() == TIMER_PENDING) || force_set_rtx_timer)
/* No timer pending. Schedule one. */
set_rtx_timer();
}
void RFC793eduTcpAgent::recv_newack_helper(Packet *pkt) {
//hdr_tcp *tcph = hdr_tcp::access(pkt);
newack(pkt);
if (!ect_ || !hdr_flags::access(pkt)->ecnecho() ||
(old_ecn_ && ecn_burst_))
/* If "old_ecn", this is not the first ACK carrying ECN-Echo
* after a period of ACKs without ECN-Echo.
* Therefore, open the congestion window. */
opencwnd();
if (ect_) {
if (!hdr_flags::access(pkt)->ecnecho())
ecn_backoff_ = 0;
if (!ecn_burst_ && hdr_flags::access(pkt)->ecnecho())
ecn_burst_ = TRUE;
else if (ecn_burst_ && ! hdr_flags::access(pkt)->ecnecho())
ecn_burst_ = FALSE;
}
if (!ect_ && hdr_flags::access(pkt)->ecnecho() &&
!hdr_flags::access(pkt)->cong_action())
ect_ = 1;
/* if the connection is done, call finish() */
if ((highest_ack_ >= curseq_-1) && !closed_) {
closed_ = 1;
finish();
}
}
void RFC793eduTcpAgent::newack(Packet* pkt)
{
double now = Scheduler::instance().clock();
hdr_tcp *tcph = hdr_tcp::access(pkt);
/*
* Wouldn't it be better to set the timer *after*
* updating the RTT, instead of *before*?
*/
newtimer(pkt);
dupacks_ = 0;
last_ack_ = tcph->seqno();
highest_ack_ = last_ack_;
if (t_seqno_ < last_ack_ + 1)
t_seqno_ = last_ack_ + 1;
/*
* Update RTT only if it's OK to do so from info in the flags header.
* This is needed for protocols in which intermediate agents
* in the network intersperse acks (e.g., ack-reconstructors) for
* various reasons (without violating e2e semantics).
*/
hdr_flags *fh = hdr_flags::access(pkt);
if (!fh->no_ts_) {
if (ts_option_)
rtt_update(now - tcph->ts_echo());
if (rtt_active_ && tcph->seqno() >= rtt_seq_) {
if (!ect_ || !ecn_backoff_ ||
!hdr_flags::access(pkt)->ecnecho()) {
/*
* Don't end backoff if still in ECN-Echo with
* a congestion window of 1 packet.
*/
t_backoff_ = 1;
ecn_backoff_ = 0;
}
rtt_active_ = 0;
if (!ts_option_)
rtt_update(now - rtt_ts_);
}
}
/* update average window */
awnd_ *= 1.0 - wnd_th_;
awnd_ += wnd_th_ * cwnd_;
}
tcp-sack1.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1990, 1997 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Lawrence Berkeley Laboratory,
* Berkeley, CA. The name of the University may not be used to
* endorse or promote products derived from this software without
* specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (PSC)";
#endif
#include
#include
#include
#include "ip.h"
#include "tcp.h"
#include "flags.h"
#include "scoreboard.h"
#include "random.h"
#define TRUE 1
#define FALSE 0
#define RECOVER_DUPACK 1
#define RECOVER_TIMEOUT 2
#define RECOVER_QUENCH 3
class Sack1TcpAgent : public TcpAgent {
public:
Sack1TcpAgent();
virtual int window();
virtual void recv(Packet *pkt, Handler*);
void reset();
virtual void timeout(int tno);
virtual void dupack_action();
void plot();
virtual void send_much(int force, int reason, int maxburst);
protected:
u_char timeout_; /* boolean: sent pkt from timeout? */
u_char fastrecov_; /* boolean: doing fast recovery? */
int pipe_; /* estimate of pipe size (fast recovery) */
ScoreBoard scb_;
};
static class Sack1TcpClass : public TclClass {
public:
Sack1TcpClass() : TclClass("Agent/TCP/Sack1") {}
TclObject* create(int, const char*const*) {
return (new Sack1TcpAgent());
}
} class_sack;
int Sack1TcpAgent::window()
{
return(cwnd_ < wnd_ ? (int) cwnd_ : (int) wnd_);
}
Sack1TcpAgent::Sack1TcpAgent() : fastrecov_(FALSE), pipe_(-1)
{
}
void Sack1TcpAgent::reset ()
{
scb_.ClearScoreBoard();
TcpAgent::reset ();
}
void Sack1TcpAgent::recv(Packet *pkt, Handler*)
{
hdr_tcp *tcph = (hdr_tcp*)pkt->access(off_tcp_);
#ifdef notdef
if (pkt->type_ != PT_ACK) {
Tcl::instance().evalf("%s error \"received non-ack\"",
name());
Packet::free(pkt);
return;
}
#endif
++nackpack_;
int ecnecho = hdr_flags::access(pkt)->ecnecho();
if (ecnecho && ecn_)
ecn(tcph->seqno());
/*
* If DSACK is being used, check for DSACK blocks here.
* Possibilities: Check for unnecessary Fast Retransmits.
*/
if (!fastrecov_) {
/* normal... not fast recovery */
if ((int)tcph->seqno() > last_ack_) {
/*
* regular ACK not in fast recovery... normal
*/
recv_newack_helper(pkt);
timeout_ = FALSE;
scb_.ClearScoreBoard();
if (last_ack_ == 0 && delay_growth_) {
cwnd_ = initial_window();
}
} else if ((int)tcph->seqno() < last_ack_) {
/*NOTHING*/
} else if (timeout_ == FALSE) {
if (tcph->seqno() != last_ack_) {
fprintf(stderr, "pkt seq %d should be %d\n" ,
tcph->seqno(), last_ack_);
abort();
}
scb_.UpdateScoreBoard (last_ack_, tcph);
/*
* Check for a duplicate ACK
*/
if (++dupacks_ == NUMDUPACKS) {
/*
* Assume we dropped just one packet.
* Retransmit last ack + 1
* and try to resume the sequence.
*/
dupack_action();
} else if (dupacks_ < NUMDUPACKS && singledup_ ) {
/*
* A more cautious implementation would
* verify that the dupack reports the
* reception of new data...
*/
send_one();
}
}
if (dupacks_ == 0)
send_much(FALSE, 0, maxburst_);
} else {
/* we are in fast recovery */
--pipe_;
if ((int)tcph->seqno() >= recover_) {
/* ACK indicates fast recovery is over */
recover_ = 0;
fastrecov_ = FALSE;
newack(pkt);
/* if the connection is done, call finish() */
if ((highest_ack_ >= curseq_-1) && !closed_) {
closed_ = 1;
finish();
}
timeout_ = FALSE;
scb_.ClearScoreBoard();
/* New window: W/2 - K or W/2? */
} else if ((int)tcph->seqno() > highest_ack_) {
/* Not out of fast recovery yet.
* Update highest_ack_, but not last_ack_. */
--pipe_;
highest_ack_ = (int)tcph->seqno();
scb_.UpdateScoreBoard (highest_ack_, tcph);
t_backoff_ = 1;
newtimer(pkt);
} else if (timeout_ == FALSE) {
/* got another dup ack */
scb_.UpdateScoreBoard (last_ack_, tcph);
if (dupacks_ > 0)
dupacks_++;
}
send_much(FALSE, 0, maxburst_);
}
Packet::free(pkt);
#ifdef notyet
if (trace_)
plot();
#endif
}
void
Sack1TcpAgent::dupack_action()
{
int recovered = (highest_ack_ > recover_);
if (recovered || (!bug_fix_ && !ecn_)) {
goto sack_action;
}
if (ecn_ && last_cwnd_action_ == CWND_ACTION_ECN) {
last_cwnd_action_ = CWND_ACTION_DUPACK;
/*
* What if there is a DUPACK action followed closely by ECN
* followed closely by a DUPACK action?
* The optimal thing to do would be to remember all
* congestion actions from the most recent window
* of data. Otherwise "bugfix" might not prevent
* all unnecessary Fast Retransmits.
*/
reset_rtx_timer(1,0);
/*
* There are three possibilities:
* (1) pipe_ = int(cwnd_) - NUMDUPACKS;
* (2) pipe_ = window() - NUMDUPACKS;
* (3) pipe_ = maxseq_ - highest_ack_ - NUMDUPACKS;
* equation (2) takes into account the receiver's
* advertised window, and equation (3) takes into
* accout a data-limited sender.
*/
pipe_ = maxseq_ - highest_ack_ - NUMDUPACKS;
//pipe_ = int(cwnd_) - NUMDUPACKS;
fastrecov_ = TRUE;
scb_.MarkRetran(highest_ack_+1);
output(last_ack_ + 1, TCP_REASON_DUPACK);
return;
}
if (bug_fix_) {
/*
* The line below, for "bug_fix_" true, avoids
* problems with multiple fast retransmits in one
* window of data.
*/
return;
}
sack_action:
recover_ = maxseq_;
last_cwnd_action_ = CWND_ACTION_DUPACK;
pipe_ = int(cwnd_) - NUMDUPACKS;
slowdown(CLOSE_SSTHRESH_HALF|CLOSE_CWND_HALF);
reset_rtx_timer(1,0);
fastrecov_ = TRUE;
scb_.MarkRetran(highest_ack_+1);
output(last_ack_ + 1, TCP_REASON_DUPACK); // from top
/*
* If dynamically adjusting NUMDUPACKS, record information
* at this point.
*/
return;
}
void Sack1TcpAgent::timeout(int tno)
{
if (tno == TCP_TIMER_RTX) {
/*
* IF DSACK and dynamic adjustment of NUMDUPACKS,
* check whether a smaller value of NUMDUPACKS
* would have prevented this retransmit timeout.
* If DSACK and detection of premature retransmit
* timeouts, then save some info here.
*/
dupacks_ = 0;
fastrecov_ = FALSE;
timeout_ = TRUE;
if (highest_ack_ > last_ack_)
last_ack_ = highest_ack_;
#ifdef DEBUGSACK1A
printf ("timeout. highest_ack: %d seqno: %d\n",
highest_ack_, t_seqno_);
#endif
recover_ = maxseq_;
scb_.ClearScoreBoard();
}
TcpAgent::timeout(tno);
}
void Sack1TcpAgent::send_much(int force, int reason, int maxburst)
{
register int found, npacket = 0;
int win = window();
int xmit_seqno;
found = 1;
if (!force && delsnd_timer_.status() == TIMER_PENDING)
return;
/*
* as long as the pipe is open and there is app data to send...
*/
while (((!fastrecov_ && (t_seqno_ <= last_ack_ + win)) ||
(fastrecov_ && (pipe_ < int(cwnd_))))
&& t_seqno_ < curseq_ && found) {
if (overhead_ == 0 || force) {
found = 0;
xmit_seqno = scb_.GetNextRetran ();
#ifdef DEBUGSACK1A
printf("highest_ack: %d xmit_seqno: %d\n",
highest_ack_, xmit_seqno);
#endif
if (xmit_seqno == -1) {
if ((!fastrecov_ && t_seqno_<=highest_ack_+win)||
(fastrecov_ && t_seqno_<=highest_ack_+int(wnd_))) {
found = 1;
xmit_seqno = t_seqno_++;
#ifdef DEBUGSACK1A
printf("sending %d fastrecovery: %d win %d\n",
xmit_seqno, fastrecov_, win);
#endif
}
} else if (recover_>0 && xmit_seqno<=highest_ack_+int(wnd_)) {
found = 1;
scb_.MarkRetran (xmit_seqno);
win = window();
}
if (found) {
output(xmit_seqno, reason);
if (t_seqno_ <= xmit_seqno)
t_seqno_ = xmit_seqno + 1;
npacket++;
pipe_++;
}
} else if (!(delsnd_timer_.status() == TIMER_PENDING)) {
/*
* Set a delayed send timeout.
* This is only for the simulator,to add some
* randomization if speficied.
*/
delsnd_timer_.resched(Random::uniform(overhead_));
return;
}
if (maxburst && npacket == maxburst)
break;
} /* while */
}
void Sack1TcpAgent::plot()
{
#ifdef notyet
double t = Scheduler::instance().clock();
sprintf(trace_->buffer(), "t %g %d rtt %g\n",
t, class_, t_rtt_ * tcp_tick_);
trace_->dump();
sprintf(trace_->buffer(), "t %g %d dev %g\n",
t, class_, t_rttvar_ * tcp_tick_);
trace_->dump();
sprintf(trace_->buffer(), "t %g %d win %f\n", t, class_, cwnd_);
trace_->dump();
sprintf(trace_->buffer(), "t %g %d bck %d\n", t, class_, t_backoff_);
trace_->dump();
#endif
}
tcp-session.cc
/* -*- 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 Research Group 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.
*/
#include
#include
#include "ip.h"
#include "flags.h"
#include "random.h"
#include "template.h"
#include "nilist.h"
#include "tcp.h"
#include "tcp-int.h"
#include "tcp-session.h"
/*
* We separate TCP functionality into two parts: that having to do with
* providing a reliable, ordered byte-stream service, and that having to do with
* congestion control and loss recovery. The former is done on a per-connection
* basis and is implemented as part of IntTcpAgent ("integrated TCP"). The
* latter is done in an integrated fashion across multiple TCP connections, and
* is implemented as part of TcpSessionAgent ("TCP session"). TcpSessionAgent is
* derived from CorresHost ("correspondent host"), which keeps track of the
* state of all TCP (TCP/Int) connections to a host that it is corresponding
* with.
*
* The motivation for this separation of functionality is to make an ensemble of
* connection more well-behaved than a set of independent TCP connections.
* The packet loss rate is cut down and the chances of losses being recovered
* via data-driven techniques (rather than via timeouts) is improved. At the
* same time, we do not introduce any unnecessary coupling between the
* logically-independent byte-streams that the set of connections represents.
* This is in contrast to the coupling that is inherent in the multiplexing at
* the application layer of multiple byte-streams onto a single TCP connection.
*
* For questions/comments, please contact:
* Venkata N. Padmanabhan (padmanab@cs.berkeley.edu)
* http://www.cs.berkeley.edu/~padmanab
*/
static class TcpSessionClass : public TclClass {
public:
TcpSessionClass() : TclClass("Agent/TCP/Session") {}
TclObject* create(int, const char*const*) {
return (new TcpSessionAgent());
}
} class_tcpsession;
TcpSessionAgent::TcpSessionAgent() : CorresHost(),
rtx_timer_(this), burstsnd_timer_(this), sessionSeqno_(0),
last_send_time_(-1), curConn_(0), numConsecSegs_(0),
schedDisp_(FINE_ROUND_ROBIN), wtSum_(0), dynWtSum_(0)
{
bind("ownd_", &ownd_);
bind("owndCorr_", &owndCorrection_);
bind_bool("proxyopt_", &proxyopt_);
bind_bool("fixedIw_", &fixedIw_);
bind("schedDisp_", &schedDisp_);
bind_bool("disableIntLossRecov_", &disableIntLossRecov_);
sessionList_.append(this);
}
int
TcpSessionAgent::command(int argc, const char*const* argv)
{
if (argc == 2) {
if (!strcmp(argv[1], "resetwt")) {
Islist_iter conn_iter(conns_);
IntTcpAgent *tcp;
while ((tcp = conn_iter()) != NULL)
tcp->wt_ = 1;
wtSum_ = conn_iter.count();
return (TCL_OK);
}
}
return (CorresHost::command(argc, argv));
}
void
SessionRtxTimer::expire(Event*)
{
a_->timeout(TCP_TIMER_RTX);
}
void
SessionResetTimer::expire(Event*)
{
a_->timeout(TCP_TIMER_RESET);
}
void
SessionBurstSndTimer::expire(Event*)
{
a_->timeout(TCP_TIMER_BURSTSND);
}
void
TcpSessionAgent::reset_rtx_timer(int /*mild*/, int backoff)
{
if (backoff)
rtt_backoff();
set_rtx_timer();
rtt_active_ = 0;
}
void
TcpSessionAgent::set_rtx_timer()
{
if (rtx_timer_.status() == TIMER_PENDING)
rtx_timer_.cancel();
if (reset_timer_.status() == TIMER_PENDING)
reset_timer_.cancel();
if (fs_enable_ && fs_mode_)
reset_timer_.resched(rtt_exact_timeout());
else
rtx_timer_.resched(rtt_timeout());
}
void
TcpSessionAgent::cancel_rtx_timer()
{
rtx_timer_.force_cancel();
reset_timer_.force_cancel();
}
void
TcpSessionAgent::cancel_timers()
{
rtx_timer_.force_cancel();
reset_timer_.force_cancel();
burstsnd_timer_.force_cancel();
delsnd_timer_.force_cancel();
}
int
TcpSessionAgent::fs_pkt() {
return (fs_enable_ && fs_mode_ && sessionSeqno_-1 >= fs_startseq_ &&
sessionSeqno_-1 < fs_endseq_);
}
void
TcpSessionAgent::rtt_update_exact(double tao)
{
double g = 1/8; /* gain used for smoothing rtt */
double h = 1/4; /* gain used for smoothing rttvar */
double delta;
if (t_exact_srtt_ != 0) {
delta = tao - t_exact_srtt_;
if (delta < 0)
delta = -delta;
/* update the fine-grained estimate of the smoothed RTT */
if (t_exact_srtt_ != 0)
t_exact_srtt_ = g*tao + (1-g)*t_exact_srtt_;
else
t_exact_srtt_ = tao;
/* update the fine-grained estimate of mean deviation in RTT */
delta -= t_exact_rttvar_;
t_exact_rttvar_ += h*delta;
}
else {
t_exact_srtt_ = tao;
t_exact_rttvar_ = tao/2;
}
}
void
TcpSessionAgent::newack(Packet *pkt)
{
double now = Scheduler::instance().clock();
Islist_iter seg_iter(seglist_);
hdr_tcp *tcph = (hdr_tcp*)pkt->access(off_tcp_);
hdr_flags *fh = (hdr_flags *)pkt->access(off_flags_);
if (!fh->no_ts_) {
/* if the timestamp option is being used */
if (ts_option_) {
rtt_update(now - tcph->ts_echo());
rtt_update_exact(now - tcph->ts_echo());
}
/* if segment being timed just got acked */
if (rtt_active_ && rtt_seg_ == NULL) {
t_backoff_ = 1;
rtt_active_ = 0;
if (!ts_option_)
rtt_update(now - rtt_ts_);
}
}
if (seg_iter.count() > 0)
set_rtx_timer();
else
cancel_rtx_timer();
}
void
TcpSessionAgent::timeout(int tno)
{
if (tno == TCP_TIMER_BURSTSND)
send_much(NULL,0,0);
else if (tno == TCP_TIMER_RESET) {
Islist_iter seg_iter(seglist_);
Segment *curseg;
Islist_iter conn_iter(conns_);
IntTcpAgent *curconn;
fs_mode_ = 0;
if (seg_iter.count() == 0 && !slow_start_restart_) {
return;
}
recover_ = sessionSeqno_ - 1;
last_cwnd_action_ = CWND_ACTION_TIMEOUT;
ownd_ = 0;
owndCorrection_ = 0;
while ((curconn = conn_iter()) != NULL) {
curconn->maxseq_ = curconn->highest_ack_;
curconn->t_seqno_ = curconn->highest_ack_ + 1;
curconn->recover_ = curconn->maxseq_;
curconn->last_cwnd_action_ = CWND_ACTION_TIMEOUT;
}
while ((curseg = seg_iter()) != NULL) {
/* XXX exclude packets sent "recently"? */
curseg->size_ = 0;
}
/*
* If first pkt sent before fast start has not gotten through,
* treat this as a regular rtx timeout. Otherwise, close cwnd
* and reset timer but don't back off timer.
*/
if (connWithPktBeforeFS_) {
connWithPktBeforeFS_ = NULL;
timeout(TCP_TIMER_RTX);
}
else {
slowdown(CLOSE_CWND_INIT);
reset_rtx_timer(0,0);
send_much(NULL, 0, TCP_REASON_TIMEOUT);
}
}
else if (tno == TCP_TIMER_RTX) {
Islist_iter seg_iter(seglist_);
Segment *curseg;
Islist_iter conn_iter(conns_);
IntTcpAgent *curconn;
if (seg_iter.count() == 0 && !slow_start_restart_) {
return;
}
recover_ = sessionSeqno_ - 1;
last_cwnd_action_ = CWND_ACTION_TIMEOUT;
if (seg_iter.count() == 0 && restart_bugfix_) {
slowdown(CLOSE_CWND_INIT);
reset_rtx_timer(0,0);
}
else {
slowdown(CLOSE_CWND_RESTART|CLOSE_SSTHRESH_HALF);
reset_rtx_timer(0,1);
}
nrexmit_++;
ownd_ = 0;
owndCorrection_ = 0;
while ((curconn = conn_iter()) != NULL) {
curconn->t_seqno_ = curconn->highest_ack_ + 1;
curconn->recover_ = curconn->maxseq_;
curconn->last_cwnd_action_ = CWND_ACTION_TIMEOUT;
}
while ((curseg = seg_iter()) != NULL) {
/* XXX exclude packets sent "recently"? */
curseg->size_ = 0;
}
send_much(NULL, 0, TCP_REASON_TIMEOUT);
}
else
printf("TcpSessionAgent::timeout(): ignoring unknown timer %d\n", tno);
}
Segment*
TcpSessionAgent::add_pkts(int size, int seqno, int sessionSeqno, int daddr,
int dport, int sport, double ts, IntTcpAgent *sender)
{
/*
* set rtx timer afresh either if it is not set now or if there are no
* data packets outstanding at this time
*/
if (!(rtx_timer_.status() == TIMER_PENDING) || seglist_.count() == 0)
set_rtx_timer();
last_seg_sent_ = CorresHost::add_pkts(size, seqno, sessionSeqno, daddr, dport, sport, ts, sender);
return last_seg_sent_;
}
void
TcpSessionAgent::add_agent(IntTcpAgent *agent, int size, double winMult,
int winInc, int ssthresh)
{
CorresHost::add_agent(agent,size,winMult,winInc,ssthresh);
wtSum_ += agent->wt_;
reset_dyn_weights();
}
int
TcpSessionAgent::window()
{
if (maxcwnd_ == 0)
return (int(cwnd_));
else
return (int(min(cwnd_,maxcwnd_)));
}
void
TcpSessionAgent::set_weight(IntTcpAgent *tcp, int wt)
{
wtSum_ -= tcp->wt_;
tcp->wt_ = wt;
wtSum_ += tcp->wt_;
}
void
TcpSessionAgent::reset_dyn_weights()
{
IntTcpAgent *tcp;
Islist_iter conn_iter(conns_);
while ((tcp = conn_iter()) != NULL)
tcp->dynWt_ = tcp->wt_;
dynWtSum_ = wtSum_;
}
IntTcpAgent *
TcpSessionAgent::who_to_snd(int how)
{
int i = 0;
switch (how) {
/* fine-grained interleaving of connections (per pkt) */
case FINE_ROUND_ROBIN: {
IntTcpAgent *next;
int wtOK = 0;
if (dynWtSum_ == 0)
reset_dyn_weights();
do {
wtOK = 0;
if ((next = (*connIter_)()) == NULL) {
connIter_->set_cur(connIter_->get_last());
next = (*connIter_)();
}
i++;
if (next && next->dynWt_>0) {
next->dynWt_--;
dynWtSum_--;
wtOK = 1;
}
} while (next && (!next->data_left_to_send() || !wtOK)
&& (i < connIter_->count()));
if (!next->data_left_to_send())
next = NULL;
return next;
}
/* coarse-grained interleaving across connections (per block of pkts) */
case COARSE_ROUND_ROBIN: {
int maxConsecSegs;
if (curConn_)
maxConsecSegs = (window()*curConn_->wt_)/wtSum_;
if (curConn_ && numConsecSegs_++ < maxConsecSegs &&
curConn_->data_left_to_send())
return curConn_;
else {
numConsecSegs_ = 0;
curConn_ = who_to_snd(FINE_ROUND_ROBIN);
if (curConn_)
numConsecSegs_++;
}
return curConn_;
}
case RANDOM: {
IntTcpAgent *next;
do {
int foo = int(Random::uniform() * nActive_ + 1);
connIter_->set_cur(connIter_->get_last());
for (;foo > 0; foo--)
(*connIter_)();
next = (*connIter_)();
} while (next && !next->data_left_to_send());
return(next);
}
default:
return NULL;
}
}
void
TcpSessionAgent::send_much(IntTcpAgent* /*agent*/, int force, int reason)
{
int npackets = 0;
Islist_iter seg_iter(seglist_);
if (reason != TCP_REASON_TIMEOUT &&
burstsnd_timer_.status() == TIMER_PENDING)
return;
/* no outstanding data and idle time >= t_rtxcur_ */
if ((seg_iter.count() == 0) && (last_send_time_ != -1) &&
(Scheduler::instance().clock() - last_send_time_ >= t_rtxcur_)) {
if (slow_start_restart_ && restart_bugfix_)
slowdown(CLOSE_CWND_INIT);
else if (slow_start_restart_)
slowdown(CLOSE_CWND_RESTART|CLOSE_SSTHRESH_HALF);
else if (fs_enable_) {
if (cwnd_ < ssthresh_)
cwnd_ = int(cwnd_/2);
else
cwnd_ -= 1;
fs_startseq_ = sessionSeqno_ + 1;
fs_endseq_ = sessionSeqno_ + window();
fs_mode_ = 1;
}
}
while (ok_to_snd(size_)) {
{
IntTcpAgent *sender = who_to_snd(schedDisp_);
if (sender) {
/*
* remember the connection over which the first
* packet just before fast start is sent
*/
if (fs_enable_ && fs_mode_ &&
sessionSeqno_ == fs_startseq_)
connWithPktBeforeFS_ = sender;
/* if retransmission */
/* XXX we pick random conn even if rtx timeout */
if (sender->t_seqno_ < sender->maxseq_) {
int i =
findSessionSeqno(sender, sender->t_seqno_);
removeSessionSeqno(i);
sender->send_one(i);
}
else {
sender->send_one(sessionSeqno_++);
if (!rtt_active_) {
rtt_active_ = 1;
rtt_seg_ = last_seg_sent_;
}
}
npackets++;
}
else
break;
}
reason = 0;
force = 0;
if (maxburst_ && npackets == maxburst_) {
if (ok_to_snd(size_))
burstsnd_timer_.resched(t_exact_srtt_*maxburst_/window());
break;
}
}
if (npackets > 0)
last_send_time_ = Scheduler::instance().clock();
}
void
TcpSessionAgent::recv(IntTcpAgent *agent, Packet *pkt, int amt_data_acked)
{
hdr_tcp *tcph = (hdr_tcp *) pkt->access(off_tcp_);
if (((hdr_flags*)pkt->access(off_flags_))->ecnecho() && ecn_)
quench(1, agent, tcph->seqno());
clean_segs(size_, pkt, agent, sessionSeqno_,amt_data_acked);
/* XXX okay to do this after clean_segs? */
/* if new data acked and this is not a partial ack */
if (amt_data_acked > 0 && (tcph->seqno() >= agent->recover_ ||
agent->last_cwnd_action_ != CWND_ACTION_DUPACK /* XXX 1*/)
&& !dontIncrCwnd_) {
int i = count_bytes_acked_ ? amt_data_acked:1;
while (i-- > 0)
opencwnd(size_,agent);
}
dontIncrCwnd_ = 0;
if (amt_data_acked > 0) {
if (fs_enable_ && fs_mode_ && connWithPktBeforeFS_ == agent)
connWithPktBeforeFS_ = NULL;
newack(pkt);
}
Packet::free(pkt);
send_much(NULL,0,0);
}
void
TcpSessionAgent::setflags(Packet *pkt)
{
hdr_flags *hf = (hdr_flags *) pkt->access(off_flags_);
if (ecn_)
hf->ect() = 1;
}
int
TcpSessionAgent::findSessionSeqno(IntTcpAgent *sender, int seqno)
{
Islist_iter seg_iter(seglist_);
Segment *cur;
int min = sessionSeqno_;
while ((cur = seg_iter()) != NULL) {
if (sender == cur->sender_ && cur->seqno_ >= seqno &&
cur->sessionSeqno_ < min)
min = cur->sessionSeqno_;
}
if (min == sessionSeqno_) {
printf("In TcpSessionAgent::findSessionSeqno: search unsuccessful\n");
min = sessionSeqno_ - 1;
}
return (min);
}
void
TcpSessionAgent::removeSessionSeqno(int sessionSeqno)
{
Islist_iter seg_iter(seglist_);
Segment *cur, *prev=NULL;
while ((cur = seg_iter()) != NULL) {
if (cur->sessionSeqno_ == sessionSeqno) {
seglist_.remove(cur, prev);
adjust_ownd(cur->size_);
return;
}
prev = cur;
}
printf("In removeSessionSeqno(): unable to find segment with sessionSeqno = %d\n", sessionSeqno);
}
void
TcpSessionAgent::quench(int how, IntTcpAgent *sender, int seqno)
{
int i = findSessionSeqno(sender,seqno);
if (i > recover_) {
recover_ = sessionSeqno_ - 1;
last_cwnd_action_ = CWND_ACTION_ECN;
sender->recover_ = sender->maxseq_;
sender->last_cwnd_action_ = CWND_ACTION_ECN;
closecwnd(how,sender);
}
}
void
TcpSessionAgent::traceVar(TracedVar* v)
{
double curtime;
Scheduler& s = Scheduler::instance();
char wrk[500];
int n;
curtime = &s ? s.clock() : 0;
if (!strcmp(v->name(), "ownd_") || !strcmp(v->name(), "owndCorr_")) {
if (!strcmp(v->name(), "ownd_"))
sprintf(wrk,"%-8.5f %-2d %-2d %-2d %-2d %s %-6.3f",
curtime, addr(), port(), daddr(), dport(),
v->name(), double(*((TracedDouble*) v)));
else if (!strcmp(v->name(), "owndCorr_"))
sprintf(wrk,"%-8.5f %-2d %-2d %-2d %-2d %s %d",
curtime, addr(), port(), daddr(), dport(),
v->name(), int(*((TracedInt*) v)));
n = strlen(wrk);
wrk[n] = '\n';
wrk[n+1] = 0;
if (channel_)
(void)Tcl_Write(channel_, wrk, n+1);
wrk[n] = 0;
}
else
TcpAgent::traceVar(v);
}
tcp-sink.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1991-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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (LBL)";
#endif
#include "flags.h"
#include "ip.h"
#include "tcp-sink.h"
static class TcpSinkClass : public TclClass {
public:
TcpSinkClass() : TclClass("Agent/TCPSink") {}
TclObject* create(int, const char*const*) {
return (new TcpSink(new Acker));
}
} class_tcpsink;
Acker::Acker() : next_(0), maxseen_(0), ecn_unacked_(0), ts_to_echo_(0)
{
memset(seen_, 0, sizeof(seen_));
}
void Acker::reset()
{
next_ = 0;
maxseen_ = 0;
memset(seen_, 0, sizeof(seen_));
}
void Acker::update_ts(int seqno, double ts)
{
if (ts >= ts_to_echo_ && seqno <= next_)
ts_to_echo_ = ts;
}
// returns number of bytes that can be "delivered" to application
// also updates the receive window (i.e. next_, maxseen, and seen_ array)
int Acker::update(int seq, int numBytes)
{
bool just_marked_as_seen = FALSE;
is_dup_ = FALSE;
// start by assuming the segment hasn't been received before
if (numBytes <= 0)
printf("Error, received TCP packet size <= 0\n");
int numToDeliver = 0;
if (seq - next_ >= MWM) {
// next_ is next packet expected; MWM is the maximum
// window size minus 1; if somehow the seqno of the
// packet is greater than the one we're expecting+MWM,
// then ignore it.
return 0;
}
if (seq > maxseen_) {
// the packet is the highest one we've seen so far
int i;
for (i = maxseen_ + 1; i < seq; ++i)
seen_[i & MWM] = 0;
// we record the packets between the old maximum and
// the new max as being "unseen" i.e. 0 bytes of each
// packet have been received
maxseen_ = seq;
seen_[maxseen_ & MWM] = numBytes;
// store how many bytes have been seen for this packet
seen_[(maxseen_ + 1) & MWM] = 0;
// clear the array entry for the packet immediately
// after this one
just_marked_as_seen = TRUE;
// necessary so this packet isn't confused as being a duplicate
}
int next = next_;
if (seq < next) {
// Duplicate packet case 1: the packet is to the left edge of
// the receive window; therefore we must have seen it
// before
#ifdef DEBUGDSACK
printf("%f\t Received duplicate packet %d\n",Scheduler::instance().clock(),seq);
#endif
is_dup_ = TRUE;
}
if (seq >= next && seq <= maxseen_) {
// next is the left edge of the recv window; maxseen_
// is the right edge; execute this block if there are
// missing packets in the recv window AND if current
// packet falls within those gaps
if (seen_[seq & MWM] && !just_marked_as_seen) {
// Duplicate case 2: the segment has already been
// recorded as being received (AND not because we just
// marked it as such)
is_dup_ = TRUE;
#ifdef DEBUGDSACK
printf("%f\t Received duplicate packet %d\n",Scheduler::instance().clock(),seq);
#endif
}
seen_[seq & MWM] = numBytes;
// record the packet as being seen
while (seen_[next & MWM]) {
// this loop first gets executed if seq==next;
// i.e., this is the next packet in order that
// we've been waiting for. the loop sets how
// many bytes we can now deliver to the
// application, due to this packet arriving
// (and the prior arrival of any segments
// immediately to the right)
numToDeliver += seen_[next & MWM];
++next;
}
next_ = next;
// store the new left edge of the window
}
return numToDeliver;
}
TcpSink::TcpSink(Acker* acker) : Agent(PT_ACK), acker_(acker), save_(NULL)
{
/*
* maxSackBlocks_ does wierd tracing things.
* don't make it delay-bound yet.
*/
#if defined(TCP_DELAY_BIND_ALL) && 0
#else /* ! TCP_DELAY_BIND_ALL */
bind("maxSackBlocks_", &max_sack_blocks_); // used only by sack
#endif /* TCP_DELAY_BIND_ALL */
}
void
TcpSink::delay_bind_init_all()
{
delay_bind_init_one("packetSize_");
delay_bind_init_one("ts_echo_bugfix_");
delay_bind_init_one("generateDSacks_"); // used only by sack
delay_bind_init_one("RFC2581_immediate_ack_");
#if defined(TCP_DELAY_BIND_ALL) && 0
delay_bind_init_one("maxSackBlocks_");
#endif /* TCP_DELAY_BIND_ALL */
Agent::delay_bind_init_all();
}
int
TcpSink::delay_bind_dispatch(const char *varName, const char *localName, TclObject *tracer)
{
if (delay_bind(varName, localName, "packetSize_", &size_, tracer)) return TCL_OK;
if (delay_bind_bool(varName, localName, "ts_echo_bugfix_", &ts_echo_bugfix_, tracer)) return TCL_OK;
if (delay_bind_bool(varName, localName, "generateDSacks_", &generate_dsacks_, tracer)) return TCL_OK;
if (delay_bind_bool(varName, localName, "RFC2581_immediate_ack_", &RFC2581_immediate_ack_, tracer)) return TCL_OK;
#if defined(TCP_DELAY_BIND_ALL) && 0
if (delay_bind(varName, localName, "maxSackBlocks_", &max_sack_blocks_, tracer)) return TCL_OK;
#endif /* TCP_DELAY_BIND_ALL */
return Agent::delay_bind_dispatch(varName, localName, tracer);
}
void Acker::append_ack(hdr_cmn*, hdr_tcp*, int) const
{
}
void Acker::update_ecn_unacked(int value)
{
ecn_unacked_ = value;
}
int TcpSink::command(int argc, const char*const* argv)
{
if (argc == 2) {
if (strcmp(argv[1], "reset") == 0) {
reset();
return (TCL_OK);
}
}
return (Agent::command(argc, argv));
}
void TcpSink::reset()
{
acker_->reset();
save_ = NULL;
}
void TcpSink::ack(Packet* opkt)
{
Packet* npkt = allocpkt();
// opkt is the "old" packet that was received
// npkt is the "new" packet being constructed (for the ACK)
double now = Scheduler::instance().clock();
hdr_flags *sf;
hdr_tcp *otcp = hdr_tcp::access(opkt);
hdr_tcp *ntcp = hdr_tcp::access(npkt);
// get the tcp headers
ntcp->seqno() = acker_->Seqno();
// get the cumulative sequence number to put in the ACK; this
// is just the left edge of the receive window - 1
ntcp->ts() = now;
// timestamp the packet
if (ts_echo_bugfix_) /* TCP/IP Illustrated, Vol. 2, pg. 870 */
ntcp->ts_echo() = acker_->ts_to_echo();
else
ntcp->ts_echo() = otcp->ts();
// echo the original's time stamp
hdr_ip* oip = (hdr_ip*)opkt->access(off_ip_);
hdr_ip* nip = (hdr_ip*)npkt->access(off_ip_);
// get the ip headers
nip->flowid() = oip->flowid();
// copy the flow id
hdr_flags* of = (hdr_flags*)opkt->access(off_flags_);
hdr_flags* nf = (hdr_flags*)npkt->access(off_flags_);
if (save_ != NULL)
sf = (hdr_flags*)save_->access(off_flags_);
// Look at delayed packet being acked.
if ( (save_ != NULL && sf->cong_action()) || of->cong_action() )
// Sender has responsed to congestion.
acker_->update_ecn_unacked(0);
if ( (save_ != NULL && sf->ect() && sf->ce()) ||
(of->ect() && of->ce()) )
// New report of congestion.
acker_->update_ecn_unacked(1);
if ( (save_ != NULL && sf->ect()) || of->ect() )
// Set EcnEcho bit.
nf->ecnecho() = acker_->ecn_unacked();
if (!of->ect() && of->ecnecho() ||
(save_ != NULL && !sf->ect() && sf->ecnecho()) )
// This is the negotiation for ECN-capability.
// We are not checking for of->cong_action() also.
// In this respect, this does not conform to the
// specifications in the internet draft
nf->ecnecho() = 1;
acker_->append_ack((hdr_cmn*)npkt->access(off_cmn_),
ntcp, otcp->seqno());
add_to_ack(npkt);
// the above function is used in TcpAsymSink
send(npkt, 0);
// send it
}
void TcpSink::add_to_ack(Packet*)
{
return;
}
void TcpSink::recv(Packet* pkt, Handler*)
{
int numToDeliver;
int numBytes = ((hdr_cmn*)pkt->access(off_cmn_))->size();
// number of bytes in the packet just received
hdr_tcp *th = hdr_tcp::access(pkt);
acker_->update_ts(th->seqno(),th->ts());
// update the timestamp to echo
numToDeliver = acker_->update(th->seqno(), numBytes);
// update the recv window; figure out how many in-order-bytes
// (if any) can be removed from the window and handed to the
// application
if (numToDeliver)
recvBytes(numToDeliver);
// send any packets to the application
ack(pkt);
// ACK the packet
Packet::free(pkt);
// remove it from the system
}
static class DelSinkClass : public TclClass {
public:
DelSinkClass() : TclClass("Agent/TCPSink/DelAck") {}
TclObject* create(int, const char*const*) {
return (new DelAckSink(new Acker));
}
} class_delsink;
DelAckSink::DelAckSink(Acker* acker) : TcpSink(acker), delay_timer_(this)
{
bind_time("interval_", &interval_);
}
void DelAckSink::recv(Packet* pkt, Handler*)
{
int numToDeliver;
int numBytes = ((hdr_cmn*)pkt->access(off_cmn_))->size();
hdr_tcp *th = hdr_tcp::access(pkt);
acker_->update_ts(th->seqno(),th->ts());
numToDeliver = acker_->update(th->seqno(), numBytes);
if (numToDeliver)
recvBytes(numToDeliver);
// If there's no timer and the packet is in sequence, set a timer.
// Otherwise, send the ack and update the timer.
if (delay_timer_.status() != TIMER_PENDING &&
th->seqno() == acker_->Seqno()) {
// There's no timer, so we can set one and choose
// to delay this ack.
// If we're following RFC2581 (section 4.2) exactly,
// we should only delay the ACK if we're know we're
// not doing recovery, i.e. not gap-filling.
// Since this is a change to previous ns behaviour,
// it's controlled by an optional bound flag.
// discussed April 2000 in the ns-users list archives.
if (RFC2581_immediate_ack_ &&
(th->seqno() < acker_->Maxseen())) {
// don't delay the ACK since
// we're filling in a gap
} else {
// delay the ACK and start the timer.
save_ = pkt;
delay_timer_.resched(interval_);
return;
}
}
// If there was a timer, turn it off.
if (delay_timer_.status() == TIMER_PENDING)
delay_timer_.cancel();
ack(pkt);
if (save_ != NULL) {
Packet::free(save_);
save_ = NULL;
}
Packet::free(pkt);
}
void DelAckSink::timeout(int)
{
// The timer expired so we ACK the last packet seen.
Packet* pkt = save_;
ack(pkt);
save_ = NULL;
Packet::free(pkt);
}
void DelayTimer::expire(Event* /*e*/) {
a_->timeout(0);
}
/* "sack1-tcp-sink" is for Matt and Jamshid's implementation of sack. */
class SackStack {
protected:
int size_;
int cnt_;
struct Sf_Entry {
int left_;
int right_;
} *SFE_;
public:
SackStack(int); // create a SackStack of size (int)
~SackStack();
int& head_right(int n = 0) { return SFE_[n].right_; }
int& head_left(int n = 0) { return SFE_[n].left_; }
int cnt() { return cnt_; } // how big is the stack
void reset() {
register int i;
for (i = 0; i < cnt_; i++)
SFE_[i].left_ = SFE_[i].right_ = -1;
cnt_ = 0;
}
inline void push(int n = 0) {
if (cnt_ >= size_) cnt_ = size_ - 1; // overflow check
register int i;
for (i = cnt_-1; i >= n; i--)
SFE_[i+1] = SFE_[i]; // not efficient for big size
cnt_++;
}
inline void pop(int n = 0) {
register int i;
for (i = n; i < cnt_-1; i++)
SFE_[i] = SFE_[i+1]; // not efficient for big size
SFE_[i].left_ = SFE_[i].right_ = -1;
cnt_--;
}
};
SackStack::SackStack(int sz)
{
register int i;
size_ = sz;
SFE_ = new Sf_Entry[sz];
for (i = 0; i < sz; i++)
SFE_[i].left_ = SFE_[i].right_ = -1;
cnt_ = 0;
}
SackStack::~SackStack()
{
delete SFE_;
}
static class Sack1TcpSinkClass : public TclClass {
public:
Sack1TcpSinkClass() : TclClass("Agent/TCPSink/Sack1") {}
TclObject* create(int, const char*const*) {
Sacker* sacker = new Sacker;
TcpSink* sink = new TcpSink(sacker);
sacker->configure(sink);
return (sink);
}
} class_sack1tcpsink;
static class Sack1DelAckTcpSinkClass : public TclClass {
public:
Sack1DelAckTcpSinkClass() : TclClass("Agent/TCPSink/Sack1/DelAck") {}
TclObject* create(int, const char*const*) {
Sacker* sacker = new Sacker;
TcpSink* sink = new DelAckSink(sacker);
sacker->configure(sink);
return (sink);
}
} class_sack1delacktcpsink;
void Sacker::configure(TcpSink *sink)
{
if (sink == NULL) {
fprintf(stderr, "warning: Sacker::configure(): no TCP sink!\n");
return;
}
TracedInt& nblocks = sink->max_sack_blocks_;
if (int(nblocks) > NSA) {
fprintf(stderr, "warning(Sacker::configure): TCP header limits number of SACK blocks to %d, not %d\n", NSA, int(nblocks));
nblocks = NSA;
}
sf_ = new SackStack(int(nblocks));
nblocks.tracer(this);
base_nblocks_ = int(nblocks);
dsacks_ = &(sink->generate_dsacks_);
}
void
Sacker::trace(TracedVar *v)
{
// we come here if "nblocks" changed
TracedInt* ti = (TracedInt*) v;
if (int(*ti) > NSA) {
fprintf(stderr, "warning(Sacker::trace): TCP header limits number of SACK blocks to %d, not %d\n", NSA, int(*ti));
*ti = NSA;
}
int newval = int(*ti);
delete sf_;
sf_ = new SackStack(newval);
base_nblocks_ = newval;
}
void Sacker::reset()
{
sf_->reset();
Acker::reset();
}
Sacker::~Sacker()
{
delete sf_;
}
void Sacker::append_ack(hdr_cmn* ch, hdr_tcp* h, int old_seqno) const
{
// ch and h are the common and tcp headers of the Ack being constructed
// old_seqno is the sequence # of the packet we just got
int sack_index, i, sack_right, sack_left;
int recent_sack_left, recent_sack_right;
int seqno = Seqno();
// the last in-order packet seen (i.e. the cumulative ACK # - 1)
sack_index = 0;
sack_left = sack_right = -1;
// initialization; sack_index=0 and sack_{left,right}= -1
if (old_seqno < 0) {
printf("Error: invalid packet number %d\n", old_seqno);
} else if (seqno >= maxseen_ && (sf_->cnt() != 0))
sf_->reset();
// if the Cumulative ACK seqno is at or beyond the right edge
// of the window, and if the SackStack is not empty, reset it
// (empty it)
else if (( (seqno < maxseen_) || is_dup_ ) && (base_nblocks_ > 0)) {
// Otherwise, if the received packet is to the left of
// the right edge of the receive window (but not at
// the right edge), OR if it is a duplicate, AND we
// can have 1 or more Sack blocks, then execute the
// following, which computes the most recent Sack
// block
if ((*dsacks_) && is_dup_) {
// Record the DSACK Block
h->sa_left(sack_index) = old_seqno;
h->sa_right(sack_index) = old_seqno+1;
// record the block
sack_index++;
#ifdef DEBUGDSACK
printf("%f\t Generating D-SACK for packet %d\n", Scheduler::instance().clock(),old_seqno);
#endif
}
// Build FIRST (traditional) SACK block
// If we already had a DSACK block due to a duplicate
// packet, and if that duplicate packet is in the
// receiver's window (i.e. the packet's sequence
// number is > than the cumulative ACK) then the
// following should find the SACK block it's a subset
// of. If it's <= cum ACK field then the following
// shouldn't record a superset SACK block for it.
if (sack_index >= base_nblocks_) {
printf("Error: can't use DSACK with less than 2 SACK blocks\n");
} else {
sack_right=-1;
// look rightward for first hole
// start at the current packet
for (i=old_seqno; i<=maxseen_; i++) {
if (!seen_[i & MWM]) {
sack_right=i;
break;
}
}
// if there's no hole set the right edge of the sack
// to be the next expected packet
if (sack_right == -1) {
sack_right = maxseen_+1;
}
// if the current packet's seqno is smaller than the
// left edge of the window, set the sack_left to 0
if (old_seqno <= seqno) {
sack_left = 0;
// don't record/send the block
} else {
// look leftward from right edge for first hole
for (i = sack_right-1; i > seqno; i--) {
if (!seen_[i & MWM]) {
sack_left = i+1;
break;
}
}
h->sa_left(sack_index) = sack_left;
h->sa_right(sack_index) = sack_right;
// record the block
sack_index++;
}
recent_sack_left = sack_left;
recent_sack_right = sack_right;
// first sack block is built, check the others
// make sure that if max_sack_blocks has been made
// large from tcl we don't over-run the stuff we
// allocated in Sacker::Sacker()
int k = 0;
while (sack_index < base_nblocks_) {
sack_left = sf_->head_left(k);
sack_right = sf_->head_right(k);
// no more history
if (sack_left < 0 || sack_right < 0 ||
sack_right > maxseen_ + 1)
break;
// newest ack "covers up" this one
if (recent_sack_left <= sack_left &&
recent_sack_right >= sack_right) {
sf_->pop(k);
continue;
}
h->sa_left(sack_index) = sack_left;
h->sa_right(sack_index) = sack_right;
// store the old sack (i.e. move it down one)
sack_index++;
k++;
}
if (old_seqno > seqno) {
/* put most recent block onto stack */
sf_->push();
// this just moves things down 1 from the
// beginning, but it doesn't push any values
// on the stack
sf_->head_left() = recent_sack_left;
sf_->head_right() = recent_sack_right;
// this part stores the left/right values at
// the top of the stack (slot 0)
}
} // this '}' is for the DSACK base_nblocks_ >= test;
// (didn't feel like re-indenting all the code and
// causing a large diff)
}
h->sa_length() = sack_index;
// set the Length of the sack stack in the header
ch->size() += sack_index * 8;
// change the size of the common header to account for the
// Sack strings (2 4-byte words for each element)
}
tcp-vegas.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1997 University of Southern California.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation, advertising
* materials, and other materials related to such distribution and use
* acknowledge that the software was developed by the University of
* Southern California, Information Sciences Institute. The name of the
* University may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
**
* ns-1 implementation:
*
* This is an implementation of U. of Arizona's TCP Vegas. I implemented
* it based on USC's NetBSD-Vegas.
* Ted Kuo
* North Carolina St. Univ. and
* Networking Software Div, IBM
* tkuo@eos.ncsu.edu
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (NCSU/IBM)";
#endif
#include
#include
#include
#include "ip.h"
#include "tcp.h"
#include "flags.h"
#define MIN(x, y) ((x)<(y) ? (x) : (y))
static class VegasTcpClass : public TclClass {
public:
VegasTcpClass() : TclClass("Agent/TCP/Vegas") {}
TclObject* create(int, const char*const*) {
return (new VegasTcpAgent());
}
} class_vegas;
VegasTcpAgent::VegasTcpAgent() : TcpAgent()
{
}
void
VegasTcpAgent::delay_bind_init_all()
{
delay_bind_init_one("v_alpha_");
delay_bind_init_one("v_beta_");
delay_bind_init_one("v_gamma_");
delay_bind_init_one("v_rtt_");
TcpAgent::delay_bind_init_all();
reset();
}
int
VegasTcpAgent::delay_bind_dispatch(const char *varName, const char *localName,
TclObject *tracer)
{
/* init vegas var */
if (delay_bind(varName, localName, "v_alpha_", &v_alpha_, tracer))
return TCL_OK;
if (delay_bind(varName, localName, "v_beta_", &v_beta_, tracer))
return TCL_OK;
if (delay_bind(varName, localName, "v_gamma_", &v_gamma_, tracer))
return TCL_OK;
if (delay_bind(varName, localName, "v_rtt_", &v_rtt_, tracer))
return TCL_OK;
return TcpAgent::delay_bind_dispatch(varName, localName, tracer);
}
void
VegasTcpAgent::reset()
{
t_cwnd_changed_ = 0.;
firstrecv_ = -1.0;
v_slowstart_ = 2;
v_sa_ = 0;
v_sd_ = 0;
v_timeout_ = 1000.;
v_worried_ = 0;
v_begseq_ = 0;
v_begtime_ = 0.;
v_cntRTT_ = 0; v_sumRTT_ = 0.;
v_baseRTT_ = 1000000000.;
v_incr_ = 0;
v_inc_flag_ = 1;
TcpAgent::reset();
}
void
VegasTcpAgent::recv_newack_helper(Packet *pkt)
{
//hdr_tcp *tcph = (hdr_tcp*)pkt->access(off_tcp_);
newack(pkt);
#if 0
// like TcpAgent::recv_newack_helper, but without this
if ( !((hdr_flags*)pkt->access(off_flags_))->ecnecho() || !ecn_ ) {
opencwnd();
}
#endif
/* if the connection is done, call finish() */
if ((highest_ack_ >= curseq_-1) && !closed_) {
closed_ = 1;
finish();
}
}
void
VegasTcpAgent::recv(Packet *pkt, Handler *)
{
double currentTime = vegastime();
hdr_tcp *tcph = (hdr_tcp*)pkt->access(off_tcp_);
hdr_flags *flagh = (hdr_flags*)pkt->access(off_flags_);
#if 0
if (pkt->type_ != PT_ACK) {
Tcl::instance().evalf("%s error \"recieved non-ack\"",
name());
Packet::free(pkt);
return;
}
#endif /* 0 */
++nackpack_;
if(firstrecv_<0) { // init vegas rtt vars
firstrecv_ = currentTime;
v_baseRTT_ = v_rtt_ = firstrecv_;
v_sa_ = v_rtt_ * 8.;
v_sd_ = v_rtt_;
v_timeout_ = ((v_sa_/4.)+v_sd_)/2.;
}
if (flagh->ecnecho())
ecn(tcph->seqno());
if (tcph->seqno() > last_ack_) {
if (last_ack_ == 0 && delay_growth_) {
cwnd_ = initial_window();
}
/* check if cwnd has been inflated */
if(dupacks_ > NUMDUPACKS && cwnd_ > v_newcwnd_) {
cwnd_ = v_newcwnd_;
// vegas ssthresh is used only during slow-start
ssthresh_ = 2;
}
int oldack = last_ack_;
recv_newack_helper(pkt);
/*
* begin of once per-rtt actions
* 1. update path fine-grained rtt and baseRTT
* 2. decide what to do with cwnd_, inc/dec/unchanged
* based on delta=expect - actual.
*/
if(tcph->seqno() >= v_begseq_) {
double rtt;
if(v_cntRTT_ > 0)
rtt = v_sumRTT_ / v_cntRTT_;
else
rtt = currentTime - v_begtime_;
v_sumRTT_ = 0.0;
v_cntRTT_ = 0;
// calc # of packets in transit
int rttLen = t_seqno_ - v_begseq_;
/*
* decide should we incr/decr cwnd_ by how much
*/
if(rtt>0) {
/* if there's only one pkt in transit, update
* baseRTT
*/
if(rtt v_gamma_) {
// slow-down a bit to ensure
// the net is not so congested
ssthresh_ = 2;
cwnd_-=(cwnd_/8);
if(cwnd_<2)
cwnd_ = 2.;
v_incr_ = 0;
} else
v_incr_ = 1;
}
} else { // congestion avoidance
if(delta>v_beta_) {
/*
* slow down a bit, retrack
* back to prev. rtt's cwnd
* and dont incr in the nxt rtt
*/
--cwnd_;
if(cwnd_<2) cwnd_ = 2;
v_incr_ = 0;
} else if(delta 0)
// tag the next packet
v_begseq_ = t_seqno_;
v_begtime_ = currentTime;
} // end of once per-rtt section
/* since we set how much to incr only once per rtt,
* need to check if we surpass ssthresh during slow-start
* before the rtt is over.
*/
if(v_incr_ == 1 && cwnd_ >= ssthresh_)
v_incr_ = 0;
/*
* incr cwnd unless we havent been able to keep up with it
*/
if(v_incr_>0 && (cwnd_-(t_seqno_-last_ack_))<=2)
cwnd_ = cwnd_+v_incr_;
/*
* See if we need to update the fine grained timeout value,
* v_timeout_
*/
// reset v_sendtime for acked pkts and incr v_transmits_
double sendTime = v_sendtime_[tcph->seqno()%v_maxwnd_];
int transmits = v_transmits_[tcph->seqno()% v_maxwnd_];
int range = tcph->seqno() - oldack;
for(int k=((oldack+1) %v_maxwnd_); \
k<=(tcph->seqno()%v_maxwnd_) && range >0 ; \
k=((++k) % v_maxwnd_), range--) {
v_sendtime_[k] = -1.0;
v_transmits_[k] = 0;
}
if((sendTime !=0.) && (transmits==1)) {
// update fine-grained timeout value, v_timeout_.
double rtt, n;
rtt = currentTime - sendTime;
v_sumRTT_ += rtt;
++v_cntRTT_;
if(rtt>0) {
v_rtt_ = rtt;
if(v_rtt_ < v_baseRTT_)
v_baseRTT_ = v_rtt_;
n = v_rtt_ - v_sa_/8;
v_sa_ += n;
n = n<0 ? -n : n;
n -= v_sd_ / 4;
v_sd_ += n;
v_timeout_ = ((v_sa_/4)+v_sd_)/2;
v_timeout_ += (v_timeout_/16);
}
}
/*
* check the 1st or 2nd acks after dup ack received
*/
if(v_worried_>0) {
/*
* check if any pkt has been timeout. if so,
* retx it. no need to change cwnd since we
* already did.
*/
--v_worried_;
int expired=vegas_expire(pkt);
if(expired>=0) {
dupacks_ = NUMDUPACKS;
output(expired, TCP_REASON_DUPACK);
} else
v_worried_ = 0;
}
} else if (tcph->seqno() == last_ack_) {
/* check if a timeout should happen */
++dupacks_;
int expired=vegas_expire(pkt);
if (expired>=0 || dupacks_ == NUMDUPACKS) {
double sendTime=v_sendtime_[(last_ack_+1) % v_maxwnd_];
int transmits=v_transmits_[(last_ack_+1) % v_maxwnd_];
/* The line below, for "bug_fix_" true, avoids
* problems with multiple fast retransmits after
* a retransmit timeout.
*/
if ( !bug_fix_ || (highest_ack_ > recover_) || \
( last_cwnd_action_ != CWND_ACTION_TIMEOUT)) {
int win = window();
last_cwnd_action_ = CWND_ACTION_DUPACK;
recover_ = maxseq_;
/* check for timeout after recv a new ack */
v_worried_ = MIN(2, t_seqno_ - last_ack_ );
/* v_rto expon. backoff */
if(transmits > 1)
v_timeout_ *=2.;
else
v_timeout_ += (v_timeout_/8.);
/*
* if cwnd hasnt changed since the pkt was sent
* we need to decr it.
*/
if(t_cwnd_changed_ < sendTime ) {
if(win<=3)
win=2;
else if(transmits > 1)
win >>=1;
else
win -= (win>>2);
// record cwnd_
v_newcwnd_ = double(win);
// inflate cwnd_
cwnd_ = v_newcwnd_ + dupacks_;
t_cwnd_changed_ = currentTime;
}
// update coarser grained rto
reset_rtx_timer(1);
if(expired>=0)
output(expired, TCP_REASON_DUPACK);
else
output(last_ack_ + 1, TCP_REASON_DUPACK);
if(transmits==1)
dupacks_ = NUMDUPACKS;
}
} else if (dupacks_ > NUMDUPACKS)
++cwnd_;
}
Packet::free(pkt);
#if 0
if (trace_)
plot();
#endif /* 0 */
/*
* Try to send more data
*/
if (dupacks_ == 0 || dupacks_ > NUMDUPACKS - 1)
send_much(0, 0, maxburst_);
}
void
VegasTcpAgent::timeout(int tno)
{
if (tno == TCP_TIMER_RTX) {
if (highest_ack_ == maxseq_ && !slow_start_restart_) {
/*
* TCP option:
* If no outstanding data, then don't do anything.
*
* Note: in the USC implementation,
* slow_start_restart_ == 0.
* I don't know what the U. Arizona implementation
* defaults to.
*/
return;
};
dupacks_ = 0;
recover_ = maxseq_;
last_cwnd_action_ = CWND_ACTION_TIMEOUT;
reset_rtx_timer(0);
++nrexmit_;
slowdown(CLOSE_CWND_RESTART|CLOSE_SSTHRESH_HALF);
cwnd_ = double(v_slowstart_);
v_newcwnd_ = 0;
t_cwnd_changed_ = vegastime();
send_much(0, TCP_REASON_TIMEOUT);
} else {
/* delayed-sent timer, with random overhead to avoid
* phase effect. */
send_much(1, TCP_REASON_TIMEOUT);
};
}
void
VegasTcpAgent::output(int seqno, int reason)
{
Packet* p = allocpkt();
hdr_tcp *tcph = (hdr_tcp*)p->access(off_tcp_);
double now = Scheduler::instance().clock();
tcph->seqno() = seqno;
tcph->ts() = now;
tcph->reason() = reason;
/* if this is the 1st pkt, setup senttime[] and transmits[]
* I alloc mem here, instrad of in the constructor, to cover
* cases which windows get set by each different tcp flows */
if (seqno==0) {
v_maxwnd_ = int(wnd_);
v_sendtime_ = new double[v_maxwnd_];
v_transmits_ = new int[v_maxwnd_];
for(int i=0;isize();
ndatabytes_ += bytes;
send(p, 0);
if (seqno == curseq_ && seqno > maxseq_)
idle(); // Tell application I have sent everything so far
if (seqno > maxseq_) {
maxseq_ = seqno;
if (!rtt_active_) {
rtt_active_ = 1;
if (seqno > rtt_seq_) {
rtt_seq_ = seqno;
rtt_ts_ = now;
}
}
}
if (!(rtx_timer_.status() == TIMER_PENDING))
/* No timer pending. Schedule one. */
set_rtx_timer();
}
/*
* return -1 if the oldest sent pkt has not been timeout (based on
* fine grained timer).
*/
int
VegasTcpAgent::vegas_expire(Packet* pkt)
{
hdr_tcp *tcph = (hdr_tcp*)pkt->access(off_tcp_);
double elapse = vegastime() - v_sendtime_[(tcph->seqno()+1)%v_maxwnd_];
if (elapse >= v_timeout_) {
return(tcph->seqno()+1);
}
return(-1);
}
tcp.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1991-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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (LBL)";
#endif
#include
#include
#include "ip.h"
#include "tcp.h"
#include "flags.h"
#include "random.h"
int hdr_tcp::offset_;
static class TCPHeaderClass : public PacketHeaderClass {
public:
TCPHeaderClass() : PacketHeaderClass("PacketHeader/TCP",
sizeof(hdr_tcp)) {
bind_offset(&hdr_tcp::offset_);
}
} class_tcphdr;
static class TcpClass : public TclClass {
public:
TcpClass() : TclClass("Agent/TCP") {}
TclObject* create(int , const char*const*) {
return (new TcpAgent());
}
} class_tcp;
TcpAgent::TcpAgent() : Agent(PT_TCP),
t_seqno_(0), t_rtt_(0), t_srtt_(0), t_rttvar_(0),
t_backoff_(0), ts_peer_(0),
rtx_timer_(this), delsnd_timer_(this),
burstsnd_timer_(this),
dupacks_(0), curseq_(0), highest_ack_(0), cwnd_(0), ssthresh_(0),
count_(0), fcnt_(0), rtt_active_(0), rtt_seq_(-1), rtt_ts_(0.0),
maxseq_(0), cong_action_(0), ecn_burst_(0), ecn_backoff_(0),
ect_(0), restart_bugfix_(1), closed_(0), nrexmit_(0),
first_decrease_(1)
{
off_ip_ = hdr_ip::offset();
off_tcp_ = hdr_tcp::offset();
#ifdef TCP_DELAY_BIND_ALL
#else /* ! TCP_DELAY_BIND_ALL */
// not delay-bound because delay-bound tracevars aren't yet supported
bind("t_seqno_", &t_seqno_);
bind("rtt_", &t_rtt_);
bind("srtt_", &t_srtt_);
bind("rttvar_", &t_rttvar_);
bind("backoff_", &t_backoff_);
bind("dupacks_", &dupacks_);
bind("seqno_", &curseq_);
bind("ack_", &highest_ack_);
bind("cwnd_", &cwnd_);
bind("ssthresh_", &ssthresh_);
bind("maxseq_", &maxseq_);
bind("ndatapack_", &ndatapack_);
bind("ndatabytes_", &ndatabytes_);
bind("nackpack_", &nackpack_);
bind("nrexmit_", &nrexmit_);
bind("nrexmitpack_", &nrexmitpack_);
bind("nrexmitbytes_", &nrexmitbytes_);
bind("singledup_", &singledup_);
#endif /* TCP_DELAY_BIND_ALL */
}
void
TcpAgent::delay_bind_init_all()
{
// Defaults for bound variables should be set in ns-default.tcl.
delay_bind_init_one("window_");
delay_bind_init_one("windowInit_");
delay_bind_init_one("windowInitOption_");
delay_bind_init_one("syn_");
delay_bind_init_one("windowOption_");
delay_bind_init_one("windowConstant_");
delay_bind_init_one("windowThresh_");
delay_bind_init_one("delay_growth_");
delay_bind_init_one("overhead_");
delay_bind_init_one("tcpTick_");
delay_bind_init_one("ecn_");
delay_bind_init_one("old_ecn_");
delay_bind_init_one("eln_");
delay_bind_init_one("eln_rxmit_thresh_");
delay_bind_init_one("packetSize_");
delay_bind_init_one("tcpip_base_hdr_size_");
delay_bind_init_one("bugFix_");
delay_bind_init_one("slow_start_restart_");
delay_bind_init_one("restart_bugfix_");
delay_bind_init_one("timestamps_");
delay_bind_init_one("maxburst_");
delay_bind_init_one("maxcwnd_");
delay_bind_init_one("maxrto_");
delay_bind_init_one("srtt_init_");
delay_bind_init_one("rttvar_init_");
delay_bind_init_one("rtxcur_init_");
delay_bind_init_one("T_SRTT_BITS");
delay_bind_init_one("T_RTTVAR_BITS");
delay_bind_init_one("rttvar_exp_");
delay_bind_init_one("awnd_");
delay_bind_init_one("decrease_num_");
delay_bind_init_one("increase_num_");
delay_bind_init_one("trace_all_oneline_");
delay_bind_init_one("nam_tracevar_");
delay_bind_init_one("QOption_");
delay_bind_init_one("EnblRTTCtr_");
delay_bind_init_one("control_increase_");
#ifdef TCP_DELAY_BIND_ALL
// out because delay-bound tracevars aren't yet supported
delay_bind_init_one("t_seqno_");
delay_bind_init_one("rtt_");
delay_bind_init_one("srtt_");
delay_bind_init_one("rttvar_");
delay_bind_init_one("backoff_");
delay_bind_init_one("dupacks_");
delay_bind_init_one("seqno_");
delay_bind_init_one("ack_");
delay_bind_init_one("cwnd_");
delay_bind_init_one("ssthresh_");
delay_bind_init_one("maxseq_");
delay_bind_init_one("ndatapack_");
delay_bind_init_one("ndatabytes_");
delay_bind_init_one("nackpack_");
delay_bind_init_one("nrexmit_");
delay_bind_init_one("nrexmitpack_");
delay_bind_init_one("nrexmitbytes_");
delay_bind_init_one("singledup_");
#endif /* TCP_DELAY_BIND_ALL */
Agent::delay_bind_init_all();
reset();
}
int
TcpAgent::delay_bind_dispatch(const char *varName, const char *localName, TclObject *tracer)
{
if (delay_bind(varName, localName, "window_", &wnd_, tracer)) return TCL_OK;
if (delay_bind(varName, localName, "windowInit_", &wnd_init_, tracer)) return TCL_OK;
if (delay_bind(varName, localName, "windowInitOption_", &wnd_init_option_, tracer)) return TCL_OK;
if (delay_bind_bool(varName, localName, "syn_", &syn_, tracer)) return TCL_OK;
if (delay_bind(varName, localName, "windowOption_", &wnd_option_ , tracer)) return TCL_OK;
if (delay_bind(varName, localName, "windowConstant_", &wnd_const_, tracer)) return TCL_OK;
if (delay_bind(varName, localName, "windowThresh_", &wnd_th_ , tracer)) return TCL_OK;
if (delay_bind_bool(varName, localName, "delay_growth_", &delay_growth_ , tracer)) return TCL_OK;
if (delay_bind(varName, localName, "overhead_", &overhead_, tracer)) return TCL_OK;
if (delay_bind(varName, localName, "tcpTick_", &tcp_tick_, tracer)) return TCL_OK;
if (delay_bind_bool(varName, localName, "ecn_", &ecn_, tracer)) return TCL_OK;
if (delay_bind_bool(varName, localName, "old_ecn_", &old_ecn_ , tracer)) return TCL_OK;
if (delay_bind(varName, localName, "eln_", &eln_ , tracer)) return TCL_OK;
if (delay_bind(varName, localName, "eln_rxmit_thresh_", &eln_rxmit_thresh_ , tracer)) return TCL_OK;
if (delay_bind(varName, localName, "packetSize_", &size_ , tracer)) return TCL_OK;
if (delay_bind(varName, localName, "tcpip_base_hdr_size_", &tcpip_base_hdr_size_, tracer)) return TCL_OK;
if (delay_bind_bool(varName, localName, "bugFix_", &bug_fix_ , tracer)) return TCL_OK;
if (delay_bind_bool(varName, localName, "slow_start_restart_", &slow_start_restart_ , tracer)) return TCL_OK;
if (delay_bind_bool(varName, localName, "restart_bugfix_", &restart_bugfix_ , tracer)) return TCL_OK;
if (delay_bind_bool(varName, localName, "timestamps_", &ts_option_ , tracer)) return TCL_OK;
if (delay_bind(varName, localName, "maxburst_", &maxburst_ , tracer)) return TCL_OK;
if (delay_bind(varName, localName, "maxcwnd_", &maxcwnd_ , tracer)) return TCL_OK;
if (delay_bind(varName, localName, "maxrto_", &maxrto_ , tracer)) return TCL_OK;
if (delay_bind(varName, localName, "srtt_init_", &srtt_init_ , tracer)) return TCL_OK;
if (delay_bind(varName, localName, "rttvar_init_", &rttvar_init_ , tracer)) return TCL_OK;
if (delay_bind(varName, localName, "rtxcur_init_", &rtxcur_init_ , tracer)) return TCL_OK;
if (delay_bind(varName, localName, "T_SRTT_BITS", &T_SRTT_BITS , tracer)) return TCL_OK;
if (delay_bind(varName, localName, "T_RTTVAR_BITS", &T_RTTVAR_BITS , tracer)) return TCL_OK;
if (delay_bind(varName, localName, "rttvar_exp_", &rttvar_exp_ , tracer)) return TCL_OK;
if (delay_bind(varName, localName, "awnd_", &awnd_ , tracer)) return TCL_OK;
if (delay_bind(varName, localName, "decrease_num_", &decrease_num_, tracer)) return TCL_OK;
if (delay_bind(varName, localName, "increase_num_", &increase_num_, tracer)) return TCL_OK;
if (delay_bind_bool(varName, localName, "trace_all_oneline_", &trace_all_oneline_ , tracer)) return TCL_OK;
if (delay_bind_bool(varName, localName, "nam_tracevar_", &nam_tracevar_ , tracer)) return TCL_OK;
if (delay_bind(varName, localName, "QOption_", &QOption_ , tracer)) return TCL_OK;
if (delay_bind(varName, localName, "EnblRTTCtr_", &EnblRTTCtr_ , tracer)) return TCL_OK;
if (delay_bind(varName, localName, "control_increase_", &control_increase_ , tracer)) return TCL_OK;
#ifdef TCP_DELAY_BIND_ALL
// not if (delay-bound delay-bound tracevars aren't yet supported
if (delay_bind(varName, localName, "t_seqno_", &t_seqno_ , tracer)) return TCL_OK;
if (delay_bind(varName, localName, "rtt_", &t_rtt_ , tracer)) return TCL_OK;
if (delay_bind(varName, localName, "srtt_", &t_srtt_ , tracer)) return TCL_OK;
if (delay_bind(varName, localName, "rttvar_", &t_rttvar_ , tracer)) return TCL_OK;
if (delay_bind(varName, localName, "backoff_", &t_backoff_ , tracer)) return TCL_OK;
if (delay_bind(varName, localName, "dupacks_", &dupacks_ , tracer)) return TCL_OK;
if (delay_bind(varName, localName, "seqno_", &curseq_ , tracer)) return TCL_OK;
if (delay_bind(varName, localName, "ack_", &highest_ack_ , tracer)) return TCL_OK;
if (delay_bind(varName, localName, "cwnd_", &cwnd_ , tracer)) return TCL_OK;
if (delay_bind(varName, localName, "ssthresh_", &ssthresh_ , tracer)) return TCL_OK;
if (delay_bind(varName, localName, "maxseq_", &maxseq_ , tracer)) return TCL_OK;
if (delay_bind(varName, localName, "ndatapack_", &ndatapack_ , tracer)) return TCL_OK;
if (delay_bind(varName, localName, "ndatabytes_", &ndatabytes_ , tracer)) return TCL_OK;
if (delay_bind(varName, localName, "nackpack_", &nackpack_ , tracer)) return TCL_OK;
if (delay_bind(varName, localName, "nrexmit_", &nrexmit_ , tracer)) return TCL_OK;
if (delay_bind(varName, localName, "nrexmitpack_", &nrexmitpack_ , tracer)) return TCL_OK;
if (delay_bind(varName, localName, "nrexmitbytes_", &nrexmitbytes_ , tracer)) return TCL_OK;
if (delay_bind(varName, localName, "singledup_", &singledup_ , tracer)) return TCL_OK;
#endif
return Agent::delay_bind_dispatch(varName, localName, tracer);
}
/* Print out all the traced variables whenever any one is changed */
void
TcpAgent::traceAll() {
double curtime;
Scheduler& s = Scheduler::instance();
char wrk[500];
int n;
curtime = &s ? s.clock() : 0;
sprintf(wrk,"time: %-8.5f saddr: %-2d sport: %-2d daddr: %-2d dport:"
" %-2d maxseq: %-4d hiack: %-4d seqno: %-4d cwnd: %-6.3f"
" ssthresh: %-3d dupacks: %-2d rtt: %-6.3f srtt: %-6.3f"
" rttvar: %-6.3f bkoff: %-d", curtime, addr(), port(),
daddr(), dport(), int(maxseq_), int(highest_ack_),
int(t_seqno_), double(cwnd_), int(ssthresh_),
int(dupacks_), int(t_rtt_)*tcp_tick_,
(int(t_srtt_) >> T_SRTT_BITS)*tcp_tick_,
int(t_rttvar_)*tcp_tick_/4.0, int(t_backoff_));
n = strlen(wrk);
wrk[n] = '\n';
wrk[n+1] = 0;
if (channel_)
(void)Tcl_Write(channel_, wrk, n+1);
wrk[n] = 0;
return;
}
/* Print out just the variable that is modified */
void
TcpAgent::traceVar(TracedVar* v)
{
double curtime;
Scheduler& s = Scheduler::instance();
char wrk[500];
int n;
curtime = &s ? s.clock() : 0;
if (!strcmp(v->name(), "cwnd_") || !strcmp(v->name(), "maxrto_"))
sprintf(wrk,"%-8.5f %-2d %-2d %-2d %-2d %s %-6.3f",
curtime, addr(), port(), daddr(), dport(),
v->name(), double(*((TracedDouble*) v)));
else if (!strcmp(v->name(), "rtt_"))
sprintf(wrk,"%-8.5f %-2d %-2d %-2d %-2d %s %-6.3f",
curtime, addr(), port(), daddr(), dport(),
v->name(), int(*((TracedInt*) v))*tcp_tick_);
else if (!strcmp(v->name(), "srtt_"))
sprintf(wrk,"%-8.5f %-2d %-2d %-2d %-2d %s %-6.3f",
curtime, addr(), port(), daddr(), dport(),
v->name(),
(int(*((TracedInt*) v)) >> T_SRTT_BITS)*tcp_tick_);
else if (!strcmp(v->name(), "rttvar_"))
sprintf(wrk,"%-8.5f %-2d %-2d %-2d %-2d %s %-6.3f",
curtime, addr(), port(), daddr(), dport(),
v->name(),
int(*((TracedInt*) v))*tcp_tick_/4.0);
else
sprintf(wrk,"%-8.5f %-2d %-2d %-2d %-2d %s %d",
curtime, addr(), port(), daddr(), dport(),
v->name(), int(*((TracedInt*) v)));
n = strlen(wrk);
wrk[n] = '\n';
wrk[n+1] = 0;
if (channel_)
(void)Tcl_Write(channel_, wrk, n+1);
wrk[n] = 0;
return;
}
void
TcpAgent::trace(TracedVar* v)
{
if (nam_tracevar_) {
Agent::trace(v);
} else if (trace_all_oneline_)
traceAll();
else
traceVar(v);
}
//
// in 1-way TCP, syn_ indicates we are modeling
// a SYN exchange at the beginning. If this is true
// and we are delaying growth, then use an initial
// window of one. If not, we do whatever initial_window()
// says to do.
//
void
TcpAgent::set_initial_window()
{
if (syn_ && delay_growth_)
cwnd_ = 1.0;
else
cwnd_ = initial_window();
}
void
TcpAgent::reset_qoption()
{
int now = (int)(Scheduler::instance().clock()/tcp_tick_ + 0.5);
T_start = now ;
RTT_count = 0 ;
RTT_prev = 0 ;
RTT_goodcount = 1 ;
F_counting = 0 ;
W_timed = -1 ;
F_full = 0 ;
Backoffs = 0 ;
}
void
TcpAgent::reset()
{
rtt_init();
rtt_seq_ = -1;
/*XXX lookup variables */
dupacks_ = 0;
curseq_ = 0;
set_initial_window();
t_seqno_ = 0;
maxseq_ = -1;
last_ack_ = -1;
highest_ack_ = -1;
ssthresh_ = int(wnd_);
wnd_restart_ = 1.;
awnd_ = wnd_init_ / 2.0;
recover_ = 0;
closed_ = 0;
last_cwnd_action_ = 0;
boot_time_ = Random::uniform(tcp_tick_);
first_decrease_ = 1;
if (control_increase_) {
prev_highest_ack_ = highest_ack_ ;
}
if (QOption_) {
int now = (int)(Scheduler::instance().clock()/tcp_tick_ + 0.5);
T_last = now ;
T_prev = now ;
W_used = 0 ;
if (EnblRTTCtr_) {
reset_qoption();
}
}
}
/*
* Initialize variables for the retransmit timer.
*/
void TcpAgent::rtt_init()
{
t_rtt_ = 0;
t_srtt_ = int(srtt_init_ / tcp_tick_) << T_SRTT_BITS;
t_rttvar_ = int(rttvar_init_ / tcp_tick_) << T_RTTVAR_BITS;
t_rtxcur_ = rtxcur_init_;
t_backoff_ = 1;
}
/* This has been modified to use the tahoe code. */
double TcpAgent::rtt_timeout()
{
double timeout;
timeout = t_rtxcur_ * t_backoff_;
if (timeout > maxrto_)
timeout = maxrto_;
if (timeout < 2 * tcp_tick_) {
if (timeout < 0) {
fprintf(stderr, "TcpAgent: negative RTO! (%f)\n",
timeout);
exit(1);
}
timeout = 2 * tcp_tick_;
}
return (timeout);
}
/* This has been modified to use the tahoe code. */
void TcpAgent::rtt_update(double tao)
{
double now = Scheduler::instance().clock();
if (ts_option_)
t_rtt_ = int(tao /tcp_tick_ + 0.5);
else {
double sendtime = now - tao;
sendtime += boot_time_;
double tickoff = fmod(sendtime, tcp_tick_);
t_rtt_ = int((tao + tickoff) / tcp_tick_);
}
if (t_rtt_ < 1)
t_rtt_ = 1;
//
// srtt has 3 bits to the right of the binary point
// rttvar has 2
//
if (t_srtt_ != 0) {
register short delta;
delta = t_rtt_ - (t_srtt_ >> T_SRTT_BITS); // d = (m - a0)
if ((t_srtt_ += delta) <= 0) // a1 = 7/8 a0 + 1/8 m
t_srtt_ = 1;
if (delta < 0)
delta = -delta;
delta -= (t_rttvar_ >> T_RTTVAR_BITS);
if ((t_rttvar_ += delta) <= 0) // var1 = 3/4 var0 + 1/4 |d|
t_rttvar_ = 1;
} else {
t_srtt_ = t_rtt_ << T_SRTT_BITS; // srtt = rtt
t_rttvar_ = t_rtt_ << (T_RTTVAR_BITS-1); // rttvar = rtt / 2
}
//
// Current retransmit value is
// (unscaled) smoothed round trip estimate
// plus 2^rttvar_exp_ times (unscaled) rttvar.
//
t_rtxcur_ = (((t_rttvar_ << (rttvar_exp_ + (T_SRTT_BITS - T_RTTVAR_BITS))) +
t_srtt_) >> T_SRTT_BITS ) * tcp_tick_;
return;
}
void TcpAgent::rtt_backoff()
{
if (t_backoff_ < 64)
t_backoff_ <<= 1;
if (t_backoff_ > 8) {
/*
* If backed off this far, clobber the srtt
* value, storing it in the mean deviation
* instead.
*/
t_rttvar_ += (t_srtt_ >> T_SRTT_BITS);
t_srtt_ = 0;
}
}
void TcpAgent::output(int seqno, int reason)
{
int force_set_rtx_timer = 0;
Packet* p = allocpkt();
hdr_tcp *tcph = hdr_tcp::access(p);
hdr_flags* hf = hdr_flags::access(p);
tcph->seqno() = seqno;
tcph->ts() = Scheduler::instance().clock();
tcph->ts_echo() = ts_peer_;
tcph->reason() = reason;
if (ecn_) {
hf->ect() = 1; // ECN-capable transport
}
if (cong_action_) {
hf->cong_action() = TRUE; // Congestion action.
cong_action_ = FALSE;
}
/* Check if this is the initial SYN packet. */
if (seqno == 0) {
if (syn_) {
hdr_cmn::access(p)->size() = tcpip_base_hdr_size_;
}
if (ecn_) {
hf->ecnecho() = 1;
// hf->cong_action() = 1;
hf->ect() = 0;
}
}
int bytes = hdr_cmn::access(p)->size();
/* if no outstanding data, be sure to set rtx timer again */
if (highest_ack_ == maxseq_)
force_set_rtx_timer = 1;
/* call helper function to fill in additional fields */
output_helper(p);
++ndatapack_;
ndatabytes_ += bytes;
send(p, 0);
if (seqno == curseq_ && seqno > maxseq_)
idle(); // Tell application I have sent everything so far
if (seqno > maxseq_) {
maxseq_ = seqno;
if (!rtt_active_) {
rtt_active_ = 1;
if (seqno > rtt_seq_) {
rtt_seq_ = seqno;
rtt_ts_ = Scheduler::instance().clock();
}
}
} else {
++nrexmitpack_;
nrexmitbytes_ += bytes;
}
if (!(rtx_timer_.status() == TIMER_PENDING) || force_set_rtx_timer)
/* No timer pending. Schedule one. */
set_rtx_timer();
}
/*
* Must convert bytes into packets for one-way TCPs.
* If nbytes == -1, this corresponds to infinite send. We approximate
* infinite by a very large number (TCP_MAXSEQ).
*/
void TcpAgent::sendmsg(int nbytes, const char* /*flags*/)
{
if (nbytes == -1 && curseq_ <= TCP_MAXSEQ)
curseq_ = TCP_MAXSEQ;
else
curseq_ += (nbytes/size_ + (nbytes%size_ ? 1 : 0));
send_much(0, 0, maxburst_);
}
void TcpAgent::advanceby(int delta)
{
curseq_ += delta;
if (delta > 0)
closed_ = 0;
send_much(0, 0, maxburst_);
}
int TcpAgent::command(int argc, const char*const* argv)
{
if (argc == 3) {
if (strcmp(argv[1], "advance") == 0) {
int newseq = atoi(argv[2]);
if (newseq > maxseq_)
advanceby(newseq - curseq_);
else
advanceby(maxseq_ - curseq_);
return (TCL_OK);
}
if (strcmp(argv[1], "advanceby") == 0) {
advanceby(atoi(argv[2]));
return (TCL_OK);
}
/*
* Curtis Villamizar's trick to transfer tcp connection
* parameters to emulate http persistent connections.
*
* Another way to do the same thing is to open one tcp
* object and use start/stop/maxpkts_ or advanceby to control
* how much data is sent in each burst.
* With a single connection, slow_start_restart_
* should be configured as desired.
*
* This implementation (persist) may not correctly
* emulate pure-BSD-based systems which close cwnd
* after the connection goes idle (slow-start
* restart). See appendix C in
* Jacobson and Karels ``Congestion
* Avoidance and Control'' at
*
* (*not* the original
* '88 paper) for why BSD does this. See
* ``Performance Interactions Between P-HTTP and TCP
* Implementations'' in CCR 27(2) for descriptions of
* what other systems do the same.
*
*/
if (strcmp(argv[1], "persist") == 0) {
TcpAgent *other
= (TcpAgent*)TclObject::lookup(argv[2]);
cwnd_ = other->cwnd_;
awnd_ = other->awnd_;
ssthresh_ = other->ssthresh_;
t_rtt_ = other->t_rtt_;
t_srtt_ = other->t_srtt_;
t_rttvar_ = other->t_rttvar_;
t_backoff_ = other->t_backoff_;
return (TCL_OK);
}
}
return (Agent::command(argc, argv));
}
int TcpAgent::window()
{
return (cwnd_ < wnd_ ? (int)cwnd_ : (int)wnd_);
}
/*
* Try to send as much data as the window will allow. The link layer will
* do the buffering; we ask the application layer for the size of the packets.
*/
void TcpAgent::send_much(int force, int reason, int maxburst)
{
send_idle_helper();
int win = window();
int npackets = 0;
if (!force && delsnd_timer_.status() == TIMER_PENDING)
return;
/* Save time when first packet was sent, for newreno --Allman */
if (t_seqno_ == 0)
firstsent_ = Scheduler::instance().clock();
if (burstsnd_timer_.status() == TIMER_PENDING)
return;
while (t_seqno_ <= highest_ack_ + win && t_seqno_ < curseq_) {
if (overhead_ == 0 || force) {
output(t_seqno_, reason);
npackets++;
if (QOption_)
process_qoption_after_send () ;
t_seqno_ ++ ;
} else if (!(delsnd_timer_.status() == TIMER_PENDING)) {
/*
* Set a delayed send timeout.
*/
delsnd_timer_.resched(Random::uniform(overhead_));
return;
}
win = window();
if (maxburst && npackets == maxburst)
break;
}
/* call helper function */
send_helper(maxburst);
}
/*
* We got a timeout or too many duplicate acks. Clear the retransmit timer.
* Resume the sequence one past the last packet acked.
* "mild" is 0 for timeouts and Tahoe dup acks, 1 for Reno dup acks.
* "backoff" is 1 if the timer should be backed off, 0 otherwise.
*/
void TcpAgent::reset_rtx_timer(int mild, int backoff)
{
if (backoff)
rtt_backoff();
set_rtx_timer();
if (!mild)
t_seqno_ = highest_ack_ + 1;
rtt_active_ = 0;
}
/*
* Set retransmit timer using current rtt estimate. By calling resched(),
* it does not matter whether the timer was already running.
*/
void TcpAgent::set_rtx_timer()
{
rtx_timer_.resched(rtt_timeout());
}
/*
* Set new retransmission timer if not all outstanding
* or available data acked, or if we are unable to send because
* cwnd is less than one (as when the ECN bit is set when cwnd was 1).
* Otherwise, if a timer is still outstanding, cancel it.
*/
void TcpAgent::newtimer(Packet* pkt)
{
hdr_tcp *tcph = hdr_tcp::access(pkt);
/*
* t_seqno_, the next packet to send, is reset (decreased)
* to highest_ack_ + 1 after a timeout,
* so we also have to check maxseq_, the highest seqno sent.
* In addition, if the packet sent after the timeout has
* the ECN bit set, then the returning ACK caused cwnd_ to
* be decreased to less than one, and we can't send another
* packet until the retransmit timer again expires.
* So we have to check for "cwnd_ < 1" as well.
*/
if (t_seqno_ > tcph->seqno() || tcph->seqno() < maxseq_ || cwnd_ < 1)
set_rtx_timer();
else
cancel_rtx_timer();
}
/*
* open up the congestion window
*/
void TcpAgent::opencwnd()
{
if (cwnd_ < ssthresh_) {
/* slow-start (exponential) */
cwnd_ += 1;
} else {
/* linear */
double f;
switch (wnd_option_) {
case 0:
if (++count_ >= cwnd_) {
count_ = 0;
++cwnd_;
}
break;
case 1:
/* This is the standard algorithm. */
cwnd_ += increase_num_ / cwnd_;
break;
case 2:
/* These are window increase algorithms
* for experimental purposes only. */
f = (t_srtt_ >> T_SRTT_BITS) * tcp_tick_;
f *= f;
f *= wnd_const_;
f += fcnt_;
if (f > cwnd_) {
fcnt_ = 0;
++cwnd_;
} else
fcnt_ = f;
break;
case 3:
f = awnd_;
f *= f;
f *= wnd_const_;
f += fcnt_;
if (f > cwnd_) {
fcnt_ = 0;
++cwnd_;
} else
fcnt_ = f;
break;
case 4:
f = awnd_;
f *= wnd_const_;
f += fcnt_;
if (f > cwnd_) {
fcnt_ = 0;
++cwnd_;
} else
fcnt_ = f;
break;
case 5:
f = (t_srtt_ >> T_SRTT_BITS) * tcp_tick_;
f *= wnd_const_;
f += fcnt_;
if (f > cwnd_) {
fcnt_ = 0;
++cwnd_;
} else
fcnt_ = f;
break;
default:
#ifdef notdef
/*XXX*/
error("illegal window option %d", wnd_option_);
#endif
abort();
}
}
// if maxcwnd_ is set (nonzero), make it the cwnd limit
if (maxcwnd_ && (int(cwnd_) > maxcwnd_))
cwnd_ = maxcwnd_;
return;
}
void
TcpAgent::slowdown(int how)
{
int win = window();
int halfwin = int (window() / 2);
int decreasewin = int (decrease_num_ * window());
if (how & CLOSE_SSTHRESH_HALF)
ssthresh_ = decreasewin;
else if (how & THREE_QUARTER_SSTHRESH)
if (ssthresh_ < 3*cwnd_/4)
ssthresh_ = (int)(3*cwnd_/4);
if (how & CLOSE_CWND_HALF)
// For the first decrease, decrease by half
// even for non-standard values of decrease_num_.
if (first_decrease_ == 1 || decrease_num_ == 0.5) {
cwnd_ = halfwin;
first_decrease_ = 0;
} else cwnd_ = decreasewin;
else if (how & CWND_HALF_WITH_MIN) {
cwnd_ = decreasewin;
if (cwnd_ < 1)
cwnd_ = 1;
}
else if (how & CLOSE_CWND_RESTART)
cwnd_ = int(wnd_restart_);
else if (how & CLOSE_CWND_INIT)
cwnd_ = int(wnd_init_);
else if (how & CLOSE_CWND_ONE)
cwnd_ = 1;
else if (how & CLOSE_CWND_HALF_WAY) {
// cwnd_ = win - (win - W_used)/2 ;
cwnd_ = W_used + decrease_num_ * (win - W_used);
if (cwnd_ < 1)
cwnd_ = 1;
}
if (ssthresh_ < 2)
ssthresh_ = 2;
if (how & (CLOSE_CWND_HALF|CLOSE_CWND_RESTART|CLOSE_CWND_INIT|CLOSE_CWND_ONE))
cong_action_ = TRUE;
fcnt_ = count_ = 0;
}
/*
* Process a packet that acks previously unacknowleged data.
*/
void TcpAgent::newack(Packet* pkt)
{
double now = Scheduler::instance().clock();
hdr_tcp *tcph = hdr_tcp::access(pkt);
/*
* Wouldn't it be better to set the timer *after*
* updating the RTT, instead of *before*?
*/
newtimer(pkt);
dupacks_ = 0;
last_ack_ = tcph->seqno();
prev_highest_ack_ = highest_ack_ ;
highest_ack_ = last_ack_;
if (t_seqno_ < last_ack_ + 1)
t_seqno_ = last_ack_ + 1;
/*
* Update RTT only if it's OK to do so from info in the flags header.
* This is needed for protocols in which intermediate agents
* in the network intersperse acks (e.g., ack-reconstructors) for
* various reasons (without violating e2e semantics).
*/
hdr_flags *fh = hdr_flags::access(pkt);
if (!fh->no_ts_) {
if (ts_option_)
rtt_update(now - tcph->ts_echo());
if (rtt_active_ && tcph->seqno() >= rtt_seq_) {
if (!ect_ || !ecn_backoff_ ||
!hdr_flags::access(pkt)->ecnecho()) {
/*
* Don't end backoff if still in ECN-Echo with
* a congestion window of 1 packet.
*/
t_backoff_ = 1;
ecn_backoff_ = 0;
}
rtt_active_ = 0;
if (!ts_option_)
rtt_update(now - rtt_ts_);
}
}
/* update average window */
awnd_ *= 1.0 - wnd_th_;
awnd_ += wnd_th_ * cwnd_;
}
/*
* Respond either to a source quench or to a congestion indication bit.
* This is done at most once a roundtrip time; after a source quench,
* another one will not be done until the last packet transmitted before
* the previous source quench has been ACKed.
*
* Note that this procedure is called before "highest_ack_" is
* updated to reflect the current ACK packet.
*/
void TcpAgent::ecn(int seqno)
{
if (seqno > recover_ ||
last_cwnd_action_ == CWND_ACTION_TIMEOUT) {
recover_ = maxseq_;
last_cwnd_action_ = CWND_ACTION_ECN;
if (cwnd_ <= 1.0) {
if (ecn_backoff_)
rtt_backoff();
else ecn_backoff_ = 1;
} else ecn_backoff_ = 0;
slowdown(CLOSE_CWND_HALF|CLOSE_SSTHRESH_HALF);
}
}
void TcpAgent::recv_newack_helper(Packet *pkt) {
//hdr_tcp *tcph = hdr_tcp::access(pkt);
newack(pkt);
if (!ect_ || !hdr_flags::access(pkt)->ecnecho() ||
(old_ecn_ && ecn_burst_)) {
/* If "old_ecn", this is not the first ACK carrying ECN-Echo
* after a period of ACKs without ECN-Echo.
* Therefore, open the congestion window. */
/* if control option is set, and the sender is not
window limited, then do not increase the window size */
if (control_increase_ ) {
int win = window () ;
if (t_seqno_ > (prev_highest_ack_ + win)) {
opencwnd();
}
}
else {
if (!control_increase_) {
opencwnd();
}
}
}
if (ect_) {
if (!hdr_flags::access(pkt)->ecnecho())
ecn_backoff_ = 0;
if (!ecn_burst_ && hdr_flags::access(pkt)->ecnecho())
ecn_burst_ = TRUE;
else if (ecn_burst_ && ! hdr_flags::access(pkt)->ecnecho())
ecn_burst_ = FALSE;
}
if (!ect_ && hdr_flags::access(pkt)->ecnecho() &&
!hdr_flags::access(pkt)->cong_action())
ect_ = 1;
/* if the connection is done, call finish() */
if ((highest_ack_ >= curseq_-1) && !closed_) {
closed_ = 1;
finish();
}
if (QOption_ && curseq_ == highest_ack_ +1) {
cancel_rtx_timer();
}
}
/*
* Set the initial window.
*/
double
TcpAgent::initial_window()
{
//
// init_option = 1: static iw of wnd_init_
//
if (wnd_init_option_ == 1) {
return (wnd_init_);
}
else if (wnd_init_option_ == 2) {
// do iw according to Internet draft
if (size_ <= 1095) {
return (4.0);
} else if (size_ < 2190) {
return (3.0);
} else {
return (2.0);
}
}
// XXX what should we return here???
fprintf(stderr, "Wrong number of wnd_init_option_ %d\n",
wnd_init_option_);
abort();
return (2.0); // XXX make msvc happy.
}
/*
* Dupack-action: what to do on a DUP ACK. After the initial check
* of 'recover' below, this function implements the following truth
* table:
*
* bugfix ecn last-cwnd == ecn action
*
* 0 0 0 tahoe_action
* 0 0 1 tahoe_action [impossible]
* 0 1 0 tahoe_action
* 0 1 1 slow-start, return
* 1 0 0 nothing
* 1 0 1 nothing [impossible]
* 1 1 0 nothing
* 1 1 1 slow-start, return
*/
/*
* A first or second duplicate acknowledgement has arrived, and
* singledup_ is enabled.
* If the receiver's advertised window permits, and we are exceeding our
* congestion window by less than NUMDUPACKS, then send a new packet.
*/
void
TcpAgent::send_one()
{
if (t_seqno_ <= highest_ack_ + wnd_ && t_seqno_ < curseq_ &&
t_seqno_ <= highest_ack_ + cwnd_ + dupacks_ ) {
output(t_seqno_, 0);
if (QOption_)
process_qoption_after_send () ;
t_seqno_ ++ ;
// send_helper(); ??
}
return;
}
void
TcpAgent::dupack_action()
{
int recovered = (highest_ack_ > recover_);
if (recovered || (!bug_fix_ && !ecn_)) {
goto tahoe_action;
}
if (ecn_ && last_cwnd_action_ == CWND_ACTION_ECN) {
last_cwnd_action_ = CWND_ACTION_DUPACK;
slowdown(CLOSE_CWND_ONE);
reset_rtx_timer(0,0);
return;
}
if (bug_fix_) {
/*
* The line below, for "bug_fix_" true, avoids
* problems with multiple fast retransmits in one
* window of data.
*/
return;
}
tahoe_action:
recover_ = maxseq_;
last_cwnd_action_ = CWND_ACTION_DUPACK;
slowdown(CLOSE_SSTHRESH_HALF|CLOSE_CWND_ONE);
reset_rtx_timer(0,0);
return;
}
/*
* main reception path - should only see acks, otherwise the
* network connections are misconfigured
*/
void TcpAgent::recv(Packet *pkt, Handler*)
{
hdr_tcp *tcph = hdr_tcp::access(pkt);
#ifdef notdef
if (pkt->type_ != PT_ACK) {
Tcl::instance().evalf("%s error \"received non-ack\"",
name());
Packet::free(pkt);
return;
}
#endif
++nackpack_;
ts_peer_ = tcph->ts();
int ecnecho = hdr_flags::access(pkt)->ecnecho();
if (ecnecho && ecn_)
ecn(tcph->seqno());
recv_helper(pkt);
/* grow cwnd and check if the connection is done */
if (tcph->seqno() > last_ack_) {
recv_newack_helper(pkt);
if (last_ack_ == 0 && delay_growth_) {
cwnd_ = initial_window();
}
} else if (tcph->seqno() == last_ack_) {
if (hdr_flags::access(pkt)->eln_ && eln_) {
tcp_eln(pkt);
return;
}
if (++dupacks_ == NUMDUPACKS) {
dupack_action();
} else if (dupacks_ < NUMDUPACKS && singledup_ ) {
send_one();
}
}
if (QOption_ && EnblRTTCtr_)
process_qoption_after_ack (tcph->seqno());
Packet::free(pkt);
/*
* Try to send more data.
*/
send_much(0, 0, maxburst_);
}
/*
* Process timeout events other than rtx timeout. Having this as a separate
* function allows derived classes to make alterations/enhancements (e.g.,
* response to new types of timeout events).
*/
void TcpAgent::timeout_nonrtx(int tno)
{
if (tno == TCP_TIMER_DELSND) {
/*
* delayed-send timer, with random overhead
* to avoid phase effects
*/
send_much(1, TCP_REASON_TIMEOUT, maxburst_);
}
}
void TcpAgent::timeout(int tno)
{
/* retransmit timer */
if (tno == TCP_TIMER_RTX) {
if (cwnd_ < 1) cwnd_ = 1;
if (highest_ack_ == maxseq_ && !slow_start_restart_) {
/*
* TCP option:
* If no outstanding data, then don't do anything.
*/
// Should this return be here?
// What if CWND_ACTION_ECN and cwnd < 1?
// return;
} else {
recover_ = maxseq_;
if (highest_ack_ == -1 && wnd_init_option_ == 2)
/*
* First packet dropped, so don't use larger
* initial windows.
*/
wnd_init_option_ = 1;
if (highest_ack_ == maxseq_ && restart_bugfix_)
/*
* if there is no outstanding data, don't cut
* down ssthresh_.
*/
slowdown(CLOSE_CWND_ONE);
else if (highest_ack_ < recover_ &&
last_cwnd_action_ == CWND_ACTION_ECN) {
/*
* if we are in recovery from a recent ECN,
* don't cut down ssthresh_.
*/
slowdown(CLOSE_CWND_ONE);
}
else {
++nrexmit_;
slowdown(CLOSE_SSTHRESH_HALF|CLOSE_CWND_RESTART);
}
}
/* if there is no outstanding data, don't back off rtx timer */
if (highest_ack_ == maxseq_ && restart_bugfix_) {
reset_rtx_timer(0,0);
}
else {
reset_rtx_timer(0,1);
}
last_cwnd_action_ = CWND_ACTION_TIMEOUT;
send_much(0, TCP_REASON_TIMEOUT, maxburst_);
}
else {
timeout_nonrtx(tno);
}
}
/*
* Check if the packet (ack) has the ELN bit set, and if it does, and if the
* last ELN-rxmitted packet is smaller than this one, then retransmit the
* packet. Do not adjust the cwnd when this happens.
*/
void TcpAgent::tcp_eln(Packet *pkt)
{
//int eln_rxmit;
hdr_tcp *tcph = hdr_tcp::access(pkt);
int ack = tcph->seqno();
if (++dupacks_ == eln_rxmit_thresh_ && ack > eln_last_rxmit_) {
/* Retransmit this packet */
output(last_ack_ + 1, TCP_REASON_DUPACK);
eln_last_rxmit_ = last_ack_+1;
} else
send_much(0, 0, maxburst_);
Packet::free(pkt);
return;
}
/*
* This function is invoked when the connection is done. It in turn
* invokes the Tcl finish procedure that was registered with TCP.
*/
void TcpAgent::finish()
{
Tcl::instance().evalf("%s done", this->name());
}
void RtxTimer::expire(Event*)
{
a_->timeout(TCP_TIMER_RTX);
}
void DelSndTimer::expire(Event*)
{
a_->timeout(TCP_TIMER_DELSND);
}
void BurstSndTimer::expire(Event*)
{
a_->timeout(TCP_TIMER_BURSTSND);
}
/*
* THE FOLLOWING FUNCTIONS ARE OBSOLETE, but REMAIN HERE
* DUE TO OTHER PEOPLE's TCPs THAT MIGHT USE THEM
*
* These functions are now replaced by ecn() and slowdown(),
* respectively.
*/
/*
* Respond either to a source quench or to a congestion indication bit.
* This is done at most once a roundtrip time; after a source quench,
* another one will not be done until the last packet transmitted before
* the previous source quench has been ACKed.
*/
void TcpAgent::quench(int how)
{
if (highest_ack_ >= recover_) {
recover_ = maxseq_;
last_cwnd_action_ = CWND_ACTION_ECN;
closecwnd(how);
}
}
/*
* close down the congestion window
*/
void TcpAgent::closecwnd(int how)
{
static int first_time = 1;
if (first_time == 1) {
fprintf(stderr, "the TcpAgent::closecwnd() function is now deprecated, please use the function slowdown() instead\n");
}
switch (how) {
case 0:
/* timeouts */
ssthresh_ = int( window() / 2 );
if (ssthresh_ < 2)
ssthresh_ = 2;
cwnd_ = int(wnd_restart_);
break;
case 1:
/* Reno dup acks, or after a recent congestion indication. */
// cwnd_ = window()/2;
cwnd_ = decrease_num_ * window();
ssthresh_ = int(cwnd_);
if (ssthresh_ < 2)
ssthresh_ = 2;
break;
case 2:
/* Tahoe dup acks
* after a recent congestion indication */
cwnd_ = wnd_init_;
break;
case 3:
/* Retransmit timeout, but no outstanding data. */
cwnd_ = int(wnd_init_);
break;
case 4:
/* Tahoe dup acks */
ssthresh_ = int( window() / 2 );
if (ssthresh_ < 2)
ssthresh_ = 2;
cwnd_ = 1;
break;
default:
abort();
}
fcnt_ = 0.;
count_ = 0;
}
/*
* Check if the sender has been idle or application-limited for more
* than an RTO, and if so, reduce the congestion window.
*/
void TcpAgent::process_qoption_after_send ()
{
int tcp_now = (int)(Scheduler::instance().clock()/tcp_tick_ + 0.5);
int rto = (int)(t_rtxcur_/tcp_tick_) ;
/*double ct = Scheduler::instance().clock();*/
if (!EnblRTTCtr_) {
if (tcp_now - T_last >= rto) {
// The sender has been idle.
slowdown(THREE_QUARTER_SSTHRESH) ;
for (int i = 0 ; i < (tcp_now - T_last)/rto; i ++) {
slowdown(CWND_HALF_WITH_MIN);
}
T_prev = tcp_now ;
W_used = 0 ;
}
T_last = tcp_now ;
if (t_seqno_ == highest_ack_+ window()) {
T_prev = tcp_now ;
W_used = 0 ;
}
else if (t_seqno_ == curseq_-1) {
// The sender has no more data to send.
int tmp = t_seqno_ - highest_ack_ ;
if (tmp > W_used)
W_used = tmp ;
if (tcp_now - T_prev >= rto) {
// The sender has been application-limited.
slowdown(THREE_QUARTER_SSTHRESH);
slowdown(CLOSE_CWND_HALF_WAY);
T_prev = tcp_now ;
W_used = 0 ;
}
}
} else {
rtt_counting();
}
}
/*
* Check if the sender has been idle or application-limited for more
* than an RTO, and if so, reduce the congestion window, for a TCP sender
* that "counts RTTs" by estimating the number of RTTs that fit into
* a single clock tick.
*/
void
TcpAgent::rtt_counting()
{
int tcp_now = (int)(Scheduler::instance().clock()/tcp_tick_ + 0.5);
int rtt = (int(t_srtt_) >> T_SRTT_BITS) ;
if (rtt < 1)
rtt = 1 ;
if (tcp_now - T_last >= 2*rtt) {
// The sender has been idle.
int RTTs ;
RTTs = (tcp_now -T_last)*RTT_goodcount/(rtt*2) ;
RTTs = RTTs - Backoffs ;
Backoffs = 0 ;
if (RTTs > 0) {
slowdown(THREE_QUARTER_SSTHRESH) ;
for (int i = 0 ; i < RTTs ; i ++) {
slowdown(CWND_HALF_WITH_MIN);
RTT_prev = RTT_count ;
W_used = 0 ;
}
}
}
T_last = tcp_now ;
if (tcp_now - T_start >= 2*rtt) {
if ((RTT_count > RTT_goodcount) || (F_full == 1)) {
RTT_goodcount = RTT_count ;
if (RTT_goodcount < 1) RTT_goodcount = 1 ;
}
RTT_prev = RTT_prev - RTT_count ;
RTT_count = 0 ;
T_start = tcp_now ;
F_full = 0;
}
if (t_seqno_ == highest_ack_ + window()) {
W_used = 0 ;
F_full = 1 ;
RTT_prev = RTT_count ;
}
else if (t_seqno_ == curseq_-1) {
// The sender has no more data to send.
int tmp = t_seqno_ - highest_ack_ ;
if (tmp > W_used)
W_used = tmp ;
if (RTT_count - RTT_prev >= 2) {
// The sender has been application-limited.
slowdown(THREE_QUARTER_SSTHRESH) ;
slowdown(CLOSE_CWND_HALF_WAY);
RTT_prev = RTT_count ;
Backoffs ++ ;
W_used = 0;
}
}
if (F_counting == 0) {
W_timed = t_seqno_ ;
F_counting = 1 ;
}
}
void TcpAgent::process_qoption_after_ack (int seqno)
{
if (F_counting == 1) {
if (seqno >= W_timed) {
RTT_count ++ ;
F_counting = 0 ;
}
else {
if (dupacks_ == NUMDUPACKS)
RTT_count ++ ;
}
}
}
tcplib-telnet.cc
/* $Header$ */
/* Generate telnet originator interarrivals from tcplib distribution. */
#include
#include "random.h"
static double tcplib_telnet[] = {
0.000606425,0.000617809,0.000629598,0.000643143,0.000643628,
0.00064655,0.000664476,0.000667254,0.000672394,0.000673758,
0.000682008,0.000687469,0.000691018,0.000693969,0.000724883,
0.000740577,0.000742143,0.000748767,0.000755347,0.000755526,
0.000765232,0.000790174,0.000797838,0.00081818,0.000850501,
0.000854608,0.000854906,0.000859485,0.000864083,0.000873226,
0.000885034,0.000912476,0.000913489,0.000916853,0.000919261,
0.000919514,0.000922924,0.000958073,0.000961155,0.000969526,
0.000971511,0.000980201,0.00098338,0.000983509,0.000990735,
0.001000319,0.001001811,0.001002151,0.001002863,0.001003641,
0.001005049,0.001007244,0.001009476,0.001010783,0.001011109,
0.001011722,0.001012177,0.001013221,0.001015247,0.001015993,
0.001017886,0.001018433,0.001019315,0.00102499,0.001030348,
0.001031957,0.0010339,0.001037434,0.001045911,0.001049322,
0.001050719,0.001050805,0.001051693,0.001051961,0.001056787,
0.001058558,0.001059402,0.001061441,0.00106194,0.001063149,
0.001064613,0.001067544,0.001069962,0.001070169,0.001085332,
0.001087341,0.001088065,0.001099885,0.001105593,0.001108601,
0.001110001,0.001116981,0.001119769,0.001121254,0.001127348,
0.00115188,0.00115398,0.001164335,0.001164352,0.001164709,
0.001169459,0.001169642,0.001171422,0.001172161,0.001173194,
0.00117532,0.001176576,0.001177351,0.001183972,0.001184679,
0.001185162,0.001188038,0.001189508,0.001192164,0.001197043,
0.001199759,0.001211616,0.001229016,0.001252279,0.001276589,
0.00127892,0.001300384,0.001304138,0.001307312,0.001307586,
0.001315486,0.001316417,0.001323284,0.001328374,0.001335633,
0.001338598,0.001347147,0.001348165,0.00134917,0.001356419,
0.001362257,0.001372066,0.001387081,0.001399696,0.001402175,
0.001412012,0.001413763,0.001417466,0.001419761,0.001420134,
0.001424412,0.001439334,0.001441171,0.001444712,0.001452554,
0.001461067,0.001461833,0.00146527,0.001467384,0.001467964,
0.001474171,0.001476138,0.001479168,0.001488608,0.001491578,
0.001497971,0.001515456,0.001518939,0.001533708,0.001534092,
0.001555606,0.001564304,0.001581632,0.001590172,0.001598017,
0.00159872,0.001599285,0.001606442,0.001614227,0.001634926,
0.001636214,0.001638917,0.001639659,0.001652443,0.001673497,
0.00167351,0.001674762,0.00168198,0.001689702,0.001726873,
0.001727579,0.00173431,0.001746475,0.001757766,0.001758551,
0.00176876,0.001769686,0.001772601,0.00178872,0.001794696,
0.001796178,0.001797238,0.001799231,0.001913671,0.001918673,
0.00192128,0.001942145,0.001948373,0.001969063,0.002002342,
0.0020028,0.00200426,0.002007535,0.002007833,0.00201702,
0.002018842,0.002018952,0.002020422,0.002031502,0.002046811,
0.002049709,0.002050466,0.002053182,0.002053365,0.002054337,
0.002058991,0.002059307,0.002065857,0.002070605,0.00207165,
0.002085012,0.002095621,0.002102974,0.002104456,0.002109999,
0.002111309,0.002125862,0.002153559,0.002154697,0.002176882,
0.002183924,0.002184434,0.002215977,0.002216784,0.002217203,
0.002225873,0.002233776,0.002269052,0.002272505,0.002276214,
0.002278465,0.002282915,0.002295668,0.002301384,0.002318613,
0.002326086,0.002333496,0.002337689,0.002345075,0.002352433,
0.002365678,0.002374506,0.002376827,0.002380943,0.00238621,
0.002386681,0.002388135,0.002391703,0.002403092,0.002404462,
0.002415816,0.002422657,0.002424746,0.002428499,0.00243592,
0.00244871,0.002475678,0.002485676,0.002488931,0.002500876,
0.002508459,0.002511286,0.002517001,0.002527966,0.002531477,
0.002533471,0.002559429,0.00256169,0.002566085,0.002573219,
0.002580028,0.002585025,0.002596933,0.002607249,0.002635368,
0.002654799,0.002658689,0.002659958,0.00267033,0.002671712,
0.002682649,0.002682815,0.002695585,0.002719267,0.002740274,
0.002743234,0.002745909,0.002751839,0.002769819,0.002774162,
0.00278442,0.002901342,0.002904116,0.002905503,0.002908674,
0.002911187,0.002912131,0.002917371,0.00292781,0.00293963,
0.002944884,0.002945726,0.002947415,0.002953362,0.002964787,
0.002973905,0.002989316,0.003009974,0.003021154,0.003027214,
0.003030365,0.003032497,0.003039665,0.003045135,0.003075974,
0.003078994,0.003115511,0.003119109,0.00313396,0.003154883,
0.003163152,0.00317028,0.003181947,0.003209376,0.003214299,
0.003226898,0.00340604,0.003406977,0.003409526,0.003412404,
0.003412929,0.003415513,0.003418361,0.00343543,0.003465806,
0.003468988,0.0034738,0.003490455,0.003520909,0.003536924,
0.003554049,0.003568319,0.003577896,0.003593967,0.003838045,
0.003888555,0.003903648,0.003938098,0.003946197,0.003949385,
0.003977947,0.003979403,0.004009518,0.004030246,0.004030843,
0.004036584,0.004052859,0.004074183,0.004076397,0.004089102,
0.004091774,0.004102168,0.004155362,0.004213037,0.004226247,
0.004227649,0.004251174,0.00428245,0.004365829,0.004392764,
0.004395163,0.004396143,0.004412838,0.004497309,0.004541017,
0.004547292,0.004586872,0.00459564,0.004639445,0.00484083,
0.004844042,0.005426287,0.005479669,0.005669934,0.005805711,
0.005900702,0.00594864,0.005956548,0.006067608,0.006103852,
0.00614831,0.006234852,0.006285052,0.00632075,0.006362597,
0.006367746,0.006432523,0.00652822,0.006535112,0.006548749,
0.006759323,0.006943223,0.006970861,0.00705717,0.007067064,
0.007144241,0.007149778,0.007165302,0.008635804,0.00874085,
0.008773917,0.008829465,0.008877117,0.008905313,0.008931805,
0.009000453,0.009022179,0.009141707,0.009172654,0.009205196,
0.009224926,0.009250782,0.009471276,0.00952517,0.00956001,
0.00958624,0.009643593,0.009712667,0.009717804,0.009779641,
0.009881726,0.010332421,0.010538809,0.010707288,0.010708647,
0.011130451,0.011335697,0.011400006,0.011697195,0.011757607,
0.01197547,0.012077955,0.012161494,0.012438468,0.012560695,
0.013007331,0.013031714,0.013057313,0.013744527,0.014306652,
0.014947571,0.01557332,0.015582146,0.01583741,0.015843315,
0.016194466,0.016651239,0.016666462,0.016770721,0.016993748,
0.017085579,0.017226334,0.017227287,0.017348885,0.01756645,
0.017593668,0.017633049,0.017790018,0.017864305,0.017960808,
0.017962111,0.01954508,0.019701256,0.019713417,0.019807884,
0.019808811,0.019862471,0.019863747,0.020042847,0.020088638,
0.020095797,0.020180588,0.02033226,0.020393114,0.020400076,
0.020410717,0.020476862,0.02057645,0.020805716,0.020881241,
0.021012329,0.021121462,0.021379604,0.021606602,0.021858013,
0.022047552,0.022063364,0.022075203,0.02226679,0.022334002,
0.022492535,0.022519215,0.022535414,0.022602722,0.022664114,
0.022756594,0.022867216,0.022876595,0.022878687,0.022916092,
0.022953289,0.024052523,0.0240592,0.024097673,0.024114542,
0.024166351,0.024180866,0.024291822,0.024329224,0.024347301,
0.024496601,0.02451395,0.02453303,0.024590317,0.024600372,
0.024635473,0.02464267,0.024738607,0.02474394,0.024776917,
0.024783764,0.024807535,0.024894995,0.024917044,0.024919609,
0.024927469,0.02504298,0.025106789,0.025257397,0.025273489,
0.025332218,0.0253661,0.025487543,0.025680393,0.02573258,
0.02577816,0.025855696,0.026077175,0.026091646,0.026350079,
0.026379145,0.026455114,0.026469519,0.026491879,0.026536737,
0.026647549,0.026671474,0.026676857,0.026726946,0.026788414,
0.026889845,0.026910501,0.026979816,0.027057377,0.027224081,
0.027420008,0.027663784,0.028069994,0.028083616,0.028128082,
0.028147736,0.028278393,0.028307131,0.02832221,0.028384531,
0.028456583,0.028554941,0.02876605,0.028862852,0.028885338,
0.030004465,0.030034399,0.030085606,0.030193855,0.03027816,
0.030306002,0.030334572,0.030442909,0.030478138,0.03052257,
0.030553375,0.030781395,0.030783609,0.030872231,0.030951096,
0.031059509,0.03110891,0.031118174,0.031167048,0.031333298,
0.031368473,0.031390257,0.03139909,0.031529272,0.031589396,
0.031636675,0.031645084,0.031657055,0.031724859,0.0317521,
0.031786081,0.031993765,0.032112068,0.032263416,0.032660732,
0.033331493,0.033569443,0.033620129,0.03369981,0.035050388,
0.035052685,0.035062099,0.035072723,0.03507505,0.03512682,
0.035170269,0.03525312,0.035412495,0.035471954,0.035582985,
0.035592876,0.035605473,0.035658188,0.035846397,0.037206734,
0.037333416,0.037348915,0.03744754,0.037515953,0.037589611,
0.037611568,0.037726643,0.037774162,0.037815498,0.037831062,
0.037859589,0.03789801,0.037920227,0.038058434,0.038330189,
0.038349991,0.038516602,0.038575977,0.038618732,0.038894112,
0.038911972,0.038945789,0.039059086,0.039129501,0.039307281,
0.039316605,0.039442234,0.039715927,0.040251358,0.040386848,
0.040608128,0.040653309,0.040834068,0.040904228,0.04111705,
0.041991753,0.04213179,0.042148109,0.042301144,0.042310837,
0.042325844,0.042357723,0.042492653,0.042553337,0.042814533,
0.042854767,0.042895061,0.042896851,0.043078533,0.043207798,
0.043215469,0.043297554,0.043437828,0.043633186,0.043729904,
0.043736271,0.04386861,0.044235691,0.044295734,0.044303661,
0.044355919,0.044367294,0.044528282,0.044551441,0.044662479,
0.044765278,0.044842533,0.044988815,0.046010056,0.04613068,
0.046153599,0.046160305,0.046177822,0.046352158,0.046474915,
0.046526058,0.046533958,0.046606754,0.046653526,0.046703358,
0.046814743,0.046840473,0.046842194,0.046864819,0.048001816,
0.048056213,0.048164001,0.048168396,0.048210941,0.048290314,
0.048407116,0.04842522,0.048439545,0.048497734,0.048606091,
0.048628529,0.048668831,0.048733147,0.048748436,0.04876532,
0.048806316,0.048840652,0.048895962,0.048930531,0.049014053,
0.049065144,0.049075142,0.049118752,0.049261322,0.049264027,
0.049266766,0.049273869,0.04958213,0.049746059,0.04977269,
0.049870342,0.04990234,0.049943096,0.049961391,0.050035896,
0.050260616,0.050665649,0.050679108,0.050785999,0.051097416,
0.051128338,0.051206776,0.0512766,0.051560677,0.051702911,
0.051747993,0.051844601,0.053401936,0.05355722,0.053739262,
0.053747524,0.053778954,0.053920906,0.053939686,0.053950542,
0.053990868,0.054041664,0.054052387,0.05409549,0.054115913,
0.054140182,0.054216995,0.054219185,0.054242241,0.054260532,
0.054267891,0.054351448,0.054411762,0.054428402,0.054490234,
0.05450872,0.054568832,0.054575714,0.054583591,0.054589977,
0.054607826,0.054725426,0.054731915,0.054810837,0.05482534,
0.054842644,0.054978245,0.056085217,0.056161369,0.056205143,
0.056266251,0.056409588,0.056476505,0.056570992,0.056642273,
0.056714523,0.056822758,0.056829235,0.056917904,0.056953751,
0.056968075,0.056971149,0.056973763,0.056992741,0.057030384,
0.057095741,0.05727071,0.057303009,0.057335926,0.057715866,
0.057786564,0.057870876,0.057958405,0.058124634,0.05818174,
0.058281132,0.058306877,0.058331551,0.058350685,0.058364559,
0.058508713,0.058513409,0.058545815,0.058560341,0.058664478,
0.058695694,0.058896667,0.058923962,0.058973892,0.059042507,
0.05922485,0.059373039,0.059410057,0.059526112,0.059828102,
0.059930466,0.060071568,0.060109196,0.060174408,0.060184357,
0.060195744,0.060244755,0.060263676,0.060320919,0.060332092,
0.060419029,0.060440163,0.060441895,0.060518112,0.06052655,
0.060532429,0.060565998,0.060681187,0.060718288,0.060792496,
0.060817093,0.060830311,0.060846893,0.060874416,0.060934357,
0.06096101,0.061165409,0.061252861,0.061285938,0.06130426,
0.061348621,0.061380657,0.061391655,0.061451218,0.061461361,
0.061473408,0.061648552,0.061793182,0.061798748,0.061933949,
0.062062805,0.06212022,0.06212368,0.062125854,0.062126251,
0.062127468,0.06214098,0.062146633,0.062181503,0.062239613,
0.062268009,0.062272697,0.062286636,0.062381355,0.06239917,
0.06244767,0.062545242,0.062595627,0.06265638,0.062817982,
0.062825661,0.063066998,0.063177608,0.063242683,0.063259178,
0.063273544,0.063316898,0.063559364,0.063629456,0.063651188,
0.063844891,0.063916458,0.063936954,0.063975487,0.065063019,
0.065074287,0.065088814,0.065121368,0.065128456,0.065146652,
0.065191307,0.065224121,0.065233879,0.065238533,0.065259407,
0.065268715,0.065329811,0.065330215,0.065384819,0.065409637,
0.065417625,0.065427483,0.065465027,0.065484879,0.065493431,
0.065610191,0.065642487,0.065681,0.065696053,0.065698174,
0.06573983,0.065779053,0.065789642,0.065800499,0.065868958,
0.065881882,0.065913086,0.066020386,0.066067833,0.066230667,
0.066463089,0.066516777,0.066581612,0.066655937,0.066710617,
0.066752296,0.066804337,0.066855156,0.066873413,0.066876305,
0.066926971,0.066962143,0.067041458,0.067110878,0.067141685,
0.067183189,0.067215302,0.067265244,0.067293709,0.067354927,
0.067414345,0.067525879,0.067715057,0.06796685,0.06798925,
0.068017555,0.068040932,0.068176918,0.068235809,0.068279007,
0.068322395,0.068337845,0.06834037,0.068439919,0.068452934,
0.068495689,0.068607246,0.068767647,0.068788399,0.068816803,
0.068926888,0.068964828,0.06897802,0.069019188,0.069030128,
0.069166847,0.069300362,0.0694758,0.069575676,0.069715981,
0.069838188,0.069982178,0.071051521,0.071083786,0.071163742,
0.071173775,0.071176941,0.071242485,0.071268684,0.071347374,
0.071362633,0.071410027,0.071411194,0.07141188,0.071434052,
0.071498039,0.071505699,0.071524452,0.071567215,0.071583656,
0.071611252,0.071640862,0.071648964,0.071807472,0.071823959,
0.071922653,0.071980492,0.072051407,0.072110909,0.072160057,
0.072171211,0.072173958,0.072210114,0.072215622,0.072308426,
0.072336052,0.072353981,0.072402443,0.072699097,0.072717751,
0.072746689,0.072791145,0.072793243,0.072849495,0.07290918,
0.07295916,0.073104393,0.073121567,0.073155327,0.073253578,
0.073369865,0.073425697,0.073430916,0.073451927,0.073502518,
0.073508347,0.073520653,0.073659286,0.073695137,0.073729439,
0.073798271,0.073801926,0.073817192,0.073851784,0.073865562,
0.073900055,0.073944313,0.073972275,0.07399675,0.075020401,
0.075035568,0.075059372,0.075121178,0.075128601,0.075136223,
0.075141953,0.075153847,0.075206886,0.075225517,0.075228622,
0.075275627,0.075302826,0.075360764,0.075361763,0.075366333,
0.075387573,0.075394257,0.075404877,0.075412811,0.07543412,
0.075478317,0.075492722,0.075521439,0.075563568,0.075585518,
0.075586739,0.075604187,0.075653114,0.075681366,0.075691528,
0.075692741,0.075697853,0.075759575,0.07579747,0.075824562,
0.075859688,0.075935951,0.075963562,0.076037514,0.076095131,
0.076115524,0.076214081,0.076214737,0.076316818,0.076380684,
0.076479767,0.076493149,0.076497581,0.076574974,0.076684616,
0.076698364,0.076703842,0.076776878,0.076866501,0.076894821,
0.07690065,0.077063698,0.077115448,0.077142189,0.077181068,
0.077242882,0.077271156,0.077324425,0.077339653,0.077362694,
0.077428749,0.077431015,0.077443291,0.077492828,0.077572067,
0.077707161,0.077754684,0.077783005,0.077909859,0.078051117,
0.078129082,0.078174446,0.078207794,0.078214737,0.078216637,
0.078241058,0.078325661,0.078407417,0.078441513,0.07845079,
0.078473663,0.078502983,0.078538216,0.078555923,0.07859079,
0.078633698,0.078833298,0.078851852,0.078860931,0.078871025,
0.078882385,0.07889286,0.078949646,0.079076752,0.079163239,
0.079195503,0.07924736,0.079262436,0.079328995,0.079379135,
0.07941008,0.079424858,0.079457695,0.079470543,0.07952784,
0.079559853,0.079647728,0.079691895,0.07974955,0.07979583,
0.079868042,0.079921974,0.079950584,0.080010582,0.080125328,
0.080153259,0.080206261,0.080228867,0.080229256,0.080230118,
0.080272995,0.080279228,0.08028598,0.080365288,0.080571617,
0.080608131,0.080628929,0.08063559,0.080706238,0.08073233,
0.080778938,0.080805153,0.080874664,0.080879776,0.080919357,
0.080938713,0.080978775,0.081149384,0.081193855,0.081195839,
0.081213516,0.081221527,0.081231758,0.081260941,0.081322891,
0.081366234,0.081374321,0.081415192,0.081436249,0.081467484,
0.081482147,0.081546585,0.081589027,0.081610306,0.081621864,
0.081658104,0.081684708,0.081698761,0.08172625,0.081727242,
0.081727577,0.081756065,0.081806023,0.081816452,0.081897446,
0.081941254,0.081956078,0.081963615,0.082257622,0.082284782,
0.082659737,0.082679955,0.082683884,0.082783669,0.08280085,
0.082828438,0.082839478,0.083057045,0.083090103,0.083095436,
0.083168533,0.083283463,0.083291489,0.0832994,0.083365768,
0.083438248,0.083455276,0.083552826,0.0837285,0.083759331,
0.083770958,0.083803131,0.083805771,0.083825157,0.083834328,
0.083834694,0.083848076,0.083910973,0.083971878,0.084069618,
0.08423806,0.08434269,0.08435183,0.084414528,0.084510124,
0.084563164,0.08467041,0.084679123,0.084882431,0.084924339,
0.084943542,0.08505986,0.085070206,0.085132957,0.085187439,
0.085385277,0.085416901,0.085464722,0.085500687,0.085570168,
0.085592735,0.085658707,0.085669724,0.08567659,0.085700851,
0.085775253,0.08580024,0.085810524,0.085833519,0.085891106,
0.085900436,0.085904266,0.085961868,0.08596653,0.086123375,
0.086218636,0.086307068,0.086312355,0.086400322,0.086460159,
0.086481964,0.086559967,0.086655151,0.086802086,0.086926544,
0.086978081,0.087017365,0.087045807,0.087046921,0.087083038,
0.087107971,0.087123604,0.08715905,0.087162422,0.087178558,
0.087226822,0.087229706,0.087262688,0.087376724,0.087396065,
0.08739843,0.08744915,0.087452667,0.087467918,0.087468719,
0.087480934,0.08748204,0.087503723,0.087547005,0.087607384,
0.087617149,0.087664093,0.087677788,0.087683189,0.087714668,
0.087754532,0.087817635,0.087841156,0.087888611,0.087919395,
0.087951073,0.087976471,0.088008675,0.088044807,0.088204453,
0.088237503,0.088241859,0.088337479,0.088351883,0.088438828,
0.088564034,0.08857486,0.088577209,0.088631973,0.088695297,
0.088828033,0.088874825,0.089028625,0.089045425,0.089151863,
0.089194473,0.089196846,0.089207123,0.08925589,0.089289177,
0.089310211,0.08934153,0.089457504,0.089475464,0.089514038,
0.089519783,0.08960025,0.089623367,0.089673851,0.089783417,
0.089853577,0.089934891,0.089938499,0.090045471,0.090188393,
0.090195862,0.090239204,0.090348083,0.090352417,0.090369087,
0.090411438,0.090453651,0.090472084,0.090534058,0.090643921,
0.090677444,0.090848145,0.090922867,0.091004951,0.091066589,
0.091103401,0.091138275,0.091149933,0.091166771,0.091167122,
0.091206741,0.091206772,0.091232628,0.091238304,0.091251312,
0.09133548,0.091340736,0.091369453,0.091378548,0.091428207,
0.091494667,0.091510735,0.091516327,0.091541229,0.091587395,
0.091598114,0.091606995,0.09162661,0.091687927,0.091709015,
0.0917258,0.091753426,0.091833153,0.091836357,0.091843979,
0.091919655,0.091971672,0.092083015,0.092112175,0.092142387,
0.092204819,0.092305725,0.092321426,0.092343269,0.092360497,
0.092388412,0.09245826,0.092510864,0.09256897,0.092571167,
0.092574814,0.092704338,0.092738327,0.09277005,0.092788498,
0.092824203,0.092888962,0.092925606,0.093001549,0.093013161,
0.093033226,0.093061501,0.093067924,0.093169502,0.093176643,
0.093307121,0.093331367,0.093337448,0.093339378,0.093406952,
0.093429413,0.093431274,0.09356115,0.093599548,0.093601158,
0.093608398,0.093656494,0.093656906,0.093677673,0.09376564,
0.093811554,0.093872833,0.093898094,0.093922104,0.093928162,
0.093971664,0.094053101,0.094091293,0.094115646,0.09415995,
0.094194,0.094249725,0.094370384,0.094489487,0.094646332,
0.094730484,0.094763489,0.094799568,0.094907761,0.095020546,
0.095037056,0.095045547,0.095078125,0.095097366,0.095155159,
0.09517926,0.095197853,0.095200798,0.095205849,0.095226028,
0.095245605,0.095322136,0.095335655,0.09537896,0.095569435,
0.095664223,0.095678696,0.095713676,0.095760208,0.095768143,
0.095779831,0.095788185,0.095821175,0.095825287,0.095860001,
0.095865356,0.095871017,0.095956985,0.095967674,0.095986183,
0.096003571,0.0960243,0.096035515,0.096238373,0.096250763,
0.096260361,0.09626561,0.096303009,0.096322289,0.096346275,
0.09643148,0.096437553,0.09652877,0.096538582,0.096603691,
0.09661869,0.096646645,0.096650383,0.09671386,0.096814606,
0.096821739,0.096867737,0.096952148,0.096971573,0.09700267,
0.097061447,0.097064804,0.097072891,0.097146729,0.097155418,
0.097178787,0.097207573,0.09721949,0.097318459,0.097356339,
0.097448364,0.097456512,0.097479301,0.097527084,0.097589104,
0.097625275,0.097732323,0.097799866,0.097808548,0.097838356,
0.097965363,0.098010567,0.098049889,0.098062645,0.098160362,
0.098161728,0.098226433,0.098272339,0.098337234,0.098378304,
0.098384911,0.098398613,0.098467316,0.098488495,0.09851284,
0.098636589,0.09867498,0.09873864,0.09877298,0.098802223,
0.098938744,0.098944267,0.09895649,0.098981583,0.098984978,
0.098998871,0.099002022,0.09901268,0.099017548,0.099023087,
0.099028282,0.099045372,0.099046082,0.099051651,0.099071533,
0.099078453,0.099116837,0.099136055,0.099152527,0.099162422,
0.099169701,0.099171959,0.099172142,0.099192627,0.099192978,
0.099194649,0.099195084,0.099206505,0.099210869,0.099245331,
0.099258652,0.09925901,0.099261093,0.099262009,0.099279251,
0.099283455,0.099293274,0.099295555,0.099302444,0.099311691,
0.099314125,0.099319046,0.099333069,0.099335777,0.099336807,
0.099340225,0.099354042,0.099355385,0.099358322,0.099371262,
0.099377304,0.099389893,0.09939399,0.099401428,0.099403114,
0.09940596,0.099421577,0.099422241,0.099439224,0.099456284,
0.09945697,0.099482925,0.099484779,0.099491615,0.099498932,
0.0995037,0.099530594,0.099538361,0.09954554,0.099548683,
0.09955619,0.099568939,0.099571426,0.099577255,0.099577927,
0.099581398,0.099589081,0.099592934,0.099631828,0.099633728,
0.09964048,0.099642845,0.099649345,0.099650345,0.099657043,
0.099692734,0.099705635,0.099714005,0.099724358,0.099726639,
0.099742874,0.099748917,0.09975087,0.099762749,0.099764763,
0.09979599,0.099796585,0.099813614,0.099813919,0.099824776,
0.099833282,0.099840034,0.099853157,0.099855286,0.099856972,
0.099863815,0.099870338,0.099878838,0.09987925,0.099881104,
0.099881638,0.099884171,0.099890068,0.099906731,0.099917572,
0.099918915,0.099923584,0.099930031,0.099936157,0.099944717,
0.09994886,0.099953995,0.099957123,0.09996846,0.099973228,
0.09999337,0.100070778,0.100108696,0.100147758,0.100150429,
0.100185005,0.100204865,0.100244308,0.100253357,0.100307373,
0.100324341,0.100454742,0.100539536,0.100646408,0.100701958,
0.100718155,0.100755775,0.100789124,0.100895454,0.100950203,
0.100963501,0.101067299,0.101233002,0.101290993,0.101305809,
0.101446243,0.101493774,0.101555672,0.101719284,0.101876625,
0.101896919,0.101899063,0.101961182,0.102034859,0.102040016,
0.102069847,0.102081863,0.10213443,0.102178406,0.102189293,
0.102198669,0.102307854,0.102463081,0.102471527,0.102472313,
0.102679504,0.102861572,0.102946815,0.103052025,0.103095436,
0.103106979,0.103118065,0.10316008,0.103173233,0.103277779,
0.103288628,0.103365341,0.103370041,0.103404457,0.103488327,
0.103589912,0.103637566,0.103697342,0.10374469,0.103871254,
0.103895668,0.103906837,0.103932281,0.103943245,0.103987,
0.104036873,0.104162529,0.104166817,0.104216446,0.104363678,
0.104389488,0.104414368,0.104433372,0.104474297,0.104477715,
0.104529388,0.104573639,0.104588905,0.104698006,0.104730003,
0.104776169,0.104854424,0.104879364,0.105079468,0.105119461,
0.105120369,0.105222862,0.105271782,0.105330635,0.105354622,
0.105360535,0.105511238,0.105515617,0.105548302,0.105560455,
0.105586731,0.105695229,0.106155327,0.106206032,0.106207352,
0.106231392,0.106256821,0.106257904,0.106266983,0.10627993,
0.106340767,0.106385849,0.106505066,0.106568001,0.106573853,
0.106576485,0.106688934,0.106781197,0.106906662,0.106947388,
0.107002007,0.107009499,0.107107727,0.107151871,0.107154892,
0.107171547,0.10736161,0.107404007,0.107446632,0.107481575,
0.107571106,0.107680084,0.107762352,0.107812027,0.107817505,
0.107831871,0.107927155,0.108101425,0.108117813,0.108119751,
0.108219154,0.108243515,0.108270782,0.108273544,0.108335297,
0.108360939,0.108372711,0.10838253,0.10839019,0.108508354,
0.108606544,0.10882003,0.108834808,0.108835617,0.108861816,
0.108873787,0.108882278,0.108962418,0.108995377,0.109031288,
0.109201584,0.109208542,0.109220985,0.109231804,0.109241402,
0.109252884,0.109364464,0.109376625,0.109406441,0.109441811,
0.109594093,0.109602798,0.109620834,0.109665497,0.109668068,
0.10973185,0.109745071,0.109753563,0.109779037,0.109911179,
0.11003537,0.11016272,0.110167198,0.11021212,0.110321335,
0.110326187,0.110395599,0.110454697,0.110456169,0.110467133,
0.110473755,0.110503029,0.110507202,0.11056105,0.110577637,
0.110614357,0.110650528,0.110756195,0.110768173,0.110773499,
0.110824158,0.11088311,0.110899239,0.110908638,0.110997444,
0.111032936,0.111108879,0.111142799,0.111204697,0.111205872,
0.111217064,0.111229439,0.111279587,0.111363297,0.111376785,
0.111414925,0.111466507,0.111484619,0.111555641,0.111585228,
0.111586647,0.111629646,0.111643074,0.111652039,0.1116847,
0.111688362,0.111732407,0.111760681,0.111797699,0.111807571,
0.111808884,0.111818726,0.111855774,0.111878281,0.111932999,
0.111965073,0.111978386,0.112010513,0.112010834,0.112076721,
0.112120728,0.11214518,0.112151443,0.11215361,0.112310898,
0.112320869,0.112332359,0.112364273,0.112476952,0.112531715,
0.11253392,0.112562553,0.112696274,0.112701172,0.11270636,
0.112718735,0.112775734,0.112776077,0.112840691,0.112888855,
0.1129105,0.113161476,0.113180275,0.113245117,0.113258018,
0.113289429,0.113305161,0.113314133,0.113376755,0.113391739,
0.113393776,0.113396317,0.113406815,0.113450806,0.113460693,
0.113572594,0.113593613,0.11364328,0.11364814,0.113698845,
0.113709442,0.113709633,0.113734398,0.113749886,0.113805824,
0.113882729,0.113957535,0.114041168,0.114045853,0.114049324,
0.114069984,0.114130463,0.114184875,0.114196091,0.114231003,
0.114260208,0.11427948,0.11436882,0.114389458,0.114438484,
0.114571838,0.114635307,0.114641602,0.114699554,0.114721123,
0.114768585,0.114839661,0.115019608,0.115019844,0.11506044,
0.115064056,0.115077438,0.115102104,0.115225578,0.115235413,
0.115293266,0.115312813,0.115441467,0.115495148,0.11562896,
0.115631996,0.115797218,0.115802162,0.115818192,0.115833611,
0.116026489,0.116045929,0.116199661,0.116205215,0.116239243,
0.116447601,0.116483406,0.116558121,0.116600266,0.116668541,
0.116669189,0.116727943,0.116734375,0.116764412,0.116783043,
0.116786278,0.116831383,0.116866051,0.116902596,0.116944473,
0.11698204,0.117053452,0.117211891,0.11721888,0.117268433,
0.117298973,0.117421982,0.117518616,0.117559052,0.117593384,
0.117608444,0.117640274,0.117679855,0.117695076,0.117725624,
0.117729256,0.117750389,0.117876488,0.117887367,0.117916618,
0.117984062,0.11798864,0.11798983,0.118005844,0.118021454,
0.118038063,0.118045418,0.118123878,0.118127747,0.118170288,
0.11820562,0.118216629,0.118219215,0.118222557,0.118285034,
0.118308388,0.118316612,0.118332664,0.118404297,0.118433929,
0.11844162,0.118485298,0.118497711,0.118641678,0.118672134,
0.118706955,0.118846001,0.118881233,0.118884789,0.118901047,
0.118931961,0.119025764,0.119042831,0.119047035,0.119128349,
0.119137115,0.119280975,0.119342384,0.119459045,0.11945932,
0.119478439,0.11952137,0.119598328,0.11960302,0.119632133,
0.119713142,0.119769905,0.119784332,0.119839203,0.119839363,
0.119866699,0.119867691,0.120189461,0.120300972,0.120537003,
0.120609261,0.120655991,0.120761551,0.120781868,0.12088295,
0.12099321,0.120997337,0.121004021,0.121234344,0.121264816,
0.121271149,0.121308754,0.121350693,0.121413063,0.121442444,
0.121445145,0.121490562,0.121548767,0.121610481,0.121632271,
0.121637131,0.121700943,0.121776505,0.121915749,0.121919823,
0.12200354,0.122036636,0.122163551,0.122326813,0.122429337,
0.122569275,0.122613106,0.122669235,0.122686943,0.122778961,
0.12281469,0.122945724,0.123029648,0.12304937,0.123087112,
0.123145462,0.123160034,0.123186493,0.123203781,0.123274773,
0.123374832,0.12344928,0.12347007,0.12352166,0.123573036,
0.123582474,0.12359166,0.123683441,0.123727425,0.123766579,
0.123816483,0.12384626,0.123917076,0.123929771,0.123936646,
0.12394136,0.124107071,0.124214821,0.124249519,0.124259254,
0.124282974,0.12429229,0.124300041,0.124333847,0.124346642,
0.124352936,0.124372612,0.12440892,0.124440674,0.124441917,
0.124468109,0.124522743,0.124556656,0.124565483,0.124605057,
0.124660919,0.124719315,0.12483091,0.124836121,0.124841515,
0.125041382,0.12510836,0.12512114,0.125142235,0.125158188,
0.125185341,0.125300659,0.125379143,0.125486412,0.125526749,
0.125535408,0.125541084,0.125612389,0.125614304,0.125658279,
0.125862709,0.125863831,0.125866989,0.12590963,0.125915306,
0.125949577,0.125962807,0.126010582,0.12611351,0.126126419,
0.12613562,0.126187004,0.126203171,0.126296906,0.126413475,
0.12646624,0.126541611,0.126612282,0.126710007,0.126730209,
0.126767975,0.12680043,0.12681012,0.126819077,0.126819397,
0.126980461,0.126993683,0.127003403,0.127152191,0.127188919,
0.127236618,0.127243324,0.127243622,0.127303459,0.127308228,
0.127347672,0.127519569,0.127597374,0.127738869,0.127739105,
0.12788131,0.127997917,0.128162521,0.12818869,0.128271683,
0.128273895,0.128280807,0.128347137,0.128358307,0.128377701,
0.128409836,0.128420181,0.128619827,0.128625534,0.128653809,
0.128724854,0.128752533,0.128774078,0.128792267,0.128828659,
0.128854935,0.128873428,0.129044418,0.129085815,0.129112381,
0.129162567,0.129214218,0.129310867,0.129337799,0.12937178,
0.129429688,0.129450577,0.129458038,0.129634888,0.129701263,
0.129747894,0.129750839,0.129751007,0.129837601,0.129889618,
0.129961807,0.130001587,0.13002243,0.130059326,0.130077011,
0.130079529,0.130091995,0.130093903,0.130095047,0.130104431,
0.130176483,0.130232178,0.130238403,0.130260834,0.130267593,
0.130268326,0.130278305,0.13034642,0.130396912,0.130419525,
0.130460663,0.130503204,0.13051532,0.130543701,0.130585373,
0.130603592,0.130681992,0.130747025,0.130802444,0.13080249,
0.13084993,0.130874268,0.130884262,0.130960068,0.130964005,
0.13098912,0.13102092,0.131033997,0.131090698,0.131176071,
0.13122879,0.131296005,0.131299881,0.131369461,0.131382263,
0.131395905,0.131415253,0.1314991,0.131531784,0.131570831,
0.131619095,0.131655518,0.131657623,0.13167717,0.131687042,
0.131722275,0.131731323,0.131785034,0.131808594,0.131833252,
0.132008804,0.132080795,0.13210791,0.132165451,0.132196091,
0.132204132,0.132227402,0.132374557,0.132504074,0.132594376,
0.132665543,0.13267038,0.132743866,0.132787766,0.132815781,
0.132816391,0.132858078,0.132860962,0.132861801,0.132872513,
0.132919739,0.132943207,0.133148148,0.133172501,0.133177795,
0.133277939,0.133280777,0.133295181,0.133304306,0.133414536,
0.133530518,0.133537445,0.133562378,0.133644608,0.133765793,
0.133834396,0.133893372,0.133969376,0.13396994,0.134008698,
0.134039276,0.134041,0.134116516,0.134299973,0.134309311,
0.134328583,0.134343872,0.134350037,0.134368011,0.134370026,
0.134375748,0.134413162,0.134420319,0.134434235,0.134616074,
0.134616287,0.134672867,0.134700607,0.134770523,0.134782379,
0.13483316,0.134874725,0.134881744,0.134891617,0.134917252,
0.134928925,0.134957352,0.135065094,0.135121445,0.135153275,
0.135215469,0.135247375,0.135269989,0.135277466,0.135413101,
0.135508713,0.135531403,0.135571167,0.135641571,0.135648315,
0.13570253,0.135745819,0.13574649,0.13578894,0.135870789,
0.135887665,0.135903854,0.135910263,0.135976822,0.135986359,
0.136019241,0.136132263,0.136166306,0.136261627,0.136390472,
0.136503036,0.136598068,0.136628723,0.136638824,0.136692764,
0.136911591,0.136975464,0.137045624,0.137102051,0.137104523,
0.137139374,0.137184097,0.137216934,0.137248856,0.137402161,
0.137432693,0.137529663,0.137545395,0.137735306,0.137759689,
0.137781845,0.137821518,0.137866547,0.137894318,0.137905869,
0.137979477,0.138063522,0.138073441,0.138108704,0.138109192,
0.138196182,0.138274872,0.138332794,0.138343781,0.138352097,
0.138360931,0.138379196,0.138382736,0.138451965,0.138471085,
0.13852504,0.13852536,0.138534485,0.138644424,0.138684814,
0.138821396,0.138827026,0.138875717,0.138928055,0.138996948,
0.139048111,0.139089111,0.139099197,0.139124588,0.139136292,
0.139143585,0.139176025,0.139285889,0.139322189,0.139390991,
0.139416138,0.139424973,0.139449966,0.139531097,0.139682205,
0.139697983,0.139726395,0.139751205,0.13978508,0.13989418,
0.139939606,0.139979202,0.140054398,0.140056366,0.140077972,
0.14007872,0.140081863,0.140086868,0.140096771,0.140103134,
0.140111832,0.140125336,0.140159592,0.140197983,0.140250839,
0.140284805,0.140331116,0.140411896,0.140532181,0.140545517,
0.140546234,0.14055777,0.140653961,0.140670959,0.140799988,
0.140819626,0.140877731,0.140966049,0.140971207,0.140993958,
0.141034348,0.141041046,0.141075958,0.141119766,0.141242081,
0.141276398,0.141325439,0.141326187,0.141365799,0.141367126,
0.141481781,0.141637085,0.141651886,0.141685883,0.141783417,
0.14184053,0.141875137,0.141877319,0.141937134,0.141942245,
0.141990112,0.142120636,0.142129379,0.142169189,0.142191406,
0.142208023,0.142213715,0.142262878,0.142277939,0.14227829,
0.142278351,0.142358109,0.142404083,0.14241571,0.142539398,
0.142640884,0.142658798,0.142661377,0.142664474,0.142666656,
0.142775024,0.142831711,0.142861816,0.142883118,0.142888351,
0.143047745,0.143053986,0.143059204,0.143184845,0.143199158,
0.143213638,0.143296005,0.143319916,0.143393784,0.143402893,
0.14341362,0.143418732,0.143420715,0.143462418,0.143524612,
0.143560333,0.143605438,0.143620499,0.143649323,0.143666626,
0.14369223,0.143888016,0.143961731,0.143992447,0.144002121,
0.144134338,0.144220047,0.144261978,0.144336777,0.1443591,
0.144449326,0.144524048,0.144539841,0.14461853,0.144654465,
0.144657959,0.144710617,0.144716721,0.144746262,0.1447547,
0.14481044,0.144825546,0.144831253,0.144945984,0.144946976,
0.144961014,0.145005463,0.145012131,0.145012314,0.145018295,
0.145070969,0.14508255,0.145117264,0.145173706,0.145185104,
0.145195389,0.145215332,0.145216766,0.145225739,0.145274445,
0.145329834,0.145336945,0.145337265,0.145354019,0.145379578,
0.145473892,0.14552066,0.145578186,0.145616516,0.145709366,
0.14577594,0.145847,0.145911041,0.145979599,0.145984634,
0.146024246,0.146024796,0.146092133,0.146140152,0.146155167,
0.146165787,0.146171768,0.146194199,0.146220718,0.146258713,
0.146260651,0.146284317,0.146349152,0.146353851,0.146395203,
0.14641629,0.146423309,0.146469101,0.146488541,0.146505615,
0.146522446,0.146598007,0.146636078,0.146696548,0.146733246,
0.146735275,0.14679184,0.146926559,0.146927673,0.146965317,
0.147020432,0.147056885,0.147066238,0.147071701,0.147079147,
0.147171371,0.147217133,0.147307159,0.147366013,0.14746312,
0.147477859,0.147562134,0.147563339,0.147628998,0.147639236,
0.147743088,0.147747696,0.147748932,0.147757095,0.147775909,
0.147791718,0.147830765,0.147864655,0.147924454,0.147933151,
0.147964432,0.147982513,0.148088577,0.148090714,0.148156876,
0.1481633,0.148174683,0.148234268,0.148250458,0.148327957,
0.148380463,0.148413147,0.148435562,0.148486084,0.148497238,
0.148546173,0.148554443,0.148684952,0.148690445,0.148861206,
0.148884949,0.14894339,0.148982925,0.149020172,0.149045044,
0.149132553,0.149202072,0.149368942,0.149428497,0.149454285,
0.149477982,0.149543457,0.14962236,0.149627472,0.14974617,
0.149759628,0.149773575,0.149790268,0.14983847,0.149853745,
0.149853836,0.149864487,0.149869919,0.149941116,0.149966232,
0.150017242,0.150018219,0.150072266,0.150078598,0.150082733,
0.150104202,0.150200165,0.150227585,0.150230896,0.150300873,
0.150370087,0.150410812,0.150683441,0.150714813,0.150716461,
0.150724197,0.150728027,0.150775253,0.150825989,0.150848236,
0.150850037,0.150893158,0.150899811,0.150919006,0.150932938,
0.150952942,0.150986786,0.151047226,0.151048752,0.151057175,
0.151085617,0.151105988,0.15116275,0.151175873,0.151191635,
0.151246964,0.151259598,0.151273468,0.151275131,0.151310608,
0.151432205,0.151455505,0.151466476,0.151482468,0.151519501,
0.151528351,0.151587463,0.151631027,0.151786758,0.151824463,
0.15187352,0.151875031,0.151920105,0.151928268,0.151967972,
0.151973938,0.151980179,0.152047089,0.152054398,0.152100754,
0.152117035,0.152133331,0.152167679,0.152209579,0.152210281,
0.152262009,0.152352737,0.152387161,0.152568008,0.152605118,
0.152626434,0.152687134,0.152708389,0.152710678,0.152744797,
0.152762924,0.152770859,0.15280397,0.152810928,0.152831421,
0.15283493,0.152840393,0.152910095,0.152958389,0.152983749,
0.153040054,0.153108978,0.153207184,0.153209808,0.153222504,
0.153254944,0.153267822,0.153268555,0.153291656,0.153341202,
0.153411057,0.153458893,0.153487137,0.153492615,0.153504547,
0.153536697,0.153561356,0.153611572,0.153630249,0.15363446,
0.153637863,0.153647949,0.153709152,0.153718323,0.153761353,
0.153785568,0.153880493,0.153962769,0.153979355,0.154018036,
0.154057297,0.154143921,0.154167343,0.154190369,0.154193665,
0.154197891,0.154216202,0.154236298,0.154258713,0.154304642,
0.1543815,0.154391235,0.154450089,0.15448735,0.154488724,
0.154588257,0.154589371,0.154595337,0.154627197,0.154628021,
0.154657715,0.154671661,0.154705856,0.154714828,0.154721664,
0.154856735,0.154911789,0.154939514,0.154939774,0.155045914,
0.155121552,0.155137451,0.155293945,0.155324493,0.155343781,
0.15536882,0.155382462,0.155415146,0.155444687,0.155446136,
0.155478012,0.155506851,0.155555191,0.155564148,0.155564346,
0.155576492,0.1555858,0.155592896,0.155681793,0.155802246,
0.155843582,0.155863663,0.155892029,0.155920242,0.155991211,
0.156003632,0.156181656,0.156201004,0.156271362,0.156272751,
0.156295837,0.156310516,0.156384796,0.156500305,0.156519531,
0.156534134,0.1565401,0.15657756,0.156619141,0.15662175,
0.156639374,0.156646759,0.156649521,0.156660446,0.156662613,
0.156664719,0.156699341,0.156749039,0.156783966,0.15679303,
0.156808212,0.156810944,0.156839172,0.156858475,0.156880417,
0.156888321,0.156891891,0.156901917,0.156910309,0.156963058,
0.156988586,0.157004242,0.15700502,0.157037262,0.157041061,
0.157045761,0.157057678,0.157058502,0.15707399,0.157147278,
0.157231476,0.157262558,0.157280533,0.157358566,0.157377747,
0.157418961,0.157424911,0.15742659,0.157509018,0.157520203,
0.157529572,0.157612701,0.157632843,0.157664963,0.157697769,
0.157807388,0.157850403,0.157858917,0.157875412,0.15788974,
0.157969772,0.158092453,0.158096588,0.158110306,0.15814534,
0.158146332,0.158172653,0.158206436,0.158208923,0.158306931,
0.158331131,0.158407516,0.158438644,0.158452942,0.158455597,
0.158468216,0.158486496,0.158511581,0.158531815,0.158559692,
0.158571152,0.15857251,0.158592941,0.158597275,0.158687347,
0.158690506,0.158698532,0.158717087,0.158763443,0.158826828,
0.158827545,0.158834839,0.15883847,0.158856247,0.158998871,
0.159015564,0.159047897,0.159101776,0.159131226,0.159157639,
0.159159714,0.159186905,0.159191757,0.159267563,0.159336655,
0.159351181,0.159470306,0.15952626,0.159548477,0.159591232,
0.159593933,0.159708755,0.159742905,0.159812286,0.159918304,
0.159969009,0.159976593,0.159991348,0.160032227,0.160051544,
0.160054688,0.160088425,0.160112457,0.160153305,0.160223633,
0.160266693,0.16028566,0.160293152,0.160299286,0.160322403,
0.160336777,0.160413696,0.160437103,0.160454575,0.160464874,
0.16063652,0.160688202,0.160764145,0.160815247,0.16086145,
0.160884766,0.161036011,0.161088867,0.161098709,0.161194656,
0.161218918,0.16125209,0.161254501,0.161432068,0.161464127,
0.161497406,0.161517288,0.161550873,0.16158905,0.161636536,
0.161639954,0.161693405,0.161697266,0.161735352,0.161737793,
0.161801346,0.161824921,0.161828369,0.161837036,0.161858521,
0.161972015,0.161973221,0.161990601,0.162015762,0.162018112,
0.162072678,0.162254761,0.162261368,0.162344406,0.16237439,
0.162376816,0.162417953,0.162422943,0.162423096,0.162444992,
0.162490265,0.162494202,0.162540939,0.16259758,0.162642761,
0.162723206,0.162810165,0.162877838,0.162903122,0.162939178,
0.162971542,0.162982407,0.163023682,0.163036331,0.163049103,
0.163054642,0.163065918,0.163148056,0.163181656,0.163193283,
0.163213989,0.163252396,0.163256149,0.163314743,0.163367676,
0.163423126,0.163518845,0.163548462,0.163661118,0.163673065,
0.163765305,0.1637686,0.163901489,0.163938248,0.164045578,
0.164112885,0.16415361,0.164189941,0.164211105,0.164220901,
0.164243088,0.164356155,0.164410873,0.164420776,0.164431885,
0.164436676,0.164438263,0.164442398,0.164493896,0.164499008,
0.16450351,0.164519348,0.164548615,0.1647164,0.164755585,
0.164803345,0.164808838,0.16481813,0.164849792,0.164973068,
0.164979904,0.165088181,0.165103699,0.16520134,0.165241425,
0.165264297,0.165286224,0.165470108,0.165497574,0.16550769,
0.165624847,0.165696213,0.165708572,0.165754501,0.165783493,
0.16584938,0.165888855,0.165895996,0.165963379,0.16598497,
0.166019806,0.166090958,0.166101868,0.166106339,0.166108917,
0.166167282,0.16617572,0.16633783,0.166353653,0.166441971,
0.166513123,0.166545212,0.166582153,0.166590134,0.16663443,
0.16668248,0.166827133,0.166922073,0.166954208,0.166954895,
0.166962799,0.166998352,0.167066208,0.167117477,0.167217422,
0.167239349,0.167287521,0.167428619,0.167476395,0.167512726,
0.167563049,0.167685608,0.167705994,0.167737076,0.167761871,
0.167791275,0.16814296,0.16828772,0.168319595,0.16832814,
0.168467133,0.168499435,0.16853598,0.168537903,0.168581299,
0.168591904,0.168623108,0.168624954,0.168656601,0.168681885,
0.168690598,0.168691269,0.168750076,0.168775436,0.168837708,
0.168840012,0.168928131,0.168961288,0.168963287,0.169002914,
0.169055161,0.169075562,0.169120148,0.169138748,0.169144745,
0.169247986,0.169266785,0.169284271,0.169315628,0.169344559,
0.169505692,0.169512131,0.169583206,0.16961615,0.169622253,
0.169633408,0.16964357,0.169676025,0.169685593,0.169746124,
0.169862976,0.169896194,0.169899719,0.169923462,0.169935074,
0.169986053,0.170047226,0.170093491,0.170209366,0.170279755,
0.170306625,0.170386581,0.170398071,0.170414124,0.170518829,
0.170521301,0.170543991,0.170559875,0.170616287,0.170768936,
0.170906998,0.170947983,0.170990936,0.171088669,0.171103653,
0.171156982,0.171262695,0.171294037,0.171294128,0.171380829,
0.171414322,0.171518112,0.171565475,0.171573425,0.171595215,
0.171621964,0.171625015,0.171639099,0.171687805,0.171698898,
0.171720078,0.171722305,0.17180246,0.171861115,0.172040176,
0.172185379,0.172186539,0.172215836,0.172272446,0.17236351,
0.172416153,0.172488312,0.172509171,0.172528122,0.172608978,
0.172632889,0.17263385,0.172755234,0.17281015,0.172834961,
0.17297789,0.172979004,0.172980637,0.173009018,0.173014694,
0.173031281,0.173034058,0.173064285,0.173102661,0.173127823,
0.173135376,0.173176971,0.173179245,0.173184738,0.173416565,
0.173429916,0.17349881,0.173600922,0.173715424,0.173738541,
0.173743835,0.173790054,0.173797836,0.173817841,0.173838516,
0.173843552,0.173934891,0.173938843,0.173975449,0.174002747,
0.174025375,0.174030167,0.174052612,0.174060379,0.174090256,
0.174099091,0.174133636,0.17419043,0.174194473,0.174223831,
0.174266525,0.174268143,0.174287994,0.17430217,0.174331085,
0.17433522,0.174381149,0.174396317,0.17443512,0.174466934,
0.17450322,0.174765182,0.174780151,0.174792542,0.17485144,
0.174907715,0.174979477,0.174991287,0.175027573,0.175028259,
0.175038651,0.175042343,0.175045624,0.1750466,0.175053558,
0.175096146,0.175096848,0.175220062,0.175240784,0.175263733,
0.175353455,0.175389725,0.175413651,0.175429626,0.175456848,
0.175521286,0.175525635,0.175600281,0.175619858,0.175624832,
0.175691788,0.17572789,0.175781647,0.175812424,0.17591301,
0.175975052,0.175977646,0.175990631,0.176000824,0.17600412,
0.17603775,0.176059433,0.176065613,0.176078064,0.176085541,
0.176100677,0.176108047,0.176157715,0.176179428,0.176188293,
0.176217957,0.176244522,0.176245819,0.176262726,0.176282639,
0.176295425,0.176304031,0.176309341,0.176322968,0.176389816,
0.176414276,0.176496887,0.176499283,0.176633087,0.176754623,
0.176783661,0.176792358,0.176805878,0.176892258,0.176899567,
0.176901627,0.176941101,0.176949722,0.177102737,0.177152618,
0.177159195,0.177165268,0.177196045,0.177208511,0.177231979,
0.177271957,0.177275131,0.177297302,0.17736232,0.177429108,
0.177476974,0.177537109,0.177560287,0.177634552,0.177657562,
0.177672424,0.177675888,0.177689789,0.177718063,0.177723862,
0.177763367,0.177800079,0.177801056,0.177827316,0.177846924,
0.177889496,0.177905991,0.177915009,0.177915344,0.177956223,
0.178019363,0.178073486,0.17812291,0.178166016,0.178240509,
0.17828009,0.178350723,0.178371323,0.178382095,0.178493713,
0.178521225,0.178533707,0.178604156,0.178679642,0.178709351,
0.178716003,0.17871936,0.17874826,0.178771896,0.178880234,
0.17891214,0.178915543,0.178926208,0.178937317,0.178938782,
0.178996323,0.17900235,0.179062546,0.179089188,0.179121353,
0.179172867,0.179173111,0.1792155,0.179222809,0.179233307,
0.179270355,0.17929187,0.179309052,0.179346954,0.17938765,
0.179388992,0.179427719,0.179473694,0.179527237,0.179573685,
0.179600876,0.179610672,0.179633972,0.179655594,0.179736603,
0.179757141,0.179817856,0.179831421,0.179833771,0.179834122,
0.17983699,0.179846176,0.179873291,0.179887741,0.179894028,
0.179963043,0.1800065,0.180007065,0.180040222,0.180084167,
0.180146225,0.180211426,0.180272964,0.180274185,0.180276794,
0.18029126,0.180345413,0.180391571,0.180529099,0.180538788,
0.180675674,0.180917664,0.180928055,0.180936859,0.180971558,
0.18106488,0.181120483,0.18112645,0.181262589,0.181350754,
0.181388733,0.181512909,0.181514832,0.18155925,0.181651062,
0.18166394,0.181667206,0.181702118,0.181828781,0.181859467,
0.1818871,0.181940781,0.181990555,0.182016006,0.18205394,
0.182170624,0.182290253,0.182291122,0.182295364,0.182328583,
0.182328842,0.182443848,0.182522888,0.182566238,0.18262793,
0.182669861,0.182679977,0.182760864,0.182817276,0.182852737,
0.182862228,0.183188705,0.18319339,0.183209229,0.183233566,
0.18325354,0.183397751,0.183399216,0.183428574,0.183581329,
0.183655441,0.183679474,0.183696716,0.183730331,0.183776672,
0.184095154,0.184426697,0.184453018,0.184486633,0.184632339,
0.184639969,0.184692245,0.184720932,0.184729019,0.184729141,
0.184771698,0.18479216,0.184819473,0.184837662,0.184865417,
0.184894836,0.18493602,0.184942413,0.184967697,0.184996017,
0.185001785,0.185084763,0.185093277,0.185130096,0.185152832,
0.185202148,0.185252655,0.185262939,0.185301376,0.185310745,
0.185314438,0.185452255,0.185478043,0.185490265,0.185512329,
0.185513351,0.185529312,0.185677368,0.185721664,0.185724777,
0.185813202,0.185839401,0.185896805,0.185908707,0.185967636,
0.185994934,0.186012924,0.186014175,0.186038528,0.186043854,
0.186075119,0.186097382,0.186122025,0.186185791,0.186201736,
0.186243698,0.186282486,0.186282516,0.186290451,0.186311935,
0.186362228,0.186370239,0.186382843,0.186438965,0.186446274,
0.186471649,0.186549133,0.186562347,0.186630112,0.186632126,
0.186722961,0.186743835,0.186766647,0.186775146,0.186826736,
0.186835938,0.186850998,0.18687413,0.186973572,0.187037384,
0.187046768,0.187063721,0.187096603,0.187134933,0.187142288,
0.187146194,0.187155609,0.187177673,0.18722316,0.18727742,
0.187289825,0.187316559,0.18737384,0.187394226,0.187410675,
0.187426987,0.187547073,0.187603333,0.187710114,0.187728775,
0.187784836,0.187843063,0.187853683,0.187923538,0.187954758,
0.187965897,0.188038345,0.188040543,0.18804837,0.188123322,
0.188129333,0.188219223,0.18824086,0.188256134,0.188312347,
0.188314301,0.188321304,0.188377747,0.188386887,0.188406281,
0.188409958,0.188421814,0.188429764,0.188554199,0.188561127,
0.188617859,0.188657806,0.188657974,0.188684631,0.188705536,
0.188761429,0.188777985,0.188780655,0.188784424,0.188817215,
0.18888855,0.188889786,0.188912354,0.188916534,0.188967468,
0.189024857,0.189068436,0.189111603,0.189112564,0.189116409,
0.189214203,0.18921962,0.189305969,0.189343552,0.189415482,
0.189418472,0.189462448,0.189510101,0.189542206,0.189560165,
0.189593643,0.189671158,0.189874329,0.189877319,0.189921448,
0.189953369,0.189985931,0.190005569,0.190022629,0.190044662,
0.190061752,0.190084335,0.190091599,0.19014212,0.190161865,
0.19016803,0.190181335,0.190198792,0.190244507,0.190245163,
0.190246002,0.190283432,0.19029216,0.190293839,0.190312729,
0.190314209,0.190315369,0.190318192,0.190327484,0.190394272,
0.190409729,0.190411316,0.190480469,0.190594727,0.190620972,
0.190658325,0.190659012,0.190664536,0.19067099,0.190707809,
0.190708801,0.190729309,0.190739136,0.190758133,0.190766953,
0.190776978,0.190795822,0.190817734,0.19082724,0.190881302,
0.19088443,0.190886414,0.190910507,0.190960114,0.190971268,
0.191052322,0.191063049,0.191100952,0.191117844,0.191119278,
0.191124969,0.191149872,0.191188995,0.191203354,0.19123381,
0.191275284,0.191361938,0.191400269,0.191432755,0.191445541,
0.191524887,0.191535797,0.191580856,0.191597214,0.191597824,
0.191603638,0.191622879,0.191634857,0.191654221,0.191683426,
0.191684738,0.191694458,0.191760269,0.191765106,0.191775284,
0.191824432,0.191824768,0.191827148,0.19183728,0.191841797,
0.191860657,0.191900787,0.191917801,0.191926682,0.191964066,
0.192016449,0.192038712,0.192040558,0.192059235,0.192110184,
0.192121063,0.192146545,0.192152374,0.192160736,0.19218782,
0.192217255,0.19226561,0.192279282,0.192334549,0.19235025,
0.192350677,0.192365723,0.192377106,0.192378448,0.192398712,
0.192399155,0.192413208,0.192418411,0.192423004,0.192462845,
0.192473343,0.192490997,0.192510513,0.192516632,0.192520737,
0.192580887,0.192588974,0.192624969,0.192626953,0.192627151,
0.192711105,0.192762527,0.192825592,0.192880493,0.192936447,
0.192977966,0.193003387,0.19303833,0.193053864,0.193097839,
0.193107468,0.193108688,0.193154236,0.193171921,0.193195633,
0.193211502,0.1932202,0.193244873,0.193256393,0.193272934,
0.193344696,0.19334523,0.193345688,0.193481674,0.193561386,
0.193568634,0.193586594,0.193639862,0.193641663,0.193762161,
0.19376709,0.193862549,0.193878036,0.193885315,0.193894043,
0.193922836,0.193952972,0.193954071,0.194038483,0.194045593,
0.194055222,0.194074982,0.194079285,0.194104919,0.194174789,
0.194189636,0.194233444,0.194243179,0.194284592,0.194290405,
0.194306458,0.19432193,0.194322571,0.194330002,0.194358734,
0.194375275,0.194393463,0.19439798,0.194404556,0.19444397,
0.194600616,0.194642029,0.194650665,0.194683868,0.194691467,
0.194699875,0.194719513,0.194731003,0.194763336,0.194795807,
0.19479892,0.194964828,0.19496907,0.194992142,0.194993805,
0.195027435,0.195028732,0.195033722,0.195074219,0.1950793,
0.195097549,0.195129303,0.195134872,0.195149155,0.195159943,
0.195164185,0.195170731,0.195174301,0.195175476,0.195227142,
0.195248306,0.195253479,0.195285721,0.195289352,0.195291321,
0.195295242,0.195306778,0.195315857,0.195342163,0.195347183,
0.195369431,0.195379501,0.195389175,0.195407181,0.195415695,
0.195465332,0.195496338,0.195520721,0.195558929,0.195560776,
0.195595718,0.195623154,0.195670364,0.195674759,0.195678589,
0.19570459,0.195731873,0.195758911,0.195778778,0.195786819,
0.195830322,0.195846527,0.195875946,0.195883484,0.195924103,
0.195956146,0.195961304,0.195968674,0.196058105,0.196115768,
0.196131363,0.196144333,0.196171707,0.196195465,0.196230438,
0.196243622,0.196283051,0.196292023,0.196304611,0.196317459,
0.196340942,0.196357819,0.196399155,0.196417419,0.196422424,
0.196447067,0.196500488,0.196524353,0.19653392,0.196558334,
0.196626343,0.196626617,0.19665155,0.196666794,0.196691162,
0.196744812,0.196795975,0.196799835,0.196843369,0.19686087,
0.196875259,0.19688237,0.196924957,0.196937378,0.197119873,
0.19712561,0.197150803,0.197162888,0.197190063,0.197244553,
0.197277298,0.197316238,0.197401718,0.197415146,0.197468948,
0.197513687,0.197576874,0.197578629,0.197618317,0.19766864,
0.197678543,0.197703186,0.197705719,0.197727493,0.197735229,
0.197743835,0.197769455,0.197816284,0.197820419,0.197881073,
0.197951965,0.197975891,0.19805043,0.198077759,0.198091599,
0.198158768,0.198165207,0.198256729,0.198292175,0.19831929,
0.198342911,0.198347214,0.198348022,0.198410294,0.198413315,
0.198422928,0.198437195,0.198438202,0.198465256,0.198506912,
0.198533875,0.198553726,0.198583328,0.198597656,0.198637024,
0.19865802,0.198678665,0.198719711,0.198791367,0.19881044,
0.198813461,0.19883194,0.198858093,0.198872116,0.198912842,
0.198932983,0.19894046,0.198958038,0.19898674,0.199023102,
0.199036255,0.199040207,0.199068527,0.199101883,0.199121841,
0.199136154,0.199157074,0.199213623,0.19924408,0.19930365,
0.199305649,0.199309753,0.199312653,0.199330887,0.199343857,
0.199379395,0.19938356,0.199394272,0.199415314,0.199443268,
0.199444534,0.19951619,0.199531937,0.199542877,0.199555939,
0.199573318,0.199578842,0.19959198,0.199625275,0.199650314,
0.199690186,0.199778534,0.19978598,0.199791031,0.199798584,
0.199819702,0.199871246,0.199893311,0.199898026,0.199919937,
0.199943344,0.20000322,0.200055725,0.200277161,0.200393463,
0.200401932,0.200409943,0.200519135,0.20062291,0.200650955,
0.200749573,0.200809875,0.200887329,0.200931625,0.200943909,
0.200945114,0.20111055,0.201138596,0.201176254,0.201177124,
0.20118486,0.201294159,0.201437759,0.201464859,0.20150029,
0.201510757,0.201541702,0.201609741,0.201611938,0.201767578,
0.2018508,0.201883621,0.202003586,0.202022354,0.202029617,
0.20205719,0.202124588,0.202127747,0.202157471,0.202167664,
0.202188705,0.202219086,0.202249802,0.202262466,0.202320862,
0.202358047,0.202382874,0.202445465,0.202607895,0.202728653,
0.202779282,0.202848923,0.202889481,0.202907837,0.202935547,
0.202963852,0.202981018,0.202994186,0.203026031,0.203039886,
0.203153931,0.203266754,0.203300446,0.203311691,0.203362839,
0.203370285,0.203430588,0.203475555,0.203488647,0.20355423,
0.203572296,0.203605148,0.203609467,0.20362674,0.203745529,
0.203749908,0.203761505,0.203789993,0.203855667,0.203870438,
0.204130966,0.204216553,0.204273972,0.204294327,0.204347824,
0.204362396,0.204389313,0.204458694,0.204464493,0.204467712,
0.204571823,0.20465773,0.20468425,0.2047267,0.204743454,
0.204743652,0.204793533,0.204798569,0.205076218,0.205080856,
0.205098206,0.205100769,0.205156052,0.205166092,0.205168442,
0.205231995,0.205263489,0.205335159,0.205343781,0.205360779,
0.205362198,0.205415543,0.205440521,0.20548204,0.205512573,
0.205561722,0.205607956,0.205687531,0.205776855,0.205824524,
0.205853027,0.205874283,0.205910858,0.20595636,0.205966705,
0.206022079,0.206100372,0.206109161,0.206128036,0.206149277,
0.206184753,0.206192032,0.206463623,0.2065047,0.206563278,
0.206566635,0.206574997,0.206605545,0.206628906,0.206667847,
0.206679031,0.206695648,0.206731125,0.20673851,0.206755661,
0.206772095,0.206773514,0.206822403,0.206887543,0.206974854,
0.207028137,0.207110138,0.207138474,0.20715123,0.207201141,
0.207220459,0.207250015,0.207265915,0.207296509,0.207358154,
0.20736348,0.207367767,0.207369843,0.207405228,0.207409668,
0.20748526,0.207572174,0.207610474,0.207665329,0.207774887,
0.207790237,0.207790344,0.207847046,0.207883087,0.207889099,
0.207909531,0.208012405,0.208064575,0.20816539,0.208173508,
0.208220963,0.208230042,0.208276138,0.208313034,0.208472046,
0.208526489,0.208561584,0.208565308,0.208781494,0.208793945,
0.208861145,0.209015488,0.209044891,0.209058823,0.209083923,
0.209152664,0.209242142,0.2092715,0.209287735,0.209325897,
0.209343185,0.209353363,0.209410812,0.209457596,0.209499039,
0.209523102,0.209546051,0.209571716,0.209587646,0.209654663,
0.209655487,0.209693436,0.209694778,0.209727783,0.209742584,
0.209860519,0.209861984,0.209916748,0.210039886,0.21010614,
0.210114914,0.210182907,0.210257111,0.210304245,0.210411774,
0.210448608,0.210480042,0.210542526,0.210592529,0.210741257,
0.210822647,0.210837402,0.210888306,0.210895462,0.210988678,
0.210999008,0.211034088,0.211076538,0.211113373,0.211122086,
0.211226196,0.211241638,0.211388962,0.211521301,0.211585052,
0.211599014,0.211600342,0.211678482,0.211692474,0.211719604,
0.211739807,0.21191246,0.211931503,0.211992447,0.212008606,
0.212019165,0.212091843,0.212258163,0.212285263,0.212397125,
0.212403534,0.212441666,0.212452637,0.212473923,0.212541122,
0.21260614,0.212676544,0.212693954,0.212773026,0.212843079,
0.212859619,0.21286026,0.212874557,0.213075928,0.213162735,
0.213177917,0.213211533,0.213222412,0.213251907,0.213261581,
0.213341049,0.213363129,0.21363208,0.213660187,0.213680435,
0.213711212,0.213763626,0.213799316,0.213799362,0.213855942,
0.213883575,0.213897354,0.214026184,0.214030441,0.214053711,
0.214082108,0.214118378,0.214127228,0.21427211,0.214335388,
0.214459,0.214548203,0.214633194,0.214641052,0.214666992,
0.214667114,0.214670364,0.214717728,0.21485173,0.214857819,
0.21489415,0.215103867,0.215196335,0.215211243,0.215230545,
0.215291,0.215487595,0.215517502,0.215536652,0.21557428,
0.21561116,0.215650726,0.215653656,0.215724014,0.215733795,
0.21573967,0.215744736,0.215768356,0.215803879,0.215933441,
0.215953583,0.21598027,0.216037613,0.216073212,0.216120224,
0.216164978,0.216236084,0.216240082,0.216312103,0.216338791,
0.216584335,0.216634567,0.216737244,0.216738815,0.216782928,
0.216890457,0.216923172,0.216974503,0.217068542,0.21721759,
0.217270676,0.217308334,0.217310242,0.217381241,0.217388367,
0.21740509,0.217452957,0.217479156,0.217550339,0.217574631,
0.217610138,0.217622177,0.21772699,0.217772629,0.217778534,
0.217800522,0.217809204,0.217858032,0.21792038,0.217951248,
0.218006989,0.218126572,0.218179062,0.218205933,0.218423904,
0.218443573,0.218600464,0.21865979,0.218715744,0.218756073,
0.218775375,0.21878743,0.21879184,0.218841812,0.218859238,
0.218869186,0.218870911,0.21888382,0.218904892,0.218956131,
0.218978531,0.219032654,0.219064987,0.219087006,0.21920108,
0.219216248,0.21927179,0.219342575,0.219405838,0.219428131,
0.219454407,0.219466965,0.219649658,0.219736023,0.219746613,
0.219828827,0.219861481,0.219916626,0.219977737,0.220005539,
0.220051102,0.220054794,0.220085785,0.220133347,0.220234955,
0.220259354,0.22028949,0.220314041,0.22033522,0.220472565,
0.220505264,0.220626663,0.220708557,0.22072702,0.220750183,
0.220817169,0.220832489,0.220850998,0.22090416,0.220912262,
0.220934494,0.220939392,0.22096347,0.221066513,0.221066833,
0.22117421,0.221287918,0.221337265,0.221356308,0.221366409,
0.221405136,0.221426346,0.221486496,0.221512344,0.22153421,
0.221555237,0.221560745,0.221563293,0.221580872,0.221656326,
0.221764725,0.22178714,0.22182164,0.221920975,0.221944794,
0.222125671,0.222182114,0.222206223,0.222283432,0.22238942,
0.222398331,0.222413818,0.222438446,0.222442276,0.222512375,
0.222537445,0.222541779,0.222571396,0.222580811,0.222582184,
0.222609955,0.222661194,0.222691299,0.222699844,0.222762344,
0.222864532,0.222927383,0.223028091,0.223033661,0.223086777,
0.223114029,0.22313652,0.223141953,0.223199829,0.223276978,
0.223306107,0.223378571,0.223411057,0.22347908,0.223480652,
0.223548691,0.223593338,0.223626434,0.223635162,0.223636597,
0.223646194,0.223727844,0.223743347,0.223957764,0.224009598,
0.224093948,0.224174637,0.224215256,0.224354782,0.224458786,
0.224738556,0.224739853,0.224752777,0.224949036,0.225016541,
0.225038605,0.225048553,0.225070679,0.225128433,0.225187317,
0.225239944,0.225292282,0.225310852,0.225381165,0.225416397,
0.22542276,0.225436417,0.225448364,0.225659256,0.225669174,
0.225748108,0.225794098,0.225812363,0.225900116,0.225915207,
0.225932449,0.225986557,0.225986877,0.226024811,0.226103806,
0.226118561,0.226163391,0.226199432,0.22620105,0.226222992,
0.22622403,0.226224289,0.226291061,0.22644014,0.226586395,
0.226607437,0.226618042,0.226641754,0.226720901,0.226745224,
0.226795197,0.226804428,0.226830261,0.226831757,0.226841354,
0.226893723,0.226930252,0.226993408,0.22699646,0.227035522,
0.227040451,0.22723291,0.22731749,0.227433136,0.227477936,
0.22754541,0.227561935,0.227575272,0.227619003,0.227623718,
0.227666214,0.227756531,0.227768692,0.227769958,0.227777542,
0.227810852,0.22784729,0.22786409,0.227929947,0.227987396,
0.228112762,0.228242447,0.228414886,0.228441605,0.228559845,
0.228668716,0.228731583,0.22873613,0.228752441,0.228827988,
0.228837646,0.228935974,0.228989578,0.229003265,0.229034576,
0.229127792,0.229159637,0.229215073,0.229284317,0.229544556,
0.229580154,0.22961084,0.229646667,0.229653381,0.229669205,
0.229682602,0.229714813,0.229739182,0.229747086,0.229818924,
0.22985228,0.229891037,0.229895554,0.229901611,0.230001007,
0.230049911,0.230062607,0.230074219,0.230145096,0.230162766,
0.23026767,0.230313339,0.230429947,0.230443756,0.230445328,
0.230601486,0.230694092,0.230767151,0.230769638,0.230858047,
0.230901855,0.230945969,0.23097496,0.230980377,0.230987717,
0.231019241,0.231034027,0.231258972,0.231365173,0.231423004,
0.231515411,0.231554886,0.231661087,0.231670853,0.231724838,
0.231746841,0.231897476,0.231943085,0.231973862,0.231992813,
0.231995651,0.232019867,0.232065689,0.232075058,0.232075439,
0.232103897,0.232111008,0.232184113,0.232239059,0.232333557,
0.232360992,0.232386978,0.232448532,0.232452789,0.23254538,
0.232555313,0.232590881,0.23261644,0.232642609,0.232663223,
0.23267218,0.232675125,0.232705811,0.23272197,0.232761063,
0.232767517,0.232816254,0.232841812,0.232868912,0.232916595,
0.232952286,0.232997421,0.233041962,0.233149033,0.233183624,
0.233203812,0.233232819,0.233302002,0.233314194,0.233335617,
0.23337326,0.233377151,0.233536453,0.233536743,0.233677628,
0.233720032,0.233752182,0.233779587,0.233799698,0.233842789,
0.233850128,0.233850677,0.233853622,0.233899948,0.23400412,
0.234115784,0.234154404,0.234159805,0.234188675,0.234198929,
0.234223892,0.234277283,0.234390594,0.234393311,0.234519806,
0.234525024,0.234690765,0.234703598,0.234769379,0.234885376,
0.23490123,0.234975342,0.235059113,0.235164505,0.235165451,
0.235205704,0.235258377,0.235286728,0.235291916,0.235326767,
0.235341385,0.235360352,0.235387604,0.235456284,0.235538712,
0.235564331,0.235609924,0.235655548,0.235661011,0.23569725,
0.235700653,0.235753891,0.2357798,0.235798096,0.2358125,
0.235827759,0.235838348,0.23588559,0.235924942,0.235931961,
0.23595488,0.236015503,0.236053864,0.236094604,0.236116745,
0.236157318,0.236325699,0.236338333,0.236374588,0.236401291,
0.23640358,0.236413391,0.236539352,0.23654274,0.236549377,
0.236590591,0.236627548,0.236636688,0.236709579,0.236726059,
0.236945801,0.236957718,0.236966736,0.236982422,0.237084229,
0.237152573,0.237179306,0.237195694,0.237215179,0.237228439,
0.237231277,0.237258316,0.237268295,0.237287018,0.237289536,
0.237376266,0.237430191,0.237444656,0.237464447,0.237471481,
0.237537323,0.23758107,0.237586182,0.237595123,0.237661606,
0.237706741,0.237711975,0.237720764,0.237756729,0.237765976,
0.23784523,0.237857697,0.237918823,0.237949814,0.237980896,
0.237984161,0.238107376,0.238241455,0.23830484,0.238416046,
0.238425674,0.238444611,0.238445114,0.238478058,0.238633652,
0.238636353,0.238670593,0.238720459,0.238772995,0.238795151,
0.238914505,0.238919586,0.238987045,0.23898822,0.239039459,
0.239127457,0.239149796,0.23930394,0.239432144,0.239564972,
0.23963501,0.239647903,0.239699692,0.239866714,0.239899185,
0.239903259,0.239907715,0.239916626,0.239941803,0.239950104,
0.239997971,0.240016357,0.240096619,0.240167496,0.24054866,
0.240579453,0.240866989,0.240955032,0.241024918,0.241053452,
0.2410923,0.241169434,0.241176437,0.241193283,0.241668091,
0.241918579,0.241947601,0.241985992,0.242130463,0.242224869,
0.242231583,0.24247905,0.242531143,0.242548828,0.242655685,
0.242702499,0.242707108,0.242755676,0.242898132,0.242909424,
0.24294165,0.242965958,0.243046417,0.243057037,0.243163361,
0.243292084,0.243330505,0.243343277,0.24334819,0.243361404,
0.24337558,0.243513443,0.243528366,0.243552917,0.243560928,
0.243763657,0.244009781,0.244014328,0.24402002,0.244091476,
0.244146225,0.244150436,0.244162064,0.244182266,0.244201782,
0.244238831,0.244245102,0.244268982,0.244376556,0.24447258,
0.244663467,0.244728607,0.244771927,0.24479071,0.244972137,
0.244974976,0.2452108,0.245277313,0.245324005,0.245331528,
0.245397324,0.245482468,0.245577332,0.245633209,0.245719589,
0.245776398,0.245806335,0.245820465,0.245927124,0.245955338,
0.246064377,0.246089813,0.246218002,0.246248611,0.246400467,
0.246445618,0.246469284,0.246485992,0.246541885,0.246566238,
0.246596436,0.246678406,0.24669725,0.24676268,0.246812637,
0.24688028,0.246985046,0.24702504,0.247084427,0.247225815,
0.247295929,0.247360153,0.247385376,0.247465775,0.247524933,
0.247526917,0.247590973,0.247642212,0.247715775,0.247749863,
0.247924088,0.247974823,0.247982193,0.247998154,0.2480401,
0.2481315,0.248142242,0.248185944,0.248211197,0.248286697,
0.248304871,0.248340118,0.248348831,0.248390717,0.248459885,
0.248476562,0.248495422,0.248594498,0.248606232,0.24879509,
0.248862381,0.248868011,0.248903564,0.249013855,0.249354477,
0.249420135,0.249485596,0.249601608,0.249662781,0.249777237,
0.249792175,0.249823151,0.24990123,0.24997084,0.249981827,
0.249986374,0.250035065,0.250083481,0.250188141,0.250214783,
0.25026944,0.250286041,0.250341354,0.25062088,0.250646027,
0.250815979,0.250881577,0.251014084,0.251123749,0.251153992,
0.251177887,0.251255539,0.251457001,0.251461105,0.251475845,
0.251576126,0.251692413,0.251694122,0.251831299,0.251861237,
0.251917221,0.251920197,0.251941544,0.25198645,0.251989319,
0.251990509,0.252032486,0.252202179,0.252215393,0.252289932,
0.252341797,0.252765823,0.252818893,0.253085251,0.253176086,
0.253215683,0.253272217,0.253293793,0.253305527,0.253369659,
0.253409409,0.253425842,0.253455627,0.253541656,0.253615906,
0.253638474,0.253660019,0.253738724,0.253866913,0.25387146,
0.253874405,0.254042999,0.254130997,0.254196274,0.254445251,
0.254460648,0.25454364,0.254576477,0.254719116,0.254820236,
0.254883392,0.254903885,0.255005432,0.255061264,0.255077881,
0.255149017,0.255156906,0.255196854,0.255231796,0.255238785,
0.255340805,0.255393097,0.25543576,0.255488754,0.255504791,
0.255549362,0.255556656,0.255571075,0.255577835,0.25559201,
0.255638107,0.255689911,0.255721237,0.255759766,0.255876266,
0.255947983,0.25601825,0.256056427,0.256235779,0.256269409,
0.256349396,0.256366333,0.256422546,0.256628845,0.256755066,
0.256870239,0.256890961,0.256919373,0.256950165,0.256959656,
0.2569841,0.25703302,0.257047211,0.257121338,0.257243652,
0.257317444,0.257380127,0.257417084,0.257432678,0.257443237,
0.257463409,0.257467957,0.257547791,0.25756366,0.257627563,
0.257703369,0.257744781,0.2580784,0.258151123,0.258203094,
0.258266205,0.258305023,0.258384949,0.258420776,0.25845755,
0.258467407,0.258509033,0.258589447,0.258600586,0.258775848,
0.25879718,0.258845154,0.258912476,0.259043396,0.25905246,
0.259101471,0.259131622,0.259201477,0.259276093,0.259455933,
0.259561981,0.259614105,0.259792175,0.25984494,0.259873932,
0.25992334,0.259962128,0.260013885,0.260062622,0.260152252,
0.260160278,0.260213226,0.260219299,0.260258148,0.260284851,
0.26034967,0.260418152,0.260422455,0.260485199,0.260498322,
0.260608551,0.260639984,0.260727875,0.260821075,0.260829376,
0.260839874,0.260845459,0.260857391,0.260874023,0.261009094,
0.261009521,0.261064819,0.261245361,0.261291107,0.261319,
0.261394379,0.261444733,0.261521637,0.261682007,0.261904175,
0.26198349,0.262145782,0.262226166,0.262233185,0.262242371,
0.262384766,0.262405579,0.262410492,0.262497253,0.262520111,
0.262573029,0.262590424,0.262772766,0.262890686,0.262924103,
0.262937347,0.263082672,0.263086884,0.263090057,0.263322235,
0.263325073,0.263367615,0.263401154,0.263437317,0.263450806,
0.263479126,0.263494202,0.263513733,0.26354715,0.26357547,
0.263718597,0.263722809,0.263784241,0.263801514,0.263823547,
0.263908386,0.263966217,0.264002167,0.264091644,0.264095032,
0.264117126,0.264303314,0.264331207,0.264350555,0.264361237,
0.264555023,0.264557312,0.26460675,0.26466333,0.26470816,
0.264843689,0.264850891,0.26499408,0.265026123,0.265147797,
0.265346832,0.26535376,0.26536026,0.265467072,0.265487305,
0.265505157,0.265515228,0.265631317,0.265673096,0.265754395,
0.265792542,0.265970856,0.265982697,0.266004242,0.266025848,
0.26605899,0.26613858,0.266449097,0.266456726,0.2664823,
0.266482819,0.266489746,0.266509521,0.266538147,0.266627716,
0.266644806,0.266644897,0.266666626,0.266845032,0.266864624,
0.266892792,0.266952393,0.26702887,0.267041504,0.267168854,
0.26731131,0.267328979,0.26745459,0.267632782,0.267642731,
0.267878265,0.267886719,0.267891022,0.267893951,0.268130554,
0.268211975,0.268238831,0.268308136,0.268319977,0.268436401,
0.268530792,0.268773163,0.26887735,0.269003448,0.269181061,
0.269194458,0.26925415,0.269358154,0.269390503,0.26955069,
0.269564148,0.269614624,0.269648621,0.269677521,0.26969696,
0.269699249,0.269767609,0.269804749,0.269811737,0.269845642,
0.269870361,0.269890289,0.269947449,0.270017975,0.270037964,
0.27019101,0.270194611,0.270254181,0.270335663,0.270376923,
0.270394623,0.270472412,0.270477051,0.270497223,0.270625671,
0.2706633,0.270699219,0.270720795,0.270745026,0.270987457,
0.271000763,0.271047028,0.271146698,0.271159485,0.271184204,
0.271190613,0.271226929,0.271328796,0.271335327,0.271380646,
0.271414673,0.271425232,0.271426117,0.271526001,0.271556305,
0.271649872,0.271716675,0.27174588,0.271773193,0.271775177,
0.27187085,0.271896423,0.27198288,0.272062988,0.272065247,
0.272162872,0.272245087,0.27231665,0.272317078,0.272364105,
0.272503845,0.272629425,0.272710968,0.272752655,0.272878174,
0.272887604,0.272936249,0.272951019,0.27301413,0.273014679,
0.273023193,0.273122223,0.273132904,0.273170868,0.273197784,
0.273292053,0.273375549,0.273401367,0.273465759,0.273499512,
0.273525085,0.273578766,0.27362796,0.273701233,0.273773621,
0.273792969,0.273810303,0.274013611,0.274019989,0.274165802,
0.27436615,0.274422943,0.274457672,0.27455127,0.274555603,
0.274582703,0.27461853,0.27462677,0.274651489,0.274739899,
0.274807037,0.274835022,0.274838806,0.274964294,0.27497998,
0.275053253,0.27518866,0.275214142,0.275288269,0.275376373,
0.275411377,0.275469482,0.275560822,0.275561035,0.275631531,
0.275685089,0.275782898,0.275845123,0.275875153,0.27591925,
0.27607486,0.276115265,0.276115967,0.276149231,0.276176056,
0.27618866,0.276203308,0.276281342,0.276516327,0.276717224,
0.276765625,0.27695105,0.276965851,0.276980499,0.277011108,
0.277095367,0.277173676,0.277194916,0.277360382,0.277441437,
0.277665253,0.277793671,0.277841553,0.277873108,0.277902313,
0.278020203,0.278052338,0.278065277,0.278162811,0.278176575,
0.278226074,0.278236206,0.278296143,0.278314148,0.278440674,
0.278537628,0.278595856,0.27868576,0.278716583,0.278723816,
0.278838898,0.278850677,0.278986084,0.278988068,0.278997711,
0.279038239,0.279042236,0.279126587,0.27924292,0.279249146,
0.279293213,0.279467865,0.27955426,0.279559326,0.279572174,
0.279585052,0.27984314,0.279971558,0.280014801,0.280041351,
0.280044861,0.280290558,0.280314484,0.280357697,0.280458557,
0.280830444,0.280834534,0.280838287,0.280867737,0.280898315,
0.281015808,0.281111847,0.281211121,0.281279816,0.28129538,
0.281366882,0.281402954,0.281447815,0.281511139,0.281571594,
0.281744049,0.281884552,0.281908508,0.281943451,0.281962006,
0.281988037,0.282004578,0.282028687,0.282110901,0.282248199,
0.282342621,0.282368103,0.282442078,0.28245871,0.282475555,
0.282594849,0.28267038,0.282715515,0.282920288,0.283043579,
0.283088409,0.283092407,0.283249939,0.283272369,0.28346994,
0.283523376,0.283713562,0.28371521,0.283853455,0.283862823,
0.283872498,0.28407608,0.284119598,0.284138245,0.284222626,
0.284283447,0.284295197,0.284405579,0.284425537,0.284500916,
0.28458905,0.284662964,0.284672272,0.28467749,0.284831146,
0.284884155,0.284884888,0.284900208,0.285140167,0.285210022,
0.285285187,0.285316833,0.28545578,0.285455872,0.285588593,
0.285609283,0.285620056,0.285767548,0.285790894,0.285848724,
0.285851898,0.286003845,0.286021027,0.286028717,0.286054688,
0.286134399,0.28617923,0.286281647,0.286364502,0.286577911,
0.28659845,0.286671661,0.286687164,0.286757538,0.286780945,
0.286803894,0.286959106,0.287016937,0.287087952,0.287154785,
0.287329224,0.287591949,0.287660919,0.287686493,0.287870178,
0.287883179,0.287886414,0.288036469,0.288053375,0.28822937,
0.288281982,0.288400299,0.288767761,0.288972382,0.288973053,
0.289057404,0.289076019,0.289101807,0.289163452,0.289286896,
0.2893974,0.289433319,0.289664093,0.28971698,0.290092926,
0.290177612,0.290209808,0.290212067,0.29023053,0.290281982,
0.290294281,0.290450226,0.290559143,0.290586029,0.290623596,
0.290661743,0.290673615,0.290722504,0.290806519,0.290938385,
0.291173859,0.291212067,0.291476837,0.291703644,0.291750793,
0.291790222,0.291818176,0.291872253,0.291932709,0.291937073,
0.291981903,0.292042084,0.29221228,0.292229919,0.292395081,
0.292419891,0.292500275,0.292530884,0.292556671,0.292599792,
0.292652618,0.292668945,0.292713501,0.292970337,0.292971558,
0.29306366,0.29314859,0.29324588,0.293264709,0.293551697,
0.293561157,0.293875061,0.294279358,0.294334106,0.294375946,
0.294466858,0.294497009,0.294529999,0.294678162,0.294799957,
0.294964172,0.295084015,0.295253113,0.295262817,0.29527002,
0.295311371,0.29534259,0.295412781,0.295451447,0.295570862,
0.295821106,0.295845459,0.295918243,0.295968567,0.296063904,
0.296069733,0.296145508,0.29615332,0.296274536,0.296443451,
0.296508911,0.296690521,0.296999908,0.297093262,0.29714267,
0.297168335,0.297181824,0.297289062,0.297319702,0.297435425,
0.29747995,0.29748468,0.297559875,0.297612732,0.297805603,
0.297818298,0.297852356,0.297897614,0.298110229,0.298150055,
0.29815509,0.298184937,0.298250763,0.298305634,0.298323425,
0.298349457,0.298438141,0.298466583,0.298578613,0.298713654,
0.298790588,0.298917389,0.298922363,0.298953491,0.299044952,
0.299104584,0.299190155,0.299316711,0.29935907,0.299423645,
0.299447754,0.299540894,0.29960495,0.299608429,0.299620392,
0.299633698,0.299645538,0.299649353,0.299703857,0.299719452,
0.299788361,0.299827148,0.299845917,0.29987735,0.299941589,
0.300024719,0.300100311,0.300107178,0.300156738,0.300180695,
0.300189056,0.300285889,0.300330933,0.300367401,0.300428589,
0.300718811,0.300742828,0.300821411,0.30083255,0.300943848,
0.300960052,0.301044708,0.301177551,0.301238983,0.301459015,
0.301493652,0.301767242,0.301807129,0.302000214,0.302060608,
0.302096649,0.30212854,0.302225067,0.302288177,0.302383301,
0.302418701,0.302442657,0.302670563,0.302694824,0.302719452,
0.302737976,0.30279776,0.302797821,0.302801514,0.30282431,
0.302891235,0.303021576,0.303069489,0.303104095,0.303126404,
0.303174652,0.303376892,0.303715149,0.303741852,0.303829468,
0.304038788,0.304080566,0.304141479,0.304638,0.304738953,
0.304765991,0.30482312,0.304918121,0.304987183,0.305048126,
0.305137421,0.30528476,0.305431396,0.305865417,0.305953278,
0.305993561,0.30622583,0.306259277,0.306279236,0.306291382,
0.306361572,0.306437469,0.306970459,0.307151794,0.307176483,
0.307372375,0.307546295,0.30759494,0.307863953,0.307983154,
0.308025818,0.308510468,0.308774292,0.308850922,0.308900116,
0.308912598,0.308997345,0.309176788,0.309302032,0.309531464,
0.30957843,0.30960614,0.309945221,0.309982574,0.310062775,
0.310192902,0.310296082,0.310324249,0.31057074,0.310593445,
0.310819031,0.31084494,0.31102832,0.311203156,0.311532562,
0.311597107,0.311735199,0.311769348,0.311971375,0.3120448,
0.312097137,0.31218396,0.312236542,0.312404358,0.312739868,
0.312745361,0.312862061,0.312942291,0.312987244,0.312990845,
0.313020081,0.313103577,0.313352448,0.313386322,0.313571075,
0.313666443,0.31380777,0.313839996,0.314147522,0.31426239,
0.314364563,0.315055206,0.315129852,0.315130341,0.315140228,
0.315244751,0.315505249,0.315679596,0.315807098,0.315882141,
0.315894531,0.316024811,0.31605249,0.316066864,0.316082367,
0.316289185,0.316423889,0.316540344,0.316741058,0.316860596,
0.316873169,0.316905945,0.316965546,0.317012177,0.317058319,
0.317070923,0.317176697,0.317195374,0.317244659,0.31727771,
0.317290192,0.317492249,0.317671021,0.317881561,0.318020874,
0.318087921,0.318142456,0.318217377,0.318223694,0.318285522,
0.318332428,0.318332764,0.318708923,0.318747498,0.318934631,
0.319043274,0.31904776,0.319179993,0.319202423,0.319237518,
0.319398376,0.319517517,0.319647247,0.319648865,0.319682953,
0.319702484,0.319750671,0.319810242,0.319922485,0.319975403,
0.319994568,0.320035431,0.320124176,0.320130524,0.320159271,
0.320176819,0.320396118,0.320599762,0.32081778,0.320940399,
0.321014069,0.32106662,0.321247101,0.321257996,0.321335632,
0.321700439,0.321812378,0.321977936,0.322003754,0.3221409,
0.322290894,0.322383392,0.322392578,0.322545532,0.322565399,
0.322696289,0.322699127,0.323108978,0.323120514,0.323156036,
0.32316687,0.323226135,0.32342337,0.323550507,0.323617889,
0.323635193,0.323679108,0.323789825,0.324191223,0.3243685,
0.324582825,0.324641998,0.324656799,0.324904541,0.325240997,
0.325363312,0.325393616,0.325534851,0.325631927,0.325684479,
0.32573407,0.325784515,0.325813232,0.325894257,0.325939301,
0.325946808,0.326013123,0.326105774,0.326115234,0.326391083,
0.326778046,0.326787506,0.327221313,0.327285675,0.327428253,
0.327584595,0.327657654,0.327663971,0.327668091,0.327751984,
0.328010834,0.328251923,0.328316559,0.328342529,0.328624268,
0.328716766,0.328727539,0.329134613,0.329227325,0.329546173,
0.329681885,0.329690948,0.329812958,0.329839081,0.329944885,
0.330173279,0.330239288,0.330274841,0.3304151,0.330434692,
0.330460114,0.330526245,0.330640198,0.330679596,0.330799652,
0.330856476,0.330950836,0.331066956,0.331163116,0.331554565,
0.331617371,0.331732391,0.331829773,0.332054535,0.332056641,
0.332110321,0.332188446,0.332237244,0.332336212,0.332503876,
0.332577972,0.332911041,0.333312836,0.333326843,0.333345367,
0.333365356,0.333454437,0.333659576,0.333664886,0.333708008,
0.333802002,0.333827942,0.333834259,0.334150452,0.334272614,
0.334316895,0.334332977,0.334389008,0.334578644,0.334603943,
0.334604218,0.334714233,0.334756744,0.334966949,0.335118011,
0.335184387,0.335267609,0.335348083,0.335463715,0.335622864,
0.33565387,0.335674469,0.33586734,0.335871277,0.335911163,
0.33597464,0.336055786,0.336390869,0.336402832,0.336491577,
0.336535919,0.336723999,0.336745453,0.336916077,0.336970276,
0.337550598,0.337857147,0.337937744,0.337970062,0.338079803,
0.338317444,0.338320801,0.338386871,0.338421051,0.338427856,
0.338478912,0.338480591,0.338508118,0.338691772,0.338770447,
0.338860199,0.338971344,0.339172943,0.339252655,0.339476379,
0.339607941,0.339641663,0.339767975,0.339789124,0.339868988,
0.341765625,0.342187378,0.342359344,0.342389923,0.342547516,
0.342689392,0.343136566,0.343224884,0.343481934,0.343540466,
0.343585907,0.343926208,0.344061066,0.344418732,0.345336121,
0.345374786,0.345427002,0.345606964,0.345611603,0.345693512,
0.345816681,0.346011658,0.346039185,0.346059875,0.346170532,
0.346381378,0.346434814,0.346725861,0.346806335,0.346821564,
0.347118866,0.347278717,0.34746701,0.347591095,0.347934723,
0.348322845,0.348473877,0.348596344,0.348638184,0.348648376,
0.348784393,0.349108215,0.349125763,0.349772675,0.350066528,
0.350193817,0.350247009,0.350297577,0.350353973,0.350436279,
0.350444153,0.350486694,0.350511383,0.350572327,0.35081662,
0.350817078,0.351231445,0.351279449,0.351323669,0.351578278,
0.351666565,0.351683289,0.351750854,0.351834198,0.351874634,
0.351892487,0.351971313,0.352105896,0.352455444,0.352894409,
0.352921051,0.353051331,0.353073975,0.353175232,0.353441284,
0.353753174,0.353781891,0.353849731,0.353946991,0.354242096,
0.354269165,0.354346008,0.354384094,0.354395172,0.354546997,
0.354628326,0.354642731,0.354663055,0.355053833,0.355546356,
0.35556842,0.355676514,0.355716858,0.355728394,0.355862366,
0.356088959,0.356353455,0.356531189,0.357064148,0.35732251,
0.3575336,0.357676514,0.357730164,0.358145782,0.358310028,
0.358411926,0.358463593,0.358574097,0.358619934,0.358790192,
0.358885742,0.358997345,0.359057129,0.359287292,0.359466522,
0.359480347,0.359498993,0.359545807,0.359585114,0.359598938,
0.35971225,0.359752625,0.359799194,0.360291687,0.360554657,
0.360893158,0.361043091,0.361269989,0.361729797,0.362127899,
0.362293488,0.362497223,0.362646118,0.362752289,0.362782715,
0.362860992,0.362879089,0.363007172,0.363107605,0.36387326,
0.364384003,0.364440979,0.364455902,0.364466705,0.364527313,
0.365033722,0.365058899,0.365103821,0.365205353,0.36522226,
0.365516418,0.365640533,0.365696838,0.365853363,0.365899994,
0.365990814,0.366152985,0.366225098,0.366299286,0.366329834,
0.366445099,0.366458954,0.36656308,0.36656897,0.366671234,
0.36678772,0.366794861,0.366872223,0.366995331,0.367528992,
0.368168304,0.368183594,0.368267944,0.368327179,0.368359375,
0.368390686,0.368501678,0.368544739,0.36858197,0.368684052,
0.368871735,0.368921143,0.369012848,0.36909552,0.369231812,
0.369270813,0.369323151,0.369544647,0.369581024,0.369616302,
0.369707581,0.369808197,0.36998407,0.370013306,0.370022644,
0.37015213,0.370277069,0.370402405,0.370732147,0.370926849,
0.371054932,0.371189606,0.371324219,0.37139566,0.371455872,
0.371485291,0.371492188,0.371629547,0.371649109,0.371718719,
0.37201767,0.372232666,0.372390381,0.372433929,0.373120514,
0.373127228,0.373190552,0.373353119,0.373730316,0.373877075,
0.374003662,0.374051575,0.374394196,0.374423218,0.374515015,
0.374557098,0.374605774,0.37468222,0.374882019,0.374937927,
0.375114838,0.375261261,0.375322906,0.375483734,0.375541138,
0.375588654,0.375590271,0.375595123,0.375640686,0.375707428,
0.375708496,0.376199432,0.376237091,0.376342468,0.376452423,
0.376522888,0.376523041,0.376624146,0.376626343,0.376631104,
0.376755585,0.376889313,0.377001282,0.377114594,0.37718808,
0.377318939,0.37748233,0.377546906,0.377598755,0.377619904,
0.3776521,0.377687531,0.377924927,0.377969421,0.377976013,
0.377992706,0.378168396,0.378210052,0.378275452,0.378534332,
0.378703552,0.378715759,0.378801025,0.379055115,0.379212921,
0.379518188,0.379522552,0.379565979,0.379986053,0.380023132,
0.380204254,0.380209534,0.380239471,0.380517181,0.380662109,
0.380767975,0.381019226,0.381072968,0.381073517,0.381118317,
0.381150818,0.381238495,0.38135318,0.381713013,0.381796082,
0.381815002,0.381822784,0.381844818,0.381968506,0.382050781,
0.382087097,0.382209259,0.382655365,0.38271283,0.382731232,
0.382755859,0.382941589,0.383076935,0.383090424,0.383092834,
0.383325012,0.383515076,0.383874084,0.383952759,0.384015503,
0.384306946,0.384436035,0.384452545,0.384570282,0.384580109,
0.384583832,0.384588898,0.384631348,0.384682343,0.384722076,
0.384753693,0.38494754,0.385013367,0.385046478,0.385078247,
0.385117554,0.38515033,0.385193329,0.385206146,0.385481812,
0.385590973,0.385591309,0.38563501,0.385662323,0.385700409,
0.38573111,0.385751984,0.38576239,0.385776489,0.385803345,
0.3858526,0.386076904,0.386082764,0.386089844,0.386316437,
0.386501831,0.386543243,0.386684357,0.386686798,0.38672348,
0.386781067,0.386851166,0.386937927,0.387054077,0.387186523,
0.387363068,0.387365417,0.387455658,0.387510254,0.387537323,
0.387704987,0.388211578,0.38831723,0.388375336,0.388728027,
0.388836731,0.388881317,0.388951935,0.388958313,0.388985596,
0.389008362,0.389130646,0.389147247,0.389161774,0.389402191,
0.389440643,0.389671112,0.389756836,0.389776459,0.389854675,
0.389980133,0.390044708,0.390059204,0.390105377,0.390147034,
0.39015683,0.390285583,0.39029245,0.390546082,0.390657074,
0.390728485,0.390840271,0.390949097,0.39096048,0.391161469,
0.391205475,0.391470245,0.391476379,0.391530914,0.391645691,
0.391646423,0.391670654,0.391796112,0.391926666,0.39196814,
0.392031189,0.392157043,0.392220856,0.392254364,0.392556,
0.392636841,0.392727264,0.392777588,0.39281955,0.393001312,
0.393036133,0.393303375,0.393945648,0.394085022,0.394124451,
0.39421283,0.394256378,0.394274872,0.394344116,0.394348907,
0.394357697,0.394375732,0.394416901,0.394617493,0.394631897,
0.394772003,0.39482962,0.394853119,0.394941101,0.394997772,
0.395026489,0.395107391,0.395256104,0.395267303,0.39530658,
0.395368317,0.395448303,0.39553186,0.395703949,0.395732544,
0.395784302,0.395887482,0.395968079,0.396008759,0.396084625,
0.396266937,0.396283722,0.396329681,0.396480469,0.396480896,
0.396623993,0.396632294,0.39665506,0.396675507,0.396684509,
0.396726501,0.396842438,0.396860321,0.396910522,0.396925995,
0.397124664,0.397170929,0.397249786,0.397367279,0.397409332,
0.397461273,0.39761264,0.397799225,0.397807587,0.397834686,
0.398051178,0.398208954,0.398480927,0.398651489,0.398728058,
0.398806274,0.398917358,0.398922546,0.398975189,0.398988586,
0.399062744,0.399073242,0.399097351,0.399113647,0.399122955,
0.399265259,0.399308533,0.399309082,0.399644379,0.399723969,
0.399736603,0.399750641,0.399864807,0.399892151,0.399917236,
0.399940582,0.399959442,0.400045898,0.400131165,0.400146729,
0.400182831,0.400252869,0.40027063,0.400340302,0.400396454,
0.400410706,0.400570129,0.40071405,0.401156372,0.401410156,
0.401441864,0.401519165,0.401715424,0.401836792,0.401915192,
0.402111542,0.40223764,0.402330261,0.402418488,0.402622101,
0.402628143,0.402697571,0.402819672,0.402937622,0.403010742,
0.403071167,0.403248108,0.403256561,0.403273102,0.403359772,
0.403503174,0.4036492,0.403651764,0.40373587,0.403759827,
0.403867767,0.403896942,0.404149841,0.404237,0.404305359,
0.40451001,0.404568512,0.404726379,0.405061462,0.405287445,
0.405610718,0.405631226,0.405951813,0.406092041,0.406256561,
0.406344238,0.406395996,0.406449432,0.40644986,0.406469025,
0.406528809,0.406634766,0.406821198,0.407,0.407110596,
0.407176453,0.407203186,0.407389557,0.407391418,0.40752301,
0.407526703,0.407557037,0.407789551,0.407917603,0.408012451,
0.408023315,0.408553619,0.408695709,0.409053741,0.409152374,
0.409225861,0.409363342,0.409365601,0.409502899,0.409506927,
0.409613617,0.410016296,0.410055176,0.410162018,0.410194763,
0.410431274,0.410508759,0.410972931,0.411087982,0.411265167,
0.411515259,0.41160791,0.411635895,0.411649719,0.411834625,
0.411903839,0.411943359,0.412051849,0.412091705,0.412242004,
0.412488464,0.412593079,0.412932617,0.41296936,0.413021576,
0.41315152,0.413223419,0.413432587,0.413603394,0.413747131,
0.414036987,0.414224426,0.414521973,0.414579041,0.414584412,
0.414615143,0.414818512,0.414840546,0.414894836,0.415108612,
0.415213959,0.415307068,0.415389618,0.415536652,0.415803253,
0.415851196,0.416024353,0.416226166,0.416481598,0.416563354,
0.41662616,0.416643433,0.417017181,0.417039368,0.417109344,
0.417118652,0.417316071,0.417598328,0.417621521,0.417658142,
0.417847137,0.417878265,0.41794754,0.417958527,0.418013306,
0.418052887,0.418072693,0.418145996,0.418407562,0.418833374,
0.418944946,0.419109283,0.419632904,0.419637054,0.419677338,
0.419761566,0.419783051,0.419912384,0.419968964,0.420367249,
0.42148053,0.422050812,0.422191711,0.422507629,0.422579773,
0.422591095,0.423097809,0.423246857,0.42359317,0.423649902,
0.423762817,0.423834076,0.423988953,0.424158203,0.424568207,
0.424897064,0.424916565,0.425004059,0.425118225,0.425691162,
0.425706116,0.425915955,0.426033386,0.426442749,0.426556488,
0.42658078,0.426627502,0.426879852,0.426982361,0.427044525,
0.427077087,0.427095062,0.427133057,0.427134247,0.427186768,
0.427208008,0.427326172,0.427406342,0.427440674,0.427665771,
0.427836487,0.42791037,0.428029358,0.428465607,0.428661621,
0.429035126,0.429647156,0.430072083,0.430279236,0.430412903,
0.431081726,0.4311026,0.431213074,0.43170816,0.431882538,
0.431899445,0.43190799,0.431922699,0.432004242,0.432083099,
0.432088379,0.432111206,0.432257294,0.432267334,0.432305664,
0.432734436,0.433323669,0.433715393,0.434090424,0.434234467,
0.434782867,0.43499646,0.435217804,0.435572174,0.435894196,
0.435978577,0.436150757,0.436196289,0.436362183,0.43644455,
0.436522583,0.436726288,0.436803375,0.437201996,0.437249634,
0.437316315,0.437359802,0.437611755,0.437682098,0.438423218,
0.43851651,0.438716827,0.439065155,0.439162842,0.439253113,
0.439659119,0.439708893,0.43979306,0.439956635,0.440405518,
0.440629974,0.440909912,0.443689026,0.443695496,0.443764435,
0.444085327,0.444217041,0.444273712,0.444443268,0.44444989,
0.445305542,0.445313782,0.445395691,0.445662354,0.445740723,
0.44635495,0.446372284,0.447317261,0.447344635,0.447552948,
0.447880798,0.447948822,0.448442535,0.448568756,0.448824585,
0.448861298,0.448987823,0.44907785,0.449146393,0.44919693,
0.449850983,0.450151825,0.450175232,0.450297028,0.45032254,
0.45049765,0.450510468,0.451013397,0.45114389,0.451353821,
0.451361389,0.451370483,0.451411346,0.45149176,0.451564941,
0.45176944,0.452148712,0.452250031,0.452344116,0.452470367,
0.452534149,0.452535034,0.453011749,0.45309845,0.453195435,
0.453302521,0.453346405,0.453465698,0.453518066,0.453585907,
0.453636505,0.453776184,0.453977386,0.454021027,0.454027618,
0.454426147,0.454479675,0.454485535,0.454598022,0.454610352,
0.454642639,0.454696259,0.454798187,0.454861267,0.454869965,
0.455513184,0.455566498,0.455657501,0.456047485,0.456242432,
0.456687378,0.458511505,0.458661652,0.458696594,0.459524597,
0.46004538,0.460110657,0.460142548,0.460205048,0.460441467,
0.460455719,0.460737549,0.46081665,0.460826538,0.460870544,
0.460956573,0.461389221,0.461444061,0.461447784,0.462217316,
0.462756134,0.462771149,0.463053741,0.463219757,0.463319611,
0.463617981,0.463930267,0.4645625,0.464744202,0.464863983,
0.464954498,0.465255554,0.465679321,0.466082031,0.466343597,
0.466744476,0.466926544,0.467033081,0.467118073,0.467384308,
0.467844666,0.468338287,0.468548431,0.468597992,0.468937042,
0.469090912,0.470120056,0.470251465,0.470288391,0.470705322,
0.470769287,0.470875458,0.471105865,0.471293854,0.471612885,
0.47191806,0.472966919,0.47318042,0.473540833,0.474070282,
0.47430896,0.474347137,0.474617584,0.474734192,0.47486438,
0.474920532,0.475399139,0.475535492,0.475773651,0.47595993,
0.476034363,0.47625238,0.47629184,0.476380646,0.47648172,
0.476489319,0.476597107,0.476944519,0.477038757,0.477087921,
0.477170441,0.477255066,0.477550049,0.477760376,0.477868927,
0.478370605,0.478575317,0.478821533,0.479108032,0.47916449,
0.479351562,0.479432098,0.479517914,0.47956778,0.479818726,
0.47985199,0.47987854,0.480201447,0.480212189,0.480789764,
0.481007965,0.481310333,0.481749054,0.482190765,0.482247498,
0.482249115,0.48264444,0.483113098,0.483251617,0.483310211,
0.483416718,0.483466583,0.483617554,0.483724762,0.483770355,
0.484208313,0.484510529,0.484566711,0.484687561,0.484707947,
0.490251526,0.49031012,0.490651642,0.490727814,0.490839661,
0.491025055,0.491076141,0.491083466,0.491107635,0.491211945,
0.4915448,0.491701324,0.491888245,0.491932098,0.491934723,
0.492143372,0.49222049,0.492323425,0.492590546,0.492631744,
0.49302829,0.493302124,0.493339691,0.493483124,0.493496002,
0.4937883,0.493910248,0.493949677,0.493995209,0.494113617,
0.494459534,0.495093201,0.495219666,0.495224396,0.495298706,
0.495307343,0.495461823,0.495618744,0.495707672,0.495753906,
0.495907196,0.496178558,0.496229584,0.496294342,0.496450195,
0.496536072,0.496542358,0.496615509,0.496665649,0.496823853,
0.496831512,0.496990662,0.497025269,0.497197235,0.497619507,
0.49764566,0.497737366,0.497786102,0.498054688,0.498069519,
0.498093903,0.498129578,0.498249451,0.498276428,0.498287598,
0.49853479,0.498550049,0.498899841,0.498964935,0.499022827,
0.499213715,0.499326538,0.499491913,0.499577667,0.499615173,
0.499706024,0.499732178,0.499774384,0.499787933,0.499986694,
0.50017038,0.500181732,0.500612518,0.500616577,0.500630493,
0.500773926,0.500806274,0.501055847,0.501255493,0.501965759,
0.502048553,0.502445312,0.503025208,0.503504913,0.503640228,
0.503755463,0.503987732,0.504072632,0.504369751,0.504496826,
0.504731903,0.504852234,0.50519812,0.505205322,0.505537567,
0.505761932,0.505766602,0.506153473,0.506269409,0.506691986,
0.506834595,0.506975586,0.507105469,0.50724054,0.507426422,
0.50746048,0.507758606,0.507761108,0.507924866,0.508159393,
0.508557007,0.508597778,0.509141937,0.509409698,0.509524017,
0.509856201,0.509934387,0.511288757,0.51179306,0.512258057,
0.513009705,0.513415466,0.513640808,0.513772705,0.513925415,
0.514325195,0.514703735,0.515020203,0.515214783,0.515698608,
0.517126343,0.517423584,0.517648743,0.51778125,0.518710693,
0.518808594,0.519021667,0.519615479,0.520324402,0.520349609,
0.52035022,0.520415771,0.52049115,0.52123468,0.521262756,
0.521781006,0.522048218,0.52217926,0.522276184,0.522323425,
0.52240564,0.522524292,0.522663086,0.522930176,0.52304657,
0.523148254,0.523659546,0.52408551,0.524092529,0.524390503,
0.5246521,0.52483667,0.524995789,0.525025269,0.525289856,
0.52531488,0.525347595,0.525513062,0.52582959,0.525871399,
0.526506775,0.526569519,0.526578552,0.526666199,0.526955688,
0.527280396,0.527451782,0.527867737,0.528191345,0.528456787,
0.528501404,0.528991638,0.529236084,0.529959961,0.530023804,
0.531248291,0.53183429,0.53185968,0.532174377,0.532294189,
0.532298218,0.532376526,0.532385864,0.532854492,0.533330994,
0.533504761,0.533572693,0.533608459,0.53373584,0.534113098,
0.534146606,0.535596375,0.535894409,0.536109619,0.536588379,
0.537957764,0.538630676,0.539477478,0.540132263,0.540281738,
0.540776672,0.540800293,0.540994995,0.541065979,0.542037231,
0.542161255,0.542713745,0.542801697,0.542925232,0.543057251,
0.543683594,0.543832886,0.544050415,0.544065552,0.544225952,
0.544532532,0.54466803,0.54505719,0.545109802,0.546022888,
0.546073669,0.547062256,0.547344727,0.547472656,0.547707703,
0.547815796,0.547872864,0.548418884,0.548756348,0.548780457,
0.548796753,0.548931335,0.54910376,0.549340759,0.549681763,
0.549844971,0.549845337,0.550164551,0.550200989,0.551751709,
0.551758789,0.551968811,0.552322571,0.552387024,0.552759338,
0.552771423,0.552820557,0.554450745,0.554527954,0.554814697,
0.554862549,0.554979736,0.555533142,0.555777893,0.556464355,
0.55656958,0.556630127,0.557062256,0.557167419,0.557403503,
0.557722168,0.558650818,0.558862244,0.558979736,0.559381775,
0.559814941,0.559952148,0.560025452,0.560187988,0.560509583,
0.560641296,0.560741272,0.561369934,0.561375366,0.561744202,
0.562188721,0.56266333,0.56279303,0.563102722,0.56316687,
0.563309937,0.563348694,0.563499084,0.563545349,0.56368866,
0.563741394,0.564220093,0.564681458,0.56480249,0.564853027,
0.565560486,0.565572632,0.565802002,0.566638062,0.567342346,
0.567497498,0.567573181,0.567922363,0.56813208,0.568387451,
0.568925781,0.569480957,0.569634399,0.569693542,0.569705261,
0.569987549,0.571251404,0.571491943,0.571866516,0.573645935,
0.57464563,0.575032654,0.575176819,0.575280701,0.575447021,
0.575734192,0.576301331,0.576369934,0.576718079,0.577401001,
0.578594238,0.57886377,0.579585876,0.580114197,0.580127441,
0.580852722,0.581475098,0.581664612,0.581873047,0.582826843,
0.583055359,0.583220459,0.583765503,0.583905273,0.585122498,
0.585420105,0.585826965,0.586064941,0.586143738,0.586354004,
0.586524902,0.586783997,0.586991455,0.58726355,0.587439209,
0.588121338,0.588491821,0.588750122,0.589005798,0.589361389,
0.589361816,0.589539673,0.589582764,0.589687561,0.590182678,
0.590269165,0.590443787,0.590867249,0.590974609,0.59118103,
0.591277893,0.591745178,0.591819946,0.592102051,0.592184143,
0.592334778,0.592389221,0.592417847,0.593023743,0.593204468,
0.59329657,0.59336676,0.593500366,0.593523499,0.594693054,
0.59474353,0.594802551,0.594826599,0.594846313,0.594903748,
0.595036499,0.595263062,0.595536743,0.59560022,0.595605225,
0.59566394,0.59582196,0.596131531,0.596246826,0.596305725,
0.596421265,0.596754089,0.596776672,0.597013062,0.597233276,
0.597660034,0.597924988,0.597953796,0.598042847,0.598316345,
0.59839209,0.598680847,0.599226135,0.599578613,0.599584839,
0.600065186,0.600111267,0.600132568,0.600146484,0.60029187,
0.600437317,0.600653137,0.600683044,0.600748352,0.600753479,
0.601626221,0.602203369,0.602276184,0.602903809,0.603397705,
0.60387738,0.603900635,0.60393158,0.604663452,0.605288147,
0.605553406,0.606626526,0.607866943,0.608201294,0.608662048,
0.608848022,0.609279297,0.609328918,0.609559021,0.611455627,
0.611698669,0.615589905,0.615726501,0.618827271,0.620282715,
0.620290344,0.62063678,0.620800354,0.621222595,0.621466858,
0.621960449,0.621973999,0.622106873,0.622110352,0.622129822,
0.6225802,0.622607605,0.622704773,0.622822388,0.622896729,
0.623042786,0.623258423,0.623344055,0.623906128,0.624497986,
0.624763123,0.625145081,0.625273071,0.625323425,0.625718506,
0.626248291,0.626718689,0.626797791,0.626955322,0.626989807,
0.627104126,0.627135498,0.627257751,0.627333557,0.627602295,
0.628237854,0.628295898,0.628619934,0.628756348,0.62877948,
0.629248718,0.62948645,0.631973145,0.632000305,0.632315552,
0.632340088,0.632773804,0.634016541,0.635677063,0.635731628,
0.636654785,0.637246338,0.637585205,0.637730103,0.638245605,
0.639354797,0.639666931,0.639796021,0.640397522,0.640498779,
0.641044617,0.641217834,0.641389893,0.641543518,0.641958435,
0.642199951,0.642716431,0.642995483,0.64312854,0.643624817,
0.64369043,0.645072388,0.645252747,0.645265625,0.646080017,
0.64626709,0.646922974,0.647000549,0.647966003,0.648031189,
0.648528992,0.648812378,0.649675781,0.649840576,0.650120789,
0.650491943,0.650834778,0.651063721,0.651265442,0.651620056,
0.652159485,0.652910828,0.655176575,0.655209778,0.655757751,
0.656091553,0.656414124,0.656955688,0.656969727,0.657683411,
0.657696472,0.657958008,0.658011108,0.658693665,0.658842346,
0.660490845,0.662095398,0.662719971,0.663071594,0.663168884,
0.66318042,0.663565918,0.663986694,0.664010803,0.664149414,
0.664613281,0.665152588,0.665553589,0.665880371,0.666034241,
0.666056885,0.666659973,0.667077026,0.66840863,0.668454407,
0.668562988,0.669175964,0.669274109,0.669886719,0.670924377,
0.672983154,0.67365094,0.674128174,0.674173645,0.674365479,
0.675844788,0.676209045,0.676534607,0.676943237,0.677263672,
0.678226501,0.679619812,0.680138184,0.680143738,0.680459717,
0.680542847,0.680703308,0.681005127,0.68160321,0.682432373,
0.683042053,0.683050293,0.683255981,0.684222778,0.684576721,
0.684728455,0.684773438,0.684999146,0.685164978,0.685439819,
0.685796204,0.686175171,0.686527893,0.68711792,0.687803345,
0.687858765,0.688248962,0.688289734,0.688841553,0.688935913,
0.68898468,0.689134583,0.689140686,0.689206177,0.689321777,
0.689334412,0.68965918,0.689675354,0.690287537,0.69036145,
0.690446289,0.692340515,0.693162659,0.694588013,0.694892883,
0.694972534,0.69552533,0.697814575,0.699097595,0.699582275,
0.700199707,0.701833923,0.702247498,0.702894287,0.703288513,
0.703409729,0.703722717,0.704959045,0.705934998,0.70629126,
0.706482361,0.707137207,0.708170715,0.708262085,0.708856812,
0.709203003,0.71008075,0.71168927,0.713598572,0.714201538,
0.714721375,0.715131165,0.715876709,0.715917847,0.716512695,
0.716750671,0.71762085,0.7183396,0.718925781,0.719209656,
0.720483215,0.720568787,0.720928467,0.721527344,0.72156958,
0.722369568,0.722858521,0.722916565,0.722919434,0.723023315,
0.723452209,0.723600403,0.723962646,0.724246094,0.724416748,
0.724645996,0.724727112,0.724731323,0.724897644,0.725788696,
0.725936279,0.72594281,0.726107178,0.726177979,0.726315369,
0.727491333,0.727622253,0.72774292,0.728012024,0.728495117,
0.728903625,0.729542236,0.72966803,0.729840088,0.730021606,
0.732743286,0.733328552,0.733785461,0.733906677,0.736881958,
0.739647644,0.739697449,0.740269958,0.740346008,0.740418335,
0.741054016,0.741282288,0.74143219,0.741747314,0.741927368,
0.742032837,0.742114197,0.742617493,0.742664062,0.742737183,
0.742764954,0.743059998,0.743060364,0.743319946,0.743716003,
0.745543274,0.746611267,0.747834106,0.748087585,0.748194519,
0.74915155,0.749645752,0.749774475,0.749909119,0.750547302,
0.750781677,0.751104858,0.751794312,0.751841797,0.752962646,
0.753519226,0.753557495,0.753651245,0.753745483,0.754881531,
0.755373779,0.755593323,0.755812561,0.75610199,0.756314636,
0.756923157,0.757118408,0.758153442,0.75816156,0.758178711,
0.758509155,0.758676392,0.759223267,0.759286682,0.759606262,
0.761542358,0.762822388,0.763117004,0.763773499,0.765508606,
0.766787781,0.767140991,0.768355957,0.769027649,0.769792542,
0.770101013,0.770434082,0.770668152,0.773200012,0.774065796,
0.776042114,0.77652002,0.777028442,0.777083191,0.778850525,
0.779525574,0.779715637,0.779981445,0.780417725,0.780673096,
0.781009521,0.781526794,0.781721924,0.782819214,0.782871399,
0.783364624,0.784521667,0.78468811,0.784691589,0.784895752,
0.785611877,0.78593573,0.786121887,0.786290344,0.786787476,
0.787353149,0.787371399,0.787412598,0.787557617,0.787578552,
0.787808716,0.788189209,0.788328796,0.788540649,0.78884668,
0.789827332,0.790282532,0.790678833,0.791284546,0.791326599,
0.791632629,0.791945251,0.791986023,0.792737671,0.792742004,
0.792850769,0.792857422,0.793447876,0.793720703,0.793971619,
0.794176208,0.794345703,0.7943573,0.794439026,0.795627869,
0.795822632,0.795855957,0.795928955,0.795974121,0.796259949,
0.796959534,0.79719751,0.797796814,0.798136108,0.799674255,
0.799709839,0.799831299,0.799891113,0.799999939,0.800324951,
0.800812744,0.802458984,0.803950195,0.806139771,0.806723206,
0.807250183,0.807554993,0.807568359,0.808579956,0.808722839,
0.810138611,0.810206482,0.810288574,0.811145508,0.811328186,
0.811500854,0.81159906,0.811876831,0.811955383,0.812183655,
0.812205139,0.812921509,0.812963867,0.814040588,0.814289307,
0.815658081,0.816173767,0.816530762,0.816721619,0.816757935,
0.816999207,0.817131775,0.818582703,0.819167847,0.830116455,
0.830974487,0.831124451,0.831189453,0.831513794,0.83153717,
0.831666809,0.832342834,0.833000488,0.833265198,0.833346436,
0.833541382,0.834044556,0.834155823,0.834419617,0.834562622,
0.835095581,0.835501038,0.835785645,0.836313232,0.836434326,
0.836443787,0.836603333,0.836995178,0.837897034,0.838089722,
0.838094543,0.83836145,0.83836261,0.838537537,0.838597961,
0.838783569,0.838869385,0.838920166,0.839444092,0.839946472,
0.842126831,0.842493225,0.843593506,0.844451477,0.845778076,
0.847719238,0.84804303,0.848647583,0.850386841,0.850627075,
0.851260925,0.853136841,0.853234802,0.853310303,0.853337463,
0.853815918,0.85403186,0.854471375,0.854535461,0.854594238,
0.854891602,0.855909485,0.856174561,0.856728882,0.856770813,
0.857020691,0.857457947,0.859177673,0.859327576,0.859634399,
0.860292114,0.86086908,0.86115686,0.861521301,0.861590088,
0.861657166,0.861722839,0.862314148,0.862332703,0.862520264,
0.862892334,0.862944031,0.863426758,0.863452332,0.863757141,
0.864963379,0.865613403,0.86614978,0.867255859,0.869021118,
0.86977179,0.871631775,0.873251221,0.875585205,0.875674194,
0.878145447,0.87901709,0.880145203,0.880870605,0.881455933,
0.881466858,0.881712341,0.882592773,0.883265503,0.883378357,
0.883922546,0.884651428,0.885242004,0.885672607,0.886496399,
0.886539856,0.886577942,0.887144775,0.88759259,0.887689148,
0.888184631,0.888563965,0.888685486,0.888712097,0.889006287,
0.889032837,0.889263428,0.889696716,0.889732666,0.891206787,
0.892576294,0.892978638,0.893987244,0.897857056,0.898330566,
0.900353821,0.900354431,0.902042664,0.902581848,0.902772827,
0.902819214,0.903039368,0.903302612,0.903393372,0.90381958,
0.903893921,0.904054443,0.904124023,0.904231018,0.905078674,
0.905196289,0.905205017,0.905244263,0.905565857,0.90566626,
0.90617334,0.907925598,0.90842688,0.908430481,0.909251099,
0.910273071,0.910351929,0.910362549,0.910505249,0.910874268,
0.911601013,0.912326355,0.912742371,0.913975952,0.915670654,
0.916180786,0.916580688,0.916606018,0.916863892,0.91719696,
0.91721814,0.917703735,0.918920532,0.919189026,0.919304138,
0.920865479,0.921624939,0.92313739,0.925736206,0.927024414,
0.929339539,0.930453979,0.933976562,0.935984131,0.936130432,
0.937126099,0.937832153,0.938113525,0.938840759,0.941758667,
0.943829834,0.944756775,0.944844238,0.946224304,0.946859741,
0.947441589,0.948545776,0.949196899,0.949986023,0.953069214,
0.954046753,0.954485901,0.95514563,0.956954773,0.957368347,
0.957686401,0.957915833,0.95792395,0.958306274,0.960031067,
0.960275696,0.960479187,0.96066394,0.963355957,0.963910522,
0.964662537,0.965075623,0.965141357,0.965589539,0.966081055,
0.966103882,0.969006836,0.969361267,0.969630981,0.96999823,
0.970072998,0.970704773,0.970910828,0.971296814,0.971862854,
0.972858398,0.973051819,0.973085083,0.973344482,0.973518188,
0.973654541,0.973817871,0.974747498,0.974876343,0.975556458,
0.975569275,0.975788879,0.97615155,0.976171509,0.976181946,
0.976368408,0.976454895,0.97686499,0.976907837,0.977476929,
0.977494629,0.977495361,0.97802771,0.978241516,0.978414856,
0.979457703,0.979822327,0.979950256,0.979987488,0.982135254,
0.983209229,0.983458618,0.983725891,0.983943726,0.984227417,
0.986948669,0.988457275,0.98883667,0.989544373,0.990015625,
0.990173096,0.990286011,0.990319336,0.99032251,0.990399841,
0.990449463,0.990467834,0.990472473,0.99061438,0.990638977,
0.990685547,0.990964355,0.991058289,0.991117371,0.99126532,
0.991321106,0.991391785,0.991435059,0.991460754,0.991563171,
0.991604797,0.991628235,0.991689392,0.991725342,0.991783447,
0.991805969,0.991880249,0.991883118,0.991885498,0.992323669,
0.992480591,0.992518372,0.992563171,0.992563354,0.992576477,
0.992657654,0.992769592,0.992827454,0.99283606,0.992883484,
0.992910156,0.992986267,0.993108704,0.993159119,0.993235291,
0.993285706,0.993598633,0.993971191,0.994171509,0.994211426,
0.994300598,0.994306335,0.994324951,0.994348267,0.994409363,
0.994696411,0.994705444,0.99489679,0.994987366,0.995185669,
0.995193848,0.995258057,0.995386658,0.995417542,0.995419006,
0.995497192,0.995605591,0.995625366,0.995943848,0.99594928,
0.99601355,0.996147766,0.996549927,0.996825256,0.996943115,
0.996958496,0.997036194,0.997093628,0.99723999,0.997318115,
0.997341309,0.997433533,0.997916077,0.997951355,0.997990662,
0.998216125,0.998389771,0.998442017,0.99855249,0.99884021,
0.998858948,0.998953918,0.998980713,0.999077026,0.999100586,
0.999174377,0.999368042,0.999409973,0.999585327,0.999657166,
0.999901306,1.001000793,1.002084717,1.00295105,1.003199646,
1.003623169,1.004984192,1.006082764,1.007553833,1.007917969,
1.009870178,1.010131287,1.010521179,1.011132507,1.012827271,
1.013084961,1.013477844,1.015720703,1.016350708,1.016629028,
1.018021057,1.018452698,1.019309753,1.020328918,1.021095154,
1.021749634,1.021948303,1.022897034,1.026079956,1.029685547,
1.030856934,1.032023682,1.032995361,1.033526855,1.034301392,
1.035035034,1.035275635,1.038085083,1.038581543,1.038970459,
1.040724365,1.041138794,1.041890747,1.042037109,1.042394531,
1.043000977,1.044542603,1.046422974,1.047574585,1.04897229,
1.050310303,1.051088379,1.052273682,1.052694214,1.053562744,
1.055188843,1.055242065,1.055854126,1.056170654,1.056728516,
1.057967529,1.058167847,1.058470947,1.059043213,1.062022949,
1.063287354,1.063418213,1.06653064,1.066884155,1.067180176,
1.069015381,1.0710896,1.071260132,1.073414917,1.073523193,
1.07440979,1.074926514,1.075327637,1.076018188,1.076737915,
1.077820679,1.081031372,1.081074951,1.082428467,1.083586792,
1.083738892,1.083987183,1.084459351,1.085224365,1.087097168,
1.089797119,1.090105225,1.090225098,1.091150879,1.091927734,
1.092077637,1.092129639,1.092156006,1.093207275,1.093774536,
1.094612915,1.094714966,1.095209961,1.09566394,1.096435913,
1.096774292,1.09738208,1.09748291,1.097608154,1.099293213,
1.101686523,1.102212036,1.102539307,1.104169556,1.104212402,
1.104480347,1.104633179,1.105067139,1.106657959,1.107026001,
1.108134399,1.109167725,1.1093573,1.111553345,1.113321655,
1.114368164,1.114578369,1.114686768,1.115543701,1.11577356,
1.116091919,1.116935669,1.117380615,1.118808594,1.119003174,
1.119252563,1.11945459,1.120711914,1.120897461,1.121367554,
1.122185303,1.1227854,1.12340271,1.123795654,1.124687866,
1.124890381,1.125386841,1.125890869,1.126934082,1.127990845,
1.129457397,1.129936157,1.130286011,1.130710205,1.131535278,
1.131941895,1.13392395,1.134045898,1.13759668,1.137717285,
1.138784546,1.140327881,1.141746582,1.143296875,1.143445923,
1.144460571,1.147721436,1.148281982,1.148564209,1.150283569,
1.152907227,1.153259644,1.153702515,1.155180054,1.156345459,
1.157547852,1.157772583,1.158139893,1.159214722,1.161239136,
1.161318848,1.161574097,1.162797852,1.162869385,1.162973877,
1.164699951,1.165045898,1.167138794,1.169135864,1.169896729,
1.171323608,1.172630493,1.172674805,1.172730347,1.173012085,
1.175005493,1.175073975,1.177733032,1.179353027,1.180281372,
1.180346191,1.180737061,1.18170874,1.182654541,1.183563965,
1.18372644,1.184732666,1.185146851,1.185277466,1.185985962,
1.186616821,1.186975464,1.187404663,1.190020508,1.192437134,
1.194540649,1.195005371,1.195277588,1.195346191,1.19867395,
1.20070459,1.201553223,1.20532019,1.212145874,1.214689209,
1.215115112,1.219803955,1.220687256,1.2227854,1.223384766,
1.224000854,1.225066528,1.229021851,1.229312988,1.229915771,
1.230252808,1.233218384,1.235617676,1.236932983,1.237187134,
1.237394043,1.240209839,1.245358765,1.245421265,1.24762085,
1.249431152,1.252744263,1.25317041,1.255528442,1.257078613,
1.259386963,1.264457031,1.264679077,1.264739014,1.266751587,
1.269782227,1.271042114,1.272080566,1.272560425,1.273954834,
1.274124146,1.275851685,1.276715942,1.277113647,1.278739014,
1.279828369,1.279889526,1.280586792,1.282549805,1.283779541,
1.287615234,1.292177002,1.292578613,1.292601562,1.298741699,
1.302103394,1.302647461,1.303253296,1.30378894,1.304247314,
1.306755249,1.307308716,1.309400635,1.310855835,1.311106445,
1.31378833,1.316479858,1.317053101,1.317522583,1.318164307,
1.318270874,1.321132202,1.321240601,1.32182666,1.321954224,
1.322248291,1.324069458,1.324309937,1.324467041,1.325013306,
1.327894409,1.32912915,1.330667358,1.330762695,1.330792114,
1.331368652,1.332085938,1.33218689,1.334186768,1.334609985,
1.33726416,1.337324341,1.337987549,1.341283936,1.343338989,
1.343872803,1.347181396,1.347185425,1.347856689,1.3480354,
1.3495354,1.351883423,1.3522771,1.355430542,1.355581909,
1.356277222,1.359136353,1.359207031,1.361409912,1.361917114,
1.364702026,1.36506189,1.365745605,1.367353882,1.367902832,
1.369015869,1.370270996,1.371610596,1.373140991,1.373476562,
1.375279175,1.377139526,1.37862561,1.380812988,1.381191284,
1.381783936,1.382362793,1.382723267,1.384870972,1.385243042,
1.385817627,1.387909912,1.391732422,1.394208252,1.394845093,
1.399474243,1.399656372,1.39969812,1.400275513,1.402613525,
1.403484497,1.404858765,1.407701904,1.410393799,1.412147949,
1.420328125,1.42205127,1.424798462,1.42933374,1.433901733,
1.434197754,1.434735474,1.439731567,1.440729126,1.445185547,
1.447816284,1.449111572,1.452306152,1.454491089,1.45473645,
1.454741089,1.456130249,1.458288574,1.459494385,1.462784668,
1.466089233,1.469988892,1.470617065,1.472662109,1.47578418,
1.477922607,1.478423584,1.479460571,1.482334351,1.483793091,
1.484786133,1.484886841,1.485599365,1.486166138,1.486946777,
1.488109253,1.490864624,1.49135083,1.491373047,1.491620361,
1.491741943,1.492733032,1.49629187,1.496573242,1.498113403,
1.504185791,1.504639771,1.504794312,1.508480347,1.508754395,
1.511386353,1.511590576,1.511604248,1.514729858,1.515040527,
1.51533313,1.521404663,1.523896606,1.524911377,1.525164795,
1.526168091,1.529264526,1.531988525,1.53225708,1.532334473,
1.535562622,1.540201294,1.541034058,1.542304565,1.548986328,
1.549563477,1.556132812,1.558362183,1.558379028,1.563819092,
1.565541382,1.570986084,1.571640991,1.575724121,1.578119385,
1.578379395,1.584365356,1.586374023,1.589832764,1.590185181,
1.591287109,1.593630005,1.594203125,1.596196533,1.597116089,
1.599792969,1.601076904,1.604870728,1.605164062,1.612198853,
1.612551636,1.614723511,1.622638184,1.624871826,1.625888428,
1.626426636,1.626959351,1.62946875,1.630190552,1.632665283,
1.634284424,1.635668213,1.637199341,1.639116333,1.64005957,
1.640544434,1.641946411,1.642114746,1.642637817,1.646208862,
1.649340088,1.649582642,1.649723022,1.651080444,1.652480225,
1.653738037,1.657194824,1.662022461,1.662425293,1.664752319,
1.666431763,1.671957275,1.673326904,1.674413574,1.675965576,
1.676283447,1.676624023,1.678227417,1.67868396,1.679069702,
1.681665039,1.682264893,1.683098267,1.683262329,1.69638562,
1.697199829,1.698327148,1.699717285,1.700046997,1.701757935,
1.705819946,1.707098633,1.712921753,1.714866455,1.7166427,
1.719520874,1.720162842,1.722974854,1.724654541,1.72628833,
1.728937378,1.729499634,1.73210437,1.733430176,1.733884277,
1.734413086,1.735576904,1.736977295,1.737994019,1.739846802,
1.740512085,1.747168701,1.747287476,1.749582031,1.75013269,
1.750341675,1.754084473,1.754317383,1.755570923,1.7573125,
1.75838623,1.759119507,1.760531006,1.764060913,1.766290039,
1.770450806,1.772911743,1.774884033,1.777070923,1.779233398,
1.785087158,1.785759888,1.787641724,1.787719482,1.788092896,
1.790958008,1.795494019,1.797495117,1.797757324,1.800034912,
1.800260254,1.803680664,1.808401978,1.813766357,1.818499878,
1.818672363,1.820279053,1.826441406,1.83024707,1.832563843,
1.834519775,1.835050903,1.838729858,1.841286865,1.84608313,
1.847658203,1.851199829,1.852805664,1.853054443,1.854439453,
1.854511841,1.856557129,1.856956787,1.858206909,1.858409058,
1.86299585,1.866013794,1.866168823,1.867863159,1.873113647,
1.874079102,1.875488403,1.878308594,1.879806396,1.882010864,
1.882031738,1.883045898,1.888545532,1.893546753,1.894928833,
1.89596167,1.897840576,1.899738525,1.902782104,1.903151001,
1.905443726,1.916994385,1.921615112,1.922505737,1.92624585,
1.928239868,1.944804688,1.955582031,1.958547974,1.962349609,
1.965199829,1.973251099,1.982303711,1.983384644,1.984899414,
1.995090698,2.000453003,2.001070435,2.001716797,2.00177124,
2.007667236,2.008972168,2.010001709,2.012715088,2.013534668,
2.016244629,2.020276367,2.024755615,2.029480713,2.034178833,
2.037913086,2.03918335,2.040546387,2.042032593,2.042979492,
2.044937622,2.046131714,2.046411987,2.048258301,2.048860352,
2.051833008,2.0519375,2.053651367,2.053776123,2.0538125,
2.054380615,2.054583252,2.056418457,2.059718506,2.062598145,
2.064525635,2.064899658,2.065977783,2.068319336,2.069439941,
2.069827393,2.070818115,2.071463379,2.07429834,2.076056885,
2.083845703,2.085324463,2.086862061,2.087311768,2.088359863,
2.089463867,2.092138428,2.094078613,2.09751709,2.099021484,
2.102568604,2.103133301,2.112424316,2.112994141,2.118729248,
2.118755127,2.11988208,2.122932861,2.123374756,2.125358154,
2.126861328,2.138140381,2.144907227,2.145472412,2.153137451,
2.158188721,2.167263428,2.172206787,2.175770508,2.178402588,
2.182637939,2.190139648,2.19203125,2.198459229,2.200186035,
2.202268066,2.204910645,2.208319092,2.211190918,2.215150391,
2.216313721,2.217993652,2.222648926,2.222721924,2.224633545,
2.226083984,2.228534912,2.228792969,2.230128174,2.23380957,
2.240642334,2.242388672,2.242767578,2.24628833,2.248499268,
2.256580322,2.25687207,2.258839355,2.267066162,2.269337158,
2.269798828,2.274237793,2.275867188,2.288794189,2.289172852,
2.291803223,2.29214209,2.295948242,2.299455811,2.300808105,
2.31117627,2.323036865,2.32549585,2.330847168,2.332133057,
2.332655029,2.334123047,2.334856689,2.339581543,2.341599121,
2.341757324,2.345629639,2.356780518,2.357526367,2.358175049,
2.365378174,2.366271973,2.366450684,2.370789062,2.370990234,
2.371526367,2.376875732,2.382968262,2.383584229,2.39359375,
2.39501709,2.39875,2.399133057,2.39988208,2.404741211,
2.415563477,2.422387939,2.425092285,2.426954834,2.428393311,
2.432313232,2.433234131,2.434164307,2.440160645,2.447610352,
2.452584473,2.455479492,2.459909668,2.464161377,2.470407471,
2.485737549,2.48706958,2.493045166,2.496272461,2.498527832,
2.498665039,2.503288086,2.542390137,2.550358398,2.552658447,
2.560479004,2.563396973,2.572145508,2.57326001,2.575140625,
2.596747314,2.602743408,2.603919922,2.607102295,2.61294165,
2.616275879,2.617774902,2.618488281,2.62016333,2.622706787,
2.632514648,2.633458008,2.644857178,2.648341064,2.657633789,
2.660433838,2.66308374,2.665605957,2.670506348,2.676703857,
2.68266748,2.686050537,2.687670654,2.692914795,2.698838623,
2.701449707,2.713996582,2.720814697,2.721899414,2.727447021,
2.732695801,2.739406738,2.750258545,2.754066895,2.754894043,
2.759366211,2.769315918,2.774402832,2.776136475,2.781078369,
2.781447021,2.786972412,2.796018799,2.796188477,2.796210938,
2.798428467,2.799642334,2.808644531,2.813160889,2.814708252,
2.822111816,2.823582764,2.825131592,2.835417236,2.838349365,
2.846992432,2.858225586,2.859194824,2.862681641,2.875594727,
2.875760742,2.89538208,2.900593262,2.905825439,2.920287598,
2.923685547,2.928446533,2.941855957,2.943194824,2.952397949,
2.956604248,2.958039551,2.969929443,2.981328857,2.984472412,
3.002790283,3.002816162,3.011059814,3.016012695,3.017810303,
3.018194092,3.025860596,3.030968506,3.036666016,3.045195068,
3.049595459,3.055766113,3.072657227,3.076478271,3.077032471,
3.090971191,3.111178955,3.1164375,3.119001465,3.121286133,
3.129217285,3.130228516,3.132900879,3.168595947,3.173787598,
3.205906982,3.224657227,3.228318115,3.229275879,3.2337771,
3.234171631,3.237051758,3.242515137,3.24283374,3.248281494,
3.26111084,3.263012207,3.268534912,3.281818115,3.28210498,
3.289982422,3.292951904,3.296039307,3.297588623,3.298346436,
3.309249512,3.313679688,3.317656982,3.3249021,3.329720703,
3.333010742,3.3447854,3.347728271,3.348918213,3.353848633,
3.354405029,3.356245361,3.363745117,3.364911377,3.368900635,
3.370177002,3.371508301,3.399830566,3.428646729,3.439663086,
3.444452881,3.450486572,3.453106689,3.457710449,3.486176514,
3.487521973,3.501026611,3.506884277,3.516647949,3.535149902,
3.542210938,3.545496826,3.5469104,3.548461426,3.57400708,
3.582498779,3.592018066,3.604897705,3.609455811,3.62267041,
3.632086914,3.633587646,3.641440186,3.662989746,3.667384033,
3.672197021,3.674447266,3.67912915,3.705628418,3.714524902,
3.714773926,3.731265625,3.791690186,3.798285156,3.903860107,
3.909766113,3.911020996,3.912746338,3.930047119,3.933247314,
3.936636475,3.940496582,3.943955322,3.958216553,3.964937988,
3.967656738,3.968608643,3.969850586,3.97277124,3.975421387,
3.976193115,3.97804834,3.978732178,3.979463379,3.986647949,
3.988596436,3.99851001,4.005006836,4.025091309,4.040290527,
4.043079834,4.049722656,4.065845947,4.078110596,4.090810059,
4.105180664,4.117133789,4.130483887,4.133375977,4.1381875,
4.146490723,4.150787109,4.150928223,4.164693848,4.164849609,
4.176842773,4.180748535,4.18211377,4.189375,4.193438965,
4.198446777,4.19872168,4.201957031,4.208887695,4.21821875,
4.236554199,4.252316406,4.253601562,4.272827148,4.295642578,
4.315491211,4.324412598,4.330452148,4.356595215,4.36140918,
4.364258301,4.376893066,4.384969727,4.386518555,4.393078613,
4.403236816,4.445647949,4.471635254,4.474141602,4.482959961,
4.485025391,4.486205078,4.489489258,4.491880859,4.494522949,
4.70516748,4.723369141,4.739822754,4.762258301,4.774524414,
4.778798828,4.807952637,4.81283252,4.818986816,4.825053711,
4.825220703,4.82667334,4.847002441,4.849364258,4.849655762,
4.861241699,4.867044434,4.870148438,4.871204102,4.873291016,
4.876619141,4.877787598,4.895299805,4.902679688,4.902760254,
4.904861328,4.906794434,4.906876465,4.90797998,4.90877002,
4.910755371,4.91203125,4.914706543,4.914730957,4.91757959,
4.923696777,4.924658691,4.926006348,4.927462891,4.933706543,
4.933763184,4.935209961,4.94005127,4.942512695,4.943708496,
4.944270996,4.944841797,4.94747168,4.948941406,4.954109863,
4.954301758,4.958184082,4.963442383,4.964400391,4.964513672,
4.966166504,4.970353516,4.978773926,4.979399902,4.979860352,
4.986195312,4.988005371,4.988169922,4.988492676,4.991200195,
4.991437988,4.993394531,4.994642578,4.995326172,4.998217773,
4.998580078,4.998897949,5.04165918,5.057301758,5.069346191,
5.075321777,5.086865234,5.095601562,5.12353125,5.130533203,
5.144615234,5.23055127,5.234211426,5.308802734,5.312199219,
5.314931152,5.348370117,5.349544922,5.366281738,5.372622559,
5.387371094,5.389810059,5.40061084,5.408188965,5.40912793,
5.415080078,5.41909375,5.467452637,5.468619141,5.478391113,
5.517120605,5.535972656,5.541043457,5.56153125,5.584540039,
5.610851562,5.620021973,5.659691406,5.774812012,5.796128906,
5.811850098,5.817922852,5.818269043,5.853930664,5.858775879,
5.868673828,5.889299805,5.961053223,6.025807129,6.039085449,
6.04397168,6.073463379,6.094379395,6.096313965,6.169668945,
6.175777344,6.186970215,6.202591309,6.258280273,6.304723633,
6.308237305,6.320072266,6.339833984,6.341913574,6.351582031,
6.368272461,6.430239746,6.437690918,6.475090332,6.477464844,
6.478616699,6.49685791,6.505789062,6.511837402,6.52198291,
6.538517578,6.548273438,6.577727051,6.593699707,6.610338379,
6.640342773,6.644941406,6.655653809,6.66830957,6.671396484,
6.697515625,6.72410791,6.755875,6.774936523,6.828643555,
6.872219727,6.915688477,6.919624023,6.920462891,6.933017578,
6.97533252,7.018027344,7.024165039,7.061348633,7.118743652,
7.176062012,7.191490234,7.209256836,7.223888184,7.249333008,
7.329322266,7.365118164,7.410904785,7.429462402,7.431371094,
7.449100586,7.500640625,7.630129883,7.681236328,7.711913574,
7.776723633,7.792081055,7.824811523,7.849945801,8.080029297,
8.180430664,8.276853516,8.352905273,8.377416016,8.380743164,
8.39090625,8.399150391,8.409282227,8.424939453,8.439873047,
8.4565625,8.520005859,8.682929688,8.730463867,8.752469727,
8.755083984,8.813133789,8.81659668,8.833368164,8.873854492,
8.892746094,8.89622168,8.945298828,8.962418945,9.033444336,
9.050657227,9.061486328,9.132459961,9.193925781,9.197686523,
9.355198242,9.371333984,9.392910156,9.442420898,9.506911133,
9.624088867,9.648463867,9.662895508,9.731837891,9.871972656,
9.88371582,9.933389648,9.966209961,9.986818359,10.046589844,
10.055272461,10.305358398,10.32906543,10.405125977,10.413963867,
10.493505859,10.511933594,10.586949219,10.622994141,10.698851562,
10.737299805,10.748189453,10.885863281,10.922195312,10.931462891,
12.048387695,12.076888672,12.109479492,12.254077148,12.436068359,
12.511602539,12.516683594,12.531848633,12.551056641,12.638005859,
12.646768555,12.656470703,12.689657227,12.696110352,12.750787109,
12.830836914,12.842152344,12.920453125,12.996629883,12.997738281,
13.291851562,13.33706543,13.391154297,13.465112305,13.679009766,
13.863282227,14.108433594,14.110461914,14.349454102,14.484691406,
14.556863281,14.741895508,14.767268555,14.79084082,14.938492188,
17.036658203,17.181916016,17.290230469,17.393863281,17.434109375,
17.484427734,17.529910156,17.712167969,17.734861328,17.759039062,
20.038845703,20.172761719,20.206966797,20.235734375,20.328359375,
20.444199219,20.580189453,20.676021484,20.7650625,21.27665625,
21.319716797,21.374023438,21.490015625,21.513216797,21.540316406,
21.559779297,21.607675781,21.780464844,21.802054688,22.053214844,
22.780626953,22.874847656,23.153798828,23.208380859,24.334546875,
24.626380859,24.877210938,24.96040625,24.989421875,25.005804688,
25.171150391,26.015640625,26.091,26.168595703,26.537908203,
26.816958984,26.94953125,27.118929688,27.303550781,27.705675781,
27.940667969,32.789222656,32.876980469,33.394457031,33.585667969,
33.691761719,35.308953125,36.124308594,36.126429688,36.378316406,
36.9625625,37.700761719,38.105875,38.892945312,39.223425781,
39.538332031,39.545082031,39.888535156,40.01253125,40.703480469,
40.985832031,41.318773438,41.453355469,41.520109375,42.869316406,
42.990523438,43.294757812,43.784105469,43.810277344,44.652226562,
44.659394531,45.505921875,45.549300781,45.914746094,59.032835938,
59.171453125,59.188242188,59.194550781,59.220753906,59.229398438,
59.2781875,59.29696875,59.360042969,59.389535156,59.444675781,
59.484121094,59.552898438,59.625878906,59.702589844,59.755855469,
59.859945312,59.935503906,59.962140625,59.981171875,64.415308594,
89.67025,115.543304688,133.35125,137.70978125,209.92121875
};
double tcplib_telnet_interarrival()
{
int pos = int(Random::uniform() * 10000.0);
if ( pos < 0 || pos >= 10000 ) {
printf("oops, bug in tcplib_telnet_interarrival, pos = %d\n",
pos);
abort();
}
return tcplib_telnet[pos];
}
#if 0
int main( int argc, char **argv )
{
int i, num;
++argv, --argc;
num = 1;
if ( argc > 0 )
num = atoi( argv[0] );
for ( i = 0; i < num; ++i )
printf( "%.9f\n", tcplib_telnet_interarrival() );
return 0;
}
#endif
telnet.cc
/* -*- 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.
*
* @(#) $Header$
*/
#include "random.h"
#include "tcp.h"
#include "telnet.h"
extern double tcplib_telnet_interarrival();
static class TelnetAppClass : public TclClass {
public:
TelnetAppClass() : TclClass("Application/Telnet") {}
TclObject* create(int, const char*const*) {
return (new TelnetApp);
}
} class_app_telnet;
TelnetApp::TelnetApp() : running_(0), timer_(this)
{
bind("interval_", &interval_);
}
void TelnetAppTimer::expire(Event*)
{
t_->timeout();
}
void TelnetApp::start()
{
running_ = 1;
double t = next();
timer_.sched(t);
}
void TelnetApp::stop()
{
running_ = 0;
}
void TelnetApp::timeout()
{
if (running_) {
/* call the TCP advance method */
agent_->sendmsg(agent_->size());
/* reschedule the timer */
double t = next();
timer_.resched(t);
}
}
double TelnetApp::next()
{
if (interval_ == 0)
/* use tcplib */
return tcplib_telnet_interarrival();
else
return Random::exponential() * interval_;
}
tfcc.cc
/*
* Copyright (c) 1998 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 MASH Research
* Group at the University of California Berkeley.
* 4. Neither the name of the University nor of the Research Group 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.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$";
#endif
/* tfcc.cc -- TCP-friently congestion control protocol */
#include
#include
#include "agent.h"
#include "random.h"
#include "rtp.h"
#include "flags.h"
/*
* TFCC HEADER DEFINITIONS
*/
struct hdr_tfcc {
double rttest_; // sender's rtt estimate
double ts_; // ts at sender
double ts_echo_; // echo'd ts (for rtt estimates)
double interval_; // sender's sending interval
int cong_seq_; // congestion sequence number
int nlost_; // total packets lost
// per-var methods
double& rttest() { return (rttest_); }
double& ts() { return (ts_); }
double& ts_echo() { return (ts_echo_); }
double& interval() { return (interval_); }
int& cseq() { return (cong_seq_); }
int& nlost() { return (nlost_); }
static int offset_; // offset of tfcc header
};
int hdr_tfcc::offset_;
static class TFCCHeaderClass : public PacketHeaderClass {
public:
TFCCHeaderClass() : PacketHeaderClass("PacketHeader/TFCC",
sizeof(hdr_tfcc)) {
bind_offset(&hdr_tfcc::offset_);
}
void export_offsets() {
field_offset("rttest_", OFFSET(hdr_tfcc, rttest_));
field_offset("ts_", OFFSET(hdr_tfcc, ts_));
field_offset("ts_echo_", OFFSET(hdr_tfcc, ts_echo_));
field_offset("cong_seq_", OFFSET(hdr_tfcc, cong_seq_));
field_offset("interval_", OFFSET(hdr_tfcc, interval_));
field_offset("nlost_", OFFSET(hdr_tfcc, nlost_));
}
} class_tfcchdr;
/**************************** class defn ********************************/
class TFCCAgent : public RTPAgent {
public:
TFCCAgent() : srtt_(-1.0), srtt_chg_(-1.0),
rttvar_(-1.0), peer_rtt_est_(-1.0),
peer_interval_(-1.0), last_ts_(-1.0), last_loss_time_(-1.0),
last_rtime_(-1.0), expected_(0), nrcv_(0), nlost_(0), plost_(0),
cseq_(0), highest_cseq_seen_(-1),
last_cseq_checked_(-1), silence_(0), needresponse_(0), last_ecn_(0),
ack_timer_(this), rtt_timer_(this) {
bind("alpha_", &alpha_);
bind("beta_", &beta_);
bind("srtt_", &srtt_);
bind("minrtt_", &minrtt_);
bind("maxrtt_", &maxrtt_);
bind("rttvar_", &rttvar_);
bind("peer_rtt_est_", &peer_rtt_est_);
bind("ack_interval_", &ack_interval_);
bind("silence_thresh_", &silence_thresh_);
}
protected:
virtual void makepkt(Packet*);
virtual void recv(Packet*, Handler*);
virtual void loss_event(int); // called when a loss is detected
virtual void ecn_event(); // called when seeing an ECN
virtual void peer_rttest_known(double); // called when peer knows rtt
virtual void rtt_known(double); // called when we know rtt
virtual void slowdown(int); // reason to slow down
virtual void speedup(); // possible opportunity to speed up
virtual void nopeer(); // lost ack stream from peer
virtual double winchg(); // pkts to speed up by
virtual void stop();
virtual void rtt_timeout(TimerHandler*); // rtt heartbeat
virtual void ack_timeout(TimerHandler*); // ack sender
void ack_rate_change(); // changes the ack gen rate
double now() const { return Scheduler::instance().clock(); };
double rtt_sample(double samp);
double alpha_; // weighting factor for rtt mean estimate
double beta_; // weighting factor for rtt var estimate
double srtt_; // current smoothed rtt
double srtt_chg_; // srtt when speedup/slowdown last called
double rttvar_;
double maxrtt_; // max rtt seen
double minrtt_; // min rtt seen
double peer_rtt_est_; // peer's est of the rtt
double peer_interval_; // last known peer's interval_
double last_ts_; // ts field carries on last pkt
double last_loss_time_; // last time we saw a loss
double ack_interval_; // "ack" sending rate
double last_rtime_; // last time a pkt was received
int expected_; // next expected sequence number
int nrcv_; // # pkts recv'd (cumulative)
int nlost_; // # pkts lost (cumulative)
int plost_; // # pkts peer reported lost
int cseq_; // congest epoch seq # (as receiver)
int highest_cseq_seen_; // peer's last cseq seen (I am sender)
int last_cseq_checked_; // last cseq value at rtt heartbeat
int last_expected_; // value of expected_ on last rtt beat
int silence_; // # of beats we've been idle
int silence_thresh_; // call nopeer() if silence_ > silence_thresh_
int needresponse_; // send a packet in response to current one
int last_ecn_; // last recv had an ecn
class TFCCAckTimer : public TimerHandler {
public:
TFCCAckTimer(TFCCAgent *a) : TimerHandler() { a_ = a; }
protected:
void expire(Event *) { a_->ack_timeout(this); }
TFCCAgent *a_;
};
friend TFCCAckTimer;
TFCCAckTimer ack_timer_; // periodic timer for ack generation
class TFCCRttTimer : public TimerHandler {
public:
TFCCRttTimer(TFCCAgent *a) : TimerHandler() { a_ = a; }
protected:
void expire(Event *) { a_->rtt_timeout(this); }
TFCCAgent *a_;
};
friend TFCCRttTimer;
TFCCRttTimer rtt_timer_; // periodic rtt-based heartbeat
};
static class TFCCAgentClass : public TclClass {
public:
TFCCAgentClass() : TclClass("Agent/RTP/TFCC") {}
TclObject* create(int, const char*const*) {
return (new TFCCAgent());
}
} class_tfcc_agent;
/************************** methods ********************************/
double
TFCCAgent::rtt_sample(double m)
{
//printf("%f %s: RTT SAMPLE: %f\n", now(), name(), m);
// m is new measurement
if (srtt_ > 0.0) {
double delta = m - srtt_;
srtt_ += alpha_ * delta;
if (delta < 0.0)
delta = -delta;
rttvar_ += beta_ * (delta - rttvar_);
} else {
printf("%s: %f: srtt initialized to %f\n",
name(), now(), m);
srtt_ = m;
rttvar_ = srtt_ / 2.0;
}
if (m > maxrtt_)
maxrtt_ = m;
if (m < minrtt_)
minrtt_ = m;
return srtt_;
}
void
TFCCAgent::makepkt(Packet* p)
{
hdr_tfcc* th = (hdr_tfcc*)p->access(hdr_tfcc::offset_);
hdr_flags* fh = (hdr_flags*)p->access(hdr_flags::offset_);
th->ts_echo() = last_ts_;
th->ts() = now();
th->interval() = interval_;
th->nlost() = nlost_;
th->rttest() = srtt_;
th->cseq() = cseq_;
fh->ecnecho() = last_ecn_;
fh->ect() = 1;
RTPAgent::makepkt(p);
}
/*
* recv- packet contains receiver report info,
* and may also contain data
*/
void
TFCCAgent::recv(Packet* pkt, Handler*)
{
hdr_rtp* rh = (hdr_rtp*)pkt->access(off_rtp_);
hdr_tfcc* th = (hdr_tfcc*)pkt->access(hdr_tfcc::offset_);
hdr_flags* fh = (hdr_flags*)pkt->access(hdr_flags::offset_);
++nrcv_;
//printf("%f: %s: recv: seq: %d, cseq: %d, expect:%d\n", now(), name(),
// rh->seqno(), th->cseq(), expected_);
/*
* do the duties of a receiver
*/
if (th->rttest() > 0.0) {
// update our picture of our peer's rtt est
if (peer_rtt_est_ <= 0.0) {
peer_rttest_known(th->rttest());
}
peer_rtt_est_ = th->rttest();
} else {
// peer has no rtt estimate, so respond right
// away so it can get one
needresponse_ = 1;
}
peer_interval_ = th->interval();
last_ts_ = th->ts();
if (fh->ect()) {
// turn around ecn's we see
if (fh->ce()) {
last_ecn_ = 1;
ecn_event();
} else if (fh->cong_action())
last_ecn_ = 0;
}
int loss = 0;
if (expected_ >= 0 && (loss = rh->seqno() - expected_) > 0) {
nlost_ += loss;
loss_event(loss);
expected_ = rh->seqno() + 1;
} else
++expected_;
/*
* do the duties of a sender
*/
if (th->ts_echo() > 0.0) {
// update our rtt estimate
double sample = now() - th->ts_echo();
if (srtt_ <= 0.0) {
rtt_known(sample);
}
rtt_sample(sample);
}
if (th->cseq() > highest_cseq_seen_) {
// look at receiver's congestion report
highest_cseq_seen_ = th->cseq();
}
if (th->nlost() > plost_)
plost_ = th->nlost(); // cumulative
last_rtime_ = now();
Packet::free(pkt);
if (needresponse_)
sendpkt();
needresponse_ = 0;
}
/*
* as a receiver, this
* is called when there is a packet loss detected
*/
void
TFCCAgent::loss_event(int nlost)
{
// if its been awhile (more than an rtt estimate) since the last loss,
// this is a new indication of congestion
printf("%f %s: loss event: nlost:%d\n", now(), name(), nlost);
if (peer_rtt_est_ < 0.0 || (last_loss_time_ < 0.0) ||
(now() - last_loss_time_) > peer_rtt_est_) {
++cseq_;
needresponse_ = 1;
last_loss_time_ = now();
}
}
void
TFCCAgent::stop()
{
rtt_timer_.force_cancel();
RTPAgent::stop();
}
/*
* as a receiver, this is called when a packet arrives with a CE
* bit set
*/
void
TFCCAgent::ecn_event()
{
loss_event(0); // for now
}
/*
* as a receiver, this is called once when the peer first known
* the rtt
*/
void
TFCCAgent::peer_rttest_known(double peerest)
{
// first time our peer has indicated it knows the rtt
if (!running_) {
// if we are not a data sender, schedule acks
printf("%s: setting ack_interval to %f\n", name(), peerest);
ack_interval_ = peerest / 2.0;
ack_rate_change();
}
}
/*
* as a sender, this is called when we first know the rtt
*/
void
TFCCAgent::rtt_known(double rtt)
{
printf("%s: RTT KNOWN (%f), starting timer\n", name(), rtt);
rtt_timer_.resched(rtt);
}
/*
* as a sender, this is called when we are receiving acks with no
* new congestion indications
*/
double
TFCCAgent::winchg()
{
return (1.0);
}
void
TFCCAgent::speedup()
{
if (srtt_chg_ < 0.0)
srtt_chg_ = srtt_;
if (running_) {
interval_ = (srtt_ * interval_) /
(srtt_chg_ + winchg() * interval_);
printf("%s %f SPEEDUP [srtt:%f], new interval:%f, ppw:%f\n",
name(), now(), srtt_, interval_, srtt_ / interval_);
srtt_chg_ = srtt_;
rate_change();
}
}
/*
* as a sender, this is called when we are receiving acks with
* new congestion indications. nce is the number of congestion
* events. Each one results in a 1/2-ing of the rate.
*/
void
TFCCAgent::slowdown(int nce)
{
if (srtt_chg_ < 0.0)
srtt_chg_ = srtt_;
if (running_) {
while (nce--)
interval_ *= 2.0;
printf("%s %f SLOWDOWN [srtt: %f], new interval:%f, ppw:%f\n",
name(), now(), srtt_, interval_, srtt_ / interval_);
srtt_chg_ = srtt_;
rate_change();
}
}
/*
* nopeer: called if we haven't heard any acks from the
* receiver in 'silence_thresh_' number of rtt ticks
*/
void
TFCCAgent::nopeer()
{
// for now, just 1/2 sending rate
printf("%s %f NOPEER, silence:%d\n", name(), now(), silence_);
slowdown(1);
silence_ = 0;
}
void
TFCCAgent::ack_rate_change()
{
ack_timer_.force_cancel();
double t = lastpkttime_ + ack_interval_;
if (t > now())
ack_timer_.resched(t - now());
else {
sendpkt();
ack_timer_.resched(ack_interval_);
}
}
/*
* called on RTT periods to determine action to take
*/
void
TFCCAgent::rtt_timeout(TimerHandler* timer)
{
printf(">>>>> %f: %s: RTT beat: last_checked:%d, highest_seen:%d\n",
now(), name(), last_cseq_checked_, highest_cseq_seen_);
if (last_cseq_checked_ < 0) {
// initialize
last_cseq_checked_ = highest_cseq_seen_;
} else {
//
// check peer's congestion status
//
if (last_cseq_checked_ == highest_cseq_seen_) {
speedup();
} else {
slowdown(highest_cseq_seen_ - last_cseq_checked_);
last_cseq_checked_ = highest_cseq_seen_;
}
//
// check if we've heard anything from peer in awhile
//
if (expected_ == last_expected_) {
// nothing since last beat
if (++silence_ >= silence_thresh_)
nopeer();
} else {
// yep, heard something
silence_ = 0;
}
}
last_expected_ = expected_;
timer->resched(srtt_);
return;
}
/*
* called periodically to send acks
*/
void
TFCCAgent::ack_timeout(TimerHandler* timer)
{
sendpkt();
timer->resched(ack_interval_);
}
/******************** EXTENSIONS ****************************/
// VTFCC -- "VegasLike" TFCC (not finished)
class VTFCCAgent : public TFCCAgent {
public:
VTFCCAgent() : lastseq_(0), lastlost_(0) { }
protected:
void slowdown(int);
void linear_slowdown();
void speedup();
void rtt_known(double rtt);
int lastseq_;
int lastlost_;
double expectedrate_;
double actualrate_;
double lowerthresh_;
double upperthresh_;
};
static class VTFCCAgentClass : public TclClass {
public:
VTFCCAgentClass() : TclClass("Agent/RTP/TFCC/VegasLike") {}
TclObject* create(int, const char*const*) {
return (new VTFCCAgent());
}
} class_vtfcc_agent;
void
VTFCCAgent::rtt_known(double rtt)
{
expectedrate_ = 1.0 / interval_;
TFCCAgent::rtt_known(rtt);
}
void
VTFCCAgent::slowdown(int)
{
if (running_) {
interval_ *= 2.0;
printf("%s V-SLOWDOWN [srtt: %f], new interval:%f, ppw:%d\n",
name(), srtt_, interval_, int(srtt_ / interval_));
rate_change();
expectedrate_ = srtt_ / (interval_ * minrtt_);
}
}
void
VTFCCAgent::linear_slowdown()
{
if (running_) {
interval_ = (srtt_ * interval_) / (srtt_ + interval_);
printf("%s V-MILDSLOWDOWN [srtt: %f], new interval:%f, ppw:%d\n",
name(), srtt_, interval_, int(srtt_ / interval_));
rate_change();
expectedrate_ = srtt_ / (interval_ * minrtt_);
}
}
void
VTFCCAgent::speedup()
{
if (running_) {
//
// we might thing we want to speed up
// because we got no drops, but in Vegas, we might
// want to slow down still
//
double actualrate = ((seqno_ - lastseq_) -
(plost_ - lastlost_)) / srtt_;
lastlost_ = plost_; // not quite right XXX
lastseq_ = seqno_;
if (actualrate < expectedrate_) {
linear_slowdown();
return;
}
// otherwise, increase by 1
interval_ = (srtt_ * interval_) / (srtt_ + interval_);
printf("%s V-SPEEDUP [srtt:%f], new interval:%f, ppw:%d\n",
name(), srtt_, interval_, int(srtt_ / interval_));
rate_change();
expectedrate_ = srtt_ / (interval_ * minrtt_);
}
}
/******************** EXTENSIONS ****************************/
// ETFCC -- "Equation-based tfcc"
class ETFCCAgent : public TFCCAgent {
public:
ETFCCAgent() : cseq_save_(0) {
bind("efactor_", &efactor_);
bind("kfactor_", &kfactor_);
}
protected:
int cseq_save_; // saved copy of cseq
double efactor_; // multiplier of eqn
double kfactor_; // slop multiplier on eqn
void loss_event(int nlost);
double eqn_interval(double);
double eqn_droprate(double interv);
int echkint();
void nopeer();
};
static class ETFCCAgentClass : public TclClass {
public:
ETFCCAgentClass() : TclClass("Agent/RTP/TFCC/ETFCC") {}
TclObject* create(int, const char*const*) {
return (new ETFCCAgent());
}
} class_etfcc_agent;
/*
* the number of packets (interval) to check for events
*/
int
ETFCCAgent::echkint()
{
int x = int(kfactor_ / eqn_droprate(peer_interval_));
printf("%f %s: ECHKINT: %d peerint:%f, eqn_dr:%f\n", now(), name(), x,
peer_interval_, eqn_droprate(peer_interval_));
return (x);
}
void
ETFCCAgent::loss_event(int nlost)
{
// echkint: equation check interval
TFCCAgent::loss_event(nlost);
needresponse_ = 0;
if (nrcv_ > echkint()) {
int newcseq = cseq_ - cseq_save_;
if (newcseq > 0) {
// this is really a new ce
double cerate = double(newcseq) / nrcv_;
printf("%f %s ECHK: newcseq:%d, cerate: %f, eqn:%f, peerint:%f\n",
now(), name(), newcseq, cerate, eqn_interval(cerate), peer_interval_);
if (cerate > (kfactor_ * eqn_droprate(peer_interval_))) {
// see if the actual droprate is > than that
// we should see at this sender's rate
needresponse_ = 1;
}
nrcv_ = 0;
cseq_save_ = cseq_;
}
}
}
double
ETFCCAgent::eqn_interval(double droprate)
{
// this is the reciprocal of the eqn
double pps = efactor_ / (peer_rtt_est_ * sqrt(droprate));
if (pps > 0.0) {
printf("%f eqn_interval(drate:%f) returning %f\n", now(), droprate, 1.0 / pps);
return (1.0/pps);
}
return (10000.); // large interval
}
double
ETFCCAgent::eqn_droprate(double interval)
{
double pp = ((efactor_ * interval) / peer_rtt_est_);
return (pp * pp);
// pp *= pp;
// return (pp);
}
void
ETFCCAgent::nopeer()
{
// don't do anything here either
}
tfrc-sink.cc
/*
* Copyright (c) 1999 International Computer Science Institute
* 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 ACIRI, the AT&T
* Center for Internet Research at ICSI (the International Computer
* Science Institute).
* 4. Neither the name of ACIRI nor of ICSI may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY ICSI 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 ICSI 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.
*/
#include
#include
#include
#include
#include "tfrc-sink.h"
#include "formula-with-inverse.h"
static class TfrcSinkClass : public TclClass {
public:
TfrcSinkClass() : TclClass("Agent/TFRCSink") {}
TclObject* create(int, const char*const*) {
return (new TfrcSinkAgent());
}
} class_tfrcSink;
TfrcSinkAgent::TfrcSinkAgent() : Agent(PT_TFRC_ACK), nack_timer_(this)
{
bind("packetSize_", &size_);
bind("InitHistorySize_", &hsz);
bind("NumFeedback_", &NumFeedback_);
bind ("AdjustHistoryAfterSS_", &adjust_history_after_ss);
bind ("NumSamples_", &numsamples);
bind ("discount_", &discount);
bind ("printLoss_", &printLoss_);
bind ("smooth_", &smooth_);
rtt_ = 0;
tzero_ = 0;
last_timestamp_ = 0;
last_arrival_ = 0;
last_report_sent=0;
maxseq = -1;
rcvd_since_last_report = 0;
loss_seen_yet = 0;
lastloss = 0;
false_sample = 0;
lastloss_round_id = -1 ;
sample_count = 1 ;
last_sample = 0;
mult_factor_ = 1.0;
UrgentFlag = 0 ;
rtvec_ = NULL;
tsvec_ = NULL;
lossvec_ = NULL;
sample = NULL ;
weights = NULL ;
mult = NULL ;
}
/*
* Receive new data packet. If appropriate, generate a new report.
*/
void TfrcSinkAgent::recv(Packet *pkt, Handler *)
{
hdr_tfrc *tfrch = hdr_tfrc::access(pkt);
double now = Scheduler::instance().clock();
int prevmaxseq = maxseq;
double p = -1;
rcvd_since_last_report ++;
if (numsamples < 0) {
// This is the first packet received.
numsamples = DEFAULT_NUMSAMPLES ;
// forget about losses before this
prevmaxseq = maxseq = tfrch->seqno-1 ;
if (smooth_ == 1) {
numsamples = numsamples + 1;
}
sample = (int *)malloc((numsamples+1)*sizeof(int));
weights = (double *)malloc((numsamples+1)*sizeof(double));
mult = (double *)malloc((numsamples+1)*sizeof(double));
for (int i = 0 ; i < numsamples+1 ; i ++) {
sample[i] = 0 ;
}
if (smooth_ == 1) {
weights[0] = 1.0;
weights[1] = 1.0;
weights[2] = 1.0;
weights[3] = 1.0;
weights[4] = 1.0;
weights[5] = 0.8;
weights[6] = 0.6;
weights[7] = 0.4;
weights[8] = 0.2;
weights[9] = 0;
} else {
weights[0] = 1.0;
weights[1] = 1.0;
weights[2] = 1.0;
weights[3] = 1.0;
weights[4] = 0.8;
weights[5] = 0.6;
weights[6] = 0.4;
weights[7] = 0.2;
weights[8] = 0;
}
for (int i = 0; i < numsamples+1; i ++) {
mult[i] = 1.0 ;
}
}
UrgentFlag = tfrch->UrgentFlag;
round_id = tfrch->round_id ;
rtt_=tfrch->rtt;
tzero_=tfrch->tzero;
psize_=tfrch->psize;
last_arrival_=now;
last_timestamp_=tfrch->timestamp;
add_packet_to_history (pkt);
/*
* if we are in slow start (i.e. (loss_seen_yet ==0)),
* and if we saw a loss, report it immediately
*/
if ((UrgentFlag) || ((rtt_ > SMALLFLOAT) && (now - last_report_sent >= rtt_/(float)NumFeedback_)) ||
((loss_seen_yet ==0) && (tfrch->seqno-prevmaxseq > 1))) {
/*
* time to generate a new report
*/
if((loss_seen_yet ==0) && (tfrch->seqno-prevmaxseq> 1)) {
loss_seen_yet = 1;
if (adjust_history_after_ss) {
p = adjust_history(tfrch->timestamp);
}
}
UrgentFlag = 0 ;
nextpkt(p);
}
Packet::free(pkt);
}
void TfrcSinkAgent::add_packet_to_history (Packet *pkt)
{
hdr_tfrc *tfrch = hdr_tfrc::access(pkt);
double now = Scheduler::instance().clock();
register int i;
register int seqno = tfrch->seqno;
if (lossvec_ == NULL) {
// Initializing history.
rtvec_=(double *)malloc(sizeof(double)*hsz);
tsvec_=(double *)malloc(sizeof(double)*hsz);
lossvec_=(char *)malloc(sizeof(double)*hsz);
if (rtvec_ && lossvec_) {
for (i = 0; i < hsz ; i ++) {
lossvec_[i] = UNKNOWN;
rtvec_[i] = -1;
tsvec_[i] = -1;
}
for (i = 0; i <= maxseq ; i++) {
lossvec_[i] = NOLOSS ;
rtvec_[i] = now ;
tsvec_[i] = last_timestamp_ ;
}
}
else {
printf ("error allocating memory for packet buffers\n");
abort ();
}
}
if (tfrch->seqno - last_sample > hsz) {
printf ("time=%f, pkt=%d, last=%d history to small\n",
now, tfrch->seqno, last_sample);
abort();
}
/* for the time being, we will ignore out of order and duplicate
packets etc. */
if (seqno > maxseq) {
rtvec_[seqno%hsz]=now;
tsvec_[seqno%hsz]=last_timestamp_;
lossvec_[seqno%hsz] = RCVD;
i = maxseq+1 ;
if (i < seqno) {
double delta = (tsvec_[seqno%hsz]-tsvec_[maxseq%hsz])/(seqno-maxseq) ;
double tstamp = tsvec_[maxseq%hsz]+delta ;
//double delta = 0 ;
//double tstamp = last_timestamp_ ;
while(i < seqno) {
rtvec_[i%hsz]=now;
tsvec_[i%hsz]=tstamp;
if ((tsvec_[i%hsz]-lastloss > rtt_) &&
(round_id > lastloss_round_id)) {
// Lost packets are marked as "LOST"
// at most once per RTT.
lossvec_[i%hsz] = LOST;
UrgentFlag = 1 ;
lastloss = tstamp;
lastloss_round_id = round_id ;
}
else {
// This lost packet is marked "NOLOSS"
// because it does not begin a loss event.
lossvec_[i%hsz] = NOLOSS;
}
i++;
tstamp = tstamp+delta;
}
}
maxseq = seqno;
}
}
/*
* Estimate the loss rate. This function calculates two loss rates,
* and returns the smaller of the two.
*/
double TfrcSinkAgent::est_loss ()
{
int i;
double ave_interval1, ave_interval2;
int ds ;
// sample[i] counts the number of packets since the i-th loss event
// sample[0] contains the most recent sample.
for (i = last_sample; i <= maxseq ; i ++) {
sample[0]++;
if (lossvec_[i%hsz] == LOST) {
// new loss event
sample_count ++;
shift_array (sample, numsamples+1, 0);
multiply_array(mult, numsamples+1, mult_factor_);
shift_array (mult, numsamples+1, 1.0);
mult_factor_ = 1.0;
}
}
last_sample = maxseq+1 ;
if (sample_count>numsamples+1)
// The array of loss intervals is full.
ds=numsamples+1;
else
ds=sample_count;
if (sample_count == 1 && false_sample == 0)
// no losses yet
return 0;
if (sample_count <= numsamples+1 && false_sample > 0) {
// slow start just ended; ds should be 2
// the false sample is added to the array.
sample[ds-1] += false_sample;
false_sample = 0 ;
}
/* do we need to discount weights? */
if (sample_count > 1 && discount && sample[0] > 0) {
double ave = weighted_average(1, ds, 1.0, mult, weights, sample);
int factor = 2;
double ratio = (factor*ave)/sample[0];
double min_ratio = 0.5;
if ( ratio < 1.0) {
// the most recent loss interval is very large
mult_factor_ = ratio;
if (mult_factor_ < min_ratio)
mult_factor_ = min_ratio;
}
}
// Calculations including the most recent loss interval.
ave_interval1 = weighted_average(0, ds, mult_factor_, mult, weights, sample);
// The most recent loss interval does not end in a loss
// event. Include the most recent interval in the
// calculations only if this increases the estimated loss
// interval.
ave_interval2 = weighted_average(1, ds, mult_factor_, mult, weights, sample);
if (ave_interval2 > ave_interval1)
ave_interval1 = ave_interval2;
if (ave_interval1 > 0) {
if (printLoss_ > 0)
print_loss(sample[0], ave_interval1);
return 1/ave_interval1;
} else return 999;
}
void TfrcSinkAgent::print_loss(int sample, double ave_interval)
{
double now = Scheduler::instance().clock();
double drops = 1/ave_interval;
printf ("time: %7.5f current_loss_interval %5d\n", now, sample);
printf ("time: %7.5f loss_rate: %7.5f\n", now, drops);
}
// Calculate the weighted average.
double TfrcSinkAgent::weighted_average(int start, int end, double factor, double *m, double *w, int *sample)
{
int i;
double wsum = 0;
double answer = 0;
if (smooth_ == 1 && start == 0) {
if (end == numsamples+1) {
// the array is full, but we don't want to uses
// the last loss interval in the array
end = end-1;
}
// effectively shift the weight arrays
for (i = start ; i < end; i++)
if (i==0)
wsum += m[i]*w[i+1];
else
wsum += factor*m[i]*w[i+1];
for (i = start ; i < end; i++)
if (i==0)
answer += m[i]*w[i+1]*sample[i]/wsum;
else
answer += factor*m[i]*w[i+1]*sample[i]/wsum;
return answer;
} else {
for (i = start ; i < end; i++)
if (i==0)
wsum += m[i]*w[i];
else
wsum += factor*m[i]*w[i];
for (i = start ; i < end; i++)
if (i==0)
answer += m[i]*w[i]*sample[i]/wsum;
else
answer += factor*m[i]*w[i]*sample[i]/wsum;
return answer;
}
}
// Shift array a[] up, starting with a[sz-2] -> a[sz-1].
void TfrcSinkAgent::shift_array(int *a, int sz, int defval)
{
int i ;
for (i = sz-2 ; i >= 0 ; i--) {
a[i+1] = a[i] ;
}
a[0] = defval;
}
void TfrcSinkAgent::shift_array(double *a, int sz, double defval) {
int i ;
for (i = sz-2 ; i >= 0 ; i--) {
a[i+1] = a[i] ;
}
a[0] = defval;
}
// Multiply array by value, starting with array index 1.
// Array index 0 of the unshifted array contains the most recent interval.
void TfrcSinkAgent::multiply_array(double *a, int sz, double multiplier) {
int i ;
for (i = 1; i <= sz-1; i++) {
double old = a[i];
a[i] = old * multiplier ;
}
}
/*
* compute estimated throughput for report.
*/
double TfrcSinkAgent::est_thput ()
{
double time_for_rcv_rate;
double now = Scheduler::instance().clock();
double thput = 1 ;
if ((rtt_ > 0) && ((now - last_report_sent) >= rtt_)) {
// more than an RTT since the last report
time_for_rcv_rate = (now - last_report_sent);
if (time_for_rcv_rate > 0 && rcvd_since_last_report > 0) {
thput = rcvd_since_last_report/time_for_rcv_rate;
}
}
else {
// count number of packets received in the last RTT
if (rtt_ > 0){
double last = rtvec_[maxseq%hsz];
int rcvd = 0;
int i = maxseq;
while (i > 0) {
if (lossvec_[i%hsz] == RCVD) {
if ((rtvec_[i%hsz] + rtt_) > last)
rcvd++;
else
break ;
}
i--;
}
if (rcvd > 0)
thput = rcvd/rtt_;
}
}
return thput ;
}
/*
* We just received our first loss, and need to adjust our history.
*/
double TfrcSinkAgent::adjust_history (double ts)
{
int i;
double p;
for (i = maxseq; i >= 0 ; i --) {
if (lossvec_[i%hsz] == LOST) {
lossvec_[i%hsz] = NOLOSS;
}
}
lastloss = ts;
lastloss_round_id = round_id ;
p=b_to_p(est_thput()*psize_, rtt_, tzero_, psize_, 1);
false_sample = (int)(1/p);
return p;
}
/*
* Schedule sending this report, and set timer for the next one.
*/
void TfrcSinkAgent::nextpkt(double p) {
sendpkt(p);
/* schedule next report rtt/NumFeedback_ later */
if (rtt_ > 0.0 && NumFeedback_ > 0)
nack_timer_.resched(1.5*rtt_/(float)NumFeedback_);
}
/*
* Create report message, and send it.
*/
void TfrcSinkAgent::sendpkt(double p)
{
double now = Scheduler::instance().clock();
/*don't send an ACK unless we've received new data*/
/*if we're sending slower than one packet per RTT, don't need*/
/*multiple responses per data packet.*/
if (last_arrival_ >= last_report_sent) {
Packet* pkt = allocpkt();
if (pkt == NULL) {
printf ("error allocating packet\n");
abort();
}
hdr_tfrc_ack *tfrc_ackh = hdr_tfrc_ack::access(pkt);
tfrc_ackh->seqno=maxseq;
tfrc_ackh->timestamp_echo=last_timestamp_;
tfrc_ackh->timestamp_offset=now-last_arrival_;
tfrc_ackh->timestamp=now;
tfrc_ackh->NumFeedback_ = NumFeedback_;
if (p < 0)
tfrc_ackh->flost = est_loss ();
else
tfrc_ackh->flost = p;
tfrc_ackh->rate_since_last_report = est_thput ();
last_report_sent = now;
rcvd_since_last_report = 0;
send(pkt, 0);
}
}
int TfrcSinkAgent::command(int argc, const char*const* argv)
{
if (argc == 3) {
if (strcmp(argv[1], "weights") == 0) {
/*
* weights is a string of numbers, seperated by + signs
* the firs number is the total number of weights.
* the rest of them are the actual weights
* this overrides the defaults
*/
char *w ;
w = (char *)calloc(strlen(argv[2])+1, sizeof(char)) ;
if (w == NULL) {
printf ("error allocating w\n");
abort();
}
strcpy(w, (char *)argv[2]);
numsamples = atoi(strtok(w,"+"));
sample = (int *)malloc((numsamples+1)*sizeof(int));
weights = (double *)malloc((numsamples+1)*sizeof(double));
mult = (double *)malloc((numsamples+1)*sizeof(double));
fflush(stdout);
if (sample && weights) {
int count = 0 ;
while (count < numsamples) {
sample[count] = 0;
mult[count] = 1;
char *w;
w = strtok(NULL, "+");
if (w == NULL)
break ;
else {
weights[count++] = atof(w);
}
}
if (count < numsamples) {
printf ("error in weights string %s\n", argv[2]);
abort();
}
sample[count] = 0;
weights[count] = 0;
mult[count] = 1;
free(w);
return (TCL_OK);
}
else {
printf ("error allocating memory for smaple and weights:2\n");
abort();
}
}
}
return (Agent::command(argc, argv));
}
void TfrcNackTimer::expire(Event *) {
a_->nextpkt(-1);
}
tfrc.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1999 International Computer Science Institute
* 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 ACIRI, the AT&T
* Center for Internet Research at ICSI (the International Computer
* Science Institute).
* 4. Neither the name of ACIRI nor of ICSI may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY ICSI 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 ICSI 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.
*/
#include
#include
#include
#include "tfrc.h"
#include "formula.h"
int hdr_tfrc::offset_;
int hdr_tfrc_ack::offset_;
static class TFRCHeaderClass : public PacketHeaderClass {
public:
TFRCHeaderClass() : PacketHeaderClass("PacketHeader/TFRC",
sizeof(hdr_tfrc)) {
bind_offset(&hdr_tfrc::offset_);
}
} class_tfrchdr;
static class TFRC_ACKHeaderClass : public PacketHeaderClass {
public:
TFRC_ACKHeaderClass() : PacketHeaderClass("PacketHeader/TFRC_ACK",
sizeof(hdr_tfrc_ack)) {
bind_offset(&hdr_tfrc_ack::offset_);
}
} class_tfrc_ackhdr;
static class TfrcClass : public TclClass {
public:
TfrcClass() : TclClass("Agent/TFRC") {}
TclObject* create(int, const char*const*) {
return (new TfrcAgent());
}
} class_tfrc;
TfrcAgent::TfrcAgent() : Agent(PT_TFRC), send_timer_(this),
NoFeedbacktimer_(this), rate_(0), oldrate_(0), maxrate_(0)
{
bind("packetSize_", &size_);
bind("df_", &df_);
bind("tcp_tick_", &tcp_tick_);
bind("ndatapack_", &ndatapack_);
bind("srtt_init_", &srtt_init_);
bind("rttvar_init_", &rttvar_init_);
bind("rtxcur_init_", &rtxcur_init_);
bind("rttvar_exp_", &rttvar_exp_);
bind("T_SRTT_BITS", &T_SRTT_BITS);
bind("T_RTTVAR_BITS", &T_RTTVAR_BITS);
bind("InitRate_", &InitRate_);
bind("overhead_", &overhead_);
bind("ssmult_", &ssmult_);
bind("bval_", &bval_);
bind("ca_", &ca_);
bind("printStatus_", &printStatus_);
}
int TfrcAgent::command(int argc, const char*const* argv)
{
if (argc==2) {
if (strcmp(argv[1],"start")==0) {
start();
return TCL_OK;
}
if (strcmp(argv[1],"stop")==0) {
stop();
return TCL_OK;
}
}
return (Agent::command(argc, argv));
}
void TfrcAgent::start()
{
seqno_=0;
rate_ = InitRate_;
delta_ = 0;
oldrate_ = rate_;
rate_change_ = SLOW_START;
UrgentFlag = 1;
rtt_=0;
sqrtrtt_=1;
rttcur_=1;
tzero_ = 0;
last_change_=0;
maxrate_ = 0;
ndatapack_=0;
active_ = 1;
round_id = 0;
t_srtt_ = int(srtt_init_/tcp_tick_) << T_SRTT_BITS;
t_rttvar_ = int(rttvar_init_/tcp_tick_) << T_RTTVAR_BITS;
t_rtxcur_ = rtxcur_init_;
rcvrate = 0 ;
first_pkt_rcvd = 0 ;
// send the first packet
sendpkt();
// ... at initial rate
send_timer_.resched(size_/rate_);
// ... and start timer so we can cut rate
// in half if we do not get feedback
NoFeedbacktimer_.resched(2*size_/rate_);
}
void TfrcAgent::stop()
{
active_ = 0;
send_timer_.force_cancel();
}
void TfrcAgent::nextpkt()
{
double next = -1;
double xrate = -1;
sendpkt();
// during slow start and congestion avoidance, we increase rate
// slowly - by amount delta per packet
if ((rate_change_ == SLOW_START) && (oldrate_+SMALLFLOAT< rate_)) {
oldrate_ = oldrate_ + delta_;
xrate = oldrate_;
}
else {
if (ca_)
xrate = rate_ * sqrtrtt_/sqrt(rttcur_);
else
xrate = rate_;
}
if (xrate > SMALLFLOAT) {
next = size_/xrate;
//
// randomize between next*(1 +/- woverhead_)
//
next = next*(2*overhead_*Random::uniform()-overhead_+1);
if (next > SMALLFLOAT)
send_timer_.resched(next);
}
}
void TfrcAgent::update_rtt (double tao, double now)
{
/* the TCP update */
t_rtt_ = int((now-tao) /tcp_tick_ + 0.5);
if (t_rtt_==0) t_rtt_=1;
if (t_srtt_ != 0) {
register short delta;
delta = t_rtt_ - (t_srtt_ >> T_SRTT_BITS);
if ((t_srtt_ += delta) <= 0)
t_srtt_ = 1;
if (delta < 0)
delta = -delta;
delta -= (t_rttvar_ >> T_RTTVAR_BITS);
if ((t_rttvar_ += delta) <= 0)
t_rttvar_ = 1;
} else {
t_srtt_ = t_rtt_ << T_SRTT_BITS;
t_rttvar_ = t_rtt_ << (T_RTTVAR_BITS-1);
}
t_rtxcur_ = (((t_rttvar_ << (rttvar_exp_ + (T_SRTT_BITS - T_RTTVAR_BITS))) + t_srtt_) >> T_SRTT_BITS ) * tcp_tick_;
tzero_=t_rtxcur_;
/* fine grained RTT estimate for use in the equation */
if (rtt_ > 0) {
rtt_ = df_*rtt_ + ((1-df_)*(now - tao));
sqrtrtt_ = df_*sqrtrtt_ + ((1-df_)*sqrt(now - tao));
} else {
rtt_ = now - tao;
sqrtrtt_ = sqrt(now - tao);
}
rttcur_ = now - tao;
}
/*
* Receive a status report from the receiver.
*/
void TfrcAgent::recv(Packet *pkt, Handler *)
{
double now = Scheduler::instance().clock();
hdr_tfrc_ack *nck = hdr_tfrc_ack::access(pkt);
double ts = nck->timestamp_echo;
double rate_since_last_report = nck->rate_since_last_report;
double NumFeedback_ = nck->NumFeedback_;
double flost = nck->flost;
round_id ++ ;
UrgentFlag = 0;
/* compute the max rate as two times rcv rate */
if (rate_since_last_report > 0)
maxrate_ = 2*rate_since_last_report*size_;
else
maxrate_ = 0;
/* update the round trip time */
update_rtt (ts, now);
/* .. and estimate of fair rate */
rcvrate = p_to_b(flost, rtt_, tzero_, size_, bval_);
/* if we get no more feedback for some time, cut rate in half */
double t = 2*rtt_ ;
if (t < 2*size_/rate_)
t = 2*size_/rate_ ;
NoFeedbacktimer_.resched(t);
/* if we are in slow start and we just saw a loss */
/* then come out of slow start */
if (first_pkt_rcvd == 0) {
first_pkt_rcvd = 1 ;
slowstart ();
nextpkt();
}
else {
if (rate_change_ == SLOW_START) {
if (flost > 0) {
rate_change_ = OUT_OF_SLOW_START;
oldrate_ = rate_ = rcvrate;
}
else {
slowstart ();
nextpkt();
}
}
else {
if (rcvrate>rate_)
increase_rate(flost);
else
decrease_rate ();
}
}
if (printStatus_) {
printf("time: %5.2f rate: %5.2f\n", now, rate_);
double packetrate = rate_ * rtt_ / size_;
printf("time: %5.2f packetrate: %5.2f\n", now, packetrate);
}
Packet::free(pkt);
}
void TfrcAgent::slowstart ()
{
double now = Scheduler::instance().clock();
if (rate_+SMALLFLOAT< size_/rtt_ ) {
/* if this is the first report, change rate to 1 per rtt */
/* compute delta so rate increases slowly to new value */
oldrate_ = rate_;
rate_ = size_/rtt_;
delta_ = (rate_ - oldrate_)/(rate_*rtt_/size_);
last_change_ = now;
} else {
/* else multiply the rate by ssmult_, and compute delta, */
/* so that the rate increases slowly to new value */
if (maxrate_ > 0) {
if (ssmult_*rate_ < maxrate_ && now - last_change_ > rtt_) {
rate_ = ssmult_*rate_;
delta_ = (rate_ - oldrate_)/(rate_*rtt_/size_);
last_change_=now;
} else {
if ( (oldrate_ > maxrate_) || (rate_ > maxrate_)) {
if (oldrate_ > maxrate_) {
delta_ = 0;
rate_ = oldrate_ = 0.5*maxrate_;
last_change_ = now;
} else {
rate_ = maxrate_;
delta_ = (rate_ - oldrate_)/(rate_*rtt_/size_);
last_change_ = now;
}
} else {
if (now - last_change_ > rtt_) {
rate_ = maxrate_;
delta_ = (rate_ - oldrate_)/(rate_*rtt_/size_);
last_change_=now;
}
}
}
} else {
rate_ = ssmult_*rate_;
delta_ = (rate_ - oldrate_)/(rate_*rtt_/size_);
last_change_=now;
}
}
}
void TfrcAgent::increase_rate (double p)
{
double now = Scheduler::instance().clock();
double mult = (now-last_change_)/rtt_ ;
if (mult > 2) mult = 2 ;
rate_ = rate_ + (size_/rtt_)*mult ;
double maximumrate = (maxrate_>size_/rtt_)?maxrate_:size_/rtt_ ;
maximumrate = (maximumrate>rcvrate)?rcvrate:maximumrate;
rate_ = (rate_ > maximumrate)?maximumrate:rate_ ;
rate_change_ = CONG_AVOID;
last_change_ = now;
}
void TfrcAgent::decrease_rate ()
{
double now = Scheduler::instance().clock();
rate_ = rcvrate;
oldrate_ = rate_;
rate_change_ = RATE_DECREASE;
last_change_ = now;
}
void TfrcAgent::sendpkt()
{
if (active_) {
Packet* p = allocpkt();
hdr_tfrc *tfrch = hdr_tfrc::access(p);
tfrch->seqno=seqno_++;
tfrch->timestamp=Scheduler::instance().clock();
tfrch->rtt=rtt_;
tfrch->tzero=tzero_;
tfrch->rate=oldrate_;
tfrch->psize=size_;
tfrch->UrgentFlag=UrgentFlag;
tfrch->round_id=round_id;
ndatapack_++;
send(p, 0);
}
}
void TfrcAgent::reduce_rate_on_no_feedback()
{
rate_change_ = RATE_DECREASE;
rate_*=0.5;
UrgentFlag = 1;
round_id ++ ;
double t = 2*rtt_ ;
if (t < 2*size_/rate_)
t = 2*size_/rate_ ;
NoFeedbacktimer_.resched(t);
nextpkt();
}
void TfrcSendTimer::expire(Event *) {
a_->nextpkt();
}
void TfrcNoFeedbackTimer::expire(Event *) {
a_->reduce_rate_on_no_feedback ();
}
timer-handler.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* timer-handler.cc
* Copyright (C) 1997 by USC/ISI
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation, advertising
* materials, and other materials related to such distribution and use
* acknowledge that the software was developed by the University of
* Southern California, Information Sciences Institute. The name of the
* University may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (USC/ISI)";
#endif
#include // abort()
#include "timer-handler.h"
void
TimerHandler::cancel()
{
if (status_ != TIMER_PENDING) {
fprintf(stderr,
"Attempting to cancel timer at %p which is not scheduled\n",
this);
abort();
}
_cancel();
status_ = TIMER_IDLE;
}
/* sched checks the state of the timer before shceduling the
* event. It the timer is already set, abort is called.
* This is different than the OTcl timers in tcl/ex/timer.tcl,
* where sched is the same as reshced, and no timer state is kept.
*/
void
TimerHandler::sched(double delay)
{
if (status_ != TIMER_IDLE) {
fprintf(stderr,"Couldn't schedule timer");
abort();
}
_sched(delay);
status_ = TIMER_PENDING;
}
void
TimerHandler::resched(double delay)
{
if (status_ == TIMER_PENDING)
_cancel();
_sched(delay);
status_ = TIMER_PENDING;
}
void
TimerHandler::handle(Event *e)
{
if (status_ != TIMER_PENDING) // sanity check
abort();
status_ = TIMER_HANDLING;
expire(e);
// if it wasn't rescheduled, it's done
if (status_ == TIMER_HANDLING)
status_ = TIMER_IDLE;
}
timewindow-est.cc
/*
* Copyright (c) Xerox Corporation 1997. All rights reserved.
*
* License is granted to copy, to use, and to make and to use derivative
* works for research and evaluation purposes, provided that Xerox is
* acknowledged in all documentation pertaining to any such copy or
* derivative work. Xerox grants no other licenses expressed or
* implied. The Xerox trade name should not be used in any advertising
* without its written permission.
*
* XEROX CORPORATION MAKES NO REPRESENTATIONS CONCERNING EITHER THE
* MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE
* FOR ANY PARTICULAR PURPOSE. The software is provided "as is" without
* express or implied warranty of any kind.
*
* These notices must be retained in any copies of any part of this
* software.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$";
#endif
//Time Window estimation
#include "estimator.h"
#include
class TimeWindow_Est : public Estimator {
public:
TimeWindow_Est() :scnt(1),maxp(0){bind("T_",&T_);};
inline void change_avload(double incr) { avload_ += incr; if (incr >0) scnt=0;}
protected:
void estimate();
int scnt;
double maxp;//maximum of previous interval
int T_;
};
void TimeWindow_Est::estimate() {
measload_ = meas_mod_->bitcnt()/period_;
if (meas_mod_->bitcnt()/period_ >avload_)
avload_=meas_mod_->bitcnt()/period_;
if (maxp < meas_mod_->bitcnt()/period_)
maxp=meas_mod_->bitcnt()/period_;
if (scnt == T_)
{
scnt-=T_;
avload_=maxp;
maxp=0;
}
//printf("%f %f %f\n",Scheduler::instance().clock(),avload_,meas_mod_->bitcnt()/period_);
fflush(stdout);
meas_mod_->resetbitcnt();
scnt++;
}
static class TimeWindow_EstClass : public TclClass {
public:
TimeWindow_EstClass() : TclClass ("Est/TimeWindow") {}
TclObject* create(int,const char*const*) {
return (new TimeWindow_Est());
}
}class_timewindow_est;
tkAppInit.cc
/*
* tkAppInit.c --
*
* Provides a default version of the Tcl_AppInit procedure for
* use in wish and similar Tk-based applications.
*
* Copyright (c) 1993 The Regents of the University of California.
* Copyright (c) 1994 Sun Microsystems, Inc.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
* SCCS: @(#) tkAppInit.c 1.21 96/03/26 16:47:07
*/
#include
#include "config.h"
extern init_misc();
extern "C" {
#ifdef TK_TEST
EXTERN int Tktest_Init _ANSI_ARGS_((Tcl_Interp *interp));
#endif /* TK_TEST */
/*
*----------------------------------------------------------------------
*
* main --
*
* This is the main program for the application.
*
* Results:
* None: Tk_Main never returns here, so this procedure never
* returns either.
*
* Side effects:
* Whatever the application does.
*
*----------------------------------------------------------------------
*/
int
main(int argc, char** argv)
{
Tk_Main(argc, argv, Tcl_AppInit);
return 0; /* Needed only to prevent compiler warning. */
}
#include "bitmap/play.xbm"
#include "bitmap/stop.xbm"
#include "bitmap/rewind.xbm"
#include "bitmap/ff.xbm"
void loadbitmaps(Tcl_Interp* tcl)
{
Tk_DefineBitmap(tcl, Tk_GetUid("play"),
play_bits, play_width, play_height);
Tk_DefineBitmap(tcl, Tk_GetUid("stop"),
stop_bits, stop_width, stop_height);
Tk_DefineBitmap(tcl, Tk_GetUid("rewind"),
rewind_bits, rewind_width, rewind_height);
Tk_DefineBitmap(tcl, Tk_GetUid("ff"),
ff_bits, ff_width, ff_height);
}
/*
*----------------------------------------------------------------------
*
* Tcl_AppInit --
*
* This procedure performs application-specific initialization.
* Most applications, especially those that incorporate additional
* packages, will have their own version of this procedure.
*
* Results:
* Returns a standard Tcl completion code, and leaves an error
* message in interp->result if an error occurs.
*
* Side effects:
* Depends on the startup script.
*
*----------------------------------------------------------------------
*/
int
Tcl_AppInit(Tcl_Interp *interp)
{
if (Tcl_Init(interp) == TCL_ERROR) {
return TCL_ERROR;
}
if (Otcl_Init(interp) == TCL_ERROR) {
return TCL_ERROR;
}
if (Tk_Init(interp) == TCL_ERROR) {
return TCL_ERROR;
}
Tcl_StaticPackage(interp, "Tk", Tk_Init, (Tcl_PackageInitProc *) NULL);
#ifdef TK_TEST
if (Tktest_Init(interp) == TCL_ERROR) {
return TCL_ERROR;
}
Tcl_StaticPackage(interp, "Tktest", Tktest_Init,
(Tcl_PackageInitProc *) NULL);
#endif /* TK_TEST */
/*
* Call the init procedures for included packages. Each call should
* look like this:
*
* if (Mod_Init(interp) == TCL_ERROR) {
* return TCL_ERROR;
* }
*
* where "Mod" is the name of the module.
*/
#ifdef HAVE_LIBTCLDBG
if (Dbg_Init(interp) == TCL_ERROR) {
return TCL_ERROR;
}
#endif
Tcl::init(interp, "ns");
Tcl::instance().tkmain(Tk_MainWindow(interp));
extern EmbeddedTcl et_ns_lib, et_tk;
et_tk.load();
et_ns_lib.load();
init_misc();
loadbitmaps(interp);
/*
* Call Tcl_CreateCommand for application-specific commands, if
* they weren't already created by the init procedures called above.
*/
/*
* Specify a user-specific startup file to invoke if the application
* is run interactively. Typically the startup file is "~/.apprc"
* where "app" is the name of the application. If this line is deleted
* then no user-specific startup file will be run under any conditions.
*/
Tcl_SetVar(interp, "tcl_rcFileName", "~/.ns.tcl", TCL_GLOBAL_ONLY);
return TCL_OK;
}
abort()
{
Tcl& tcl = Tcl::instance();
tcl.evalc("[Simulator instance] flush-trace");
#ifdef abort
#undef abort
abort();
#else
exit(1);
#endif /*abort*/
/*NOTREACHED*/
}
}
topography.cc
/* -*- 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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*/
/* Ported from CMU/Monarch's code, nov'98 -Padma.*/
#include
#include
#include "object.h"
#include "dem.h"
#include "topography.h"
static class TopographyClass : public TclClass {
public:
TopographyClass() : TclClass("Topography") {}
TclObject* create(int, const char*const*) {
return (new Topography);
}
} class_topography;
double
Topography::height(double x, double y) {
if(grid == 0)
return 0.0;
#ifndef WIN32
int a = (int) rint(x/grid_resolution);
int b = (int) rint(y/grid_resolution);
int c = (int) rint(maxY);
#else
int a = (int) ceil(x/grid_resolution+0.5);
int b = (int) ceil(y/grid_resolution+0.5);
int c = (int) ceil(maxY+0.5);
#endif /* !WIN32 */
return (double) grid[a * c + b];
}
int
Topography::load_flatgrid(int x, int y, int res)
{
/* No Reason to malloc a grid */
grid_resolution = res; // default is 1 meter
maxX = (double) x;
maxY = (double) y;
return 0;
}
int
Topography::load_demfile(const char *fname)
{
DEMFile *dem;
fprintf(stderr, "Opening DEM file %s...\n", fname);
dem = new DEMFile((char*) fname);
if(dem == 0)
return 1;
fprintf(stderr, "Processing DEM file...\n");
grid = dem->process();
if(grid == 0) {
delete dem;
return 1;
}
dem->dump_ARecord();
double tx, ty; tx = maxX; ty = maxY;
dem->range(tx, ty); // Get the grid size
maxX = tx; maxY = ty;
dem->resolution(grid_resolution); // Get the resolution of each entry
/* Close the DEM file */
delete dem;
/*
* Sanity Check
*/
if(maxX <= 0.0 || maxY <= 0.0 || grid_resolution <= 0.0)
return 1;
fprintf(stderr, "DEM File processing complete...\n");
return 0;
}
int
Topography::command(int argc, const char*const* argv)
{
if(argc == 3) {
if(strcmp(argv[1], "load_demfile") == 0) {
if(load_demfile(argv[2]))
return TCL_ERROR;
return TCL_OK;
}
}
else if(argc == 4) {
if(strcmp(argv[1], "load_flatgrid") == 0) {
if(load_flatgrid(atoi(argv[2]), atoi(argv[3])))
return TCL_ERROR;
return TCL_OK;
}
}
else if(argc == 5) {
if(strcmp(argv[1], "load_flatgrid") == 0) {
if(load_flatgrid(atoi(argv[2]), atoi(argv[3]), atoi(argv[4])))
return TCL_ERROR;
return TCL_OK;
}
}
return TclObject::command(argc, argv);
}
trace-ip.cc
/* -*- 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 Giao Nguyen, http://daedalus.cs.berkeley.edu/~gnguyen
*/
#include "ip.h"
#include "trace.h"
#include "mac.h"
#include "packet.h"
class TraceIp : public Trace {
public:
TraceIp(int type) : Trace(type) {
bind("mask_", &mask_);
bind("shift_", &shift_);
}
void recv(Packet*, Handler*);
protected:
int mask_;
int shift_;
};
class TraceIpMac : public TraceIp {
public:
TraceIpMac(int type) : TraceIp(type) {}
void recv(Packet*, Handler*);
};
class TraceIpClass : public TclClass {
public:
TraceIpClass() : TclClass("TraceIp") { }
TclObject* create(int args, const char*const* argv) {
if (args >= 5)
return (new TraceIp(*argv[4]));
else
return NULL;
}
} traceip_class;
class TraceIpMacClass : public TclClass {
public:
TraceIpMacClass() : TclClass("TraceIp/Mac") { }
TclObject* create(int args, const char*const* argv) {
if (args >= 5)
return (new TraceIpMac(*argv[4]));
else
return NULL;
}
} trace_ip_mac_class;
void TraceIp::recv(Packet* p, Handler* h)
{
// XXX: convert IP address to node number
hdr_ip *iph = hdr_ip::access(p);
int src = (src_ >= 0) ? src_ : (iph->saddr() >> shift_) & mask_;
int dst = (iph->daddr() >> shift_) & mask_;
format(type_, src, dst , p);
dump();
target_ ? send(p, h) : Packet::free(p);
}
void TraceIpMac::recv(Packet* p, Handler* h)
{
// XXX: convert IP address to node number
// hdr_ip *iph = hdr_ip::access(p);
hdr_ip *iph = HDR_IP(p);
int src = (src_ >= 0) ? src_ : (iph->saddr() >> shift_) & mask_;
int dst = (iph->daddr() >> shift_) & mask_;
hdr_mac* mh = HDR_MAC(p);
if (mh->ftype() == MF_ACK || mh->ftype() == MF_CTS)
format(type_, dst, src , p);
else
format(type_, src, dst , p);
dump();
target_ ? send(p, h) : Packet::free(p);
}
trace.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8 -*- */
/*
* Copyright (c) 1990-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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*
* @(#) $Header$ (LBL)
*/
#include
#include
#include "packet.h"
#include "ip.h"
#include "tcp.h"
#include "rtp.h"
#include "srm.h"
#include "tfrc.h"
#include "flags.h"
#include "address.h"
#include "trace.h"
#include "rap/rap.h"
//const double Trace::PRECISION = 1.0e+6;
class TraceClass : public TclClass {
public:
TraceClass() : TclClass("Trace") { }
TclObject* create(int argc, const char*const* argv) {
if (argc >= 5)
return (new Trace(*argv[4]));
return 0;
}
} trace_class;
Trace::Trace(int type)
: Connector(), type_(type), channel_(0), callback_(0)
#ifdef NAM_TRACE
, namChan_(0)
#endif
{
bind("src_", (int*)&src_);
bind("dst_", (int*)&dst_);
bind("callback_", &callback_);
bind("show_tcphdr_", &show_tcphdr_);
#ifdef OFF_HDR
bind("off_ip_", &off_ip_);
bind("off_tcp_", &off_tcp_);
bind("off_rtp_", &off_rtp_);
bind("off_srm_", &off_srm_);
#endif
}
Trace::~Trace()
{
}
/*
* $trace detach
* $trace flush
* $trace attach $fileID
*/
int Trace::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
if (argc == 2) {
if (strcmp(argv[1], "detach") == 0) {
channel_ = 0;
#ifdef NAM_TRACE
namChan_ = 0;
#endif
return (TCL_OK);
}
if (strcmp(argv[1], "flush") == 0) {
#ifdef NAM_TRACE
if (channel_ != 0)
Tcl_Flush(channel_);
if (namChan_ != 0)
Tcl_Flush(namChan_);
#else
Tcl_Flush(channel_);
#endif
return (TCL_OK);
}
} else if (argc == 3) {
if (strcmp(argv[1], "annotate") == 0) {
if (channel_ != 0)
annotate(argv[2]);
return (TCL_OK);
}
if (strcmp(argv[1], "attach") == 0) {
int mode;
const char* id = argv[2];
channel_ = Tcl_GetChannel(tcl.interp(), (char*)id,
&mode);
if (channel_ == 0) {
tcl.resultf("trace: can't attach %s for writing", id);
return (TCL_ERROR);
}
return (TCL_OK);
}
#ifdef NAM_TRACE
if (strcmp(argv[1], "namattach") == 0) {
int mode;
const char* id = argv[2];
namChan_ = Tcl_GetChannel(tcl.interp(), (char*)id,
&mode);
if (namChan_ == 0) {
tcl.resultf("trace: can't attach %s for writing", id);
return (TCL_ERROR);
}
return (TCL_OK);
}
if (strcmp(argv[1], "ntrace") == 0) {
if (namChan_ != 0)
write_nam_trace(argv[2]);
return (TCL_OK);
}
#endif
}
return (Connector::command(argc, argv));
}
#ifdef NAM_TRACE
void Trace::write_nam_trace(const char *s)
{
sprintf(nwrk_, "%s", s);
namdump();
}
#endif
void Trace::annotate(const char* s)
{
sprintf(wrk_, "v "TIME_FORMAT" eval {set sim_annotation {%s}}",
round(Scheduler::instance().clock()), s);
dump();
sprintf(nwrk_, "v -t %.17g sim_annotation %g %s",
Scheduler::instance().clock(),
Scheduler::instance().clock(), s);
namdump();
}
// char* pt_names[] = {
// PT_NAMES
// };
char* srm_names[] = {
SRM_NAMES
};
// this function should retain some backward-compatibility, so that
// scripts don't break.
void Trace::format(int tt, int s, int d, Packet* p)
{
#ifdef OFF_HDR
hdr_cmn *th = (hdr_cmn*)p->access(off_cmn_);
hdr_ip *iph = (hdr_ip*)p->access(off_ip_);
hdr_tcp *tcph = (hdr_tcp*)p->access(off_tcp_);
hdr_rtp *rh = (hdr_rtp*)p->access(off_rtp_);
hdr_srm *sh = (hdr_srm*)p->access(off_srm_);
hdr_rap *raph = hdr_rap::access(p);
#if 0
hdr_tfrc *tfrch = (hdr_tfrc*)p->access(off_tfrc_);
#else /* ! 0 */
hdr_tfrc *tfrch = hdr_tfrc::access(p);
#endif /* 0 */
#else
hdr_cmn *th = hdr_cmn::access(p);
hdr_ip *iph = hdr_ip::access(p);
hdr_tcp *tcph = hdr_tcp::access(p);
hdr_rtp *rh = hdr_rtp::access(p);
hdr_srm *sh = hdr_srm::access(p);
hdr_rap *raph = hdr_rap::access(p);
hdr_tfrc *tfrch = hdr_tfrc::access(p);
#endif
const char* sname = "null";
packet_t t = th->ptype();
const char* name = packet_info.name(t);
/* SRM-specific */
if (strcmp(name,"SRM") == 0 || strcmp(name,"cbr") == 0 || strcmp(name,"udp") == 0) {
if ( sh->type() < 5 && sh->type() > 0 ) {
sname = srm_names[sh->type()];
}
}
if (name == 0)
abort();
int seqno;
/* XXX */
/* UDP's now have seqno's too */
if (t == PT_RTP || t == PT_CBR || t == PT_UDP || t == PT_EXP ||
t == PT_PARETO)
seqno = rh->seqno();
else if (t == PT_RAP_DATA || t == PT_RAP_ACK)
seqno = raph->seqno();
else if (t == PT_TCP || t == PT_ACK || t == PT_HTTP || t == PT_FTP ||
t == PT_TELNET)
seqno = tcph->seqno();
else if (t == PT_TFRC)
seqno = tfrch->seqno;
else
seqno = -1;
/*
* When new flags are added, make sure to change NUMFLAGS
* in trace.h
*/
char flags[NUMFLAGS+1];
for (int i = 0; i < NUMFLAGS; i++)
flags[i] = '-';
flags[NUMFLAGS] = 0;
#ifdef OFF_HDR
hdr_flags* hf = (hdr_flags*)p->access(off_flags_);
#else
hdr_flags* hf = hdr_flags::access(p);
#endif
flags[0] = hf->ecn_ ? 'C' : '-'; // Ecn Echo
flags[1] = hf->pri_ ? 'P' : '-';
flags[2] = '-';
flags[3] = hf->cong_action_ ? 'A' : '-'; // Congestion Action
flags[4] = hf->ecn_to_echo_ ? 'E' : '-'; // Congestion Experienced
flags[5] = hf->fs_ ? 'F' : '-'; // Fast start: see tcp-fs and tcp-int
flags[6] = hf->ecn_capable_ ? 'N' : '-';
#ifdef notdef
flags[1] = (iph->flags() & PF_PRI) ? 'P' : '-';
flags[2] = (iph->flags() & PF_USR1) ? '1' : '-';
flags[3] = (iph->flags() & PF_USR2) ? '2' : '-';
flags[5] = 0;
#endif
char *src_nodeaddr = Address::instance().print_nodeaddr(iph->saddr());
char *src_portaddr = Address::instance().print_portaddr(iph->sport());
char *dst_nodeaddr = Address::instance().print_nodeaddr(iph->daddr());
char *dst_portaddr = Address::instance().print_portaddr(iph->dport());
if (!show_tcphdr_) {
sprintf(wrk_, "%c "TIME_FORMAT" %d %d %s %d %s %d %s.%s %s.%s %d %d",
tt,
round(Scheduler::instance().clock()),
s,
d,
name,
th->size(),
flags,
iph->flowid() /* was p->class_ */,
// iph->src() >> (Address::instance().NodeShift_[1]),
// iph->src() & (Address::instance().PortMask_),
// iph->dst() >> (Address::instance().NodeShift_[1]),
// iph->dst() & (Address::instance().PortMask_),
src_nodeaddr,
src_portaddr,
dst_nodeaddr,
dst_portaddr,
seqno,
th->uid() /* was p->uid_ */);
} else {
sprintf(wrk_,
"%c "TIME_FORMAT" %d %d %s %d %s %d %s.%s %s.%s %d %d %d 0x%x %d %d",
tt,
round(Scheduler::instance().clock()),
s,
d,
name,
th->size(),
flags,
iph->flowid(), /* was p->class_ */
// iph->src() >> (Address::instance().NodeShift_[1]),
// iph->src() & (Address::instance().PortMask_),
// iph->dst() >> (Address::instance().NodeShift_[1]),
// iph->dst() & (Address::instance().PortMask_),
src_nodeaddr,
src_portaddr,
dst_nodeaddr,
dst_portaddr,
seqno,
th->uid(), /* was p->uid_ */
tcph->ackno(),
tcph->flags(),
tcph->hlen(),
tcph->sa_length());
}
#ifdef NAM_TRACE
if (namChan_ != 0)
sprintf(nwrk_,
"%c -t "TIME_FORMAT" -s %d -d %d -p %s -e %d -c %d -i %d -a %d -x {%s.%s %s.%s %d %s %s}",
tt,
Scheduler::instance().clock(),
s,
d,
name,
th->size(),
iph->flowid(),
th->uid(),
iph->flowid(),
src_nodeaddr,
src_portaddr,
dst_nodeaddr,
dst_portaddr,
seqno,flags,sname);
#endif
delete [] src_nodeaddr;
delete [] src_portaddr;
delete [] dst_nodeaddr;
delete [] dst_portaddr;
}
void Trace::dump()
{
int n = strlen(wrk_);
if ((n > 0) && (channel_ != 0)) {
/*
* tack on a newline (temporarily) instead
* of doing two writes
*/
wrk_[n] = '\n';
wrk_[n + 1] = 0;
(void)Tcl_Write(channel_, wrk_, n + 1);
wrk_[n] = 0;
}
if (callback_) {
Tcl& tcl = Tcl::instance();
tcl.evalf("%s handle { %s }", name(), wrk_);
}
}
#ifdef NAM_TRACE
void Trace::namdump()
{
int n = 0;
/* Otherwise nwrk_ isn't initialized */
if (namChan_ != 0)
n = strlen(nwrk_);
if ((n > 0) && (namChan_ != 0)) {
/*
* tack on a newline (temporarily) instead
* of doing two writes
*/
nwrk_[n] = '\n';
nwrk_[n + 1] = 0;
(void)Tcl_Write(namChan_, nwrk_, n + 1);
nwrk_[n] = 0;
}
}
#endif
void Trace::recv(Packet* p, Handler* h)
{
format(type_, src_, dst_, p);
dump();
namdump();
/* hack: if trace object not attached to anything free packet */
if (target_ == 0)
Packet::free(p);
else
send(p, h);
}
void Trace::trace(TracedVar* var)
{
char tmp[256] = "";
Scheduler& s = Scheduler::instance();
if (&s == 0)
return;
// format: use Mark's nam feature code without the '-' prefix
sprintf(wrk_, "%c t"TIME_FORMAT" a%s n%s v%s",
type_,
round(s.clock()),
var->owner()->name(),
var->name(),
var->value(tmp, 256));
dump();
}
//
// we need a DequeTraceClass here because a 'h' event need to go together
// with the '-' event. It's possible to use a postprocessing script, but
// seems that's inconvient.
//
static class DequeTraceClass : public TclClass {
public:
DequeTraceClass() : TclClass("Trace/Deque") { }
TclObject* create(int args, const char*const* argv) {
if (args >= 5)
return (new DequeTrace(*argv[4]));
return NULL;
}
} dequetrace_class;
DequeTrace::~DequeTrace()
{
}
void
DequeTrace::recv(Packet* p, Handler* h)
{
// write the '-' event first
format(type_, src_, dst_, p);
dump();
namdump();
#ifdef NAM_TRACE
if (namChan_ != 0) {
#ifdef OFF_HDR
hdr_cmn *th = (hdr_cmn*)p->access(off_cmn_);
hdr_ip *iph = (hdr_ip*)p->access(off_ip_);
hdr_srm *sh = (hdr_srm*)p->access(off_srm_);
#else
hdr_cmn *th = hdr_cmn::access(p);
hdr_ip *iph = hdr_ip::access(p);
hdr_srm *sh = hdr_srm::access(p);
#endif
const char* sname = "null";
packet_t t = th->ptype();
const char* name = packet_info.name(t);
if (strcmp(name,"SRM") == 0 || strcmp(name,"cbr") == 0 || strcmp(name,"udp") == 0) {
if ( sh->type() < 5 && sh->type() > 0 ) {
sname = srm_names[sh->type()];
}
}
char *src_nodeaddr = Address::instance().print_nodeaddr(iph->saddr());
char *src_portaddr = Address::instance().print_portaddr(iph->sport());
char *dst_nodeaddr = Address::instance().print_nodeaddr(iph->daddr());
char *dst_portaddr = Address::instance().print_portaddr(iph->dport());
char flags[NUMFLAGS+1];
for (int i = 0; i < NUMFLAGS; i++)
flags[i] = '-';
flags[NUMFLAGS] = 0;
#ifdef OFF_HDR
hdr_flags* hf = (hdr_flags*)p->access(off_flags_);
#else
hdr_flags* hf = hdr_flags::access(p);
#endif
flags[0] = hf->ecn_ ? 'C' : '-'; // Ecn Echo
flags[1] = hf->pri_ ? 'P' : '-';
flags[2] = '-';
flags[3] = hf->cong_action_ ? 'A' : '-'; // Congestion Action
flags[4] = hf->ecn_to_echo_ ? 'E' : '-'; // Congestion Experienced
flags[5] = hf->fs_ ? 'F' : '-';
flags[6] = hf->ecn_capable_ ? 'N' : '-';
#ifdef notdef
flags[1] = (iph->flags() & PF_PRI) ? 'P' : '-';
flags[2] = (iph->flags() & PF_USR1) ? '1' : '-';
flags[3] = (iph->flags() & PF_USR2) ? '2' : '-';
flags[5] = 0;
#endif
sprintf(nwrk_,
"%c -t "TIME_FORMAT" -s %d -d %d -p %s -e %d -c %d -i %d -a %d -x {%s.%s %s.%s %d %s %s}",
'h',
Scheduler::instance().clock(),
src_,
dst_,
name,
th->size(),
iph->flowid(),
th->uid(),
iph->flowid(),
src_nodeaddr,
src_portaddr,
dst_nodeaddr,
dst_portaddr,
-1, flags, sname);
namdump();
delete [] src_nodeaddr;
delete [] src_portaddr;
delete [] dst_nodeaddr;
delete [] dst_portaddr;
}
#endif
/* hack: if trace object not attached to anything free packet */
if (target_ == 0)
Packet::free(p);
else
send(p, h);
}
traffictrace.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) Xerox Corporation 1997. All rights reserved.
*
* License is granted to copy, to use, and to make and to use derivative
* works for research and evaluation purposes, provided that Xerox is
* acknowledged in all documentation pertaining to any such copy or derivative
* work. Xerox grants no other licenses expressed or implied. The Xerox trade
* name should not be used in any advertising without its written permission.
*
* XEROX CORPORATION MAKES NO REPRESENTATIONS CONCERNING EITHER THE
* MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE
* FOR ANY PARTICULAR PURPOSE. The software is provided "as is" without
* express or implied warranty of any kind.
*
* These notices must be retained in any copies of any part of this software.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (Xerox)";
#endif
/* XXX: have not dealt with errors. e.g., if something fails during
* TraceFile::setup(), or TrafficTrace is not pointing to a TraceFile,
* no guarantee about results.
*/
#include
#include
#include
#include "config.h"
#ifdef HAVE_NETINET_IN_H
#include
#endif /* HAVE_NETINET_IN_H */
#include "random.h"
/* module to implement a traffic generator in ns driven by a trace
* file. records in the trace file consist of 2 32 bit fields, the
* first indicating the inter-packet time in microseconds, and the
* second indicating the packet length in bytes. multiple TraffficTrace
* objects can use the same trace file, and different TrafficTrace
* objects can use different trace files. For each TrafficTrace, a
* random starting point within the trace file is selected.
*/
#include "object.h"
#include "trafgen.h"
struct tracerec {
u_int32_t trec_time; /* inter-packet time (usec) */
u_int32_t trec_size; /* size of packet (bytes */
};
/* object to hold a single trace file */
class TraceFile : public NsObject {
public:
TraceFile();
void get_next(int&, struct tracerec&); /* called by TrafficGenerator
* to get next record in trace.
*/
int setup(); /* initialize the trace file */
int command(int argc, const char*const* argv);
private:
void recv(Packet*, Handler*); /* must be defined for NsObject */
int status_;
char *name_; /* name of the file in which the trace is stored */
int nrec_; /* number of records in the trace file */
struct tracerec *trace_; /* array holding the trace */
};
/* instance of a traffic generator. has a pointer to the TraceFile
* object and implements the interval() function.
*/
class TrafficTrace : public TrafficGenerator {
public:
TrafficTrace();
int command(int argc, const char*const* argv);
virtual double next_interval(int &);
protected:
void timeout();
TraceFile *tfile_;
struct tracerec trec_;
int ndx_;
void init();
};
static class TraceFileClass : public TclClass {
public:
TraceFileClass() : TclClass("Tracefile") {}
TclObject* create(int, const char*const*) {
return (new TraceFile());
}
} class_tracefile;
TraceFile::TraceFile() : status_(0)
{
}
int TraceFile::command(int argc, const char*const* argv)
{
if (argc == 3) {
if (strcmp(argv[1], "filename") == 0) {
name_ = new char[strlen(argv[2])+1];
strcpy(name_, argv[2]);
return(TCL_OK);
}
}
return (NsObject::command(argc, argv));
}
void TraceFile::get_next(int& ndx, struct tracerec& t)
{
t.trec_time = trace_[ndx].trec_time;
t.trec_size = trace_[ndx].trec_size;
if (++ndx == nrec_)
ndx = 0;
}
int TraceFile::setup()
{
tracerec* t;
struct stat buf;
int i;
FILE *fp;
/* only open/read the file once (could be shared by multiple
* SourceModel's
*/
if (! status_) {
status_ = 1;
if (stat(name_, (struct stat *)&buf)) {
printf("could not stat %s\n", name_);
return -1;
}
nrec_ = buf.st_size/sizeof(tracerec);
unsigned nrecplus = nrec_ * sizeof(tracerec);
unsigned bufst = buf.st_size;
// if ((unsigned)(nrec_ * sizeof(tracerec)) != buf.st_size) {
if (nrecplus != bufst) {
printf("bad file size in %s\n", name_);
return -1;
}
trace_ = new struct tracerec[nrec_];
if ((fp = fopen(name_, "r")) == NULL) {
printf("can't open file %s\n", name_);
return -1;
}
for (i = 0, t = trace_; i < nrec_; i++, t++)
if (fread((char *)t, sizeof(tracerec), 1, fp) != 1) {
printf("read failed\n");
return -1 ;
}
else {
t->trec_time = htonl(t->trec_time);
t->trec_size = htonl(t->trec_size);
}
}
/* pick a random starting place in the trace file */
return (int(Random::uniform((double)nrec_)+.5));
}
void TraceFile::recv(Packet*, Handler*)
{
/* shouldn't get here */
abort();
}
/**************************************************************/
static class TrafficTraceClass : public TclClass {
public:
TrafficTraceClass() : TclClass("Application/Traffic/Trace") {}
TclObject* create(int, const char*const*) {
return(new TrafficTrace());
}
} class_traffictrace;
TrafficTrace::TrafficTrace()
{
tfile_ = (TraceFile *)NULL;
}
void TrafficTrace::init()
{
if (tfile_)
ndx_ = tfile_->setup();
}
int TrafficTrace::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
if (argc == 3) {
if (strcmp(argv[1], "attach-tracefile") == 0) {
tfile_ = (TraceFile *)TclObject::lookup(argv[2]);
if (tfile_ == 0) {
tcl.resultf("no such node %s", argv[2]);
return(TCL_ERROR);
}
return(TCL_OK);
}
}
return (TrafficGenerator::command(argc, argv));
}
void TrafficTrace::timeout()
{
if (! running_)
return;
/* send a packet */
// Note: May need to set "NEW_BURST" flag in sendmsg() for
// signifying a new talkspurt when using vat traces.
// (see expoo.cc, tcl/ex/test-rcvr.tcl)
agent_->sendmsg(size_);
/* figure out when to send the next one */
nextPkttime_ = next_interval(size_);
/* schedule it */
timer_.resched(nextPkttime_);
}
double TrafficTrace::next_interval(int& size)
{
tfile_->get_next(ndx_, trec_);
size = trec_.trec_size;
return(((double)trec_.trec_time)/1000000.0); /* usecs->secs */
}
trafgen.cc
/* -*- 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, http://daedalus.cs.berkeley.edu
*
*/
#include "trafgen.h"
#include "agent.h"
TrafficGenerator::TrafficGenerator() : nextPkttime_(-1), timer_(this)
{
}
void TrafficGenerator::start()
{
init();
running_ = 1;
nextPkttime_ = next_interval(size_);
timer_.resched(nextPkttime_);
// Enable line below if you want to send immediately upon start
//timeout();
}
void TrafficGenerator::stop()
{
if (running_)
timer_.cancel();
running_ = 0;
}
void TrafficGenerator::timeout()
{
if (! running_)
return;
/* send a packet */
send(size_);
/* figure out when to send the next one */
nextPkttime_ = next_interval(size_);
/* schedule it */
if (nextPkttime_ > 0)
timer_.resched(nextPkttime_);
else
running_ = 0;
}
void TrafficTimer::expire(Event *)
{
tgen_->timeout();
}
ttl.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1996-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 MASH Research
* Group at the University of California Berkeley.
* 4. Neither the name of the University nor of the Research Group 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.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$";
#endif
#include "packet.h"
#include "ip.h"
#include "connector.h"
class TTLChecker : public Connector {
public:
TTLChecker() : noWarn_(1), tick_(1) {}
int command(int argc, const char*const* argv) {
if (argc == 3) {
if (strcmp(argv[1], "warning") == 0) {
noWarn_ = ! atoi(argv[2]);
return TCL_OK;
}
if (strcmp(argv[1], "tick") == 0) {
int tick = atoi(argv[2]);
if (tick > 0) {
tick_ = tick;
return TCL_OK;
} else {
Tcl& tcl = Tcl::instance();
tcl.resultf("%s: TTL must be positive (specified = %d)\n",
name(), tick);
return TCL_ERROR;
}
}
}
return Connector::command(argc, argv);
}
void recv(Packet* p, Handler* h) {
hdr_ip* iph = hdr_ip::access(p);
int ttl = iph->ttl() - tick_;
if (ttl <= 0) {
/* XXX should send to a drop object.*/
// Yes, and now it does...
// Packet::free(p);
if (! noWarn_)
printf("ttl exceeded\n");
drop(p);
return;
}
iph->ttl() = ttl;
send(p, h);
}
protected:
int noWarn_;
int tick_;
};
static class TTLCheckerClass : public TclClass {
public:
TTLCheckerClass() : TclClass("TTLChecker") {}
TclObject* create(int, const char*const*) {
return (new TTLChecker);
}
} ttl_checker_class;
class SessionTTLChecker : public Connector {
public:
SessionTTLChecker() {}
int command(int argc, const char*const* argv);
void recv(Packet* p, Handler* h) {
hdr_ip* iph = hdr_ip::access(p);
int ttl = iph->ttl() - tick_;
if (ttl <= 0) {
/* XXX should send to a drop object.*/
// Yes, and now it does...
// Packet::free(p);
printf("ttl exceeded\n");
drop(p);
return;
}
iph->ttl() = ttl;
send(p, h);
}
protected:
int tick_;
};
static class SessionTTLCheckerClass : public TclClass {
public:
SessionTTLCheckerClass() : TclClass("TTLChecker/Session") {}
TclObject* create(int, const char*const*) {
return (new SessionTTLChecker);
}
} session_ttl_checker_class;
int SessionTTLChecker::command(int argc, const char*const* argv)
{
if (argc == 3) {
if (strcmp(argv[1], "tick") == 0) {
tick_ = atoi(argv[2]);
return (TCL_OK);
}
}
return (Connector::command(argc, argv));
}
tworayground.cc
/* -*- 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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*/
/* tworayground.cc
$Id: AllCode__.html 980 2002-12-12 09:36:58Z ffilali $
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
static class TwoRayGroundClass: public TclClass {
public:
TwoRayGroundClass() : TclClass("Propagation/TwoRayGround") {}
TclObject* create(int, const char*const*) {
return (new TwoRayGround);
}
} class_tworayground;
TwoRayGround::TwoRayGround()
{
last_hr = last_ht = 0.0;
crossover_dist = 0.0;
}
// use Friss at less than crossover distance
static double
Friss(double Pt, double Gt, double Gr, double lambda, double L, double d)
{
/*
* Friss free space equation:
*
* Pt * Gt * Gr * (lambda^2)
* P = --------------------------
* (4 *pi * d)^2 * L
*/
double M = lambda / (4 * PI * d);
return (Pt * Gt * Gr * (M * M)) / L;
}
// use two-ray at more than crossover distance
static double
TwoRay(double Pt, double Gt, double Gr, double ht, double hr, double d)
{
/*
* Two-ray ground reflection model.
*
* Pt * Gt * Gr * (ht^2 * hr^2)
* Pr = ----------------------------
* d^4
*
*/
return Pt * Gt * Gr * (hr * hr * ht * ht) / (d * d * d * d);
}
double
TwoRayGround::Pr(PacketStamp *t, PacketStamp *r, double L, double lambda)
{
double rX, rY, rZ; // loc of receiver
double tX, tY, tZ; // loc of xmitter
double d; // dist
double hr, ht; // height of recv and xmit antennas
double Pr;
r->getNode()->getLoc(&rX, &rY, &rZ);
t->getNode()->getLoc(&tX, &tY, &tZ);
rX += r->getAntenna()->getX();
rY += r->getAntenna()->getY();
tX += t->getAntenna()->getX();
tY += t->getAntenna()->getY();
d = sqrt((rX - tX) * (rX - tX)
+ (rY - tY) * (rY - tY)
+ (rZ - tZ) * (rZ - tZ));
/* We're going to assume the ground is essentially flat.
This empirical two ground ray reflection model doesn't make
any sense if the ground is not a plane. */
if (rZ != tZ) {
printf("%s: TwoRayGround propagation model assume flat ground\n",
__FILE__);
}
hr = rZ + r->getAntenna()->getZ();
ht = tZ + t->getAntenna()->getZ();
if (hr != last_hr || ht != last_ht)
{ // recalc the cross-over distance
/*
16 * PI^2 * L * hr^2 * ht^2
d^2 = ---------------------------------
lambda^2
*/
crossover_dist = sqrt((16 * PI * PI * L * ht * ht * hr * hr)
/ (lambda * lambda));
last_hr = hr; last_ht = ht;
#if DEBUG > 3
printf("TRG: xover %e.10 hr %f ht %f\n",
crossover_dist, hr, ht);
#endif
}
/*
* If the transmitter is within the cross-over range , use the
* Friss equation. Otherwise, use the two-ray
* ground reflection model.
*/
double Gt = t->getAntenna()->getTxGain(rX - tX, rY - tY, rZ - tZ,
t->getLambda());
double Gr = r->getAntenna()->getRxGain(tX - rX, tY - rY, tZ - rZ,
r->getLambda());
#if DEBUG > 3
printf("TRG %.9f %d(%d,%d)@%d(%d,%d) d=%f xo=%f :",
Scheduler::instance().clock(),
t->getNode()->index(), (int)tX, (int)tY,
r->getNode()->index(), (int)rX, (int)rY,
d, crossover_dist);
// printf("\n\t Pt %e Gt %e Gr %e lambda %e L %e :",
// t->getTxPr(), Gt, Gr, lambda, L);
#endif
if(d <= crossover_dist) {
Pr = Friss(t->getTxPr(), Gt, Gr, lambda, L, d);
#if DEBUG > 3
printf("Friss %e\n",Pr);
#endif
return Pr;
}
else {
Pr = TwoRay(t->getTxPr(), Gt, Gr, ht, hr, d);
#if DEBUG > 3
printf("TwoRay %e\n",Pr);
#endif
return Pr;
}
}
udp.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (C) Xerox Corporation 1997. All rights reserved.
*
* License is granted to copy, to use, and to make and to use derivative
* works for research and evaluation purposes, provided that Xerox is
* acknowledged in all documentation pertaining to any such copy or derivative
* work. Xerox grants no other licenses expressed or implied. The Xerox trade
* name should not be used in any advertising without its written permission.
*
* XEROX CORPORATION MAKES NO REPRESENTATIONS CONCERNING EITHER THE
* MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE
* FOR ANY PARTICULAR PURPOSE. The software is provided "as is" without
* express or implied warranty of any kind.
*
* These notices must be retained in any copies of any part of this software.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (Xerox)";
#endif
#include "udp.h"
#include "rtp.h"
#include "random.h"
static class UdpAgentClass : public TclClass {
public:
UdpAgentClass() : TclClass("Agent/UDP") {}
TclObject* create(int, const char*const*) {
return (new UdpAgent());
}
} class_udp_agent;
UdpAgent::UdpAgent() : Agent(PT_UDP), seqno_(-1)
{
bind("packetSize_", &size_);
bind("off_rtp_", &off_rtp_);
}
UdpAgent::UdpAgent(packet_t type) : Agent(type)
{
bind("packetSize_", &size_);
}
// put in timestamp and sequence number, even though UDP doesn't usually
// have one.
void UdpAgent::sendmsg(int nbytes, const char* flags)
{
Packet *p;
int n;
if (size_)
n = nbytes / size_;
else
printf("Error: UDP size = 0\n");
if (nbytes == -1) {
printf("Error: sendmsg() for UDP should not be -1\n");
return;
}
double local_time = Scheduler::instance().clock();
while (n-- > 0) {
p = allocpkt();
hdr_rtp* rh = (hdr_rtp*)p->access(off_rtp_);
rh->flags() = 0;
rh->seqno() = ++seqno_;
hdr_cmn::access(p)->timestamp() =
(u_int32_t)(SAMPLERATE*local_time);
// add "beginning of talkspurt" labels (tcl/ex/test-rcvr.tcl)
if (flags && (0 ==strcmp(flags, "NEW_BURST")))
rh->flags() |= RTP_M;
target_->recv(p);
}
n = nbytes % size_;
if (n > 0) {
p = allocpkt();
hdr_cmn::access(p)->size() = n;
hdr_rtp* rh = (hdr_rtp*)p->access(off_rtp_);
rh->flags() = 0;
rh->seqno() = ++seqno_;
hdr_cmn::access(p)->timestamp() =
(u_int32_t)(SAMPLERATE*local_time);
// add "beginning of talkspurt" labels (tcl/ex/test-rcvr.tcl)
if (flags && (0 == strcmp(flags, "NEW_BURST")))
rh->flags() |= RTP_M;
target_->recv(p);
}
idle();
}
varp.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1996 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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory and the Daedalus
* research group at UC 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.
*/
#include "varp.h"
#include "mac.h"
#include "packet.h"
static class VARPTableClass : public TclClass {
public:
VARPTableClass() : TclClass("VARPTable") {}
TclObject* create(int, const char*const*) {
return (new VARPTable);
}
} class_VARPTable;
VARPTable::VARPTable(void) : TclObject(), maddr_(0), size_(0)
{}
VARPTable::~VARPTable(void)
{
delete [] maddr_;
}
void VARPTable::sizeinit(int n)
{
int *temp = maddr_;
int osize = size_;
int i;
if (size_ == 0)
size_ = 10;
while(!(n < size_))
size_ = 2*size_;
maddr_ = new int[size_];
for (i=0;iVARPTable::command(int argc, const char*const* argv) {
if (argc == 4) {
if (strcmp(argv[1], "mac-addr") == 0) {
int n = atoi(argv[2]);
if(!(n < size_))
sizeinit(n);
maddr_[n] = atoi(argv[3]);
return (TCL_OK);
}
}
return TclObject::command(argc, argv);
}
int VARPTable::arpresolve(int IPaddr, Packet* p) {
if (IPaddr > size_)
return 1;
int ma = maddr_[IPaddr];
if (ma >= 0) {
HDR_MAC(p)->macDA_ = ma;
return 0;
}
return 1;
}
vatrcvr.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) Xerox Corporation 1997. All rights reserved.
*
* License is granted to copy, to use, and to make and to use derivative
* works for research and evaluation purposes, provided that Xerox is
* acknowledged in all documentation pertaining to any such copy or
* derivative work. Xerox grants no other licenses expressed or
* implied. The Xerox trade name should not be used in any advertising
* without its written permission.
*
* XEROX CORPORATION MAKES NO REPRESENTATIONS CONCERNING EITHER THE
* MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE
* FOR ANY PARTICULAR PURPOSE. The software is provided "as is" without
* express or implied warranty of any kind.
*
* These notices must be retained in any copies of any part of this
* software.
*/
/*
* Copyright (c) 1995 The 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 Network Research
* Group at Lawrence Berkeley National Laboratory.
* 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
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$";
#endif
#ifndef WIN32
#include
#endif
#include "agent.h"
#include "rtp.h"
#include "adaptive-receiver.h"
#include "vat.h"
//Most of this code is taken from the decoder.cc file of the publicly available
//vat code with some minor midifications.
class VatRcvr : public AdaptiveRcvr {
public:
VatRcvr();
protected:
int adapt(Packet *pkt, u_int32_t time);
void count(int statno);
u_int32_t hostoffset_;
int32_t var_; //variance in this host's interarrival time
u_int32_t playout_; // playout delay (in media units)
int maxdel_;
int block_size_;
int lecture_mode_;
u_int32_t lastrecv_;
u_int32_t predicted_drop_;
int delvar_;
/*XXX*/
#define MAXSTAT 16
struct statcntr {
const char* name;
u_int cnt;
} stat_[MAXSTAT];
int nstat_;
};
inline void VatRcvr::count(int statno)
{
++stat_[statno].cnt;
}
inline int absdiff(int x, int y)
{
register int r = y - x;
return (r < 0? -r : r);
}
static inline int newoffset(
int nvar,
int playout,
int maxdel, int mindel, int lecture)
{
register int offset = nvar;
if (offset > maxdel)
offset = maxdel;
register int diff = playout - offset;
if (diff > 0) {
// offset going down: in LectureMode, drop at most
// one frametime per talkspurt. In ConferenceMode,
// drop at most 1/2 of difference.
if (lecture) {
if (diff > FRAMESIZE) {
if (playout > (maxdel * 3) / 4 &&
diff > 10 * FRAMESIZE)
diff = 5 * FRAMESIZE;
else
diff = FRAMESIZE;
}
} else
diff >>= 1;
offset = playout - diff;
} else if (-diff > maxdel) {
// offset going way up: only allow 3/4 of max.
offset = (maxdel * 3) / 4;
}
if (offset > (maxdel * 7) / 8)
offset = (maxdel * 7) / 8;
else if (offset < mindel)
offset = mindel;
return (offset);
}
int VatRcvr::adapt(Packet *pkt, u_int32_t local_clock)
{
hdr_cmn* ch = (hdr_cmn*)pkt->access(off_cmn_);
register u_int32_t tstamp = (int)ch->timestamp();
register int hoff = (int)hostoffset_;
register int offset = (tstamp + hoff - local_clock) &~ 3;
hdr_rtp *rh = (hdr_rtp*)pkt->access(off_rtp_);
int new_ts = rh->flags() & RTP_M ;
//struct timeval tv;
//static long int last;
/* printf("%1d %10d %10d\n", new_ts ? 1: 0, tstamp, local_clock);*/
/* printf("%u\n", tstamp); */
//gettimeofday(&tv, NULL);
//last = tv.tv_usec - last;
//if (last < 0)
// last += 1000000;
/*
printf("%u %u %u ==> %d\n", tstamp, hoff, local_clock, offset);
printf("%u %d\n", tv.tv_usec, last);
*/
//last = tv.tv_usec;
/* printf("%u ==> %d\n", local_clock, offset); */
if (hoff == 0 || new_ts) {
/* if (hoff == 0) { */
/* printf("TS: var = %d playback = %d", var_ >> (VAR_FILTER + 3),
playout_ >> (PLAYO_FILTER + 3)); */
/*
* start of new talk spurt --
* use accumulated variance to compute new offset if
* this would make a significant change. We change if
* - the variance is currently 'small', or
* - the change would be a least a packet time
*/
register int nvar = var_ >> (VAR_FILTER - VAR_MULT);
offset = playout_ >> PLAYO_FILTER;
if (nvar < 3*FRAMESIZE || absdiff(nvar, offset) >= FRAMESIZE) {
offset = newoffset(nvar, offset, maxdel_,
block_size_, lecture_mode_);
/*
* assume that a talk spurt starts with TALK_LEAD
* samples of history & subtract them off if possible.
*/
/* CHANGED THIS PART OF VAT CODE AS WELL */
if (new_ts) {
offset -= 4 * FRAMESIZE;
if (offset < block_size_)
offset = block_size_;
}
}
hostoffset_ = local_clock - tstamp + offset;
/* printf(" new playback = %d\n", offset >> 3); */
} else if (offset < 0 || offset > maxdel_) {
/* printf("LP: late by %d var = %d playback = %d", (0 - (offset >> 3)),
var_ >> (VAR_FILTER + 3), playout_ >> (PLAYO_FILTER + 3)); */
/*
* packet out of range -- if last packet also out of
* range or if the delay would increase, resync.
*/
if (offset < 0 || predicted_drop_ == tstamp) {
offset = newoffset(var_ >> (VAR_FILTER - VAR_MULT),
playout_ >> PLAYO_FILTER,
maxdel_, block_size_,
lecture_mode_);
hostoffset_ = local_clock - tstamp + offset;
} else {
/* printf("late packet\n"); */
predicted_drop_ = tstamp + block_size_;
lastrecv_ = tstamp - local_clock;
count(STAT_LATE);
return (-1);
}
/* printf(" new playback = %d\n", offset >> 3); */
} else {
// packet in range, update interarrival var. est.
register int nvar = var_;
register int off = tstamp - local_clock - lastrecv_;
/* printf("offset = %3d", off >> 3); */
if (off < 0)
off = -off;
off -= (nvar >> VAR_FILTER);
var_ = nvar + off;
/* printf(" var = %3d", var_ >> 8); */
}
lastrecv_ = tstamp - local_clock;
register u_int avgplay = playout_;
playout_ = avgplay + (offset - (avgplay >> PLAYO_FILTER));
/* printf(" offset = %3d avg playout = %3d", offset >> 3, playout_ >> 8); */
offset &= ~3;
delvar_ = var_ >> VAR_FILTER;
/* printf("\n"); */
return (offset);
}
static class VatRcvrClass : public TclClass {
public:
VatRcvrClass() : TclClass("Agent/VatRcvr") {}
TclObject* create(int, const char*const*) {
return (new VatRcvr());
}
} class_vat_rcvr;
VatRcvr::VatRcvr() :
hostoffset_(0),
var_(INITIAL_OFFSET << (VAR_FILTER - VAR_MULT)),
playout_(INITIAL_OFFSET << PLAYO_FILTER),
maxdel_(8000*6),
block_size_(FRAMESIZE),
lecture_mode_(0),
lastrecv_(0),
predicted_drop_(~0),
nstat_(0)
{
for (int i = 0; i < MAXSTAT; ++i) {
stat_[i].name = 0;
stat_[i].cnt = 0;
}
stat_[STAT_LATE].name = "Late-Pkts";
nstat_ = 1;
}
wired-phy.cc
/* -*- 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.
* @(#) $Header:
*/
#include
static class WiredPhyClass: public TclClass {
public:
WiredPhyClass() : TclClass("Phy/WiredPhy") {}
TclObject* create(int, const char*const*) {
return (new WiredPhy);
}
} class_WiredPhy;
WiredPhy::WiredPhy(void) : Phy()
{
//propagation_ = 0;
//bandwidth_ = 10*1e6; // 10M
bind_bw("bandwidth_", &bandwidth_);
}
int
WiredPhy::command(int argc, const char*const* argv) {
if (argc == 3) {
TclObject *obj;
if( (obj = TclObject::lookup(argv[2])) == 0) {
fprintf(stderr, "%s lookup failed\n", argv[1]);
return TCL_ERROR;
}
//if (strcmp(argv[1], "propagation") == 0) {
//assert(propagation_ == 0);
//propagation_ = (TclObject*) obj;
//return TCL_OK;
//}
}
return Phy::command(argc, argv);
}
void WiredPhy::sendDown(Packet *p)
{
/*
* Sanity Check
*/
assert(initialized());
/*
* Stamp the packet with the interface arguments, txtime and
* info reqd by recving interface to determine
* collision/contention
*/
// and then send the packet
channel_->recv(p, this);
}
int WiredPhy::sendUp(Packet *)
{
/*
* Sanity Check
*/
assert(initialized());
/* check with propagation model for collision, channel
contention */
/* Return one if pkt recv , else return zero.*/
return 1;
}
wireless-phy.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- *
*
* Copyright (c) 1996 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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory and the Daedalus
* research group at UC 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.
*
* Ported from CMU/Monarch's code, nov'98 -Padma Haldar.
wireless-phy.cc
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/* ======================================================================
WirelessPhy Interface
====================================================================== */
static class WirelessPhyClass: public TclClass {
public:
WirelessPhyClass() : TclClass("Phy/WirelessPhy") {}
TclObject* create(int, const char*const*) {
return (new WirelessPhy);
}
} class_WirelessPhy;
WirelessPhy::WirelessPhy() : Phy()
{
node_ = 0;
propagation_ = 0;
modulation_ = 0;
bandwidth_ = 2*1e6; // 100 kb
// Pt_ = 16.267 * 1e-3; // 16.267 mW for 100m range with TwoRay model
Pt_ = pow(10, 2.45) * 1e-3; // 24.5 dbm, ~ 281.8mw
Pr_ = Pt_;
lambda_ = SPEED_OF_LIGHT / (914 * 1e6); // 914 mHz
L_ = 1.0;
freq_ = -1.0;
/*
* It sounds like 10db should be the capture threshold.
*
* If a node is presently receiving a packet a a power level
* Pa, and a packet at power level Pb arrives, the following
* comparion must be made to determine whether or not capture
* occurs:
*
* 10 * log(Pa) - 10 * log(Pb) > 10db
*
* OR equivalently
*
* Pa/Pb > 10.
*
*/
CPThresh_ = 10.0;
CSThresh_ = 1.559e-11;
RXThresh_ = 3.652e-10;
//bind("CPThresh_", &CPThresh_);
//bind("CSThresh_", &CSThresh_);
//bind("RXThresh_", &RXThresh_);
//bind("bandwidth_", &bandwidth_);
//bind("Pt_", &Pt_);
//bind("freq_", &freq_);
//bind("L_", &L_);
if (freq_ != -1.0)
{ // freq was set by tcl code
lambda_ = SPEED_OF_LIGHT / freq_;
}
}
int
WirelessPhy::command(int argc, const char*const* argv)
{
TclObject *obj;
if(argc == 3) {
if (strcasecmp(argv[1], "setTxPower") == 0) {
Pt_ = atof(argv[2]);
return TCL_OK;
}
else if (strcasecmp(argv[1], "setRxPower") == 0) {
Pr_ = atof(argv[2]);
return TCL_OK;
}
else if( (obj = TclObject::lookup(argv[2])) == 0) {
fprintf(stderr, "WirelessPhy: %s lookup of %s failed\n",
argv[1], argv[2]);
return TCL_ERROR;
}
if (strcmp(argv[1], "propagation") == 0) {
assert(propagation_ == 0);
propagation_ = (Propagation*) obj;
return TCL_OK;
}
else if (strcasecmp(argv[1], "antenna") == 0) {
ant_ = (Antenna*) obj;
return TCL_OK;
}
else if (strcasecmp(argv[1], "node") == 0) {
assert(node_ == 0);
node_ = (MobileNode*) obj;
return TCL_OK;
}
}
return Phy::command(argc,argv);
}
void
WirelessPhy::sendDown(Packet *p)
{
/*
* Sanity Check
*/
assert(initialized());
/*
* Decrease node's energy
*/
if(node_->energy_model()) {
if ((node_->energy_model())->energy() > 0) {
double txtime = (8. * hdr_cmn::access(p)->size()) / bandwidth_;
node_->add_sndtime(txtime);
(node_->energy_model())->DecrTxEnergy(txtime,Pt_);
if ((node_->energy_model())->energy() <= 0) {
node_->energy_model()->setenergy(0);
node_->log_energy(0);
}
} else {
Packet::free(p);
return;
}
}
/*
* Stamp the packet with the interface arguments
*/
p->txinfo_.stamp(node_, ant_->copy(), Pt_, lambda_);
// Send the packet
channel_->recv(p, this);
}
int
WirelessPhy::sendUp(Packet *p)
{
/*
* Sanity Check
*/
assert(initialized());
PacketStamp s;
double Pr;
int pkt_recvd = 0;
// if the node is in sleeping mode, drop the packet simply
if (node_->sleep()) {
pkt_recvd = 0;
goto DONE;
}
// if the energy goes to ZERO, drop the packet simply
if (node_->energy_model()) {
if ((node_->energy_model())->energy() <= 0) {
pkt_recvd = 0;
goto DONE;
}
}
if(propagation_) {
s.stamp(node_, ant_, 0, lambda_);
Pr = propagation_->Pr(&p->txinfo_, &s, this);
if (Pr < CSThresh_) {
pkt_recvd = 0;
goto DONE;
}
if (Pr < RXThresh_) {
/*
* We can detect, but not successfully receive
* this packet.
*/
hdr_cmn *hdr = HDR_CMN(p);
hdr->error() = 1;
#if DEBUG > 3
printf("SM %f.9 _%d_ drop pkt from %d low POWER %e/%e\n",
Scheduler::instance().clock(), node_->index(),
p->txinfo_.getNode()->index(),
Pr,RXThresh);
#endif
}
}
if(modulation_) {
hdr_cmn *hdr = HDR_CMN(p);
hdr->error() = modulation_->BitError(Pr);
}
/*
* The MAC layer must be notified of the packet reception
* now - ie; when the first bit has been detected - so that
* it can properly do Collision Avoidance / Detection.
*/
pkt_recvd = 1;
DONE:
p->txinfo_.getAntenna()->release();
//*RxPr = Pr;
/* WILD HACK: The following two variables are a wild hack.
They will go away in the next release...
They're used by the mac-802_11 object to determine
capture. This will be moved into the net-if family of
objects in the future. */
p->txinfo_.RxPr = Pr;
p->txinfo_.CPThresh = CPThresh_;
/*
* Decrease energy if packet successfully received
*/
if(pkt_recvd && node_->energy_model()) {
double rcvtime = (8. * hdr_cmn::access(p)->size()) / bandwidth_;
// no way to reach here if the energy level < 0
node_->add_rcvtime(rcvtime);
(node_->energy_model())->DecrRcvEnergy(rcvtime,Pr_);
if ((node_->energy_model())->energy() <= 0) {
// saying node died
node_->energy_model()->setenergy(0);
node_->log_energy(0);
}
}
return pkt_recvd;
}
void
WirelessPhy::dump(void) const
{
Phy::dump();
fprintf(stdout,
"\tPt: %f, Gt: %f, Gr: %f, lambda: %f, L: %f\n",
Pt_, ant_->getTxGain(0,0,0,lambda_), ant_->getRxGain(0,0,0,lambda_), lambda_, L_);
fprintf(stdout, "\tbandwidth: %f\n", bandwidth_);
fprintf(stdout, "--------------------------------------------------\n");
}
inet.c
/*
* Copyright (c) 1991 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Lawrence Berkeley Laboratory,
* Berkeley, CA. The name of the University may not be used to
* endorse or promote products derived from this software without
* specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
static const char rcsid[] =
"@(#) $Header$ (LBL)";
#include
#include
#include
#ifdef WIN32
#include
#include
#else
#include
#include
#include
#include
#endif
#include "config.h"
#include "inet.h"
u_int32_t
LookupHostAddr(const char *s)
{
if (isdigit(*s))
return (u_int32_t)inet_addr(s);
else {
struct hostent *hp = gethostbyname(s);
if (hp == 0)
/*XXX*/
return (0);
return *((u_int32_t **)hp->h_addr_list)[0];
}
}
u_int32_t
LookupLocalAddr(void)
{
static u_int32_t local_addr;
char name[MAXHOSTNAMELEN];
if (local_addr == 0) {
(void)gethostname(name, sizeof(name));
local_addr = LookupHostAddr(name);
}
return (local_addr);
}
/*
* A faster replacement for inet_ntoa().
* Extracted from tcpdump 2.1.
*/
const char *
intoa(u_int32_t addr)
{
register char *cp;
register u_int byte;
register int n;
static char buf[sizeof(".xxx.xxx.xxx.xxx")];
NTOHL(addr);
cp = &buf[sizeof buf];
*--cp = '\0';
n = 4;
do {
byte = addr & 0xff;
*--cp = byte % 10 + '0';
byte /= 10;
if (byte > 0) {
*--cp = byte % 10 + '0';
byte /= 10;
if (byte > 0)
*--cp = byte + '0';
}
*--cp = '.';
addr >>= 8;
} while (--n > 0);
return cp + 1;
}
char *
InetNtoa(u_int32_t addr)
{
const char *s = intoa(addr);
char *p = (char *)malloc(strlen(s) + 1);
strcpy(p, s);
return p;
}
char *
LookupHostName(u_int32_t addr)
{
char *p;
struct hostent* hp;
/*XXX*/
if (IN_MULTICAST(ntohl(addr)))
return (InetNtoa(addr));
hp = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET);
if (hp == 0)
return InetNtoa(addr);
p = (char *)malloc(strlen(hp->h_name) + 1);
strcpy(p, hp->h_name);
return p;
}
/*
* in_cksum --
* Checksum routine for Internet Protocol family headers (C Version)
* [taken from ping.c]
*/
u_short
in_cksum(addr, len)
u_short *addr;
int len;
{
register int nleft = len;
register u_short *w = addr;
register int sum = 0;
u_short answer = 0;
/*
* Our algorithm is simple, using a 32 bit accumulator (sum), we add
* sequential 16 bit words to it, and at the end, fold back all the
* carry bits from the top 16 bits into the lower 16 bits.
*/
while (nleft > 1) {
sum += *w++;
nleft -= 2;
}
/* mop up an odd byte, if necessary */
if (nleft == 1) {
*(u_char *)(&answer) = *(u_char *)w ;
sum += answer;
}
/* add back carry outs from top 16 bits to low 16 bits */
sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
sum += (sum >> 16); /* add carry */
answer = ~sum; /* truncate to 16 bits */
return(answer);
}
#include
#include
#include
void
print_ip(struct ip *ip)
{
char buf[64];
u_short off = ntohs(ip->ip_off);
printf("IP v:%d, ihl:%d, tos:%d, id:%d, off:%d [df:%d, mf:%d], sum:%d, prot:%d\n",
ip->ip_v, ip->ip_hl, ip->ip_tos, ntohs(ip->ip_id),
off & IP_OFFMASK,
(off & IP_DF) ? 1 : 0,
(off & IP_MF) ? 1 : 0,
ip->ip_sum, ip->ip_p);
#ifdef HAVE_ADDR2ASCII
addr2ascii(AF_INET, &ip->ip_src, 4, buf);
#else
inet_ntoa(ip->ip_src);
#endif /* HAVE_ADDR2ASCII */
#ifdef HAVE_ADDR2ASCII
printf("IP len:%d ttl: %d, src: %s, dst: %s\n",
ntohs(ip->ip_len), ip->ip_ttl, buf,
addr2ascii(AF_INET, &ip->ip_dst, 4, 0));
#else
printf("IP len:%d ttl: %d, src: %s, dst: %s\n",
ntohs(ip->ip_len), ip->ip_ttl, buf,
inet_ntoa(ip->ip_src));
#endif /* HAVE_ADDR2ASCII */
}
aodv.cc
/*
aodv.cc
$Id: AllCode__.html 980 2002-12-12 09:36:58Z ffilali $
*/
/* The AODV code developed by the CMU/MONARCH group was optimized
* and tuned by Samir Das (UTSA) and Mahesh Marina (UTSA). The
* work was partially done in Sun Microsystems.
*
* The original CMU copyright is below.
*/
/*
Copyright (c) 1997, 1998 Carnegie Mellon University. All Rights
Reserved.
Permission to use, copy, modify, and distribute this
software and its documentation is hereby granted (including for
commercial or for-profit use), provided that both the copyright notice
and this permission notice appear in all copies of the software,
derivative works, or modified versions, and any portions thereof, and
that both notices appear in supporting documentation, and that credit
is given to Carnegie Mellon University in all publications reporting
on direct or indirect use of this code or its derivatives.
ALL CODE, SOFTWARE, PROTOCOLS, AND ARCHITECTURES DEVELOPED BY THE CMU
MONARCH PROJECT ARE EXPERIMENTAL AND ARE KNOWN TO HAVE BUGS, SOME OF
WHICH MAY HAVE SERIOUS CONSEQUENCES. CARNEGIE MELLON PROVIDES THIS
SOFTWARE OR OTHER INTELLECTUAL PROPERTY IN ITS ``AS IS'' CONDITION,
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 CARNEGIE MELLON UNIVERSITY
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 OR
INTELLECTUAL PROPERTY, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
Carnegie Mellon encourages (but does not require) users of this
software or intellectual property to return any improvements or
extensions that they make, and to grant Carnegie Mellon the rights to
redistribute these changes without encumbrance.
*/
#include
#include
#include
#include
#include
#define max(a,b) a > b ? a : b
#define CURRENT_TIME Scheduler::instance().clock()
//#define DEBUG
#ifdef DEBUG
static int extra_route_reply = 0;
static int limit_route_request = 0;
static int route_request = 0;
#endif
/* ===================================================================
TCL Hooks
================================================================= */
static class AODVHeaderClass : public PacketHeaderClass {
public:
AODVHeaderClass() : PacketHeaderClass("PacketHeader/AODV",
AODV_HDR_LEN) { }
} class_rtProtoAODV_hdr;
static class AODVclass : public TclClass {
public:
AODVclass() : TclClass("Agent/AODV") {}
TclObject* create(int argc, const char*const* argv) {
assert(argc == 5);
return (new AODV((nsaddr_t) atoi(argv[4])));
}
} class_rtProtoAODV;
/* ================================================================
Timers
============================================================= */
void
BroadcastTimer::handle(Event*)
{
agent->id_purge();
Scheduler::instance().schedule(this, &intr, BCAST_ID_SAVE);
}
void
HelloTimer::handle(Event*)
{
agent->sendHello();
double interval = MinHelloInterval +
((MaxHelloInterval - MinHelloInterval) * Random::uniform());
assert(interval >= 0);
Scheduler::instance().schedule(this, &intr, interval);
}
void
NeighborTimer::handle(Event*)
{
agent->nb_purge();
Scheduler::instance().schedule(this, &intr, HELLO_INTERVAL);
}
void
RouteCacheTimer::handle(Event*)
{
agent->rt_purge();
#define FREQUENCY 0.5 // sec
Scheduler::instance().schedule(this, &intr, FREQUENCY);
}
void
LocalRepairTimer::handle(Event* p) // SRD: 5/4/99
{
rt_entry *rt;
/* you get here after the timeout in a local repair attempt */
/* fprintf(stderr, "%s\n", __FUNCTION__); */
struct hdr_ip *ih = HDR_IP( (Packet *)p);
// rt = agent->rtable.rt_lookup(ih->dst_);
rt = agent->rtable.rt_lookup(ih->daddr());
if (rt && rt->rt_flags != RTF_UP) { // route is yet to be repaired
// I will be conservative and bring down the route
// and send triggered replies upstream.
/* The following assert fails, not sure why */
/* assert (rt->rt_flags == RTF_IN_REPAIR); */
agent->rt_down(rt);
/*
printf("Node %d: Dst - %d, failed local repair\n",index, rt->rt_dst);
*/
}
Packet::free((Packet *)p);
}
/*================================================================== */
AODV::AODV(nsaddr_t id) : Agent(PT_AODV),
btimer(this), htimer(this), ntimer(this), rtimer(this), lrtimer(this),
rqueue()
{
bind("off_AODV_", &off_AODV_);
index = id;
seqno = 1;
bid = 1;
LIST_INIT(&nbhead);
LIST_INIT(&bihead);
logtarget = 0;
ifqueue = 0;
}
int
AODV::command(int argc, const char*const* argv)
{
if(argc == 2) {
Tcl& tcl = Tcl::instance();
if(strncasecmp(argv[1], "id", 2) == 0) {
tcl.resultf("%d", index);
return TCL_OK;
}
if(strncasecmp(argv[1], "start", 2) == 0) {
btimer.handle((Event*) 0);
#ifndef AODV_LINK_LAYER_DETECTION
htimer.handle((Event*) 0);
ntimer.handle((Event*) 0);
#endif
rtimer.handle((Event*) 0);
return TCL_OK;
}
}
else if(argc == 3) {
if(strcmp(argv[1], "index") == 0) {
index = atoi(argv[2]);
return TCL_OK;
}
else if(strcmp(argv[1], "log-target") == 0 || strcmp(argv[1], "tracetarget") == 0 ) {
logtarget = (Trace*) TclObject::lookup(argv[2]);
if(logtarget == 0)
return TCL_ERROR;
return TCL_OK;
}
else if(strcmp(argv[1], "drop-target") == 0) {
int stat = rqueue.command(argc,argv);
if (stat != TCL_OK) return stat;
return Agent::command(argc, argv);
}
else if(strcmp(argv[1], "if-queue") == 0) {
ifqueue = (PriQueue*) TclObject::lookup(argv[2]);
if(ifqueue == 0)
return TCL_ERROR;
return TCL_OK;
}
}
return Agent::command(argc, argv);
}
/* =====================================================================
Neighbor Management Functions
===================================================================== */
void
AODV::nb_insert(nsaddr_t id)
{
Neighbor *nb = new Neighbor(id);
assert(nb);
nb->nb_expire = CURRENT_TIME +
(1.5 * ALLOWED_HELLO_LOSS * HELLO_INTERVAL);
LIST_INSERT_HEAD(&nbhead, nb, nb_link);
seqno += 1; // set of neighbors changed
}
Neighbor*
AODV::nb_lookup(nsaddr_t id)
{
Neighbor *nb = nbhead.lh_first;
for(; nb; nb = nb->nb_link.le_next) {
if(nb->nb_addr == id)
break;
}
return nb;
}
/*
* Called when we receive *explicit* notification that a Neighbor
* is no longer reachable.
*/
void
AODV::nb_delete(nsaddr_t id)
{
Neighbor *nb = nbhead.lh_first;
rt_entry *rt;
log_link_del(id);
seqno += 1; // Set of neighbors changed
for(; nb; nb = nb->nb_link.le_next) {
if(nb->nb_addr == id) {
LIST_REMOVE(nb,nb_link);
delete nb;
break;
}
}
for(rt = rtable.head(); rt; rt = rt->rt_link.le_next) {
if(rt->rt_nexthop == id) {
rt_down(rt);
}
}
}
/*
* Purges all timed-out Neighbor Entries - runs every
* HELLO_INTERVAL * 1.5 seconds.
*/
void
AODV::nb_purge()
{
Neighbor *nb = nbhead.lh_first;
Neighbor *nbn;
double now = CURRENT_TIME;
for(; nb; nb = nbn) {
nbn = nb->nb_link.le_next;
if(nb->nb_expire <= now) {
nb_delete(nb->nb_addr);
}
}
}
/* =====================================================================
Broadcast ID Management Functions
===================================================================== */
void
AODV::id_insert(nsaddr_t id, u_int32_t bid)
{
BroadcastID *b = new BroadcastID(id, bid);
assert(b);
b->expire = CURRENT_TIME + BCAST_ID_SAVE;
LIST_INSERT_HEAD(&bihead, b, link);
}
/* I changed this, SRD */
u_int32_t
AODV::id_lookup(nsaddr_t id, u_int32_t bid)
{
BroadcastID *b = bihead.lh_first;
// Search the list for a match of source and bid
for( ; b; b = b->link.le_next) {
if ((b->src == id) && (b->id == bid))
return ID_FOUND;
}
return ID_NOT_FOUND;
}
void
AODV::id_purge()
{
BroadcastID *b = bihead.lh_first;
BroadcastID *bn;
double now = CURRENT_TIME;
for(; b; b = bn) {
bn = b->link.le_next;
if(b->expire <= now) {
LIST_REMOVE(b,link);
delete b;
}
}
}
/* ================================================================= */
static void
aodv_rt_failed_callback(Packet *p, void *arg)
{
((AODV*) arg)->rt_ll_failed(p);
}
/*
* This routine is invoked when the link-layer reports a route failed.
*/
void
AODV::rt_ll_failed(Packet *p)
{
#ifndef AODV_LINK_LAYER_DETECTION
drop(p, DROP_RTR_MAC_CALLBACK);
#else
struct hdr_cmn *ch = HDR_CMN(p);
struct hdr_ip *ih = HDR_IP(p);
rt_entry *rt;
/*
* Non-data packets and Broadcast Packets can be dropped.
*/
if(! DATA_PACKET(ch->ptype()) ||
// (u_int32_t) ih->dst_ == IP_BROADCAST) {
ih->daddr() == (nsaddr_t)IP_BROADCAST) {
drop(p, DROP_RTR_MAC_CALLBACK);
return;
}
log_link_broke(p);
// if((rt = rtable.rt_lookup(ih->dst_)) == 0) {
if((rt = rtable.rt_lookup(ih->daddr())) == 0) {
drop(p, DROP_RTR_MAC_CALLBACK);
return;
}
log_link_del(ch->next_hop_);
/* if the broken link is closer to the dest than source,
attempt a local repair. Otherwise, bring down the route. */
#ifdef AODV_LOCAL_REPAIR
if (ch->num_forwards() > rt->rt_hops) {
local_rt_repair(rt, p); // local repair
// Mahesh 09/11/99
// retrieve all the packets in the ifq using this link,
// queue the packets for which local repair is done,
// drop the rest of the packets and send triggered replies
return;
}
else
#endif
{
/* Increment the sequence no. and bring down the route */
rt->rt_seqno++;
rt_down(rt);
// if (index == ih->src_) {
if (index == ih->saddr()) {
// If I am the source,
// queue the packet since rt_down tries to send a request
// Mahesh 09/11/99
rqueue.enque(p);
}
else {
drop(p,DROP_RTR_NO_ROUTE);
}
}
#endif /* AODV_LINK_LAYER_DETECTION */
}
void
AODV::local_rt_repair(rt_entry *rt, Packet *p)
{
/* fprintf(stderr,"%s: Dst - %d\n", __FUNCTION__, rt->rt_dst); */
/* Buffer the packet */
rqueue.enque(p);
/* mark the route as under repair */
rt->rt_flags = RTF_IN_REPAIR;
/* start a route discovery */
/* Mahesh 09/11/99
Note that the following does not ensure that route request
is actually sent.
*/
sendRequest(rt->rt_dst);
/* set up a timer interrupt */
Scheduler::instance().schedule(&lrtimer, p->copy(),
rt->rt_req_timeout);
}
void
AODV::rt_down(rt_entry *rt)
{
/*
* Make sure that you don't "down" a route more than once.
*/
if(rt->rt_flags == RTF_DOWN) {
return;
}
/*
// Mahesh 09/11/99
// This function has changed considerably from the last version.
// Need to check whether the next hop for the destination
// is changed before bringing down the route, for the time being
// we ignore this case.
*/
rt->rt_flags = RTF_DOWN;
rt->rt_expire = 0;
#ifndef ERROR_BROADCAST
{
Neighbor *nb = rt->rt_nblist.lh_first;
Neighbor *nbn;
for( ; nb; nb = nbn) {
nbn = nb->nb_link.le_next;
if (nb->nb_expire > CURRENT_TIME)
// If this neighbor is still considered active
sendTriggeredReply(nb->nb_addr,
rt->rt_dst,
rt->rt_seqno);
LIST_REMOVE(nb, nb_link);
delete nb;
}
}
#else
// Broadcast an unsolicited route reply to the upstream neighbors
sendTriggeredReply(rt->rt_dst, rt->rt_seqno);
#endif
/*
* Now purge the Network Interface queues that
* may have packets destined for this broken
* neighbor.
*/
{
Packet *p;
while((p = ifqueue->filter(rt->rt_nexthop))) {
// struct hdr_cmn *ch = HDR_CMN(p);
struct hdr_ip *ih = HDR_IP(p);
// if (index == ih->src_) {
if (index == ih->saddr()) {
// If I am the source of the packet,
// queue this packet and send route request.
rqueue.enque(p);
// sendRequest(ih->dst_);
sendRequest(ih->daddr());
}
else {
// else drop the packet. We don't try to salvage anything
// on an intermediate node.
drop(p, DROP_RTR_NO_ROUTE);
}
} /* while */
} /* purge ifq */
} /* rt_down function */
void
AODV::rt_resolve(Packet *p)
{
struct hdr_cmn *ch = HDR_CMN(p);
struct hdr_ip *ih = HDR_IP(p);
rt_entry *rt;
/*
* Set the transmit failure callback. That
* won't change.
*/
ch->xmit_failure_ = aodv_rt_failed_callback;
ch->xmit_failure_data_ = (void*) this;
// rt = rtable.rt_lookup(ih->dst_);
rt = rtable.rt_lookup(ih->daddr());
if(rt == 0) {
// rt = rtable.rt_add(ih->dst_);
rt = rtable.rt_add(ih->daddr());
}
/*
* If the route is up, forward the packet
*/
if(rt->rt_flags == RTF_UP) {
forward(rt, p, NO_DELAY);
}
/*
* A local repair is in progress. Buffer the packet.
*/
else if (rt->rt_flags == RTF_IN_REPAIR) {
rqueue.enque(p);
}
/*
* if I am the source of the packet, then do a Route Request.
*/
// else if(ih->src_ == index) {
else if(ih->saddr() == index) {
rqueue.enque(p);
sendRequest(rt->rt_dst);
}
/*
* I am trying to forward a packet for someone else to which
* I don't have a route.
*/
else {
#ifndef ERROR_BROADCAST
/*
* For now, drop the packet and send triggered reply upstream.
* Now the unsolicited route replies are broadcast to upstream
* neighbors - Mahesh 09/11/99
*/
sendTriggeredReply(ch->prev_hop_,rt->rt_dst, rt->rt_seqno);
#else
sendTriggeredReply(rt->rt_dst, rt->rt_seqno);
#endif
drop(p, DROP_RTR_NO_ROUTE);
}
}
void
AODV::rt_purge()
{
rt_entry *rt, *rtn;
double now = CURRENT_TIME;
double delay = 0.0;
Packet *p;
for(rt = rtable.head(); rt; rt = rtn) { // for each rt entry
rtn = rt->rt_link.le_next;
if ((rt->rt_flags == RTF_UP)
&& (rt->rt_expire < now)) {
// if a valid route has expired, purge all packets from
// send buffer and invalidate the route.
while((p = rqueue.deque(rt->rt_dst))) {
#ifdef DEBUG
fprintf(stderr, "%s: calling drop()\n",
__FUNCTION__);
#endif
// drop(p, DROP_RTR_RTEXPIRE);
drop(p, DROP_RTR_NO_ROUTE);
}
rt->rt_flags = RTF_DOWN;
/* LIST_REMOVE(rt, rt_link);
delete rt;
*/
}
else if (rt->rt_flags == RTF_UP) {
// If the route is not expired,
// and there are packets in the sendbuffer waiting,
// forward them. This should not be needed, but this extra check
// does no harm.
while((p = rqueue.deque(rt->rt_dst))) {
forward (rt, p, delay);
delay += ARP_DELAY;
}
}
else if (rqueue.find(rt->rt_dst))
// If the route is down and
// if there is a packet for this destination waiting in
// the sendbuffer, then send out route request. sendRequest
// will check whether it is time to really send out request
// or not.
// This may not be crucial to do it here, as each generated
// packet will do a sendRequest anyway.
sendRequest(rt->rt_dst);
}
}
/* =====================================================================
Packet Reception Routines
===================================================================== */
void
AODV::recv(Packet *p, Handler*)
{
struct hdr_cmn *ch = HDR_CMN(p);
struct hdr_ip *ih = HDR_IP(p);
assert(initialized());
//assert(p->incoming == 0);
if(ch->ptype() == PT_AODV) {
ih->ttl_ -= 1;
recvAODV(p);
return;
}
/*
* Must be a packet I'm originating...
*/
// if(ih->src_ == index && ch->num_forwards() == 0) {
if(ih->saddr() == index && ch->num_forwards() == 0) {
/*
* Add the IP Header
*/
ch->size() += IP_HDR_LEN;
ih->ttl_ = NETWORK_DIAMETER;
}
/*
* I received a packet that I sent. Probably
* a routing loop.
*/
//else if(ih->src_ == index) {
else if(ih->saddr() == index) {
drop(p, DROP_RTR_ROUTE_LOOP);
return;
}
/*
* Packet I'm forwarding...
*/
else {
/*
* Check the TTL. If it is zero, then discard.
*/
if(--ih->ttl_ == 0) {
drop(p, DROP_RTR_TTL);
return;
}
}
rt_resolve(p);
}
void
AODV::recvAODV(Packet *p)
{
struct hdr_ip *ih = HDR_IP(p);
struct hdr_aodv *ah = HDR_AODV(p);
//assert(ih->sport_ == RT_PORT);
//assert(ih->dport_ == RT_PORT);
assert(ih->sport() == RT_PORT);
assert(ih->dport() == RT_PORT);
/*
* Incoming Packets.
*/
switch(ah->ah_type) {
case AODVTYPE_HELLO:
recvHello(p);
break;
case AODVTYPE_RREQ:
recvRequest(p);
break;
case AODVTYPE_RREP:
recvReply(p);
break;
case AODVTYPE_UREP:
recvTriggeredReply(p);
break;
default:
fprintf(stderr, "Invalid AODV type (%x)\n", ah->ah_type);
exit(1);
}
}
void
AODV::recvRequest(Packet *p)
{
Node *thisnode;
struct hdr_ip *ih = HDR_IP(p);
struct hdr_aodv_request *rq = HDR_AODV_REQUEST(p);
rt_entry *rt;
/*
* Drop if:
* - I'm the source
* - I recently heard this request.
*/
if(rq->rq_src == index) {
#ifdef DEBUG
fprintf(stderr, "%s: got my own REQUEST\n", __FUNCTION__);
#endif
Packet::free(p);
return;
}
if (id_lookup(rq->rq_src,rq->rq_bcast_id) == ID_FOUND) {
#ifdef DEBUG
fprintf(stderr, "%s: discarding request\n", __FUNCTION__);
#endif
Packet::free(p);
return;
}
/*
* Cache the broadcast ID
*/
id_insert(rq->rq_src, rq->rq_bcast_id);
rt = rtable.rt_lookup(rq->rq_dst);
/*
* We are either going to forward the REQUEST or generate a
* REPLY. Before we do anything, we make sure that the REVERSE
* route is in the route table.
*/
{
rt_entry *rt0; // rt0 is the reverse route
Packet *buffered_pkt;
rt0 = rtable.rt_lookup(rq->rq_src);
if(rt0 == 0) { /* if not in the route table */
// create an entry for the reverse route.
rt0 = rtable.rt_add(rq->rq_src);
}
/*
* Changed to make sure the expiry times are set
* appropriately - Mahesh 09/11/99
*/
if (rt0->rt_flags != RTF_UP) { // Route wasn't up
if (rt0->rt_req_timeout > 0.0) {
// Reset the soft state and
// Set expiry time to CURRENT_TIME + ACTIVE_ROUTE_TIMEOUT
// This is because route is used in the forward direction,
// but only sources get benefited by this change
rt0->rt_req_cnt = 0;
rt0->rt_req_timeout = 0.0;
if (rq->rq_hop_count != INFINITY2)
rt0->rt_req_last_ttl = rq->rq_hop_count;
rt0->rt_expire = CURRENT_TIME + ACTIVE_ROUTE_TIMEOUT;
}
else {
// Set expiry time to CURRENT_TIME + REV_ROUTE_LIFE
rt0->rt_expire = CURRENT_TIME + REV_ROUTE_LIFE;
}
// In any case, change your entry
rt0->rt_flags = RTF_UP;
#ifdef ERROR_BROADCAST
rt0->error_propagate_counter = 0; // Mahesh 09/11/99
#endif
rt0->rt_hops = rq->rq_hop_count;
rt0->rt_seqno = rq->rq_src_seqno;
// rt0->rt_nexthop = ih->src_;
rt0->rt_nexthop = ih->saddr();
}
else // Route was up
if ((rq->rq_src_seqno > rt0->rt_seqno ) ||
((rt0->rt_seqno == rq->rq_src_seqno) &&
(rt0->rt_hops > rq->rq_hop_count))) {
// If we have a fresher seq no. or lesser #hops for the
// same seq no., update the rt entry. Else don't bother.
rt0->rt_expire = max(rt0->rt_expire,
CURRENT_TIME + REV_ROUTE_LIFE);
rt0->rt_flags = RTF_UP;
#ifdef ERROR_BROADCAST
// Only if next hop is changed
// rt0->error_propagate_counter = 0; Mahesh 09/11/99
#endif
rt0->rt_hops = rq->rq_hop_count;
rt0->rt_seqno = rq->rq_src_seqno;
//rt0->rt_nexthop = ih->src_;
rt0->rt_nexthop = ih->saddr();
}
/* Find out whether any buffered packet can benefit from the
* reverse route.
*/
assert (rt0->rt_flags == RTF_UP);
while ((buffered_pkt = rqueue.deque(rt0->rt_dst))) {
if (rt0 && (rt0->rt_flags == RTF_UP))
/* need to do the above check again,
* as deque has side-effects */
forward(rt0, buffered_pkt, NO_DELAY);
}
} // End for putting reverse route in rt table
/*
* We have taken care of the reverse route stuff. Now see whether
* we can send a route reply.
*/
// First check if I am the destination ..
if(rq->rq_dst == index) {
#ifdef DEBUG
fprintf(stderr, "%d - %s: destination sending reply\n",
index, __FUNCTION__);
#endif
// Just to be safe, I use the max. Somebody may have
// incremented the dst seqno.
//
seqno = max((unsigned)seqno, (unsigned)rq->rq_dst_seqno) + 1;
sendReply(rq->rq_src, // IP Destination
0, // Hop Count
index, // Dest IP Address
seqno, // Dest Sequence Num
MY_ROUTE_TIMEOUT, // Lifetime
rq->rq_timestamp); // timestamp
Packet::free(p);
// Sending replying, I am in the route
thisnode = Node::get_node_by_address(index);
if (thisnode->powersaving()) {
thisnode->set_node_sleep(0);
thisnode->set_node_state(INROUTE);
}
}
// I am not the destination, but I may have a fresh enough route.
else if (rt && (rt->rt_flags == RTF_UP)
&& (rt->rt_seqno >= rq->rq_dst_seqno)) {
sendReply(rq->rq_src,
rt->rt_hops + 1,
rq->rq_dst,
rt->rt_seqno,
(u_int32_t) (rt->rt_expire - CURRENT_TIME),
rq->rq_timestamp);
Packet::free(p);
thisnode = Node::get_node_by_address(index);
if (thisnode->powersaving()) {
thisnode->set_node_sleep(0);
thisnode->set_node_state(INROUTE);
}
}
/*
* Can't reply. So forward the Route Request
*/
else {
//ih->dst_ = IP_BROADCAST;
//ih->src_ = index;
ih->daddr() = IP_BROADCAST;
ih->saddr() = index;
rq->rq_hop_count += 1;
forward((rt_entry*) 0, p, DELAY);
thisnode = Node::get_node_by_address(index);
if (thisnode->powersaving()) {
thisnode->set_node_sleep(0);
thisnode->set_node_state(WAITING);
}
}
}
void
AODV::recvReply(Packet *p)
{
Node *thisnode;
struct hdr_cmn *ch = HDR_CMN(p);
struct hdr_ip *ih = HDR_IP(p);
struct hdr_aodv_reply *rp = HDR_AODV_REPLY(p);
rt_entry *rt;
// char suppress_reply = 0;
double delay = 0.0;
#ifdef DEBUG
fprintf(stderr, "%d - %s: received a REPLY\n", index, __FUNCTION__);
#endif
/*
* Handle "Route Errors" separately...
*/
if(rp->rp_hop_count == INFINITY2) {
recvTriggeredReply(p);
return;
}
/*
* Got a reply. So reset the "soft state" maintained for
* route requests in the request table. We don't really have
* have a separate request table. It is just a part of the
* routing table itself.
*/
// Note that rp_dst is the dest of the data packets, not the
// the dest of the reply, which is the src of the data packets.
if((rt = rtable.rt_lookup(rp->rp_dst))) {
// reset the soft state
rt->rt_req_cnt = 0;
rt->rt_req_timeout = 0.0;
if (rp->rp_hop_count != INFINITY2)
rt->rt_req_last_ttl = rp->rp_hop_count;
}
/*
* If I don't have a rt entry to this host... adding
*/
if(rt == 0) {
rt = rtable.rt_add(rp->rp_dst);
}
/*
* Add a forward route table entry... here I am following
* Perkins-Royer AODV paper almost literally - SRD 5/99
*/
if ((rt->rt_flags != RTF_UP) || // no route before
((rt->rt_seqno < rp->rp_dst_seqno) || // newer route
((rt->rt_seqno == rp->rp_dst_seqno) &&
(rt->rt_hops > rp->rp_hop_count)))) { // shorter or equal route
// Update the rt entry
rt->rt_expire = CURRENT_TIME + rp->rp_lifetime;
#ifdef ERROR_BROADCAST
if (rt->rt_flags != RTF_UP) // or next hop changed
rt->error_propagate_counter = 0; // Mahesh 09/11/99
#endif
rt->rt_flags = RTF_UP;
rt->rt_hops = rp->rp_hop_count;
rt->rt_seqno = rp->rp_dst_seqno;
rt->rt_nexthop = ch->prev_hop_;
#ifdef AODV_LINK_LAYER_DETECTION
rt->rt_errors = 0;
rt->rt_error_time = 0.0;
#endif
// if (ih->dst_ == index) { // If I am the original source
if (ih->daddr() == index) {
//assert(rp->rp_hop_count);
// Update the route discovery latency statistics
// rp->rp_timestamp is the time of request origination
rt->rt_disc_latency[rt->hist_indx] =
(CURRENT_TIME - rp->rp_timestamp)
/ (double) rp->rp_hop_count;
// increment indx for next time
rt->hist_indx = (rt->hist_indx + 1) % MAX_HISTORY;
}
}
/*
* If reply is for me, discard it.
*/
//if(ih->dst_ == index) {
if(ih->daddr() == index) {
Packet::free(p);
}
/*
* Otherwise, forward the Route Reply.
*/
else {
// Find the rt entry
//rt_entry *rt0 = rtable.rt_lookup(ih->dst_);
rt_entry *rt0 = rtable.rt_lookup(ih->daddr());
// If the rt is up, forward
if(rt0 && (rt0->rt_flags == RTF_UP)) {
#ifdef ERROR_BROADCAST
rt_entry *rt_dst = rtable.rt_lookup(rp->rp_dst);
(rt_dst->error_propagate_counter)++; // Mahesh 09/11/99
#endif
rp->rp_hop_count += 1;
forward(rt0, p, NO_DELAY);
thisnode = Node::get_node_by_address(index);
if (thisnode->powersaving()) {
thisnode->set_node_sleep(0);
thisnode->set_node_state(INROUTE);
}
}
else {
// I don't know how to forward .. drop the reply.
#ifdef DEBUG
fprintf(stderr, "%s: droping Route Reply\n", __FUNCTION__);
#endif
drop(p, DROP_RTR_NO_ROUTE);
}
}
/*
* Send all packets queued in the sendbuffer destined for
* this destination.
* XXX - observe the "second" use of p.
*/
while((p = rqueue.deque(rt->rt_dst))) {
if(rt->rt_flags == RTF_UP) {
// Delay them a little to help ARP. Otherwise ARP
// may drop packets. -SRD 5/23/99
forward(rt, p, delay);
delay += ARP_DELAY;
}
}
}
void
AODV::recvTriggeredReply(Packet *p)
{
struct hdr_ip *ih = HDR_IP(p);
struct hdr_aodv_reply *rp = HDR_AODV_REPLY(p);
rt_entry *rt = rtable.rt_lookup(rp->rp_dst);
if(rt && (rt->rt_flags == RTF_UP)) {
// We bring down the route if the source of the
// triggered reply is my next hop. We don't check
// sequence numbers (!!!) as this next hop won't
// forward packets for me anyways.
// if (rt->rt_nexthop == ih->src_) {
if (rt->rt_nexthop == ih->saddr()) {
rt->rt_seqno = rp->rp_dst_seqno;
rt_down(rt);
}
}
Packet::free(p);
}
void
AODV::recvHello(Packet *p)
{
struct hdr_aodv_reply *rp = HDR_AODV_REPLY(p);
Neighbor *nb;
/*
* XXX: I use a separate TYPE for hello messages rather than
* a bastardized Route Reply.
*/
nb = nb_lookup(rp->rp_dst);
if(nb == 0) {
nb_insert(rp->rp_dst);
}
else {
nb->nb_expire = CURRENT_TIME +
(1.5 * ALLOWED_HELLO_LOSS * HELLO_INTERVAL);
}
Packet::free(p);
}
/* ======================================================================
Packet Transmission Routines
===================================================================== */
void
AODV::forward(rt_entry *rt, Packet *p, double delay)
{
struct hdr_cmn *ch = HDR_CMN(p);
struct hdr_ip *ih = HDR_IP(p);
Neighbor *nb;
if(ih->ttl_ == 0) {
#ifdef DEBUG
fprintf(stderr, "%s: calling drop()\n", __PRETTY_FUNCTION__);
#endif
drop(p, DROP_RTR_TTL);
return;
}
#ifndef ERROR_BROADCAST
/*
* Keep the "official" Neighbor List up-to-date.
*/
if(nb_lookup(ch->prev_hop_) == 0) {
nb_insert(ch->prev_hop_);
}
#endif
if(rt) {
// If it is a unicast packet to be forwarded
assert(rt->rt_flags == RTF_UP);
rt->rt_expire = CURRENT_TIME + ACTIVE_ROUTE_TIMEOUT;
#ifndef ERROR_BROADCAST
/*
* Need to maintain the per-route Neighbor List. This is
* kept separate from the Neighbor list that is the list
* of ACTIVE neighbors.
*/
if(rt->nb_lookup(ch->prev_hop_) == 0) {
rt->nb_insert(ch->prev_hop_);
}
// set the expiry times
nb = rt->nb_lookup(ch->prev_hop_);
nb->nb_expire = CURRENT_TIME + ACTIVE_ROUTE_TIMEOUT;
#endif
ch->prev_hop_ = index;
ch->next_hop_ = rt->rt_nexthop;
ch->addr_type() = NS_AF_INET;
ch->direction() = hdr_cmn::DOWN; //important: change the packet's direction
}
else { // if it is a broadcast packet
assert(ch->ptype() == PT_AODV);
//assert(ih->dst_ == (nsaddr_t) IP_BROADCAST);
assert(ih->daddr() == (nsaddr_t) IP_BROADCAST);
ch->addr_type() = NS_AF_NONE;
ch->direction() = hdr_cmn::DOWN;
}
//if (ih->dst_ == (nsaddr_t) IP_BROADCAST) {
if (ih->daddr() == (nsaddr_t) IP_BROADCAST) {
// If it is a broadcast packet
assert(rt == 0);
/*
* Jitter the sending of broadcast packets by 10ms
*/
Scheduler::instance().schedule(target_, p, 0.01 * Random::uniform());
}
else // Not a broadcast packet
if(delay > 0.0) {
Scheduler::instance().schedule(target_, p, delay);
}
else {
// Not a broadcast packet, no delay, send immediately
//target_->recv(p, (Handler*) 0);
Scheduler::instance().schedule(target_, p, 0.);
}
}
void
AODV::sendRequest(nsaddr_t dst)
{
Node *thisnode = Node::get_node_by_address(index);
// Allocate a RREQ packet
Packet *p = Packet::alloc();
struct hdr_cmn *ch = HDR_CMN(p);
struct hdr_ip *ih = HDR_IP(p);
struct hdr_aodv_request *rq = HDR_AODV_REQUEST(p);
rt_entry *rt = rtable.rt_lookup(dst);
assert(rt);
/*
* Rate limit sending of Route Requests. We are very conservative
* about sending out route requests.
*/
if ((rt->rt_flags == RTF_UP) ||
(rt->rt_req_timeout > CURRENT_TIME))
return;
// rt_req_cnt is the no. of times we did network-wide broadcast
// RREQ_RETRIES is the maximum number we will allow before
// going to a long timeout.
if (rt->rt_req_cnt > RREQ_RETRIES) {
rt->rt_req_timeout = CURRENT_TIME + MAX_RREQ_TIMEOUT;
rt->rt_req_cnt = 0;
return;
}
#ifdef DEBUG
fprintf(stderr, "(%2d) - %2d sending Route Request, dst: %d\n",
++route_request, index, rt->rt_dst);
#endif
// set node into active state and remain it for MAX_RREQ_TIMEOUT
if (thisnode->powersaving()) {
thisnode->set_node_sleep(0);
thisnode->set_node_state(WAITING);
}
// Fill out the RREQ packet
// ch->uid() = 0;
ch->ptype() = PT_AODV;
ch->size() = IP_HDR_LEN + sizeof(*rq);
ch->iface() = -2;
ch->error() = 0;
ch->addr_type() = NS_AF_NONE;
ch->prev_hop_ = index; // AODV hack
/*
ih->src_ = index;
ih->dst_ = IP_BROADCAST;
ih->sport_ = RT_PORT;
ih->dport_ = RT_PORT;
*/
ih->saddr() = index;
ih->daddr() = IP_BROADCAST;
ih->sport() = RT_PORT;
ih->dport() = RT_PORT;
// Determine the TTL to be used this time.
if (0 == rt->rt_req_last_ttl) {
// first time query broadcast
ih->ttl_ = TTL_START;
}
else {
// Expanding ring search.
if (rt->rt_req_last_ttl < TTL_THRESHOLD)
ih->ttl_ = rt->rt_req_last_ttl + TTL_INCREMENT;
else {
// network-wide broadcast
ih->ttl_ = NETWORK_DIAMETER;
rt->rt_req_cnt += 1;
}
}
// PerHopTime is the roundtrip time per hop for route requests.
// The factor 2.0 is just to be safe .. SRD 5/22/99
// Also note that we are making timeouts to be larger if we have
// done network wide broadcast before.
rt->rt_req_timeout = 2.0 * (double) ih->ttl_ * PerHopTime(rt);
if (rt->rt_req_cnt > 0)
rt->rt_req_timeout *= rt->rt_req_cnt;
//printf("timeout=%f\n",rt->rt_req_timeout);
rt->rt_req_timeout += CURRENT_TIME;
// Don't let the timeout to be too large, however .. SRD 6/8/99
if (rt->rt_req_timeout > CURRENT_TIME + MAX_RREQ_TIMEOUT)
rt->rt_req_timeout = CURRENT_TIME + MAX_RREQ_TIMEOUT;
rt->rt_expire = 0;
#ifdef DEBUG
fprintf(stderr,
"(%2d) - %2d sending Route Request, dst: %d, tout %f ms\n",
++route_request,
index, rt->rt_dst,
rt->rt_req_timeout - CURRENT_TIME);
#endif
// remember the TTL used for the next time
rt->rt_req_last_ttl = ih->ttl_;
// Fill up some more fields.
rq->rq_type = AODVTYPE_RREQ;
rq->rq_hop_count = 0;
rq->rq_bcast_id = bid++;
rq->rq_dst = dst;
rq->rq_dst_seqno = (rt ? rt->rt_seqno : 0);
rq->rq_src = index;
rq->rq_src_seqno = seqno++; // Mahesh - 09/11/99
rq->rq_timestamp = CURRENT_TIME;
// target_->recv(p, (Handler*) 0);
Scheduler::instance().schedule(target_, p, 0.);
}
void
AODV::sendReply(nsaddr_t ipdst, u_int32_t hop_count, nsaddr_t rpdst,
u_int32_t rpseq, u_int32_t lifetime, double timestamp)
{
Packet *p = Packet::alloc();
struct hdr_cmn *ch = HDR_CMN(p);
struct hdr_ip *ih = HDR_IP(p);
struct hdr_aodv_reply *rp = HDR_AODV_REPLY(p);
rt_entry *rt = rtable.rt_lookup(ipdst);
assert(rt);
// ch->uid() = 0;
ch->ptype() = PT_AODV;
ch->size() = IP_HDR_LEN + sizeof(*rp);
ch->iface() = -2;
ch->error() = 0;
ch->addr_type() = NS_AF_INET;
ch->next_hop_ = rt->rt_nexthop;
ch->prev_hop_ = index; // AODV hack
ch->direction() = hdr_cmn::DOWN;
/*
ih->src_ = index;
ih->dst_ = ipdst;
ih->sport_ = RT_PORT;
ih->dport_ = RT_PORT;
*/
ih->saddr() = index;
ih->daddr() = ipdst;
ih->sport() = RT_PORT;
ih->dport() = RT_PORT;
ih->ttl_ = NETWORK_DIAMETER;
rp->rp_type = AODVTYPE_RREP;
rp->rp_flags = 0x00;
rp->rp_hop_count = hop_count;
rp->rp_dst = rpdst;
rp->rp_dst_seqno = rpseq;
rp->rp_lifetime = lifetime;
rp->rp_timestamp = timestamp;
//target_->recv(p, (Handler*) 0);
Scheduler::instance().schedule(target_, p, 0.);
}
#ifndef ERROR_BROADCAST
void
AODV::sendTriggeredReply(nsaddr_t ipdst, nsaddr_t rpdst, u_int32_t rpseq)
{
// 08/28/98 - added this extra check
if(ipdst == 0 || ipdst == index) return;
Packet *p = Packet::alloc();
struct hdr_cmn *ch = HDR_CMN(p);
struct hdr_ip *ih = HDR_IP(p);
struct hdr_aodv_reply *rp = HDR_AODV_REPLY(p);
// ch->uid() = 0;
ch->ptype() = PT_AODV;
ch->size() = IP_HDR_LEN + sizeof(*rp);
ch->iface() = -2;
ch->error() = 0;
ch->addr_type() = NS_AF_NONE;
ch->next_hop_ = 0;
ch->prev_hop_ = index; // AODV hack
/*
ih->src_ = index;
ih->dst_ = ipdst;
ih->sport_ = RT_PORT;
ih->dport_ = RT_PORT;
*/
ih->saddr() = index;
ih->daddr() = ipdst;
ih->sport() = RT_PORT;
ih->dport() = RT_PORT;
ih->ttl_ = 1;
rp->rp_type = AODVTYPE_UREP;
rp->rp_flags = 0x00;
rp->rp_hop_count = INFINITY2;
rp->rp_dst = rpdst;
rp->rp_dst_seqno = rpseq;
rp->rp_lifetime = 0; // XXX
target_->recv(p, (Handler*) 0);
}
#else
void
AODV::sendTriggeredReply(nsaddr_t rpdst, u_int32_t rpseq) {
// Broadcast unsolicited route replies - Mahesh 09/11/99
rt_entry *rt = rtable.rt_lookup(rpdst);
if (rt->error_propagate_counter == 0) return;
Packet *p = Packet::alloc();
struct hdr_cmn *ch = HDR_CMN(p);
struct hdr_ip *ih = HDR_IP(p);
struct hdr_aodv_reply *rp = HDR_AODV_REPLY(p);
// ch->uid() = 0;
ch->ptype() = PT_AODV;
ch->size() = IP_HDR_LEN + sizeof(*rp);
ch->iface() = -2;
ch->error() = 0;
ch->addr_type() = NS_AF_NONE;
ch->next_hop_ = 0;
ch->prev_hop_ = index; // AODV hack
ih->src_ = index;
ih->dst_ = IP_BROADCAST;
ih->sport_ = RT_PORT;
ih->dport_ = RT_PORT;
ih->ttl_ = 1;
rp->rp_type = AODVTYPE_UREP;
rp->rp_flags = 0x00;
rp->rp_hop_count = INFINITY2;
rp->rp_dst = rpdst;
rp->rp_dst_seqno = rpseq;
rp->rp_lifetime = 0;
//target_->recv(p, (Handler*) 0);
// Do we need any jitter?
Scheduler::instance().schedule(target_, p, 0.);
}
#endif
void
AODV::sendHello()
{
Packet *p = Packet::alloc();
struct hdr_cmn *ch = HDR_CMN(p);
struct hdr_ip *ih = HDR_IP(p);
struct hdr_aodv_reply *rh = HDR_AODV_REPLY(p);
// ch->uid() = 0;
ch->ptype() = PT_AODV;
ch->size() = IP_HDR_LEN + sizeof(*rh);
ch->iface() = -2;
ch->error() = 0;
ch->addr_type() = NS_AF_NONE;
ch->prev_hop_ = index; // AODV hack
/*
ih->src_ = index;
ih->dst_ = IP_BROADCAST;
ih->sport_ = RT_PORT;
ih->dport_ = RT_PORT;
*/
ih->saddr() = index;
ih->daddr() = IP_BROADCAST;
ih->sport() = RT_PORT;
ih->dport() = RT_PORT;
ih->ttl_ = 1;
rh->rp_type = AODVTYPE_HELLO;
rh->rp_flags = 0x00;
rh->rp_hop_count = 0;
rh->rp_dst = index;
rh->rp_dst_seqno = seqno;
rh->rp_lifetime = (1 + ALLOWED_HELLO_LOSS) * HELLO_INTERVAL;
target_->recv(p, (Handler*) 0);
}
/*
======================================================================
Helper routines
=======================================================================
*/
double
AODV::PerHopTime(rt_entry *rt)
{
int num_non_zero = 0, i;
double total_latency = 0.0;
if (!rt)
return ((double) NODE_TRAVERSAL_TIME );
for (i=0; i < MAX_HISTORY; i++) {
if (rt->rt_disc_latency[i] > 0.0) {
num_non_zero++;
total_latency += rt->rt_disc_latency[i];
}
}
if (num_non_zero > 0)
return(total_latency / (double) num_non_zero);
else
return((double) NODE_TRAVERSAL_TIME);
}
aodv_logs.cc
/*
aodv_logs.cc
$Id: AllCode__.html 980 2002-12-12 09:36:58Z ffilali $
*/
#include
#include
#include
#define CURRENT_TIME Scheduler::instance().clock()
static const int verbose = 0;
/* =====================================================================
Logging Functions
===================================================================== */
void
AODV::log_link_del(nsaddr_t dst)
{
static int link_del = 0;
if(! logtarget || ! verbose) return;
/*
* If "god" thinks that these two nodes are still
* reachable then this is an erroneous deletion.
*/
sprintf(logtarget->buffer(),
"A %.9f _%d_ deleting LL hop to %d (delete %d is %s)",
CURRENT_TIME,
index,
dst,
++link_del,
God::instance()->hops(index, dst) != 1 ? "VALID" : "INVALID");
logtarget->dump();
}
void
AODV::log_link_broke(Packet *p)
{
static int link_broke = 0;
struct hdr_cmn *ch = HDR_CMN(p);
if(! logtarget || ! verbose) return;
sprintf(logtarget->buffer(),
"A %.9f _%d_ LL unable to deliver packet %d to %d (%d) (reason = %d, ifqlen = %d)",
CURRENT_TIME,
index,
ch->uid_,
ch->next_hop_,
++link_broke,
ch->xmit_reason_,
ifqueue->length());
logtarget->dump();
}
void
AODV::log_link_kept(nsaddr_t dst)
{
static int link_kept = 0;
if(! logtarget || ! verbose) return;
/*
* If "god" thinks that these two nodes are now
* unreachable, then we are erroneously keeping
* a bad route.
*/
sprintf(logtarget->buffer(),
"A %.9f _%d_ keeping LL hop to %d (keep %d is %s)",
CURRENT_TIME,
index,
dst,
++link_kept,
God::instance()->hops(index, dst) == 1 ? "VALID" : "INVALID");
logtarget->dump();
}
dsdv.cc
/* -*- 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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*/
/* Ported from CMU/Monarch's code, nov'98 -Padma.*/
/* dsdv.cc
$Id: AllCode__.html 980 2002-12-12 09:36:58Z ffilali $
*/
extern "C" {
#include
#include
};
#include "dsdv.h"
#include "priqueue.h"
#include
#include
#include
#include
#define DSDV_STARTUP_JITTER 2.0 // secs to jitter start of periodic activity from
// when start-dsr msg sent to agent
#define DSDV_ALMOST_NOW 0.1 // jitter used for events that should be effectively
// instantaneous but are jittered to prevent
// synchronization
#define DSDV_BROADCAST_JITTER 0.01 // jitter for all broadcast packets
#define DSDV_MIN_TUP_PERIOD 1.0 // minimum time between triggered updates
#define IP_DEF_TTL 32 // default TTTL
#undef TRIGGER_UPDATE_ON_FRESH_SEQNUM
//#define TRIGGER_UPDATE_ON_FRESH_SEQNUM
/* should the receipt of a fresh (newer) sequence number cause us
to send a triggered update? If undef'd, we'll only trigger on
routing metric changes */
// Returns a random number between 0 and max
static inline double
jitter (double max, int be_random_)
{
return (be_random_ ? Random::uniform(max) : 0);
}
void DSDV_Agent::
trace (char *fmt,...)
{
va_list ap;
if (!tracetarget)
return;
va_start (ap, fmt);
vsprintf (tracetarget->buffer (), fmt, ap);
tracetarget->dump ();
va_end (ap);
}
void
DSDV_Agent::tracepkt (Packet * p, double now, int me, const char *type)
{
char buf[1024];
unsigned char *walk = p->accessdata ();
int ct = *(walk++);
int seq, dst, met;
snprintf (buf, 1024, "V%s %.5f _%d_ [%d]:", type, now, me, ct);
while (ct--)
{
dst = *(walk++);
dst = dst << 8 | *(walk++);
dst = dst << 8 | *(walk++);
dst = dst << 8 | *(walk++);
met = *(walk++);
seq = *(walk++);
seq = seq << 8 | *(walk++);
seq = seq << 8 | *(walk++);
seq = seq << 8 | *(walk++);
snprintf (buf, 1024, "%s (%d,%d,%d)", buf, dst, met, seq);
}
// Now do trigger handling.
//trace("VTU %.5f %d", now, me);
if (verbose_)
trace ("%s", buf);
}
// Prints out an rtable element.
void
DSDV_Agent::output_rte(const char *prefix, rtable_ent * prte, DSDV_Agent * a)
{
a->trace("DFU: deimplemented");
printf("DFU: deimplemented");
prte = 0;
prefix = 0;
#if 0
printf ("%s%d %d %d %d %f %f %f %f 0x%08x\n",
prefix, prte->dst, prte->hop, prte->metric, prte->seqnum,
prte->udtime, prte->new_seqnum_at, prte->wst, prte->changed_at,
(unsigned int) prte->timeout_event);
a->trace ("VTE %.5f %d %d %d %d %f %f %f %f 0x%08x",
Scheduler::instance ().clock (), prte->dst, prte->hop, prte->metric,
prte->seqnum, prte->udtime, prte->new_seqnum_at, prte->wst, prte->changed_at,
prte->timeout_event);
#endif
}
class DSDVTriggerHandler : public Handler {
public:
DSDVTriggerHandler(DSDV_Agent *a_) { a = a_; }
virtual void handle(Event *e);
private:
DSDV_Agent *a;
};
void
DSDVTriggerHandler::handle(Event *e)
// send a triggered update (or a full update if one's needed)
{
//DEBUG
//printf("(%d)-->triggered update with e=%x\n", a->myaddr_,e);
Scheduler & s = Scheduler::instance ();
Time now = s.clock ();
rtable_ent *prte;
int update_type; // we want periodic (=1) or triggered (=0) update?
Time next_possible = a->lasttup_ + DSDV_MIN_TUP_PERIOD;
for (a->table_->InitLoop(); (prte = a->table_->NextLoop());)
if (prte->trigger_event == e) break;
assert(prte && prte->trigger_event == e);
if (now < next_possible)
{
//DEBUG
//printf("(%d)..Re-scheduling triggered update\n",a->myaddr_);
s.schedule(a->trigger_handler, e, next_possible - now);
a->cancelTriggersBefore(next_possible);
return;
}
update_type = 0;
Packet * p = a->makeUpdate(/*in-out*/update_type);
if (p != NULL)
{
if (update_type == 1)
{ // we got a periodic update, though we only asked for triggered
// cancel and reschedule periodic update
s.cancel(a->periodic_callback_);
//DEBUG
//printf("we got a periodic update, though asked for trigg\n");
s.schedule (a->helper_, a->periodic_callback_,
a->perup_ * (0.75 + jitter (0.25, a->be_random_)));
if (a->verbose_) a->tracepkt (p, now, a->myaddr_, "PU");
}
else
{
if (a->verbose_) a->tracepkt (p, now, a->myaddr_, "TU");
}
assert (!HDR_CMN (p)->xmit_failure_); // DEBUG 0x2
s.schedule (a->target_, p, jitter(DSDV_BROADCAST_JITTER, a->be_random_));
a->lasttup_ = now; // even if we got a full update, it still counts
// for our last triggered update time
}
// free this event
for (a->table_->InitLoop (); (prte = a->table_->NextLoop ());)
if (prte->trigger_event && prte->trigger_event == e)
{
prte->trigger_event = 0;
delete e;
}
}
void
DSDV_Agent::cancelTriggersBefore(Time t)
// Cancel any triggered events scheduled to take place *before* time
// t (exclusive)
{
rtable_ent *prte;
Scheduler & s = Scheduler::instance ();
for (table_->InitLoop (); (prte = table_->NextLoop ());)
if (prte->trigger_event && prte->trigger_event->time_ < t)
{
//DEBUG
//printf("(%d) cancel event %x\n",myaddr_,prte->trigger_event);
s.cancel(prte->trigger_event);
delete prte->trigger_event;
prte->trigger_event = 0;
}
}
void
DSDV_Agent::needTriggeredUpdate(rtable_ent *prte, Time t)
// if no triggered update already pending, make one so
{
Scheduler & s = Scheduler::instance();
Time now = Scheduler::instance().clock();
assert(t >= now);
if (prte->trigger_event)
s.cancel(prte->trigger_event);
else
prte->trigger_event = new Event;
//DEBUG
//printf("(%d)..scheduling trigger-update with event %x\n",myaddr_,prte->trigger_event);
s.schedule(trigger_handler, prte->trigger_event, t - now);
}
void
DSDV_Agent::helper_callback (Event * e)
{
Scheduler & s = Scheduler::instance ();
double now = s.clock ();
rtable_ent *prte;
rtable_ent *pr2;
int update_type; // we want periodic (=1) or triggered (=0) update?
//DEBUG
//printf("Triggered handler on 0x%08x\n", e);
// Check for periodic callback
if (periodic_callback_ && e == periodic_callback_)
{
update_type = 1;
Packet *p = makeUpdate(/*in-out*/update_type);
if (verbose_)
{
trace ("VPC %.5f _%d_", now, myaddr_);
tracepkt (p, now, myaddr_, "PU");
}
if (p) {
assert (!HDR_CMN (p)->xmit_failure_); // DEBUG 0x2
// send out update packet jitter to avoid sync
//DEBUG
//printf("(%d)..sendout update pkt (periodic=%d)\n",myaddr_,update_type);
s.schedule (target_, p, jitter(DSDV_BROADCAST_JITTER, be_random_));
}
// put the periodic update sending callback back onto the
// the scheduler queue for next time....
s.schedule (helper_, periodic_callback_,
perup_ * (0.75 + jitter (0.25, be_random_)));
// this will take the place of any planned triggered updates
lasttup_ = now;
return;
}
// Check for timeout
// If it was a timeout, fix the routing table.
for (table_->InitLoop (); (prte = table_->NextLoop ());)
if (prte->timeout_event && (prte->timeout_event == e))
break;
// If it was a timeout, prte will be non-NULL
// Note that in the if we don't touch the changed_at time, so that when
// wst is computed, it doesn't consider the infinte metric the best
// one at that sequence number.
if (prte)
{
if (verbose_)
{
trace ("VTO %.5f _%d_ %d->%d", now, myaddr_, myaddr_, prte->dst);
/* trace ("VTO %.5f _%d_ trg_sch %x on sched %x time %f", now, myaddr_,
trigupd_scheduled,
trigupd_scheduled ? s.lookup(trigupd_scheduled->uid_) : 0,
trigupd_scheduled ? trigupd_scheduled->time_ : 0); */
}
for (table_->InitLoop (); (pr2 = table_->NextLoop ()); )
{
if (pr2->hop == prte->dst && pr2->metric != BIG)
{
if (verbose_)
trace ("VTO %.5f _%d_ marking %d", now, myaddr_, pr2->dst);
pr2->metric = BIG;
pr2->advertise_ok_at = now;
pr2->advert_metric = true;
pr2->advert_seqnum = true;
pr2->seqnum++;
// And we have routing info to propogate.
//DEBUG
//printf("(%d)..we have routing info to propagate..trigger update for dst %d\n",myaddr_,pr2->dst);
needTriggeredUpdate(pr2, now);
}
}
// OK the timeout expired, so we'll free it. No dangling pointers.
prte->timeout_event = 0;
}
else
{ // unknown event on queue
fprintf(stderr,"DFU: unknown queue event\n");
abort();
}
if (e)
delete e;
}
void
DSDV_Agent::lost_link (Packet *p)
{
hdr_cmn *hdrc = HDR_CMN (p);
rtable_ent *prte = table_->GetEntry (hdrc->next_hop_);
if(use_mac_ == 0) {
drop(p, DROP_RTR_MAC_CALLBACK);
return;
}
//DEBUG
//printf("(%d)..Lost link..\n",myaddr_);
if (verbose_ && hdrc->addr_type_ == NS_AF_INET)
trace ("VLL %.8f %d->%d lost at %d",
Scheduler::instance ().clock (),
((hdr_ip *) p->access (off_ip_))->saddr(),
((hdr_ip *) p->access (off_ip_))->daddr(),
myaddr_);
if (!use_mac_ || !prte || hdrc->addr_type_ != NS_AF_INET)
return;
if (verbose_)
trace ("VLP %.5f %d:%d->%d:%d lost at %d [hop %d]",
Scheduler::instance ().clock (),
((hdr_ip *) p->access (off_ip_))->saddr(),
((hdr_ip *) p->access (off_ip_))->sport(),
((hdr_ip *) p->access (off_ip_))->daddr(),
((hdr_ip *) p->access (off_ip_))->dport(),
myaddr_, prte->dst);
if (prte->timeout_event)
{
Scheduler::instance ().cancel (prte->timeout_event);
helper_callback (prte->timeout_event);
}
else if (prte->metric != BIG)
{
assert(prte->timeout_event == 0);
prte->timeout_event = new Event ();
helper_callback (prte->timeout_event);
}
// Queue these packets up...
recv(p, 0);
#if 0
while (p2 = ((PriQueue *) target_)->filter (prte->dst))
{
if (verbose_)
trace ("VRS %.5f %d:%d->%d:%d lost at %d", Scheduler::instance ().clock (),
((hdr_ip *) p2->access (off_ip_))->saddr(),
((hdr_ip *) p2->access (off_ip_))->sport(),
((hdr_ip *) p2->access (off_ip_))->daddr(),
((hdr_ip *) p2->access (off_ip_))->dport(), myaddr_);
recv(p2, 0);
}
while (p2 = ll_queue->filter (prte->dst))
{
if (verbose_)
trace ("VRS %.5f %d:%d->%d:%d lost at %d", Scheduler::instance ().clock (),
((hdr_ip *) p2->access (off_ip_))->saddr(),
((hdr_ip *) p2->access (off_ip_))->sport(),
((hdr_ip *) p2->access (off_ip_))->daddr(),
((hdr_ip *) p2->access (off_ip_))->dport(), myaddr_);
recv (p2, 0);
}
#endif
}
static void
mac_callback (Packet * p, void *arg)
{
((DSDV_Agent *) arg)->lost_link (p);
}
Packet *
DSDV_Agent::makeUpdate(int& periodic)
// return a packet advertising the state in the routing table
// makes a full ``periodic'' update if requested, or a ``triggered''
// partial update if there are only a few changes and full update otherwise
// returns with periodic = 1 if full update returned, or = 0 if partial
// update returned
{
//DEBUG
//printf("(%d)-->Making update pkt\n",myaddr_);
Packet *p = allocpkt ();
hdr_ip *iph = (hdr_ip *) p->access (off_ip_);
hdr_cmn *hdrc = HDR_CMN (p);
double now = Scheduler::instance ().clock ();
rtable_ent *prte;
unsigned char *walk;
int change_count; // count of entries to go in this update
int rtbl_sz; // counts total entries in rt table
int unadvertiseable; // number of routes we can't advertise yet
//printf("Update packet from %d [per=%d]\n", myaddr_, periodic);
// The packet we send wants to be broadcast
hdrc->next_hop_ = IP_BROADCAST;
hdrc->addr_type_ = NS_AF_INET;
iph->daddr() = IP_BROADCAST << Address::instance().nodeshift();
iph->dport() = ROUTER_PORT;
change_count = 0;
rtbl_sz = 0;
unadvertiseable = 0;
for (table_->InitLoop ();
(prte = table_->NextLoop ()); )
{
rtbl_sz++;
if ((prte->advert_seqnum || prte->advert_metric)
&& prte->advertise_ok_at <= now)
change_count++;
if (prte->advertise_ok_at > now) unadvertiseable++;
}
//printf("change_count = %d\n",change_count);
if (change_count * 3 > rtbl_sz && change_count > 3)
{ // much of the table has changed, just do a periodic update now
periodic = 1;
}
// Periodic update... increment the sequence number...
if (periodic)
{
change_count = rtbl_sz - unadvertiseable;
//printf("rtbsize-%d, unadvert-%d\n",rtbl_sz,unadvertiseable);
rtable_ent rte;
bzero(&rte, sizeof(rte));
/* inc sequence number on any periodic update, even if we started
off wanting to do a triggered update, b/c we're doing a real
live periodic update now that'll take the place of our next
periodic update */
seqno_ += 2;
rte.dst = myaddr_;
//rte.hop = iph->src();
rte.hop = Address::instance().get_nodeaddr(iph->saddr());
rte.metric = 0;
rte.seqnum = seqno_;
rte.advertise_ok_at = 0.0; // can always advert ourselves
rte.advert_seqnum = true; // always include ourselves in Triggered Upds
rte.changed_at = now;
rte.new_seqnum_at = now;
rte.wst = 0;
rte.timeout_event = 0; // Don't time out our localhost :)
rte.q = 0; // Don't buffer pkts for self!
table_->AddEntry (rte);
}
if (change_count == 0)
{
Packet::free(p); // allocated by us, no drop needed
return NULL; // nothing to advertise
}
/* ****** make the update packet.... ***********
with less than 100+ nodes, an update for all nodes is less than the
MTU, so don't bother worrying about splitting the update over
multiple packets -dam 4/26/98 */
assert(rtbl_sz <= (1500 / 12));
p->allocdata((change_count * 9) + 1);
walk = p->accessdata ();
*(walk++) = change_count;
// hdrc->size_ = change_count * 12 + 20; // DSDV + IP
hdrc->size_ = change_count * 12 + IP_HDR_LEN; // DSDV + IP
for (table_->InitLoop (); (prte = table_->NextLoop ());)
{
if (periodic && prte->advertise_ok_at > now)
{ // don't send out routes that shouldn't be advertised
// even in periodic updates
continue;
}
if (periodic ||
((prte->advert_seqnum || prte->advert_metric)
&& prte->advertise_ok_at <= now))
{ // include this rte in the advert
if (!periodic && verbose_)
trace ("VCT %.5f _%d_ %d", now, myaddr_, prte->dst);
//assert (prte->dst < 256 && prte->metric < 256);
//*(walk++) = prte->dst;
*(walk++) = prte->dst >> 24;
*(walk++) = (prte->dst >> 16) & 0xFF;
*(walk++) = (prte->dst >> 8) & 0xFF;
*(walk++) = (prte->dst >> 0) & 0xFF;
*(walk++) = prte->metric;
*(walk++) = (prte->seqnum) >> 24;
*(walk++) = ((prte->seqnum) >> 16) & 0xFF;
*(walk++) = ((prte->seqnum) >> 8) & 0xFF;
*(walk++) = (prte->seqnum) & 0xFF;
prte->last_advertised_metric = prte->metric;
// seqnum's only need to be advertised once after they change
prte->advert_seqnum = false; // don't need to advert seqnum again
if (periodic)
{ // a full advert means we don't have to re-advert either
// metrics or seqnums again until they change
prte->advert_seqnum = false;
prte->advert_metric = false;
}
change_count--;
}
}
assert(change_count == 0);
return p;
}
void
DSDV_Agent::updateRoute(rtable_ent *old_rte, rtable_ent *new_rte)
{
int negvalue = -1;
assert(new_rte);
Time now = Scheduler::instance().clock();
char buf[1024];
// snprintf (buf, 1024, "%c %.5f _%d_ (%d,%d->%d,%d->%d,%d->%d,%lf)",
snprintf (buf, 1024, "%c %.5f _%d_ (%d,%d->%d,%d->%d,%d->%d,%f)",
(new_rte->metric != BIG
&& (!old_rte || old_rte->metric != BIG)) ? 'D' : 'U',
now, myaddr_, new_rte->dst,
old_rte ? old_rte->metric : negvalue, new_rte->metric,
old_rte ? old_rte->seqnum : negvalue, new_rte->seqnum,
old_rte ? old_rte->hop : -1, new_rte->hop,
new_rte->advertise_ok_at);
table_->AddEntry (*new_rte);
//printf("(%d),Route table updated..\n",myaddr_);
if (trace_wst_)
trace ("VWST %.12lf frm %d to %d wst %.12lf nxthp %d [of %d]",
now, myaddr_, new_rte->dst, new_rte->wst, new_rte->hop,
new_rte->metric);
if (verbose_)
trace ("VS%s", buf);
}
void
DSDV_Agent::processUpdate (Packet * p)
{
hdr_ip *iph = (hdr_ip *) p->access (off_ip_);
Scheduler & s = Scheduler::instance ();
double now = s.clock ();
// it's a dsdv packet
int i;
unsigned char *d = p->accessdata ();
unsigned char *w = d + 1;
rtable_ent rte; // new rte learned from update being processed
rtable_ent *prte; // ptr to entry *in* routing tbl
//DEBUG
//int src, dst;
//src = Address::instance().get_nodeaddr(iph->src());
//dst = Address::instance().get_nodeaddr(iph->dst());
//printf("Received DSDV packet from %d(%d) to %d(%d) [%d)]\n", src, iph->sport(), dst, iph->dport(), myaddr_);
for (i = *d; i > 0; i--)
{
bool trigger_update = false; // do we need to do a triggered update?
nsaddr_t dst;
prte = NULL;
dst = *(w++);
dst = dst << 8 | *(w++);
dst = dst << 8 | *(w++);
dst = dst << 8 | *(w++);
if ((prte = table_->GetEntry (dst)))
{
bcopy(prte, &rte, sizeof(rte));
}
else
{
bzero(&rte, sizeof(rte));
}
rte.dst = dst;
//rte.hop = iph->src();
rte.hop = Address::instance().get_nodeaddr(iph->saddr());
rte.metric = *(w++);
rte.seqnum = *(w++);
rte.seqnum = rte.seqnum << 8 | *(w++);
rte.seqnum = rte.seqnum << 8 | *(w++);
rte.seqnum = rte.seqnum << 8 | *(w++);
rte.changed_at = now;
if (rte.metric != BIG) rte.metric += 1;
if (rte.dst == myaddr_)
{
if (rte.metric == BIG && periodic_callback_)
{
// You have the last word on yourself...
// Tell the world right now that I'm still here....
// This is a CMU Monarch optimiziation to fix the
// the problem of other nodes telling you and your neighbors
// that you don't exist described in the paper.
s.cancel (periodic_callback_);
s.schedule (helper_, periodic_callback_, 0);
}
continue; // don't corrupt your own routing table.
}
/********** fill in meta data for new route ***********/
// If it's in the table, make it the same timeout and queue.
if (prte)
{ // we already have a route to this dst
if (prte->seqnum == rte.seqnum)
{ // we've got an update with out a new squenece number
// this update must have come along a different path
// than the previous one, and is just the kind of thing
// the weighted settling time is supposed to track.
// this code is now a no-op left here for clarity -dam XXX
rte.wst = prte->wst;
rte.new_seqnum_at = prte->new_seqnum_at;
}
else
{ // we've got a new seq number, end the measurement period
// for wst over the course of the old sequence number
// and update wst with the difference between the last
// time we changed the route (which would be when the
// best route metric arrives) and the first time we heard
// the sequence number that started the measurement period
// do we care if we've missed a sequence number, such
// that we have a wst measurement period that streches over
// more than a single sequence number??? XXX -dam 4/20/98
rte.wst = alpha_ * prte->wst +
(1.0 - alpha_) * (prte->changed_at - prte->new_seqnum_at);
rte.new_seqnum_at = now;
}
}
else
{ // inititallize the wst for the new route
rte.wst = wst0_;
rte.new_seqnum_at = now;
}
// Now that we know the wst_, we know when to update...
if (rte.metric != BIG && (!prte || prte->metric != BIG))
rte.advertise_ok_at = now + (rte.wst * 2);
else
rte.advertise_ok_at = now;
/*********** decide whether to update our routing table *********/
if (!prte)
{ // we've heard from a brand new destination
if (rte.metric < BIG)
{
rte.advert_metric = true;
trigger_update = true;
}
updateRoute(prte,&rte);
}
else if ( prte->seqnum == rte.seqnum )
{ // stnd dist vector case
if (rte.metric < prte->metric)
{ // a shorter route!
if (rte.metric == prte->last_advertised_metric)
{ // we've just gone back to a metric we've already advertised
rte.advert_metric = false;
trigger_update = false;
}
else
{ // we're changing away from the last metric we announced
rte.advert_metric = true;
trigger_update = true;
}
updateRoute(prte,&rte);
}
else
{ // ignore the longer route
}
}
else if ( prte->seqnum < rte.seqnum )
{ // we've heard a fresher sequence number
// we *must* believe its rt metric
rte.advert_seqnum = true; // we've got a new seqnum to advert
if (rte.metric == prte->last_advertised_metric)
{ // we've just gone back to our old metric
rte.advert_metric = false;
}
else
{ // we're using a metric different from our last advert
rte.advert_metric = true;
}
updateRoute(prte,&rte);
#ifdef TRIGGER_UPDATE_ON_FRESH_SEQNUM
trigger_update = true;
#else
trigger_update = false;
#endif
}
else if ( prte->seqnum > rte.seqnum )
{ // our neighbor has older sequnum info than we do
if (rte.metric == BIG && prte->metric != BIG)
{ // we must go forth and educate this ignorant fellow
// about our more glorious and happy metric
prte->advertise_ok_at = now;
prte->advert_metric = true;
// directly schedule a triggered update now for
// prte, since the other logic only works with rte.*
needTriggeredUpdate(prte,now);
}
else
{ // we don't care about their stale info
}
}
else
{
fprintf(stderr,
"%s DFU: unhandled adding a route entry?\n", __FILE__);
abort();
}
if (trigger_update)
{
prte = table_->GetEntry (rte.dst);
assert(prte != NULL && prte->advertise_ok_at == rte.advertise_ok_at);
needTriggeredUpdate(prte, prte->advertise_ok_at);
}
// see if we can send off any packets we've got queued
if (rte.q && rte.metric != BIG)
{
Packet *queued_p;
while ((queued_p = rte.q->deque()))
// XXX possible loop here
// while ((queued_p = rte.q->deque()))
// Only retry once to avoid looping
// for (int jj = 0; jj < rte.q->length(); jj++){
// queued_p = rte.q->deque();
recv(queued_p, 0); // give the packets to ourselves to forward
// }
delete rte.q;
rte.q = 0;
table_->AddEntry(rte); // record the now zero'd queue
}
} // end of all destination mentioned in routing update packet
// Reschedule the timeout for this neighbor
prte = table_->GetEntry(Address::instance().get_nodeaddr(iph->saddr()));
if (prte)
{
if (prte->timeout_event)
s.cancel (prte->timeout_event);
else
{
prte->timeout_event = new Event ();
}
s.schedule (helper_, prte->timeout_event, min_update_periods_ * perup_);
}
else
{ // If the first thing we hear from a node is a triggered update
// that doesn't list the node sending the update as the first
// thing in the packet (which is disrecommended by the paper)
// we won't have a route to that node already. In order to timeout
// the routes we just learned, we need a harmless route to keep the
// timeout metadata straight.
// Hi there, nice to meet you. I'll make a fake advertisement
bzero(&rte, sizeof(rte));
rte.dst = Address::instance().get_nodeaddr(iph->saddr());
rte.hop = Address::instance().get_nodeaddr(iph->saddr());
rte.metric = 1;
rte.seqnum = 0;
rte.advertise_ok_at = now + 604800; // check back next week... :)
rte.changed_at = now;
rte.new_seqnum_at = now;
rte.wst = wst0_;
rte.timeout_event = new Event ();
rte.q = 0;
updateRoute(NULL, &rte);
s.schedule(helper_, rte.timeout_event, min_update_periods_ * perup_);
}
/*
* Freeing a routing layer packet --> don't need to
* call drop here.
*/
Packet::free (p);
}
int
DSDV_Agent::diff_subnet(int dst)
{
char* dstnet = Address::instance().get_subnetaddr(dst);
if (subnet_ != NULL) {
if (dstnet != NULL) {
if (strcmp(dstnet, subnet_) != 0) {
delete [] dstnet;
return 1;
}
delete [] dstnet;
}
}
//assert(dstnet == NULL);
return 0;
}
void
DSDV_Agent::forwardPacket (Packet * p)
{
hdr_ip *iph = (hdr_ip *) p->access (off_ip_);
Scheduler & s = Scheduler::instance ();
double now = s.clock ();
hdr_cmn *hdrc = HDR_CMN (p);
int dst;
rtable_ent *prte;
// We should route it.
//printf("(%d)-->forwardig pkt\n",myaddr_);
// set direction of pkt to -1 , i.e downward
hdrc->direction() = hdr_cmn::DOWN;
// if the destination is outside mobilenode's domain
// forward it to base_stn node
// Note: pkt is not buffered if route to base_stn is unknown
dst = Address::instance().get_nodeaddr(iph->daddr());
if (diff_subnet(iph->daddr())) {
prte = table_->GetEntry (dst);
if (prte && prte->metric != BIG)
goto send;
//int dst = (node_->base_stn())->address();
dst = node_->base_stn();
prte = table_->GetEntry (dst);
if (prte && prte->metric != BIG)
goto send;
else {
//drop pkt with warning
fprintf(stderr, "warning: Route to base_stn not known: dropping pkt\n");
Packet::free(p);
return;
}
}
prte = table_->GetEntry (dst);
// trace("VDEBUG-RX %d %d->%d %d %d 0x%08x 0x%08x %d %d",
// myaddr_, iph->src(), iph->dst(), hdrc->next_hop_, hdrc->addr_type_,
// hdrc->xmit_failure_, hdrc->xmit_failure_data_,
// hdrc->num_forwards_, hdrc->opt_num_forwards);
if (prte && prte->metric != BIG)
{
//printf("(%d)-have route for dst\n",myaddr_);
goto send;
}
else if (prte)
{ /* must queue the packet */
//printf("(%d)-no route, queue pkt\n",myaddr_);
if (!prte->q)
{
prte->q = new PacketQueue ();
}
prte->q->enque(p);
if (verbose_)
trace ("VBP %.5f _%d_ %d:%d -> %d:%d", now, myaddr_, iph->saddr(),
iph->sport(), iph->daddr(), iph->dport());
while (prte->q->length () > MAX_QUEUE_LENGTH)
drop (prte->q->deque (), DROP_RTR_QFULL);
return;
}
else
{ // Brand new destination
rtable_ent rte;
double now = s.clock();
bzero(&rte, sizeof(rte));
rte.dst = dst;
rte.hop = dst;
rte.metric = BIG;
rte.seqnum = 0;
rte.advertise_ok_at = now + 604800; // check back next week... :)
rte.changed_at = now;
rte.new_seqnum_at = now; // was now + wst0_, why??? XXX -dam
rte.wst = wst0_;
rte.timeout_event = 0;
rte.q = new PacketQueue();
rte.q->enque(p);
assert (rte.q->length() == 1 && 1 <= MAX_QUEUE_LENGTH);
table_->AddEntry(rte);
if (verbose_)
trace ("VBP %.5f _%d_ %d:%d -> %d:%d", now, myaddr_,
iph->saddr(), iph->sport(), iph->daddr(), iph->dport());
return;
}
send:
hdrc->addr_type_ = NS_AF_INET;
hdrc->xmit_failure_ = mac_callback;
hdrc->xmit_failure_data_ = this;
if (prte->metric > 1)
hdrc->next_hop_ = prte->hop;
else
hdrc->next_hop_ = dst;
if (verbose_)
trace ("Routing pkts outside domain: \
VFP %.5f _%d_ %d:%d -> %d:%d", now, myaddr_, iph->saddr(),
iph->sport(), iph->daddr(), iph->dport());
assert (!HDR_CMN (p)->xmit_failure_ ||
HDR_CMN (p)->xmit_failure_ == mac_callback);
target_->recv(p, (Handler *)0);
return;
}
void
DSDV_Agent::sendOutBCastPkt(Packet *p)
{
Scheduler & s = Scheduler::instance ();
//hdr_ip *iph = (hdr_ip*)p->access(off_ip_);
//hdr_cmn *hdrc = (hdr_cmn *)p->access (off_cmn_);
//hdrc->next_hop_ = IP_BROADCAST;
//hdrc->addr_type_ = NS_AF_INET;
//iph->dst() = IP_BROADCAST << Address::instance().nodeshift();
//iph->dport() = 0;
// send out bcast pkt with jitter to avoid sync
s.schedule (target_, p, jitter(DSDV_BROADCAST_JITTER, be_random_));
}
void
DSDV_Agent::recv (Packet * p, Handler *)
{
hdr_ip *iph = (hdr_ip*)p->access(off_ip_);
hdr_cmn *cmh = (hdr_cmn *)p->access (off_cmn_);
int src = Address::instance().get_nodeaddr(iph->saddr());
int dst = cmh->next_hop();
/*
* Must be a packet I'm originating...
*/
if(src == myaddr_ && cmh->num_forwards() == 0) {
/*
* Add the IP Header
*/
cmh->size() += IP_HDR_LEN;
iph->ttl_ = IP_DEF_TTL;
}
/*
* I received a packet that I sent. Probably
* a routing loop.
*/
else if(src == myaddr_) {
drop(p, DROP_RTR_ROUTE_LOOP);
return;
}
/*
* Packet I'm forwarding...
*/
else {
/*
* Check the TTL. If it is zero, then discard.
*/
if(--iph->ttl_ == 0) {
drop(p, DROP_RTR_TTL);
return;
}
}
if ((src != myaddr_) && (iph->dport() == ROUTER_PORT))
{
// XXX disable this feature for mobileIP where
// the MH and FA (belonging to diff domains)
// communicate with each other.
// Drop pkt if rtg update from some other domain:
// if (diff_subnet(iph->src()))
// drop(p, DROP_OUTSIDE_SUBNET);
//else
processUpdate(p);
}
else if ((u_int32_t) dst == IP_BROADCAST &&
(iph->dport() != ROUTER_PORT))
{
if (src == myaddr_) {
// handle brdcast pkt
sendOutBCastPkt(p);
}
else {
// hand it over to the port-demux
port_dmux_->recv(p, (Handler*)0);
}
}
else
{
forwardPacket(p);
}
}
static class DSDVClass:public TclClass
{
public:
DSDVClass ():TclClass ("Agent/DSDV")
{
}
TclObject *create (int, const char *const *)
{
return (new DSDV_Agent ());
}
} class_dsdv;
DSDV_Agent::DSDV_Agent (): Agent (PT_MESSAGE), ll_queue (0), seqno_ (0),
myaddr_ (0), subnet_ (0), node_ (0), port_dmux_(0),
periodic_callback_ (0), be_random_ (1),
use_mac_ (0), verbose_ (1), trace_wst_ (0), lasttup_ (-10),
alpha_ (0.875), wst0_ (6), perup_ (15),
min_update_periods_ (3) // constants
{
table_ = new RoutingTable ();
helper_ = new DSDV_Helper (this);
trigger_handler = new DSDVTriggerHandler(this);
bind_time ("wst0_", &wst0_);
bind_time ("perup_", &perup_);
bind ("use_mac_", &use_mac_);
bind ("be_random_", &be_random_);
bind ("alpha_", &alpha_);
bind ("min_update_periods_", &min_update_periods_);
bind ("verbose_", &verbose_);
bind ("trace_wst_", &trace_wst_);
//DEBUG
address = 0;
}
void
DSDV_Agent::startUp()
{
Time now = Scheduler::instance().clock();
subnet_ = Address::instance().get_subnetaddr(myaddr_);
//DEBUG
address = Address::instance().print_nodeaddr(myaddr_);
//printf("myaddress: %d -> %s\n",myaddr_,address);
rtable_ent rte;
bzero(&rte, sizeof(rte));
rte.dst = myaddr_;
rte.hop = myaddr_;
rte.metric = 0;
rte.seqnum = seqno_;
seqno_ += 2;
rte.advertise_ok_at = 0.0; // can always advert ourselves
rte.advert_seqnum = true;
rte.advert_metric = true;
rte.changed_at = now;
rte.new_seqnum_at = now;
rte.wst = 0;
rte.timeout_event = 0; // Don't time out our localhost :)
rte.q = 0; // Don't buffer pkts for self!
table_->AddEntry (rte);
// kick off periodic advertisments
periodic_callback_ = new Event ();
Scheduler::instance ().schedule (helper_,
periodic_callback_,
jitter (DSDV_STARTUP_JITTER, be_random_));
}
int
DSDV_Agent::command (int argc, const char *const *argv)
{
if (argc == 2)
{
if (strcmp (argv[1], "start-dsdv") == 0)
{
startUp();
return (TCL_OK);
}
else if (strcmp (argv[1], "dumprtab") == 0)
{
Packet *p2 = allocpkt ();
hdr_ip *iph2 = (hdr_ip *) p2->access (off_ip_);
rtable_ent *prte;
printf ("Table Dump %d[%d]\n----------------------------------\n",
iph2->saddr(), iph2->sport());
trace ("VTD %.5f %d:%d\n", Scheduler::instance ().clock (),
iph2->saddr(), iph2->sport());
/*
* Freeing a routing layer packet --> don't need to
* call drop here.
*/
Packet::free (p2);
for (table_->InitLoop (); (prte = table_->NextLoop ());)
output_rte ("\t", prte, this);
printf ("\n");
return (TCL_OK);
}
else if (strcasecmp (argv[1], "ll-queue") == 0)
{
if (!(ll_queue = (PriQueue *) TclObject::lookup (argv[2])))
{
fprintf (stderr, "DSDV_Agent: ll-queue lookup of %s failed\n", argv[2]);
return TCL_ERROR;
}
return TCL_OK;
}
}
else if (argc == 3)
{
if (strcasecmp (argv[1], "addr") == 0) {
int temp;
temp = Address::instance().str2addr(argv[2]);
myaddr_ = temp;
return TCL_OK;
}
TclObject *obj;
if ((obj = TclObject::lookup (argv[2])) == 0)
{
fprintf (stderr, "%s: %s lookup of %s failed\n", __FILE__, argv[1],
argv[2]);
return TCL_ERROR;
}
if (strcasecmp (argv[1], "tracetarget") == 0)
{
tracetarget = (Trace *) obj;
return TCL_OK;
}
else if (strcasecmp (argv[1], "node") == 0) {
node_ = (MobileNode*) obj;
return TCL_OK;
}
else if (strcasecmp (argv[1], "port-dmux") == 0) {
port_dmux_ = (NsObject *) obj;
return TCL_OK;
}
}
return (Agent::command (argc, argv));
}
rtable.cc
/* -*- 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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*/
/* Ported from CMU/Monarch's code, nov'98 -Padma.*/
#include
#include
#include
#include
#include "rtable.h"
static int rtent_trich(const void *a, const void *b) {
nsaddr_t ia = ((const rtable_ent *) a)->dst;
nsaddr_t ib = ((const rtable_ent *) b)->dst;
if (ia > ib) return 1;
if (ib > ia) return -1;
return 0;
}
RoutingTable::RoutingTable() {
// Let's start with a ten element maxelts.
elts = 0;
maxelts = 10;
rtab = new rtable_ent[maxelts];
}
void
RoutingTable::AddEntry(const rtable_ent &ent)
{
rtable_ent *it;
//DEBUG
assert(ent.metric <= BIG);
if ((it = (rtable_ent*) bsearch(&ent, rtab, elts, sizeof(rtable_ent),
rtent_trich))) {
bcopy(&ent,it,sizeof(rtable_ent));
return;
/*
if (it->seqnum < ent.seqnum || it->metric > (ent.metric+em)) {
bcopy(&ent,it,sizeof(rtable_ent));
it->metric += em;
return NEW_ROUTE_SUCCESS_OLDENT;
} else {
return NEW_ROUTE_METRIC_TOO_HIGH;
}
*/
}
if (elts == maxelts) {
rtable_ent *tmp = rtab;
maxelts *= 2;
rtab = new rtable_ent[maxelts];
bcopy(tmp, rtab, elts*sizeof(rtable_ent));
delete tmp;
}
int max;
for (max=0;max= max)
rtab[i+1] = rtab[i--];
//}
bcopy(&ent, &rtab[max], sizeof(rtable_ent));
elts++;
return;
}
void
RoutingTable::InitLoop() {
ctr = 0;
}
rtable_ent *
RoutingTable::NextLoop() {
if (ctr >= elts)
return 0;
return &rtab[ctr++];
}
// Only valid (duh) as long as no new routes are added
int
RoutingTable::RemainingLoop() {
return elts-ctr;
}
rtable_ent *
RoutingTable::GetEntry(nsaddr_t dest) {
rtable_ent ent;
ent.dst = dest;
return (rtable_ent *) bsearch(&ent, rtab, elts, sizeof(rtable_ent),
rtent_trich);
}
dsragent.cc
/* -*- 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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*/
/* Ported from CMU/Monarch's code, nov'98 -Padma.*/
/*
dsragent.cc
requires a radio model such that sendPacket returns true
iff the packet is recieved by the destination node.
$Id: AllCode__.html 980 2002-12-12 09:36:58Z ffilali $
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "path.h"
#include "srpacket.h"
#include "routecache.h"
#include "requesttable.h"
#include "dsragent.h"
/*==============================================================
Declarations and global defintions
------------------------------------------------------------*/
static const int verbose = 0;
static const int verbose_srr = 0;
DSRAgent_List DSRAgent::agthead = { 0 };
Time arp_timeout = 30.0e-3; // (sec) arp request timeout
Time rt_rq_period = 0.5; // (sec) length of one backoff period
Time rt_rq_max_period = 10.0; // (sec) maximum time between rt reqs
#if 0
/* used in route reply holdoffs, which are currently disabled -dam 5/98 */
Time rt_rep_holdoff_period = 3.0e-3; // secs (about 2*process_time)
// to determine how long to sit on our rt reply we pick a number
// U(O.0,rt_rep_holdoff_period) + (our route length-1)*rt_rep_holdoff
#endif // 0
Time grat_hold_down_time = 1.0; // (sec) min time between grat replies for
// same route
Time max_err_hold = 1.0; // (sec)
// maximum time between when we recv a route error told to us, and when we
// transmit a propagating route request that can carry that data. used to
// keep us from propagating stale route error data
/*************** selectors ******************/
bool dsragent_snoop_forwarded_errors = true;
// give errors we forward to our cache?
bool dsragent_snoop_source_routes = true;
// should we snoop on any source routes we see?
bool dsragent_reply_only_to_first_rtreq = false;
// should we only respond to the first route request we receive from a host?
bool dsragent_propagate_last_error = true;
// should we take the data from the last route error msg sent to us
// and propagate it around on the next propagating route request we do?
// this is aka grat route error propagation
bool dsragent_send_grat_replies = true;
// should we send gratuitous replies to effect route shortening?
bool dsragent_salvage_with_cache = true;
// should we consult our cache for a route if we get a xmitfailure
// and salvage the packet using the route if possible
bool dsragent_use_tap = true;
// should we listen to a promiscuous tap?
bool dsragent_reply_from_cache_on_propagating = true;
// should we consult the route cache before propagating rt req's and
// answer if possible?
bool dsragent_ring_zero_search = true;
// should we send a non-propagating route request as the first action
// in each route discovery action?
// NOTE: to completely turn off replying from cache, you should
// set both dsragent_ring_zero_search and
// dsragent_reply_from_cache_on_propagating to false
bool dsragent_dont_salvage_bad_replies = true;
// if we have an xmit failure on a packet, and the packet contains a
// route reply, should we scan the reply to see if contains the dead link?
// if it does, we won't salvage the packet unless there's something aside
// from a reply in it (in which case we salvage, but cut out the rt reply)
bool dsragent_require_bi_routes = true;
// do we need to have bidirectional source routes?
// [XXX this flag doesn't control all the behaviors and code that assume
// bidirectional links -dam 5/14/98]
#if 0
bool lsnode_holdoff_rt_reply = true;
// if we have a cached route to reply to route_request with, should we
// hold off and not send it for a while?
bool lsnode_require_use = true;
// do we require ourselves to hear a route requestor use a route
// before we withold our route, or is merely hearing another (better)
// route reply enough?
#endif
/*
Our strategy is as follows:
- it's only worth discovering bidirectional routes, since all data
paths will have to have to be bidirectional for 802.11 ACKs to work
- reply to all route requests for us that we recv (not just the first one)
but reply to them by reversing the route and unicasting. don't do
a route request (since that will end up returning the requestor lots of
routes that are potentially unidirectional). By reversing the discovered
route for the route reply, only routes that are bidirectional will make it
back the original requestor
- once a packet goes into the sendbuffer, it can't be piggybacked on a
route request. the code assumes that the only thing that removes
packets from the send buff is the StickPktIn routine, or the route reply
arrives routine
*/
/*===========================================================================
SendBuf management and helpers
---------------------------------------------------------------------------*/
//void
//SendBufferTimer::expire(Event *e)
//{
// a_->sendBufferCheck();
// resched(BUFFER_CHECK + BUFFER_CHECK * (double) ((int) e>>5 & 0xff) / 256.0);
//}
void
SendBufferTimer::expire(Event *e)
{
a_->sendBufferCheck();
resched(BUFFER_CHECK + BUFFER_CHECK * Random::uniform(1.0));
}
void
DSRAgent::dropSendBuff(SRPacket &p)
// log p as being dropped by the sendbuffer in DSR agent
{
trace("Ssb %.5f _%s_ dropped %s -> %s", Scheduler::instance().clock(),
net_id.dump(), p.src.dump(), p.dest.dump());
drop(p.pkt, DROP_RTR_QTIMEOUT);
p.pkt = 0;
p.route.reset();
}
void
DSRAgent::stickPacketInSendBuffer(SRPacket& p)
{
Time min = DBL_MAX;
int min_index = 0;
int c;
if (verbose)
trace("Sdebug %.5f _%s_ stuck into send buff %s -> %s",
Scheduler::instance().clock(),
net_id.dump(), p.src.dump(), p.dest.dump());
for (c = 0 ; c < SEND_BUF_SIZE ; c ++)
if (send_buf[c].p.pkt == NULL)
{
send_buf[c].t = Scheduler::instance().clock();
send_buf[c].p = p;
return;
}
else if (send_buf[c].t < min)
{
min = send_buf[c].t;
min_index = c;
}
// kill somebody
dropSendBuff(send_buf[min_index].p);
send_buf[min_index].t = Scheduler::instance().clock();
send_buf[min_index].p = p;
}
void
DSRAgent::sendBufferCheck()
// see if any packets in send buffer need route requests sent out
// for them, or need to be expired
{ // this is called about once a second. run everybody through the
// get route for pkt routine to see if it's time to do another
// route request or what not
int c;
for (c = 0 ; c SEND_TIMEOUT)
{
dropSendBuff(send_buf[c].p);
send_buf[c].p.pkt = 0;
continue;
}
#ifdef DEBUG
trace("Sdebug %.5f _%s_ checking for route for dst %s",
Scheduler::instance().clock(), net_id.dump(),
send_buf[c].p.dest.dump());
#endif
handlePktWithoutSR(send_buf[c].p, true);
#ifdef DEBUG
if (send_buf[c].p.pkt == NULL)
trace("Sdebug %.5f _%s_ sendbuf pkt to %s liberated by handlePktWOSR",
Scheduler::instance().clock(), net_id.dump(),
send_buf[c].p.dest.dump());
#endif
}
}
/*==============================================================
Route Request backoff
------------------------------------------------------------*/
static bool
BackOffTest(Entry *e, Time time)
// look at the entry and decide if we can send another route
// request or not. update entry as well
{
Time next = ((Time) (0x1<<(e->rt_reqs_outstanding*2))) * rt_rq_period;
if (next > rt_rq_max_period) next = rt_rq_max_period;
if (next + e->last_rt_req > time) return false;
// don't let rt_reqs_outstanding overflow next on the LogicalShiftsLeft's
if (e->rt_reqs_outstanding < 15) e->rt_reqs_outstanding++;
e->last_rt_req = time;
return true;
}
/*===========================================================================
DSRAgent OTcl linkage
---------------------------------------------------------------------------*/
static class DSRAgentClass : public TclClass {
public:
DSRAgentClass() : TclClass("Agent/DSRAgent") {}
TclObject* create(int, const char*const*) {
return (new DSRAgent);
}
} class_DSRAgent;
static class BS_DSRAgentClass : public TclClass {
public:
BS_DSRAgentClass() : TclClass("Agent/DSRAgent/BS_DSRAgent") {}
TclObject* create(int, const char*const*) {
return (new BS_DSRAgent);
}
} class_BS_DSRAgent;
/* ***************************************************
* Comment on compilation error at line 297, dsragent.cc
* The following compiler error has been seen in sunos and solaris
* for compiler version egcs-2.90.27 980315 (egcs-1.0.2 release)
* dsr/dsragent.cc: In method `SendBufEntry::SendBufEntry()':
* dsr/dsragent.cc:297: Internal compiler error.
* dsr/dsragent.cc:297: Please submit a full bug report to
* `egcs-bugs@cygnus.com'.
*** Error code 1
* Solution: Use GCC or more recent version of C++.
(the problem seems to go away with egcs 1.1.2 release).
* Padma Haldar, 09/30/99.
***********************************************************/
/*===========================================================================
DSRAgent methods
---------------------------------------------------------------------------*/
DSRAgent::DSRAgent(): Agent(PT_DSR),request_table(128), \
route_cache(NULL), send_buf_timer(this)
{
port_dmux_ = 0;
int c;
route_request_num = 1;
route_cache = makeRouteCache();
for (c = 0 ; c < RTREP_HOLDOFF_SIZE ; c++)
rtrep_holdoff[c].requested_dest = invalid_addr;
num_heldoff_rt_replies = 0;
target_ = 0;
logtarget = 0;
grat_hold_victim = 0;
for (c = 0; c < RTREP_HOLDOFF_SIZE ; c++)
grat_hold[c].p.reset();
bind("off_SR_", &off_sr_);
bind("off_ll_", &off_ll_);
bind("off_mac_", &off_mac_);
bind("off_ip_", &off_ip_);
LIST_INSERT_HEAD(&agthead, this, link);
route_error_held = false;
}
DSRAgent::~DSRAgent()
{
fprintf(stderr,"DFU: Don't do this! I haven't figured out ~DSRAgent\n");
exit(-1);
}
void
DSRAgent::Terminate()
{
int c;
for (c = 0 ; c < SEND_BUF_SIZE ; c++) {
if (send_buf[c].p.pkt) {
drop(send_buf[c].p.pkt, DROP_END_OF_SIMULATION);
send_buf[c].p.pkt = 0;
}
}
}
void
DSRAgent::testinit()
{
struct hdr_sr hsr;
if (net_id == ID(1,::IP))
{
printf("adding route to 1\n");
hsr.init();
hsr.append_addr( 1, NS_AF_INET );
hsr.append_addr( 2, NS_AF_INET );
hsr.append_addr( 3, NS_AF_INET );
hsr.append_addr( 4, NS_AF_INET );
route_cache->addRoute(Path(hsr.addrs, hsr.num_addrs()), 0.0, ID(1,::IP));
}
if (net_id == ID(3,::IP))
{
printf("adding route to 3\n");
hsr.init();
hsr.append_addr( 3, NS_AF_INET );
hsr.append_addr( 2, NS_AF_INET );
hsr.append_addr( 1, NS_AF_INET );
route_cache->addRoute(Path(hsr.addrs, hsr.num_addrs()), 0.0, ID(3,::IP));
}
}
int
DSRAgent::command(int argc, const char*const* argv)
{
TclObject *obj;
if (argc == 2)
{
if (strcasecmp(argv[1], "testinit") == 0)
{
testinit();
return TCL_OK;
}
if (strcasecmp(argv[1], "reset") == 0)
{
Terminate();
return Agent::command(argc, argv);
}
if (strcasecmp(argv[1], "check-cache") == 0)
{
return route_cache->command(argc, argv);
}
if (strcasecmp(argv[1], "startdsr") == 0)
{
if (ID(1,::IP) == net_id)
{ // log the configuration parameters of the dsragent
trace("Sconfig %.5f tap: %s snoop: rts? %s errs? %s",
Scheduler::instance().clock(),
dsragent_use_tap ? "on" : "off",
dsragent_snoop_source_routes ? "on" : "off",
dsragent_snoop_forwarded_errors ? "on" : "off");
trace("Sconfig %.5f salvage: %s !bd replies? %s",
Scheduler::instance().clock(),
dsragent_salvage_with_cache ? "on" : "off",
dsragent_dont_salvage_bad_replies ? "on" : "off");
trace("Sconfig %.5f grat error: %s grat reply: %s",
Scheduler::instance().clock(),
dsragent_propagate_last_error ? "on" : "off",
dsragent_send_grat_replies ? "on" : "off");
trace("Sconfig %.5f $reply for props: %s ring 0 search: %s",
Scheduler::instance().clock(),
dsragent_reply_from_cache_on_propagating ? "on" : "off",
dsragent_ring_zero_search ? "on" : "off");
}
// cheap source of jitter
send_buf_timer.sched(BUFFER_CHECK
+ BUFFER_CHECK * Random::uniform(1.0));
return route_cache->command(argc,argv);
}
}
else if(argc == 3)
{
if (strcasecmp(argv[1], "addr") == 0)
{
int temp;
temp = Address::instance().str2addr(argv[2]);
net_id = ID(temp, ::IP);
route_cache->net_id = net_id;
return TCL_OK;
}
else if(strcasecmp(argv[1], "mac-addr") == 0)
{
MAC_id = ID(atoi(argv[2]), ::MAC);
route_cache->MAC_id = MAC_id;
return TCL_OK;
}
if( (obj = TclObject::lookup(argv[2])) == 0)
{
fprintf(stderr, "DSRAgent: %s lookup of %s failed\n", argv[1],
argv[2]);
return TCL_ERROR;
}
if (strcasecmp(argv[1], "log-target") == 0 )
{
logtarget = (Trace*) obj;
return route_cache->command(argc, argv);
}
else if (strcasecmp(argv[1], "tracetarget") == 0 )
{
logtarget = (Trace*) obj;
return route_cache->command(argc, argv);
}
else if (strcasecmp(argv[1], "install-tap") == 0)
{
Mac *m = (Mac*) obj;
m->installTap(this);
return TCL_OK;
}
else if (strcasecmp(argv[1], "node") == 0)
{
node_ = (MobileNode *) obj;
return TCL_OK;
}
else if (strcasecmp (argv[1], "port-dmux") == 0)
{
port_dmux_ = (NsObject *) obj;
return TCL_OK;
}
}
else if (argc == 4)
{
if (strcasecmp(argv[1], "add-ll") == 0)
{
if( (obj = TclObject::lookup(argv[2])) == 0) {
fprintf(stderr, "DSRAgent: %s lookup of %s failed\n", argv[1],
argv[2]);
return TCL_ERROR;
}
ll = (NsObject*) obj;
if( (obj = TclObject::lookup(argv[3])) == 0) {
fprintf(stderr, "DSRAgent: %s lookup of %s failed\n", argv[1],
argv[3]);
return TCL_ERROR;
}
ifq = (PriQueue *) obj;
return TCL_OK;
}
}
return Agent::command(argc, argv);
}
int
DSRAgent::diff_subnet(ID dest, ID myid)
{
int dst = dest.addr;
int id = myid.addr;
char* dstnet = Address::instance().get_subnetaddr(dst);
char * subnet = Address::instance().get_subnetaddr(id);
if (subnet != NULL) {
if (dstnet != NULL) {
if (strcmp(dstnet, subnet) != 0) {
delete [] dstnet;
return 1;
}
delete [] dstnet;
}
delete [] subnet;
}
assert(dstnet == NULL);
return 0;
}
void
DSRAgent::sendOutBCastPkt(Packet *p)
{
// no jitter required
Scheduler::instance().schedule(ll, p, 0.0);
}
void
DSRAgent::recv(Packet* packet, Handler*)
/* handle packets with a MAC destination address of this host, or
the MAC broadcast addr */
{
hdr_sr *srh = (hdr_sr*)packet->access(off_sr_);
hdr_ip *iph = (hdr_ip*)packet->access(off_ip_);
hdr_cmn *cmh = (hdr_cmn*)packet->access(off_cmn_);
assert(cmh->size() >= 0);
SRPacket p(packet, srh);
p.dest = ID((Address::instance().get_nodeaddr(iph->daddr())),::IP);
p.src = ID((Address::instance().get_nodeaddr(iph->saddr())),::IP);
assert(logtarget != 0);
if (srh->valid() != 1) {
unsigned int dst = cmh->next_hop();
if (dst == IP_BROADCAST) {
// extensions for mobileIP --Padma, 04/99.
// Brdcast pkt - treat differently
if (p.src == net_id)
// I have originated this pkt
sendOutBCastPkt(packet);
else
//hand it over to port-dmux
port_dmux_->recv(packet, (Handler*)0);
} else {
// this must be an outgoing packet, it doesn't have a SR header on it
srh->init(); // give packet an SR header now
cmh->size() += IP_HDR_LEN; // add on IP header size
if (verbose)
trace("S %.9f _%s_ originating %s -> %s",
Scheduler::instance().clock(), net_id.dump(), p.src.dump(),
p.dest.dump());
handlePktWithoutSR(p, false);
goto done;
}
}
else if (srh->valid() == 1)
{
if (p.dest == net_id || p.dest == IP_broadcast)
{ // this packet is intended for us
handlePacketReceipt(p);
goto done;
}
// should we check to see if it's an error packet we're handling
// and if so call processBrokenRouteError to snoop
if (dsragent_snoop_forwarded_errors && srh->route_error())
{
processBrokenRouteError(p);
}
if (srh->route_request())
{ // propagate a route_request that's not for us
// DISABLE:drop rte-req if from outside subnet
// if (diff_subnet(p.src,net_id)) {
// // from outside our subnet, drop pkt
// Packet::free(p.pkt);
// p.pkt = 0;
// return;
// }
handleRouteRequest(p);
}
else
{ // we're not the intended final recpt, but we're a hop
handleForwarding(p);
}
}
else {
// some invalid pkt has reached here
fprintf(stderr,"dsragent: Error-received Invalid pkt!\n");
Packet::free(p.pkt);
p.pkt =0; // drop silently
}
done:
assert(p.pkt == 0);
p.pkt = 0;
return;
}
/*===========================================================================
handlers for each class of packet
---------------------------------------------------------------------------*/
void
DSRAgent::handlePktWithoutSR(SRPacket& p, bool retry)
/* obtain a source route to p's destination and send it off.
this should be a retry if the packet is already in the sendbuffer */
{
hdr_sr *srh = (hdr_sr*)p.pkt->access(off_sr_);
assert(srh->valid());
if (p.dest == net_id)
{ // it doesn't need a source route, 'cause it's for us
handlePacketReceipt(p);
return;
}
// Extensions for wired cum wireless simulation mode
//if pkt dst outside my subnet, route to base_stn
ID dest;
if (diff_subnet(p.dest,net_id)) {
dest = ID(node_->base_stn(),::IP);
if (dest == net_id)
// Iam the base-station
dest = p.dest;
}
else
dest = p.dest;
if (route_cache->findRoute(dest, p.route, 1))
{ // we've got a route...
if (verbose)
trace("S$hit %.5f _%s_ %s -> %s %s",
Scheduler::instance().clock(), net_id.dump(),
p.src.dump(), dest.dump(), p.route.dump());
sendOutPacketWithRoute(p, true);
return;
} // end if we have a route
else
{ // we don't have a route...
if (verbose)
trace("S$miss %.5f _%s_ %s -> %s",
Scheduler::instance().clock(), net_id.dump(),
net_id.dump(), p.dest.dump());
getRouteForPacket(p, dest, retry);
return;
} // end of we don't have a route
}
void
DSRAgent::handlePacketReceipt(SRPacket& p)
/* Handle a packet destined to us */
{
hdr_sr *srh = (hdr_sr*)p.pkt->access(off_sr_);
//hdr_ip *iph = HDR_IP(p.pkt);
if (srh->route_reply())
{ // we got a route_reply piggybacked on a route_request
// accept the new source route before we do anything else
// (we'll send off any packet's we have queued and waiting)
acceptRouteReply(p);
}
if (srh->route_request())
{
if (dsragent_reply_only_to_first_rtreq && ignoreRouteRequestp(p))
{ //we only respond to the first route request
// we receive from a host
Packet::free(p.pkt); // drop silently
p.pkt = 0;
return;
}
else
{
request_table.insert(p.src, p.src, srh->rtreq_seq());
returnSrcRouteToRequestor(p);
}
}
if (srh->route_error())
{ // register the dead route
processBrokenRouteError(p);
}
/* give the data in the packet to our higher layer (our port dmuxer, most
likely) */
handPktToDmux(p);
}
void
DSRAgent::handPktToDmux(SRPacket &p)
{
// hdr_ip *iph = HDR_IP(p.pkt);
assert(p.dest == net_id || p.dest == MAC_id);
#if 0
if (iph->dport() == 255) {
int mask = Address::instance().portmask();
int shift = Address::instance().portshift();
iph->daddr() = ((iph->dport() & mask) << shift) | ((~(mask) << shift) & iph->dst());
}
#endif
target_->recv(p.pkt, (Handler*)0);
p.pkt = 0;
}
void
BS_DSRAgent::handPktToDmux(SRPacket &p)
{
//since the demux has handed this pkt to the DSRagent,
// silently drop this pkt.
Packet::free(p.pkt);
p.pkt = 0;
}
void
DSRAgent::handleForwarding(SRPacket &p)
/* forward packet on to next host in source route,
snooping as appropriate */
{
hdr_sr *srh = (hdr_sr*)p.pkt->access(off_sr_);
hdr_cmn *cmh = (hdr_cmn*)p.pkt->access(off_cmn_);
trace("SF %.9f _%s_ --- %d [%s -> %s] %s",
Scheduler::instance().clock(), net_id.dump(), cmh->uid(),
p.src.dump(), p.dest.dump(), srh->dump());
// first make sure we are the ``current'' host along the source route.
// if we're not, the previous node set up the source route incorrectly.
assert(p.route[p.route.index()] == net_id
|| p.route[p.route.index()] == MAC_id);
if (p.route.index() >= p.route.length())
{
fprintf(stderr,"dfu: ran off the end of a source route\n");
trace("SDFU: ran off the end of a source route\n");
drop(p.pkt, DROP_RTR_ROUTE_LOOP);
p.pkt = 0;
// maybe we should send this packet back as an error...
return;
}
// if there's a source route, maybe we should snoop it too
if (dsragent_snoop_source_routes)
route_cache->noticeRouteUsed(p.route, Scheduler::instance().clock(),
net_id);
// sendOutPacketWithRoute will add in the size of the src hdr, so
// we have to subtract it out here
struct hdr_cmn *ch = HDR_CMN(p.pkt);
ch->size() -= srh->size();
// now forward the packet...
sendOutPacketWithRoute(p, false);
}
void
DSRAgent::handleRouteRequest(SRPacket &p)
/* process a route request that isn't targeted at us */
{
hdr_sr *srh = (hdr_sr*)p.pkt->access(off_sr_);
assert (srh->route_request());
if (ignoreRouteRequestp(p))
{
if (verbose_srr)
trace("SRR %.5f _%s_ dropped %s #%d (ignored)",
Scheduler::instance().clock(), net_id.dump(), p.src.dump(),
srh->rtreq_seq());
Packet::free(p.pkt); // pkt is a route request we've already processed
p.pkt = 0;
return; // drop silently
}
// we're going to process this request now, so record the req_num
request_table.insert(p.src, p.src, srh->rtreq_seq());
/* - if it's a Ring 0 search, check the rt$ for a reply and use it if
possible. There's not much point in doing Ring 0 search if you're
not going to check the cache. See the comment about turning off all
reply from cache behavior near the definition of d_r_f_c_o_p (if your
workload had really good spatial locality, it might still make
sense 'cause your target is probably sitting next to you)
- if reply from cache is on, check the cache and reply if possible
- otherwise, just propagate if possible. */
if ((srh->max_propagation() == 0 || dsragent_reply_from_cache_on_propagating)
&& replyFromRouteCache(p))
return; // all done
// does the orginator want us to propagate?
if (p.route.length() > srh->max_propagation())
{ // no propagation
if (verbose_srr)
trace("SRR %.5f _%s_ dropped %s #%d (prop limit exceeded)",
Scheduler::instance().clock(), net_id.dump(), p.src.dump(),
srh->rtreq_seq());
Packet::free(p.pkt); // pkt isn't for us, and isn't data carrying
p.pkt = 0;
return;
}
// can we propagate?
if (p.route.full())
{ // no propagation
trace("SRR %.5f _%s_ dropped %s #%d (SR full)",
Scheduler::instance().clock(), net_id.dump(), p.src.dump(),
srh->rtreq_seq());
/* pkt is a rt req, even if data carrying, we don't want to log
the drop using drop() since many nodes could be dropping the
packet in this fashion */
Packet::free(p.pkt);
p.pkt = 0;
return;
}
// add ourselves to the source route
p.route.appendToPath(net_id);
if (verbose_srr)
trace("SRR %.5f _%s_ rebroadcast %s #%d ->%s %s",
Scheduler::instance().clock(), net_id.dump(), p.src.dump(),
srh->rtreq_seq(), p.dest.dump(), p.route.dump());
sendOutPacketWithRoute(p, false);
return;
}
/*===========================================================================
Helpers
---------------------------------------------------------------------------*/
bool
DSRAgent::ignoreRouteRequestp(SRPacket &p)
// should we ignore this route request?
{
hdr_sr *srh = (hdr_sr*)p.pkt->access(off_sr_);
if (request_table.get(p.src) >= srh->rtreq_seq())
{ // we've already processed a copy of this reqest so
// we should drop the request silently
return true;
}
if (p.route.member(net_id,MAC_id))
{ // we're already on the route, drop silently
return true;
}
if (p.route.full())
{ // there won't be room for us to put our address into
// the route
// so drop silently - sigh, so close, and yet so far...
// Note that since we don't record the req_id of this message yet,
// we'll process the request if it gets to us on a shorter path
return true;
}
return false;
}
bool
DSRAgent::replyFromRouteCache(SRPacket &p)
/* - see if can reply to this route request from our cache
if so, do it and return true, otherwise, return false
- frees or hands off p iff returns true */
{
Path rest_of_route;
Path complete_route = p.route;
/* we shouldn't yet be on on the pkt's current source route */
assert(!p.route.member(net_id, MAC_id));
// do we have a cached route the target?
/* XXX what if we have more than 1? (and one is legal for reply from
cache and one isn't?) 1/28/97 -dam */
if (!route_cache->findRoute(p.dest, rest_of_route, 0))
{ // no route => we're done
return false;
}
/* but we should be on on the remainder of the route (and should be at
the start of the route */
assert(rest_of_route[0] == net_id || rest_of_route[0] == MAC_id);
if (rest_of_route.length() + p.route.length() > MAX_SR_LEN)
return false; // too long to work with...
// add our suggested completion to the route so far
complete_route.appendPath(rest_of_route);
// call compressPath to remove any double backs
::compressPath(complete_route);
if (!complete_route.member(net_id, MAC_id))
{ // we're not on the suggested route, so we can't return it
return false;
}
// if there is any other information piggybacked into the
// route request pkt, we need to forward it on to the dst
hdr_cmn *cmh = (hdr_cmn*)p.pkt->access(off_cmn_);
hdr_sr *srh = (hdr_sr*)p.pkt->access(off_sr_);
int request_seqnum = srh->rtreq_seq();
if (PT_DSR != cmh->ptype() // there's data
|| srh->route_reply()
|| (srh->route_error() &&
srh->down_links()[srh->num_route_errors()-1].tell_addr
!= GRAT_ROUTE_ERROR))
{ // must forward the packet on
SRPacket p_copy = p;
p.pkt = 0;
srh->route_request() = 0;
p_copy.route = complete_route;
p_copy.route.setIterator(p.route.length());
assert(p.route[p.route.index()] == net_id);
if (verbose) trace("Sdebug %.9f _%s_ splitting %s to %s",
Scheduler::instance().clock(), net_id.dump(),
p.route.dump(), p_copy.route.dump());
sendOutPacketWithRoute(p_copy,false);
}
else
{
Packet::free(p.pkt); // free the rcvd rt req before making rt reply
p.pkt = 0;
}
// make up and send out a route reply
p.route.appendToPath(net_id);
p.route.reverseInPlace();
route_cache->addRoute(p.route, Scheduler::instance().clock(), net_id);
p.dest = p.src;
p.src = net_id;
p.pkt = allocpkt();
hdr_ip *iph = (hdr_ip*)p.pkt->access(off_ip_);
iph->saddr() = Address::instance().create_ipaddr(p.src.addr,RT_PORT);
iph->sport() = RT_PORT;
iph->daddr() = Address::instance().create_ipaddr(p.dest.addr,RT_PORT);
iph->dport() = RT_PORT;
iph->ttl() = 255;
srh = (hdr_sr*)p.pkt->access(off_sr_);
srh->init();
for (int i = 0 ; i < complete_route.length() ; i++)
complete_route[i].fillSRAddr(srh->reply_addrs()[i]);
srh->route_reply_len() = complete_route.length();
srh->route_reply() = 1;
// propagate the request sequence number in the reply for analysis purposes
srh->rtreq_seq() = request_seqnum;
hdr_cmn *cmnh = (hdr_cmn*)p.pkt->access(off_cmn_);
cmnh->ptype() = PT_DSR;
cmnh->size() = IP_HDR_LEN;
trace("SRR %.9f _%s_ cache-reply-sent %s -> %s #%d (len %d) %s",
Scheduler::instance().clock(), net_id.dump(),
p.src.dump(), p.dest.dump(), request_seqnum, complete_route.length(),
complete_route.dump());
sendOutPacketWithRoute(p, true);
return true;
}
void
DSRAgent::sendOutPacketWithRoute(SRPacket& p, bool fresh, Time delay)
// take packet and send it out, packet must a have a route in it
// return value is not very meaningful
// if fresh is true then reset the path before using it, if fresh
// is false then our caller wants us use a path with the index
// set as it currently is
{
hdr_sr *srh = (hdr_sr*)p.pkt->access(off_sr_);
hdr_cmn *cmnh = (hdr_cmn*)p.pkt->access(off_cmn_);
assert(srh->valid());
assert(cmnh->size() > 0);
ID dest;
if (diff_subnet(p.dest,net_id)) {
//dest = ID(node_->base_stn()->address(),::IP);
dest = ID(node_->base_stn(),::IP);
if (dest == net_id)
// Iam the base-station
dest = p.dest;
}
else
dest = p.dest;
if (dest == net_id)
{ // it doesn't need to go on the wire, 'cause it's for us
recv(p.pkt, (Handler *) 0);
p.pkt = 0;
return;
}
if (fresh)
{
p.route.resetIterator();
if (verbose && !srh->route_request())
{
trace("SO %.9f _%s_ originating %s %s",
Scheduler::instance().clock(),
net_id.dump(), packet_info.name(cmnh->ptype()), p.route.dump());
}
}
p.route.fillSR(srh);
cmnh->size() += srh->size();
// set direction of pkt to DOWN , i.e downward
cmnh->direction() = hdr_cmn::DOWN;
if (srh->route_request())
{ // broadcast forward
cmnh->xmit_failure_ = 0;
cmnh->next_hop() = MAC_BROADCAST;
cmnh->addr_type() = NS_AF_ILINK;
}
else
{ // forward according to source route
cmnh->xmit_failure_ = XmitFailureCallback;
cmnh->xmit_failure_data_ = (void *) this;
cmnh->next_hop() = srh->get_next_addr();
cmnh->addr_type() = srh->get_next_type();
srh->cur_addr() = srh->cur_addr() + 1;
}
/* put route errors at the head of the ifq somehow? -dam 4/13/98 */
// make sure we aren't cycling packets
//assert(p.pkt->incoming == 0); // this is an outgoing packet
//assert(cmnh->direction() == hdr_cmn::DOWN);
if (ifq->length() > 25)
trace("SIFQ %.5f _%s_ len %d",
Scheduler::instance().clock(), net_id.dump(), ifq->length());
// off it goes!
if (srh->route_request())
{ // route requests need to be jittered a bit
Scheduler::instance().schedule(ll, p.pkt,
Random::uniform(RREQ_JITTER) + delay);
}
else
{ // no jitter required
Scheduler::instance().schedule(ll, p.pkt, delay);
}
p.pkt = NULL; /* packet sent off */
}
void
DSRAgent::getRouteForPacket(SRPacket &p, ID dest, bool retry)
/* try to obtain a route for packet
pkt is freed or handed off as needed, unless retry == true
in which case it is not touched */
{
// since we'll commonly be only one hop away, we should
// arp first before route discovery as an optimization...
Entry *e = request_table.getEntry(dest);
Time time = Scheduler::instance().clock();
/* for now, no piggybacking at all, queue all pkts */
if (!retry)
{
stickPacketInSendBuffer(p);
p.pkt = 0; // pkt is handled for now (it's in sendbuffer)
}
#if 0
/* pre 4/13/98 logic -dam removed b/c it seemed more complicated than
needed since we're not doing piggybacking and we're returning
route replies via a reversed route (the copy in this code is
critical if we need to piggyback route replies on the route request to
discover the return path) */
/* make the route request packet */
SRPacket rrp = p;
rrp.pkt = p.pkt->copy();
hdr_sr *srh = (hdr_sr*)rrp.pkt->access(off_sr_);
hdr_ip *iph = (hdr_ip*)rrp.pkt->access(off_ip_);
hdr_cmn *cmnh = (hdr_cmn*)rrp.pkt->access(off_cmn_);
iph->daddr() = Address::instance().create_ipaddr(dest.getNSAddr_t(),RT_PORT);
iph->dport() = RT_PORT;
iph->saddr() = Address::instance().create_ipaddr(net_id.getNSAddr_t(),RT_PORT);
iph->sport() = RT_PORT;
cmnh->ptype() = PT_DSR;
cmnh->size() = size_;
cmnh->num_forwards() = 0;
#endif
/* make the route request packet */
SRPacket rrp;
rrp.dest = dest;
rrp.src = net_id;
rrp.pkt = allocpkt();
hdr_sr *srh = (hdr_sr*)rrp.pkt->access(off_sr_);
hdr_ip *iph = (hdr_ip*)rrp.pkt->access(off_ip_);
hdr_cmn *cmnh = (hdr_cmn*)rrp.pkt->access(off_cmn_);
iph->daddr() = Address::instance().create_ipaddr(dest.getNSAddr_t(),RT_PORT);
iph->dport() = RT_PORT;
iph->saddr() =
Address::instance().create_ipaddr(net_id.getNSAddr_t(),RT_PORT);
iph->sport() = RT_PORT;
cmnh->ptype() = PT_DSR;
cmnh->size() = size_ + IP_HDR_LEN; // add in IP header
cmnh->num_forwards() = 0;
srh->init();
if (BackOffTest(e, time))
{ // it's time to start another route request cycle
if (dsragent_ring_zero_search)
{ // do a ring zero search
e->last_type = LIMIT0;
sendOutRtReq(rrp, 0);
}
else
{ // do a propagating route request right now
e->last_type = UNLIMIT;
sendOutRtReq(rrp, MAX_SR_LEN);
}
e->last_arp = time;
}
else if (LIMIT0 == e->last_type && (time - e->last_arp) > arp_timeout)
{ // try propagating rt req since we haven't heard back from limited one
e->last_type = UNLIMIT;
sendOutRtReq(rrp, MAX_SR_LEN);
}
else
{ // it's not time to send another route request...
if (!retry && verbose_srr)
trace("SRR %.5f _%s_ RR-not-sent %s -> %s",
Scheduler::instance().clock(),
net_id.dump(), rrp.src.dump(), rrp.dest.dump());
Packet::free(rrp.pkt); // dump the route request packet we made up
rrp.pkt = 0;
return;
}
}
void
DSRAgent::sendOutRtReq(SRPacket &p, int max_prop)
// turn p into a route request and launch it, max_prop of request is
// set as specified
// p.pkt is freed or handed off
{
hdr_sr *srh = (hdr_sr*)p.pkt->access(off_sr_);
assert(srh->valid());
srh->route_request() = 1;
srh->rtreq_seq() = route_request_num++;
srh->max_propagation() = max_prop;
p.route.reset();
p.route.appendToPath(net_id);
if (dsragent_propagate_last_error && route_error_held
&& Scheduler::instance().clock() - route_error_data_time < max_err_hold)
{
assert(srh->num_route_errors() < MAX_ROUTE_ERRORS);
srh->route_error() = 1;
link_down *deadlink = &(srh->down_links()[srh->num_route_errors()]);
deadlink->addr_type = NS_AF_INET;
deadlink->from_addr = err_from.getNSAddr_t();
deadlink->to_addr = err_to.getNSAddr_t();
deadlink->tell_addr = GRAT_ROUTE_ERROR;
srh->num_route_errors() += 1;
/*
* Make sure that the Route Error gets on a propagating request.
*/
if(max_prop > 0) route_error_held = false;
}
trace("SRR %.5f _%s_ new-request %d %s #%d -> %s",
Scheduler::instance().clock(), net_id.dump(),
max_prop, p.src.dump(), srh->rtreq_seq(), p.dest.dump());
sendOutPacketWithRoute(p, false);
}
// XXX not used currently
void
DSRAgent::handleRteRequestForOutsideDomain(SRPacket& p)
{
/* process a route request for a outside-domain dst*/
hdr_sr *srh = (hdr_sr*)p.pkt->access(off_sr_);
assert (srh->route_request());
if (dsragent_reply_only_to_first_rtreq && ignoreRouteRequestp(p))
{ //we only respond to the first route request
// we receive from a host
Packet::free(p.pkt); // drop silently
p.pkt = 0;
return;
}
else
{ // we're going to process this request now, so record the req_num
request_table.insert(p.src, p.src, srh->rtreq_seq());
returnSrcRteForOutsideDomainToRequestor(p);
}
if (srh->route_error())
{ // register the dead route
processBrokenRouteError(p);
}
}
// XXX not used currently
void
DSRAgent::returnSrcRteForOutsideDomainToRequestor(SRPacket &p)
{
// take the route in p, add us and the destination outside subnet
// to the end of it and return the route to the sender of p
// doesn't free p.pkt
hdr_sr *old_srh = (hdr_sr*)p.pkt->access(off_sr_);
if (p.route.full())
return; // alas, the route would be to long once we add ourselves
SRPacket p_copy = p;
p_copy.pkt = allocpkt();
//p_copy.dest = p.src;
//p_copy.src = ID(old_srh->req_dest(),::IP);
p_copy.route.appendToPath(net_id);
p_copy.route.appendToPath(p_copy.src);
hdr_ip *new_iph = (hdr_ip*)p_copy.pkt->access(off_ip_);
new_iph->daddr() = Address::instance().create_ipaddr(p_copy.dest.getNSAddr_t(),RT_PORT);
new_iph->dport() = RT_PORT;
new_iph->saddr() =
Address::instance().create_ipaddr(p_copy.src.getNSAddr_t(),RT_PORT);
new_iph->sport() = RT_PORT;
new_iph->ttl() = 255;
hdr_sr *new_srh = (hdr_sr*)p_copy.pkt->access(off_sr_);
new_srh->init();
for (int i = 0 ; i < p_copy.route.length() ; i++)
p_copy.route[i].fillSRAddr(new_srh->reply_addrs()[i]);
new_srh->route_reply_len() = p_copy.route.length();
new_srh->route_reply() = 1;
// propagate the request sequence number in the reply for analysis purposes
new_srh->rtreq_seq() = old_srh->rtreq_seq();
hdr_cmn *new_cmnh = (hdr_cmn*)p_copy.pkt->access(off_cmn_);
new_cmnh->ptype() = PT_DSR;
new_cmnh->size() = IP_HDR_LEN;
trace("SRR %.9f _%s_ reply-sent %s -> %s #%d (len %d) %s",
Scheduler::instance().clock(), net_id.dump(),
p_copy.src.dump(), p_copy.dest.dump(), old_srh->rtreq_seq(),
p_copy.route.length(), p_copy.route.dump());
// flip the route around for the return to the requestor, and
// cache the route for future use
p_copy.route.reverseInPlace();
route_cache->addRoute(p_copy.route, Scheduler::instance().clock(), net_id);
p_copy.route.resetIterator();
p_copy.route.fillSR(new_srh);
new_cmnh->size() += new_srh->size();
/* we now want to jitter when we first originate route replies, since
they are a transmission we make in response to a broadcast packet
-dam 4/23/98
sendOutPacketWithRoute(p_copy, true); */
Scheduler::instance().schedule(this, p_copy.pkt, Random::uniform(RREQ_JITTER));
}
void
DSRAgent::returnSrcRouteToRequestor(SRPacket &p)
// take the route in p, add us to the end of it and return the
// route to the sender of p
// doesn't free p.pkt
{
hdr_sr *old_srh = (hdr_sr*)p.pkt->access(off_sr_);
if (p.route.full())
return; // alas, the route would be to long once we add ourselves
SRPacket p_copy = p;
p_copy.pkt = allocpkt();
p_copy.dest = p.src;
p_copy.src = net_id;
p_copy.route.appendToPath(net_id);
hdr_ip *new_iph = (hdr_ip*)p_copy.pkt->access(off_ip_);
new_iph->daddr() = Address::instance().create_ipaddr(p_copy.dest.getNSAddr_t(),RT_PORT);
new_iph->dport() = RT_PORT;
new_iph->saddr() =
Address::instance().create_ipaddr(p_copy.src.getNSAddr_t(),RT_PORT);
new_iph->sport() = RT_PORT;
new_iph->ttl() = 255;
hdr_sr *new_srh = (hdr_sr*)p_copy.pkt->access(off_sr_);
new_srh->init();
for (int i = 0 ; i < p_copy.route.length() ; i++)
p_copy.route[i].fillSRAddr(new_srh->reply_addrs()[i]);
new_srh->route_reply_len() = p_copy.route.length();
new_srh->route_reply() = 1;
// propagate the request sequence number in the reply for analysis purposes
new_srh->rtreq_seq() = old_srh->rtreq_seq();
hdr_cmn *new_cmnh = (hdr_cmn*)p_copy.pkt->access(off_cmn_);
new_cmnh->ptype() = PT_DSR;
new_cmnh->size() = IP_HDR_LEN;
trace("SRR %.9f _%s_ reply-sent %s -> %s #%d (len %d) %s",
Scheduler::instance().clock(), net_id.dump(),
p_copy.src.dump(), p_copy.dest.dump(), old_srh->rtreq_seq(),
p_copy.route.length(), p_copy.route.dump());
// flip the route around for the return to the requestor, and
// cache the route for future use
p_copy.route.reverseInPlace();
route_cache->addRoute(p_copy.route, Scheduler::instance().clock(), net_id);
p_copy.route.resetIterator();
p_copy.route.fillSR(new_srh);
new_cmnh->size() += new_srh->size();
/* we now want to jitter when we first originate route replies, since
they are a transmission we make in response to a broadcast packet
-dam 4/23/98
sendOutPacketWithRoute(p_copy, true); */
Scheduler::instance().schedule(this, p_copy.pkt, Random::uniform(RREQ_JITTER));
}
void
DSRAgent::acceptRouteReply(SRPacket &p)
/* - enter the packet's source route into our cache
- see if any packets are waiting to be sent out with this source route
- doesn't free the pkt */
{
hdr_sr *srh = (hdr_sr*)p.pkt->access(off_sr_);
Path reply_route(srh->reply_addrs(), srh->route_reply_len());
if (!srh->route_reply())
{ // somethings wrong...
trace("SDFU non route containing packet given to acceptRouteReply");
fprintf(stderr,
"dfu: non route containing packet given to acceptRouteReply\n");
}
/* check to see if this reply is valid or not using god info */
bool good_reply = true;
#ifdef USE_GOD_FEEDBACK
int i;
for (i = 0; i < reply_route.length()-1 ; i++)
if (God::instance()->hops(reply_route[i].getNSAddr_t(),
reply_route[i+1].getNSAddr_t()) != 1)
{
good_reply = false;
break;
}
#endif
trace("SRR %.9f _%s_ reply-received %d from %s %s #%d -> %s %s",
Scheduler::instance().clock(), net_id.dump(),
good_reply ? 1 : 0,
p.src.dump(), reply_route[0].dump(), srh->rtreq_seq(),
reply_route[reply_route.length()-1].dump(),
reply_route.dump());
// add the new route into our cache
route_cache->addRoute(reply_route, Scheduler::instance().clock(), p.src);
// back down the route request counters
Entry *e = request_table.getEntry(reply_route[reply_route.length()-1]);
e->rt_reqs_outstanding = 0;
e->last_rt_req = 0.0;
// see if the addtion of this route allows us to send out
// any of the packets we have waiting
Time delay = 0.0;
ID dest;
for (int c = 0; c < SEND_BUF_SIZE; c++)
{
if (send_buf[c].p.pkt == NULL) continue;
// check if pkt is destined to outside domain
if (diff_subnet(send_buf[c].p.dest,net_id)) {
dest = ID(node_->base_stn(),::IP);
if (dest == net_id)
// Iam the base-station
dest = send_buf[c].p.dest;
}
else
dest = send_buf[c].p.dest;
if (route_cache->findRoute(dest, send_buf[c].p.route, 1))
{ // we have a route!
#ifdef DEBUG
struct hdr_cmn *ch = HDR_CMN(send_buf[c].p.pkt);
if(ch->size() < 0) {
drop(send_buf[c].p.pkt, "XXX");
abort();
}
#endif
if (verbose)
trace("Sdebug %.9f _%s_ liberated from sendbuf %s->%s %s",
Scheduler::instance().clock(), net_id.dump(),
send_buf[c].p.src.dump(), send_buf[c].p.dest.dump(),
send_buf[c].p.route.dump());
/* we need to spread out the rate at which we send packets
in to the link layer to give ARP time to complete. If we
dump all the packets in at once, all but the last one will
be dropped. XXX THIS IS A MASSIVE HACK -dam 4/14/98 */
sendOutPacketWithRoute(send_buf[c].p, true, delay);
delay += arp_timeout;
send_buf[c].p.pkt = NULL;
}
}
}
void
DSRAgent::processBrokenRouteError(SRPacket& p)
// take the error packet and proccess our part of it.
// if needed, send the remainder of the errors to the next person
// doesn't free p.pkt
{
hdr_sr *srh = (hdr_sr*)p.pkt->access(off_sr_);
if (!srh->route_error())
return; // what happened??
/* if we hear A->B is dead, should we also run the link B->A through the
cache as being dead, since 802.11 requires bidirectional links
XXX -dam 4/23/98 */
// since CPU time is cheaper than network time, we'll process
// all the dead links in the error packet
assert(srh->num_route_errors() > 0);
for (int c = 0 ; c < srh->num_route_errors() ; c++)
{
assert(srh->down_links()[c].addr_type == NS_AF_INET);
route_cache->noticeDeadLink(ID(srh->down_links()[c].from_addr,::IP),
ID(srh->down_links()[c].to_addr,::IP),
Scheduler::instance().clock());
// I'll assume everything's of type NS_AF_INET for the printout... XXX
if (verbose_srr)
trace("SRR %.9f _%s_ dead-link tell %d %d -> %d",
Scheduler::instance().clock(), net_id.dump(),
srh->down_links()[c].tell_addr,
srh->down_links()[c].from_addr,
srh->down_links()[c].to_addr);
}
ID who = ID(srh->down_links()[srh->num_route_errors()-1].tell_addr, ::IP);
if (who != net_id && who != MAC_id)
{ // this error packet wasn't meant for us to deal with
// since the outer entry doesn't list our name
return;
}
// record this route error data for possible propagation on our next
// route request
route_error_held = true;
err_from = ID(srh->down_links()[srh->num_route_errors()-1].from_addr,::IP);
err_to = ID(srh->down_links()[srh->num_route_errors()-1].to_addr,::IP);
route_error_data_time = Scheduler::instance().clock();
if (1 == srh->num_route_errors())
{ // this error packet has done its job
// it's either for us, in which case we've done what it sez
// or it's not for us, in which case we still don't have to forward
// it to whoever it is for
return;
}
/* make a copy of the packet and send it to the next tell_addr on the
error list. the copy is needed in case there is other data in the
packet (such as nested route errors) that need to be delivered */
if (verbose)
trace("Sdebug %.5f _%s_ unwrapping nested route error",
Scheduler::instance().clock(), net_id.dump());
SRPacket p_copy = p;
p_copy.pkt = p.pkt->copy();
hdr_sr *new_srh = (hdr_sr*)p_copy.pkt->access(off_sr_);
hdr_ip *new_iph = (hdr_ip*)p_copy.pkt->access(off_ip_);
// remove us from the list of errors
new_srh->num_route_errors() -= 1;
// send the packet to the person listed in what's now the last entry
p_copy.dest = ID(new_srh->down_links()[new_srh->num_route_errors()-1].tell_addr, ::IP);
p_copy.src = net_id;
//new_iph->dst() = (p_copy.dest.addr) << Address::instance().nodeshift();
new_iph->daddr() = Address::instance().create_ipaddr(p_copy.dest.getNSAddr_t(),RT_PORT);
new_iph->dport() = RT_PORT;
//new_iph->src() = (p_copy.src.addr) << Address::instance().nodeshift();
new_iph->saddr() = Address::instance().create_ipaddr(p_copy.src.getNSAddr_t(),RT_PORT);
new_iph->sport() = RT_PORT;
new_iph->ttl() = 255;
// an error packet is a first class citizen, so we'll
// use handlePktWOSR to obtain a route if needed
handlePktWithoutSR(p_copy, false);
}
void
DSRAgent::tap(const Packet *packet)
/* process packets that are promiscously listened to from the MAC layer tap
*** do not change or free packet *** */
{
hdr_sr *srh = (hdr_sr*)packet->access(off_sr_);
hdr_ip *iph = (hdr_ip*)packet->access(off_ip_);
hdr_cmn *cmh = (hdr_cmn*)packet->access(off_cmn_);
if (!dsragent_use_tap) return;
if (srh->valid() != 1) return; // can't do anything with it
// don't trouble me with packets I'm about to receive anyway
/* this change added 5/13/98 -dam */
ID next_hop(srh->addrs[srh->cur_addr()]);
if (next_hop == net_id || next_hop == MAC_id) return;
SRPacket p((Packet *) packet, srh);
p.dest = ID((Address::instance().get_nodeaddr(iph->daddr())),::IP);
p.src = ID((Address::instance().get_nodeaddr(iph->saddr())),::IP);
// don't trouble me with my own packets
if (p.src == net_id) return;
/* snoop on the SR data */
if (srh->route_error())
{
if (verbose)
trace("Sdebug _%s_ tap saw error %d", net_id.dump(), cmh->uid());
processBrokenRouteError(p);
}
if (srh->route_reply())
{
Path reply_path(srh->reply_addrs(), srh->route_reply_len());
if(verbose)
trace("Sdebug _%s_ tap saw route reply %d %s",
net_id.dump(), cmh->uid(), reply_path.dump());
route_cache->noticeRouteUsed(reply_path, Scheduler::instance().clock(),
p.src);
}
/* we can't decide whether we should snoop on the src routes in
route requests. We've seen cases where we hear a route req from a
node, but can't complete an arp with that node (and so can't actually
deliver packets through it if called on to do so) -dam 4/16/98 */
if (srh->route_request()) return; // don't path shorten route requests
// the logic is wrong for shortening rtreq's anyway, cur_addr always = 0
if (dsragent_snoop_source_routes)
{
if (verbose)
trace("Sdebug _%s_ tap saw route use %d %s", net_id.dump(),
cmh->uid(), p.route.dump());
route_cache->noticeRouteUsed(p.route, Scheduler::instance().clock(),
net_id);
}
if (PT_DSR == cmh->ptype()) return; // no route shortening on any
// DSR packet
/* I think we ended up sending grat route replies for source routes on
route replies for route requests that were answered by someone else's
cache, resulting in the wrong node receiving the route. For now, I
outlaw it.
The root of the problem is that when we salvage a pkt from a failed
link using a route from our cache, we break what had been an invariant
that the IP src of a packet was also the first machine listed on the
source route. Here's the route of the problem that results in the
simulator crashing at 8.56135 when 44 recieves a route reply that
has 24 listed as the first node in the route.
SSendFailure 8.52432 24 [10 |24 46 45 1 40 ]
S$hit 8.52432 salvaging 10 -> 40 with [(24) 44 50 9 40 ]
S$hit 8.52432 salvaging 44 -> 40 with [(24) 44 50 9 40 ]
D 8.52432 [20 42 2e 18 800] 24 DSR 156 -- 10->40 6 [0] [1 9 39] [0 0 0->0]
s 8.52438 [1b 45e 2c 18 0] 24 MAC 20
r 8.52446 [1b 45e 2c 18 0] 44 MAC 20
s 8.52454 [101b 27e 23 1b 0] 27 MAC 20
s 8.52564 [101b 27e 23 1b 0] 27 MAC 20
s 8.52580 [101b 45e 2c 18 0] 24 MAC 20
r 8.52588 [101b 45e 2c 18 0] 44 MAC 20
s 8.52589 [1c 41c 18 0 0] 44 MAC 14
r 8.52595 [1c 41c 18 0 0] 24 MAC 14
s 8.52600 [20 42 2c 18 800] 24 DSR 244 -- 10->40 5 [0] [1 9 39] [0 0 24->46]
r 8.52698 [20 42 2c 18 800] 44 DSR 216 -- 10->40 5 [0] [1 9 39] [0 0 24->46]
s 8.53947 [20 42 2c 18 800] 24 DSR 204 -- 44->40 5 [0] [1 8 39] [0 0 0->0]
r 8.54029 [20 42 2c 18 800] 44 DSR 176 -- 44->40 5 [0] [1 8 39] [0 0 0->0]
Sdebug 50 consider grat arp for [24 (44) 50 9 40 ]
SRR 8.54029 50 gratuitous-reply-sent 50 -> 44 [24 (50) 9 40 ]
SF 8.54029 44 [44 -> 40] via 0x3200 [24 |44 50 9 40 ]
s 8.54030 [1d 0 18 0 0] 44 MAC 14
r 8.54036 [1d 0 18 0 0] 24 MAC 14
s 8.54044 [101b 54f 32 2c 0] 44 MAC 20
r 8.54053 [101b 54f 32 2c 0] 50 MAC 20
s 8.54054 [1c 50d 2c 0 0] 50 MAC 14
r 8.54059 [1c 50d 2c 0 0] 44 MAC 14
s 8.54064 [20 42 32 2c 800] 44 DSR 304 -- 10->40 5 [0] [1 9 39] [0 0 24->46]
r 8.54186 [20 42 32 2c 800] 50 DSR 276 -- 10->40 5 [0] [1 9 39] [0 0 24->46]
SF 8.54186 50 [10 -> 40] via 0x900 [24 44 |50 9 40 ]
s 8.56101 [20 42 2c 18 800] 24 DSR 84 -- 50->44 2 [0] [1 4 40] [0 0 0->0]
r 8.56135 [20 42 2c 18 800] 44 DSR 56 -- 50->44 2 [0] [1 4 40] [0 0 0->0]
*/
/* check to see if we can shorten the route being used */
if (p.route[p.route.index()] != net_id
&& p.route[p.route.index()] != MAC_id)
{ // it's not immeadiately to us
for (int i = p.route.index() + 1; i < p.route.length(); i++)
if (p.route[i] == net_id || p.route[i] == MAC_id)
{ // but it'll get here eventually...
sendRouteShortening(p, p.route.index(), i);
}
}
}
static GratReplyHoldDown *
FindGratHoldDown(GratReplyHoldDown *hd, int sz, Path& query)
{
int c;
for (c = 0; c < sz; c++)
if (query == hd[c].p) return &hd[c];
return NULL;
}
void
DSRAgent::sendRouteShortening(SRPacket &p, int heard_at, int xmit_at)
// p was overheard at heard_at in it's SR, but we aren't supposed to
// get it till xmit_at, so all the nodes between heard_at and xmit_at
// can be elided. Send originator of p a gratuitous route reply to
// tell them this.
{
// this shares code with returnSrcRouteToRequestor - factor them -dam */
if (!dsragent_send_grat_replies) return;
if (verbose)
trace("Sdebug %s consider grat arp for %s", net_id.dump(), p.route.dump());
GratReplyHoldDown *g = FindGratHoldDown(grat_hold, RTREP_HOLDOFF_SIZE,
p.route);
if (!g)
{
grat_hold[grat_hold_victim].p = p.route;
grat_hold_victim = (grat_hold_victim + 1) % RTREP_HOLDOFF_SIZE;
g = &grat_hold[grat_hold_victim];
}
else if (Scheduler::instance().clock() - g->t < grat_hold_down_time) return;
g->t = Scheduler::instance().clock();
SRPacket p_copy = p;
p_copy.pkt = allocpkt();
p_copy.dest = p.route[0]; // tell the originator of this long source route
p_copy.src = net_id;
// reverse the route to get the packet back
p_copy.route[p_copy.route.index()] = net_id;
p_copy.route.reverseInPlace();
p_copy.route.removeSection(0,p_copy.route.index());
hdr_ip *new_iph = (hdr_ip*)p_copy.pkt->access(off_ip_);
//new_iph->dst() = (p_copy.dest.addr) << Address::instance().nodeshift();
new_iph->daddr() = Address::instance().create_ipaddr(p_copy.dest.getNSAddr_t(),RT_PORT);
new_iph->dport() = RT_PORT;
//new_iph->src() = (p_copy.src.addr) << Address::instance().nodeshift();
new_iph->saddr() = Address::instance().create_ipaddr(p_copy.src.getNSAddr_t(),RT_PORT);
new_iph->sport() = RT_PORT;
new_iph->ttl() = 255;
// shorten's p's route
p.route.removeSection(heard_at, xmit_at);
hdr_sr *new_srh = (hdr_sr*)p_copy.pkt->access(off_sr_);
new_srh->init();
for (int i = 0 ; i < p.route.length() ; i++)
p.route[i].fillSRAddr(new_srh->reply_addrs()[i]);
new_srh->route_reply_len() = p.route.length();
new_srh->route_reply() = 1;
// grat replies will have a 0 seq num (it's only for trace analysis anyway)
new_srh->rtreq_seq() = 0;
hdr_cmn *new_cmnh = (hdr_cmn*)p_copy.pkt->access(off_cmn_);
new_cmnh->ptype() = PT_DSR;
new_cmnh->size() += IP_HDR_LEN;
trace("SRR %.9f _%s_ gratuitous-reply-sent %s -> %s (len %d) %s",
Scheduler::instance().clock(), net_id.dump(),
p_copy.src.dump(), p_copy.dest.dump(), p.route.length(),
p.route.dump());
// cache the route for future use (we learned the route from p)
route_cache->addRoute(p_copy.route, Scheduler::instance().clock(), p.src);
sendOutPacketWithRoute(p_copy, true);
}
/*==============================================================
debug and trace output
------------------------------------------------------------*/
void
DSRAgent::trace(char* fmt, ...)
{
va_list ap;
if (!logtarget) return;
if (verbose) {
va_start(ap, fmt);
vsprintf(logtarget->buffer(), fmt, ap);
logtarget->dump();
va_end(ap);
}
}
/*==============================================================
Callback for link layer transmission failures
------------------------------------------------------------*/
struct filterfailuredata {
nsaddr_t dead_next_hop;
int off_cmn_;
DSRAgent *agent;
};
int
FilterFailure(Packet *p, void *data)
{
struct filterfailuredata *ffd = (filterfailuredata *) data;
hdr_cmn *cmh = (hdr_cmn*)p->access(ffd->off_cmn_);
int remove = cmh->next_hop() == ffd->dead_next_hop;
if (remove) ffd->agent->undeliverablePkt(p,1);
return remove;
}
void
DSRAgent::undeliverablePkt(Packet *pkt, int mine)
/* when we've got a packet we can't deliver, what to do with it?
frees or hands off p if mine = 1, doesn't hurt it otherwise */
{
hdr_sr *srh = (hdr_sr*)pkt->access(off_sr_);
hdr_ip *iph = (hdr_ip*)pkt->access(off_ip_);
hdr_cmn *cmh;
SRPacket p(pkt,srh);
p.dest = ID((Address::instance().get_nodeaddr(iph->daddr())),::IP);
p.src = ID((Address::instance().get_nodeaddr(iph->saddr())),::IP);
p.pkt = mine ? pkt : pkt->copy();
srh = (hdr_sr*)p.pkt->access(off_sr_);
iph = (hdr_ip*)p.pkt->access(off_ip_);
cmh = (hdr_cmn*)p.pkt->access(off_cmn_);
if (p.src == net_id)
{ // it's our packet we couldn't send
cmh->size() -= srh->size(); // remove SR header
assert(cmh->size() >= 0);
handlePktWithoutSR(p, false);
}
else
{ // it's a packet we're forwarding for someone, save it if we can...
Path salvage_route;
if (dsragent_salvage_with_cache
&& route_cache->findRoute(p.dest, salvage_route, 0))
{ // be nice and send the packet out
#if 0
/* we'd like to create a ``proper'' source route with the
IP src of the packet as the first node, but we can't actually
just append the salvage route onto the route used so far,
since the append creates routes with loops in them
like 1 2 3 4 3 5
If we were to squish the route to remove the loop, then we'd be
removing ourselves from the route, which is verboten.
If we did remove ourselves, and our salvage route contained
a stale link, we might never hear the route error.
-dam 5/13/98
Could we perhaps allow SRs with loops in them on the air?
Since it's still a finite length SR, the pkt can't loop
forever... -dam 8/5/98 */
// truncate the route at the bad link and append good bit
int our_index = p.route.index();
p.route.setLength(our_index); // yes this cuts us off the route,
p.route.appendPath(salvage_route); // but we're at the front of s_r
p.route.setIterator(our_index);
#else
p.route = salvage_route;
p.route.resetIterator();
#endif
if (dsragent_dont_salvage_bad_replies && srh->route_reply())
{ // check to see if we'd be salvaging a packet with the
// dead link in it
ID to_id(srh->addrs[srh->cur_addr()+1].addr,
(ID_Type) srh->addrs[srh->cur_addr()].addr_type);
bool bad_reply = false;
for (int i = 0 ; i < srh->route_reply_len()-1 ; i++)
{
if (net_id == ID(srh->reply_addrs()[i])
&& to_id == ID(srh->reply_addrs()[i+1])
|| (dsragent_require_bi_routes
&& to_id == ID(srh->reply_addrs()[i])
&& net_id == ID(srh->reply_addrs()[i+1])))
{
bad_reply = true;
break;
}
}
if (bad_reply)
{ // think about killing this packet
srh->route_reply() = 0;
if (PT_DSR == cmh->ptype() && !srh->route_request() &&
!srh->route_error())
{ // this packet has no reason to live
trace("SRR %.5f _%s_ --- %d dropping bad-reply %s -> %s",
Scheduler::instance().clock(), net_id.dump(),
cmh->uid(), p.src.dump(), p.dest.dump());
if (mine) drop(pkt, DROP_RTR_MAC_CALLBACK);
return;
}
}
}
trace("Ssalv %.5f _%s_ salvaging %s -> %s --- %d with %s",
Scheduler::instance().clock(), net_id.dump(),
p.src.dump(), p.dest.dump(), cmh->uid(), p.route.dump());
sendOutPacketWithRoute(p, false);
}
else
{ // we don't have a route, and it's not worth us doing a
// route request to try to help the originator out, since
// it might be counter productive
trace("Ssalv %.5f _%s_ dropping --- %d %s -> %s",
Scheduler::instance().clock(),
net_id.dump(), cmh->uid(), p.src.dump(), p.dest.dump());
if (mine) drop(pkt, DROP_RTR_NO_ROUTE);
}
}
}
#ifdef USE_GOD_FEEDBACK
static int linkerr_is_wrong = 0;
#endif
void
DSRAgent::xmitFailed(Packet *pkt)
/* mark our route cache reflect the failure of the link between
srh[cur_addr] and srh[next_addr], and then create a route err
message to send to the orginator of the pkt (srh[0])
p.pkt freed or handed off */
{
hdr_sr *srh = (hdr_sr*)pkt->access(off_sr_);
hdr_ip *iph = (hdr_ip*)pkt->access(off_ip_);
hdr_cmn *cmh = (hdr_cmn*)pkt->access(off_cmn_);
assert(cmh->size() >= 0);
srh->cur_addr() -= 1; // correct for inc already done on sending
if (verbose)
trace("SSendFailure %.9f _%s_ --- %d - %s",
Scheduler::instance().clock(), net_id.dump(), cmh->uid(), srh->dump());
if (srh->cur_addr() >= srh->num_addrs() - 1)
{
trace("SDFU: route error beyond end of source route????");
fprintf(stderr,"SDFU: route error beyond end of source route????\n");
Packet::free(pkt);
return;
}
if (srh->route_request())
{
trace("SDFU: route error forwarding route request????");
fprintf(stderr,"SDFU: route error forwarding route request????\n");
Packet::free(pkt);
return;
}
ID tell_id(srh->addrs[0].addr,
(ID_Type) srh->addrs[srh->cur_addr()].addr_type);
ID from_id(srh->addrs[srh->cur_addr()].addr,
(ID_Type) srh->addrs[srh->cur_addr()].addr_type);
ID to_id(srh->addrs[srh->cur_addr()+1].addr,
(ID_Type) srh->addrs[srh->cur_addr()].addr_type);
assert(from_id == net_id || from_id == MAC_id);
#ifdef USE_GOD_FEEDBACK
if (God::instance()->hops(from_id.getNSAddr_t(), to_id.getNSAddr_t()) == 1)
{ /* god thinks this link is still valid */
linkerr_is_wrong++;
trace("SxmitFailed %.5f _%s_ %d->%d god okays #%d",
Scheduler::instance().clock(), net_id.dump(),
from_id.getNSAddr_t(), to_id.getNSAddr_t(), linkerr_is_wrong);
fprintf(stderr,
"xmitFailed on link %d->%d god okays - ignoring & recycling #%d\n",
from_id.getNSAddr_t(), to_id.getNSAddr_t(), linkerr_is_wrong);
/* put packet back on end of ifq for xmission */
srh->cur_addr() += 1; // correct for decrement earlier in proc
// make sure we aren't cycling packets
assert(p.pkt->incoming == 0); // this is an outgoing packet
ll->recv(pkt, (Handler*) 0);
return;
}
#endif
/* kill any routes we have using this link */
route_cache->noticeDeadLink(from_id, to_id,
Scheduler::instance().clock());
/* give ourselves a chance to save the packet */
undeliverablePkt(pkt->copy(), 1);
/* now kill all the other packets in the output queue that would
use the same next hop. This is reasonable, since 802.11 has
already retried the xmission multiple times => a persistent failure. */
Packet *r, *nr, *head = 0; // pkts to be recycled
while((r = ifq->filter(to_id.getNSAddr_t()))) {
r->next_ = head;
head = r;
}
for(r = head; r; r = nr) {
nr = r->next_;
undeliverablePkt(r, 1);
}
/* warp pkt into a route error message */
if (tell_id == net_id || tell_id == MAC_id)
{ // no need to send the route error if it's for us
if (verbose)
trace("Sdebug _%s_ not bothering to send route error to ourselves",
tell_id.dump());
Packet::free(pkt); // no drop needed
pkt = 0;
return;
}
if (srh->num_route_errors() >= MAX_ROUTE_ERRORS)
{ // no more room in the error packet to nest an additional error.
// this pkt's been bouncing around so much, let's just drop and let
// the originator retry
// Another possibility is to just strip off the outer error, and
// launch a Route discovey for the inner error XXX -dam 6/5/98
trace("SDFU %.5f _%s_ dumping maximally nested error %s %d -> %d",
Scheduler::instance().clock(), net_id.dump(),
tell_id.dump(),
from_id.dump(),
to_id.dump());
Packet::free(pkt); // no drop needed
pkt = 0;
return;
}
link_down *deadlink = &(srh->down_links()[srh->num_route_errors()]);
deadlink->addr_type = srh->addrs[srh->cur_addr()].addr_type;
deadlink->from_addr = srh->addrs[srh->cur_addr()].addr;
deadlink->to_addr = srh->addrs[srh->cur_addr()+1].addr;
deadlink->tell_addr = srh->addrs[0].addr;
srh->num_route_errors() += 1;
if (verbose)
trace("Sdebug %.5f _%s_ sending into dead-link (nest %d) tell %d %d -> %d",
Scheduler::instance().clock(), net_id.dump(),
srh->num_route_errors(),
deadlink->tell_addr,
deadlink->from_addr,
deadlink->to_addr);
srh->route_error() = 1;
srh->route_reply() = 0;
srh->route_request() = 0;
//iph->dst() = (deadlink->tell_addr) << Address::instance().nodeshift();
iph->daddr() = Address::instance().create_ipaddr(deadlink->tell_addr,RT_PORT);
iph->dport() = RT_PORT;
//iph->src() = (net_id.addr) << Address::instance().nodeshift();
iph->saddr() = Address::instance().create_ipaddr(net_id.addr,RT_PORT);
iph->sport() = RT_PORT;
iph->ttl() = 255;
cmh->ptype() = PT_DSR; // cut off data
cmh->size() = IP_HDR_LEN;
cmh->num_forwards() = 0;
// assign this packet a new uid, since we're sending it
cmh->uid() = uidcnt_++;
SRPacket p(pkt, srh);
p.route.setLength(p.route.index()+1);
p.route.reverseInPlace();
p.dest = tell_id;
p.src = net_id;
/* send out the Route Error message */
sendOutPacketWithRoute(p, true);
}
void
XmitFailureCallback(Packet *pkt, void *data)
{
DSRAgent *agent = (DSRAgent *)data; // cast of trust
agent->xmitFailed(pkt);
}
#if 0
/* this is code that implements Route Reply holdoff to prevent route
reply storms. It's described in the kluwer paper and was used in
those simulations, but isn't currently used. -dam 8/5/98 */
/*==============================================================
Callback Timers to deal with holding off route replies
Basic theory: if we see a node S that has requested a route to D
send a packet to D via a route of length <= ours then don't send
our route. We record that S has used a good route to D by setting
the best_length to -1, meaning that our route can't possibly do
S any good (unless S has been lied to, but we probably can't know
that).
NOTE: there is confusion in this code as to whether the requestor
and requested_dest ID's are MAC or IP... It doesn't matter for now
but will later when they are not the same.
------------------------------------------------------------*/
struct RtHoldoffData: public EventData {
RtHoldoffData(DSRAgent *th, Packet *pa, int ind):t(th), p(pa), index(ind)
{}
DSRAgent *t;
Packet *p;
int index;
};
void
RouteReplyHoldoffCallback(Node *node, Time time, EventData *data)
// see if the packet inside the data is still in the
// send buffer and expire it if it is
{
Packet *p = ((RtHoldoffData *)data)->p;
DSRAgent *t = ((RtHoldoffData *)data)->t;
int index = ((RtHoldoffData *)data)->index;
RtRepHoldoff *entry = &(t->rtrep_holdoff[index]);
assert((entry->requestor == p->dest));
// if we haven't heard the requestor use a route equal or better
// than ours then send our reply.
if ((lsnode_require_use && entry->best_length != -1)
|| (!lsnode_require_use && entry->best_length > entry->our_length))
{ // we send
world_statistics.sendingSrcRtFromCache(t,time,p);
t->sendPacket(t,time,p);
}
else
{ // dump our packet
delete p;
}
entry->requestor = invalid_addr;
entry->requested_dest = invalid_addr;
delete data;
t->num_heldoff_rt_replies--;
}
void
DSRAgent::scheduleRouteReply(Time t, Packet *new_p)
// schedule a time to send new_p if we haven't heard a better
// answer in the mean time. Do not modify new_p after calling this
{
for (int c = 0; c < RTREP_HOLDOFF_SIZE; c ++)
if (rtrep_holdoff[c].requested_dest == invalid_addr) break;
assert(c < RTREP_HOLDOFF_SIZE);
Path *our_route = &(new_p->data.getRoute().source_route);
rtrep_holdoff[c].requested_dest = (*our_route)[our_route->length() - 1];
rtrep_holdoff[c].requestor = new_p->dest;
rtrep_holdoff[c].best_length = MAX_ROUTE_LEN + 1;
rtrep_holdoff[c].our_length = our_route->length();
Time send_time = t +
(Time) (our_route->length() - 1) * rt_rep_holdoff_period
+ U(0.0, rt_rep_holdoff_period);
RegisterCallback(this,&RouteReplyHoldoffCallback, send_time,
new RtHoldoffData(this,new_p,c));
num_heldoff_rt_replies++;
}
void
DSRAgent::snoopForRouteReplies(Time t, Packet *p)
// see if p is a route reply that we're watching for
// or if it was sent off using a route reply we're watching for
{
for (int c = 0 ; c requestor == p->dest
&& (p->type == ::route_reply || p->data.sourceRoutep()))
{ // see if this route reply is one we're watching for
Path *srcrt = &(p->data.getRoute().source_route);
if (!(entry->requested_dest == (*srcrt)[srcrt->length()-1]))
continue; // it's not ours
if (entry->best_length > srcrt->length())
entry->best_length = srcrt->length();
} // end if we heard a route reply being sent
else if (entry->requestor == p->src
&& entry->requested_dest == p->dest)
{ // they're using a route reply! see if ours is better
if (p->route.length() <= entry->our_length)
{ // Oh no! they've used a better path than ours!
entry->best_length = -1; //there's no point in replying.
}
} // end if they used used route reply
else
continue;
}
}
#endif // 0
hdr_sr.cc
/* -*- 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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*/
/* Ported from CMU/Monarch's code, nov'98 -Padma.*/
/* -*- c++ -*-
hdr_sr.cc
source route header
$Id: AllCode__.html 980 2002-12-12 09:36:58Z ffilali $
*/
#include
#include
int hdr_sr::offset_;
static class SRHeaderClass : public PacketHeaderClass {
public:
SRHeaderClass() : PacketHeaderClass("PacketHeader/SR",
sizeof(hdr_sr)) {
offset(&hdr_sr::offset_);
}
void export_offsets() {
field_offset("valid_", OFFSET(hdr_sr, valid_));
field_offset("num_addrs_", OFFSET(hdr_sr, num_addrs_));
field_offset("cur_addr_", OFFSET(hdr_sr, cur_addr_));
}
} class_SRhdr;
char *
hdr_sr::dump()
{
static char buf[100];
dump(buf);
return (buf);
}
void
hdr_sr::dump(char *buf)
{
char *ptr = buf;
*ptr++ = '[';
for (int i = 0; i < num_addrs_; i++)
{
sprintf(ptr, "%s%d ", (i == cur_addr_) ? "|" : "", addrs[i].addr);
ptr += strlen(ptr);
}
*ptr++ = ']';
*ptr = '\0';
}
mobicache.cc
/* -*- 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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*/
/* Ported from CMU/Monarch's code, nov'98 -Padma.*/
/* mobicache.cc
$Id: AllCode__.html 980 2002-12-12 09:36:58Z ffilali $
cache used in the mobicom 98 submission. see the paper for a description
*/
extern "C" {
#include
#include
};
#undef DEBUG
#include
#include "path.h"
#include "routecache.h"
#ifdef DSR_CACHE_STATS
#include
#endif
/* invariants
- no path contains an id more than once
- all paths start with the id of our node (MAC_id or net_id)
*/
#define fout stdout
static const int verbose = 1;
static const int verbose_debug = 0;
/*===============================================================
function selectors
----------------------------------------------------------------*/
bool cache_ignore_hints = false; // ignore all hints?
bool cache_use_overheard_routes = true;
// if we are A, and we over hear a rt Z Y (X) W V U, do we add route
// A X W V U to the cache?
/*===============================================================
Class declaration
----------------------------------------------------------------*/
class MobiCache;
class Cache {
friend class MobiCache;
public:
Cache(char *name, int size, MobiCache *rtcache);
~Cache();
int pickVictim(int exclude = -1);
// returns the index of a suitable victim in the cache
// will spare the life of exclude
bool searchRoute(const ID& dest, int& i, Path &path, int &index);
// look for dest in cache, starting at index,
//if found, rtn true with path s.t. cache[index] == path && path[i] == dest
Path* addRoute(Path &route, int &prefix_len);
// rtns a pointer the path in the cache that we added
void noticeDeadLink(const ID&from, const ID& to);
// the link from->to isn't working anymore, purge routes containing
// it from the cache
private:
Path *cache;
int size;
int victim_ptr; // next victim for eviction
MobiCache *routecache;
char *name;
};
///////////////////////////////////////////////////////////////////////////
class MobiCache : public RouteCache {
friend class Cache;
friend class MobiHandler;
public:
MobiCache(const ID& MAC_id, const ID& net_id,int psize = 30,int ssize = 34 );
MobiCache();
~MobiCache();
void noticeDeadLink(const ID&from, const ID& to, Time t);
// the link from->to isn't working anymore, purge routes containing
// it from the cache
void noticeRouteUsed(const Path& route, Time t,
const ID& who_from);
// tell the cache about a route we saw being used
void addRoute(const Path& route, Time t, const ID& who_from);
// add this route to the cache (presumably we did a route request
// to find this route and don't want to lose it)
bool findRoute(ID dest, Path& route, int for_use = 0);
// if there is a cached path from us to dest returns true and fills in
// the route accordingly. returns false otherwise
// if for_use, then we assume that the node really wants to keep
// the returned route so it will be promoted to primary storage if not there
// already
int command(int argc, const char*const* argv);
protected:
Cache *primary_cache; /* routes that we are using, or that we have reason
to believe we really want to hold on to */
Cache *secondary_cache; /* routes we've learned via a speculative process
that might not pan out */
#ifdef DSR_CACHE_STATS
void periodic_checkCache(void);
void checkRoute(Path *p, int action, int prefix_len);
void checkRoute(Path &p, int&, int&, double&, int&, int&);
#endif
};
RouteCache *
makeRouteCache()
{
return new MobiCache();
}
/*===============================================================
OTcl definition
----------------------------------------------------------------*/
static class MobiCacheClass : public TclClass {
public:
MobiCacheClass() : TclClass("MobiCache") {}
TclObject* create(int, const char*const*) {
return (new MobiCache);
}
} class_MobiCache;
/*===============================================================
Constructors
----------------------------------------------------------------*/
MobiCache::MobiCache(): RouteCache()
{
primary_cache = new Cache("primary", 30, this);
secondary_cache = new Cache("secondary", 34, this);
assert(primary_cache != NULL && secondary_cache != NULL);
#ifdef DSR_CACHE_STATS
stat.reset();
#endif
}
MobiCache::~MobiCache()
{
delete primary_cache;
delete secondary_cache;
}
int
MobiCache::command(int argc, const char*const* argv)
{
if(argc == 2 && strcasecmp(argv[1], "startdsr") == 0)
{
if (ID(1,::IP) == net_id)
trace("Sconfig %.5f using MOBICACHE", Scheduler::instance().clock());
// FALL-THROUGH
}
return RouteCache::command(argc, argv);
}
#ifdef DSR_CACHE_STATS
void
MobiCache::periodic_checkCache()
{
int c;
int route_count = 0;
int route_bad_count = 0;
int subroute_count = 0;
int subroute_bad_count = 0;
int link_bad_count = 0;
double link_bad_time = 0.0;
int link_bad_tested = 0;
int link_good_tested = 0;
for(c = 0; c < primary_cache->size; c++)
{
int x = 0;
if (primary_cache->cache[c].length() == 0) continue;
checkRoute(primary_cache->cache[c],
x,
link_bad_count,
link_bad_time,
link_bad_tested,
link_good_tested);
route_count += 1;
route_bad_count += x ? 1 : 0;
subroute_count += primary_cache->cache[c].length() - 1;
subroute_bad_count += x;
}
for(c = 0; c < secondary_cache->size; c++)
{
int x = 0;
if (secondary_cache->cache[c].length() == 0) continue;
checkRoute(secondary_cache->cache[c],
x,
link_bad_count,
link_bad_time,
link_bad_tested,
link_good_tested);
route_count += 1;
route_bad_count += x ? 1 : 0;
subroute_count += secondary_cache->cache[c].length() - 1;
subroute_bad_count += x;
}
trace("SRC %.9f _%s_ cache-summary %d %d %d %d | %d %.9f %d %d | %d %d %d %d %d | %d %d %d %d %d | %d %d %d %d %d %d",
Scheduler::instance().clock(), net_id.dump(),
route_count,
route_bad_count,
subroute_count,
subroute_bad_count,
link_bad_count,
link_bad_count ? link_bad_time/link_bad_count : 0.0,
link_bad_tested,
link_good_tested,
stat.route_add_count,
stat.route_add_bad_count,
stat.subroute_add_count,
stat.subroute_add_bad_count,
stat.link_add_tested,
stat.route_notice_count,
stat.route_notice_bad_count,
stat.subroute_notice_count,
stat.subroute_notice_bad_count,
stat.link_notice_tested,
stat.route_find_count,
stat.route_find_for_me,
stat.route_find_bad_count,
stat.route_find_miss_count,
stat.subroute_find_count,
stat.subroute_find_bad_count);
stat.reset();
}
#endif /* DSR_CACHE_STATS */
/*===============================================================
member functions
----------------------------------------------------------------*/
void
MobiCache::addRoute(const Path& route, Time t, const ID& who_from)
// add this route to the cache (presumably we did a route request
// to find this route and don't want to lose it)
// who_from is the id of the routes provider
{
Path rt;
if(pre_addRoute(route, rt, t, who_from) == 0)
return;
// must call addRoute before checkRoute
int prefix_len = 0;
#ifdef DSR_CACHE_STATS
Path *p = primary_cache->addRoute(rt, prefix_len);
checkRoute(p, ACTION_ADD_ROUTE, prefix_len);
#else
(void) primary_cache->addRoute(rt, prefix_len);
#endif
}
void
MobiCache::noticeDeadLink(const ID&from, const ID& to, Time)
// the link from->to isn't working anymore, purge routes containing
// it from the cache
{
if(verbose_debug)
trace("SRC %.9f _%s_ dead link %s->%s",
Scheduler::instance().clock(), net_id.dump(),
from.dump(), to.dump());
primary_cache->noticeDeadLink(from, to);
secondary_cache->noticeDeadLink(from, to);
return;
}
void
MobiCache::noticeRouteUsed(const Path& p, Time t, const ID& who_from)
// tell the cache about a route we saw being used
{
Path stub;
if(pre_noticeRouteUsed(p, stub, t, who_from) == 0)
return;
int prefix_len = 0;
#ifdef DSR_CACHE_STATS
Path *p0 = secondary_cache->addRoute(stub, prefix_len);
checkRoute(p0, ACTION_NOTICE_ROUTE, prefix_len);
#else
(void) secondary_cache->addRoute(stub, prefix_len);
#endif
}
bool
MobiCache::findRoute(ID dest, Path& route, int for_me)
// if there is a cached path from us to dest returns true and fills in
// the route accordingly. returns false otherwise
// if for_me, then we assume that the node really wants to keep
// the returned route so it will be promoted to primary storage if not there
// already
{
Path path;
int min_index = -1;
int min_length = MAX_SR_LEN + 1;
int min_cache = 0; // 2 == primary, 1 = secondary
int index;
int len;
assert(!(net_id == invalid_addr));
index = 0;
while (primary_cache->searchRoute(dest, len, path, index))
{
min_cache = 2;
if (len < min_length)
{
min_length = len;
route = path;
}
index++;
}
index = 0;
while (secondary_cache->searchRoute(dest, len, path, index))
{
if (len < min_length)
{
min_index = index;
min_cache = 1;
min_length = len;
route = path;
}
index++;
}
if (min_cache == 1 && for_me)
{ // promote the found route to the primary cache
int prefix_len;
primary_cache->addRoute(secondary_cache->cache[min_index], prefix_len);
// no need to run checkRoute over the Path* returned from
// addRoute() because whatever was added was already in
// the cache.
// prefix_len = 0
// - victim was selected in primary cache
// - data should be "silently" migrated from primary to the
// secondary cache
// prefix_len > 0
// - there were two copies of the first prefix_len routes
// in the cache, but after the migration, there will be
// only one.
// - log the first prefix_len bytes of the secondary cache
// entry as "evicted"
if(prefix_len > 0)
{
secondary_cache->cache[min_index].setLength(prefix_len);
#ifdef DSR_CACHE_STATS
checkRoute_logall(&secondary_cache->cache[min_index],
ACTION_EVICT, 0);
#endif
}
secondary_cache->cache[min_index].setLength(0); // kill route
}
if (min_cache)
{
route.setLength(min_length + 1);
if (verbose_debug)
trace("SRC %.9f _%s_ $hit for %s in %s %s",
Scheduler::instance().clock(), net_id.dump(),
dest.dump(), min_cache == 1 ? "secondary" : "primary",
route.dump());
#ifdef DSR_CACHE_STATS
int bad = checkRoute_logall(&route, ACTION_FIND_ROUTE, 0);
stat.route_find_count += 1;
if (for_me) stat.route_find_for_me += 1;
stat.route_find_bad_count += bad ? 1 : 0;
stat.subroute_find_count += route.length() - 1;
stat.subroute_find_bad_count += bad;
#endif
return true;
}
else
{
if (verbose_debug)
trace("SRC %.9f _%s_ find-route [%d] %s->%s miss %d %.9f",
Scheduler::instance().clock(), net_id.dump(),
0, net_id.dump(), dest.dump(), 0, 0.0);
#ifdef DSR_CACHE_STATS
stat.route_find_count += 1;
if (for_me) stat.route_find_for_me += 1;
stat.route_find_miss_count += 1;
#endif
return false;
}
}
/*===========================================================================
class Cache routines
---------------------------------------------------------------------------*/
Cache::Cache(char *name, int size, MobiCache *rtcache)
{
this->name = name;
this->size = size;
cache = new Path[size];
routecache = rtcache;
victim_ptr = 0;
}
Cache::~Cache()
{
delete[] cache;
}
bool
Cache::searchRoute(const ID& dest, int& i, Path &path, int &index)
// look for dest in cache, starting at index,
//if found, return true with path s.t. cache[index] == path && path[i] == dest
{
for (; index < size; index++)
for (int n = 0 ; n < cache[index].length(); n++)
if (cache[index][n] == dest)
{
i = n;
path = cache[index];
return true;
}
return false;
}
Path*
Cache::addRoute(Path & path, int &common_prefix_len)
{
int index, m, n;
int victim;
// see if this route is already in the cache
for (index = 0 ; index < size ; index++)
{ // for all paths in the cache
for (n = 0 ; n < cache[index].length() ; n ++)
{ // for all nodes in the path
if (n >= path.length()) break;
if (cache[index][n] != path[n]) break;
}
if (n == cache[index].length())
{ // new rt completely contains cache[index] (or cache[index] is empty)
common_prefix_len = n;
for ( ; n < path.length() ; n++)
cache[index].appendToPath(path[n]);
if (verbose_debug)
routecache->trace("SRC %.9f _%s_ %s suffix-rule (len %d/%d) %s",
Scheduler::instance().clock(), routecache->net_id.dump(),
name, n, path.length(), path.dump());
goto done;
}
else if (n == path.length())
{ // new route already contained in the cache
common_prefix_len = n;
if (verbose_debug)
routecache->trace("SRC %.9f _%s_ %s prefix-rule (len %d/%d) %s",
Scheduler::instance().clock(), routecache->net_id.dump(),
name, n, cache[index].length(), cache[index].dump());
goto done;
}
else
{ // keep looking at the rest of the cache
}
}
// there are some new goodies in the new route
victim = pickVictim();
if(verbose_debug) {
routecache->trace("SRC %.9f _%s_ %s evicting %s",
Scheduler::instance().clock(), routecache->net_id.dump(),
name, cache[victim].dump());
routecache->trace("SRC %.9f _%s_ while adding %s",
Scheduler::instance().clock(), routecache->net_id.dump(),
path.dump());
}
cache[victim].reset();
CopyIntoPath(cache[victim], path, 0, path.length() - 1);
common_prefix_len = 0;
index = victim; // remember which cache line we stuck the path into
done:
#ifdef DEBUG
{
Path &p = path;
int c;
char buf[1000];
char *ptr = buf;
ptr += sprintf(buf,"Sdebug %.9f _%s_ adding ",
Scheduler::instance().clock(), routecache->net_id.dump());
for (c = 0 ; c < p.length(); c++)
ptr += sprintf(ptr,"%s [%d %.9f] ",p[c].dump(), p[c].link_type, p[c].t);
routecache->trace(buf);
}
#endif // DEBUG
// freshen all the timestamps on the links in the cache
for (m = 0 ; m < size ; m++)
{ // for all paths in the cache
#ifdef DEBUG
{
if (cache[m].length() == 0) continue;
Path &p = cache[m];
int c;
char buf[1000];
char *ptr = buf;
ptr += sprintf(buf,"Sdebug %.9f _%s_ checking ",
Scheduler::instance().clock(), routecache->net_id.dump());
for (c = 0 ; c < p.length(); c++)
ptr += sprintf(ptr,"%s [%d %.9f] ",p[c].dump(), p[c].link_type, p[c].t);
routecache->trace(buf);
}
#endif // DEBUG
for (n = 0 ; n < cache[m].length() - 1 ; n ++)
{ // for all nodes in the path
if (n >= path.length() - 1) break;
if (cache[m][n] != path[n]) break;
if (cache[m][n+1] == path[n+1])
{ // freshen the timestamps and type of the link
#ifdef DEBUG
routecache->trace("Sdebug %.9f _%s_ freshening %s->%s to %d %.9f",
Scheduler::instance().clock(), routecache->net_id.dump(),
path[n].dump(), path[n+1].dump(), path[n].link_type,
path[n].t);
#endif // DEBUG
cache[m][n].t = path[n].t;
cache[m][n].link_type = path[n].link_type;
/* NOTE: we don't check to see if we're turning a TESTED
into an UNTESTED link. Last change made rules -dam 5/19/98 */
}
}
}
return &cache[index];
}
void
Cache::noticeDeadLink(const ID&from, const ID& to)
// the link from->to isn't working anymore, purge routes containing
// it from the cache
{
for (int p = 0 ; p < size ; p++)
{ // for all paths in the cache
for (int n = 0 ; n < (cache[p].length()-1) ; n ++)
{ // for all nodes in the path
if (cache[p][n] == from && cache[p][n+1] == to)
{
if(verbose_debug)
routecache->trace("SRC %.9f _%s_ %s truncating %s %s",
Scheduler::instance().clock(),
routecache->net_id.dump(),
name, cache[p].dump(),
cache[p].owner().dump());
#ifdef DSR_CACHE_STATS
routecache->checkRoute(&cache[p], ACTION_CHECK_CACHE, 0);
routecache->checkRoute_logall(&cache[p], ACTION_DEAD_LINK, n);
#endif
if (n == 0)
cache[p].reset(); // kill the whole path
else {
cache[p].setLength(n+1); // truncate the path here
cache[p][n].log_stat = LS_UNLOGGED;
}
if(verbose_debug)
routecache->trace("SRC %.9f _%s_ to %s %s",
Scheduler::instance().clock(), routecache->net_id.dump(),
cache[p].dump(), cache[p].owner().dump());
break;
} // end if this is a dead link
} // end for all nodes
} // end for all paths
return;
}
int
Cache::pickVictim(int exclude)
// returns the index of a suitable victim in the cache
// never return exclude as the victim, but rather spare their life
{
for(int c = 0; c < size ; c++)
if (cache[c].length() == 0) return c;
int victim = victim_ptr;
while (victim == exclude)
{
victim_ptr = (victim_ptr+1 == size) ? 0 : victim_ptr+1;
victim = victim_ptr;
}
victim_ptr = (victim_ptr+1 == size) ? 0 : victim_ptr+1;
#ifdef DSR_CACHE_STATS
routecache->checkRoute(&cache[victim], ACTION_CHECK_CACHE, 0);
int bad = routecache->checkRoute_logall(&cache[victim], ACTION_EVICT, 0);
routecache->trace("SRC %.9f _%s_ evicting %d %d %s",
Scheduler::instance().clock(), routecache->net_id.dump(),
cache[victim].length() - 1, bad, name);
#endif
return victim;
}
#ifdef DSR_CACHE_STATS
/*
* Called only for the once-per-second cache check.
*/
void
MobiCache::checkRoute(Path & p,
int & subroute_bad_count,
int & link_bad_count,
double & link_bad_time,
int & link_bad_tested,
int & link_good_tested)
{
int c;
int flag = 0;
if(p.length() == 0)
return;
assert(p.length() >= 2);
for (c = 0; c < p.length() - 1; c++)
{
assert(LS_UNLOGGED == p[c].log_stat || LS_LOGGED == p[c].log_stat );
if (God::instance()->hops(p[c].getNSAddr_t(), p[c+1].getNSAddr_t()) != 1)
{ // the link's dead
if(p[c].log_stat == LS_UNLOGGED)
{
trace("SRC %.9f _%s_ check-cache [%d %d] %s->%s dead %d %.9f",
Scheduler::instance().clock(), net_id.dump(),
p.length(), c, p[c].dump(), p[c+1].dump(),
p[c].link_type, p[c].t);
p[c].log_stat = LS_LOGGED;
}
if(flag == 0)
{
subroute_bad_count += p.length() - c - 1;
flag = 1;
}
link_bad_count += 1;
link_bad_time += Scheduler::instance().clock() - p[c].t;
link_bad_tested += (p[c].link_type == LT_TESTED) ? 1 : 0;
}
else
{
if(p[c].log_stat == LS_LOGGED)
{
trace("SRC %.9f _%s_ resurrected-link [%d %d] %s->%s dead %d %.9f",
Scheduler::instance().clock(), net_id.dump(),
p.length(), c, p[c].dump(), p[c+1].dump(),
p[c].link_type, p[c].t);
p[c].log_stat = LS_UNLOGGED;
}
link_good_tested += (p[c].link_type == LT_TESTED) ? 1 : 0;
}
}
}
void
MobiCache::checkRoute(Path *p, int action, int prefix_len)
{
int c;
int subroute_bad_count = 0;
int tested = 0;
if(p->length() == 0)
return;
assert(p->length() >= 2);
assert(action == ACTION_ADD_ROUTE ||
action == ACTION_CHECK_CACHE ||
action == ACTION_NOTICE_ROUTE);
for (c = 0; c < p->length() - 1; c++)
{
if (God::instance()->hops((*p)[c].getNSAddr_t(),
(*p)[c+1].getNSAddr_t()) != 1)
{ // the link's dead
if((*p)[c].log_stat == LS_UNLOGGED)
{
trace("SRC %.9f _%s_ %s [%d %d] %s->%s dead %d %.9f",
Scheduler::instance().clock(), net_id.dump(),
action_name[action], p->length(), c,
(*p)[c].dump(), (*p)[c+1].dump(),
(*p)[c].link_type, (*p)[c].t);
(*p)[c].log_stat = LS_LOGGED;
}
if(subroute_bad_count == 0)
subroute_bad_count = p->length() - c - 1;
}
else
{
if((*p)[c].log_stat == LS_LOGGED)
{
trace("SRC %.9f _%s_ resurrected-link [%d %d] %s->%s dead %d %.9f",
Scheduler::instance().clock(), net_id.dump(),
p->length(), c, (*p)[c].dump(), (*p)[c+1].dump(),
(*p)[c].link_type, (*p)[c].t);
(*p)[c].log_stat = LS_UNLOGGED;
}
}
tested += (*p)[c].link_type == LT_TESTED ? 1 : 0;
}
/*
* Add Route or Notice Route actually did something
*/
if(prefix_len < p->length())
{
switch(action)
{
case ACTION_ADD_ROUTE:
stat.route_add_count += 1;
stat.route_add_bad_count += subroute_bad_count ? 1 : 0;
stat.subroute_add_count += p->length() - prefix_len - 1;
stat.subroute_add_bad_count += subroute_bad_count;
stat.link_add_tested += tested;
break;
case ACTION_NOTICE_ROUTE:
stat.route_notice_count += 1;
stat.route_notice_bad_count += subroute_bad_count ? 1 : 0;
stat.subroute_notice_count += p->length() - prefix_len - 1;
stat.subroute_notice_bad_count += subroute_bad_count;
stat.link_notice_tested += tested;
break;
}
}
}
#endif /* DSR_CACHE_STATS */
path.cc
/* -*- 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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*/
/* Ported from CMU/Monarch's code, nov'98 -Padma.*/
/* path.cc
handles source routes
$Id: AllCode__.html 980 2002-12-12 09:36:58Z ffilali $
*/
extern "C" {
#include
#include
}
#include
#include
#include
#include "path.h"
/*===========================================================================
global statics
---------------------------------------------------------------------------*/
ID invalid_addr(0xffffffff,::NONE);
ID IP_broadcast(IP_BROADCAST,::IP);
/*===========================================================================
ID methods
---------------------------------------------------------------------------*/
void
ID::unparse(FILE *out) const
{
fprintf(out,"%d",(int) addr);
}
char *
ID::dump() const
{
static char buf[MAX_SR_LEN+1][50];
static int which = 0;
char *ptr = buf[which];
which = (which + 1) % (MAX_SR_LEN+1);
assert(type == ::NONE || type == ::MAC || type == ::IP);
if (type == ::IP)
sprintf(ptr,"%d",(int) addr);
else if (type == ::NONE)
sprintf(ptr,"NONE");
else
sprintf(ptr,"0x%x",(int) addr);
return ptr;
}
/*===========================================================================
Path methods
---------------------------------------------------------------------------*/
/* rep invariants:
-1 <= cur_index <= len (neither bound is really hard)
0 <= len < MAX_SR_LEN
*/
Path::Path(int route_len, const ID *route)
{
path = new ID[MAX_SR_LEN];
assert(route_len <= MAX_SR_LEN);
// route_len = (route == NULL : 0 ? route_len);
// a more cute solution, follow the above with the then clause
if (route != NULL)
{
for (int c = 0; c < route_len; c++)
{
path[c] = route[c];
}
len = route_len;
}
else
{
len = 0;
}
cur_index = 0;
}
Path::Path()
{
path = new ID[MAX_SR_LEN];
len = 0;
cur_index = 0;
}
Path::Path(const struct sr_addr *addrs, int len)
{ /* make a path from the bits of an NS source route header */
assert(len <= MAX_SR_LEN);
path = new ID[MAX_SR_LEN];
for (int i = 0 ; i < len ; i++)
path[i] = ID(addrs[i]);
this->len = len;
cur_index = 0;
}
Path::Path(const struct hdr_sr *srh)
{ /* make a path from the bits of an NS source route header */
path = new ID[MAX_SR_LEN];
if (srh->valid_ != 1)
{
len = 0;
cur_index = 0;
return;
}
len = srh->num_addrs_;
cur_index = srh->cur_addr_;
assert(len <= MAX_SR_LEN);
for (int i = 0 ; i < len ; i++)
path[i] = ID(srh->addrs[i]);
}
void
Path::fillSR(struct hdr_sr *srh)
{
for (int i = 0 ; i < len ; i++)
{
path[i].fillSRAddr(srh->addrs[i]);
}
srh->num_addrs() = len;
srh->cur_addr() = cur_index;
}
Path::Path(const Path& old)
{
path = new ID[MAX_SR_LEN];
if (old.path != NULL)
{
for (int c = 0; c < old.len; c++)
path[c] = old.path[c];
len = old.len;
}
else
{
len = 0;
}
cur_index = old.cur_index;
path_owner = old.path_owner;
}
Path::~Path()
{
delete[] path;
}
void
Path::operator=(const Path &rhs)
// makes the lhs a copy of the rhs: lhs may share data with
// the rhs such that changes to one will be seen by the other
// use the provided copy operation if you don't want this.
{
/* OLD NOTE:
we save copying the path by doing a delete[] path; path = rhs.path;
but then the following code will be fatal (it calls delete[]
twice on the same address)
{ Path p1();
{ Path p2();
p2 = p1;
}
}
you'd have to implement reference counts on the path array to
save copying the path.
NEW NOTE: we just copy like everything else
*/
if (this != &rhs)
{// beware of path = path (see Stroustrup p. 238)
cur_index = rhs.cur_index;
path_owner = rhs.path_owner;
len = rhs.len;
for (int c = 0 ; c < len ; c++)
path[c] = rhs.path[c];
}
// note: i don't return *this cause I don't think assignments should
// be expressions (and it has slightly incorrect semantics: (a=b) should
// have the value of b, not the new value of a)
}
bool
Path::operator==(const Path &rhs)
{
int c;
if (len != rhs.len) return false;
for (c = 0; c < len; c++)
if (path[c] != rhs.path[c]) return false;
return true;
}
void
Path::appendPath(Path& p)
{
int i;
for (i = 0; i < p.length() ; i++)
{
path[len] = p[i];
len++;
if (len > MAX_SR_LEN)
{
fprintf(stderr,"DFU: overflow in appendPath len2 %d\n",
p.length());
len--;
return;
}
}
}
void
Path::removeSection(int from, int to)
// the elements at indices from -> to-1 are removed from the path
{
int i,j;
if (to <= from) return;
if (cur_index > from) cur_index = cur_index - (to - from);
for (i = to, j = 0; i < len ; i++, j++)
path[from + j] = path[i];
len = from + j;
}
Path
Path::copy() const
{
Path p(len,path);
p.cur_index = cur_index;
p.path_owner = path_owner;
return p;
}
void
Path::copyInto(Path& to) const
{
to.cur_index = cur_index;
to.len = len;
for (int c = 0 ; c < len ; c++)
to.path[c] = path[c];
to.path_owner = path_owner;
}
Path
Path::reverse() const
// return an identical path with the index pointing to the same
// host, but the path in reverse order
{
if (len == 0) return *this;
Path p;
int from, to;
for (from = 0, to = (len-1) ; from < len ; from++,to--)
p.path[to] = path[from];
p.len = len;
p.cur_index = (len - 1) - cur_index;
return p;
}
void
Path::reverseInPlace()
{
if (len == 0) return;
int fp,bp; // forward ptr, back ptr
ID temp;
for (fp = 0, bp = (len-1) ; fp < bp ; fp++, bp--)
{
temp = path[fp];
path[fp] = path[bp];
path[bp] = temp;
}
cur_index = (len - 1) - cur_index;
}
int
Path::size() const
{
// this should be more clever and ask the id's what their sizes are.
return len*4;
}
bool
Path::member(const ID& id) const
// rtn true iff id is in path
{
return member(id, invalid_addr);
}
bool
Path::member(const ID& id, const ID& MAC_id) const
// rtn true iff id or MAC_id is in path
{
for (int c = 0; c < len ; c++)
if (path[c] == id || path[c] == MAC_id)
return true;
return false;
}
void
Path::unparse(FILE *out) const
{
// change to put ()'s around the cur_index entry?
if (len==0)
{
fprintf(out,"");
return;
}
for (int c = 0 ; c < len-1 ; c ++)
{
if (c == cur_index) fprintf(out,"(");
path[c].unparse(out);
if (c == cur_index) fprintf(out,")");
fprintf(out,",");
}
if (len-1 == cur_index) fprintf(out,"(");
path[len-1].unparse(out);
if (len-1 == cur_index) fprintf(out,")");
}
char *
Path::dump() const
{
static int which = 0;
static char buf[4][100];
char *ptr = buf[which];
char *rtn_buf = ptr;
which = (which + 1) % 4;
if (len == 0)
{
sprintf(rtn_buf,"[]");
return rtn_buf;
}
*ptr++ = '[';
for (int c = 0 ; c < len ; c ++)
{
if (c == cur_index) *ptr++ = '(';
sprintf(ptr,"%s%s ",path[c].dump(), c == cur_index ? ")" : "");
ptr += strlen(ptr);
}
*ptr++ = ']';
*ptr++ = '\0';
return rtn_buf;
}
void
compressPath(Path &path)
// take a path and remove any double backs from it
// eg: A B C B D --> A B D
{
// idea: walk one pointer from begining
// for each elt1 start at end of path and walk a pointer backwards (elt2)
// if forward pointer = backward pointer, go on and walk foward one more
// if elt1 = elt2 then append {(elt2 + 1) to end} after forward pointer
// update length of path (we just cut out a loopback) and walk forward
// when forward walking pointer reaches end of path we're done
int fp = 0, bp; // the forward walking ptr and the back walking ptr
while (fp < path.len)
{
for (bp = path.len - 1; bp != fp; bp--)
{
if (path.path[fp] == path.path[bp])
{ int from, to;
for (from = bp, to = fp;
from < path.len ;
from++, to++)
path.path[to] = path.path[from];
path.len = to;
break;
} // end of removing double back
} // end of scaning to check for double back
fp++; // advance the forward moving pointer
}
}
void
CopyIntoPath(Path& to, const Path& from, int start, int stop)
// sets to[0->(stop-start)] = from[start->stop]
{
assert(start >= 0 && stop < from.len);
int f, t,c ; // from and to indices
for(f = start, t = 0; f <= stop; f++, t++)
to.path[t] = from.path[f];
if (to.len < stop - start + 1) to.len = stop - start + 1;
for (c = to.len - 1; c >= 0; c--)
{
if (to.path[c] == to.owner()) break;
if (to.path[c] == ((Path &)from).owner())
{
to.owner() = ((Path &)from).owner();
break;
}
}
}
void
Path::checkpath() const
{
for(int c = 0; c < MAX_SR_LEN; c++)
{
assert(path[c].type == NONE ||
path[c].type == MAC ||
path[c].type == IP);
}
}
requesttable.cc
/* -*- 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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*/
/* Ported from CMU/Monarch's code, nov'98 -Padma.*/
/* requesttable.h
implement a table to keep track of the most current request
number we've heard from a node in terms of that node's id
$Id: AllCode__.html 980 2002-12-12 09:36:58Z ffilali $
*/
#include "path.h"
#include "constants.h"
#include "requesttable.h"
RequestTable::RequestTable(int s): size(s)
{
table = new Entry[size];
size = size;
ptr = 0;
}
RequestTable::~RequestTable()
{
delete[] table;
}
int
RequestTable::find(const ID& net_id, const ID& MAC_id) const
{
for (int c = 0 ; c < size ; c++)
if (table[c].net_id == net_id || table[c].MAC_id == MAC_id)
return c;
return size;
}
int
RequestTable::get(const ID& id) const
{
int existing_entry = find(id, id);
if (existing_entry >= size)
{
return 0;
}
return table[existing_entry].req_num;
}
Entry*
RequestTable::getEntry(const ID& id)
{
int existing_entry = find(id, id);
if (existing_entry >= size)
{
table[ptr].MAC_id = ::invalid_addr;
table[ptr].net_id = id;
table[ptr].req_num = 0;
table[ptr].last_arp = 0.0;
table[ptr].rt_reqs_outstanding = 0;
table[ptr].last_rt_req = -(rt_rq_period + 1.0);
existing_entry = ptr;
ptr = (ptr+1)%size;
}
return &(table[existing_entry]);
}
void
RequestTable::insert(const ID& net_id, int req_num)
{
insert(net_id,::invalid_addr,req_num);
}
void
RequestTable::insert(const ID& net_id, const ID& MAC_id, int req_num)
{
int existing_entry = find(net_id, MAC_id);
if (existing_entry < size)
{
if (table[existing_entry].MAC_id == ::invalid_addr)
table[existing_entry].MAC_id = MAC_id; // handle creations by getEntry
table[existing_entry].req_num = req_num;
return;
}
// otherwise add it in
table[ptr].MAC_id = MAC_id;
table[ptr].net_id = net_id;
table[ptr].req_num = req_num;
table[ptr].last_arp = 0.0;
table[ptr].rt_reqs_outstanding = 0;
table[ptr].last_rt_req = -(rt_rq_period + 1.0);
ptr = (ptr+1)%size;
}
routecache.cc
/* -*- 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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*/
/* Ported from CMU/Monarch's code, nov'98 -Padma.*/
/* routecache.cc
$Id: AllCode__.html 980 2002-12-12 09:36:58Z ffilali $
handles routes
*/
extern "C" {
#include
#include
};
#undef DEBUG
#include "path.h"
#include "routecache.h"
#include
static const int verbose_debug = 0;
/*===============================================================
OTcl definition
----------------------------------------------------------------*/
static class RouteCacheClass : public TclClass {
public:
RouteCacheClass() : TclClass("RouteCache") {}
TclObject* create(int, const char*const*) {
return makeRouteCache();
}
} class_RouteCache;
/*===============================================================
Constructors
----------------------------------------------------------------*/
RouteCache::RouteCache():
#ifdef DSR_CACHE_STATS
mh(this),
#endif
MAC_id(invalid_addr), net_id(invalid_addr)
{
#ifdef DSR_CACHE_STATS
stat.reset();
#endif
}
RouteCache::~RouteCache()
{
}
int
RouteCache::command(int argc, const char*const* argv)
{
if(argc == 2)
{
if(strcasecmp(argv[1], "startdsr") == 0)
{
#ifdef DSR_CACHE_STATS
mh.start();
#endif
return TCL_OK;
}
}
else if(argc == 3)
{
if (strcasecmp(argv[1], "ip-addr") == 0) {
net_id = ID(atoi(argv[2]), ::IP);
return TCL_OK;
}
else if(strcasecmp(argv[1], "mac-addr") == 0) {
MAC_id = ID(atoi(argv[2]), ::MAC);
return TCL_OK;
}
/* must be a looker up */
TclObject *obj = (TclObject*) TclObject::lookup(argv[2]);
if(obj == 0)
return TCL_ERROR;
if(strcasecmp(argv[1], "log-target") == 0 ||\
strcasecmp(argv[1], "tracetarget") == 0) {
logtarget = (Trace*) obj;
return TCL_OK;
}
return TCL_ERROR;
}
return TclObject::command(argc, argv);
}
void
RouteCache::trace(char* fmt, ...)
{
va_list ap;
if (!logtarget) return;
va_start(ap, fmt);
vsprintf(logtarget->buffer(), fmt, ap);
logtarget->dump();
va_end(ap);
}
void
RouteCache::dump(FILE *out)
{
fprintf(out, "Route cache dump unimplemented\n");
fflush(out);
}
///////////////////////////////////////////////////////////////////////////
extern bool cache_ignore_hints;
extern bool cache_use_overheard_routes;
#define STOP_PROCESSING 0
#define CONT_PROCESSING 1
int
RouteCache::pre_addRoute(const Path& route, Path& rt,
Time t, const ID& who_from)
{
assert(!(net_id == invalid_addr));
if (route.length() < 2)
return STOP_PROCESSING; // we laugh in your face
if(verbose_debug)
trace("SRC %.9f _%s_ adding rt %s from %s",
Scheduler::instance().clock(), net_id.dump(),
route.dump(), who_from.dump());
if (route[0] != net_id && route[0] != MAC_id)
{
fprintf(stderr,"%.9f _%s_ adding bad route to cache %s %s\n",
t, net_id.dump(), who_from.dump(), route.dump());
return STOP_PROCESSING;
}
rt = (Path&) route; // cast away const Path&
rt.owner() = who_from;
Link_Type kind = LT_TESTED;
for (int c = 0; c < rt.length(); c++)
{
rt[c].log_stat = LS_UNLOGGED;
if (rt[c] == who_from) kind = LT_UNTESTED; // remaining ids came from $
rt[c].link_type = kind;
rt[c].t = t;
}
return CONT_PROCESSING;
}
int
RouteCache::pre_noticeRouteUsed(const Path& p, Path& stub,
Time t, const ID& who_from)
{
int c;
bool first_tested = true;
if (p.length() < 2)
return STOP_PROCESSING;
if (cache_ignore_hints == true)
return STOP_PROCESSING;
for (c = 0; c < p.length() ; c++) {
if (p[c] == net_id || p[c] == MAC_id) break;
}
if (c == p.length() - 1)
return STOP_PROCESSING; // path contains only us
if (c == p.length()) { // we aren't in the path...
if (cache_use_overheard_routes) {
// assume a link from us to the person who
// transmitted the packet
if (p.index() == 0) {
/* must be a route request */
return STOP_PROCESSING;
}
stub.reset();
stub.appendToPath(net_id);
int i = p.index() - 1;
for ( ; i < p.length() && !stub.full() ; i++) {
stub.appendToPath(p[i]);
}
// link to xmiter might be unidirectional
first_tested = false;
}
else {
return STOP_PROCESSING;
}
}
else { // we are in the path, extract the subpath
CopyIntoPath(stub, p, c, p.length() - 1);
}
Link_Type kind = LT_TESTED;
for (c = 0; c < stub.length(); c++) {
stub[c].log_stat = LS_UNLOGGED;
// remaining ids came from $
if (stub[c] == who_from)
kind = LT_UNTESTED;
stub[c].link_type = kind;
stub[c].t = t;
}
if (first_tested == false)
stub[0].link_type = LT_UNTESTED;
return CONT_PROCESSING;
}
#undef STOP_PROCESSING
#undef CONT_PROCESSING
//////////////////////////////////////////////////////////////////////
#ifdef DSR_CACHE_STATS
void
MobiHandler::handle(Event *) {
cache->periodic_checkCache();
Scheduler::instance().schedule(this, &intr, interval);
}
int
RouteCache::checkRoute_logall(Path *p, int action, int start)
{
int c;
int subroute_bad_count = 0;
if(p->length() == 0)
return 0;
assert(p->length() >= 2);
assert(action == ACTION_DEAD_LINK ||
action == ACTION_EVICT ||
action == ACTION_FIND_ROUTE);
for (c = start; c < p->length() - 1; c++)
{
if (God::instance()->hops((*p)[c].getNSAddr_t(), (*p)[c+1].getNSAddr_t()) != 1)
{
trace("SRC %.9f _%s_ %s [%d %d] %s->%s dead %d %.9f",
Scheduler::instance().clock(), net_id.dump(),
action_name[action], p->length(), c,
(*p)[c].dump(), (*p)[c+1].dump(),
(*p)[c].link_type, (*p)[c].t);
if(subroute_bad_count == 0)
subroute_bad_count = p->length() - c - 1;
}
}
return subroute_bad_count;
}
#endif /* DSR_CACHE_STAT */
arp.cc
/*
* Copyright (c) 1998 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 Network Research
* Group at Lawrence Berkeley National Laboratory.
* 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.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$";
#endif
#include "object.h"
#include "packet.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "emulate/net.h"
#include "emulate/ether.h"
#include "emulate/internet.h"
//
// arp.cc -- this object may be used within nse as
// an ARP requestor/responder. Only the request side
// is implemented now [5/98]
//
class ArpAgent : public NsObject, public IOHandler {
public:
ArpAgent();
~ArpAgent();
protected:
struct acache_entry {
in_addr ip;
ether_addr ether;
char code; // 'D' - dynamic, 'P' - publish
};
char icode(const char*);
acache_entry* find(in_addr&);
void insert(in_addr&, ether_addr&, char code);
void dispatch(int);
int sendreq(in_addr&);
int sendresp(ether_addr&, in_addr&, ether_addr&);
int resolve(const char* host, char*& result, int sendreq);
void doreq(ether_arp*);
void doreply(ether_arp*);
void recv(Packet*, Handler*) { abort(); }
int command(int, const char*const*);
Network* net_;
ether_header eh_template_;
ether_arp ea_template_;
ether_addr my_ether_;
in_addr my_ip_;
int base_size_; // size of rcv buf
u_char* rcv_buf_;
acache_entry* acache_; // arp mapping cache
int nacache_; // # entries in cache
int cur_; // cur posn in cache
int pending_; // resolve pending?
};
static class ArpAgentClass : public TclClass {
public:
ArpAgentClass() : TclClass("ArpAgent") {}
TclObject* create(int , const char*const*) {
return (new ArpAgent());
}
} class_arpagent;
ArpAgent::ArpAgent() : net_(NULL), pending_(0)
{
/* dest addr is broadcast */
eh_template_.ether_dhost[0] = 0xff;
eh_template_.ether_dhost[1] = 0xff;
eh_template_.ether_dhost[2] = 0xff;
eh_template_.ether_dhost[3] = 0xff;
eh_template_.ether_dhost[4] = 0xff;
eh_template_.ether_dhost[5] = 0xff;
/* src addr is mine */
memcpy(&eh_template_.ether_shost, &my_ether_, ETHER_ADDR_LEN);
/* type is ARP */
eh_template_.ether_type = htons(ETHERTYPE_ARP);
ea_template_.ea_hdr.ar_hrd = htons(ARPHRD_ETHER);
ea_template_.ea_hdr.ar_pro = htons(ETHERTYPE_IP);
ea_template_.ea_hdr.ar_hln = ETHER_ADDR_LEN;
ea_template_.ea_hdr.ar_pln = 4; /* ip addr len */
ea_template_.ea_hdr.ar_op = htons(ARPOP_REQUEST);
memcpy(&ea_template_.arp_sha, &my_ether_, ETHER_ADDR_LEN); /* sender hw */
memset(&ea_template_.arp_spa, 0, 4); /* sender IP */
memset(&ea_template_.arp_tha, 0, ETHER_ADDR_LEN); /* target hw */
memset(&ea_template_.arp_tpa, 0, 4); /* target hw */
base_size_ = sizeof(eh_template_) + sizeof(ea_template_);
rcv_buf_ = new u_char[base_size_];
bind("cachesize_", &nacache_);
acache_ = new acache_entry[nacache_];
memset(acache_, 0, nacache_*sizeof(acache_entry));
cur_ = nacache_;
}
ArpAgent::~ArpAgent()
{
delete[] rcv_buf_;
delete[] acache_;
}
ArpAgent::acache_entry*
ArpAgent::find(in_addr& target)
{
int n = nacache_;
acache_entry* ae = &acache_[n-1];
while (--n >= 0) {
if (ae->ip.s_addr == target.s_addr) {
return (ae);
}
--ae;
}
return (NULL);
}
char
ArpAgent::icode(const char *how)
{
if (strcmp(how, "publish") == 0)
return 'P';
return 'D';
}
void
ArpAgent::insert(in_addr& target, ether_addr& eaddr, char code)
{
acache_entry* ae;
if (--cur_ < 0)
cur_ = nacache_ - 1;
ae = &acache_[cur_];
ae->ip = target;
ae->ether = eaddr;
ae->code = code;
//printf("INSERTED inet %s, ether %s\n",
//inet_ntoa(target), Ethernet::etheraddr_string((u_char*)&eaddr));
return;
}
int
ArpAgent::sendreq(in_addr& target)
{
int pktsz = sizeof(eh_template_) + sizeof(ea_template_);
if (pktsz < 64)
pktsz = 64;
u_char* buf = new u_char[pktsz];
memset(buf, 0, pktsz);
ether_header* eh = (ether_header*) buf;
ether_arp* ea = (ether_arp*) (buf + sizeof(eh_template_));
*eh = eh_template_; /* set ether header */
*ea = ea_template_; /* set ether/IP arp pkt */
memcpy(ea->arp_tpa, &target, sizeof(target));
if (net_->send(buf, pktsz) < 0) {
fprintf(stderr,
"ArpAgent(%s): sendpkt (%p, %d): %s\n",
name(), buf, pktsz, strerror(errno));
return (-1);
}
delete[] buf;
return (0);
}
/*
* resp: who to send response to
* tip: the IP address we are responding for
* tea: the ether address we want to advertise with tip
*/
int
ArpAgent::sendresp(ether_addr& dest, in_addr& tip, ether_addr& tea)
{
int pktsz = sizeof(eh_template_) + sizeof(ea_template_);
if (pktsz < 64)
pktsz = 64;
u_char* buf = new u_char[pktsz];
memset(buf, 0, pktsz);
ether_header* eh = (ether_header*) buf;
ether_arp* ea = (ether_arp*) (buf + sizeof(eh_template_));
// destination link layer address is back to sender
// (called dest here)
*eh = eh_template_; /* set ether header */
memcpy(eh->ether_dhost, &dest, ETHER_ADDR_LEN);
// set code as ARP reply
*ea = ea_template_; /* set ether/IP arp pkt */
ea->ea_hdr.ar_op = htons(ARPOP_REPLY);
// make it look like a regular arp reply
memcpy(ea->arp_tpa, ea->arp_spa, sizeof(in_addr));
memcpy(ea->arp_tha, ea->arp_sha, sizeof(in_addr));
memcpy(ea->arp_sha, &tea, ETHER_ADDR_LEN);
memcpy(ea->arp_spa, &tip, ETHER_ADDR_LEN);
if (net_->send(buf, pktsz) < 0) {
fprintf(stderr,
"ArpAgent(%s): sendpkt (%p, %d): %s\n",
name(), buf, pktsz, strerror(errno));
return (-1);
}
delete[] buf;
return (0);
}
/*
* receive pkt from network:
* note that net->recv() gives us the pkt starting
* just BEYOND the frame header
*/
void
ArpAgent::dispatch(int)
{
double ts;
sockaddr sa;
int cc = net_->recv(rcv_buf_, base_size_, sa, ts);
if (cc < int(base_size_ - sizeof(ether_header))) {
if (cc == 0)
return;
fprintf(stderr,
"ArpAgent(%s): recv small pkt (%d) [base sz:%d]: %s\n",
name(), cc, base_size_, strerror(errno));
return;
}
ether_arp* ea = (ether_arp*) rcv_buf_;
int op = ntohs(ea->ea_hdr.ar_op);
switch (op) {
case ARPOP_REPLY:
doreply(ea);
break;
case ARPOP_REQUEST:
doreq(ea);
break;
default:
fprintf(stderr,
"ArpAgent(%s): cannot interpret ARP op %d\n",
name(), op);
return;
}
return;
}
/*
* process an ARP reply frame -- insert into cache
*/
void
ArpAgent::doreply(ether_arp* ea)
{
/*
* reply will be from the replier's point of view,
* so, look in the sender ha/pa fields for the info
* we want
*/
in_addr t;
ether_addr e;
memcpy(&t, ea->arp_spa, 4); // copy IP address
memcpy(&e, ea->arp_sha, ETHER_ADDR_LEN);
insert(t, e, 'D');
return;
}
/*
* process an ARP request frame
*/
void
ArpAgent::doreq(ether_arp* ea)
{
in_addr t;
memcpy(&t, ea->arp_tpa, 4); // requested IP addr
acache_entry *ae;
if ((ae = find(t)) == NULL) {
//printf("doreq: didn't find mapping for IP addr %s\n",
//inet_ntoa(t));
return;
}
if (ae->code == 'P') {
// return answer to the sender's hardware addr
ether_addr dst;
memcpy(&dst, ea->arp_sha, ETHER_ADDR_LEN);
sendresp(dst, t, ae->ether);
}
return;
}
extern "C" {
ether_addr* ether_aton();
}
int
ArpAgent::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
if (argc == 2) {
if (strcmp(argv[1], "network") == 0) {
if (net_ == NULL)
tcl.result("");
else
tcl.result(net_->name());
return (TCL_OK);
}
} else if (argc == 3) {
if (strcmp(argv[1], "network") == 0) {
net_ = (Network *)TclObject::lookup(argv[2]);
if (net_ != 0) {
link(net_->rchannel(), TCL_READABLE);
return (TCL_OK);
} else {
fprintf(stderr,
"ArpAgent(%s): unknown network %s\n",
name(), argv[2]);
return (TCL_ERROR);
}
return(TCL_OK);
}
if (strcmp(argv[1], "myether") == 0) {
my_ether_ = *(::ether_aton(argv[2]));
memcpy(&eh_template_.ether_shost, &my_ether_,
ETHER_ADDR_LEN);
memcpy(&ea_template_.arp_sha,
&my_ether_, ETHER_ADDR_LEN);
return (TCL_OK);
}
if (strcmp(argv[1], "myip") == 0) {
u_long a = inet_addr(argv[2]);
if (a == 0)
return (TCL_ERROR);
in_addr ia;
ia.s_addr = a;
my_ip_ = ia;
memcpy(&ea_template_.arp_spa,
&my_ip_, 4);
return (TCL_OK);
}
if (strcmp(argv[1], "lookup") == 0) {
char *p = NULL;
if (resolve(argv[2], p, 0) < 0)
return (TCL_ERROR);
if (p)
tcl.result(p);
return (TCL_OK);
}
if (strcmp(argv[1], "resolve") == 0) {
char *p = NULL;
if (resolve(argv[2], p, 1) < 0)
return (TCL_ERROR);
if (p)
tcl.resultf("%s", p);
return (TCL_OK);
}
} else if (argc == 5) {
// $obj insert iaddr eaddr how
if (strcmp(argv[1], "insert") == 0) {
u_long a = inet_addr(argv[2]);
if (a == 0)
return (TCL_ERROR);
in_addr ia;
ia.s_addr = a;
ether_addr ea = *(::ether_aton(argv[3]));
insert(ia, ea, icode(argv[4]));
return (TCL_OK);
}
}
return (NsObject::command(argc, argv));
}
int
ArpAgent::resolve(const char* host, char*& result, int doreq)
{
u_long a = inet_addr(host);
in_addr ia;
ia.s_addr = a;
acache_entry* ae;
if ((ae = find(ia)) == NULL) {
result = NULL;
if (doreq)
return(sendreq(ia));
return (0);
}
result = Ethernet::etheraddr_string((u_char*) &ae->ether);
return (1);
}
ether.cc
/*-
* Copyright (c) 1993-1994, 1998 The 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 University of
* California, Berkeley and the Network Research Group at
* Lawrence Berkeley Laboratory.
* 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.
*/
#if 0
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (LBL)";
#endif
#endif
#include
#include
#include
#include
#include
#include
#include "config.h"
#include "ether.h"
char Ethernet::hex[] = "0123456789abcdef";
void
Ethernet::ether_print(const u_char *bp)
{
const struct ether_header *ep;
ep = (const struct ether_header *)bp;
printf("src: %s\n",
etheraddr_string(ep->ether_shost));
printf("dst: %s\n",
etheraddr_string(ep->ether_dhost));
printf("prot: %hx\n",
ntohs(ep->ether_type));
}
char *
Ethernet::etheraddr_string(const u_char *ep)
{
unsigned i, j;
register char *cp;
static char buf[sizeof("00:00:00:00:00:00")];
cp = buf;
if ((j = *ep >> 4) != 0)
*cp++ = hex[j];
*cp++ = hex[*ep++ & 0xf];
for (i = 5; (int)--i >= 0;) {
*cp++ = ':';
if ((j = *ep >> 4) != 0)
*cp++ = hex[j];
*cp++ = hex[*ep++ & 0xf];
}
*cp = '\0';
return(buf);
}
#include
u_char *
Ethernet::nametoaddr(const char *devname)
{
static struct ifreq ifr;
int s;
memset(&ifr, 0, sizeof(ifr));
if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
fprintf(stderr, "Ethernet::nametoaddr-- failed socket\n");
return NULL;
}
strncpy(ifr.ifr_name, devname, sizeof(ifr.ifr_name));
if (ioctl(s, SIOCGIFADDR, (char *)&ifr) < 0) {
fprintf(stderr, "Ethernet::nametoaddr-- failed SIOCGIFADDR\n");
return NULL;
}
return ((u_char*) &ifr.ifr_data);
}
icmp.cc
/*
* Copyright (c) 1998 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 Network Research
* Group at Lawrence Berkeley National Laboratory.
* 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.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$";
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include "agent.h"
#include "scheduler.h"
#include "packet.h"
#include "emulate/net.h"
#include "emulate/internet.h"
#ifndef IPPROTO_GGP
#define IPPROTO_GGP 3
#endif // IPPROTO_GGP
//
// icmp.cc -- a very limited-functionality set of icmp routines
//
class IcmpAgent : public Agent {
public:
IcmpAgent();
void recv(Packet*, Handler*) { abort(); }
protected:
void sendredirect(in_addr& me, in_addr& target, in_addr& dest, in_addr& gw);
int command(int argc, const char*const* argv);
int ttl_;
};
static class IcmpAgentClass : public TclClass {
public:
IcmpAgentClass() : TclClass("Agent/IcmpAgent") {}
TclObject* create(int , const char*const*) {
return (new IcmpAgent());
}
} class_icmpsagent;
IcmpAgent::IcmpAgent() : Agent(PT_LIVE)
{
bind("ttl_", &ttl_);
}
/*
* sendredirect -- send a packet to "target" containing a redirect
* for the network specified by "dst", so that the gateway "gw" is used
* also, forge the source address so as to appear to come from "me"
*/
void
IcmpAgent::sendredirect(in_addr& me, in_addr& target, in_addr& dst, in_addr& gw)
{
// make a simulator packet to hold the IP packet, which in turn
// holds: ip header, icmp header, embedded ip header, plus 64 bits
// data
int iplen = sizeof(ip) + 8 + sizeof(ip) + 8;
Packet* p = allocpkt(iplen);
hdr_cmn* hc = (hdr_cmn*)p->access(off_cmn_);
ip* iph = (ip*) p->accessdata();
hc->size() = iplen;
// make an IP packet ready to send to target
// size will be min icmp + a dummy'd-up IP header
Internet::makeip(iph, iplen, ttl_, IPPROTO_ICMP, me, target);
// make an ICMP host redirect, set the gwaddr field
icmp* icp = (icmp*) (iph + 1);
icp->icmp_gwaddr = gw;
// make a dummy IP packet to go in the ICMP data, which will
// be used to indicate to the end host which routing table
// entry to update
ip* dummyhdr = (ip*)((u_char*)icp + 8); // past icmp hdr
// deprecated protocol inside
Internet::makeip(dummyhdr, 20, 254, IPPROTO_GGP, target, dst);
u_short *port = (u_short*) (dummyhdr + 1); // past ip hdr
*port++ = htons(9); // discard port
*port = htons(9); // discard port
icp->icmp_cksum = 0;
icp->icmp_type = ICMP_REDIRECT;
icp->icmp_code = ICMP_REDIRECT_HOST;
icp->icmp_cksum = Internet::in_cksum((u_short*)icp,
8 + sizeof(ip) + 8);
send(p, 0);
return;
}
int
IcmpAgent::command(int argc, const char*const* argv)
{
if (argc > 5) {
// $obj send name src dst [...stuff...]
if (strcmp(argv[1], "send") == 0) {
if (strcmp(argv[2], "redirect") == 0 &&
argc == 7) {
// $obj send redirect src target dst gwaddr
// as src, send to targ, so that it changes
// its route to dst to use gwaddr
u_long s, t, d, g;
s = inet_addr(argv[3]);
t = inet_addr(argv[4]);
d = inet_addr(argv[5]);
g = inet_addr(argv[6]);
in_addr src, targ, dst, gw;
src.s_addr = s;
targ.s_addr = t;
dst.s_addr = d;
gw.s_addr = g;
sendredirect(src, targ, dst, gw);
return (TCL_OK);
}
}
}
return (Agent::command(argc, argv));
}
internet.cc
#include
#include
#include
#include
#include
#include
#include
#include
#include "emulate/internet.h"
#include "scheduler.h"
/*
* in_cksum --
* Checksum routine for Internet Protocol family headers (C Version)
* [taken from ping.c]
*/
u_short
Internet::in_cksum(u_short* addr, int len)
{
register int nleft = len;
register u_short *w = addr;
register int sum = 0;
u_short answer = 0;
/*
* Our algorithm is simple, using a 32 bit accumulator (sum), we add
* sequential 16 bit words to it, and at the end, fold back all the
* carry bits from the top 16 bits into the lower 16 bits.
*/
while (nleft > 1) {
sum += *w++;
nleft -= 2;
}
/* mop up an odd byte, if necessary */
if (nleft == 1) {
*(u_char *)(&answer) = *(u_char *)w ;
sum += answer;
}
/* add back carry outs from top 16 bits to low 16 bits */
sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
sum += (sum >> 16); /* add carry */
answer = ~sum; /* truncate to 16 bits */
return(answer);
}
#include
#include
void
Internet::print_ip(ip *ip)
{
u_short off = ntohs(ip->ip_off);
printf("IP v:%d, ihl:%d, tos:0x%x, id:%d, off:%d [df:%d, mf:%d], "
"sum:%d, prot:%d\n",
ip->ip_v, ip->ip_hl, ip->ip_tos, ntohs(ip->ip_id),
off & IP_OFFMASK,
(off & IP_DF) ? 1 : 0,
(off & IP_MF) ? 1 : 0,
ip->ip_sum, ip->ip_p);
printf("IP src:%s, ", inet_ntoa(ip->ip_src));
printf("dst: %s\n", inet_ntoa(ip->ip_dst));
printf("IP len:%d ttl: %d\n",
ntohs(ip->ip_len), ip->ip_ttl);
}
/*
* cons up a basic-looking ip header, no options
* multi-byte quantities are assumed to be in HOST byte order
*/
void
Internet::makeip(ip* iph, u_short len, u_char ttl, u_char proto, in_addr& src,
in_addr& dst)
{
u_char *p = (u_char*) iph;
*p = 0x45; /* ver + hl */
iph->ip_tos = 0;
iph->ip_len = htons(len);
iph->ip_id = (u_short) Scheduler::instance().clock(); // why not?
iph->ip_off = 0x0000; // mf and df bits off, offset zero
iph->ip_ttl = ttl;
iph->ip_p = proto;
memcpy(&iph->ip_src, &src, 4);
memcpy(&iph->ip_dst, &dst, 4);
iph->ip_sum = Internet::in_cksum((u_short*) iph, 20);
return;
}
nat.cc
/*
* Copyright (c) 1998 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 Network Research
* Group at Lawrence Berkeley National Laboratory.
* 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.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$";
#endif
#include
#include
#include
#include
#include
#ifndef __FAVOR_BSD
#define __FAVOR_BSD
#endif;
#include
#include
#include "agent.h"
#include "scheduler.h"
#include "packet.h"
#include "ip.h"
#include "emulate/net.h"
#include "emulate/internet.h"
//
// Nat -- a limited-functionality TCP address rewriting
// facility for emulation mode.
//
class NatAgent : public Agent {
public:
NatAgent() : Agent(PT_LIVE) { }
void recv(Packet*, Handler*);
protected:
virtual void rewrite_addr(ip*) = 0;
u_short addrsum(in_addr*);
u_short addrsum(in_addr*, in_addr*);
void nat(Packet*);
virtual u_short newval() = 0;
virtual u_short oldval(ip*) = 0;
void fixipcksum(ip*, int); // ip only
void fixtcpudpcksum(ip*, int); // tcp,udp/ip
virtual void fixtsum(ip*, int) { }; // any transport
int command(int argc, const char*const* argv);
};
class TCPDestNat : public virtual NatAgent {
protected:
void rewrite_addr(ip*);
void fixtsum(ip* iph, int hlen) {
fixtcpudpcksum(iph, hlen);
}
u_short newval();
u_short oldval(ip*);
int command(int argc, const char*const* argv);
in_addr newdst_;
};
class TCPSrcNat : public virtual NatAgent {
protected:
void rewrite_addr(ip*);
void fixtsum(ip* iph, int hlen) {
fixtcpudpcksum(iph, hlen);
}
u_short newval();
u_short oldval(ip*);
int command(int argc, const char*const* argv);
in_addr newsrc_;
};
class TCPSrcDestNat : public TCPDestNat, public TCPSrcNat {
protected:
void rewrite_addr(ip*);
u_short newval();
u_short oldval(ip*);
void fixtsum(ip* iph, int hlen) {
fixtcpudpcksum(iph, hlen);
}
int command(int argc, const char*const* argv);
};
static class NatTCPSrcAgentClass : public TclClass {
public:
NatTCPSrcAgentClass() : TclClass("Agent/NatAgent/TCPSrc") {}
TclObject* create(int , const char*const*) {
return (new TCPSrcNat());
}
} class_tcpsrcnat;
static class NatTCPDestAgentClass : public TclClass {
public:
NatTCPDestAgentClass() : TclClass("Agent/NatAgent/TCPDest") {}
TclObject* create(int , const char*const*) {
return (new TCPDestNat());
}
} class_tcpdstnat;
static class NatTCPSrcDestAgentClass : public TclClass {
public:
NatTCPSrcDestAgentClass() : TclClass("Agent/NatAgent/TCPSrcDest") {}
TclObject* create(int , const char*const*) {
return (new TCPSrcDestNat());
}
} class_tcpsrcdstnat;
void
NatAgent::recv(Packet *pkt, Handler *)
{
nat(pkt);
// we are merely rewriting an already-existing
// packet (which was destined for us), so be
// sure to rewrite the simulator's notion of the
// address, otherwise we just keep sending to ourselves
// (ouch).
hdr_ip* iph = hdr_ip::access(pkt);
iph->src() = here_;
iph->dst() = dst_;
send(pkt, 0);
}
/*
* NatAgent base class: fix only IP-layer checksums
*/
void
NatAgent::fixipcksum(ip* iph, int iphlen)
{
// fix IP cksum
iph->ip_sum = 0;
iph->ip_sum = Internet::in_cksum((u_short*) iph, iphlen);
return;
}
/*
* rewrite packet addresses, calls object-specific rewrite_addr() function
*/
void
NatAgent::nat(Packet* pkt)
{
hdr_cmn* hc = (hdr_cmn*)pkt->access(off_cmn_);
ip* iph = (ip*) pkt->accessdata();
if (pkt->datalen() < hc->size()) {
fprintf(stderr,
"NatAgent(%s): recvd packet with pkt sz %d but bsize %d\n",
name(), hc->size(), pkt->datalen());
return;
}
int iphlen = (((u_char*)iph)[0] & 0x0f) << 2;
fixtcpudpcksum(iph, iphlen); // requires orig header!
rewrite_addr(iph);
fixipcksum(iph, iphlen);
}
/*
* functions to compute 1's complement sum of 1 and 2 IP addresses
* (note: only the sum, not the complement of the sum)
*/
u_short
NatAgent::addrsum(in_addr* ia)
{
u_short* p = (u_short*) ia;
u_short sum = 0;
sum += *p++;
sum += *p;
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
return (sum);
}
u_short
NatAgent::addrsum(in_addr* ia1, in_addr* ia2)
{
u_short* p = (u_short*) ia1;
u_short sum = 0;
sum += *p++;
sum += *p;
p = (u_short*) ia2;
sum += *p++;
sum += *p;
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
return (sum);
}
/*
* incrementally update tcp or udp packet for new addresses:
* rewrite IP addresses and recompute IP header cksum
* recompute TCP or UDP pseudoheader checksum
*
* note: this code is tricky because of the masking.
* Please do not modify without careful testing.
*/
void
NatAgent::fixtcpudpcksum(ip* iph, int iphlen)
{
tcphdr* tcph = (tcphdr*)(((u_char*) iph) + iphlen);
u_short sum = tcph->th_sum;
//printf("isum: 0x%x\n", sum & 0xffff);
//printf("oval: 0x%x, nval: 0x%x\n",
//~oldval(iph) & 0xffff, newval());
u_long nsum;
nsum = ~sum & 0xffff;
nsum += ~oldval(iph) & 0xffff;
nsum += newval();
//printf("nsum2: 0x%x\n", nsum);
nsum = (nsum >> 16) + (nsum & 0xffff);
nsum += (nsum >> 16);
sum = ~nsum;
tcph->th_sum = sum & 0xffff;
//printf("fsum: 0x%hx\n", tcph->th_sum);
return;
}
void
TCPSrcNat::rewrite_addr(ip* iph)
{
iph->ip_src = newsrc_;
}
u_short
TCPSrcNat::newval()
{
return (addrsum(&newsrc_));
}
u_short
TCPSrcNat::oldval(ip* iph)
{
return (addrsum(&iph->ip_src));
}
u_short
TCPDestNat::newval()
{
return (addrsum(&newdst_));
}
u_short
TCPDestNat::oldval(ip* iph)
{
return (addrsum(&iph->ip_dst));
}
void
TCPDestNat::rewrite_addr(ip* iph)
{
iph->ip_dst = newdst_;
}
void
TCPSrcDestNat::rewrite_addr(ip* iph)
{
TCPSrcNat::rewrite_addr(iph);
TCPDestNat::rewrite_addr(iph);
}
u_short
TCPSrcDestNat::newval()
{
return(addrsum(&newsrc_, &newdst_));
}
u_short
TCPSrcDestNat::oldval(ip* iph)
{
//printf("oldval:%hx\n", addrsum(&iph->ip_src, &iph->ip_dst));
return(addrsum(&iph->ip_src, &iph->ip_dst));
}
int
NatAgent::command(int argc, const char*const* argv)
{
return(Agent::command(argc, argv));
}
int
TCPSrcNat::command(int argc, const char*const* argv)
{
// $srcnat source
if (argc == 3) {
if (strcmp(argv[1], "source") == 0) {
u_long ns;
ns = inet_addr(argv[2]);
newsrc_.s_addr = ns;
return (TCL_OK);
}
}
return (NatAgent::command(argc, argv));
}
int
TCPDestNat::command(int argc, const char*const* argv)
{
// $srcnat destination
if (argc == 3) {
if (strcmp(argv[1], "destination") == 0) {
u_long nd;
nd = inet_addr(argv[2]);
newdst_.s_addr = nd;
return (TCL_OK);
}
}
return (NatAgent::command(argc, argv));
}
int
TCPSrcDestNat::command(int argc, const char*const* argv)
{
// $srcnat source
if (argc == 3) {
if (strcmp(argv[1], "source") == 0) {
u_long ns;
ns = inet_addr(argv[2]);
newsrc_.s_addr = ns;
return (TCL_OK);
}
}
// $srcnat destination
if (argc == 3) {
if (strcmp(argv[1], "destination") == 0) {
u_long nd;
nd = inet_addr(argv[2]);
newdst_.s_addr = nd;
return (TCL_OK);
}
}
return (NatAgent::command(argc, argv));
}
net-ip.cc
/*-
* Copyright (c) 1993-1994, 1998
* The 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 University of
* California, Berkeley and the Network Research Group at
* Lawrence Berkeley Laboratory.
* 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.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (LBL)";
#endif
#include
#ifndef WIN32
#include
#endif
#include
#include
#include
#ifdef WIN32
#include
#define close closesocket
#else
#include
#include
#include
#include
#include
#include
typedef int Socket;
#endif
#if defined(sun) && defined(__svr4__)
#include
#endif
#include "config.h"
#include "net.h"
#include "inet.h"
#include "tclcl.h"
#include "scheduler.h"
//#define NIPDEBUG 1
#ifdef NIPDEBUG
#define NIDEBUG(x) { if (NIPDEBUG) fprintf(stderr, (x)); }
#define NIDEBUG2(x,y) { if (NIPDEBUG) fprintf(stderr, (x), (y)); }
#define NIDEBUG3(x,y,z) { if (NIPDEBUG) fprintf(stderr, (x), (y), (z)); }
#define NIDEBUG4(w,x,y,z) { if (NIPDEBUG) fprintf(stderr, (w), (x), (y), (z)); }
#define NIDEBUG5(v,w,x,y,z) { if (NIPDEBUG) fprintf(stderr, (v), (w), (x), (y), (z)); }
#else
#define NIDEBUG(x) { }
#define NIDEBUG2(x,y) { }
#define NIDEBUG3(x,y,z) { }
#define NIDEBUG4(w,x,y,z) { }
#define NIDEBUG5(v,w,x,y,z) { }
#endif
/*
* Net-ip.cc: this file defines the IP and IP/UDP network
* objects. IP provides a raw IP interface and support functions
* [such as setting multicast parameters]. IP/UDP provides a standard
* UDP datagram interface.
*/
//
// IPNetwork: a low-level (raw) IP network object
//
class IPNetwork : public Network {
public:
IPNetwork();
inline int ttl() const { return (mttl_); } // current mcast ttl
inline int noloopback_broken() { // no loopback filter?
return (noloopback_broken_);
}
int setmttl(Socket, int); // set mcast ttl
int setmloop(Socket, int); // set mcast loopback
int command(int argc, const char*const* argv); // virtual in Network
inline Socket rchannel() { return(rsock_); } // virtual in Network
inline Socket schannel() { return(ssock_); } // virtual in Network
int send(u_char* buf, int len); // virtual in Network
int recv(u_char* buf, int len, sockaddr& from, double& ); // virtual in Network
inline in_addr& laddr() { return (localaddr_); }
inline in_addr& dstaddr() { return (destaddr_); }
int add_membership(Socket, in_addr& grp); // join mcast
int drop_membership(Socket, in_addr& grp); // leave mcast
/* generally useful routines */
static int bindsock(Socket, in_addr&, u_int16_t, sockaddr_in&);
static int connectsock(Socket, in_addr&, u_int16_t, sockaddr_in&);
static int rbufsize(Socket, int);
static int sbufsize(Socket, int);
protected:
in_addr destaddr_; // remote side, if set (network order)
in_addr localaddr_; // local side (network order)
int mttl_; // multicast ttl to use
Socket rsock_; // socket to receive on
Socket ssock_; // socket to send on
int noloopback_broken_; // couldn't turn (off) mcast loopback
int loop_; // do we want loopbacks?
// (system usually assumes yes)
void reset(int reconfigure); // reset + reconfig?
virtual int open(int mode); // open sockets/endpoints
virtual void reconfigure(); // restore state after reset
int close();
time_t last_reset_;
};
class UDPIPNetwork : public IPNetwork {
public:
UDPIPNetwork();
int send(u_char*, int);
int recv(u_char*, int, sockaddr&, double&);
int open(int mode); // mode only
int command(int argc, const char*const* argv);
void reconfigure();
void add_membership(Socket, in_addr&, u_int16_t); // udp version
protected:
int bind(in_addr&, u_int16_t port); // bind to addr/port, mcast ok
int connect(in_addr& remoteaddr, u_int16_t port); // connect()
u_int16_t lport_; // local port (network order)
u_int16_t port_; // remote (dst) port (network order)
};
static class IPNetworkClass : public TclClass {
public:
IPNetworkClass() : TclClass("Network/IP") {}
TclObject* create(int, const char*const*) {
return (new IPNetwork);
}
} nm_ip;
static class UDPIPNetworkClass : public TclClass {
public:
UDPIPNetworkClass() : TclClass("Network/IP/UDP") {}
TclObject* create(int, const char*const*) {
return (new UDPIPNetwork);
}
} nm_ip_udp;
IPNetwork::IPNetwork() :
mttl_(0),
rsock_(-1),
ssock_(-1),
noloopback_broken_(0),
loop_(1)
{
localaddr_.s_addr = 0L;
destaddr_.s_addr = 0L;
NIDEBUG("IPNetwork: ctor\n");
}
UDPIPNetwork::UDPIPNetwork() :
lport_(htons(0)),
port_(htons(0))
{
NIDEBUG("UDPIPNetwork: ctor\n");
}
/*
* UDPIP::send -- send "len" bytes in buffer "buf" out the sending
* channel.
*
* returns the number of bytes written
*/
int
UDPIPNetwork::send(u_char* buf, int len)
{
int cc = ::send(schannel(), (char*)buf, len, 0);
NIDEBUG5("UDPIPNetwork(%s): ::send(%d, buf, %d) returned %d\n",
name(), schannel(), len, cc);
if (cc < 0) {
switch (errno) {
case ECONNREFUSED:
/* no one listening at some site - ignore */
#if defined(__osf__) || defined(_AIX) || defined(__FreeBSD__)
/*
* Here's an old comment...
*
* Due to a bug in kern/uipc_socket.c, on several
* systems, datagram sockets incorrectly persist
* in an error state on receipt of an ICMP
* port-unreachable. This causes unicast connection
* rendezvous problems, and worse, multicast
* transmission problems because several systems
* incorrectly send port unreachables for
* multicast destinations. Our work around
* is to simply close and reopen the socket
* (by calling reset() below).
*
* This bug originated at CSRG in Berkeley
* and was present in the BSD Reno networking
* code release. It has since been fixed
* in 4.4BSD and OSF-3.x. It is known to remain
* in AIX-4.1.3.
*
* A fix is to change the following lines from
* kern/uipc_socket.c:
*
* if (so_serror)
* snderr(so->so_error);
*
* to:
*
* if (so->so_error) {
* error = so->so_error;
* so->so_error = 0;
* splx(s);
* goto release;
* }
*
*/
reset(1);
#endif
break;
case ENETUNREACH:
case EHOSTUNREACH:
/*
* These "errors" are totally meaningless.
* There is some broken host sending
* icmp unreachables for multicast destinations.
* UDP probably aborted the send because of them --
* try exactly once more. E.g., the send we
* just did cleared the errno for the previous
* icmp unreachable, so we should be able to
* send now.
*/
cc = ::send(schannel(), (char*)buf, len, 0);
break;
default:
fprintf(stderr, "UDPIPNetwork(%s): send failed: %s\n",
name(), strerror(errno));
return (-1);
}
}
return cc; // bytes sent
}
int
UDPIPNetwork::recv(u_char* buf, int len, sockaddr& from, double& ts)
{
sockaddr_in sfrom;
int fromlen = sizeof(sfrom);
int cc = ::recvfrom(rsock_, (char*)buf, len, 0,
(sockaddr*)&sfrom, &fromlen);
NIDEBUG5("UDPIPNetwork(%s): ::recvfrom(%d, buf, %d) returned %d\n",
name(), rsock_, len, cc);
if (cc < 0) {
if (errno != EWOULDBLOCK) {
fprintf(stderr,
"UDPIPNetwork(%s): recvfrom failed: %s\n",
name(), strerror(errno));
}
return (-1);
}
from = *((sockaddr*)&sfrom);
/*
* if we received multicast data and we don't want the look,
* there is a chance it is
* what we sent if "noloopback_broken_" is set.
* If so, filter out the stuff we don't want right here.
*/
if (!loop_ && noloopback_broken_ &&
sfrom.sin_addr.s_addr == localaddr_.s_addr &&
sfrom.sin_port == lport_) {
NIDEBUG2("UDPIPNetwork(%s): filtered out our own pkt\n", name());
return (0); // empty
}
ts = Scheduler::instance().clock();
return (cc); // number of bytes received
}
int
UDPIPNetwork::open(int mode)
{
if (mode == O_RDONLY || mode == O_RDWR) {
rsock_ = socket(AF_INET, SOCK_DGRAM, 0);
if (rsock_ < 0) {
fprintf(stderr,
"UDPIPNetwork(%s): open: couldn't open rcv sock\n",
name());
}
nonblock(rsock_);
int on = 1;
if (::setsockopt(rsock_, SOL_SOCKET, SO_REUSEADDR, (char *)&on,
sizeof(on)) < 0) {
fprintf(stderr,
"UDPIPNetwork(%s): open: warning: unable set REUSEADDR: %s\n",
name(), strerror(errno));
}
#ifdef SO_REUSEPORT
on = 1;
if (::setsockopt(rsock_, SOL_SOCKET, SO_REUSEPORT, (char *)&on,
sizeof(on)) < 0) {
fprintf(stderr,
"UDPIPNetwork(%s): open: warning: unable set REUSEPORT: %s\n",
name(), strerror(errno));
}
#endif
/*
* XXX don't need this for the session socket.
*/
if (rbufsize(rsock_, 80*1024) < 0) {
if (rbufsize(rsock_, 32*1024) < 0) {
fprintf(stderr,
"UDPIPNetwork(%s): open: unable to set r bufsize to %d: %s\n",
name(), 32*1024, strerror(errno));
}
}
}
if (mode == O_WRONLY || mode == O_RDWR) {
ssock_ = socket(AF_INET, SOCK_DGRAM, 0);
if (ssock_ < 0) {
fprintf(stderr,
"UDPIPNetwork(%s): open: couldn't open snd sock\n",
name());
}
nonblock(ssock_);
int firsttry = 80 * 1024;
int secondtry = 48 * 1024;
if (sbufsize(ssock_, firsttry) < 0) {
if (sbufsize(ssock_, secondtry) < 0) {
fprintf(stderr,
"UDPIPNetwork(%s): open: cannot set send sockbuf size to %d bytes, using default\n",
name(), secondtry);
}
}
}
mode_ = mode;
NIDEBUG5("UDPIPNetwork(%s): opened network w/mode %d, ssock:%d, rsock:%d\n",
name(), mode_, rsock_, ssock_);
return (0);
}
//
// IP/UDP version of add_membership: try binding
//
void
UDPIPNetwork::add_membership(Socket sock, in_addr& addr, u_int16_t port)
{
int failure = 0;
sockaddr_in sin;
if (bindsock(sock, addr, port, sin) < 0)
failure = 1;
if (failure) {
in_addr addr2 = addr;
addr2.s_addr = INADDR_ANY;
if (bindsock(sock, addr2, port, sin) < 0)
failure = 1;
else
failure = 0;
}
if (IPNetwork::add_membership(sock, addr) < 0)
failure = 1;
if (failure) {
fprintf(stderr,
"UDPIPNetwork(%s): add_membership: failed bind on mcast addr %s and INADDR_ANY\n",
name(), inet_ntoa(addr));
}
}
//
// server-side bind (or mcast subscription)
//
int
UDPIPNetwork::bind(in_addr& addr, u_int16_t port)
{
NIDEBUG4("UDPIPNetwork(%s): attempt to bind to addr %s, port %d [net order]\n",
name(), inet_ntoa(addr), ntohs(port));
if (rsock_ < 0) {
fprintf(stderr,
"UDPIPNetwork(%s): bind/listen called before net is open\n",
name());
return (-1);
}
if (mode_ == O_WRONLY) {
fprintf(stderr,
"UDPIPNetwork(%s): attempted bind/listen but net is write-only\n",
name());
return (-1);
}
#ifdef IP_ADD_MEMBERSHIP
if (IN_CLASSD(ntohl(addr.s_addr))) {
// MULTICAST case, call UDPIP vers of add_membership
add_membership(rsock_, addr, port);
} else
#endif
{
// UNICAST case
sockaddr_in sin;
if (bindsock(rsock_, addr, port, sin) < 0) {
port = ntohs(port);
fprintf(stderr,
"UDPIPNetwork(%s): bind: unable to bind %s [port:%hu]: %s\n",
name(), inet_ntoa(addr),
port, strerror(errno));
return (-1);
}
/*
* MS Windows currently doesn't compy with the Internet Host
* Requirements standard (RFC-1122) and won't let us include
* the source address in the receive socket demux state.
*/
#ifndef WIN32
/*
* (try to) connect the foreign host's address to this socket.
*/
(void)connectsock(rsock_, addr, 0, sin);
#endif
}
localaddr_ = addr;
lport_ = port;
return (0);
}
//
// client-side connect
//
int
UDPIPNetwork::connect(in_addr& addr, u_int16_t port)
{
sockaddr_in sin;
if (ssock_ < 0) {
fprintf(stderr,
"UDPIPNetwork(%s): connect called before net is open\n",
name());
return (-1);
}
if (mode_ == O_RDONLY) {
fprintf(stderr,
"UDPIPNetwork(%s): attempted connect but net is read-only\n",
name());
return (-1);
}
int rval = connectsock(ssock_, addr, port, sin);
if (rval < 0)
return (rval);
destaddr_ = addr;
port_ = port;
last_reset_ = 0;
return(rval);
}
int
UDPIPNetwork::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
if (argc == 2) {
// $udpip port
if (strcmp(argv[1], "port") == 0) {
tcl.resultf("%d", ntohs(port_));
return (TCL_OK);
}
// $udpip lport
if (strcmp(argv[1], "lport") == 0) {
tcl.resultf("%d", ntohs(lport_));
return (TCL_OK);
}
} else if (argc == 4) {
// $udpip listen addr port
// $udpip bind addr port
if (strcmp(argv[1], "listen") == 0 ||
strcmp(argv[1], "bind") == 0) {
in_addr addr;
if (strcmp(argv[2], "any") == 0)
addr.s_addr = INADDR_ANY;
else
addr.s_addr = LookupHostAddr(argv[2]);
u_int16_t port = htons(atoi(argv[3]));
if (bind(addr, port) < 0) {
tcl.resultf("%s %hu",
inet_ntoa(addr), port);
} else {
tcl.result("0");
}
return (TCL_OK);
}
// $udpip connect addr port
if (strcmp(argv[1], "connect") == 0) {
in_addr addr;
addr.s_addr = LookupHostAddr(argv[2]);
u_int16_t port = htons(atoi(argv[3]));
if (connect(addr, port) < 0) {
tcl.resultf("%s %hu",
inet_ntoa(addr), port);
} else {
tcl.result("0");
}
return (TCL_OK);
}
}
return (IPNetwork::command(argc, argv));
}
//
// raw IP network recv()
//
int
IPNetwork::recv(u_char* buf, int len, sockaddr& sa, double& ts)
{
if (mode_ == O_WRONLY) {
fprintf(stderr,
"IPNetwork(%s) recv while in writeonly mode!\n",
name());
abort();
}
int fromlen = sizeof(sa);
int cc = ::recvfrom(rsock_, (char*)buf, len, 0, &sa, &fromlen);
if (cc < 0) {
if (errno != EWOULDBLOCK)
perror("recvfrom");
return (-1);
}
ts = Scheduler::instance().clock();
return (cc);
}
//
// we are given a "raw" IP datagram.
// the raw interface appears to want the len and off fields
// in *host* order, so make it this way here
// note also, that it will compute the cksum "for" us... :(
//
int
IPNetwork::send(u_char* buf, int len)
{
struct ip *ip = (struct ip*) buf;
ip->ip_len = ntohs(ip->ip_len);
ip->ip_off = ntohs(ip->ip_off);
return (::send(ssock_, (char*)buf, len, 0));
}
int IPNetwork::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
if (argc == 2) {
if (strcmp(argv[1], "close") == 0) {
close();
return (TCL_OK);
}
char* cp = tcl.result();
if (strcmp(argv[1], "destaddr") == 0) {
strcpy(cp, inet_ntoa(destaddr_));
return (TCL_OK);
}
if (strcmp(argv[1], "localaddr") == 0) {
strcpy(cp, inet_ntoa(localaddr_));
return (TCL_OK);
}
if (strcmp(argv[1], "mttl") == 0) {
tcl.resultf("%d", mttl_);
return (TCL_OK);
}
/* for backward compatability */
if (strcmp(argv[1], "ismulticast") == 0) {
tcl.result(IN_CLASSD(ntohl(destaddr_.s_addr)) ?
"1" : "0");
return (TCL_OK);
}
if (strcmp(argv[1], "addr") == 0) {
strcpy(cp, inet_ntoa(destaddr_));
return (TCL_OK);
}
if (strcmp(argv[1], "ttl") == 0) {
tcl.resultf("%d", mttl_);
return (TCL_OK);
}
if (strcmp(argv[1], "interface") == 0) {
strcpy(cp, inet_ntoa(localaddr_));
return (TCL_OK);
}
} else if (argc == 3) {
if (strcmp(argv[1], "open") == 0) {
int mode = parsemode(argv[2]);
if (open(mode) < 0)
return (TCL_ERROR);
return (TCL_OK);
}
if (strcmp(argv[1], "add-membership") == 0) {
in_addr addr;
addr.s_addr = LookupHostAddr(argv[2]);
if (add_membership(rchannel(), addr) < 0)
tcl.result("0");
else
tcl.result("1");
return (TCL_OK);
}
if (strcmp(argv[1], "drop-membership") == 0) {
in_addr addr;
addr.s_addr = LookupHostAddr(argv[2]);
if (drop_membership(rchannel(), addr) < 0)
tcl.result("0");
else
tcl.result("1");
return (TCL_OK);
}
if (strcmp(argv[1], "loopback") == 0) {
int val = atoi(argv[2]);
if (strcmp(argv[2], "true") == 0)
val = 1;
else if (strcmp(argv[2], "false") == 0)
val = 0;
if (setmloop(schannel(), val) < 0)
tcl.result("0");
else
tcl.result("1");
return (TCL_OK);
}
}
return (Network::command(argc, argv));
}
int
IPNetwork::setmttl(Socket s, int ttl)
{
/* set the multicast TTL */
#ifdef WIN32
u_int t = ttl;
#else
u_char t = ttl;
#endif
t = (ttl > 255) ? 255 : (ttl < 0) ? 0 : ttl;
if (::setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL,
(char*)&t, sizeof(t)) < 0) {
fprintf(stderr,
"IPNetwork(%s): couldn't set multicast ttl to %d\n",
name(), t);
return (-1);
}
return (0);
}
/*
* open a RAW IP socket (will require privilege).
* turn on HDRINCL, specifying that we will be writing the raw IP header
*/
int
IPNetwork::open(int mode)
{
// obtain a raw socket we can use to send ip datagrams
Socket fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
if (fd < 0) {
perror("socket(RAW)");
if (::getuid() != 0 && ::geteuid() != 0) {
fprintf(stderr,
"IPNetwork(%s): open: use of the Network/IP object requires super-user privs\n",
name());
}
return (-1);
}
// turn on HDRINCL option (we will be writing IP header)
// in FreeBSD 2.2.5 (and possibly others), the IP id field
// is set by the kernel routine rip_output()
// only if it is non-zero, so we should be ok.
int one = 1;
if (::setsockopt(fd, IPPROTO_IP, IP_HDRINCL, &one, sizeof(one)) < 0) {
fprintf(stderr,
"IPNetwork(%s): open: unable to turn on IP_HDRINCL: %s\n",
name(), strerror(errno));
return (-1);
}
// sort of curious, but do a connect() even though we have
// HDRINCL on. Otherwise, we get ENOTCONN when doing a send()
sockaddr_in sin;
in_addr ia = { INADDR_ANY };
if (connectsock(fd, ia, 0, sin) < 0) {
fprintf(stderr,
"IPNetwork(%s): open: unable to connect : %s\n",
name(), strerror(errno));
}
rsock_ = ssock_ = fd;
mode_ = mode;
NIDEBUG5("IPNetwork(%s): opened with mode %d, rsock_:%d, ssock_:%d\n",
name(), mode_, rsock_, ssock_);
return 0;
}
/*
* close both sending and receiving sockets
*/
int
IPNetwork::close()
{
if (ssock_ >= 0) {
(void)::close(ssock_);
ssock_ = -1;
}
if (rsock_ >= 0) {
(void)::close(rsock_);
rsock_ = -1;
}
return (0);
}
/*
* add multicast group membership on the socket
*/
int
IPNetwork::add_membership(Socket fd, in_addr& addr)
{
#if defined(IP_ADD_MEMBERSHIP)
if (IN_CLASSD(ntohl(addr.s_addr))) {
#ifdef notdef
/*
* Try to bind the multicast address as the socket
* dest address. On many systems this won't work
* so fall back to a destination of INADDR_ANY if
* the first bind fails.
*/
sockaddr_in sin;
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr = addr;
if (::bind(fd, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
sin.sin_addr.s_addr = INADDR_ANY;
if (::bind(fd, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
fprintf(stderr,
"IPNetwork(%s): add_membership: unable to bind to addr %s: %s\n",
name(), inet_ntoa(sin.sin_addr),
strerror(errno));
return (-1);
}
}
#endif
/*
* XXX This is bogus multicast setup that really
* shouldn't have to be done (group membership should be
* implicit in the IP class D address, route should contain
* ttl & no loopback flag, etc.). Steve Deering has promised
* to fix this for the 4.4bsd release. We're all waiting
* with bated breath.
*/
struct ip_mreq mr;
mr.imr_multiaddr = addr;
mr.imr_interface.s_addr = INADDR_ANY;
if (::setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
(char *)&mr, sizeof(mr)) < 0) {
fprintf(stderr, "IPNetwork(%s): add_membership: unable to add membership for addr %s: %s\n",
name(), inet_ntoa(addr), strerror(errno));
return (-1);
}
NIDEBUG3("IPNetwork(%s): add_membership for grp %s done\n",
name(), inet_ntoa(addr));
return (0);
}
#else
fprintf(stderr, "IPNetwork(%s): add_membership: host does not support IP multicast\n",
name());
#endif
NIDEBUG3("IPNetwork(%s): add_membership for grp %s failed\n",
name(), inet_ntoa(addr));
return (-1);
}
/*
* drop membership from the specified group on the specified socket
*/
int
IPNetwork::drop_membership(Socket fd, in_addr& addr)
{
#if defined(IP_DROP_MEMBERSHIP)
if (IN_CLASSD(ntohl(addr.s_addr))) {
struct ip_mreq mr;
mr.imr_multiaddr = addr;
mr.imr_interface.s_addr = INADDR_ANY;
if (::setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP,
(char *)&mr, sizeof(mr)) < 0) {
fprintf(stderr, "IPNetwork(%s): drop_membership: unable to drop membership for addr %s: %s\n",
name(), inet_ntoa(addr), strerror(errno));
return (-1);
}
NIDEBUG3("IPNetwork(%s): drop_membership for grp %s done\n",
name(), inet_ntoa(addr));
return (0);
}
#else
fprintf(stderr, "IPNetwork(%s): drop_membership: host does not support IP multicast\n",
name());
#endif
NIDEBUG3("IPNetwork(%s): drop_membership for grp %s failed\n",
name(), inet_ntoa(addr));
return (-1);
}
int
IPNetwork::bindsock(Socket s, in_addr& addr, u_int16_t port, sockaddr_in& sin)
{
memset((char *)&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = port;
sin.sin_addr = addr;
return(::bind(s, (struct sockaddr *)&sin, sizeof(sin)));
}
int
IPNetwork::connectsock(Socket s, in_addr& addr, u_int16_t port, sockaddr_in& sin)
{
memset((char *)&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = port;
sin.sin_addr = addr;
return(::connect(s, (struct sockaddr *)&sin, sizeof(sin)));
}
int
IPNetwork::sbufsize(Socket s, int cnt)
{
return(::setsockopt(s, SOL_SOCKET, SO_SNDBUF, (char *)&cnt, sizeof(cnt)));
}
int
IPNetwork::rbufsize(Socket s, int cnt)
{
return(::setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&cnt, sizeof(cnt)));
}
int
IPNetwork::setmloop(Socket s, int loop)
{
#ifdef IP_MULTICAST_LOOP
u_char c = loop;
if (::setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, &c, sizeof(c)) < 0) {
/*
* If we cannot turn off loopback (Like on the
* Microsoft TCP/IP stack), then declare this
* option broken so that our packets can be
* filtered on the recv path.
*/
if (c != loop) {
noloopback_broken_ = 1;
loop_ = c;
}
return (-1);
}
noloopback_broken_ = 0;
#else
fprintf(stderr, "IPNetwork(%s): msetloop: host does not support IP multicast\n",
name());
#endif
loop_ = c;
return (0);
}
void
IPNetwork::reset(int restart)
{
time_t t = time(0);
int d = int(t - last_reset_);
NIDEBUG2("IPNetwork(%s): reset\n", name());
if (d > 3) { // Steve: why?
last_reset_ = t;
if (ssock_ >= 0)
(void)::close(ssock_);
if (rsock_ >= 0)
(void)::close(rsock_);
if (open(mode_) < 0) {
fprintf(stderr,
"IPNetwork(%s): couldn't reset\n",
name());
mode_ = -1;
return;
}
if (restart)
(void) reconfigure();
}
}
/*
* after a reset, we may want to re-establish our state
* [set up addressing, etc]. Do this here
*/
void
IPNetwork::reconfigure()
{
}
void
UDPIPNetwork::reconfigure()
{
}
net-pcap.cc
/*-
* Copyright (c) 1998 The 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 University of
* California, Berkeley and the Network Research Group at
* Lawrence Berkeley Laboratory.
* 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.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (LBL)";
#endif
#include
#ifndef WIN32
#include
#endif
#include
#include
#include
#ifdef WIN32
#include
#define close closesocket
#else
#include
#include
#include
#endif
#if defined(sun) && defined(__svr4__)
#include
#endif
#ifdef __cplusplus
extern "C" {
#include
}
#else
#include
#endif
#include "config.h"
#include "scheduler.h"
#include "net.h"
#include "tclcl.h"
/*
* observations about pcap library
* device name is in the ifreq struct sense, should be doc'd
* pcap_lookupdev returns a ptr to static data
* q: does lookupdev only return devs in the AF_INET addr family?
* why does pcap_compile require a netmask? seems odd
* would like some way to tell it what buffer to use
* arriving packets have the link layer hdr at the beginning, doc
* not convenient/possible to open bpf read/write
* no real way to know what file (/dev/bpf?) it is using
* would be nice if pcap_lookdev helped out more by
* returning ifnet or ifreq or whatever structure
* pcap_lookupnet makes calls to get our addr, but
* then tosses it anyhow, should get us addr and netmask
* interface type codes could be via rfc1573
* see freebsd net/if_types.h
* want a way to set immed mode
* pcap_next masks errors by returning 0 if pcap_dispatch fails
* a pcap_t carries it's own internal buffer, and
* _dispatch gives pointers into it when invoked [eek]
* when you open pcap using a file, pcap_fileno always
* returns -1; not so convenient
*
*/
#define PNET_PSTATE_INACTIVE 0
#define PNET_PSTATE_ACTIVE 1
//
// PcapNetwork: a "network" (source or possibly sink of packets)
// this is a base class only-- the derived classes are:
// PcapLiveNetwork [a live net; currently bpf + ethernet]
// PcapFileNetwork [packets from a tcpdump-style trace file]
//
class PcapNetwork : public Network {
public:
PcapNetwork() : t_firstpkt_(0.0),
pfd_(-1), pcnt_(0), local_netmask_(0) { }
int rchannel() { return(pfd_); }
int schannel() { return(pfd_); }
virtual int command(int argc, const char*const* argv);
virtual int open(int mode, const char *) = 0;
virtual int skiphdr() = 0;
virtual double gents(pcap_pkthdr*) = 0; // generate timestamp
int recv(u_char *buf, int len, sockaddr&, double&); // get from net
int send(u_char *buf, int len); // write to net
void close();
void reset();
int filter(const char*); // compile + install a filter
int stat_pkts();
int stat_pdrops();
double offset_; // time offset to 1st pkt in a trace
double t_firstpkt_; // ts of 1st pkt recvd
protected:
static void phandler(u_char* u, pcap_pkthdr* h, u_char* p);
virtual void bindvars() = 0;
char errbuf_[PCAP_ERRBUF_SIZE]; // place to put err msgs
char srcname_[PATH_MAX]; // device or file name
int pfd_; // pcap fd
int pcnt_; // # pkts counted
int state_; // PNET_PSTATE_xxx (above)
int optimize_; // bpf optimizer enable
pcap_t* pcap_; // reference to pcap state
struct bpf_program bpfpgm_; // generated program
struct pcap_stat pcs_; // status
unsigned int local_netmask_; // seems shouldn't be necessary :(
};
//
// PcapLiveNetwork: a live network tap
//
struct NetworkAddress {
u_int len_;
u_char addr_[16]; // enough for IPv6 ip addr
};
class PcapLiveNetwork : public PcapNetwork {
public:
PcapLiveNetwork() : local_net_(0), dlink_type_(-1) {
linkaddr_.len_ = 0;
netaddr_.len_ = 0;
bindvars(); reset();
}
NetworkAddress& laddr() { return (linkaddr_); }
NetworkAddress& naddr() { return (netaddr_); }
protected:
double gents(pcap_pkthdr*) {
return Scheduler::instance().clock();
}
int devtonaddr(const char* name, NetworkAddress&);
int open(int mode);
int open(int mode, const char*);
int command(int argc, const char*const* argv);
int skiphdr();
const char* autodevname();
void bindvars();
int snaplen_; // # of bytes to grab
int promisc_; // put intf into promisc mode?
double timeout_;
NetworkAddress linkaddr_; // link-layer address
NetworkAddress netaddr_; // network-layer (IP) address
unsigned int local_net_;
int dlink_type_; // data link type (see pcap)
private:
// XXX somewhat specific to bpf-- this stuff is a hack until pcap
// can be fixed to allow for opening the bpf r/w
#ifdef MT_OWN_PCAP
pcap_t * pcap_open_live(char *, int slen, int prom, int, char *, int);
int bpf_open(pcap_t *p, char *errbuf, int how);
#endif
};
class PcapFileNetwork : public PcapNetwork {
public:
int open(int mode, const char *);
int skiphdr() { return 0; } // XXX check me
protected:
double gents(pcap_pkthdr* p) {
// time stamp of packet is its relative time
// in the trace file, plus sim start time, plus offset
double pts = p->ts.tv_sec + p->ts.tv_usec * 0.000001;
pts -= t_firstpkt_;
pts += offset_ + Scheduler::instance().clock();
return (pts);
}
void bindvars();
int command(int argc, const char*const* argv);
};
static class PcapLiveNetworkClass : public TclClass {
public:
PcapLiveNetworkClass() : TclClass("Network/Pcap/Live") {}
TclObject* create(int, const char*const*) {
return (new PcapLiveNetwork);
}
} net_pcaplive;
static class PcapFileNetworkClass : public TclClass {
public:
PcapFileNetworkClass() : TclClass("Network/Pcap/File") {}
TclObject* create(int, const char*const*) {
return (new PcapFileNetwork);
}
} net_pcapfile;
//
// defs for base PcapNetwork class
//
void
PcapNetwork::bindvars()
{
bind_bool("optimize_", &optimize_);
}
void
PcapNetwork::reset()
{
state_ = PNET_PSTATE_INACTIVE;
pfd_ = -1;
pcap_ = NULL;
*errbuf_ = '\0';
*srcname_ = '\0';
pcnt_ = 0;
}
void
PcapNetwork::close()
{
if (state_ == PNET_PSTATE_ACTIVE && pcap_)
pcap_close(pcap_);
reset();
}
/* compile up a bpf program */
/* XXXwe aren't using 'bcast', so don't care about mask... sigh */
int
PcapNetwork::filter(const char *pgm)
{
if (pcap_compile(pcap_, &bpfpgm_, (char *)pgm,
optimize_, local_netmask_) < 0) {
fprintf(stderr, "pcapnet obj(%s): couldn't compile filter pgm",
name());
return -1;
}
if (pcap_setfilter(pcap_, &bpfpgm_) < 0) {
fprintf(stderr, "pcapnet obj(%s): couldn't set filter pgm",
name());
return -1;
}
return(bpfpgm_.bf_len);
}
/* return number of pkts received */
int
PcapNetwork::stat_pkts()
{
if (pcap_stats(pcap_, &pcs_) < 0)
return (-1);
return (pcs_.ps_recv);
}
/* return number of pkts dropped */
int
PcapNetwork::stat_pdrops()
{
if (pcap_stats(pcap_, &pcs_) < 0)
return (-1);
return (pcs_.ps_drop);
}
#ifndef MIN
#define MIN(x, y) ((x)<(y) ? (x) : (y))
#endif
#include "ether.h"
/* recv is what others call to grab a packet from the pfilter */
struct pcap_singleton {
struct pcap_pkthdr *hdr;
const u_char *pkt;
};
void
PcapNetwork::phandler(u_char* userdata, pcap_pkthdr* ph, u_char* pkt)
{
pcap_singleton *ps = (pcap_singleton*) userdata;
ps->hdr = ph;
ps->pkt = pkt;
}
int
PcapNetwork::recv(u_char *buf, int len, sockaddr& /*fromaddr*/, double &ts)
{
if (state_ != PNET_PSTATE_ACTIVE) {
fprintf(stderr, "warning: net/pcap obj(%s) read-- not active\n",
name());
return -1;
}
int pktcnt = 1; // all in buffer, or until error
int np; // counts # of pkts dispatched
pcap_singleton ps = { 0, 0 };
np = pcap_dispatch(pcap_, pktcnt, phandler, (u_char*) &ps);
if (np < 0) {
fprintf(stderr,
"PcapNetwork(%s): recv: pcap_dispatch: %s\n",
name(), pcap_strerror(errno));
return (np);
} else if (np == 0) {
/* we get here on EOF of a Pcap/File Network */
return (np);
} else if (np != pktcnt) {
fprintf(stderr,
"PcapNetwork(%s): warning: recv: pcap_dispatch: requested pktcnt (%d) doesn't match actual (%d)\n",
name(), pktcnt, np);
}
pcap_pkthdr* ph = ps.hdr;
if (ph == NULL || ps.pkt == NULL) {
fprintf(stderr,
"PcapNetwork(%s): recv: pcap_dispatch: no packet present\n",
name());
return (-1);
}
if (++pcnt_ == 1) {
// mark time stamp of first pkt
t_firstpkt_ = ph->ts.tv_sec + ph->ts.tv_usec * 0.000001;
}
int n = MIN(ph->caplen, (unsigned)len);
ts = gents(ph); // mark with timestamp
// link layer header will be placed at the beginning from pcap
int s = skiphdr(); // go to IP header
memcpy(buf, ps.pkt + s, n - s);
return n - s;
}
/* send a packet out through the packet filter */
int
PcapNetwork::send(u_char *buf, int len)
{
int n;
if ((n = write(pfd_, buf, len)) < 0)
perror("write to pcap fd");
return n;
}
int PcapNetwork::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
if (argc == 2) {
if (strcmp(argv[1], "close") == 0) {
close();
return (TCL_OK);
}
if (strcmp(argv[1], "srcname") == 0) {
tcl.result(srcname_);
return (TCL_OK);
}
if (strcmp(argv[1], "pkts") == 0) {
tcl.resultf("%d", stat_pkts());
return (TCL_OK);
}
if (strcmp(argv[1], "pdrops") == 0) {
tcl.resultf("%d", stat_pdrops());
return (TCL_OK);
}
} else if (argc == 3) {
if (strcmp(argv[1], "filter") == 0) {
if (state_ != PNET_PSTATE_ACTIVE) {
fprintf(stderr, "net/pcap obj(%s): can't install filter prior to opening data source\n",
name());
return (TCL_ERROR);
}
int plen;
if ((plen = filter(argv[2])) < 0) {
fprintf(stderr, "problem compiling/installing filter program\n");
return (TCL_ERROR);
}
tcl.resultf("%d", plen);
return (TCL_OK);
}
}
return (Network::command(argc, argv));
}
//
// defs for PcapLiveNetwork
//
#include
#include
int
PcapLiveNetwork::open(int mode, const char *devname)
{
close();
#ifdef MY_OWN_PCAP
pcap_ = pcap_open_live((char*) devname, snaplen_, promisc_,
int(timeout_ * 1000.), errbuf_, mode);
#else
pcap_ = pcap_open_live((char*) devname, snaplen_, promisc_,
int(timeout_ * 1000.), errbuf_);
#endif // MY_OWN_PCAP
if (pcap_ == NULL) {
fprintf(stderr,
"pcap/live object (%s) couldn't open packet source %s: %s\n",
name(), devname, errbuf_);
return -1;
}
mode_ = mode;
dlink_type_ = pcap_datalink(pcap_);
pfd_ = pcap_fileno(pcap_);
strncpy(srcname_, devname, sizeof(srcname_)-1);
{
// use SIOCGIFADDR hook in bpf to get link addr
struct ifreq ifr;
struct sockaddr *sa = &ifr.ifr_addr;
#ifdef HAVE_SIOCGIFHWADDR
memset(&ifr, 0, sizeof(struct ifreq));
strcpy(ifr.ifr_name, devname);
if (ioctl(pfd_, SIOCGIFHWADDR, &ifr) < 0) {
fprintf(stderr,
"pcap/live (%s) SIOCGIFHWADDR on bpf fd %d\n",
name(), pfd_);
}
#else
if (ioctl(pfd_, SIOCGIFADDR, &ifr) < 0) {
fprintf(stderr,
"pcap/live (%s) SIOCGIFADDR on bpf fd %d\n",
name(), pfd_);
}
#endif
if (dlink_type_ != DLT_EN10MB) {
fprintf(stderr,
"sorry, only ethernet supported\n");
return -1;
}
linkaddr_.len_ = ETHER_ADDR_LEN; // for now
memcpy(linkaddr_.addr_, sa->sa_data, linkaddr_.len_);
}
(void) devtonaddr(devname, netaddr_);
state_ = PNET_PSTATE_ACTIVE;
if (pcap_lookupnet(srcname_, &local_net_, &local_netmask_, errbuf_) < 0) {
fprintf(stderr,
"warning: pcap/live (%s) couldn't get local IP network info: %s\n",
name(), errbuf_) ;
}
{
int immed = 1;
if (ioctl(pfd_, BIOCIMMEDIATE, &immed) < 0) {
fprintf(stderr,
"warning: pcap/live (%s) couldn't set immed\n",
name());
perror("ioctl(BIOCIMMEDIATE)");
}
}
return 0;
}
/*
* how many bytes of link-hdr to skip before net-layer hdr
*/
int
PcapLiveNetwork::skiphdr()
{
switch (dlink_type_) {
case DLT_NULL:
return 0;
case DLT_EN10MB:
return ETHER_HDR_LEN;
default:
fprintf(stderr,
"Network/Pcap/Live(%s): unknown link type: %d\n",
name(), dlink_type_);
}
return -1;
}
const char *
PcapLiveNetwork::autodevname()
{
const char *dname;
if ((dname = pcap_lookupdev(errbuf_)) == NULL) {
fprintf(stderr, "warning: PcapNet/Live(%s) : %s\n",
name(), errbuf_);
return (NULL);
}
return (dname); // ptr to static data in pcap library
}
/*
* devtonaddr -- map device name to its IP/Network layer address
* this routine wouldn't be necessary if pcap_lookupnet gave
* out the info it gets anyhow
*/
#include
int
PcapLiveNetwork::devtonaddr(const char *devname, NetworkAddress& na)
{
register int fd;
ifreq ifr;
fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0) {
fprintf(stderr,
"PcapLiveNet(%s): devtoaddr: couldn't create sock\n",
name());
return (-1);
}
memset(&ifr, 0, sizeof(ifr));
#ifdef linux
/* XXX Work around Linux kernel bug */
ifr.ifr_addr.sa_family = AF_INET;
#endif
(void)strncpy(ifr.ifr_name, devname, sizeof(ifr.ifr_name));
if (ioctl(fd, SIOCGIFADDR, (char *)&ifr) < 0) {
fprintf(stderr, "PcapLiveNetwork(%s): devtoaddr: no addr\n",
name());
(void)::close(fd);
return (-1);
}
sockaddr* sa = &ifr.ifr_addr;
if (sa->sa_family != AF_INET) {
fprintf(stderr,
"PcapLiveNet(%s): af not AF_INET (%d)\n",
name(), sa->sa_family);
}
sockaddr_in* sin = (sockaddr_in*) sa;
na.len_ = 4; // for now, assump IPv4
memset(na.addr_, 0, sizeof(na.addr_));
unsigned sz = sizeof(na.addr_);
if (sizeof(ifr) < sz)
sz = sizeof(ifr);
memcpy(na.addr_, &sin->sin_addr, sz);
return (0);
}
void
PcapLiveNetwork::bindvars()
{
bind("snaplen_", &snaplen_);
bind_bool("promisc_", &promisc_);
bind_time("timeout_", &timeout_);
bind("offset_", &offset_);
PcapNetwork::bindvars();
}
void
PcapFileNetwork::bindvars()
{
bind("offset_", &offset_);
}
int
PcapLiveNetwork::open(int mode)
{
return (open(mode, autodevname()));
}
int PcapLiveNetwork::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
if (argc == 2) {
if (strcmp(argv[1], "linkaddr") == 0) {
/// XXX: only for ethernet now
tcl.result(Ethernet::etheraddr_string(linkaddr_.addr_));
return (TCL_OK);
}
if (strcmp(argv[1], "netaddr") == 0) {
if (netaddr_.len_ != 4) {
fprintf(stderr,
"PcapLive(%s): net addr not len 4 (%d)\n",
name(), netaddr_.len_);
return (TCL_ERROR);
}
tcl.resultf("%d.%d.%d.%d",
netaddr_.addr_[0],
netaddr_.addr_[1],
netaddr_.addr_[2],
netaddr_.addr_[3]);
return (TCL_OK);
}
} else if (argc == 3) {
// $obj open mode
if (strcmp(argv[1], "open") == 0) {
int mode = parsemode(argv[2]);
if (open(mode) < 0)
return (TCL_ERROR);
tcl.result(srcname_);
return (TCL_OK);
}
} else if (argc == 4) {
// $obj open mode devicename
if (strcmp(argv[1], "open") == 0) {
int mode = parsemode(argv[2]);
if (open(mode, argv[3]) < 0)
return (TCL_ERROR);
tcl.result(srcname_);
return (TCL_OK);
}
}
return (PcapNetwork::command(argc, argv));
}
//
// defs for PcapFileNetwork
// use a file instead of a live network
//
int
PcapFileNetwork::open(int /*mode*/, const char *filename)
{
close();
pcap_ = pcap_open_offline((char*) filename, errbuf_);
if (pcap_ == NULL) {
fprintf(stderr,
"pcap/file object (%s) couldn't open packet source %s: %s\n",
name(), filename, errbuf_);
return -1;
}
mode_ = O_RDONLY; // sorry, that's all for now
//
// pcap only ever puts -1 in the pcap_fileno, which
// isn't so convenient, so do this instead:
// pfd_ = pcap_fileno(pcap_);
pfd_ = fileno(pcap_file(pcap_));
strncpy(srcname_, filename, sizeof(srcname_)-1);
state_ = PNET_PSTATE_ACTIVE;
return 0;
}
int PcapFileNetwork::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
if (argc == 4) {
// $obj open mode filename
if (strcmp(argv[1], "open") == 0) {
int mode = parsemode(argv[2]);
if (open(mode, argv[3]) < 0)
return (TCL_ERROR);
tcl.resultf("%s", argv[3]);
return (TCL_OK);
}
}
return (PcapNetwork::command(argc, argv));
}
//
// XXX: the following routines are unfortunately necessary,
// because libpcap has no obvious was of making the bpf fd
// be read-write :(. The implication here is nasty:
// our own version of bpf_open and pcap_open_live
// and the later routine requires the struct pcap internal state
/*
* Savefile
*/
struct pcap_sf {
FILE *rfile;
int swapped;
int version_major;
int version_minor;
u_char *base;
};
struct pcap_md {
struct pcap_stat stat;
/*XXX*/
int use_bpf;
u_long TotPkts; /* can't oflow for 79 hrs on ether */
u_long TotAccepted; /* count accepted by filter */
u_long TotDrops; /* count of dropped packets */
long TotMissed; /* missed by i/f during this run */
long OrigMissed; /* missed by i/f before this run */
#ifdef linux
int pad;
int skip;
char *device;
#endif
};
struct pcap {
int fd;
int snapshot;
int linktype;
int tzoff; /* timezone offset */
int offset; /* offset for proper alignment */
struct pcap_sf sf;
struct pcap_md md;
/*
* Read buffer.
*/
int bufsize;
u_char *buffer;
u_char *bp;
int cc;
/*
* Place holder for pcap_next().
*/
u_char *pkt;
/*
* Placeholder for filter code if bpf not in kernel.
*/
struct bpf_program fcode;
char errbuf[PCAP_ERRBUF_SIZE];
};
/*
* the routines bpf_open and pcap_open_live really
* should not be here, and instead should be part of the
* pcap library. Unfortunately, if we ever want to writes to
* the bpf fd, we need to open it r/w, and the normal pcap
* library does not permit us to do this. So for now, here
* are these routines.
*/
#include
#ifdef MY_OWN_PCAP
int
PcapLiveNetwork::bpf_open(pcap_t *, char *errbuf, int how)
{
int fd;
int n = 0;
char device[sizeof "/dev/bpf000"];
/*
* Go through all the minors and find one that isn't in use.
*/
do {
(void)sprintf(device, "/dev/bpf%d", n++);
fd = ::open(device, how, 0);
} while (fd < 0 && n < 1000 && errno == EBUSY);
/*
* XXX better message for all minors used
*/
if (fd < 0)
sprintf(errbuf, "%s: %s", device, pcap_strerror(errno));
return (fd);
}
pcap_t *
PcapLiveNetwork::pcap_open_live(char *device, int snaplen, int promisc, int to_ms, char *ebuf, int how)
{
int fd;
struct ifreq ifr;
struct bpf_version bv;
u_int v;
pcap_t *p;
p = (pcap_t *)malloc(sizeof(*p));
if (p == NULL) {
sprintf(ebuf, "malloc: %s", pcap_strerror(errno));
return (NULL);
}
bzero(p, sizeof(*p));
fd = bpf_open(p, ebuf, how);
if (fd < 0)
goto bad;
p->fd = fd;
p->snapshot = snaplen;
if (ioctl(fd, BIOCVERSION, (caddr_t)&bv) < 0) {
sprintf(ebuf, "BIOCVERSION: %s", pcap_strerror(errno));
goto bad;
}
if (bv.bv_major != BPF_MAJOR_VERSION ||
bv.bv_minor < BPF_MINOR_VERSION) {
sprintf(ebuf, "kernel bpf filter out of date");
goto bad;
}
(void)strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name));
if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) < 0) {
sprintf(ebuf, "%s: %s", device, pcap_strerror(errno));
goto bad;
}
/* Get the data link layer type. */
if (ioctl(fd, BIOCGDLT, (caddr_t)&v) < 0) {
sprintf(ebuf, "BIOCGDLT: %s", pcap_strerror(errno));
goto bad;
}
#if _BSDI_VERSION - 0 >= 199510
/* The SLIP and PPP link layer header changed in BSD/OS 2.1 */
switch (v) {
case DLT_SLIP:
v = DLT_SLIP_BSDOS;
break;
case DLT_PPP:
v = DLT_PPP_BSDOS;
break;
}
#endif
p->linktype = v;
/* set timeout */
if (to_ms != 0) {
struct timeval to;
to.tv_sec = to_ms / 1000;
to.tv_usec = (to_ms * 1000) % 1000000;
if (ioctl(p->fd, BIOCSRTIMEOUT, (caddr_t)&to) < 0) {
sprintf(ebuf, "BIOCSRTIMEOUT: %s",
pcap_strerror(errno));
goto bad;
}
}
if (promisc)
/* set promiscuous mode, okay if it fails */
(void)ioctl(p->fd, BIOCPROMISC, NULL);
if (ioctl(fd, BIOCGBLEN, (caddr_t)&v) < 0) {
sprintf(ebuf, "BIOCGBLEN: %s", pcap_strerror(errno));
goto bad;
}
p->bufsize = v;
p->buffer = (u_char *)malloc(p->bufsize);
if (p->buffer == NULL) {
sprintf(ebuf, "malloc: %s", pcap_strerror(errno));
goto bad;
}
return (p);
bad:
::close(fd);
free(p);
return (NULL);
}
#endif // MY_OWN_PCAP
net.cc
/*-
* Copyright (c) 1993-1994, 1998 The 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 University of
* California, Berkeley and the Network Research Group at
* Lawrence Berkeley Laboratory.
* 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.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (LBL)";
#endif
#include
#include
#ifndef WIN32
#include
#endif
#include
#include
#include
#include
#include
#ifdef WIN32
#include
#include
#else
#include
#include
#include
#endif
#include "net.h"
/*
* Linux does not have sendmsg
*/
#if defined(__linux__) || defined(WIN32)
#define MAXPACKETSIZE (1500-28)
static int
sendmsg(int s, struct msghdr* mh, int flags)
{
u_char wrkbuf[MAXPACKETSIZE];
int len = mh->msg_iovlen;
struct iovec* iov = mh->msg_iov;
u_char* cp;
u_char* ep;
for (cp = wrkbuf, ep = wrkbuf + MAXPACKETSIZE; --len >= 0; ++iov) {
int plen = iov->iov_len;
if (cp + plen >= ep) {
errno = E2BIG;
return (-1);
}
memcpy(cp, iov->iov_base, plen);
cp += plen;
}
return (send(s, (char*)wrkbuf, cp - wrkbuf, flags));
}
#endif
int Network::command(int argc, const char*const* argv)
{
if (argc == 2) {
Tcl& tcl = Tcl::instance();
if (strcmp(argv[1], "flush") == 0) {
if (mode_ == O_RDWR || mode_ == O_RDONLY) {
unsigned char buf[1024];
sockaddr from;
double ts;
while (recv(buf, sizeof(buf), from, ts) > 0)
;
}
return (TCL_OK);
}
if (strcmp(argv[1], "mode") == 0) {
tcl.result(modename(mode_));
return (TCL_OK);
}
}
return (TclObject::command(argc, argv));
}
int
Network::nonblock(int fd)
{
#ifdef WIN32
u_long flag = 1;
if (ioctlsocket(fd, FIONBIO, &flag) == -1) {
fprintf(stderr,
"Network::nonblock(): ioctlsocket: FIONBIO: %lu\n",
GetLastError());
return -1;
}
#else
int flags;
if ((flags = fcntl(fd, F_GETFL, 0)) < 0) {
perror("Network::nonblock(): fcntl");
return (-1);
}
#if defined(hpux) || defined(__hpux)
flags |= O_NONBLOCK;
#else
flags |= O_NONBLOCK|O_NDELAY;
#endif
if (fcntl(fd, F_SETFL, flags) == -1) {
perror("Network::nonblock(): fcntl: F_SETFL");
return -1;
}
#endif
return 0;
}
int
Network::parsemode(const char *mname)
{
if (strcmp(mname, "readonly") == 0) {
return (O_RDONLY);
} else if (strcmp(mname, "readwrite") == 0) {
return (O_RDWR);
} else if (strcmp(mname, "writeonly") == 0) {
return (O_WRONLY);
}
return (::atoi(mname));
}
char *
Network::modename(int mode)
{
switch (mode) {
case O_RDONLY:
return ("readonly");
case O_WRONLY:
return ("writeonly");
case O_RDWR:
return ("readwrite");
}
return ("unknown");
}
ping_responder.cc
/*
* Copyright (c) 1998 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 Network Research
* Group at Lawrence Berkeley National Laboratory.
* 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.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$";
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include "agent.h"
#include "scheduler.h"
#include "emulate/internet.h"
//
// ping_responder.cc -- this agent may be inserted into nse as
// a real-world responder to ICMP ECHOREQUEST operations. It's
// used to test emulation mode, mostly (and in particular the rt scheduler)
//
class PingResponder : public Agent {
public:
PingResponder() : Agent(PT_LIVE) { }
void recv(Packet*, Handler*);
protected:
icmp* validate(int, ip*);
void reflect(ip*);
};
static class PingResponderClass : public TclClass {
public:
PingResponderClass() : TclClass("Agent/PingResponder") {}
TclObject* create(int , const char*const*) {
return (new PingResponder());
}
} class_pingresponder;
/*
* receive an ICMP echo request packet from the simulator.
* Actual IP packet is in the "data" portion of the packet, and
* is assumed to start with the IP header
*/
void
PingResponder::recv(Packet* pkt, Handler*)
{
hdr_cmn *ch = (hdr_cmn*)pkt->access(off_cmn_);
int psize = ch->size();
u_char* payload = pkt->accessdata();
if (payload == NULL) {
fprintf(stderr, "%f: PingResponder(%s): recv NULL data area\n",
Scheduler::instance().clock(), name());
Packet::free(pkt);
return;
}
/*
* assume here that the data area contains an IP header!
*/
icmp* icmph;
if ((icmph = validate(psize, (ip*) payload)) == NULL) {
Internet::print_ip((ip*) payload);
Packet::free(pkt);
return;
}
/*
* tasks: change icmp type to echo-reply, recompute IP hdr cksum,
* recompute ICMP cksum
*/
icmph->icmp_type = ICMP_ECHOREPLY;
reflect((ip*) payload); // like kernel routine icmp_reflect()
/*
* set up simulator packet to go to the correct place
*/
Agent::initpkt(pkt);
ch->size() = psize; // will have been overwrittin by initpkt
target_->recv(pkt);
return;
}
/*
* check a bunch of stuff:
* ip vers ok, ip hlen is 5, proto is icmp, len big enough,
* not fragmented, cksum ok, saddr not mcast/bcast
*/
icmp*
PingResponder::validate(int sz, ip* iph)
{
if (sz < 20) {
fprintf(stderr, "%f: PingResponder(%s): sim pkt too small for base IP header(%d)\n",
Scheduler::instance().clock(), name(), sz);
return (NULL);
}
int ipver = (*((char*)iph) & 0xf0) >> 4;
if (ipver != 4) {
fprintf(stderr, "%f: PingResponder(%s): IP bad ver (%d)\n",
Scheduler::instance().clock(), name(), ipver);
return (NULL);
}
int iplen = ntohs(iph->ip_len);
int iphlen = (*((char*)iph) & 0x0f) << 2;
if (iplen < (iphlen + 8)) {
fprintf(stderr, "%f: PingResponder(%s): IP dgram not long enough (len: %d)\n",
Scheduler::instance().clock(), name(), iplen);
return (NULL);
}
if (sz < iplen) {
fprintf(stderr, "%f: PingResponder(%s): IP dgram not long enough (len: %d)\n",
Scheduler::instance().clock(), name(), iplen);
return (NULL);
}
if (iphlen != 20) {
fprintf(stderr, "%f: PingResponder(%s): IP bad hlen (%d)\n",
Scheduler::instance().clock(), name(), iphlen);
return (NULL);
}
if (Internet::in_cksum((u_short*) iph, iphlen)) {
fprintf(stderr, "%f: PingResponder(%s): IP bad cksum\n",
Scheduler::instance().clock(), name());
return (NULL);
}
if (iph->ip_p != IPPROTO_ICMP) {
fprintf(stderr, "%f: PingResponder(%s): not ICMP (proto: %d)\n",
Scheduler::instance().clock(), name(), iph->ip_p);
return (NULL);
}
if (iph->ip_off != 0) {
fprintf(stderr, "%f: PingResponder(%s): fragment! (off: 0x%x)\n",
Scheduler::instance().clock(), name(), ntohs(iph->ip_off));
return (NULL);
}
if (iph->ip_src.s_addr == 0xffffffff || iph->ip_src.s_addr == 0) {
fprintf(stderr, "%f: PingResponder(%s): bad src addr (%s)\n",
Scheduler::instance().clock(), name(),
inet_ntoa(iph->ip_src));
return (NULL);
}
if (IN_MULTICAST(ntohl(iph->ip_src.s_addr))) {
fprintf(stderr, "%f: PingResponder(%s): mcast src addr (%s)\n",
Scheduler::instance().clock(), name(),
inet_ntoa(iph->ip_src));
return (NULL);
}
icmp* icp = (icmp*) (iph + 1);
if (Internet::in_cksum((u_short*) icp, iplen - iphlen) != 0) {
fprintf(stderr,
"%f: PingResponder(%s): bad ICMP cksum\n",
Scheduler::instance().clock(), name());
return (NULL);
}
if (icp->icmp_type != ICMP_ECHO) {
fprintf(stderr, "%f: PingResponder(%s): not echo request (%d)\n",
Scheduler::instance().clock(), name(),
icp->icmp_type);
return (NULL);
}
if (icp->icmp_code != 0) {
fprintf(stderr, "%f: PingResponder(%s): bad code (%d)\n",
Scheduler::instance().clock(), name(),
icp->icmp_code);
return (NULL);
}
return (icp);
}
/*
* reflect: fix up the IP and ICMP info to reflect the packet
* back from where it came in real life
*
* this routine will just assume no IP options on the pkt
*/
void
PingResponder::reflect(ip* iph)
{
in_addr daddr = iph->ip_dst;
int iplen = ntohs(iph->ip_len);
int iphlen = (*((char*)iph) & 0x0f) << 2;
/* swap src and dest IP addresses on IP header */
iph->ip_dst = iph->ip_src;
iph->ip_src = daddr;
iph->ip_sum = 0;
iph->ip_sum = Internet::in_cksum((u_short*) iph, iphlen);
/* recompute the icmp cksum */
icmp* icp = (icmp*)(iph + 1); // just past standard IP header
icp->icmp_cksum = 0;
icp->icmp_cksum = Internet::in_cksum((u_short*)icp, iplen - iphlen);
}
tap.cc
/*
* Copyright (c) 1997, 1998 The 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 MASH Research
* Group at the University of California, Berkeley.
* 4. Neither the name of the University nor of the Research Group 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.
*/
#ifndef lint
static const char rcsid[] =
"@(#) $Header$ (UCB)";
#endif
#include "tclcl.h"
#include "net.h"
#include "packet.h"
#include "agent.h"
//#define TAPDEBUG 1
#ifdef TAPDEBUG
#define TDEBUG(x) { if (TAPDEBUG) fprintf(stderr, (x)); }
#define TDEBUG2(x,y) { if (TAPDEBUG) fprintf(stderr, (x), (y)); }
#define TDEBUG3(x,y,z) { if (TAPDEBUG) fprintf(stderr, (x), (y), (z)); }
#define TDEBUG4(w,x,y,z) { if (TAPDEBUG) fprintf(stderr, (w), (x), (y), (z)); }
#define TDEBUG5(v,w,x,y,z) { if (TAPDEBUG) fprintf(stderr, (v), (w), (x), (y), (z)); }
#else
#define TDEBUG(x) { }
#define TDEBUG2(x,y) { }
#define TDEBUG3(x,y,z) { }
#define TDEBUG4(w,x,y,z) { }
#define TDEBUG5(v,w,x,y,z) { }
#endif
#include
class TapAgent : public Agent, public IOHandler {
public:
TapAgent();
int command(int, const char*const*);
void recv(Packet* p, Handler*); // sim->live net
protected:
int maxpkt_; // max size allocated to recv a pkt
void dispatch(int); // invoked via scheduler on I/O event
int linknet(); // establish I/O handler
Network* net_; // live network object
int sendpkt(Packet*);
void recvpkt();
double now() { return Scheduler::instance().clock(); }
};
static class TapAgentClass : public TclClass {
public:
TapAgentClass() : TclClass("Agent/Tap") {}
TclObject* create(int, const char*const*) {
return (new TapAgent());
}
} class_tap_agent;
TapAgent::TapAgent() : Agent(PT_LIVE), net_(NULL)
{
bind("maxpkt_", &maxpkt_);
}
//
// link in a network to the agent. Assumes net_ is non-zero
//
int
TapAgent::linknet()
{
int mode = net_->mode();
int rchan = net_->rchannel();
int wchan = net_->schannel();
unlink();
if (mode == O_RDONLY || mode == O_RDWR) {
// reading enabled?
if (rchan < 0) {
fprintf(stderr,
"TapAgent(%s): network %s not open for reading (mode:%d)\n",
name(), net_->name(), mode);
return (TCL_ERROR);
}
link(rchan, TCL_READABLE);
TDEBUG3("TapAgent(%s): linked sock %d as READABLE\n",
name(), rchan);
} else if (mode != O_WRONLY) {
if (mode == -1) {
fprintf(stderr,
"TapAgent(%s): Network(%s) not opened properly.\n",
name(), net_->name());
fprintf(stderr,
"(choose: readonly, readwrite, or writeonly)\n");
} else {
fprintf(stderr,
"TapAgent(%s): unknown mode %d in Network(%s)\n",
name(), mode, net_->name());
}
return (TCL_ERROR);
}
if (mode == O_WRONLY || mode == O_RDWR) {
// writing enabled?
if (wchan < 0) {
fprintf(stderr,
"TapAgent(%s): network %s not open for writing\n",
name(), net_->name());
return (TCL_ERROR);
}
}
return (TCL_OK);
}
int
TapAgent::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
if (argc == 2) {
if (strcmp(argv[1], "network") == 0) {
tcl.result(name());
return(TCL_OK);
}
}
if (argc == 3) {
if (strcmp(argv[1], "network") == 0) {
net_ = (Network *)TclObject::lookup(argv[2]);
if (net_ != 0) {
return(linknet());
} else {
fprintf(stderr,
"TapAgent(%s): unknown network %s\n",
name(), argv[2]);
return (TCL_ERROR);
}
return(TCL_OK);
}
}
return (Agent::command(argc, argv));
}
/*
* Receive a packet off the network and inject into the simulation.
*/
void
TapAgent::recvpkt()
{
if (net_->mode() != O_RDWR && net_->mode() != O_RDONLY) {
fprintf(stderr,
"TapAgent(%s): recvpkt called while in write-only mode!\n",
name());
return;
}
if (maxpkt_ <= 0) {
fprintf(stderr,
"TapAgent(%s): recvpkt: maxpkt_ value too low (%d)\n",
name(), maxpkt_);
return;
}
// allocate packet and a data payload
Packet* p = allocpkt(maxpkt_);
// fill up payload
sockaddr addr; // not really used (yet)
double tstamp;
int cc = net_->recv(p->accessdata(), maxpkt_, addr, tstamp);
if (cc <= 0) {
if (cc < 0) {
perror("recv");
}
Packet::free(p);
return;
}
TDEBUG4("%f: Tap(%s): recvpkt, cc:%d\n", now(), name(), cc);
// inject into simulator
hdr_cmn* ch = (hdr_cmn*)p->access(off_cmn_);
ch->size() = cc;
/*
* if the time-stamp on the pkt is sufficiently far in the future,
* put it in the scheduler instead of forwarding it immediately.
* This can happen if we are pulling packet from a trace file
* and we don't want them to be dispatched until later
*
* this agent assumes that the time stamps are in absolute
* time, so adjust it to relative time here
*/
double when = tstamp - now();
if (when > 0.0) {
TDEBUG5("%f: Tap(%s): DEFERRED PACKET %f secs, uid: %d\n",
now(), name(), when, p->uid_);
ch->timestamp() = when;
Scheduler::instance().schedule(target_, p, when);
} else {
TDEBUG4("%f: Tap(%s): recvpkt, writing to target: %s\n",
now(), name(), target_->name());
ch->timestamp() = now();
target_->recv(p);
}
return;
}
void
TapAgent::dispatch(int)
{
/*
* Just process one packet. We could put a loop here
* but instead we allow the dispatcher to call us back
* if there is a queue in the socket buffer; this allows
* other events to get a chance to slip in...
*/
#ifdef notdef
Scheduler::instance().sync(); // sim clock gets set to now
#endif
recvpkt();
}
/*
* SIM -> Live
*
* Receive a packet from the simulation and inject into the network.
* if there is no network attached, call Connector::drop() to send
* to drop target
*/
void
TapAgent::recv(Packet* p, Handler*)
{
(void) sendpkt(p);
Packet::free(p);
return;
}
int
TapAgent::sendpkt(Packet* p)
{
if (net_->mode() != O_RDWR && net_->mode() != O_WRONLY) {
fprintf(stderr,
"TapAgent(%s): sendpkt called while in read-only mode!\n",
name());
return (-1);
}
// send packet into the live network
hdr_cmn* hc = (hdr_cmn*)p->access(off_cmn_);
if (net_ == NULL) {
fprintf(stderr,
"TapAgent(%s): sendpkt attempted with NULL net\n",
name());
drop(p);
return (-1);
}
if (net_->send(p->accessdata(), hc->size()) < 0) {
fprintf(stderr,
"TapAgent(%s): sendpkt (%p, %d): %s\n",
name(), p->accessdata(), hc->size(), strerror(errno));
return (-1);
}
TDEBUG3("TapAgent(%s): sent packet (sz: %d)\n",
name(), hc->size());
return 0;
}
dest_queue.cc
/* -*- 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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*/
/* Ported from CMU/Monarch's code*/
/*
dest_queue.cc
$Id: AllCode__.html 980 2002-12-12 09:36:58Z ffilali $
implement a group of resequencing queues. one for each source of packets.
the name destination queue is a misnomer.
*/
#include
#ifdef TEST_ONLY
#include
#else
#include
#endif
#ifndef TEST_ONLY
#define CURRENT_TIME Scheduler::instance().clock()
#else
extern double CURRENT_TIME;
#endif
static const int verbose = 0;
//////////////////////////////////////////////////////////////////////
// Transmission Queue Entry
txent::txent(double e, u_int32_t s, Packet *p)
{
expire_ = e;
seqno_ = s;
pkt_ = p;
}
//////////////////////////////////////////////////////////////////////
// Destination Queue Entry
dstent::dstent(nsaddr_t index)
{
LIST_INIT(&txentHead);
ipaddr_ = index;
seqno_ = ILLEGAL_SEQ;
}
static int
SEQ_GT(u_int8_t a, u_int8_t b)
{
int8_t reg = (int8_t)a - (int8_t) b;
return reg > 0;
}
void
dstent::addEntry(double e, u_int32_t s, Packet *p)
{
txent *t, *u, *v = 0;
if((t = findEntry(s)) == 0) {
t = new txent(e, s, p);
assert(t);
for(u = txentHead.lh_first; u; u = u->link.le_next) {
if(SEQ_GT(u->seqno(), s))
break;
v = u;
}
if(u == 0 && v == 0) {
LIST_INSERT_HEAD(&txentHead, t, link);
} else if(u) {
LIST_INSERT_BEFORE(u, t, link);
} else {
assert(v);
LIST_INSERT_AFTER(v, t, link);
}
} else {
Packet::free(p);
// already have a copy of this packet
}
#ifdef DEBUG
// verify that I did not fuck up...
u_int32_t max = 0;
for(t = txentHead.lh_first; t; t = t->link.le_next) {
if(max == 0)
max = t->seqno();
else {
assert(t->seqno() > max);
max = t->seqno();
}
}
#endif
}
void
dstent::delEntry(txent *t)
{
LIST_REMOVE(t, link);
delete t;
}
txent*
dstent::findEntry(u_int32_t s)
{
txent *t;
for(t = txentHead.lh_first; t; t = t->link.le_next) {
if(t->seqno() == s)
return t;
}
return 0;
}
txent*
dstent::findFirstEntry(void)
{
return txentHead.lh_first;
// this gives the minimum sequence number for the destination
}
//////////////////////////////////////////////////////////////////////
// Destination Queue
dstQueue::dstQueue(imepAgent *a, nsaddr_t index) : agent_(a), ipaddr_(index)
{
LIST_INIT(&dstentHead);
}
void
dstQueue::addEntry(nsaddr_t dst, double e, u_int32_t s, Packet *p)
{
dstent *t;
if((t = findEntry(dst)) == 0) {
t = new dstent(dst);
assert(t);
LIST_INSERT_HEAD(&dstentHead, t, link);
}
if (NULL == t->txentHead.lh_first) agent_->stats.num_holes_created++;
t->addEntry(e, s, p);
}
dstent*
dstQueue::findEntry(nsaddr_t dst)
{
dstent *t;
for(t = dstentHead.lh_first; t; t = t->link.le_next) {
if(t->ipaddr() == dst)
return t;
}
return 0;
}
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
Packet*
dstQueue::getPacket(nsaddr_t dst, u_int32_t seqno)
{
dstent *d;
txent *t;
for(d = dstentHead.lh_first; d; d = d->link.le_next) {
if(d->ipaddr() == dst)
break;
}
if(d && (t = d->findEntry(seqno))) {
Packet *p = t->pkt();
d->delEntry(t);
// make sure packets come out in increasing order only
// int8_t reg = (int8_t) seqno - (int8_t) d->seqno();
//assert(reg > 0); // SEQ_GT(seqno, d->seqno())
//d->seqno() = seqno;
return p;
}
return 0;
}
double
dstQueue::getNextExpire()
{
dstent *t;
Time min = 0.0;
for(t = dstentHead.lh_first; t; t = t->link.le_next) {
Time texp = t->expire(); // computed by traversing a list
if(min == 0.0 || (texp && texp < min))
min = texp;
}
if (verbose)
agent_->trace("T %.9f _%d_ dest_queue - getNextExpire is %.9f",
CURRENT_TIME, ipaddr_, min);
return min;
}
Packet*
dstQueue::getNextPacket(u_int32_t& s)
{
dstent *d;
for(d = dstentHead.lh_first; d; d = d->link.le_next) {
txent *t = d->findFirstEntry();
if (t == 0)
{
d->seqno() = ILLEGAL_SEQ;
continue; // no packets here
}
Time texp = d->expire();
assert(texp);
#ifdef TEST_ONLY
fprintf(stderr,
"IN:\td->expire: %f, d->seqno: %d, t->expire: %f, t->seqno: %d\n",
d->expire(), d->seqno(), t->expire(), t->seqno());
#endif
if (texp > CURRENT_TIME && d->seqno() == ILLEGAL_SEQ) continue;
if (t->expire() <= CURRENT_TIME)
{ // remember this seq as starting a chain we can pull off
d->seqno() = t->seqno();
}
else if (d->seqno() != ILLEGAL_SEQ && t->seqno() != (u_int8_t) (d->seqno() + 1))
{ // the next pkt isn't part of the chain we were pulling off
// stop pulling off packets
d->seqno() = ILLEGAL_SEQ;
continue;
}
Packet *p = t->pkt();
assert(p);
s = t->seqno();
#ifdef TEST_ONLY
fprintf(stderr, "\t%s: returning seqno: %d\n",
__FUNCTION__, s);
#endif
if (d->seqno() != ILLEGAL_SEQ)
{ // advance d->seqno() along the chain
d->seqno() = t->seqno();
}
d->delEntry(t);
#ifdef TEST_ONLY
fprintf(stderr,
"OUT:\td->expire: %f, d->seqno: %d, t->expire: %f, t->seqno: %d\n",
d->expire(), d->seqno(), t->expire(), t->seqno());
#endif
return p;
}
return 0;
}
void
dstQueue::deleteDst(nsaddr_t dst)
{
dstent *d;
txent *t;
if (verbose)
agent_->trace("T %.9f _%d_ purge dstQ id %d",
CURRENT_TIME, ipaddr_, dst);
for(d = dstentHead.lh_first; d; d = d->link.le_next)
{
if(d->ipaddr() == dst)
break;
}
if (!d) return;
while ((t = d->findFirstEntry()))
{
Packet *p = t->pkt();
if (verbose)
agent_->trace("T %.9f _%d_ dstQ id %d delete seq %d",
CURRENT_TIME, ipaddr_, dst, t->seqno());
Packet::free(p);
d->delEntry(t);
agent_->stats.num_reseqq_drops++;
}
LIST_REMOVE(d,link);
delete d;
}
void
dstQueue::dumpAll()
{
dstent *t;
for(t = dstentHead.lh_first; t; t = t->link.le_next) {
if (verbose)
agent_->trace("T %.9f _%d_ dest_queue - src %d expire %.9f seqno %d",
CURRENT_TIME, ipaddr_, t->ipaddr(), t->expire(), t->seqno());
txent *u = t->findFirstEntry();
for(;u; u = u->link.le_next) {
if(verbose)
agent_->trace("T %.9f _%d_ dest_queue - src %d seq %d expire %.9f",
CURRENT_TIME, ipaddr_, t->ipaddr(),
u->seqno(), u->expire());
}
}
}
imep.cc
/* -*- 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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*/
/* Ported from CMU/Monarch's code */
/*
imep.cc
$Id: AllCode__.html 980 2002-12-12 09:36:58Z ffilali $
*/
#include
#include
#include
#include
#include
#define CURRENT_TIME Scheduler::instance().clock()
static const int verbose = 0;
static const int imep_use_mac_callback = 1;
// ======================================================================
// TCL Hooks
// ======================================================================
static class IMEPHeaderClass : public PacketHeaderClass {
public:
IMEPHeaderClass() : PacketHeaderClass("PacketHeader/IMEP",
IMEP_HDR_LEN) { }
} class_imep_hdr;
static class agentIMEPclass : public TclClass {
public:
agentIMEPclass() : TclClass("Agent/IMEP") {}
TclObject* create(int argc, const char*const* argv) {
assert(argc == 5);
return (new imepAgent((nsaddr_t) atoi(argv[4])));
}
} class_imepAgent;
// ======================================================================
// ======================================================================
// MAC layer callback
static void
imep_failed_callback(Packet *p, void *arg)
{
if(imep_use_mac_callback)
((imepAgent*) arg)->imepPacketUndeliverable(p);
else {
Packet::free(p);
// XXX: Should probably call a "drop" agent here
}
}
imepAgent::imepAgent(nsaddr_t index) :
Agent(PT_TORA),
beaconTimer(this, BEACON_TIMER),
controlTimer(this, CONTROL_TIMER),
rexmitTimer(this, REXMIT_TIMER),
incomingTimer(this, INCOMING_TIMER),
ipaddr(index),
incomingQ(this, index)
{
bind("off_IMEP_", &off_IMEP_);
bind("off_TORA_", &off_TORA_);
controlSequence = 0;
recvtarget_ = sendtarget_ = 0;
logtarget_ = 0;
rtagent_ = 0;
LIST_INIT(&imepLinkHead);
bzero(&stats, sizeof(stats));
}
int
imepAgent::command(int argc, const char*const* argv)
{
if(argc == 2) {
if(strcmp(argv[1], "start") == 0) {
beaconTimer.start(BEACON_PERIOD);
return TCL_OK;
}
else if(strcmp(argv[1], "reset") == 0) {
Terminate();
return TCL_OK;
}
} else if (argc == 3) {
if (strcmp(argv[1], "recvtarget") == 0) {
recvtarget_ = (NsObject*) TclObject::lookup(argv[2]);
assert(recvtarget_);
return (TCL_OK);
}
else if (strcmp(argv[1], "sendtarget") == 0) {
sendtarget_ = (NsObject*) TclObject::lookup(argv[2]);
assert(sendtarget_);
return (TCL_OK);
}
else if (strcmp(argv[1], "rtagent") == 0) {
rtagent_ = (rtAgent*) TclObject::lookup(argv[2]);
assert(rtagent_);
return (TCL_OK);
}
else if(strcmp(argv[1], "log-target") == 0) {
logtarget_ = (Trace*) TclObject::lookup(argv[2]);
assert(logtarget_);
return (TCL_OK);
}
}
return Agent::command(argc, argv);
}
imepLink*
imepAgent::findLink(nsaddr_t index)
{
imepLink *l;
for(l = imepLinkHead.lh_first; l; l = l->link.le_next) {
if(l->index() == index)
return l;
}
return 0;
}
Packet*
imepAgent::findObjectSequence(u_int8_t seqno)
{
Packet *p;
ReXmitQIter iter = rexmitq.iter();
struct imep_object_block *ob;
while ((p = iter.next())) {
ob = findObjectBlock(p);
if(ob == 0) continue;
// no OBJECT block
if(ob->ob_sequence != seqno) continue;
// wrong SEQUENCE number
if(ob->ob_num_responses <=0) {
fprintf(stderr,
"imepAgent::findObjectSequence: "
"Object Block without response list\n");
abort();
}
return p;
}
/* abort();
this isn't an abort condition. consider an ack arriving
for a pkt after it's timed out from the rexmit q -dam */
return NULL;
}
void
imepAgent::removeObjectResponse(Packet *p, nsaddr_t index)
{
struct imep_object_block *ob = findObjectBlock(p);
struct imep_response *r = findResponseList(p);
struct imep_response *r0;
struct hdr_cmn *ch = HDR_CMN(p);
int i;
assert(ob && r);
for(i = 0, r0 = r; i < ob->ob_num_responses; i++, r0++) {
if(INT32_T(r0->resp_ipaddr) == index)
break;
}
if(INT32_T(r0->resp_ipaddr) != index) {
if (verbose)
trace("T %.9f _%d_ dup ack(?) from %d", CURRENT_TIME, ipaddr, index);
return;
}
if(ob->ob_num_responses == 1) {
if (verbose)
trace("T %.9f _%d_ remove from reXq pkt %d",
CURRENT_TIME, ipaddr, ch->uid());
rexmitq.remove(p);
Packet::free(p);
stats.num_rexmitable_fully_acked++;
} else {
// find the last "response"
r += (ob->ob_num_responses - 1);
if(r != r0) {
INT32_T(r0->resp_ipaddr) = INT32_T(r->resp_ipaddr);
}
ob->ob_num_responses -= 1;
if (verbose)
trace("T %.9f _%d_ remove %d from resp list %d (%d left) RL %s",
CURRENT_TIME, ipaddr, index, ch->uid(), ob->ob_num_responses,
dumpResponseList(p));
struct hdr_imep *im = HDR_IMEP(p);
ch->size() -= sizeof(struct imep_response);
U_INT16_T(im->imep_length) -= sizeof(struct imep_response);
}
}
void
imepAgent::purgeReXmitQ(nsaddr_t index)
// remove index from any response lists in the rexmit q
{
Packet *p;
ReXmitQIter iter = rexmitq.iter();
struct imep_object_block *ob;
struct imep_response *r,*r0;
struct hdr_cmn *ch;
int i;
if (verbose)
trace("T %.9f _%d_ purge %d from reXmit Q",
CURRENT_TIME, ipaddr, index);
while ((p = iter.next())) {
ob = findObjectBlock(p);
if(ob == 0) assert(0); // should always be an object block
r = findResponseList(p);
ch = HDR_CMN(p);
assert(ob && r);
for(i = 0, r0 = r; i < ob->ob_num_responses; i++, r0++) {
if(INT32_T(r0->resp_ipaddr) == index)
break;
}
if(INT32_T(r0->resp_ipaddr) != index) {
continue; // index not in this response list
}
if(ob->ob_num_responses == 1) {
if (verbose)
trace("T %.9f _%d_ remove from reXq pkt %d",
CURRENT_TIME, ipaddr, ch->uid());
rexmitq.remove(p);
drop(p, DROP_RTR_QTIMEOUT);
stats.num_rexmitable_fully_acked++;
} else {
// find the last "response"
r += (ob->ob_num_responses - 1);
if(r != r0) {
INT32_T(r0->resp_ipaddr) = INT32_T(r->resp_ipaddr);
}
ob->ob_num_responses -= 1;
if (verbose)
trace("T %.9f _%d_ purge %d from resp list %d (%d left) RL %s",
CURRENT_TIME, ipaddr, index, ch->uid(), ob->ob_num_responses,
dumpResponseList(p));
struct hdr_imep *im = HDR_IMEP(p);
ch->size() -= sizeof(struct imep_response);
U_INT16_T(im->imep_length) -= sizeof(struct imep_response);
}
}
}
// ======================================================================
// ======================================================================
// Timer Handling Functions
void
imepAgent::handlerTimer(imepTimerType t)
{
switch(t) {
case BEACON_TIMER:
handlerBeaconTimer();
break;
case CONTROL_TIMER:
handlerControlTimer();
break;
case REXMIT_TIMER:
handlerReXmitTimer();
break;
case INCOMING_TIMER:
handlerIncomingTimer();
break;
default:
abort();
}
}
void
imepAgent::handlerBeaconTimer(void)
{
imepLink *l;
// garbage collect old links
purgeLink();
if (verbose) log_neighbor_list();
/* aside from the debugging asserts, handleControlTimer will generate a
``beacon'' packet if there are no objects pending, so we could
just call it. Since we have sendBeacon() laying around, though,
I'll call it and leave the debugging asserts in handleControlTimer()
The packet generated by handlerControlTimer is a beacon equivelent,
so we don't need to generate a beacon. */
if (controlTimer.busy() || helloQueue.length() > 0)
{ // a control timer is pending or there's left over Hello's in the queue,
// but we're about to service all pending acks, hellos, and objects now,
// so cancel the timer
if (controlTimer.busy()) controlTimer.cancel();
handlerControlTimer();
}
else
{ // all the Hellos we had to send out during the last BEACON_PERIOD
// went out. Assuming there were some, they were beacon equivelent,
// and we don't need to send a beacon now. If there were none, beacon
if (NULL == imepLinkHead.lh_first) sendBeacon();
// this is a touch conservative, since if there were hellos that went out
// but their links were down'd by purgeLink, we'll still beacon.
// But, if we *have*no* adjacencies, we should do something to get some
}
/* Send a hello to all our IN adjacencies (everyone we've heard a
packet from). This loads up the helloQueue with all the hellos that
that need to go out sometime in the next BEACON_PERIOD before the
beaconTimer goes off, but doesn't start the controlTimer. If a
control packet is sent for some other reason, the hellos will ride out
for free, otherwise they'll go out when the beacon timer goes off.
*/
int busy_before_hello_load = controlTimer.busy();
for(l = imepLinkHead.lh_first; l; l = l->link.le_next)
{
if (l->status() & LINK_IN) sendHello(l->index());
}
if (!busy_before_hello_load && controlTimer.busy()) controlTimer.cancel();
// restart the beacon timer
beaconTimer.start(BEACON_PERIOD);
}
// transmit all queued ACKs, HELLOs, and OBJECTs.
void
imepAgent::handlerControlTimer(void)
{
Packet *p;
int num_acks = ackQueue.length();
int num_hellos = helloQueue.length();
int num_objects = objectQueue.length();
MAKE_PACKET:
assert(num_acks + num_hellos + num_objects > 0);
// now have to aggregate multiple control packets
p = Packet::alloc();
struct hdr_cmn *ch = HDR_CMN(p);
struct hdr_ip *ih = HDR_IP(p);
struct hdr_imep *im = HDR_IMEP(p);
ch->uid() = uidcnt_++;
ch->ptype() = PT_IMEP;
ch->size() = BEACON_HDR_LEN;
ch->iface() = -2;
ch->error() = 0;
ch->addr_type() = NS_AF_NONE;
ch->prev_hop_ = ipaddr;
ih->saddr() = ipaddr;
ih->daddr() = IP_BROADCAST;
ih->sport() = RT_PORT;
ih->dport() = RT_PORT;
ih->ttl_ = 1;
im->imep_version = IMEP_VERSION;
im->imep_block_flags = 0x00;
U_INT16_T(im->imep_length) = sizeof(struct hdr_imep);
aggregateAckBlock(p);
aggregateHelloBlock(p);
aggregateObjectBlock(p);
imep_output(p);
num_acks = ackQueue.length();
num_hellos = helloQueue.length();
num_objects = objectQueue.length();
if (num_acks + num_hellos + num_objects > 0)
{ // not done yet...
if (verbose)
trace("T %.9f _%d_ imep pkt overflow %d %d %d leftover",
CURRENT_TIME, ipaddr, num_acks, num_hellos, num_objects);
goto MAKE_PACKET;
}
// don't need to restart the controlTimer because the arrival of
// the next packet from an ULP will start it.
}
void
imepAgent::handlerReXmitTimer()
{
Packet *p;
Time rexat;
int num_xmits_left;
rexmitq.peekHead(&rexat, &p, &num_xmits_left);
if (NULL == p) return; // no more pkts on queue
struct hdr_cmn *ch = HDR_CMN(p);
if (0 == num_xmits_left)
{
if (verbose)
{
trace("T %.9f _%d_ rexmit timed out %d RL:%s",
CURRENT_TIME, ipaddr, ch->uid(), dumpResponseList(p));
}
struct imep_object_block *ob = findObjectBlock(p);
struct imep_response *r = findResponseList(p);
int i;
for(i = 0; i < ob->ob_num_responses; i++, r++)
{
if (verbose) trace("T %.9f _%d_ punting neighbor %d",
CURRENT_TIME, ipaddr, INT32_T(r->resp_ipaddr));
imepSetLinkDownStatus(INT32_T(r->resp_ipaddr));
}
stats.num_rexmitable_retired++;
stats.sum_rexmitable_retired_response_sz += ob->ob_num_responses;
// don't need to explicitly remove p from q and drop it, since
// by downing all the links on it's response list, it'll have bee
// dropped anyway
// rexmitq.removeHead();
// drop(p, DROP_RTR_QTIMEOUT);
}
else if (rexat <= CURRENT_TIME)
{
if (verbose)
trace("T %.9f _%d_ rexmit %d as %d",
CURRENT_TIME, ipaddr, ch->uid(), uidcnt_);
ch->uid() = uidcnt_++;
imep_output(p->copy());
num_xmits_left--;
rexmitq.removeHead(); // take it off the queue and reinsert it
rexmitq.insert(CURRENT_TIME + RETRANS_PERIOD, p, num_xmits_left);
stats.num_rexmits++;
}
// reschedule the timer
rexmitq.peekHead(&rexat, &p, &num_xmits_left);
if (NULL == p) return; // no more pkts on queue
if (verbose) trace("T %.9f _%d_ rexmit trigger again for %d at %.9f (in %.9f)",
CURRENT_TIME, ipaddr, ch->uid(), rexat, rexat - CURRENT_TIME );
rexmitTimer.start(rexat - CURRENT_TIME);
}
void
imepAgent::handlerIncomingTimer()
{
Packet *p;
u_int32_t s;
double expire;
int index;
if (verbose) trace("T %.9f _%d_ inorder - timer expired",
CURRENT_TIME, ipaddr);
incomingQ.dumpAll();
while((p = incomingQ.getNextPacket(s))) {
stats.num_holes_retired++;
index = HDR_IP(p)->saddr();
imepLink *l = findLink(index);
assert(l); // if there's no link entry, then the incoming
// q should have been cleared, when the link entry was destroyed
if(verbose)
trace("T %.9f _%d_ inorder - src %d hole retired seq %d -> %d",
CURRENT_TIME, ipaddr, index, l->lastSeq(), s);
/* tell ULP that we've effectively broken our link to
neighbor by retiring the hole and accepting the deletion.
since we don't do this till at least MAX_REXMIT_TIME after
receiving the out of seq packet, we're sure the packet's sender
must have timed us out when we didn't ack their packet.
can't call imepLinkDown b/c it'll purge the reseq q */
rtagent_->rtNotifyLinkDN(index);
stats.delete_neighbor3++;
rtagent_->rtNotifyLinkUP(index);
stats.new_neighbor++;
if (verbose)
trace("T %.9f _%d_ inorder - src %d seq %d (timer delivery)",
CURRENT_TIME, ipaddr, index, s);
l->lastSeq() = s; // advance sequence number for this neighbor
stats.num_recvd_from_queue++;
imep_object_process(p);
Packet::free(p);
// now deliver as many in sequence packets to ULP as possible
Packet *p0;
while((p0 = incomingQ.getPacket(index, l->lastSeq() + 1)))
{
if (verbose)
trace("T %.9f _%d_ inorder - src %d seq %d (chain"
" timer delivery)", CURRENT_TIME, ipaddr,
HDR_IP(p0)->saddr(), l->lastSeq() + 1);
l->lastSeq() += 1;
stats.num_recvd_from_queue++;
imep_object_process(p0);
Packet::free(p0);
}
}
if((expire = incomingQ.getNextExpire()) != 0.0) {
assert(expire > CURRENT_TIME);
if (verbose)
trace("T %.9f _%d_ inorder - timer started (delay %.9f)",
CURRENT_TIME, ipaddr, expire - CURRENT_TIME);
incomingTimer.start(expire - CURRENT_TIME);
}
}
//////////////////////////////////////////////////////////////////////
void
imepAgent::scheduleReXmit(Packet *p)
{
rexmitq.insert(CURRENT_TIME + RETRANS_PERIOD, p, MAX_REXMITS);
// start the timer
if (!rexmitTimer.busy()) rexmitTimer.start(RETRANS_PERIOD);
}
void
imepAgent::scheduleIncoming(Packet *p, u_int32_t s)
{
struct hdr_ip *ip = HDR_IP(p);
incomingQ.addEntry(ip->saddr(), CURRENT_TIME + MAX_RETRANS_TIME, s, p);
// start the timer
if (!incomingTimer.busy()) {
if (verbose)
trace("T %.9f _%d_ inorder - timer started",
CURRENT_TIME, ipaddr);
incomingTimer.start(MAX_RETRANS_TIME);
}
}
// ======================================================================
// Packet Processing Functions
void
imepAgent::recv(Packet *p, Handler *)
{
//struct hdr_ip *ih = HDR_IP(p);
struct hdr_cmn *ch = HDR_CMN(p);
assert(initialized());
if(ch->prev_hop_ == ipaddr) {
// I hate all uses of prev_hop, but the only other way to
// do this test is by checking for a nonNULL handler (like
// mac-801_11.cc does), which would
// require changing tora to send out pkts with a non 0 hndler
// -dam
recv_outgoing(p);
} else {
recv_incoming(p);
}
}
void
imepAgent::recv_outgoing(Packet *p)
{
struct hdr_cmn *ch = HDR_CMN(p);
struct hdr_ip *ip = HDR_IP(p);
if(DATA_PACKET(ch->ptype())) {
imep_output(p);
return;
}
if(ip->daddr() != (nsaddr_t) IP_BROADCAST) {
fprintf(stderr, "IP dst is unicast - not encapsulating\n");
imep_output(p);
return;
}
assert(ch->ptype() == PT_TORA);
// XXX: for debugging purposes - IMEP supports other object types
objectQueue.enque(p);
// this queue is a queue of "packets" passed down from the
// upper layer routing protocols that IMEP will buffer and try
// to aggregate before transmitting. Although these are valid
// packets, they must not be transmitted before encaspulating
// them in an IMEP packet to ensure reliability.
double send_delay = MIN_TRANSMIT_WAIT_TIME_HIGHP
+ ((MAX_TRANSMIT_WAIT_TIME_HIGHP - MIN_TRANSMIT_WAIT_TIME_HIGHP)
* Random::uniform());
if (controlTimer.busy() == 0)
{
controlTimer.start(send_delay);
}
else if (controlTimer.timeLeft() > send_delay)
{
controlTimer.cancel();
controlTimer.start(send_delay);
}
}
void
imepAgent::recv_incoming(Packet *p)
{
struct hdr_cmn *ch = HDR_CMN(p);
struct hdr_ip *ih = HDR_IP(p);
struct hdr_imep *im = HDR_IMEP(p);
if(DATA_PACKET(ch->ptype())) {
imep_input(p);
return;
}
// if it's a data packet, the ip->src could be from far away,
// so we can't use it for link indications. If we RARPd the
// MAC source addr, we could use that...
imepSetLinkInStatus(ih->saddr());
// XXX: this could be done at the MAC layer. In fact, I will
// augment the IEEE 802.11 layer so that the receipt of an
// ACK confirms bidirectional status. -josh
// hasn't actually be done. seems unlikely to be of help, and is
// fairly hard to do. -dam 8/19/98
assert(ch->ptype() == PT_IMEP);
assert(im->imep_version == IMEP_VERSION);
if(im->imep_block_flags == 0) {
imep_beacon_input(p);
Packet::free(p);
return;
}
if(im->imep_block_flags & BLOCK_FLAG_ACK)
imep_ack_input(p);
if(im->imep_block_flags & BLOCK_FLAG_HELLO)
imep_hello_input(p);
if(im->imep_block_flags & BLOCK_FLAG_OBJECT) {
imep_object_input(p);
// each upper layer object will be decapsulated and
// placed into its own packet before being passed
// to the upper layer. This provides total transparency
// to the upper layer.
}
Packet::free(p);
}
void
imepAgent::imep_beacon_input(Packet *p)
{
struct hdr_ip *ip = HDR_IP(p);
sendHello(ip->saddr());
}
// If there is an ACK for us we need to (1) removed the sender
// of the ACK from the "ack list", and we need to update the
// status of this neighbor to "BIDIRECTIONAL".
void
imepAgent::imep_ack_input(Packet *p)
{
struct hdr_ip *ih = HDR_IP(p);
struct imep_ack_block *ab = findAckBlock(p);
struct imep_ack *ack;
assert(ab);
ack = (struct imep_ack*) (ab + 1);
// According to the IMEP specs, the ACK block (if it exists)
// immediately follows the 3-byte IMEP header.
for(int i = 0; i < ab->ab_num_acks; i++, ack++) {
if(INT32_T(ack->ack_ipaddr) == ipaddr) {
Packet *p0 = findObjectSequence(ack->ack_seqno);
if (NULL == p0)
{
if(verbose)
trace("T %.9f _%d_ %d acks seq %d : no obj"
" block", CURRENT_TIME, ipaddr,
ih->saddr(), ack->ack_seqno);
stats.num_unexpected_acks++;
continue;
}
removeObjectResponse(p0, ih->saddr());
imepSetLinkBiStatus(ih->saddr());
}
}
}
void
imepAgent::imep_hello_input(Packet *p)
{
struct hdr_ip *ip = HDR_IP(p);
struct imep_hello_block *hb = findHelloBlock(p);
struct imep_hello *hello;
assert(hb);
hello = (struct imep_hello*) (hb + 1);
for(int i = 0; i < hb->hb_num_hellos; i++, hello++) {
if(INT32_T(hello->hello_ipaddr) == ipaddr) {
imepSetLinkBiStatus(ip->saddr());
break;
}
}
}
void
imepAgent::imep_object_input(Packet *p)
{
struct imep_object_block *ob;
// First, send an ack for the object
imep_ack_object(p);
// now see what to do with the object
ob = findObjectBlock(p);
assert(ob);
struct hdr_ip *iph = HDR_IP(p);
imepLink *l = findLink(iph->saddr());
assert(l); // if we have an object, a link entry should already exist
if (!l->lastSeqValid())
{ // first object we've heard from this node
l->lastSeqValid() = 1;
l->lastSeq() = ob->ob_sequence - 1;
if (verbose)
trace("T %.9f _d_ first object from neighbor %d seq %d",
CURRENT_TIME, ipaddr, iph->src(), ob->ob_sequence);
}
// This calc requires sequence number SEQ_GT() semantics
// Life will be very bad if this calc isn't actually done in
// a register the size of the sequence number space
int8_t reg = (int8_t) ob->ob_sequence - (int8_t) l->lastSeq();
if(reg <= 0)
{ // already passed this pkt up to ULP or declared it a permenant
// hole
if (verbose)
trace("T %.9f _%d_ from %d ignored seq %d (already heard)",
CURRENT_TIME, ipaddr, iph->src(), ob->ob_sequence);
stats.num_out_of_window_objs++;
return;
}
if (verbose && reg > 1)
{ // found a hole in the sequence number space...
trace("T %.9f _%d_ inorder - src %d seq %d out of order (%d expected)",
CURRENT_TIME, ipaddr, iph->src(),
ob->ob_sequence, l->lastSeq()+1);
}
if (1 == reg)
{ // ``fast path''
// got the expected next seq num
if (verbose)
trace("T %.9f _%d_ inorder - fastpath src %d seq %d (delivering)",
CURRENT_TIME, ipaddr, HDR_IP(p)->src(), ob->ob_sequence);
stats.num_in_order_objs++;
imep_object_process(p);
assert((u_int8_t)(l->lastSeq() + 1) == ob->ob_sequence);
l->lastSeq() = ob->ob_sequence;
}
else
{
// put this packet on the resequencing queue
scheduleIncoming(p->copy(), ob->ob_sequence);
stats.num_out_of_order_objs++;
}
// now deliver as many in-sequence packets to ULP as possible
Packet *p0;
while((p0 = incomingQ.getPacket(iph->saddr(), l->lastSeq() + 1)))
{
stats.num_recvd_from_queue++;
if (verbose)
trace("T %.9f _%d_ inorder - src %d seq %d (delivering)",
CURRENT_TIME, ipaddr, HDR_IP(p0)->src(), l->lastSeq() + 1);
l->lastSeq() += 1;
imep_object_process(p0);
Packet::free(p0);
}
}
void
imepAgent::imep_object_process(Packet *p)
// hand the conents of any object in the pkt to the respective ULP
{
struct imep_object_block *ob;
struct imep_object *object;
int i;
stats.num_object_pkts_recvd++;
ob = findObjectBlock(p);
assert(ob);
assert(ob->ob_protocol_type == PROTO_TORA); // XXX: more general later
object = (struct imep_object*) (ob + 1);
for(i = 0; i < ob->ob_num_objects; i++)
{
Packet *p0 = p->copy();
assert(object->o_length > 0); // sanity check
toraCreateHeader(p0,
((char*) object) + sizeof(struct imep_object),
object->o_length);
imep_input(p0);
object = (struct imep_object*) ((char*) object +
sizeof(struct imep_object) + object->o_length);
}
}
void
imepAgent::imep_ack_object(Packet *p)
// send an ack for the object in p, if any
{
struct hdr_ip *iph = HDR_IP(p);
struct imep_object_block *ob;
struct imep_object *object;
int i;
ob = findObjectBlock(p);
if (!ob) return;
if (0 == ob->ob_num_responses)
return;
if (31 == ob->ob_num_responses)
{ // a ``broadcast'' response list to which everyone replies
sendAck(iph->saddr(), ob->ob_sequence);
return;
}
object = (struct imep_object*) (ob + 1);
// walk the objects to find the response list
for(i = 0; i < ob->ob_num_objects; i++)
{
object = (struct imep_object*) ((char*) object +
sizeof(struct imep_object) + object->o_length);
}
struct imep_response *r = (struct imep_response*) object;
for (i = 0; i < ob->ob_num_responses; i++)
{
if (INT32_T(r->resp_ipaddr) == ipaddr)
{
sendAck(iph->saddr(), ob->ob_sequence);
break;
}
r = r + 1;
}
}
// ======================================================================
// ======================================================================
// Routines by which packets leave the IMEP object
void
imepAgent::imep_input(Packet *p)
{
recvtarget_->recv(p, (Handler*) 0);
}
void
imepAgent::imep_output(Packet *p)
{
struct hdr_cmn *ch = HDR_CMN(p);
if(imep_use_mac_callback) {
ch->xmit_failure_ = imep_failed_callback;
ch->xmit_failure_data_ = (void*) this;
} else {
ch->xmit_failure_ = 0;
ch->xmit_failure_data_ = 0;
}
ch->xmit_reason_ = 0;
sendtarget_->recv(p, (Handler*) 0);
}
// ======================================================================
// ======================================================================
// Debugging routines
void
imepAgent::imep_dump_header(Packet *p)
{
struct hdr_imep *im = HDR_IMEP(p);
fprintf(stderr,
"imep_version: 0x%x\n", im->imep_version);
fprintf(stderr,
"imep_block_flags: 0x%x\n", im->imep_block_flags);
fprintf(stderr,
"imep_length: 0x%04x\n", U_INT16_T(im->imep_length));
Packet::dump_header(p, off_IMEP_, 64);
}
void
imepAgent::log_neighbor_list()
{
imepLink *l;
int offset = 0;
if(! verbose ) return;
sprintf(logtarget_->buffer(),
"T %.9f _%d_ neighbors: ", CURRENT_TIME, ipaddr);
for(l = imepLinkHead.lh_first; l; l = l->link.le_next) {
offset = strlen(logtarget_->buffer());
sprintf(logtarget_->buffer() + offset,
"%d%c ", l->index(),
l->status() == LINK_BI ? '+' :
(l->status() == LINK_IN ? '-' :
(l->status() == LINK_OUT ? '|' : 'X')));
}
logtarget_->dump();
}
void
imepAgent::trace(char* fmt, ...)
{
va_list ap;
if (!logtarget_) return;
va_start(ap, fmt);
vsprintf(logtarget_->buffer(), fmt, ap);
logtarget_->dump();
va_end(ap);
}
char *
imepAgent::dumpResponseList(Packet *p)
{
static char buf[512];
char *ptr = buf;
struct imep_object_block *ob = findObjectBlock(p);
struct imep_response *r = findResponseList(p);
struct imep_response *r0;
int i;
for(i = 0, r0 = r; i < ob->ob_num_responses; i++, r0++)
{
ptr += (int)sprintf(ptr,"%d ", INT32_T(r0->resp_ipaddr));
}
return buf;
}
void
imepAgent::Terminate()
{
trace("IL %.9f _%d_ Add-Adj: %d New-Neigh: %d Del-Neigh1: %d Del-Neigh2: %d Del-Neigh3: %d",
CURRENT_TIME, ipaddr,
stats.new_in_adjacency,
stats.new_neighbor ,
stats.delete_neighbor1 ,
stats.delete_neighbor2,
stats.delete_neighbor3);
trace("IL %.9f _%d_ Created QRY: %d UPD: %d CLR: %d",CURRENT_TIME, ipaddr,
stats.qry_objs_created ,
stats.upd_objs_created ,
stats.clr_objs_created);
trace("IL %.9f _%d_ Received QRY: %d UPD: %d CLR: %d",CURRENT_TIME, ipaddr,
stats.qry_objs_recvd ,
stats.upd_objs_recvd ,
stats.clr_objs_recvd);
trace("IL %.9f _%d_ Total-Obj-Created: %d Obj-Pkt-Created: %d Obj-Pkt-Recvd: %d",
CURRENT_TIME, ipaddr,
stats.num_objects_created ,
stats.num_object_pkts_created ,
stats.num_object_pkts_recvd);
trace("IL %.9f _%d_ Rexmit Pkts: %d Acked: %d Retired: %d Rexmits: %d",
CURRENT_TIME, ipaddr,
stats.num_rexmitable_pkts ,
stats.num_rexmitable_fully_acked ,
stats.num_rexmitable_retired ,
stats.num_rexmits);
trace("IL %.9f _%d_ Sum-Response-List-Size Created: %d Retired: %d",
CURRENT_TIME, ipaddr,
stats.sum_response_list_sz ,
stats.sum_rexmitable_retired_response_sz);
trace("IL %.9f _%d_ Holes Created: %d Retired: %d ReSeqQ-Drops: %d ReSeqQ-Recvd: %d",
CURRENT_TIME, ipaddr,
stats.num_holes_created ,
stats.num_holes_retired ,
stats.num_reseqq_drops,
stats.num_recvd_from_queue);
trace("IL %.9f _%d_ Unexpected-Acks: %d Out-Win-Obj: %d Out-Order-Obj: %d In-Order-Obj: %d", CURRENT_TIME, ipaddr,
stats.num_unexpected_acks ,
stats.num_out_of_window_objs ,
stats.num_out_of_order_objs ,
stats.num_in_order_objs);
}
imep_api.cc
/* -*- 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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*/
/* Ported from CMU/Monarch's code*/
/*
imep_api.cc
$Id: AllCode__.html 980 2002-12-12 09:36:58Z ffilali $
*/
#include
#define CURRENT_TIME Scheduler::instance().clock()
static const int verbose = 0;
// ======================================================================
// ======================================================================
// The IMEP API
void
imepAgent::imepRegister(rtAgent *rt)
{
rtagent_ = rt;
assert(rtagent_);
}
void
imepAgent::imepGetLinkStatus(nsaddr_t index, u_int32_t &status)
{
imepLink *l = findLink(index);
if(l == 0)
{
status = LINK_DOWN;
}
else
{
status = l->status();
}
}
void
imepAgent::imepSetLinkInStatus(nsaddr_t index)
{
imepLink *l = findLink(index);
if(l == 0)
{
l = new imepLink(index);
LIST_INSERT_HEAD(&imepLinkHead, l, link);
l->status() = LINK_DOWN;
}
if (LINK_DOWN == l->status())
{ // the link is an in adjacency
if (verbose) trace("T %.9f _%d_ new link to %d",
CURRENT_TIME, ipaddr, index);
l->lastSeq() = 0; // expect 1 next (not needed XXX)
l->lastSeqValid() = 0;
l->out_expire() = -1.0;
stats.new_in_adjacency++;
// using the imep-spec-01 logic (sec 3.4.2)
// send a hello immeadiately after learning of a new
// adjacency.
sendHello(index);
// the draft sez ``immeadiate'' --- I'll allow the
// time for aggregation. we could cancel the controlTimer
// here and launch the packet immeadiately if we wanted. -dam
}
u_int ostatus = l->status();
l->status() |= LINK_IN;
if (ostatus != l->status() && l->status() == LINK_BI)
{
rtagent_->rtNotifyLinkUP(index);
stats.new_neighbor++;
}
l->in_expire() = CURRENT_TIME + MAX_BEACON_TIME;
}
void
imepAgent::imepSetLinkOutStatus(nsaddr_t index)
{
imepLink *l = findLink(index);
// how could we know that someone hears us w/o us
// first receiving a packet from them (which would create
// in status and create a link record for them)? -dam
assert(l);
u_int ostatus = l->status();
l->status() |= LINK_OUT;
if (ostatus != l->status() && l->status() == LINK_BI)
{
rtagent_->rtNotifyLinkUP(index);
stats.new_neighbor++;
}
l->out_expire() = CURRENT_TIME + MAX_BEACON_TIME;
}
void
imepAgent::imepSetLinkBiStatus(nsaddr_t index)
{
imepSetLinkInStatus(index);
imepSetLinkOutStatus(index);
}
void
imepAgent::imepSetLinkDownStatus(nsaddr_t index)
{
imepLink *l = findLink(index);
if(l == 0) {
return;
}
l->status() = LINK_DOWN;
l->in_expire() = -1.0;
l->out_expire() = -1.0;
// clear the resequencing queue for the neighbor
incomingQ.deleteDst(index);
// clean this node off the response list of any packets
// we're expecting them to ack
purgeReXmitQ(index);
// tell the routing layer the link is gone
rtagent_->rtNotifyLinkDN(index);
stats.delete_neighbor1++;
if (verbose) trace("T %.9f _%d_ down link to %d",
CURRENT_TIME, ipaddr, index);
}
void
imepAgent::imepPacketUndeliverable(Packet *p)
{
struct hdr_cmn *cmh = HDR_CMN(p);
struct hdr_ip *ip = HDR_IP(p);
if (NS_AF_INET == cmh->addr_type())
imepSetLinkDownStatus(cmh->next_hop());
if (verbose) trace("T %.9f _%d_ undeliverable pkt to %d",
CURRENT_TIME, ipaddr, ip->dst());
rtagent_->rtRoutePacket(p);
}
void
imepAgent::purgeLink()
{
imepLink *l, *nl;
for(l = imepLinkHead.lh_first ; l; l = nl)
{
nl = l->link.le_next;
// Is this a bug? should save old status now, and then
// notify rtagent if ostatus == LINK_BI and new status doesn't
// -dam 8/26/98
// I don't think it's a problem, since any packet that
// sets link_out expire time also sets link_in expire time,
// so a LINK_OUT && !LINK_IN state should never be possible -dam
int ostatus = l->status();
if (l->in_expire() < CURRENT_TIME) l->status() &= ~LINK_IN;
if (l->out_expire() < CURRENT_TIME) l->status() &= ~LINK_OUT;
if (LINK_BI == ostatus && LINK_BI != l->status())
{
imepSetLinkDownStatus(l->index());
}
if (LINK_DOWN == l->status())
{
stats.delete_neighbor2++;
LIST_REMOVE(l, link);
delete l;
}
}
}
void
imepAgent::imepGetBiLinks(int*& nblist, int& nbcnt)
{
imepLink *l;
int cnt = 0;
for(l = imepLinkHead.lh_first; l; l = l->link.le_next) {
if(l->status() == LINK_BI) cnt++;
}
nbcnt = cnt;
if(cnt == 0) return;
// no neighbors
nblist = new int[cnt];
cnt = 0;
for(l = imepLinkHead.lh_first; l; l = l->link.le_next) {
if(l->status() == LINK_BI) {
nblist[cnt] = l->index();
cnt++;
}
}
}
imep_io.cc
/* -*- 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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*/
/* Ported from CMU/Monarch's code*/
/*
imep_io.cc
$Id: AllCode__.html 980 2002-12-12 09:36:58Z ffilali $
marshall IMEP packets
*/
#include
#include
#include
// ======================================================================
// ======================================================================
// Outgoing Packets
void
imepAgent::sendBeacon()
{
Packet *p = Packet::alloc();
struct hdr_cmn *ch = HDR_CMN(p);
struct hdr_ip *ih = HDR_IP(p);
struct hdr_imep *im = HDR_IMEP(p);
ch->ptype() = PT_IMEP;
ch->size() = BEACON_HDR_LEN;
ch->iface() = -2;
ch->error() = 0;
ch->addr_type() = NS_AF_NONE;
ch->prev_hop_ = ipaddr;
ch->uid() = uidcnt_++;
ih->saddr() = ipaddr;
ih->daddr() = IP_BROADCAST;
ih->sport() = RT_PORT;
ih->dport() = RT_PORT;
ih->ttl_ = 1;
im->imep_version = IMEP_VERSION;
im->imep_block_flags = 0x00;
U_INT16_T(im->imep_length) = sizeof(struct hdr_imep);
imep_output(p);
}
void
imepAgent::sendHello(nsaddr_t index)
{
Packet *p = Packet::alloc();
struct hdr_cmn *ch = HDR_CMN(p);
struct hdr_ip *ih = HDR_IP(p);
struct hdr_imep *im = HDR_IMEP(p);
struct imep_hello_block *hb = (struct imep_hello_block*) (im + 1);
struct imep_hello *hello = (struct imep_hello*) (hb + 1);
ch->ptype() = PT_IMEP;
ch->size() = HELLO_HDR_LEN;
ch->iface() = -2;
ch->error() = 0;
ch->addr_type() = NS_AF_NONE;
ch->prev_hop_ = ipaddr;
ch->uid() = uidcnt_++;
ih->saddr() = ipaddr;
ih->daddr() = IP_BROADCAST;
ih->sport() = RT_PORT;
ih->dport() = RT_PORT;
ih->ttl_ = 1;
im->imep_version = IMEP_VERSION;
im->imep_block_flags = BLOCK_FLAG_HELLO;
U_INT16_T(im->imep_length) = sizeof(struct hdr_imep) +
sizeof(struct imep_hello_block) + sizeof(struct imep_hello);
hb->hb_num_hellos = 1;
INT32_T(hello->hello_ipaddr) = index;
helloQueue.enque(p);
// aggregate as many control messages as possible before sending
if(controlTimer.busy() == 0) {
controlTimer.start(MIN_TRANSMIT_WAIT_TIME_LOWP
+ ((MAX_TRANSMIT_WAIT_TIME_LOWP - MIN_TRANSMIT_WAIT_TIME_LOWP)
* Random::uniform()));
}
}
void
imepAgent::sendAck(nsaddr_t index, u_int32_t seqno)
{
Packet *p = Packet::alloc();
struct hdr_cmn *ch = HDR_CMN(p);
struct hdr_ip *ih = HDR_IP(p);
struct hdr_imep *im = HDR_IMEP(p);
struct imep_ack_block *ab = (struct imep_ack_block*) (im + 1);
struct imep_ack *ack = (struct imep_ack*) (ab + 1);
ch->ptype() = PT_IMEP;
ch->size() = ACK_HDR_LEN;
ch->iface() = -2;
ch->error() = 0;
ch->addr_type() = NS_AF_NONE;
ch->prev_hop_ = ipaddr;
ch->uid() = uidcnt_++;
ih->saddr() = ipaddr;
ih->daddr() = IP_BROADCAST;
ih->sport() = RT_PORT;
ih->dport() = RT_PORT;
ih->ttl_ = 1;
im->imep_version = IMEP_VERSION;
im->imep_block_flags = BLOCK_FLAG_ACK;
U_INT16_T(im->imep_length) = sizeof(struct hdr_imep) +
sizeof(struct imep_ack_block) + sizeof(struct imep_ack)
;
ab->ab_num_acks = 1;
ack->ack_seqno = seqno;
INT32_T(ack->ack_ipaddr) = index;
// aggregate as many control messages as possible before sending
ackQueue.enque(p);
if(controlTimer.busy() == 0) {
controlTimer.start(MIN_TRANSMIT_WAIT_TIME_LOWP
+ ((MAX_TRANSMIT_WAIT_TIME_LOWP - MIN_TRANSMIT_WAIT_TIME_LOWP)
* Random::uniform()));
}
}
imep_rt.cc
/* -*- 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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*/
/* Ported from CMU/Monarch's code*/
/*
imep_rt.cc
$Id
Routing Protocol (or more generally, ULP) Specific Functions
*/
#include
#include
#include
// ======================================================================
// computes the length of a TORA header, copies the header to "dst"
// and returns the length of the header in "length".
int
imepAgent::toraHeaderLength(struct hdr_tora *t)
{
switch(t->th_type) {
case TORATYPE_QRY:
return sizeof(struct hdr_tora_qry);
break;
case TORATYPE_UPD:
return sizeof(struct hdr_tora_upd);
break;
case TORATYPE_CLR:
return sizeof(struct hdr_tora_clr);
break;
default:
abort();
}
return 0; /* Make msvc happy */
}
void
imepAgent::toraExtractHeader(Packet *p, char* dst)
{
struct hdr_tora *t = HDR_TORA(p);
int length = toraHeaderLength(t);
bcopy((char*) t, dst, length);
switch(t->th_type) {
case TORATYPE_QRY:
stats.qry_objs_created++;
break;
case TORATYPE_UPD:
stats.upd_objs_created++;
break;
case TORATYPE_CLR:
stats.clr_objs_created++;
break;
default:
abort();
}
}
void
imepAgent::toraCreateHeader(Packet *p, char *src, int length)
{
struct hdr_cmn *ch = HDR_CMN(p);
struct hdr_tora *t = HDR_TORA(p);
ch->size() = IP_HDR_LEN + length;
ch->ptype() = PT_TORA;
bcopy(src, (char*) t, length);
switch(t->th_type) {
case TORATYPE_QRY:
stats.qry_objs_recvd++;
break;
case TORATYPE_UPD:
stats.upd_objs_recvd++;
break;
case TORATYPE_CLR:
stats.clr_objs_recvd++;
break;
default:
abort();
}
}
imep_timers.cc
/* -*- 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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*/
/* Ported from CMU/Monarch's code*/
/*
imep_timers.cc
$Id: AllCode__.html 980 2002-12-12 09:36:58Z ffilali $
*/
#include
#include
#include
#define CURRENT_TIME Scheduler::instance().clock()
// ======================================================================
// ======================================================================
void
imepTimer::handle(Event *)
{
busy_ = 0;
agent->handlerTimer(type_);
}
void
imepTimer::start(double time)
{
assert(busy_ == 0);
busy_ = 1;
Scheduler::instance().schedule(this, &intr, time);
}
void
imepTimer::cancel()
{
assert(busy_ == 1);
busy_ = 0;
Scheduler::instance().cancel(&intr);
}
double
imepTimer::timeLeft()
{
assert(busy_ == 1);
return intr.time_ - CURRENT_TIME;
}
imep_util.cc
/* -*- 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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*/
/* Ported from CMU/Monarch's code*/
/*
imep_util.cc
$Id: AllCode__.html 980 2002-12-12 09:36:58Z ffilali $
*/
#include
#include
#define CURRENT_TIME Scheduler::instance().clock()
static const int verbose = 0;
// ======================================================================
// Utility routines to manipulate IMEP packets.
imep_ack_block*
imepAgent::findAckBlock(Packet *p)
{
struct hdr_imep *im = HDR_IMEP(p);
struct imep_ack_block *ab;
assert(im->imep_version == IMEP_VERSION);
if((im->imep_block_flags & BLOCK_FLAG_ACK) == 0)
return 0;
ab = (struct imep_ack_block*) (im + 1);
assert(ab->ab_num_acks > 0);
return ab;
}
imep_hello_block*
imepAgent::findHelloBlock(Packet *p)
{
struct hdr_imep *im = HDR_IMEP(p);
struct imep_hello_block *hb;
assert(im->imep_version == IMEP_VERSION);
if((im->imep_block_flags & BLOCK_FLAG_HELLO) == 0)
return 0;
if(im->imep_block_flags & BLOCK_FLAG_ACK) {
struct imep_ack_block *ab = findAckBlock(p);
struct imep_ack *ack = (struct imep_ack*) (ab + 1);
hb = (struct imep_hello_block*) (ack + ab->ab_num_acks);
}
else {
hb = (struct imep_hello_block*) (im + 1);
}
assert(hb->hb_num_hellos > 0);
return hb;
}
imep_object_block*
imepAgent::findObjectBlock(Packet *p)
{
struct hdr_imep *im = HDR_IMEP(p);
struct imep_ack_block *ab;
struct imep_hello_block *hb;
struct imep_object_block *ob;
char *ptr;
assert(im->imep_version == IMEP_VERSION);
if((im->imep_block_flags & BLOCK_FLAG_OBJECT) == 0)
return 0;
ptr = (char *) (im + 1);
if (im->imep_block_flags & BLOCK_FLAG_ACK) {
ab = (struct imep_ack_block*) ptr;
ptr += ab->ab_num_acks * sizeof(struct imep_ack)
+ sizeof(struct imep_ack_block);
assert(ab->ab_num_acks > 0);
}
if (im->imep_block_flags & BLOCK_FLAG_HELLO) {
hb = (struct imep_hello_block *) ptr;
ptr += hb->hb_num_hellos * sizeof(struct imep_hello)
+ sizeof(struct imep_hello_block);
assert(hb->hb_num_hellos > 0);
}
ob = (struct imep_object_block*) ptr;
assert(ob->ob_protocol_type == PROTO_TORA);
// for debugging purposes only
assert(ob->ob_num_objects > 0);
return ob;
}
struct imep_response*
imepAgent::findResponseList(Packet *p)
{
struct hdr_imep *im = HDR_IMEP(p);
struct imep_object_block *ob;
struct imep_object *object;
assert(im->imep_version == IMEP_VERSION);
if((ob = findObjectBlock(p)) == 0)
return 0;
if(ob->ob_num_responses <= 0)
return 0;
object = (struct imep_object*) (ob + 1);
for(int i = 0; i < ob->ob_num_objects; i++) {
object = (struct imep_object*) ((char*) object +
sizeof(struct imep_object) + object->o_length);
}
return (struct imep_response*) object;
}
// ======================================================================
// ======================================================================
void
imepAgent::aggregateAckBlock(Packet *p)
{
struct hdr_cmn *ch = HDR_CMN(p);
struct hdr_imep *im = HDR_IMEP(p);
struct imep_ack_block *ab = (struct imep_ack_block*) (im + 1);
struct imep_ack *ack = (struct imep_ack*) (ab + 1);
int num_acks = ackQueue.length();
if(num_acks == 0) return;
ch->size() += sizeof(struct imep_ack_block);
U_INT16_T(im->imep_length) += sizeof(struct imep_ack_block);
im->imep_block_flags |= BLOCK_FLAG_ACK;
ab->ab_num_acks = 0;
for(int i = 0; i < num_acks; i++) {
if (U_INT16_T(im->imep_length) + sizeof(struct imep_ack)
> IMEP_HDR_LEN) break;
Packet *p0 = ackQueue.deque();
assert(p0);
struct imep_ack_block *ab0 = findAckBlock(p0);
assert(ab0);
struct imep_ack *ack0 = (struct imep_ack*) (ab0 + 1);
ack->ack_seqno = ack0->ack_seqno;
U_INT32_T(ack->ack_ipaddr) = U_INT32_T(ack0->ack_ipaddr);
if (verbose) trace("T %0.9f _%d_ ack %d:%d",
CURRENT_TIME, ipaddr,
U_INT32_T(ack->ack_ipaddr),
ack->ack_seqno);
ch->size() += sizeof(struct imep_ack);
U_INT16_T(im->imep_length) += sizeof(struct imep_ack);
ab->ab_num_acks++;
ack++;
Packet::free(p0);
}
assert(U_INT16_T(im->imep_length) <= IMEP_HDR_LEN);
}
void
imepAgent::aggregateHelloBlock(Packet *p)
{
struct hdr_cmn *ch = HDR_CMN(p);
struct hdr_imep *im = HDR_IMEP(p);
struct imep_hello_block *hb;
struct imep_hello *hello;
int num_hellos = helloQueue.length();
int inserted_hellos = 0;
if (num_hellos == 0) return;
if (U_INT16_T(im->imep_length) + sizeof(struct imep_hello_block)
+ sizeof(struct imep_hello) > IMEP_HDR_LEN) return;
hb = (struct imep_hello_block*)
(((char*) im) + U_INT16_T(im->imep_length));
hello = (struct imep_hello *) (hb + 1);
ch->size() += sizeof(struct imep_hello_block);
U_INT16_T(im->imep_length) += sizeof(struct imep_hello_block);
for(int i = 0; i < num_hellos; i++) {
if ( U_INT16_T(im->imep_length) + sizeof(struct imep_hello)
> IMEP_HDR_LEN ) break; // no more room
Packet *p0 = helloQueue.deque();
assert(p0);
struct imep_hello_block *hb0 = findHelloBlock(p0);
assert(hb0);
struct imep_hello *hello0 = (struct imep_hello*) (hb0 + 1);
imepLink *l = findLink(U_INT32_T(hello0->hello_ipaddr));
if (l && CURRENT_TIME != l->lastEcho())
{
if (verbose) trace("T %0.9f _%d_ hello %d",
CURRENT_TIME, ipaddr,
U_INT32_T(hello0->hello_ipaddr));
U_INT32_T(hello->hello_ipaddr) =
U_INT32_T(hello0->hello_ipaddr);
hello++;
l->lastEcho() = CURRENT_TIME;
inserted_hellos++;
ch->size() += sizeof(struct imep_hello);
U_INT16_T(im->imep_length) += sizeof(struct imep_hello);
}
else
{ // this dest is already in this hello block,
// or it was removed from our adj list since the
// Hello was scheduled for it, so skip the dest
}
Packet::free(p0);
}
if (0 == inserted_hellos)
{
ch->size() -= sizeof(struct imep_hello_block);
U_INT16_T(im->imep_length) -= sizeof(struct imep_hello_block);
return;
}
hb->hb_num_hellos = inserted_hellos;
im->imep_block_flags |= BLOCK_FLAG_HELLO;
assert(U_INT16_T(im->imep_length) <= IMEP_HDR_LEN);
}
// XXX: Objects generatd by TORA or other upper layer protocols
// don't contain a "response list". Since, there is only one "response list"
// for potentially many objects, we generate the response list when
// the packet is sent. I can't think of a case where the response
// list would NOT be "all of my neighbors."
void
imepAgent::aggregateObjectBlock(Packet *p)
{
struct hdr_cmn *ch = HDR_CMN(p);
struct hdr_imep *im = HDR_IMEP(p);
struct imep_object_block *ob;
struct imep_object *object;
int num_objects = objectQueue.length();
int response_list_len;
int num_objects_inserted;
if(num_objects == 0) return;
if (U_INT16_T(im->imep_length) >= IMEP_HDR_LEN) return;
ob = (struct imep_object_block*)
((char*) im + U_INT16_T(im->imep_length));
U_INT16_T(im->imep_length) += sizeof(struct imep_object_block);
ch->size() += sizeof(struct imep_object_block);
object = (struct imep_object*) (ob + 1);
ob->ob_sequence = 0;
// initialized when the "response list" is created.
ob->ob_protocol_type = PROTO_TORA; // XXX
ob->ob_num_responses = 0;
// initialized when the "response list" is created.
response_list_len = getResponseListSize();
for(num_objects_inserted = 0;
num_objects_inserted < num_objects;
num_objects_inserted++) {
Packet *p0 = objectQueue.deque();
assert(p0);
struct hdr_cmn *ch0 = HDR_CMN(p0);
struct hdr_tora *t = HDR_TORA(p0);
int obj_length = toraHeaderLength(t);
int new_len = U_INT16_T(im->imep_length)
+ sizeof(struct imep_object) + obj_length
+ response_list_len;
if (new_len > IMEP_HDR_LEN)
{ // object won't fit, stop now
objectQueue.enqueHead(p0);
break;
}
switch(ch0->ptype()) {
case PT_TORA:
toraExtractHeader(p0, ((char*) object)
+ sizeof(struct imep_object));
break;
default:
fprintf(stderr, "Invalid Packet Type %d\n",
ch0->ptype());
abort();
}
ch->size() += sizeof(struct imep_object) + obj_length;
U_INT16_T(im->imep_length) +=
sizeof(struct imep_object) + obj_length;
object->o_length = obj_length;
object = (struct imep_object*) ((char*) object +
sizeof(struct imep_object) + obj_length);
Packet::free(p0);
}
assert(U_INT16_T(im->imep_length) <= IMEP_HDR_LEN);
if (0 == num_objects_inserted)
{ // remove the object block hdr we just worked so hard to put in
U_INT16_T(im->imep_length) -= sizeof(struct imep_object_block);
ch->size() -= sizeof(struct imep_object_block);
return;
}
im->imep_block_flags |= BLOCK_FLAG_OBJECT;
ob->ob_num_objects = num_objects_inserted;
assert(ob->ob_protocol_type == PROTO_TORA);
// add the response list
createResponseList(p);
stats.num_objects_created += num_objects_inserted;
stats.num_object_pkts_created++;
stats.sum_response_list_sz += ob->ob_num_responses;
if (verbose)
trace("T %.9f _%d_ # obj %d seq %d #resp %d RL %s", CURRENT_TIME, ipaddr,
ob->ob_num_objects, ob->ob_sequence, ob->ob_num_responses, dumpResponseList(p));
if (ob->ob_num_responses > 0)
{ // now make a packet to save on the rexmit queue with only the
// object block and response list in it
Packet *sp = p->copy();
struct hdr_imep *sim = HDR_IMEP(sp);
struct hdr_cmn *sch = HDR_CMN(sp);
sim->imep_block_flags &= ~(BLOCK_FLAG_ACK | BLOCK_FLAG_HELLO);
int objrep_len = U_INT16_T(im->imep_length)
- (int) ((char*) ob - (char*) im);
bcopy(ob, (char *)(sim + 1), objrep_len);
U_INT16_T(sim->imep_length) = sizeof(struct hdr_imep) + objrep_len;
sch->size() = IP_HDR_LEN + sizeof(struct hdr_imep) + objrep_len;
stats.num_rexmitable_pkts++;
scheduleReXmit(sp);
}
}
// ======================================================================
// ======================================================================
int
imepAgent::getResponseListSize()
{
int size = 0;
for(imepLink *l = imepLinkHead.lh_first; l; l = l->link.le_next) {
if (l->status() != LINK_BI) continue;
size += sizeof(struct imep_response);
}
return size;
}
void
imepAgent::createResponseList(Packet *p)
{
struct hdr_cmn *ch = HDR_CMN(p);
struct hdr_imep *im = HDR_IMEP(p);
struct imep_object_block *ob;
if((im->imep_block_flags & BLOCK_FLAG_OBJECT) == 0)
return;
ob = findObjectBlock(p);
assert(ob);
ob->ob_sequence = nextSequence();
struct imep_response *r = (struct imep_response*)
((char*) im + U_INT16_T(im->imep_length));
for(imepLink *l = imepLinkHead.lh_first; l; l = l->link.le_next) {
if (l->status() != LINK_BI) continue;
ch->size() += sizeof(struct imep_response);
U_INT16_T(im->imep_length) += sizeof(struct imep_response);
ob->ob_num_responses += 1;
INT32_T(r->resp_ipaddr) = l->index();
r++;
}
}
rxmit_queue.cc
/* -*- 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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*/
/* Ported from CMU/Monarch's code*/
/* -*- c++ -*-
rexmit_queue.cc
$Id: AllCode__.html 980 2002-12-12 09:36:58Z ffilali $
*/
#include
#include
#include
#include
ReXmitQ::ReXmitQ()
{
LIST_INIT(&head)
}
void
ReXmitQ::insert(Time rxat, Packet *p, int num_rexmits)
{
struct rexent *r = new rexent;
r->rexmit_at = rxat;
r->p = p;
r->rexmits_left = num_rexmits;
struct rexent *i;
if (NULL == head.lh_first || rxat < head.lh_first->rexmit_at)
{
LIST_INSERT_HEAD(&head, r, next);
return;
}
for (i = head.lh_first ; i != NULL ; i = i->next.le_next )
{
if (rxat < i->rexmit_at)
{
LIST_INSERT_BEFORE(i, r, next);
return;
}
if (NULL == i->next.le_next)
{
LIST_INSERT_AFTER(i, r, next);
return;
}
}
}
void
ReXmitQ::peekHead(Time *rxat, Packet **pp, int *rexmits_left)
{
struct rexent *i;
i = head.lh_first;
if (NULL == i) {
*rxat = -1; *pp = NULL; *rexmits_left = -1;
return;
}
*rxat = i->rexmit_at;
*pp = i->p;
*rexmits_left = i->rexmits_left;
}
void
ReXmitQ::removeHead()
{
struct rexent *i;
i = head.lh_first;
if (NULL == i) return;
LIST_REMOVE(i, next);
delete i;
}
void
ReXmitQ::remove(Packet *p)
{
struct rexent *i;
for (i = head.lh_first ; i != NULL ; i = i->next.le_next )
{
if (p == i->p)
{
LIST_REMOVE(i, next);
delete i;
return;
}
}
}
dmalloc_support.cc
/*
* dmalloc_support.cc
* Copyright (C) 1997 by USC/ISI
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation, advertising
* materials, and other materials related to such distribution and use
* acknowledge that the software was developed by the University of
* Southern California, Information Sciences Institute. The name of the
* University may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* @(#) $Header$ (USC/ISI)
*/
/*
* Redefine new and friends to use dmalloc.
*/
#ifdef HAVE_LIBDMALLOC
/*
* NOTE: If dmalloc does not work for you (e.g., it hangs), make sure that
* you are using dmalloc 4.2.0 or above. If you are using lower versions,
* change the following to:
*
* #define DMALLOC_MAJOR_VERSION 3
*/
#define DMALLOC_MAJOR_VERSION 4
#if DMALLOC_MAJOR_VERSION > 3
/*
* This portion copied from ~dmalloc/dmalloc.cc
* Copyright 1999 by Gray Watson
*/
extern "C" {
#include
#define DMALLOC_DISABLE
#include "dmalloc.h"
#include "return.h"
}
/*
* An overload function for the C++ new.
*/
void *
operator new[](size_t size)
{
char *file;
GET_RET_ADDR(file);
return _malloc_leap(file, 0, size);
}
/*
* An overload function for the C++ delete.
*/
void
operator delete(void *pnt)
{
char *file;
GET_RET_ADDR(file);
_free_leap(file, 0, pnt);
}
/*
* An overload function for the C++ delete[]. Thanks to Jens Krinke
*
*/
void
operator delete[](void *pnt)
{
char *file;
GET_RET_ADDR(file);
_free_leap(file, 0, pnt);
}
#else /* DMALLOC_MAJOR_VERSION == 3 */
extern "C" {
#include
#include "dmalloc.h"
#include "return.h"
}
void *
operator new(size_t n)
{
SET_RET_ADDR(_dmalloc_file, _dmalloc_line);
return _malloc_leap(_dmalloc_file, _dmalloc_line, n);
}
void *
operator new[](size_t n)
{
SET_RET_ADDR(_dmalloc_file, _dmalloc_line);
return _malloc_leap(_dmalloc_file, _dmalloc_line, n);
}
void
operator delete(void * cp)
{
if (!cp) // silently ignore null pointers
return;
SET_RET_ADDR(_dmalloc_file, _dmalloc_line);
_free_leap(_dmalloc_file, _dmalloc_line, cp);
}
#endif /* DMALLOC_VERSION */
#endif /* HAVE_LIBDMALLOC */
int.RVec.cc
// This may look like C code, but it is really -*- C++ -*-
/*-
* Copyright (c) 1997 The 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 Network Research
* Group at Lawrence Berkeley National Laboratory.
* 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.
*/
#ifdef __GNUG__
#pragma implementation
#endif
#include "lib/int.RVec.h"
#include // for memset
void
intRVec::grow(const int desired_cap)
{
int *p;
int old_cap = len;
int new_cap = old_cap * 2;
if (new_cap == 0) // always alloc something
new_cap = 4;
if (new_cap < desired_cap) // and make sure it's enough
new_cap = desired_cap;
resize(new_cap);
// zero new spaces
for (p = &s[old_cap]; p < &s[new_cap]; )
*p++ = 0;
}
int.Vec.cc
// This may look like C code, but it is really -*- C++ -*-
/*
Copyright (C) 1988 Free Software Foundation
written by Doug Lea (dl@rocky.oswego.edu)
This file is part of the GNU C++ Library. This library is free
software; you can redistribute it and/or modify it under the terms of
the GNU Library General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your
option) any later version. This library is distributed in the hope
that it will be useful, but WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the GNU Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free Software
Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifdef __GNUG__
#pragma implementation
#endif
// #include
#include
#include "lib/builtin.h"
#include "lib/int.Vec.h"
// error handling
void default_intVec_error_handler(const char* msg)
{
#if 0
cerr << "Fatal intVec error. " << msg << "\n";
#else
// ns doesn't use streams
fprintf(stderr, "Fatal intVec error. %s\n", msg);
#endif
exit(1);
}
one_arg_error_handler_t intVec_error_handler = default_intVec_error_handler;
one_arg_error_handler_t set_intVec_error_handler(one_arg_error_handler_t f)
{
one_arg_error_handler_t old = intVec_error_handler;
intVec_error_handler = f;
return old;
}
void intVec::error(const char* msg)
{
(*intVec_error_handler)(msg);
}
void intVec::range_error()
{
(*intVec_error_handler)("Index out of range.");
}
intVec::intVec(const intVec& v)
{
s = new int [len = v.len];
int* top = &(s[len]);
int* t = s;
const int* u = v.s;
while (t < top) *t++ = *u++;
}
intVec::intVec(int l, int fill_value)
{
s = new int [len = l];
int* top = &(s[len]);
int* t = s;
while (t < top) *t++ = fill_value;
}
intVec& intVec::operator = (const intVec& v)
{
if (this != &v)
{
delete [] s;
s = new int [len = v.len];
int* top = &(s[len]);
int* t = s;
const int* u = v.s;
while (t < top) *t++ = *u++;
}
return *this;
}
void intVec::apply(intProcedure f)
{
int* top = &(s[len]);
int* t = s;
while (t < top) (*f)(*t++);
}
// can't just realloc since there may be need for constructors/destructors
void intVec::resize(int newl)
{
int* news = new int [newl];
int* p = news;
int minl = (len < newl)? len : newl;
int* top = &(s[minl]);
int* t = s;
while (t < top) *p++ = *t++;
delete [] s;
s = news;
len = newl;
}
intVec concat(intVec & a, intVec & b)
{
int newl = a.len + b.len;
int* news = new int [newl];
int* p = news;
int* top = &(a.s[a.len]);
int* t = a.s;
while (t < top) *p++ = *t++;
top = &(b.s[b.len]);
t = b.s;
while (t < top) *p++ = *t++;
return intVec(newl, news);
}
intVec combine(intCombiner f, intVec& a, intVec& b)
{
int newl = (a.len < b.len)? a.len : b.len;
int* news = new int [newl];
int* p = news;
int* top = &(a.s[newl]);
int* t = a.s;
int* u = b.s;
while (t < top) *p++ = (*f)(*t++, *u++);
return intVec(newl, news);
}
int intVec::reduce(intCombiner f, int base)
{
int r = base;
int* top = &(s[len]);
int* t = s;
while (t < top) r = (*f)(r, *t++);
return r;
}
intVec reverse(intVec& a)
{
int* news = new int [a.len];
if (a.len != 0)
{
int* lo = news;
int* hi = &(news[a.len - 1]);
while (lo < hi)
{
int tmp = *lo;
*lo++ = *hi;
*hi-- = tmp;
}
}
return intVec(a.len, news);
}
void intVec::reverse()
{
if (len != 0)
{
int* lo = s;
int* hi = &(s[len - 1]);
while (lo < hi)
{
int tmp = *lo;
*lo++ = *hi;
*hi-- = tmp;
}
}
}
int intVec::index(int targ)
{
for (int i = 0; i < len; ++i) if (intEQ(targ, s[i])) return i;
return -1;
}
intVec map(intMapper f, intVec& a)
{
int* news = new int [a.len];
int* p = news;
int* top = &(a.s[a.len]);
int* t = a.s;
while(t < top) *p++ = (*f)(*t++);
return intVec(a.len, news);
}
int operator == (intVec& a, intVec& b)
{
if (a.len != b.len)
return 0;
int* top = &(a.s[a.len]);
int* t = a.s;
int* u = b.s;
while (t < top) if (!(intEQ(*t++, *u++))) return 0;
return 1;
}
void intVec::fill(int val, int from, int n)
{
int to;
if (n < 0)
to = len - 1;
else
to = from + n - 1;
if ((unsigned)from > (unsigned)to)
range_error();
int* t = &(s[from]);
int* top = &(s[to]);
while (t <= top) *t++ = val;
}
intVec intVec::at(int from, int n)
{
int to;
if (n < 0)
{
n = len - from;
to = len - 1;
}
else
to = from + n - 1;
if ((unsigned)from > (unsigned)to)
range_error();
int* news = new int [n];
int* p = news;
int* t = &(s[from]);
int* top = &(s[to]);
while (t <= top) *p++ = *t++;
return intVec(n, news);
}
intVec merge(intVec & a, intVec & b, intComparator f)
{
int newl = a.len + b.len;
int* news = new int [newl];
int* p = news;
int* topa = &(a.s[a.len]);
int* as = a.s;
int* topb = &(b.s[b.len]);
int* bs = b.s;
for (;;)
{
if (as >= topa)
{
while (bs < topb) *p++ = *bs++;
break;
}
else if (bs >= topb)
{
while (as < topa) *p++ = *as++;
break;
}
else if ((*f)(*as, *bs) <= 0)
*p++ = *as++;
else
*p++ = *bs++;
}
return intVec(newl, news);
}
static int gsort(int*, int, intComparator);
void intVec::sort (intComparator compar)
{
gsort(s, len, compar);
}
// An adaptation of Schmidt's new quicksort
static inline void SWAP(int* A, int* B)
{
int tmp = *A; *A = *B; *B = tmp;
}
/* This should be replaced by a standard ANSI macro. */
#define BYTES_PER_WORD 8
#define BYTES_PER_LONG 4
/* The next 4 #defines implement a very fast in-line stack abstraction. */
#define STACK_SIZE (BYTES_PER_WORD * BYTES_PER_LONG)
#define PUSH(LOW,HIGH) do {top->lo = LOW;top++->hi = HIGH;} while (0)
#define POP(LOW,HIGH) do {LOW = (--top)->lo;HIGH = top->hi;} while (0)
#define STACK_NOT_EMPTY (stack < top)
/* Discontinue quicksort algorithm when partition gets below this size.
This particular magic number was chosen to work best on a Sun 4/260. */
#define MAX_THRESH 4
/* Order size using quicksort. This implementation incorporates
four optimizations discussed in Sedgewick:
1. Non-recursive, using an explicit stack of pointer that
store the next array partition to sort. To save time, this
maximum amount of space required to store an array of
MAX_INT is allocated on the stack. Assuming a 32-bit integer,
this needs only 32 * sizeof (stack_node) == 136 bits. Pretty
cheap, actually.
2. Chose the pivot element using a median-of-three decision tree.
This reduces the probability of selecting a bad pivot value and
eliminates certain extraneous comparisons.
3. Only quicksorts TOTAL_ELEMS / MAX_THRESH partitions, leaving
insertion sort to order the MAX_THRESH items within each partition.
This is a big win, since insertion sort is faster for small, mostly
sorted array segements.
4. The larger of the two sub-partitions is always pushed onto the
stack first, with the algorithm then concentrating on the
smaller partition. This *guarantees* no more than log (n)
stack size is needed! */
static int gsort (int *base_ptr, int total_elems, intComparator cmp)
{
/* Stack node declarations used to store unfulfilled partition obligations. */
struct stack_node { int *lo; int *hi; };
int pivot_buffer;
int max_thresh = MAX_THRESH;
if (total_elems > MAX_THRESH)
{
int *lo = base_ptr;
int *hi = lo + (total_elems - 1);
int *left_ptr;
int *right_ptr;
stack_node stack[STACK_SIZE]; /* Largest size needed for 32-bit int!!! */
stack_node *top = stack + 1;
while (STACK_NOT_EMPTY)
{
{
int *pivot = &pivot_buffer;
{
/* Select median value from among LO, MID, and HI. Rearrange
LO and HI so the three values are sorted. This lowers the
probability of picking a pathological pivot value and
skips a comparison for both the LEFT_PTR and RIGHT_PTR. */
int *mid = lo + ((hi - lo) >> 1);
if ((*cmp) (*mid, *lo) < 0)
SWAP (mid, lo);
if ((*cmp) (*hi, *mid) < 0)
{
SWAP (mid, hi);
if ((*cmp) (*mid, *lo) < 0)
SWAP (mid, lo);
}
*pivot = *mid;
pivot = &pivot_buffer;
}
left_ptr = lo + 1;
right_ptr = hi - 1;
/* Here's the famous ``collapse the walls'' section of quicksort.
Gotta like those tight inner loops! They are the main reason
that this algorithm runs much faster than others. */
do
{
while ((*cmp) (*left_ptr, *pivot) < 0)
left_ptr += 1;
while ((*cmp) (*pivot, *right_ptr) < 0)
right_ptr -= 1;
if (left_ptr < right_ptr)
{
SWAP (left_ptr, right_ptr);
left_ptr += 1;
right_ptr -= 1;
}
else if (left_ptr == right_ptr)
{
left_ptr += 1;
right_ptr -= 1;
break;
}
}
while (left_ptr <= right_ptr);
}
/* Set up pointers for next iteration. First determine whether
left and right partitions are below the threshold size. If so,
ignore one or both. Otherwise, push the larger partition's
bounds on the stack and continue sorting the smaller one. */
if ((right_ptr - lo) <= max_thresh)
{
if ((hi - left_ptr) <= max_thresh) /* Ignore both small partitions. */
POP (lo, hi);
else /* Ignore small left partition. */
lo = left_ptr;
}
else if ((hi - left_ptr) <= max_thresh) /* Ignore small right partition. */
hi = right_ptr;
else if ((right_ptr - lo) > (hi - left_ptr)) /* Push larger left partition indices. */
{
PUSH (lo, right_ptr);
lo = left_ptr;
}
else /* Push larger right partition indices. */
{
PUSH (left_ptr, hi);
hi = right_ptr;
}
}
}
/* Once the BASE_PTR array is partially sorted by quicksort the rest
is completely sorted using insertion sort, since this is efficient
for partitions below MAX_THRESH size. BASE_PTR points to the beginning
of the array to sort, and END_PTR points at the very last element in
the array (*not* one beyond it!). */
{
int *end_ptr = base_ptr + 1 * (total_elems - 1);
int *run_ptr;
int *tmp_ptr = base_ptr;
int *thresh = (end_ptr < (base_ptr + max_thresh))?
end_ptr : (base_ptr + max_thresh);
/* Find smallest element in first threshold and place it at the
array's beginning. This is the smallest array element,
and the operation speeds up insertion sort's inner loop. */
for (run_ptr = tmp_ptr + 1; run_ptr <= thresh; run_ptr += 1)
if ((*cmp) (*run_ptr, *tmp_ptr) < 0)
tmp_ptr = run_ptr;
if (tmp_ptr != base_ptr)
SWAP (tmp_ptr, base_ptr);
/* Insertion sort, running from left-hand-side up to `right-hand-side.'
Pretty much straight out of the original GNU qsort routine. */
for (run_ptr = base_ptr + 1; (tmp_ptr = run_ptr += 1) <= end_ptr; )
{
while ((*cmp) (*run_ptr, *(tmp_ptr -= 1)) < 0)
;
if ((tmp_ptr += 1) != run_ptr)
{
int *trav;
for (trav = run_ptr + 1; --trav >= run_ptr;)
{
int c = *trav;
int *hi, *lo;
for (hi = lo = trav; (lo -= 1) >= tmp_ptr; hi = lo)
*hi = *lo;
*hi = c;
}
}
}
}
return 1;
}
media-app.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
//
// Copyright (c) 1997 by the University of Southern California
// All rights reserved.
//
// Permission to use, copy, modify, and distribute this software and its
// documentation in source and binary forms for non-commercial purposes
// and without fee is hereby granted, provided that the above copyright
// notice appear in all copies and that both the copyright notice and
// this permission notice appear in supporting documentation. and that
// any documentation, advertising materials, and other materials related
// to such distribution and use acknowledge that the software was
// developed by the University of Southern California, Information
// Sciences Institute. The name of the University may not be used to
// endorse or promote products derived from this software without
// specific prior written permission.
//
// THE UNIVERSITY OF SOUTHERN CALIFORNIA makes no representations about
// the suitability of this software for any purpose. THIS SOFTWARE IS
// PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
// INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
//
// Other copyrights might apply to parts of this software and are so
// noted when applicable.
//
// Implementation of media application
//
// $Header$
#include
#include "template.h"
#include "media-app.h"
#include "utilities.h"
//----------------------------------------------------------------------
// Classes related to a multimedia object
//
// MediaSegment
// MediaSegmentList: segments in a layer
// MediaPage: a stored multimedia object (stream)
//----------------------------------------------------------------------
MediaSegment::MediaSegment(const HttpMediaData& d) : flags_(0)
{
start_ = d.st();
end_ = d.et();
if (d.is_last())
set_last();
if (d.is_pref())
set_pref();
}
void MediaSegmentList::add(const MediaSegment& s)
{
MediaSegment* tmp = (MediaSegment *)head_;
while ((tmp != NULL) && (tmp->before(s))) {
tmp = tmp->next();
}
// Append at the tail, or the first element in list
if (tmp == NULL) {
length_ += s.datasize();
if ((tail_ != NULL) && ((MediaSegment *)tail_)->overlap(s))
// Don't need to merge because it's merged at the end
((MediaSegment*)tail_)->merge(s);
else {
MediaSegment *p = new MediaSegment(s);
if (head_ == NULL)
head_ = tail_ = p;
else
append(p, tail_);
}
if (getsize() != length_) {
fprintf(stderr,
"MediaSegmentList corrupted: Point 1.\n");
abort();
}
return;
}
// Update total stored length ONLY IF s is not in tmp.
if (tmp->in(s)) {
fprintf(stderr,
"MediaSegmentList: get a seg (%d %d) which is already in cache!\n",
s.start(), s.end());
fprintf(stderr, "List contents: ");
print();
#if 1
//Tcl::instance().eval("[Test instance] flush-trace");
//abort();
#endif
// XXX Don't abort, simply continue
return;
}
// Insert a MediaSegment into list. Note: Don't do merge!
if (tmp->overlap(s)) {
length_ += (s.datasize() - tmp->merge(s));
} else {
MediaSegment *p = new MediaSegment(s);
insert(p, tmp);
tmp = p;
length_ += s.datasize();
}
if (getsize() != length_) {
fprintf(stderr, "MediaSegmentList corrupted: Point 2.\n");
abort();
}
merge_seg(tmp);
if (getsize() != length_) {
fprintf(stderr, "MediaSegmentList corrupted: Point 3.\n");
abort();
}
}
void MediaSegmentList::merge_seg(MediaSegment* tmp)
{
// See if can be merged with next segments
MediaSegment *q = tmp->next();
while (q && q->overlap(*tmp)) {
#if 1
if ((tmp->start() == q->start()) && (tmp->end() == q->end())) {
abort();
}
#endif
tmp->merge(*q);
detach(q);
delete q;
q = tmp->next();
}
// See if can be merged with previous segments
q = tmp->prev();
while (q && q->overlap(*tmp)) {
tmp->merge(*q);
assert(tail_ != q);
detach(q);
delete q;
q = tmp->prev();
}
}
int MediaSegmentList::in(const MediaSegment& s)
{
MediaSegment* tmp = (MediaSegment *)head_;
while ((tmp != NULL) && (tmp->before(s)))
tmp = tmp->next();
// If all segments are before s, or the first segment which isn't
// before s doesn't overlap with s, s isn't in this list.
if ((tmp == NULL) || !s.in(*tmp))
return 0;
else
return 1;
}
// Get the next segment which is not before 's', but with the same size
// as the given 's'. This segment may not overlap with s.
MediaSegment MediaSegmentList::get_nextseg(const MediaSegment& s)
{
MediaSegment res(0, 0); // If unsuccessful, return start() = 0
MediaSegment* tmp = (MediaSegment *)head_;
while ((tmp != NULL) && (tmp->before(s)))
tmp = tmp->next();
if (tmp == NULL) {
res.set_last();
return res;
}
assert(tmp->end() > s.start());
// // Don't return a segment which do not *OVERLAP* with s
// // (boundary overlap is excluded).
// if ((tmp->end() <= s.start()) || (tmp->start() >= s.end()))
// return res;
// XXX How to flag that no more data is available in the future??
res = s;
int orig_size = s.datasize();
if (res.start() < tmp->start()) {
// |-------| (s) ---> time axis
// |--------| (tmp)
//
// The start time of s is invalid, we need to adjust both
// the start time (and size if necessary)
res.set_start(tmp->start());
if (tmp->datasize() < orig_size)
// Not enough data available??
res.set_datasize(tmp->datasize());
else
res.set_datasize(orig_size);
} else if (res.end() > tmp->end()) {
// |---------| (s) ---> time axis
// |-------| (tmp)
//
// The start time in s is valid, but we may need to adjust the
// end time (i.e., size) of s.
res.set_datasize(tmp->end()-res.start());
}
// Falling through means that the requested segment is available
// and can be returned as it is.
assert(res.datasize() <= tmp->datasize());
if ((res.end() == tmp->end()) && (tmp->next() == NULL))
// This is the last data segment of the layer
res.set_last();
return res;
}
// Note that evicting all segments in this layer may not leave enough
// space, so we return the number of bytes evicted from this layer
int MediaSegmentList::evict_tail(int size)
{
int sz = size, tz;
MediaSegment *tmp = (MediaSegment *)tail_;
while ((tmp != NULL) && (sz > 0)) {
// Reduce the last segment's size and adjust its playout time
tz = tmp->evict_tail(sz);
length_ -= tz;
sz -= tz;
if (tmp->datasize() == 0) {
// This segment is empty now
detach(tmp);
delete tmp;
tmp = (MediaSegment *)tail_;
}
}
return size - sz;
}
// Evicting from the head of the list
int MediaSegmentList::evict_head(int size)
{
int sz = size, tz;
MediaSegment *tmp = (MediaSegment *)head_;
while ((tmp != NULL) && (sz > 0)) {
// Reduce the last segment's size and adjust its playout time
tz = tmp->evict_head(sz);
sz -= tz;
length_ -= tz;
if (tmp->datasize() == 0) {
// This segment is empty now
detach(tmp);
delete tmp;
tmp = (MediaSegment *)head_;
}
}
return size - sz;
}
// Evict all segments before from head and returns the size of
// evicted segments.
int MediaSegmentList::evict_head_offset(int offset)
{
int sz = 0;
MediaSegment *tmp = (MediaSegment *)head_;
while ((tmp != NULL) && (tmp->start() < offset)) {
if (tmp->end() <= offset) {
// delete whole segment
sz += tmp->datasize();
length_ -= tmp->datasize();
detach(tmp);
delete tmp;
tmp = (MediaSegment *)head_;
} else {
// remove part of the segment
sz += offset - tmp->start();
length_ -= offset - tmp->start();
tmp->set_start(offset);
}
}
if (head_ == NULL)
tail_ = NULL;
return sz;
}
// Return a list of "holes" between the given offsets
MediaSegmentList MediaSegmentList::check_holes(const MediaSegment& s)
{
MediaSegmentList res; // empty list
MediaSegment* tmp = (MediaSegment *)head_;
while ((tmp != NULL) && (tmp->before(s)))
tmp = tmp->next();
// If all segments are before s, s is a hole
if (tmp == NULL) {
res.add(s);
return res;
}
// If s is within *tmp, there is no hole
if (s.in(*tmp))
return res;
// Otherwise return a list of holes
int soff, eoff;
soff = s.start();
eoff = s.end();
while ((tmp != NULL) && (tmp->overlap(s))) {
if (soff < tmp->start()) {
// Only refetches the missing part
res.add(MediaSegment(soff, min(eoff, tmp->start())));
#if 1
// DEBUG ONLY
// Check if these holes are really holes!
if (in(MediaSegment(soff, min(eoff, tmp->start())))) {
fprintf(stderr, "Wrong hole: (%d %d) ",
soff, min(eoff, tmp->start()));
fprintf(stderr, "tmp(%d %d), s(%d %d)\n",
tmp->start(), tmp->end(),
soff, eoff);
fprintf(stderr, "List content: ");
print();
}
#endif
}
soff = tmp->end();
tmp = tmp->next();
}
if (soff < eoff) {
res.add(MediaSegment(soff, eoff));
#if 1
// DEBUG ONLY
// Check if these holes are really holes!
if (in(MediaSegment(soff, eoff))) {
fprintf(stderr, "Wrong hole #2: (%d %d)\n",
soff, eoff);
fprintf(stderr, "List content: ");
print();
}
#endif
}
#if 0
check_integrity();
#endif
return res;
}
void MediaSegmentList::check_integrity()
{
MediaSegment *p, *q;
p = (MediaSegment*)head_;
while (p != NULL) {
q = p;
p = p->next();
if (p == NULL)
break;
if (!q->before(*p)) {
fprintf(stderr,
"Invalid segment added: (%d %d), (%d %d)\n",
q->start(), q->end(), p->start(), p->end());
abort();
}
}
}
// Return the portion in s that is overlap with any segments in this list
// Sort of complementary to check_holes(), but it does not return a list,
// hence smaller overhead.
int MediaSegmentList::overlap_size(const MediaSegment& s) const
{
int res = 0;
MediaSegment* tmp = (MediaSegment *)head_;
while ((tmp != NULL) && (tmp->before(s)))
tmp = tmp->next();
// If all segments are before s, there's no overlap
if (tmp == NULL)
return 0;
// If s is within *tmp, entire s overlaps with the list
if (s.in(*tmp))
return s.datasize();
// Otherwise adds all overlapping parts together.
int soff, eoff;
soff = s.start();
eoff = s.end();
while ((tmp != NULL) && (tmp->overlap(s))) {
res += min(eoff, tmp->end()) - max(soff, tmp->start());
soff = tmp->end();
tmp = tmp->next();
}
return res;
}
// Debug only
void MediaSegmentList::print()
{
MediaSegment *p = (MediaSegment *)head_;
int i = 0, sz = 0;
while (p != NULL) {
printf("(%d, %d) ", p->start(), p->end());
sz += p->datasize();
p = p->next();
if (++i % 8 == 0)
printf("\n");
}
printf("\nTotal = %d\n", sz);
}
// Debug only
int MediaSegmentList::getsize()
{
MediaSegment *p = (MediaSegment *)head_;
int sz = 0;
while (p != NULL) {
sz += p->datasize();
p = p->next();
}
return sz;
}
// Print into a char array with a given size. Abort if the size is exceeded.
char* MediaSegmentList::dump2buf()
{
char *buf = new char[1024];
char *b = buf;
MediaSegment *p = (MediaSegment *)head_;
int i = 0, sz = 1024;
buf[0] = 0;
while (p != NULL) {
// XXX snprintf() should either be in libc or implemented
// by TclCL (see Tcl2.cc there).
i = snprintf(b, sz, "{%d %d} ", p->start(), p->end());
sz -= i;
// Boundary check: if less than 50 bytes, allocate new buf
if (sz < 50) {
char *tmp = new char[strlen(buf)+1024];
strcpy(tmp, buf);
delete []buf;
buf = tmp;
b = buf + strlen(buf);
sz += 1024;
} else
b += i;
p = p->next();
}
return buf;
}
HttpMediaData::HttpMediaData(const char* sender, const char* page, int layer,
int st, int et) :
HttpData(MEDIA_DATA, 0), layer_(layer), st_(st), et_(et), flags_(0)
{
assert(strlen(page)+1 <= (size_t)HTTP_MAXURLLEN);
strcpy(page_, page);
assert(strlen(sender)+1 <= (size_t)HTTP_MAXURLLEN);
strcpy(sender_, sender);
}
static class MappClass : public TclClass {
public:
MappClass() : TclClass("Application/MediaApp") {}
TclObject* create(int argc, const char*const* argv) {
if (argc > 4)
return (new MediaApp(argv[4]));
return NULL;
}
} class_mapp;
MediaApp::MediaApp(const char* page) :
log_(0), num_layer_(0), last_layer_(0)
{
strcpy(page_, page);
// Initialize all layer data pointers
for (int i = 0; i < MAX_LAYER; i++)
data_[i].set_start(0);
bind("segmentSize_", &seg_size_);
}
void MediaApp::start()
{
fprintf(stderr, "MediaApp::start() not supported\n");
abort();
}
void MediaApp::stop()
{
// Called when we want to stop the RAP agent
rap()->stop();
}
AppData* MediaApp::get_data(int& nbytes, AppData* req)
{
AppData *res;
if (req == NULL) {
MediaRequest p(MEDIAREQ_GETSEG);
p.set_name(page_);
// We simply rotating the layers from which to send data
if (num_layer_ > 0) {
p.set_layer(last_layer_++);
last_layer_ = last_layer_ % num_layer_;
} else
p.set_layer(0);
p.set_st(data_[0].start());
p.set_datasize(seg_size_);
p.set_app(this);
res = target()->get_data(nbytes, &p);
} else
res = target()->get_data(nbytes, req);
// Update the current data pointer
assert(res != NULL);
HttpMediaData *p = (HttpMediaData *)res;
// XXX For now, if the return size is 0, we assume that the
// transmission stops. Otherwise there is no way to tell the
// RAP agent that there's no more data to send
if (p->datasize() <= 0) {
// Should NOT advance sending data pointer because
// if this is a cache which is downloading from a slow
// link, it is possible that the requested data will
// become available in the near future!!
delete p;
return NULL;
} else {
// Set current data pointer to the right ones
// If available data is more than seg_size_, only advance data
// pointer by seg_size_. If less data is available, only
// advance data by the amount of available data.
//
// XXX Currently the cache above does NOT pack data from
// discontinugous blocks into one packet. May need to do
// that later.
assert((p->datasize() > 0) && (p->datasize() <= seg_size_));
data_[p->layer()].set_start(p->et());
data_[p->layer()].set_datasize(seg_size_);
}
return res;
}
int MediaApp::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
if (strcmp(argv[1], "log") == 0) {
int mode;
log_ = Tcl_GetChannel(tcl.interp(),
(char*)argv[2], &mode);
if (log_ == 0) {
tcl.resultf("%s: invalid log file handle %s\n",
name(), argv[2]);
return TCL_ERROR;
}
return TCL_OK;
} else if (strcmp(argv[1], "evTrace") == 0) {
char buf[1024], *p;
if (log_ != 0) {
sprintf(buf, "%.17g ",
Scheduler::instance().clock());
p = &(buf[strlen(buf)]);
for (int i = 2; i < argc; i++) {
strcpy(p, argv[i]);
p += strlen(argv[i]);
*(p++) = ' ';
}
// Stick in a newline.
*(p++) = '\n', *p = 0;
Tcl_Write(log_, buf, p-buf);
}
return TCL_OK;
} else if (strcmp(argv[1], "set-layer") == 0) {
int n = atoi(argv[2]);
if (n >= MAX_LAYER) {
fprintf(stderr,
"Too many layers than maximum allowed.\n");
return TCL_ERROR;
}
num_layer_ = n;
return TCL_OK;
}
return Application::command(argc, argv);
}
void MediaApp::log(const char* fmt, ...)
{
char buf[1024], *p;
char *src = Address::instance().print_nodeaddr(rap()->addr());
sprintf(buf, "%.17g i %s ", Scheduler::instance().clock(), src);
delete []src;
p = &(buf[strlen(buf)]);
va_list ap;
va_start(ap, fmt);
vsprintf(p, fmt, ap);
if (log_ != 0)
Tcl_Write(log_, buf, strlen(buf));
}
//----------------------------------------------------------------------
// MediaApp enhanced with quality adaptation
//----------------------------------------------------------------------
void QATimer::expire(Event *)
{
a_->UpdateState();
resched(a_->UpdateInterval());
}
static class QAClass : public TclClass {
public:
QAClass() : TclClass("Application/MediaApp/QA") {}
TclObject* create(int argc, const char*const* argv) {
if (argc > 4)
return (new QA((const char *)(argv[4])));
return NULL;
}
} class_qa_app;
//#define CHECK 1
//#define DBG 1
QA::QA(const char *page) : MediaApp(page)
{
updTimer_ = new QATimer(this);
bind("LAYERBW_", &LAYERBW_);
bind("MAXACTIVELAYERS_", &MAXACTIVELAYERS_);
bind("SRTTWEIGHT_", &SRTTWEIGHT_);
bind("SMOOTHFACTOR_", &SMOOTHFACTOR_);
bind("MAXBKOFF_", &MAXBKOFF_);
bind("debug_output_", &debug_);
bind("pref_srtt_", &pref_srtt_);
for(int j = 0; j < MAX_LAYER; j++) {
buffer_[j] = 0.0;
sending_[j] = 0;
playing_[j] = 0;
drained_[j] = 0.0;
bw_[j] = 0.0;
pref_[j] = 0;
}
poffset_ = 0;
playTime_ = 0; // Should initialize it
startTime_ = -1; // Used to tell the first packet
// Moving average weight for transmission rate average
rate_weight_ = 0.01;
avgrate_ = 0.0;
}
QA::~QA()
{
if (updTimer_) {
if (updTimer_->status() != TIMER_IDLE)
updTimer_->cancel();
delete updTimer_;
}
}
void QA::debug(const char* fmt, ...)
{
if (!debug_)
return;
char buf[1024], *p;
char *src = Address::instance().print_nodeaddr(rap()->addr());
char *port = Address::instance().print_portaddr(rap()->addr());
sprintf(buf, "# t %.17g i %s.%s QA ",
Scheduler::instance().clock(), src, port);
delete []port;
delete []src;
p = &(buf[strlen(buf)]);
va_list ap;
va_start(ap, fmt);
vsprintf(p, fmt, ap);
fprintf(stderr, "%s", buf);
}
void QA::panic(const char* fmt, ...)
{
char buf[1024], *p;
char *src = Address::instance().print_nodeaddr(rap()->addr());
char *port = Address::instance().print_portaddr(rap()->addr());
sprintf(buf, "# t %.17g i %s.%s QA PANIC ",
Scheduler::instance().clock(), src, port);
delete []port;
delete []src;
p = &(buf[strlen(buf)]);
va_list ap;
va_start(ap, fmt);
vsprintf(p, fmt, ap);
fprintf(stderr, "%s", buf);
// XXX This is specific to OUR test. Remove it in release!!
Tcl::instance().eval("[Test instance] flush-trace");
abort();
}
// Stop all timers
void QA::stop()
{
rap()->stop();
if (updTimer_->status() != TIMER_IDLE)
updTimer_->cancel();
}
// Empty for now
int QA::command(int argc, const char*const* argv)
{
return MediaApp::command(argc, argv);
}
// When called by RAP, req is NULL. We fill in the next data segment and
// return its real size in 'size' and return the app data.
AppData* QA::get_data(int& size, AppData*)
{
int layers, dropped, i, l, idx, bs1, bs2,scenario, done, cnt;
double slope, bufavail, bufneeded, totbufs1, totbufs2,
optbufs1[MAX_LAYER], optbufs2[MAX_LAYER], bufToDrain;
static double last_rate = 0.0, last_depart, nextAdjPoint = -1,
FinalDrainArray[MAX_LAYER],
tosend[MAX_LAYER], FinalBuffer[MAX_LAYER];
static int flag, /* flag keeps the state of the last phase */
tosendPtr = 0;
// Get RAP info
double rate = seg_size_ / rap()->ipg();
double srtt = rap()->srtt();
Scheduler& s = Scheduler::instance();
double now = s.clock();
int anyAck = rap()->anyack();
assert((num_layer_ > 0) && (num_layer_ < MAX_LAYER));
// this part is added for the startup
// to send data for the base layer until the first ACK arrives.
// This is because we don't have an estimate for SRTT and slope of inc
// Make sure that SRTT is updated properly when ACK arrives
if (anyAck == 0) {
sending_[0] = 1;
return output(size, 0);
debug("INIT Phase, send packet: layer 0 in send_pkt, \
rate: %.3f, avgrate: %.3f, srtt:%.3f\n", rate, avgrate_, srtt);
}
layers = 0;
// we can only calc slope when srttt has a right value
// i.e. RAP has received an ACK
slope = seg_size_/srtt;
bufavail = 0.0;
// XXX Is this a correct initial value????
bufneeded = 0.0;
// calculate layers & bufavail
for (i = 0; i < MAX_LAYER; i++) {
layers += sending_[i];
if (sending_[i] == 1)
bufavail += buffer_[i];
else
/* debug only */
if ((i < MAX_LAYER - 1) && (sending_[i+1] == 1))
panic("ERROR L%d is not sent but L%d is.\n",
i, i+1);
}
// check for startup phase
if((layers == 1) && (playing_[0] != 1)){
// L0 still buffers data, we are in startup phase
// let's check
if (sending_[0] == 0) {
panic("ERROR sending[0]=0 !!!");
}
AppData *res = output(size, 0);
debug("STARTUP, send packet: layer 0\n");
// Start playout if we have enough data for L0
// The amount of buffered data for startup can be diff
bufneeded = max(4*BufNeed((LAYERBW_-rate/2.0), slope),
2*MWM(srtt));
if (buffer_[0] >= bufneeded) {
playing_[0] = 1;
sending_[0] = 1;
drained_[0] = 0; /* srtt*LAYERBW; */
startTime_ = now; // start the playback at the client
playTime_ = now; // playout time of the receiver.
debug("... START Playing_ layer 0, buffer[0] = %f!\n",
buffer_[0]);
// start emulating clients consumption
if (updTimer_->status() == TIMER_IDLE)
updTimer_->sched(srtt);
}
return(res);
}
// Store enough buffer before playing a layer.
// XXX, NOTE: it is hard to do this, when we add a new layer
// the server sets the playout time of the first segment
// to get to the client in time, It is hard to make sure
// that a layer has MRM worth if data before stasting its
// playback because it adds more delay
// the base layer starts when it has enough buffering
// the higher layers are played out when their data is available
// so this is not needed
//for (i = 0; i < MAX_LAYER; i++) {
// if ((sending_[i] == 1) && (playing_[i] == 0) &&
// (buffer_[i] > MWM(srtt))) {
// debug("Resume PLAYING Layer %d, play: %d send: %d\n",
// i, playing_[i], sending_[i]);
// playing_[i]=1;
// drained_[i] = 0; /* XXX, not sure about this yet
// * but if we set this to max it causes
// * a spike at the adding time
// */
// /* drained_[i]=LAYERBW*SRTT; */
//}
//}
// perform the primary drop if we are in drain phase
if (rate < layers*LAYERBW_) {
bufneeded = (MWM(srtt)*layers) +
BufNeed((layers*LAYERBW_-rate), slope);
// debug("tot_bufavail: %7.1f bufneeded: %7.1f, layers: %d",
// bufavail, bufneeded, layers);
dropped = 0;
// XXX Never ever do primary drop layer 0!!!!
while ((bufneeded > TotalBuf(layers, buffer_)) &&
(layers > 1)) {
debug("** Primary DROPPED L%d, TotBuf(avail:%.1f \
needed:%.1f), buf[%d]: %.2f\n",
layers-1, TotalBuf(layers, buffer_), bufneeded,
layers-1,buffer_[layers-1]);
layers--;
dropped++;
sending_[layers] = 0;
bufneeded = (MWM(srtt)*layers)+
BufNeed(((layers)*LAYERBW_-rate),slope);
}
}
// just for debugging
// here is the case when even the base layer can not be kept
if ((bufneeded > TotalBuf(layers, buffer_)) && (layers == 1)) {
// XXX We should still continue, shouldn't we????
debug("** Not enough buf to keep the base layer, \
TotBuf(avail:%.1f, needed:%.1f), \n",
TotalBuf(layers, buffer_), bufneeded);
}
if (layers == 0) {
// panic("** layers =0 !!");
sending_[0] = 1;
playing_[0] = 0;
if (updTimer_->status() != TIMER_IDLE)
updTimer_->cancel();
debug("** RESTART Phase, set playing_[0] to 0 to rebuffer data\n");
return output(size, 0);
}
// now check to see which phase we are in
if (rate >= layers*LAYERBW_) {
/******************
** filling phase **
*******************/
/*
debug("-->> FILLING, layers: %d now: %.2f, rate: %.3f, avgrate: %.3f, \
srtt:%.3f, slope: %.3f\n",
layers, now, rate, avgrate_, srtt, slope);
*/
last_rate = rate; /* this is used for the next drain phase */
flag = 1;
/*
* 1) send for any layer that its buffer is below MWM
* MWM is the min amount of buffering required to absorbe
* jitter
* each active layer must have atleast MWM data at all time
* this also ensures proper bw share, we do NOT explicitly
* alloc BW share during filling
* Note: since we update state of the buffers on a per-packet
* basis, we don't need to ensure that each layer gets a share
* of bandwidth equal to its consumption rate.
*/
for (i=0;i 0))
idx = i-1;
else
idx = i;
// debug("A:sending layer %d, less than MWM, t: %.2f\n",
// i,now);
return output(size, idx);
}
}
/*
* Main filling algorithm based on the pesudo code
* find the next optimal state to reach
*/
/* init param */
bs1 = 0;
bs2 = 0;
totbufs1 = 0;
totbufs2 = 0;
for (l=0; l= MAXBKOFF_)
debug("WARNING: MAX No of backoff Reached, bs1: %d, \
bs2: %d\n", bs1, bs2);
/* Check for adding condition */
//if ((bs1 > SMOOTHFACTOR_) && (bs2 > SMOOTHFACTOR_) &&
// (layers < MAX_LAYER)) {
if ((bs1 > SMOOTHFACTOR_) && (bs2 > SMOOTHFACTOR_)){
// Check if all layers are already playing
// Assume all streams have the same # of layer:
// MAX_LAYER
assert(layers <= num_layer_);
// XXX Only limit the rate when we have all layers
// playing. There should be a better way to limit the
// transmission rate earlier! Note that we need RAP to
// fix its IPG as soon as we fix the rate here. Thus,
// RAP should do that in its IpgTimeout()
// instead of DecreaseIpg(). See rap.cc.
if (layers == num_layer_){
#if 0
if (rate < num_layer_*LAYERBW_)
panic("ERROR: rate: %.2f is less than \
MAX BW for all %d layers!\n", rate, layers);
#endif
// Ask RAP to fix the rate at MAX_LAYER*LAYERBW
rap()->FixIpg((double)seg_size_/
(double)(num_layer_*LAYERBW_));
// Mux the bandwidth evenly among layers
return output(size, layers - 1);
}
// Calculate the first packet offset in this new layer
int off_start = (int)floor((poffset_ + MWM(srtt)) /
seg_size_) * seg_size_;
// XXX Does the application have data between
// off_start_ and off_start_+MWM(srtt)??
// XXX If the computed offset falls behind, we just
// continue to send.
if (data_[layers].start() <= off_start) {
// Set LayerOffset[newlayer] =
// poffset_ + MWM(srtt) * n:
// - n times roundtrip time of data, LET n BE 1
// Round this offset to whole segment
data_[layers].set_start(off_start);
data_[layers].set_datasize(seg_size_);
}
// Make sure that all corresponding data in lower
// layers have been sent out, i.e., the last byte of
// current segment of the new layer should be less
// than the last byte of all lower layers
if (data_[layers].end() > data_[layers-1].start())
// XXX Do not send anything if we don't have
// data!! Otherwise we'll dramatically increase
// the sending rate of lower laters.
return NULL;
// return output(size, layers-1);
sending_[layers] = 1;
AppData *res = output(size, layers);
if (res == NULL) {
// Drop the newly added layer because we
// don't have data
sending_[layers] = 0;
// However, do prefetching in case we'll add
// it again later
int st = (int)floor((data_[layers].start()+
pref_srtt_*LAYERBW_)
/seg_size_+0.5)*seg_size_;
int et = (int)floor((data_[layers].end()+
pref_srtt_*LAYERBW_)
/seg_size_+0.5)*seg_size_;
if (et > pref_[layers]) {
pref_[i] = et;
MediaSegment s(st, et);
check_availability(i, s);
}
for (i = 0; i < layers; i++)
if (buffer_[i] < MWM(srtt)) {
res = output(size, i);
if (res != NULL)
break;
}
} else {
/* LAYERBW_*srtt;should we drain this */
drained_[layers]= 0;
debug("sending Just ADDED layer %d, t: %.2f\n",
i, now);
}
return res;
}
/*
* Find out which next step is closer
* Second cond is for the cases where totbufs2 becomes
* saturated
*/
scenario = 0; // Initial value
if((totbufs1 <= totbufs2) &&
(totbufs1 > TotalBuf(layers, buffer_))) {
/* go for next scenario 1 with sb1 backoff */
scenario = 1;
} else {
/* go for next scenario 2 with sb2 backoffs */
scenario = 2;
}
/* decide which layer needs more data */
if (scenario == 1) {
for (l=0; l= optbufs1[l])
continue;
//if (buffer_[l] < optbufs1[l]) {
if ((buffer_[l-1] <= buffer_[l]+seg_size_) &&
(l > 0))
idx = l-1;
else
idx = l;
// debug("Cs1:sending layer %d to fill buffer, t: %.2f\n",
// idx,now);
return output(size, idx);
}
} else if (scenario == 2) {
l=0;
done = 0;
while ((l= totbufs2) {
done ++;
} else {
if (buffer_[l]0))
idx = l-1;
else
idx = l;
// debug("Cs2:sending layer %d to fill buffer, t: %.2f\n",
// idx,now);
return output(size, idx);
}
l++;
}
} /* while */
} else
panic("# ERROR: Unknown scenario: %d !!\n", scenario);
/* special cases when we get out of this for loop */
if(scenario == 1){
panic("# Should not reach here, totbuf: %.2f, \
totbufs1: %.2f, layers: %d\n",
TotalBuf(layers, buffer_), totbufs1, layers);
}
if (scenario == 2) {
/*
* this is the point where we have satisfied buffer
* requirement for the next scenario 1 already,
* i.e. the MIN() value.
* so we relax that and shoot for bufs2[l]
*/
/*
* if scenario 2, repeat the while loop without min
* cond we have alreddy satisfied the condition for
* the next scenario 1
*/
l=0;
while (l < layers) {
if (buffer_[l] < optbufs2[l]) {
if ((buffer_[l-1] <= buffer_[l]+
seg_size_) && (l>0))
idx = l-1;
else
idx = l;
// debug("Cs22:sending layer %d to fill buffer, t: %.2f\n", idx,now);
return output(size, idx);
}
l++;
}/* while */
}
panic("# Opps, should not reach here, bs1: %d, bs2: %d, \
scen: %d, totbufs1: %.2f, totbufs2: %.2f, totbufavail: %.2f\n",
bs1, bs2, scenario, totbufs1,
totbufs2, TotalBuf(layers, buffer_));
/* NEVER REACH HERE */
} else { /* rate < layers*LAYERBW_ */
/*******************
** Draining phase **
*******************/
/*
debug("-->> DRAINING, layers: %d rate: %.3f, avgrate: %.3f, srtt:%.3f, \
slope: %.3f\n",
layers, rate, avgrate_, srtt, seg_size_/srtt);
*/
/*
* At the beginning of a new drain phase OR
* another drop in rate during a draining phase OR
* dec of slope during a draining phase that results in
* a new drop
*/
/*
* 1) the highest priority action at this point is to ensure
* all surviving layers have min amount of buffering, if not,
* try to fill that layer
*/
double lowest=buffer_[0];
int lowix=0;
for(i=0;ibuffer_[i]) {
lowest=buffer_[i];
lowix=i;
}
}
if (lowest= 0) || /* after a filling phase */
(now >= nextAdjPoint) || /* end of the curr interval */
((rate < last_rate) && (flag < 0)) || /* new backoff */
(AllZero(tosend, layers))) /* all pkt are sent */ {
/* start of a new interval */
/*
* XXX, should update the nextAdjPoint diff for
* diff cases
*/
nextAdjPoint = now + srtt;
bufToDrain = LAYERBW_*layers - rate;
/*
* calculate optimal dist. of bufToDrain across all
* layers. FinalDrainArray[] is the output
* FinalBuffer[] is the final state
*/
if (bufToDrain <= 0)
panic("# ERROR: bufToDrain: %.2f\n",
bufToDrain);
DrainPacket(bufToDrain, FinalDrainArray, layers, rate,
srtt, FinalBuffer);
for(l=0; l 0) {
if ((buffer_[tosendPtr-1] <= buffer_[tosendPtr]
+ seg_size_) && (tosendPtr > 0))
idx = tosendPtr-1;
else
idx = tosendPtr;
tosend[tosendPtr] -= seg_size_;
if (tosend[tosendPtr] < 0)
tosend[tosendPtr] = 0;
return output(size, idx);
}
cnt++;
tosendPtr = (tosendPtr+1) % layers;
}
// XXX End of Drain Phase
// For now, send a chunk from the base layer. Modify it later!!
return output(size, 0);
} /* if (rate >= layers*LAYERBW_) */
panic("# QA::get_data() reached the end. \n");
/*NOTREACHED*/
return NULL;
}
//-----------------------------------------
//-------------- misc routine
//------------------------------------------
// return 1 is all first "len" element of "arr" are zero
// and 0 otherwise
int QA::AllZero(double *arr, int len)
{
int i;
for (i=0; iQA::TotalBuf(int n, double *buffer)
{
double totbuf = 0.0;
int i;
for(i=0; iQA::output(int& size, int layer)
{
int i;
assert((sending_[layer] == 1) || (startTime_ == -1));
// In order to send out a segment, all corresponding segments of
// the lower layers must have been sent out
if (layer > 0)
if (data_[layer-1].start() <= data_[layer].start())
return output(size, layer-1);
// Get and output the data at the current data pointer
MediaRequest q(MEDIAREQ_GETSEG);
q.set_name(page_);
q.set_layer(layer);
q.set_st(data_[layer].start());
q.set_datasize(seg_size_);
q.set_app(this);
AppData* res = target()->get_data(size, &q);
assert(res != NULL);
HttpMediaData *p = (HttpMediaData *)res;
if (p->datasize() <= 0) {
// When the data is not available:
// Should NOT advance sending data pointer because
// if this is a cache which is downloading from a slow
// link, it is possible that the requested data will
// become available in the near future!!
// We have already sent out the last segment of the base layer,
// now we are requested for the segment beyond the last one
// in the base layer. In this case, consider the transmission
// is complete and tear down the connection.
if (p->is_finished()) {
rap()->stop();
// XXX Shouldn't this be done inside mcache/mserver??
Tcl::instance().evalf("%s finish-stream %s",
target()->name(), name());
} else if (!p->is_last()) {
// If we coulnd't find anything within q, move data
// pointer forward to skip holes.
MediaSegment tmp(q.et(), q.et()+seg_size_);
check_layers(p->layer(), tmp);
// If we can, advance. Otherwise wait for
// lower layers to advance first.
if (tmp.datasize() > 0) {
assert(tmp.datasize() <= seg_size_);
data_[p->layer()].set_start(tmp.start());
data_[p->layer()].set_end(tmp.end());
}
}
delete p;
return NULL;
}
// Set current data pointer to the right ones
// If available data is more than seg_size_, only
// advance data pointer by seg_size_. If less data
// is available, only advance data by the amount
// of available data.
//
// XXX Currently the cache above does NOT pack data
// from discontinugous blocks into one packet. May
// need to do that later.
// if (p->is_last())
// data_[p->layer()].set_last();
assert((p->datasize() > 0) && (p->datasize() <= seg_size_));
// XXX Before we move data pointer forward, make sure we don't violate
// layer ordering rules. Note we only need to check end_ because
// start_ is p->et() which is guaranteed to be valid
MediaSegment tmp(p->et(), p->et()+seg_size_);
check_layers(p->layer(), tmp);
if (tmp.datasize() > 0) {
assert(tmp.datasize() <= seg_size_);
data_[p->layer()].set_start(tmp.start());
data_[p->layer()].set_end(tmp.end());
} else {
// Print error messages, do not send anything and wait for
// next time so that hopefully lower layers will already
// have advanced.
fprintf(stderr, "# ERROR We cannot advance pointers for "
"segment (%d %d)\n", tmp.start(), tmp.end());
for (i = 0; i < layer; i++)
fprintf(stderr, "Layer %d, data ptr (%d %d) \n",
i, data_[i].start(), data_[i].end());
delete p;
return NULL;
}
// Let me know that we've sent out this segment. This is used
// later to drain data (DrainBuffers())
outlist_[p->layer()].add(MediaSegment(p->st(), p->et()));
buffer_[layer] += p->datasize();
bw_[layer] += p->datasize();
drained_[layer] -= p->datasize();
//offset_[layer] += seg_size_;
avgrate_ = rate_weight_*rate() + (1-rate_weight_)*avgrate_;
// DEBUG check
for (i = 0; i < layer-1; i++)
if (data_[i].end() < data_[i+1].end()) {
for (int j = 0; j < layer; j++)
fprintf(stderr, "layer i: (%d %d)\n",
data_[i].start(), data_[i].end());
panic("# ERROR Wrong layer sending order!!\n");
}
return res;
}
void QA::check_layers(int layer, MediaSegment& tmp) {
// XXX While we are moving pointer forward, make sure
// that we are not violating layer boundary constraint
for (int i = layer-1; i >= 0; i--)
// We cannot go faster than a lower layer!!
if (tmp.end() > data_[i].end())
tmp.set_end(data_[i].end());
}
//
// This is optimal buffer distribution for scenario 1.
// NOTE: rate is the current rate before the backoff
// Jan 28, 99
//
// This routines performs buffer sharing by giveing max share
// to the lowest layer, i.e. it fills the triangle in a bottom-up
// starting from the base layer. We use this routine instead of bufOpt,
// for all cases during filling phase. Allocation based on diagonal strips
//
double QA::bufOptScen1(int layer, int layers, double currrate,
double slope, int backoffs)
{
double smallt, larget, side, rate;
if (backoffs < 0) {
panic("# ERROR: backoff: %d in bufOptScen1\n",
backoffs);
}
rate = currrate/pow(2,backoffs);
side = LAYERBW_*layers - (rate + layer*LAYERBW_);
if (side <= 0.0)
return(0.0);
larget = BufNeed(side, slope);
side = LAYERBW_*layers - (rate + (layer+1)*LAYERBW_);
if (side < 0.0)
side = 0.0;
smallt = BufNeed(side, slope);
return (larget-smallt);
}
//
// This routine calculate optimal buffer distribution for a layer
// in scenario 2 based on the
// 1) current rate, 2) no of layers, 3) no of backoffs
//
// Jan 28, 99bufOptScen1(layer, layers, currrate, slope, backoffs)
//
double QA::bufOptScen2(int layer, int layers, double currrate,
double slope, int backoffs)
{
double bufopt = 0.0;
int bmin, done;
if(backoffs < 0) {
panic("# ERROR: backoff: %d in bufOptScen2\n",
backoffs);
}
if ((currrate/pow(2,backoffs)) >= layers*LAYERBW_)
return(0.0);
bmin = 0;
done = 0;
while ((!done) && bmin<=backoffs) {
if(currrate/pow(2,bmin) >= LAYERBW_*layers)
bmin++;
else
done++;
}
// buf required for the first triangle
// we could have dec bmin and go for 1 backoff as well
bufopt = bufOptScen1(layer, layers, currrate/pow(2,bmin), slope, 0);
// remaining sequential backoffs
bufopt += (backoffs - bmin)*BufNeed(layers*LAYERBW_/2, slope);
return(bufopt);
}
//
// This routine returns the optimal distribution of requested-to-drained
// buffer across active layers based on:
// 1) curr rate, 2) curr drain distr(FinalDrainArry), etc
// NOTE, the caller must update FinalDrainArray from
//
// Jan 29, 99
//
// DrainArr: return value, used as an incremental chaneg for
// FinalDrainArray
// bufAvail: current buffer_ state
void QA::drain_buf(double* DrainArr, double bufToDrain,
double* FinalDrainArray, double* bufAvail,
int layers, double rate, double srtt)
{
double bufReq1, bufReq2, bufs1[MAX_LAYER], bufs2[MAX_LAYER], slope,
extra, targetArr[MAX_LAYER], maxDrainRemain;
int bs1, bs2, l;
slope = seg_size_/srtt;
bs1 = MAXBKOFF_ + 1;
bs2 = MAXBKOFF_ + 1;
bufReq1 = bufReq2 = 0;
for(l=0; l TotalBuf(layers, bufAvail)){
bufReq1 = 0;
bs1--;
for(l=0; l TotalBuf(layers, bufAvail)){
bufReq2 = 0;
bs2--;
for(l=0; l= bufReq2) {
// drain toward last optimal scenario 1
for (l=layers-1; l>=0; l--){
// we try to drain the maximum amount from
// min no of highest layers
// note that there is a limit on total draining
// from a layer
maxDrainRemain = srtt*LAYERBW_ - FinalDrainArray[l];
if ((bufAvail[l] > bufs1[l] + maxDrainRemain) &&
(bufToDrain >= maxDrainRemain)) {
DrainArr[l] = maxDrainRemain;
bufToDrain -= maxDrainRemain;
} else {
if(bufAvail[l] > bufs1[l] + maxDrainRemain){
DrainArr[l] = bufToDrain;
bufToDrain = 0.0;
} else {
DrainArr[l] = bufAvail[l] - bufs1[l];
bufToDrain -= bufAvail[l] - bufs1[l];
/* for debug */
if(DrainArr[l] < 0.0){
// panic("# ERROR, DrainArr[%d]: %.2f, bufAvail: %.2f, bufs1: %.2f\n",
// l, DrainArr[l], bufAvail[l], bufs1[l]);
DrainArr[l] = 0.0;
}
}
}
if(bufToDrain == 0.0)
return;
}
return;
} else { /* if (bufReq1 >= bufReq2) */
// Drain towards he last optima scenario 2
// We're draining - don't care about the upper bound on
// scenario 2.
// Have to calculate all the layers together to get this max
// thing to work
extra = 0.0;
// Calculate the extra buffering
for (l=0; l bufs2[l])
extra += bufs1[l] - bufs2[l];
}
for (l=layers-1; l>=0; l--)
if(bufs1[l] >= bufs2[l])
targetArr[l] = bufs1[l];
else
if (bufs2[l] - bufs1[l] >= extra) {
targetArr[l] = bufs2[l] - extra;
extra = 0;
} else {
// there is enough extra to compenstae the dif
if (extra > 0) {
targetArr[l] = bufs2[l];
extra -= bufs2[l] - bufs1[l];
} else
panic("# ERROR Should not \
reach here, extra: %.2f, bufs2: %.2f, bufs1: %.2f, L%d\n",
extra, bufs2[l], bufs1[l], l);
}
} /* end of if (bufReq1 >= bufReq2) */
// drain toward last optimal scenario 2
for (l=layers-1; l>=0; l--) {
// we try to drain the maximum amount from
// min no of highest layers
// note that there is a limit on total draining
// from a layer
maxDrainRemain = srtt*LAYERBW_ - FinalDrainArray[l];
if ((bufAvail[l] > targetArr[l] + maxDrainRemain) &&
(bufToDrain >= maxDrainRemain)) {
DrainArr[l] = maxDrainRemain;
bufToDrain -= maxDrainRemain;
} else {
if(bufAvail[l] > targetArr[l] + maxDrainRemain){
DrainArr[l] = bufToDrain;
bufToDrain = 0.0;
} else {
DrainArr[l] = bufAvail[l] - targetArr[l];
bufToDrain -= bufAvail[l] - targetArr[l];
// for debug
if (DrainArr[l] < 0.0) {
// panic("# ERROR, DrainArr[%d]: %.2f, bufAvail: %.2f, bufs1: %.2f\n",
// l, DrainArr[l], bufAvail[l], bufs1[l]);
DrainArr[l] = 0;
}
}
}
if (bufToDrain == 0.0)
return;
} /* end of for */
return;
}
//
// This routine calculate an optimal distribution of a given
// amount of buffered data to drain.
// the main algorithm is in drain_buf() and this one mainly init
// the input and calls that routine ad then update FinalDrainArray,
// based on its old value and return value for DrainArr.
//
// FinalDrainArray: output
// FinalBuffer: output, expected buf state at the end of the interval
void QA::DrainPacket(double bufToDrain, double* FinalDrainArray, int layers,
double rate, double srtt, double* FinalBuffer)
{
double DrainArr[MAX_LAYER], bufAvail[MAX_LAYER], TotBufAvail;
int l,cnt;
for(l=0; l 0) && (cnt < 10)) {
// debug("bufToDrain%d: %.2f\n", cnt, bufToDrain);
drain_buf(DrainArr, bufToDrain, FinalDrainArray, bufAvail,
layers, rate, srtt);
for(l=0; lQA::check_availability(int layer, const MediaSegment& s)
{
int dummy;
MediaRequest p(MEDIAREQ_CHECKSEG);
p.set_name(page_);
p.set_layer(layer);
p.set_st(s.start());
p.set_et(s.end());
p.set_app(this);
// Ask cache/server to do prefetching if necessary.
target()->get_data(dummy, &p);
}
/*
* This routine is called once every SRTT to drain some data from
* recv's buffer and src's image from recv's buf.
*/
void QA::DrainBuffers()
{
int i, j, layers = 0;
Scheduler& s = Scheduler::instance();
double now = s.clock();
// interval since last drain
double interval = now - playTime_;
playTime_ = now; // update playTime
if ((layers > 1) && (playing_[0] != 1)) {
panic("ERROR in DrainBuffer: layers>0 but L0 isn't playing\n");
}
// Updating playout offset, but do nothing if we are in the initial
// startup filling phase! This offset measures the playing progress
// of the client side. It is actually the playing offset of the lowest
// layer.
// This is the real amount of data to be drained from layers
int todrain[MAX_LAYER];
// Expected offset of base layer after draining, without considering
// holes in data. This has to be satisfied, otherwise base layer will
// be dropped and an error condition will be raised.
poffset_ += (int)floor(interval*LAYERBW_+0.5);
// Started from MAX_LAYER to make debugging easier
for (i = MAX_LAYER-1; i >= 0; i--) {
// If this layer is not being played, don't drain anything
if (sending_[i] == 0) {
todrain[i] = 0;
drained_[i] = 0.0;
continue;
}
todrain[i] = outlist_[i].evict_head_offset(poffset_);
assert(todrain[i] >= 0);
buffer_[i] -= todrain[i];
// A buffer must have more than one byte
if ((int)buffer_[i] <= 0) {
debug("Buffer %d ran dry: %.2f after draining, DROP\n",
i, buffer_[i]);
playing_[i] = 0;
sending_[i] = 0;
buffer_[i] = 0;
/* Drop all higher layers if they still have data */
for (j = i+1; j < MAX_LAYER; j++)
if (sending_[j] == 1) {
/*
panic("# ERROR: layer %d \
is playing with %.2f buf but layer %d ran dry with %.2f buf\n",
j, buffer_[j], i, buffer_[i]);
*/
debug("# DROP layer %d: it \
is playing with %.2f buf but layer %d ran dry with %.2f buf\n",
j, buffer_[j], i, buffer_[i]);
sending_[j] = 0;
playing_[j] = 0;
buffer_[j] = 0;
}
// We don't need to set it to -1. The old address
// will be used to see if we are sending old data if
// that later is added again
//
// XXX Where is this -1 mark ever used????
// data_[i].set_start(-1); // drop layer i
} else {
// Prefetch for this layer. Round to whole segment
int st = (int)floor((poffset_+pref_srtt_*LAYERBW_)
/seg_size_+0.5)*seg_size_;
int et = (int)floor((poffset_+(pref_srtt_+interval)*
LAYERBW_)/seg_size_+0.5)*seg_size_;
if (et > pref_[i]) {
pref_[i] = et;
MediaSegment s(st, et);
check_availability(i, s);
}
}
} /* end of for */
}
// This routine dumps info into a file
// format of each line is as follows:
// time tot-rate avg-rate per-layer-bw[MAXLAYER] tot-bw drain-rate[MAXLAYER]
// & cumulative-buffer[MAXLAYER] & no-of-layers
// ADDED: use the old value of SRTT for bw/etc estimation !!! Jan 26
// XXX: need to be more compressed add more hooks to for ctrling from
// tcl level
void QA::DumpInfo(double t, double last_t, double rate,
double avgrate, double srtt)
{
#define MAXLEN 2000
int i,j;
char s1[MAXLEN], s2[MAXLEN], tmp[MAXLEN];
static double last_srtt = 0, t1,t2 = 0;
#undef MAXLEN
double tot_bw = 0.0, interval, diff;
// if(rate > 1000000.0){
// debug("WARNING rate: %f is too large\n", rate);
// }
interval = t - last_t ;
if((t2 != last_t) && (t2 > 0)){
diff = interval - last_srtt;
if ((diff > 0.001) || (diff < -0.001)) {
if (last_t == 0)
// Startup phase
return;
/*
debug("WARNING: last_srtt: %.4f != \
interval: %.4f, diff: %f t1: %f, t2: %f, last_t: %f, t: %f\n",
last_srtt, interval, diff, t1, t2, last_t, t);
*/
//abort();
}
} else
/* for the first call to init */
last_srtt = srtt;
t1 = last_t;
t2 = t;
if (interval <= 0.0) {
panic("# ERROR interval is negative\n");
}
sprintf(s1, " %.2f %.2f %.2f X", last_t, rate, avgrate);
sprintf(s2, " %.2f %.2f %.2f X", t, rate, avgrate);
j = 0;
for (i = 0; i < MAX_LAYER; i++)
//if (playing_[i] == 1)
if (sending_[i] == 1)
j++;
//no of layers being playback
sprintf(tmp, " %d", j*LAYERBW_);
strcat(s1, tmp);
strcat(s2, tmp);
for (i = 0; i < MAX_LAYER; i++) {
sprintf(tmp, " %.2g ", (bw_[i]/interval)+i*10000.0);
strcat(s1,tmp);
strcat(s2,tmp);
tot_bw += bw_[i]/interval;
bw_[i] = 0;
}
sprintf(tmp, " %.2f X", tot_bw );
strcat(s1,tmp);
strcat(s2,tmp);
j = 0;
for (i = 0; i < MAX_LAYER; i++) {
//if (playing_[i] == 1) {
if(sending_[i] ==1){
j++;
// drained_[] can be neg when allocated buf for this
// layer is more than consumed data
if (drained_[i] < 0.0) {
// this means that this layer was drained
// with max rate
drained_[i] = 0.0;
}
// XXX, we could have used interval*LAYERBW_ - bw_[i]
// that was certainly better
sprintf(tmp, " %.2f ",
(drained_[i]/interval)+i*10000.0);
strcat(s1,tmp);
strcat(s2,tmp);
// Note that drained[] shows the amount of data that
// is used from buffered data, i.e. rd[i]
// This must be srtt instead of interval because this
// is for next dumping.
drained_[i]=srtt*LAYERBW_;
} else {
sprintf(tmp, " %.2f ", i*10000.0);
strcat(s1,tmp);
strcat(s2,tmp);
drained_[i] = 0.0;
}
}
for (i=0;iQA::UpdateState()
{
double last_ptime = playTime_; // Last time to drain buffer
DrainBuffers();
DumpInfo(Scheduler::instance().clock(), last_ptime,
rate(), avgrate_, rap()->srtt());
}
rap.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
//
// Copyright (c) 1997 by the University of Southern California
// All rights reserved.
//
// Permission to use, copy, modify, and distribute this software and its
// documentation in source and binary forms for non-commercial purposes
// and without fee is hereby granted, provided that the above copyright
// notice appear in all copies and that both the copyright notice and
// this permission notice appear in supporting documentation. and that
// any documentation, advertising materials, and other materials related
// to such distribution and use acknowledge that the software was
// developed by the University of Southern California, Information
// Sciences Institute. The name of the University may not be used to
// endorse or promote products derived from this software without
// specific prior written permission.
//
// THE UNIVERSITY OF SOUTHERN CALIFORNIA makes no representations about
// the suitability of this software for any purpose. THIS SOFTWARE IS
// PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
// INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
//
// Other copyrights might apply to parts of this software and are so
// noted when applicable.
//
// rap.cc
// Code for the 'RAP Source' Agent Class
//
// Author:
// Mohit Talwar (mohit@catarina.usc.edu)
//
// $Header$
#include "rap.h"
int hdr_rap::offset_; // static offset of RAP header
static class RapHeaderClass : public PacketHeaderClass
{
public:
RapHeaderClass() : PacketHeaderClass("PacketHeader/RAP",
sizeof(hdr_rap)) {
bind_offset(&hdr_rap::offset_);
}
} class_raphdr;
static class RapClass : public TclClass
{
public:
RapClass() : TclClass("Agent/RAP") {}
TclObject* create(int, const char*const*)
{ return (new RapAgent()); }
} class_rap;
void IpgTimer::expire(Event *)
{
a_->timeout(RAP_IPG_TIMEOUT);
}
void RttTimer::expire(Event *)
{
a_->timeout(RAP_RTT_TIMEOUT);
}
//----------------------------------------------------------------------
// EqualSeqno
// Compare TransHistory Entries on the seqno field.
//
// "i1", "i2" are the TransHistory entries to be compared.
//----------------------------------------------------------------------
int EqualSeqno(void *i1, void *i2)
{
return (((TransHistoryEntry *) i1)->seqno ==
((TransHistoryEntry *) i2)->seqno);
}
//----------------------------------------------------------------------
// RapAgent::RapAgent
// Initialize the RAP agent.
// Bind variables which have to be accessed in both Tcl and C++.
// Initializes time values.
//----------------------------------------------------------------------
RapAgent::RapAgent() : Agent(PT_RAP_DATA), ipgTimer_(this), rttTimer_(this),
seqno_(0), sessionLossCount_(0), curseq_(0), ipg_(2.0), srtt_(2.0),
timeout_(2.0), lastRecv_(0), lastMiss_(0), prevRecv_(0), dctr_(0),
flags_(0), fixIpg_(0)
{
bind("packetSize_", &size_); // Default 512
bind("seqno_", &seqno_); // Default 0
bind("sessionLossCount_", &sessionLossCount_); // Default 0
bind("ipg_", &ipg_); // Default 2 seconds
bind("beta_", &beta_); // Default 0.5
bind("alpha_", &alpha_); // Default 1.0
bind("srtt_", &srtt_); // Default 2 seconds
bind("variance_", &variance_);// Default 0
bind("delta_", &delta_); // Default 0.5
bind("mu_", &mu_); // Default 1.2
bind("phi_", &phi_); // Default 4
bind("timeout_", &timeout_); // Default 2 seconds
bind("overhead_", &overhead_); // Default 0
bind("useFineGrain_", &useFineGrain_); // Default FALSE
bind("kfrtt_", &kfrtt_); // Default 0.9
bind("kxrtt_", &kxrtt_); // Default 0.01
bind("debugEnable_", &debugEnable_); // Default FALSE
bind("off_rap_", &off_rap_);
bind("rap_base_hdr_size_", &rap_base_hdr_size_);
bind("dpthresh_", &dpthresh_);
frtt_ = xrtt_ = srtt_;
}
// Cancel all our timers before we quit
RapAgent::~RapAgent()
{
// fprintf(stderr, "%g: rap agent %s(%d) stops.\n",
// Scheduler::instance().clock(), name(), addr());
// Tcl::instance().eval("[Simulator instance] flush-trace");
stop();
}
//----------------------------------------------------------------------
// RapAgent::UpdateTimeValues
// Update the values for srtt_, variance_ and timeout_ based on
// the "sampleRtt". Use Jacobson/Karl's algorithm.
//
// "sampleRtt" is the sample round trip time obtained from the
// current ack packet.
//----------------------------------------------------------------------
void RapAgent::UpdateTimeValues(double sampleRtt)
{
double diff;
static int initial = TRUE;
if (initial) {
frtt_ = xrtt_ = srtt_ = sampleRtt; // First sample, no history
variance_ = 0;
initial = FALSE;
}
diff = sampleRtt - srtt_;
srtt_ += delta_ * diff;
diff = (diff < 0) ? diff * -1 : diff; // Take mod
variance_ += delta_ * (diff - variance_);
timeout_ = mu_ * srtt_ + phi_ * variance_;
if (useFineGrain_) {
frtt_ = (1 - kfrtt_) * frtt_ + kfrtt_ * sampleRtt;
xrtt_ = (1 - kxrtt_) * xrtt_ + kxrtt_ * sampleRtt;
}
double debugSrtt = srtt_; // $%#& stoopid compiler
Debug(debugEnable_, logfile_,
"- srtt updated to %f\n", debugSrtt);
}
void RapAgent::start()
{
if (debugEnable_)
logfile_ = DebugEnable(this->addr() >>
Address::instance().NodeShift_[1]);
else
// Should initialize it regardless of whether it'll be used.
logfile_ = NULL;
Debug(debugEnable_, logfile_, "%.3f: RAP start.\n",
Scheduler::instance().clock());
flags_ = flags_ & ~RF_STOP;
startTime_ = Scheduler::instance().clock();
RttTimeout(); // Decreases initial IPG
IpgTimeout();
}
// Used by a sink to listen to incoming packets
void RapAgent::listen()
{
if (debugEnable_)
logfile_ = DebugEnable(this->addr() >>
Address::instance().NodeShift_[1]);
}
void RapAgent::stop()
{
Debug(debugEnable_, logfile_,
"%.3f: RAP stop.\n", Scheduler::instance().clock());
// Cancel the timer only when there is one
if (ipgTimer_.status() == TIMER_PENDING)
ipgTimer_.cancel();
if (rttTimer_.status() == TIMER_PENDING)
rttTimer_.cancel();
stopTime_ = Scheduler::instance().clock();
int debugSeqno = seqno_;
Debug(debugEnable_, logfile_,
"- numPackets %d, totalTime %f\n",
debugSeqno, stopTime_ - startTime_);
flags_ |= RF_STOP;
}
//----------------------------------------------------------------------
// RapAgent::command
// Called when a Tcl command for the RAP agent is executed.
// Two commands are supported
// $rapsource start
// $rapsource stop
//----------------------------------------------------------------------
int RapAgent::command(int argc, const char*const* argv)
{
if (argc == 2) {
if (strcmp(argv[1], "start") == 0) {
start();
// return TCL_OK, so the calling function knows that
// the command has been processed
return (TCL_OK);
} else if (strcmp(argv[1], "stop") == 0) {
stop();
return (TCL_OK);
} else if (strcmp(argv[1], "listen") == 0) {
listen();
return (TCL_OK);
}
} else if (argc == 3) {
if (strcmp(argv[1], "advanceby") == 0) {
advanceby(atoi(argv[2]));
return (TCL_OK);
}
}
// If the command hasn't been processed by RapAgent()::command,
// call the command() function for the base class
return (Agent::command(argc, argv));
}
//----------------------------------------------------------------------
// RapAgent::SendPacket
// Called in IpgTimeout().
// Create a packet, increase seqno_, send the packet out.
//----------------------------------------------------------------------
void RapAgent::SendPacket(int nbytes, AppData *data)
{
TransHistoryEntry *pktInfo;
Packet *pkt;
type_ = PT_RAP_DATA;
if (data)
pkt = allocpkt(data->size());
else
pkt = allocpkt();
// Fill in RAP headers
hdr_rap* hdr = (hdr_rap*) pkt->access(off_rap_);
hdr->seqno() = ++seqno_; // Start counting from 1;
hdr->lastRecv = hdr->lastMiss = hdr->prevRecv = 0; // Ignore @ sender
hdr->flags() = RH_DATA;
if (data) {
hdr->size() = data->size();
pkt->setdata(data);
} else {
hdr->size() = size_;
}
// XXX Simply set packet size to the given ADU's nominal size.
// Make sure that the size is reasonable!!
hdr_cmn *ch = (hdr_cmn*)pkt->access(off_cmn_);
ch->size() = nbytes;
send(pkt, 0);
pktInfo = new TransHistoryEntry(seqno_);
transmissionHistory_.SetInsert((void *) pktInfo, EqualSeqno);
int debugSeqno = seqno_;
Debug(debugEnable_, logfile_,
"- packet %d sent\n", debugSeqno);
}
//----------------------------------------------------------------------
// RapAgent::recv
// Called when a packet is received.
// Should be of type PT_RAP_ACK.
//----------------------------------------------------------------------
void RapAgent::recv(Packet* pkt, Handler*)
{
Debug(debugEnable_, logfile_,
"%.3f: RAP packet received.\n", Scheduler::instance().clock());
hdr_rap* hdr = (hdr_rap*) pkt->access(off_rap_); // Access RAP header
switch (hdr->flags()) {
case RH_DATA:
UpdateLastHole(hdr->seqno());
SendAck(hdr->seqno());
if ((pkt->datalen() > 0) && app_)
// We do have user data, process it
app_->process_data(pkt->datalen(), pkt->userdata());
break;
case RH_ACK:
RecvAck(hdr);
break;
default:
fprintf(stderr,
"RAP agent %s received a packet with unknown flags %x",
name(), hdr->flags());
break;
}
Packet::free(pkt); // Discard the packet
}
//----------------------------------------------------------------------
// RapAgent::RecvAck
// Called when an Ack is received.
//
// "header" is the RAP header of the Ack.
//----------------------------------------------------------------------
void RapAgent::RecvAck(hdr_rap *ackHeader)
{
double sampleRtt;
TransHistoryEntry *old, key(ackHeader->seqno_);
assert(ackHeader->seqno_ > 0);
Debug(debugEnable_, logfile_,
"- ack %d\n", ackHeader->seqno_);
old = (TransHistoryEntry *)
transmissionHistory_.SetRemove((void *) &key, EqualSeqno);
if (old != NULL) {
Debug(debugEnable_, logfile_,
"- found in transmission history\n");
assert((old->status == RAP_SENT) || (old->status == RAP_INACTIVE));
// Get sample rtt
sampleRtt = key.departureTime - old->departureTime;
UpdateTimeValues(sampleRtt);
delete old;
}
if (!anyack()) {
flags_ |= RF_ANYACK;
ipg_ = srtt_;
}
if (LossDetection(RAP_ACK_BASED, ackHeader))
LossHandler();
// XXX We only stop by sequence number when we are in
// "counting sequence number" mode. -- haoboy
if (counting_pkt() && (ackHeader->seqno_ >= curseq_))
finish();
}
//----------------------------------------------------------------------
// RapAgent::timeout
// Called when a timer fires.
//
// "type" is the type of Timeout event
//----------------------------------------------------------------------
void RapAgent::timeout(int type)
{
if (type == RAP_IPG_TIMEOUT)
IpgTimeout();
else if (type == RAP_RTT_TIMEOUT)
RttTimeout();
else
assert(FALSE);
}
//----------------------------------------------------------------------
// RapAgent::IpgTimeout
// Called when the ipgTimer_ fires.
//----------------------------------------------------------------------
void RapAgent::IpgTimeout()
{
double waitPeriod; // Time before next transmission
Debug(debugEnable_, logfile_,
"%.3f: IPG Timeout.\n", Scheduler::instance().clock());
if (LossDetection(RAP_TIMER_BASED))
LossHandler();
else if (!counting_pkt()) {
if (app_) {
int nbytes;
AppData* data = app_->get_data(nbytes);
// Missing data in application. What should we do??
// For now, simply schedule the next SendPacket().
// If the application has nothing to send, it'll stop
// the rap agent later on.
if (data != NULL) {
SendPacket(nbytes, data);
dctr_++;
}
} else {
// If RAP doesn't have application, just go ahead and
// send packet
SendPacket(size_);
dctr_++;
}
} else if (seqno_ < curseq_) {
SendPacket(size_);
dctr_++;
}
// XXX If we only bound IPG in DecreaseIpg(), the thresholding will
// happen immediately because DecreaseIpg() isn't called immediately.
// So we do it here.
if (fixIpg_ != 0)
ipg_ = fixIpg_;
if (useFineGrain_)
waitPeriod = frtt_ / xrtt_ * ipg_;
else
waitPeriod = ipg_;
// By this point, we may have been stopped by applications above
// Thus, do not reschedule a timer if we are stopped.
if (!is_stopped())
ipgTimer_.resched(waitPeriod + Random::uniform(overhead_));
}
//----------------------------------------------------------------------
// RapAgent::RttTimeout
// Called when the rttTimer_ fires.
// Decrease IPG. Restart rttTimer_.
//----------------------------------------------------------------------
void RapAgent::RttTimeout()
{
Debug(debugEnable_, logfile_,
"%.3f: RTT Timeout.\n", Scheduler::instance().clock());
// During the past srtt_, we are supposed to send out srtt_/ipg_
// packets. If we sent less than that, we may not increase rate
if (100*dctr_*(ipg_/srtt_) >= dpthresh_)
DecreaseIpg(); // Additive increase in rate
else
Debug(debugEnable_, logfile_,
"- %f Cannot increase rate due to insufficient data.\n",
Scheduler::instance().clock());
dctr_ = 0;
double debugIpg = ipg_ + overhead_ / 2;
Debug(debugEnable_, logfile_,
"- ipg decreased at %.3f to %f\n",
Scheduler::instance().clock(), debugIpg);
rttTimer_.resched(srtt_);
}
//----------------------------------------------------------------------
// RapAgent::LossDetection
// Called in ipgTimeout (RAP_TIMER_BASED)
// or in RecvAck (RAP_ACK_BASED).
//
// Returns:
// TRUE if loss detected, FALSE otherwise.
//
// "ackHeader" is the RAP header of the received Ack (PT_RAP_ACK).
//----------------------------------------------------------------------
static double currentTime;
static hdr_rap *ackHdr;
static RapAgent *rapAgent;
static int numLosses;
int EqualStatus(void *i1, void *i2)
{
return (((TransHistoryEntry *) i1)->status ==
((TransHistoryEntry *) i2)->status);
}
void DestroyTransHistoryEntry(int item)
{
TransHistoryEntry *entry = (TransHistoryEntry *) item;
Debug(rapAgent->GetDebugFlag(), rapAgent->GetLogfile(),
"- purged seq num %d\n", entry->seqno);
delete entry;
}
void TimerLostPacket(int item)
{
TransHistoryEntry *entry = (TransHistoryEntry *) item;
if ((entry->departureTime + rapAgent->GetTimeout()) <= currentTime) {
// ~ Packets lost in RAP session
rapAgent->IncrementLossCount();
// Ignore cluster losses
if (entry->status != RAP_INACTIVE) {
assert(entry->status == RAP_SENT);
numLosses++;
Debug(rapAgent->GetDebugFlag(), rapAgent->GetLogfile(),
"- timerlost seq num %d , last sent %d\n",
entry->seqno, rapAgent->GetSeqno());
}
entry->status = RAP_PURGED;
}
}
void AckLostPacket(int item)
{
TransHistoryEntry *entry = (TransHistoryEntry *) item;
int seqno, lastRecv, lastMiss, prevRecv;
seqno = entry->seqno;
lastRecv = ackHdr->lastRecv;
lastMiss = ackHdr->lastMiss;
prevRecv = ackHdr->prevRecv;
if (seqno <= lastRecv) {
if ((seqno > lastMiss) || (seqno == prevRecv))
entry->status = RAP_PURGED; // Was Received, now purge
else if ((lastRecv - seqno) >= 3) {
// ~ Packets lost in RAP session
rapAgent->IncrementLossCount();
if (entry->status != RAP_INACTIVE) {
assert(entry->status == RAP_SENT);
numLosses++;
Debug(rapAgent->GetDebugFlag(),
rapAgent->GetLogfile(),
"- acklost seqno %d , last sent %d\n",
seqno, rapAgent->GetSeqno());
}
// Was Lost, purge from history
entry->status = RAP_PURGED;
}
}
}
int RapAgent::LossDetection(RapLossType type, hdr_rap *ackHeader)
{
TransHistoryEntry key(0, RAP_PURGED);
currentTime = key.departureTime;
ackHdr = ackHeader;
rapAgent = this;
numLosses = 0;
switch(type) {
case RAP_TIMER_BASED:
transmissionHistory_.Mapcar(TimerLostPacket);
break;
case RAP_ACK_BASED:
transmissionHistory_.Mapcar(AckLostPacket);
break;
default:
assert(FALSE);
}
Debug(debugEnable_, logfile_,
"- %d losses detected\n", numLosses);
Debug(debugEnable_, logfile_,
"- history size %d\n", transmissionHistory_.Size());
transmissionHistory_.Purge((void *) &key,
EqualStatus, // Purge PURGED packets
DestroyTransHistoryEntry);
Debug(debugEnable_, logfile_,
"- history size %d\n", transmissionHistory_.Size());
if (numLosses)
return TRUE;
else
return FALSE;
}
//----------------------------------------------------------------------
// RapAgent::LossHandler
// Called when loss detected.
// Increase IPG. Mark packets INACTIVE. Reschedule rttTimer_.
//----------------------------------------------------------------------
void MarkInactive(int item)
{
TransHistoryEntry *entry = (TransHistoryEntry *) item;
entry->status = RAP_INACTIVE;
}
void RapAgent::LossHandler()
{
IncreaseIpg(); // Multiplicative decrease in rate
double debugIpg = ipg_ + overhead_ / 2;
Debug(debugEnable_, logfile_,
"- ipg increased at %.3f to %f\n",
Scheduler::instance().clock(), debugIpg);
transmissionHistory_.Mapcar(MarkInactive);
Debug(debugEnable_, logfile_,
"- window full packets marked inactive\n");
rttTimer_.resched(srtt_);
}
//----------------------------------------------------------------------
// RapAgent::SendAck
// Create an ack packet, set fields, send the packet out.
//
// "seqNum" is the sequence number of the packet being acked.
//----------------------------------------------------------------------
void RapAgent::SendAck(int seqNum)
{
type_ = PT_RAP_ACK;
Packet* pkt = allocpkt(); // Create a new packet
hdr_rap* hdr = (hdr_rap*) pkt->access(off_rap_); // Access header
hdr->seqno() = seqNum;
hdr->flags() = RH_ACK;
hdr->lastRecv = lastRecv_;
hdr->lastMiss = lastMiss_;
hdr->prevRecv = prevRecv_;
hdr_cmn *ch = (hdr_cmn*)pkt->access(off_cmn_);
ch->size() = rap_base_hdr_size_;
send(pkt, 0);
Debug(debugEnable_, logfile_,
"- ack sent %u [%u %u %u]\n",
seqNum, lastRecv_, lastMiss_, prevRecv_);
}
//----------------------------------------------------------------------
// RapSinkAgent::UpdateLastHole
// Update the last hole in sequence number space at the receiver.
//
// "seqNum" is the sequence number of the data packet received.
//----------------------------------------------------------------------
void RapAgent::UpdateLastHole(int seqNum)
{
assert(seqNum > 0);
if (seqNum > (lastRecv_ + 1)) {
prevRecv_ = lastRecv_;
lastRecv_ = seqNum;
lastMiss_ = seqNum - 1;
return;
}
if (seqNum == (lastRecv_ + 1)) {
lastRecv_ = seqNum;
return;
}
if ((lastMiss_ < seqNum) && (seqNum <= lastRecv_)) // Duplicate
return;
if (seqNum == lastMiss_) {
if ((prevRecv_ + 1) == seqNum) // Hole filled
prevRecv_ = lastMiss_ = 0;
else
lastMiss_--;
return;
}
if ((prevRecv_ < seqNum) && (seqNum < lastMiss_)) {
prevRecv_ = seqNum;
return;
}
assert(seqNum <= prevRecv_); // Pretty late...
}
// take pkt count
void RapAgent::advanceby(int delta)
{
flags_ |= RF_COUNTPKT;
curseq_ = delta;
start();
}
void RapAgent::finish()
{
stop();
Tcl::instance().evalf("%s done", this->name());
}
raplist.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
* Copyright (c) 1993 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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*
* Author:
* Mohit Talwar (mohit@catarina.usc.edu)
*
* $Header$
*
* This is taken from UCB Nachos project
*
* list.cc
*
* Routines to manage a singly-linked list of "things".
*
* A "ListElement" is allocated for each item to be put on the
* list; it is de-allocated when the item is removed. This means
* we don't need to keep a "next" pointer in every object we
* want to put on a list.
*/
#include "utilities.h"
#include "raplist.h"
//----------------------------------------------------------------------
// ListElement::ListElement
// Initialize a List element, so it can be added somewhere on a List.
//
// "itemPtr" is the item to be put on the List. It can be a pointer
// to anything.
// "sortKey" is the priority of the item, if any.
//----------------------------------------------------------------------
ListElement::ListElement(void *itemPtr, float sortKey)
{
item = itemPtr;
key = sortKey;
next = NULL; // assume we'll put it at the end of the List
}
//----------------------------------------------------------------------
// List::List
// Initialize a List, empty to start with.
// Elements can now be added to the List.
//----------------------------------------------------------------------
List::List()
{
size = 0;
first = last = NULL;
}
//----------------------------------------------------------------------
// List::~List
// Prepare a List for deallocation. If the List still contains any
// ListElements, de-allocate them. However, note that we do *not*
// de-allocate the "items" on the List -- this module allocates
// and de-allocates the ListElements to keep track of each item,
// but a given item may be on multiple Lists, so we can't
// de-allocate them here.
//----------------------------------------------------------------------
List::~List()
{
while (Remove() != NULL)
; // delete all the List elements
}
//----------------------------------------------------------------------
// List::Append
// Append an "item" to the end of the List.
//
// Allocate a ListElement to keep track of the item.
// If the List is empty, then this will be the only element.
// Otherwise, put it at the end.
//
// "item" is the thing to put on the List, it can be a pointer to
// anything.
//----------------------------------------------------------------------
void List::Append(void *item)
{
ListElement *element = new ListElement(item, 0);
if (IsEmpty())
{ // List is empty
first = element;
last = element;
}
else
{ // else put it after last
last->next = element;
last = element;
}
size++;
}
//----------------------------------------------------------------------
// List::Prepend
// Put an "item" on the front of the List.
//
// Allocate a ListElement to keep track of the item.
// If the List is empty, then this will be the only element.
// Otherwise, put it at the beginning.
//
// "item" is the thing to put on the List, it can be a pointer to
// anything.
//----------------------------------------------------------------------
void List::Prepend(void *item)
{
ListElement *element = new ListElement(item, 0);
if (IsEmpty())
{ // List is empty
first = element;
last = element;
}
else
{ // else put it before first
element->next = first;
first = element;
}
size++;
}
//----------------------------------------------------------------------
// List::Remove
// Remove the first "item" from the front of the List.
//
// Returns:
// Pointer to removed item, NULL if nothing on the List.
//----------------------------------------------------------------------
void *List::Remove()
{
return SortedRemove(NULL); // Same as SortedRemove, but ignore the key
}
//----------------------------------------------------------------------
// List::Mapcar
// Apply a function to each item on the List, by walking through
// the List, one element at a time.
//
// Unlike LISP, this mapcar does not return anything!
//
// "func" is the procedure to apply to each element of the List.
//----------------------------------------------------------------------
void List::Mapcar(VoidFunctionPtr func)
{
for (ListElement *ptr = first; ptr != NULL; ptr = ptr->next)
(*func)((int)ptr->item);
}
//----------------------------------------------------------------------
// List::IsEmpty
// Returns TRUE if the List is empty (has no items).
//----------------------------------------------------------------------
int List::IsEmpty()
{
if (first == NULL)
return TRUE;
else
return FALSE;
}
//----------------------------------------------------------------------
// List::SortedInsert
// Insert an "item" into a List, so that the List elements are
// sorted in increasing order by "sortKey".
//
// Allocate a ListElement to keep track of the item.
// If the List is empty, then this will be the only element.
// Otherwise, walk through the List, one element at a time,
// to find where the new item should be placed.
//
// "item" is the thing to put on the List, it can be a pointer to
// anything.
// "sortKey" is the priority of the item.
//----------------------------------------------------------------------
void List::SortedInsert(void *item, float sortKey)
{
ListElement *element = new ListElement(item, sortKey);
ListElement *ptr; // keep track
if (IsEmpty())
{ // List is empty, put in front
first = element;
last = element;
}
else if (sortKey < first->key)
{ // item goes on front of List
element->next = first;
first = element;
}
else
{ // look for first elt in List bigger than item
for (ptr = first; ptr->next != NULL; ptr = ptr->next) {
if (sortKey < ptr->next->key)
{
element->next = ptr->next;
ptr->next = element;
return;
}
}
last->next = element; // item goes at end of List
last = element;
}
size++;
}
//----------------------------------------------------------------------
// List::SortedRemove
// Remove the first "item" from the front of a sorted List.
//
// Returns:
// Pointer to removed item, NULL if nothing on the List.
// Sets *keyPtr to the priority value of the removed item
// (this is needed by interrupt.cc, for instance).
//
// "keyPtr" is a pointer to the location in which to store the
// priority of the removed item.
//----------------------------------------------------------------------
void *List::SortedRemove(float *keyPtr)
{
ListElement *element = first;
void *thing;
if (IsEmpty())
return NULL;
thing = first->item;
if (first == last)
{ // List had one item, now has none
first = NULL;
last = NULL;
}
else
first = element->next;
if (keyPtr != NULL)
*keyPtr = element->key;
delete element;
size--;
return thing;
}
//----------------------------------------------------------------------
// List::MinKey
// Return the key of the item in the front of a sorted List.
//
// Returns :
// -1 if List is empty
//----------------------------------------------------------------------
float List::MinKey()
{
if (IsEmpty())
return -1;
else
return first->key;
}
//----------------------------------------------------------------------
// List::SetInsert
// Insert "key" into a List, so that the List elements are unique.
//
// Returns :
// TRUE if "key" inserted, FALSE o/w
//
// Caveat :
// Does not allocate space for key! Provided by calling function.
// If FALSE, calling function should delete allocated space.
//
// "key" is the thing to put on the List.
// "eq" is the comparison function used to compare two items.
//----------------------------------------------------------------------
int List::SetInsert(void *key, CompareFunction eq)
{
if (IsPresent(key, eq))
return FALSE;
Prepend(key);
return TRUE;
}
//----------------------------------------------------------------------
// List::SetRemove
// Remove "key" from List.
//
// Returns :
// handle to "key" in list if removed, NULL o/w
//
// Caveat :
// if not NULL, Calling function should delete allocated space.
//
// "key" is the item to be removed.
// "eq" is the comparison function used to compare two items.
//----------------------------------------------------------------------
void *List::SetRemove(void *key, CompareFunction eq)
{
ListElement *prev, *curr;
void *thing;
if (!IsPresent(key, eq))
return NULL;
for (prev = NULL, curr = first;
curr != NULL;
prev = curr, curr = curr->next)
if ((*eq)(key, curr->item))
break;
assert(curr != NULL); // Since its present we'd better find it
if (curr == first)
first = curr->next;
else
prev->next = curr->next;
if (curr == last)
last = prev;
thing = curr->item;
delete curr;
size--;
return thing;
}
//----------------------------------------------------------------------
// List::IsPresent
// Is "key" there in the Set
//
// Returns :
// handle to the "key" in list (NULL if not found)
//
// "key" is the item to be searched.
// "eq" is the comparison function used to compare two items.
//----------------------------------------------------------------------
void *List::IsPresent(void *key, CompareFunction eq)
{
ListElement *ptr;
for (ptr = first; ptr != NULL; ptr = ptr->next) // Check all items
if ((*eq)(key, ptr->item))
return ptr->item;
return NULL;
}
//----------------------------------------------------------------------
// List::Purge
// Remove all elements with (key eq "key") from the Set
//
// "key" is the item to be searched and deleted.
// "eq" is the comparison function used to compare two items.
// "destroy" is the function used to delete the purged "key".
//----------------------------------------------------------------------
void List::Purge(void *key, CompareFunction eq, VoidFunctionPtr destroy)
{
ListElement *prev, *curr, *temp;
void *thing;
for (prev = NULL, curr = first; curr != NULL; ) // Check all items
if ((*eq)(key, curr->item))
{
if (curr == first)
first = curr->next;
else
prev->next = curr->next;
if (curr == last)
last = prev;
thing = curr->item;
temp = curr;
curr = curr->next;
(* destroy)((int) thing);
delete temp;
size--;
}
else
{
prev = curr;
curr = curr->next;
}
}
utilities.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
//
// Copyright (c) 1997 by the University of Southern California
// All rights reserved.
//
// Permission to use, copy, modify, and distribute this software and its
// documentation in source and binary forms for non-commercial purposes
// and without fee is hereby granted, provided that the above copyright
// notice appear in all copies and that both the copyright notice and
// this permission notice appear in supporting documentation. and that
// any documentation, advertising materials, and other materials related
// to such distribution and use acknowledge that the software was
// developed by the University of Southern California, Information
// Sciences Institute. The name of the University may not be used to
// endorse or promote products derived from this software without
// specific prior written permission.
//
// THE UNIVERSITY OF SOUTHERN CALIFORNIA makes no representations about
// the suitability of this software for any purpose. THIS SOFTWARE IS
// PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
// INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
//
// Other copyrights might apply to parts of this software and are so
// noted when applicable.
//
// utilities.cc
// Debugging routines.
//
// Author:
// Mohit Talwar (mohit@catarina.usc.edu)
//
// $Header$
#include
#include "utilities.h"
// Constants...
#ifndef NAME_MAX
// Maximum length of a file name
// In case that it's not defined in limits.h
#define NAME_MAX 14
#endif
//----------------------------------------------------------------------
// DebugEnable
// Enable DEBUG messages.
//----------------------------------------------------------------------
FILE * DebugEnable(unsigned int nodeid)
{
FILE *log; // Logfile for debug messages
char logFileName[NAME_MAX];
sprintf(logFileName, "rap.%u.log", nodeid);
log = fopen(logFileName, "w");
assert(log != NULL);
return log;
}
//----------------------------------------------------------------------
// Debug
// Print a debug message if debugFlag is enabled. Like printf.
//----------------------------------------------------------------------
void Debug(int debugFlag, FILE *log, char *format, ...)
{
if (debugFlag)
{
va_list ap;
va_start(ap, format);
vfprintf(log, format, ap);
va_end(ap);
fflush(log);
}
}
// Data structures
void DoubleList::destroy()
{
DoubleListElem *p = head_, *q;
while (p != NULL) {
q = p;
p = p->next();
delete q;
}
head_ = tail_ = NULL;
}
agent-list.cc
// Author: Satish Kumar, kkumar@isi.edu
extern "C" {
#include
#include
};
#include "agent-list.h"
AgentList* AgentList::instance_;
static class AgentListClass:public TclClass
{
public:
AgentListClass ():TclClass ("AgentList")
{
}
TclObject *create (int, const char *const *)
{
return (new AgentList ());
}
} class_agent_list;
int
AgentList::command (int argc, const char *const *argv)
{
if(argc == 3) {
if (strcasecmp(argv[1], "num_agents") == 0) {
assert(num_agents_ == 0);
num_agents_ = atoi(argv[2]);
agents_ = new void*[num_agents_];
bzero((char*) agents_, sizeof(void*) * num_agents_);
instance_ = this;
return TCL_OK;
}
}
return (TclObject::command(argc, argv));
}
void
AgentList::AddAgent(nsaddr_t node_addr, void *a) {
assert(agents_);
assert(node_addr < num_agents_);
agents_[node_addr] = a;
}
flood-agent.cc
// Author: Satish Kumar, kkumar@isi.edu
extern "C" {
#include
#include
};
#include "flood-agent.h"
#include
#include
#include
#define OLD_QRY_ENTRY 0
#define OLD_SHORTER_ENTRY 1
#define NEW_QRY_ENTRY 2
#define MAX_CACHE_ITEMS 200
void FloodAgent::
trace (char *fmt,...)
{
va_list ap; // Define a variable ap that will refer to each argument in turn
if (!tracetarget_)
return;
// Initializes ap to first argument
va_start (ap, fmt);
// Prints the elements in turn
vsprintf (tracetarget_->buffer (), fmt, ap);
tracetarget_->dump ();
// Does the necessary clean-up before returning
va_end (ap);
}
static class FloodAgentClass:public TclClass
{
public:
FloodAgentClass ():TclClass ("Agent/flood")
{
}
TclObject *create (int, const char *const *)
{
return (new FloodAgent ());
}
} class_flood_agent;
FloodAgent::FloodAgent() : Agent(PT_MESSAGE)
{
node_ = NULL;
tag_list_ = NULL;
query_list_ = NULL;
cache_ = 0; // Disable caching by default
tag_cache_ = new TagCache[MAX_CACHE_ITEMS];
num_cached_items_ = 0;
}
int
FloodAgent::command (int argc, const char *const *argv)
{
if (argc == 2)
{
if (strcmp (argv[1], "start-floodagent") == 0)
{
startUp();
return (TCL_OK);
}
if (strcmp (argv[1], "enable-caching") == 0)
{
cache_ = 1;
return (TCL_OK);
}
else if (strcasecmp (argv[1], "ll-queue") == 0)
{
if (!(ll_queue = (PriQueue *) TclObject::lookup (argv[2])))
{
fprintf (stderr, "Flood_Agent: ll-queue lookup of %s failed\n", argv[2]);
return TCL_ERROR;
}
return TCL_OK;
}
}
else if (argc == 3)
{
if (strcasecmp (argv[1], "tracetarget") == 0)
{
TclObject *obj;
if ((obj = TclObject::lookup (argv[2])) == 0)
{
fprintf (stderr, "%s: %s lookup of %s failed\n", __FILE__, argv[1],
argv[2]);
return TCL_ERROR;
}
tracetarget_ = (Trace *) obj;
return TCL_OK;
}
else if (strcasecmp (argv[1], "addr") == 0) {
int temp;
temp = Address::instance().str2addr(argv[2]);
myaddr_ = temp;
return TCL_OK;
}
else if (strcasecmp (argv[1], "attach-tag-dbase") == 0)
{
TclObject *obj;
if ((obj = TclObject::lookup (argv[2])) == 0)
{
fprintf (stderr, "%s: %s lookup of %s failed\n", __FILE__, argv[1],
argv[2]);
return TCL_ERROR;
}
tag_dbase_ = (tags_database *) obj;
return TCL_OK;
}
else if (strcasecmp (argv[1], "node") == 0)
{
assert(node_ == NULL);
TclObject *obj;
if ((obj = TclObject::lookup (argv[2])) == 0)
{
fprintf (stderr, "%s: %s lookup of %s failed\n", __FILE__, argv[1],
argv[2]);
return TCL_ERROR;
}
node_ = (MobileNode *) obj;
return TCL_OK;
}
}
return (Agent::command (argc, argv));
}
void
FloodAgent::startUp()
{
compr_taglist *local_tags0, *t_ptr;
int ntags = 0;
double x,y,z;
node_->getLoc(&x,&y,&z);
// printf("Node %d position: (%f,%f,%f)\n",myaddr_,x,y,z);
// Detection range smaller than transmission range. This is because, if
// the tags are passive, they may not have sufficient energy to re-radiate
// information to the sensor
double r = 60;
local_tags0 = tag_dbase_->Gettags(x,y,r);
trace("Node %d's at (%f,%f,%f) senses tags:",myaddr_,x,y,z);
t_ptr = local_tags0;
ntags = 0;
while(t_ptr) {
trace("tag name: %d.%d.%d",(t_ptr->obj_name_ >> 24) & 0xFF,(t_ptr->obj_name_ >> 16) & 0xFF,(t_ptr->obj_name_) & 0xFFFF);
++ntags;
t_ptr = t_ptr->next_;
}
trace("Number of tags: %d",ntags);
tag_list_ = local_tags0;
}
void
FloodAgent::recv(Packet *p, Handler *)
{
hdr_ip *iph = (hdr_ip *) p->access (off_ip_);
hdr_cmn *cmh = (hdr_cmn *) p->access (off_cmn_);
unsigned char *walk, *X_ptr, *Y_ptr;
compr_taglist *tag_ptr;
int found = FALSE, action, X, Y, obj_name, origin_time, next_hop_level;
int cached = FALSE, cache_index = -1;
int num_src_hops;
double local_x, local_y, local_z;
nsaddr_t last_hop_id;
Scheduler &s = Scheduler::instance();
double now = s.clock();
walk = p->accessdata();
// Type of advertisement
action = *walk++;
X_ptr = walk;
X = *walk++;
X = (X << 8) | *walk++;
Y_ptr = walk;
Y = *walk++;
Y = (Y << 8) | *walk++;
// Used in LM
next_hop_level = *walk++;
obj_name = *walk++;
obj_name = (obj_name << 8) | *walk++;
obj_name = (obj_name << 8) | *walk++;
obj_name = (obj_name << 8) | *walk++;
// origin time of advertisement
origin_time = *walk++;
origin_time = (origin_time << 8) | *walk++;
origin_time = (origin_time << 8) | *walk++;
origin_time = (origin_time << 8) | *walk++;
num_src_hops = *walk++;
num_src_hops = (num_src_hops << 8) | *walk++;
// Query from an agent at our node
if(iph->saddr() == myaddr_ && iph->sport() == 0 && action == QUERY_PKT) {
// Increase the number of source hops to 1
// Add IP header length
cmh->size_ += 20;
}
else {
++num_src_hops;
walk = walk - 2;
(*walk++) = (num_src_hops >> 8) & 0xFF;
(*walk++) = (num_src_hops) & 0XFF;
}
if(num_src_hops) {
last_hop_id = *walk++;
last_hop_id = (last_hop_id << 8) | *walk++;
}
else {
last_hop_id = myaddr_;
walk = walk + 2;
}
if(action == QUERY_PKT) {
walk = walk - 2;
*walk++ = (myaddr_ >> 8) & 0xFF;
*walk++ = myaddr_ & 0xFF;
}
// Packet will be send down the stack
cmh->direction() = hdr_cmn::DOWN;
// Query pkt if X and Y are 65000
if(X == 65000 && Y == 65000) {
// Method returns 1 if query seen before. Otherwise returns 0
// and adds query info to the list
int query_type = search_queries_list(iph->saddr(),obj_name,origin_time,num_src_hops,last_hop_id);
if(query_type == OLD_QRY_ENTRY) {
Packet::free(p);
return;
}
// Check if info is in cache if caching is enabled
if(cache_) {
// If this is a query pkt; check if we have the relevant information
// cached. TTL = 600 seconds for the cache entries
cached = FALSE;
for(int i = 0; i < num_cached_items_; ++i) {
if(tag_cache_[i].obj_name_ == obj_name && tag_cache_[i].origin_time_ > origin_time - 600) {
cached = TRUE;
cache_index = i;
break;
}
}
}
// check if our node has the requested information
tag_ptr = tag_list_;
while(tag_ptr) {
if(tag_ptr->obj_name_ == obj_name) {
found = TRUE;
break;
}
tag_ptr = tag_ptr->next_;
}
if(query_type == OLD_SHORTER_ENTRY && found) {
trace("Node %d: Query received along shorter path",myaddr_);
Packet::free(p);
return;
}
if(found || cached) {
// generate response
// trace("Node %d: Generating response",myaddr_);
if(cached) {
(*X_ptr++) = ((int)tag_cache_[cache_index].X_ >> 8) & 0xFF;
(*X_ptr) = ((int)tag_cache_[cache_index].X_) & 0xFF;
(*Y_ptr++) = ((int)tag_cache_[cache_index].Y_ >> 8) & 0xFF;
(*Y_ptr) = ((int)tag_cache_[cache_index].Y_) & 0xFF;
}
else {
node_->getLoc(&local_x, &local_y, &local_z);
(*X_ptr++) = ((int)local_x >> 8) & 0xFF;
(*X_ptr) = ((int)local_x) & 0xFF;
(*Y_ptr++) = ((int)local_y >> 8) & 0xFF;
(*Y_ptr) = ((int)local_y) & 0xFF;
}
iph->ttl_ = 1000;
iph->daddr() = iph->saddr();
iph->dport() = QUERY_PORT;
cmh->next_hop() = last_hop_id;
cmh->addr_type_ = NS_AF_INET;
// Add 50 bytes to response
cmh->size() += 50;
if(last_hop_id == myaddr_) {
// TEMPORARY HACK! Cant forward from routing agent to some other
// agent on our node!
Packet::free(p);
trace("FloodAgent Found object %d.%d.%d at (%d,%d) at time %f", (obj_name >> 24) & 0xFF, (obj_name >> 16) & 0xFF, obj_name & 0xFFFF,X,Y,now);
return;
}
s.schedule(target_,p,0);
}
else {
// flood pkt
// trace("Node %d: Flooding packet; query type %d",myaddr_,query_type);
if(--iph->ttl_ == 0) {
drop(p, DROP_RTR_TTL);
return;
}
cmh->next_hop_ = IP_BROADCAST; // need to broadcast packet
cmh->addr_type_ = NS_AF_INET;
iph->daddr() = IP_BROADCAST; // packet needs to be broadcast
iph->dport() = ROUTER_PORT;
s.schedule(target_,p,0);
}
}
else {
// Forward response
// trace("Node %d: Forwarding response",myaddr_);
if(--iph->ttl_ == 0) {
drop(p, DROP_RTR_TTL);
return;
}
if(cache_) {
if(num_cached_items_ < MAX_CACHE_ITEMS) {
int replace_index = num_cached_items_;
// If object already exists in cache, update info if necessary
for(int i = 0; i < num_cached_items_; ++i) {
if(tag_cache_[i].obj_name_ == obj_name && tag_cache_[i].origin_time_ < origin_time) {
replace_index = i;
break;
}
}
tag_cache_[replace_index].obj_name_ = obj_name;
tag_cache_[replace_index].origin_time_ = origin_time;
tag_cache_[replace_index].X_ = X;
tag_cache_[replace_index].Y_ = Y;
++num_cached_items_;
}
else {
// Use LRU cache replacement
int replace_index = 0;
int least_time = tag_cache_[replace_index].origin_time_;
for(int i = 0; i < MAX_CACHE_ITEMS; ++i) {
if(tag_cache_[i].origin_time_ < least_time)
replace_index = i;
}
tag_cache_[replace_index].obj_name_ = obj_name;
tag_cache_[replace_index].origin_time_ = origin_time;
tag_cache_[replace_index].X_ = X;
tag_cache_[replace_index].Y_ = Y;
}
}
cmh->next_hop_ = get_next_hop(iph->saddr(),obj_name,origin_time);
assert(cmh->next_hop_ != NO_NEXT_HOP);
s.schedule(target_,p,0);
}
}
int
FloodAgent::search_queries_list(nsaddr_t src, int obj_name, int origin_time, int num_hops, nsaddr_t last_hop_id)
{
QueryList *ql, *newql = NULL, *replql = NULL;
ql = query_list_;
while(ql) {
if(ql->src_ == src && ql->obj_name_ == obj_name && ql->origin_time_ == origin_time) {
if(ql->num_hops_ > num_hops) {
ql->num_hops_ = num_hops;
ql->last_hop_id_ = last_hop_id;
return(OLD_SHORTER_ENTRY);
}
return(OLD_QRY_ENTRY);
}
// Replace very old entries
if(ql->origin_time_ + 100 < origin_time && !replql)
replql = ql;
else if(!replql)
newql = ql;
ql = ql->next_;
}
if(!query_list_) {
query_list_ = new QueryList;
query_list_->src_ = src;
query_list_->obj_name_ = obj_name;
query_list_->origin_time_ = origin_time;
query_list_->num_hops_ = num_hops;
query_list_->last_hop_id_ = last_hop_id;
return(NEW_QRY_ENTRY);
}
if(replql) {
replql->src_ = src;
replql->obj_name_ = obj_name;
replql->origin_time_ = origin_time;
replql->num_hops_ = num_hops;
replql->last_hop_id_ = last_hop_id;
}
else {
newql->next_ = new QueryList;
newql = newql->next_;
newql->src_ = src;
newql->obj_name_ = obj_name;
newql->origin_time_ = origin_time;
newql->num_hops_ = num_hops;
newql->last_hop_id_ = last_hop_id;
}
return(NEW_QRY_ENTRY);
}
nsaddr_t
FloodAgent::get_next_hop(nsaddr_t src, int obj_name, int origin_time)
{
QueryList *ql;
ql = query_list_;
while(ql) {
if(ql->src_ == src && ql->obj_name_ == obj_name && ql->origin_time_ == origin_time) {
return(ql->last_hop_id_);
}
ql = ql->next_;
}
return(NO_NEXT_HOP);
}
landmark.cc
// Author: Satish Kumar, kkumar@isi.edu
extern "C" {
#include
#include
};
#include "landmark.h"
#include
#include
#include
static int lm_index = 0;
void
LMNode::copy_tag_list(compr_taglist *taglist)
{
compr_taglist *tags = NULL;
compr_taglist *tag_ptr1, *tag_ptr2;
// Delete the old tag list if it exists
if(tag_list_) {
tag_ptr1 = tag_list_;
while(tag_ptr1) {
tag_ptr2 = tag_ptr1;
tag_ptr1 = tag_ptr1->next_;
delete tag_ptr2;
}
tag_list_ = NULL;
}
// Copy the specified taglist
tag_ptr1 = taglist;
while(tag_ptr1) {
if(!tag_list_) {
tag_list_ = new compr_taglist;
tag_ptr2 = tag_list_;
}
else {
tag_ptr2->next_ = new compr_taglist;
tag_ptr2 = tag_ptr2->next_;
}
tag_ptr2->obj_name_ = tag_ptr1->obj_name_;
tag_ptr1 = tag_ptr1->next_;
}
}
// Returns a random number between 0 and max
inline double
LandmarkAgent::jitter(double max, int be_random_)
{
return (be_random_ ? Random::uniform(max) : 0);
}
// Returns a random number between 0 and max
inline double
LandmarkAgent::random_timer(double max, int be_random_)
{
return (be_random_ ? rn_->uniform(max) : 0);
}
void
LandmarkAgent::stop()
{
ParentChildrenList *pcl = parent_children_list_, *tmp_pcl;
trace("Node %d: LM Agent going down at time %f",myaddr_,NOW);
// Cancel any running timers and reset relevant variables
if(promo_timer_running_) {
promo_timer_running_ = 0;
promo_timer_->cancel();
}
num_resched_ = 0;
wait_state_ = 0;
total_wait_time_ = 0;
// Reset highest level of node
highest_level_ = 0;
// Delete ParentChildrenList objects for all levels
while(pcl) {
tmp_pcl = pcl;
pcl = pcl->next_;
delete tmp_pcl;
}
parent_children_list_ = NULL;
// Indicate that node is dead so that packets will not be processed
node_dead_ = 1;
global_lm_id_ = NO_GLOBAL_LM;
global_lm_level_ = -1;
// Event id > 0 for scheduled event
if(tag_advt_event_->uid_ > 0) {
Scheduler &s = Scheduler::instance();
s.cancel(tag_advt_event_);
}
}
void LandmarkAgent::
trace (char *fmt,...)
{
va_list ap; // Define a variable ap that will refer to each argument in turn
if (!tracetarget_)
return;
// Initializes ap to first argument
va_start (ap, fmt);
// Prints the elements in turn
vsprintf (tracetarget_->buffer (), fmt, ap);
tracetarget_->dump ();
// Does the necessary clean-up before returning
va_end (ap);
}
static class LandmarkClass:public TclClass
{
public:
LandmarkClass ():TclClass ("Agent/landmark")
{
}
TclObject *create (int, const char *const *)
{
return (new LandmarkAgent ());
}
} class_landmark;
LandmarkAgent::LandmarkAgent (): Agent(PT_MESSAGE), promo_start_time_(0), promo_timeout_(50), promo_timeout_decr_(1), promo_timer_running_(0), seqno_(0), highest_level_(0), parent_children_list_(NULL), ll_queue(0), be_random_(1), wait_state_(0), total_wait_time_(0), debug_(1) ,qry_debug_(0)
{
promo_timer_ = new PromotionTimer(this);
// bind_time ("promo_timeout_", "&promo_timeout_");
num_resched_ = 0;
tag_dbase_ = NULL;
node_ = NULL;
cache_ = 0; // default is to disable caching
tag_cache_ = new TagCache[MAX_CACHE_ITEMS];
num_cached_items_ = 0;
recent_demotion_msgs_ = new RecentMsgRecord[MAX_DEMOTION_RECORDS];
num_demotion_msgs_ = 0;
// default value for the update period
update_period_ = 400;
update_timeout_ = update_period_ + 4 * LM_STARTUP_JITTER;
adverts_type_ = FLOOD; // default is to flood adverts
global_lm_ = 0; // No global LMs by default
global_lm_id_ = NO_GLOBAL_LM;
global_lm_level_ = -1;
// myaddr_ not defined at this point ... So use lm_index for init
rn_ = new RNG(RNG::RAW_SEED_SOURCE,lm_index++);;
// Throw away a bunch of initial values
for(int i = 0; i < 128; ++i) {
rn_->uniform(200);
}
node_dead_ = 0;
bind ("be_random_", &be_random_);
// bind ("myaddr_", &myaddr_);
bind ("debug_", &debug_);
num_nbrs_ = 0;
nbrs_ = NULL;
tag_mobility_ = new TagMobilityHandler(this);
tag_mobility_event_ = new Event;
// myaddr_ not defined at this point ... So use lm_index for init
tag_rng_ = new RNG(RNG::RAW_SEED_SOURCE,lm_index++);;
// Throw away a bunch of initial values
for(int i = 0; i < 128; ++i) {
tag_rng_->uniform(200);
}
mobility_period_ = 60;
mobile_tags_ = NULL;
tag_advt_handler_ = new TagAdvtHandler(this);
tag_advt_event_ = new Event;
}
int
LandmarkAgent::CheckDemotionMsg(nsaddr_t id, int level, int origin_time)
{
int i = 0;
// If object already exists in cache, update info if necessary
for(i = 0; i < num_demotion_msgs_; ++i) {
if(recent_demotion_msgs_[i].id_ == id && recent_demotion_msgs_[i].level_ == level) {
if(recent_demotion_msgs_[i].origin_time_ >= origin_time) {
return(OLD_MESSAGE);
}
else {
recent_demotion_msgs_[i].origin_time_ = origin_time;
return(NEW_ENTRY);
}
}
}
if(num_demotion_msgs_ < MAX_DEMOTION_RECORDS) {
i = num_demotion_msgs_;
++num_demotion_msgs_;
recent_demotion_msgs_[i].id_ = id;
recent_demotion_msgs_[i].level_ = level;
recent_demotion_msgs_[i].origin_time_ = origin_time;
}
else {
// Use LRU cache replacement
int replace_index = 0;
int least_time = recent_demotion_msgs_[replace_index].origin_time_;
for(i = 0; i < MAX_DEMOTION_RECORDS; ++i) {
if(recent_demotion_msgs_[i].origin_time_ < least_time)
replace_index = i;
}
recent_demotion_msgs_[replace_index].id_ = id;
recent_demotion_msgs_[replace_index].level_ = level;
recent_demotion_msgs_[replace_index].origin_time_ = origin_time;
}
return(NEW_ENTRY);
}
void
ParentChildrenList::UpdateChildLMAddr(nsaddr_t id, int num_lm_addrs, int64_t *lm_addrs)
{
LMNode *potl_ch = NULL;
potl_ch = pchildren_;
while(potl_ch) {
if(potl_ch->id_ == id)
break;
potl_ch = potl_ch->next_;
}
assert(potl_ch);
(potl_ch->lmaddr_)->delete_lm_addrs();
for(int i = 0; i < num_lm_addrs; ++i)
(potl_ch->lmaddr_)->add_lm_addr(lm_addrs[i]);
}
int
ParentChildrenList::UpdatePotlParent(nsaddr_t id, nsaddr_t next_hop, int num_hops, int level, int num_children, int energy, int origin_time, int delete_flag)
{
LMNode *potl_parent, *list_ptr;
double now = Scheduler::instance().clock();
// Extract seqnum and origin time
int seqnum = origin_time & 0xFFFF;
origin_time = origin_time >> 16;
assert(num_pparent_ >= 0);
// cannot delete from an empty list!
if(delete_flag && !pparent_)
return(ENTRY_NOT_FOUND);
// if((a_->debug_) && (a_->myaddr_ == 24)) {
// a_->trace("Node %d: Updating Potl Parent level %d, id %d, delete_flag %d, time %f",a_->myaddr_,level_,id,delete_flag,now);
// }
if(pparent_ == NULL) {
pparent_ = new LMNode(id, next_hop, num_hops, level, num_children, energy, origin_time, now);
pparent_->last_upd_seqnum_ = seqnum;
parent_ = pparent_;
++num_pparent_;
return(NEW_ENTRY);
}
list_ptr = pparent_;
potl_parent = list_ptr;
while(list_ptr != NULL) {
if(list_ptr->id_ == id) {
// Check if this is a old message floating around in the network
if(list_ptr->last_upd_origin_time_ > origin_time || (list_ptr->last_upd_origin_time_ == origin_time && list_ptr->last_upd_seqnum_ >= seqnum)) {
// Check if we got the old update on a shorter path
if(list_ptr->num_hops_ > num_hops) {
list_ptr->next_hop_ = next_hop;
list_ptr->num_hops_ = num_hops;
return(OLD_ENTRY);
}
return(OLD_MESSAGE);
}
if(!delete_flag) {
// Make this node as parent if it's closer than current parent
if(parent_->num_hops_ > num_hops + 10 || num_hops == 0) {
parent_ = list_ptr;
}
list_ptr->next_hop_ = next_hop;
list_ptr->num_hops_ = num_hops;
list_ptr->level_ = level;
list_ptr->num_children_ = num_children;
list_ptr->energy_ = energy;
list_ptr->last_upd_origin_time_ = origin_time;
list_ptr->last_upd_seqnum_ = seqnum;
list_ptr->last_update_rcvd_ = Scheduler::instance().clock();
}
else { // delete the entry
if(num_pparent_)
--(num_pparent_);
if(pparent_ == list_ptr)
pparent_ = list_ptr->next_;
else
potl_parent->next_ = list_ptr->next_;
if(parent_->id_ == list_ptr->id_)
assert(parent_ == list_ptr);
// No parent if potl parent list is empty
if(pparent_ == NULL) {
parent_ = NULL;
}
else if(parent_ == list_ptr) {
// Select new parent if current parent is deleted and
// potl parent is not empty; closest potl parent is new parent
LMNode *tmp = pparent_;
int best_num_hops = pparent_->num_hops_;
LMNode *best_parent = pparent_;
while(tmp != NULL) {
if(tmp->num_hops_ < best_num_hops) {
best_num_hops = tmp->num_hops_;
best_parent = tmp;
}
tmp = tmp->next_;
}
parent_ = best_parent;
}
delete list_ptr;
}
return(OLD_ENTRY);
}
potl_parent = list_ptr;
list_ptr = list_ptr->next_;
}
if(delete_flag)
return(ENTRY_NOT_FOUND);
potl_parent->next_ = new LMNode(id, next_hop, num_hops, level, num_children, energy, origin_time, now);
(potl_parent->next_)->last_upd_seqnum_ = seqnum;
++num_pparent_;
// Make this node as parent if it's closer than current parent
if(parent_->num_hops_ > num_hops) {
parent_ = potl_parent->next_;
}
return(NEW_ENTRY);
}
int
ParentChildrenList::UpdatePotlChild(nsaddr_t id, nsaddr_t next_hop, int num_hops, int level, int num_children, int energy, int origin_time, int child_flag, int delete_flag, compr_taglist *taglist)
{
LMNode *potl_child, *list_ptr;
double now = Scheduler::instance().clock();
int new_child = 0;
int tags_changed = 0;
int seqnum = origin_time & 0xFFFF;
origin_time = origin_time >> 16;
// if(a_->debug_) printf("Node %d: Number of potl children %d",a_->myaddr_,num_potl_children_);
// cannot delete from an empty list!
if(delete_flag && !pchildren_) {
return(ENTRY_NOT_FOUND);
}
assert(num_potl_children_ >= 0);
assert(num_children_ >= 0);
if(pchildren_ == NULL) {
pchildren_ = new LMNode(id, next_hop, num_hops, level, num_children, energy, origin_time, now);
pchildren_->child_flag_ = child_flag;
pchildren_->last_upd_seqnum_ = seqnum;
if(child_flag == IS_CHILD) ++(num_children_);
if(child_flag != NOT_POTL_CHILD) ++(num_potl_children_);
++(num_heard_);
pchildren_->copy_tag_list(taglist);
if(child_flag == IS_CHILD)
return(NEW_CHILD);
else
return(NEW_ENTRY);
}
list_ptr = pchildren_;
potl_child = list_ptr;
while(list_ptr != NULL) {
if(list_ptr->id_ == id) {
// Check if this is a old message floating around in the network
if(list_ptr->last_upd_origin_time_ > origin_time || (list_ptr->last_upd_origin_time_ == origin_time && list_ptr->last_upd_seqnum_ >= seqnum)) {
// Check if we got this update on a shorter path; If the update rcvd
// on a shorter path, we need to forward this message to ensure that
// this message reaches all nodes in the advertising node's vicinity
if(list_ptr->num_hops_ > num_hops) {
list_ptr->next_hop_ = next_hop;
list_ptr->num_hops_ = num_hops;
return(OLD_ENTRY);
}
return(OLD_MESSAGE);
}
if(!delete_flag) {
// Old entry but the status has changed to child or vice-versa
if((list_ptr->child_flag_ == NOT_CHILD || list_ptr->child_flag_ == NOT_POTL_CHILD) && (child_flag == IS_CHILD)) {
list_ptr->child_flag_ = child_flag;
++(num_children_);
new_child = 1;
}
else if((list_ptr->child_flag_ == IS_CHILD) && (child_flag == NOT_CHILD || child_flag == NOT_POTL_CHILD)) {
list_ptr->child_flag_ = child_flag;
--(num_children_);
}
list_ptr->next_hop_ = next_hop;
list_ptr->num_hops_ = num_hops;
list_ptr->level_ = level;
list_ptr->num_children_ = num_children;
list_ptr->energy_ = energy;
list_ptr->last_upd_origin_time_ = origin_time;
list_ptr->last_upd_seqnum_ = seqnum;
list_ptr->last_update_rcvd_ = Scheduler::instance().clock();
if(!a_->compare_tag_lists(list_ptr->tag_list_,-1,taglist,-1)) {
tags_changed = 1;
// Delete the old tag list and copy the specified taglist
list_ptr->copy_tag_list(taglist);
}
}
else {
if(pchildren_ == list_ptr)
pchildren_ = list_ptr->next_;
else
potl_child->next_ = list_ptr->next_;
if(list_ptr->child_flag_ == IS_CHILD) --num_children_;
if(list_ptr->child_flag_ != NOT_POTL_CHILD) --num_potl_children_;
--num_heard_;
delete list_ptr;
}
if(new_child)
return(NEW_CHILD);
else if(tags_changed && child_flag == IS_CHILD)
return(OLD_CHILD_TAGS_CHANGED);
else
return(OLD_ENTRY);
}
potl_child = list_ptr;
list_ptr = list_ptr->next_;
}
// delete flag must be FALSE if we are here
// assert(!delete_flag);
if(delete_flag) {
return(ENTRY_NOT_FOUND);
}
potl_child->next_ = new LMNode(id, next_hop, num_hops, level, num_children, energy, origin_time, now);
(potl_child->next_)->copy_tag_list(taglist);
(potl_child->next_)->child_flag_ = child_flag;
(potl_child->next_)->last_upd_seqnum_ = seqnum;
if(child_flag == IS_CHILD) ++(num_children_);
if(child_flag != NOT_POTL_CHILD) ++(num_potl_children_);
++(num_heard_);
if(child_flag == IS_CHILD)
return(NEW_CHILD);
else
return(NEW_ENTRY);
}
void
LandmarkAgent::ProcessHierUpdate(Packet *p)
{
hdr_ip *iph = (hdr_ip *) p->access(off_ip_);
hdr_cmn *hdrc = HDR_CMN(p);
Scheduler &s = Scheduler::instance();
double now = s.clock();
int origin_time = 0;
unsigned char *walk;
nsaddr_t origin_id, parent, next_hop;
int i, level, vicinity_radius, num_hops, potl_parent_flag = FALSE;
int action, energy = 0;
nsaddr_t *potl_children;
int num_children = 0;
int num_potl_children = 0;
int num_lm_addrs = 0;
int num_advert_lm_addrs = 0;
int64_t *advert_lm_addrs = NULL;
int64_t *lm_addrs = NULL;
// Packet will have seqnum, level, vicinity radii, parent info
// and possibly potential children (if the node is at level > 0)
int num_tags = 0;
compr_taglist *adv_tags = NULL, *tag_ptr;
compr_taglist *tag_ptr1, *tag_ptr2;
Packet *newp;
// if(now > 412.5) {
// purify_printf("ProcessHierUpdate1, %f, %d\n",now,myaddr_);
// purify_new_leaks();
// }
// if(debug_)
// trace("Node %d: Received packet from %d with ttl %d", myaddr_,iph->saddr(),iph->ttl_);
walk = p->accessdata();
// Originator of the LM advertisement and the next hop to reach originator
origin_id = iph->saddr();
// Free and return if we are seeing our own packet again
if(origin_id == myaddr_) {
Packet::free(p);
return;
}
// type of advertisement
action = *walk++;
num_advert_lm_addrs = *walk++;
if(num_advert_lm_addrs)
advert_lm_addrs = new int64_t[num_advert_lm_addrs];
for(int j = 0; j < num_advert_lm_addrs; ++j) {
advert_lm_addrs[j] = *walk++;
advert_lm_addrs[j] = (advert_lm_addrs[j] << 8) | *walk++;
advert_lm_addrs[j] = (advert_lm_addrs[j] << 8) | *walk++;
advert_lm_addrs[j] = (advert_lm_addrs[j] << 8) | *walk++;
advert_lm_addrs[j] = (advert_lm_addrs[j] << 8) | *walk++;
advert_lm_addrs[j] = (advert_lm_addrs[j] << 8) | *walk++;
advert_lm_addrs[j] = (advert_lm_addrs[j] << 8) | *walk++;
advert_lm_addrs[j] = (advert_lm_addrs[j] << 8) | *walk++;
}
// level of the originator
level = *walk++;
// if(num_advert_lm_addrs)
// trace("Node 10: Rcved msg from 0, level %d, num_lm_addrs %d, advert_lm_addrs %x, time %f",level,num_advert_lm_addrs,advert_lm_addrs[0],now);
// trace("Node %d: Processing Hierarchy Update Packet", myaddr_);
// if((myaddr_ == 153) && (origin_id == 29)) {
// trace("Node 153: Receiving level %d update from node 29 at time %f,action = %d",level,s.clock(),action);
// }
// energy level of advertising node
energy = *walk++;
energy = (energy << 8) | *walk++;
energy = (energy << 8) | *walk++;
energy = (energy << 8) | *walk++;
// next hop info
next_hop = *walk++;
next_hop = (next_hop << 8) | *walk;
// Change next hop to ourselves for the outgoing packet
--walk;
(*walk++) = (myaddr_ >> 8) & 0xFF;
(*walk++) = (myaddr_) & 0xFF;
// vicinity radius
vicinity_radius = *walk++;
vicinity_radius = (vicinity_radius << 8) | *walk++;
// number of hops away from advertising LM
num_hops = vicinity_radius - (iph->ttl_ - 1);
// if(myaddr_ == 155)
// trace("Node 155: Receiving level %d update from node %d at time %f,action = %d, num_hops = %d",level,origin_id,s.clock(),action,num_hops);
// origin time of advertisement
origin_time = *walk++;
origin_time = (origin_time << 8) | *walk++;
origin_time = (origin_time << 8) | *walk++;
origin_time = (origin_time << 8) | *walk++;
// if(debug_ && myaddr_ == 33)
// trace("Node %d: Processing level %d pkt from %d at t=%f, origin %d, num hops %d", myaddr_,level,iph->saddr_,now,origin_time,num_hops);
// Parent of the originator
parent = *walk++;
parent = parent << 8 | *walk++;
// Number of hops has to be less than vicinity radius to ensure that atleast
// 2 level K LMs see each other if they exist
if(level > 0 && (action == PERIODIC_ADVERTS || action == GLOBAL_ADVERT || action == UNICAST_ADVERT_CHILD || action == UNICAST_ADVERT_PARENT)) {
// Number of children
num_children = *walk++;
num_children = num_children << 8 | *walk++;
// Number of potential children
num_potl_children = *walk++;
num_potl_children = num_potl_children << 8 | *walk++;
// If level of advertising LM > 1, check if we are in potl children list.
// If so, add as potl parent to level - 1
if(num_potl_children) {
potl_children = new nsaddr_t[num_potl_children];
for(i = 0; i < num_potl_children; ++i) {
potl_children[i] = *walk++;
potl_children[i] = potl_children[i] << 8 | *walk++;
int tmp_num_addrs = *walk++;
if(potl_children[i] == myaddr_) {
potl_parent_flag = TRUE;
num_lm_addrs = tmp_num_addrs;
if(num_lm_addrs) {
lm_addrs = new int64_t[num_lm_addrs];
for(int j = 0; j < num_lm_addrs; ++j) {
lm_addrs[j] = *walk++;
lm_addrs[j] = (lm_addrs[j] << 8) | *walk++;
lm_addrs[j] = (lm_addrs[j] << 8) | *walk++;
lm_addrs[j] = (lm_addrs[j] << 8) | *walk++;
lm_addrs[j] = (lm_addrs[j] << 8) | *walk++;
lm_addrs[j] = (lm_addrs[j] << 8) | *walk++;
lm_addrs[j] = (lm_addrs[j] << 8) | *walk++;
lm_addrs[j] = (lm_addrs[j] << 8) | *walk++;
}
}
}
else
walk = walk + tmp_num_addrs*8;
}
}
}
num_tags = 0;
if(action == PERIODIC_ADVERTS || action == GLOBAL_ADVERT || action == UNICAST_ADVERT_CHILD || action == UNICAST_ADVERT_PARENT) {
num_tags = *walk++;
num_tags = (num_tags << 8) | *walk++;
}
adv_tags = NULL;
// Store tag info only if the level of advertising LM is less than
// our highest level; otherwise we dont need this information
if(num_tags && level < highest_level_) {
adv_tags = new compr_taglist;
tag_ptr = adv_tags;
i = 0;
while(i < num_tags) {
if(i) {
tag_ptr->next_ = new compr_taglist;
tag_ptr = tag_ptr->next_;
}
tag_ptr->obj_name_ = *walk++;
tag_ptr->obj_name_ = (tag_ptr->obj_name_ << 8) | *walk++;
tag_ptr->obj_name_ = (tag_ptr->obj_name_ << 8) | *walk++;
tag_ptr->obj_name_ = (tag_ptr->obj_name_ << 8) | *walk++;
++i;
// trace("tag name: %d.%d.%d",(tag_ptr->obj_name_ >> 24) & 0xFF,(tag_ptr->obj_name_ >> 16) & 0xFF,(tag_ptr->obj_name_) & 0xFFFF);
}
}
// if(level == 253)
// trace("Level is 253 at time %f\n",now);
ParentChildrenList **pcl1 = NULL;
ParentChildrenList **pcl2 = NULL;
int found1 = FALSE;
int found2 = FALSE;
ParentChildrenList **pcl = &parent_children_list_;
// Insert parent-child objects for levels: level-1 (if level > 0) & level + 1
while((*pcl) != NULL) {
if((*pcl)->level_ == (level-1)) {
found1 = TRUE;
pcl1 = pcl;
}
if((*pcl)->level_ == (level+1)) {
found2 = TRUE;
pcl2 = pcl;
}
pcl = &((*pcl)->next_);
}
// check if level > 0
if(!found1 && level) {
*pcl = new ParentChildrenList(level-1, this);
pcl1 = pcl;
pcl = &((*pcl)->next_);
}
if(!found2) {
*pcl = new ParentChildrenList(level+1, this);
pcl2 = pcl;
pcl = &((*pcl)->next_);
}
// If level is same as our level, we can decrease the promotion timer
// if it's running provided we havent already heard advertisements from
// this node
int delete_flag = FALSE; // Add the child/potl parent entry
if(action == DEMOTION) delete_flag = TRUE;
int child_flag = NOT_CHILD; // Indicates whether this node is our child
if(parent == myaddr_)
child_flag = IS_CHILD;
else if(action == GLOBAL_ADVERT && num_hops > vicinity_radius)
// The global LM may not be a potential child for us at any level if
// it is farther away than the vicinity radius
child_flag = NOT_POTL_CHILD;
int ret_value = (*pcl2)->UpdatePotlChild(origin_id, next_hop, num_hops, level, num_children, energy, origin_time, child_flag, delete_flag,adv_tags);
// Free packet and return if we have seen this packet before
if(ret_value == OLD_MESSAGE && action != UNICAST_ADVERT_CHILD && action != UNICAST_ADVERT_PARENT) {
if(num_potl_children) delete[] potl_children;
if(num_lm_addrs) delete[] lm_addrs;
if(num_advert_lm_addrs) delete[] advert_lm_addrs;
// Free the tag list
tag_ptr1 = adv_tags;
while(tag_ptr1) {
tag_ptr2 = tag_ptr1;
tag_ptr1 = tag_ptr1->next_;
delete tag_ptr2;
}
Packet::free(p);
return;
}
// Send hierarchy advts if tag list has changed due to new child
// or change in the taglist of an old child
if(ret_value == NEW_CHILD || ret_value == OLD_CHILD_TAGS_CHANGED)
SendChangedTagListUpdate(0,level);
// if(level == highest_level_)
// num_resched_ = (*pcl2)->num_potl_children_ - 1;
// If promotion timer is running, decrement by constant amount
if((ret_value == NEW_ENTRY) && (level == highest_level_) && (action == PERIODIC_ADVERTS || action == UNICAST_ADVERT_CHILD || action == UNICAST_ADVERT_PARENT) && (num_hops < radius(level))) {
// Promotion timer is running but is not in wait state
if(promo_timer_running_ && !wait_state_) {
double resched_time = promo_timeout_ - (now - promo_start_time_) - promo_timeout_decr_;
if(resched_time < 0) resched_time = 0;
// trace("Node %d: Rescheduling timer in ProcessHierUpdate, now %f, st %f, decr %f, resch %f\n", myaddr_, now, promo_start_time_, promo_timeout_decr_, resched_time);
promo_timer_->resched(resched_time);
}
}
// If our parent has demoted itself, we might have to start
// the election process again
if(action == DEMOTION) {
(*pcl1)->UpdatePotlParent(origin_id, next_hop, num_hops, level, num_children, energy, origin_time, TRUE);
if(((*pcl1)->parent_ == NULL) && (!promo_timer_running_ || (promo_timer_running_ && wait_state_)) && (highest_level_ == level-1)) {
// if (debug_) printf("Node %d: sched timer in ProcessHierUpdate\n",myaddr_);
ParentChildrenList *tmp_pcl = parent_children_list_;
while(tmp_pcl) {
if(tmp_pcl->level_ == level) break;
tmp_pcl = tmp_pcl->next_;
}
assert(tmp_pcl);
num_resched_ = tmp_pcl->num_heard_ - 1;
if(num_resched_) {
// Cancel any timer running in wait state
if(promo_timer_running_)
promo_timer_->cancel();
promo_timer_running_ = 1;
wait_state_ = 0;
total_wait_time_ = 0;
promo_timeout_ = random_timer(double(CONST_TIMEOUT + PROMO_TIMEOUT_MULTIPLES * radius(level) + MAX_TIMEOUT/((num_resched_+1) * pow(2,highest_level_+1))), be_random_);
trace("Node %d: Promotion timeout after wait period in ProcessHierUpdate: %f", myaddr_,promo_timeout_);
num_resched_ = 0;
promo_start_time_ = s.clock();
promo_timer_->resched(promo_timeout_);
}
else if(!promo_timer_running_) {
double wait_time = PERIODIC_WAIT_TIME;
promo_timer_running_ = 1;
wait_state_ = 1;
total_wait_time_ += wait_time;
trace("Node %d: Entering wait state in ProcessHierUpdate because of no parent: %f", myaddr_,now);
promo_timer_->resched(wait_time);
}
}
}
// If the advertising LM is a potl parent, add to level-1
// ParentChildrenList object
else if(potl_parent_flag) {
LMNode *old_parent = (*pcl1)->parent_;
(*pcl1)->UpdatePotlParent(origin_id, next_hop, num_hops, level, num_children, energy, origin_time, FALSE);
// If we receive this message from a parent at some level, update
// the assigned addresses
if((((*pcl1)->parent_)->id_ == origin_id) && (level-1 == highest_level_)) {
// if(num_lm_addrs)
// trace("Node %d: Rcvd higher level lm addr from %d at time %f",myaddr_,origin_id,now);
// else
// trace("Node %d: Rcvd higher level lm addr msg with no addrs from %d at time %f",myaddr_,origin_id,now);
((*pcl1)->mylmaddrs_)->delete_lm_addrs();
assign_lmaddress(lm_addrs, num_lm_addrs, (*pcl1)->level_);
}
// Check if parent has changed
int new_advert = 0;
// The first condition may arise if the old parent obj is deleted ... (?)
if((*pcl1)->parent_ == old_parent && old_parent) {
if(((*pcl1)->parent_)->id_ != old_parent->id_)
new_advert = 1;
}
else if((*pcl1)->parent_ != old_parent && (*pcl1)->parent_ && old_parent) {
if(((*pcl1)->parent_)->id_ != old_parent->id_)
new_advert = 1;
}
else if((*pcl1)->parent_ != old_parent)
new_advert = 1;
// Trigger advertisement if parent has changed
if(new_advert && (level-1 <= highest_level_)) {
newp = makeUpdate((*pcl1), HIER_ADVS, PERIODIC_ADVERTS);
s.schedule(target_,newp,0);
(*pcl1)->last_update_sent_ = now;
}
// If a parent has been selected for highest_level_, cancel promotion timer
// (for promotion to highest_level_+1) if it's running
if((level == (highest_level_ + 1)) && ((*pcl1)->parent_ != NULL)) {
if(promo_timer_running_ && !wait_state_) {
trace("Node %d: Promotion timer cancelled at time %f in ProcessHierUpdate\n",myaddr_,s.clock());
promo_timer_->cancel();
total_wait_time_ = 0;
wait_state_ = 1;
double wait_time = PERIODIC_WAIT_TIME;
total_wait_time_ += wait_time;
promo_timer_->sched(wait_time);
}
}
else if(level > 0 && level == highest_level_) {
// Check if the potl parent for highest_level_-1 that we see covers our
// potential children. If so, we can demote ourselves and cancel our
// current promotion timer
pcl = &parent_children_list_;
while((*pcl) != NULL) {
if((*pcl)->level_ == level) {
break;
}
pcl = &((*pcl)->next_);
}
assert(*pcl);
LMNode *lm = (*pcl)->pchildren_;
int is_subset = TRUE;
if((*pcl)->num_potl_children_ > num_potl_children) {
is_subset = FALSE;
lm = NULL;
}
int is_element = FALSE;
while(lm) {
is_element = FALSE;
for(i = 0; i < num_potl_children; ++i)
if(lm->id_ == potl_children[i] && lm->child_flag_ != NOT_POTL_CHILD) {
is_element = TRUE;
break;
}
if(is_element == FALSE && lm->child_flag_ != NOT_POTL_CHILD) {
is_subset = FALSE;
break;
}
lm = lm->next_;
}
// Demotion process
if(is_subset == TRUE) {
--(highest_level_);
delete_flag = TRUE;
if((*pcl1)->parent_)
trace("Node %d: Num potl ch %d, Node %d: Num potl ch %d, time %d",myaddr_, (*pcl)->num_potl_children_,origin_id,num_potl_children,(int)now);
trace("Node %d: Parent before demotion: %d, msg from %d at time %f",myaddr_, ((*pcl1)->parent_)->id_,origin_id,now);
int upd_time = (int)now;
upd_time = (upd_time << 16) | ((*pcl1)->seqnum_ & 0xFFFF);
++((*pcl1)->seqnum_);
(*pcl1)->UpdatePotlParent(myaddr_, 0, 0, 0, 0, 0, upd_time, delete_flag);
if((*pcl1)->parent_)
trace("Node %d: Parent after demotion: %d",myaddr_, ((*pcl1)->parent_)->id_);
upd_time = (int) now;
upd_time = (upd_time << 16) | ((*pcl2)->seqnum_ & 0xFFFF);
++((*pcl2)->seqnum_);
(*pcl2)->UpdatePotlChild(myaddr_, 0, 0, 0, 0, 0, upd_time, IS_CHILD, delete_flag,NULL);
// Send out demotion messages
newp = makeUpdate(*pcl, HIER_ADVS, DEMOTION);
s.schedule(target_, newp, 0);
if(promo_timer_running_ && !wait_state_) {
trace("Node %d: Promotion timer cancelled due to demotion at time %d\n",myaddr_,(int)now);
promo_timer_->cancel();
total_wait_time_ = 0;
wait_state_ = 1;
double wait_time = PERIODIC_WAIT_TIME;
total_wait_time_ += wait_time;
promo_timer_->sched(wait_time);
}
}
}
}
// If new entry, flood advertisements for level > adv LM's level
if(ret_value == NEW_ENTRY) {
ParentChildrenList *tmp_pcl = parent_children_list_;
while(tmp_pcl) {
// New nodes should have an initial wait time of atleast 0.1 seconds
if(tmp_pcl->level_ <= highest_level_ && tmp_pcl->level_ >= level && (now - tmp_pcl->last_update_sent_) > 0.1) {
newp = makeUpdate(tmp_pcl, HIER_ADVS, PERIODIC_ADVERTS);
s.schedule(target_,newp,0);
tmp_pcl->last_update_sent_ = now;
}
tmp_pcl = tmp_pcl->next_;
}
}
if(num_potl_children) delete[] potl_children;
if(num_lm_addrs) delete[] lm_addrs;
if(num_advert_lm_addrs) delete[] advert_lm_addrs;
// Delete tag list
tag_ptr1 = adv_tags;
while(tag_ptr1) {
tag_ptr2 = tag_ptr1;
tag_ptr1 = tag_ptr1->next_;
delete tag_ptr2;
}
// Do not forward demotion message if we have seen this message before
if(action == DEMOTION) {
// if(myaddr_ == 30)
// printf("Am here\n");
if(CheckDemotionMsg(origin_id, level, (int)origin_time) == OLD_MESSAGE) {
Packet::free(p);
return;
}
}
// Do not forward packet if ttl is lower unless the packet is from the global
// LM in which case the packet needs to be flooded throughout the network
if(--iph->ttl_ == 0 && action != GLOBAL_ADVERT) {
// drop(p, DROP_RTR_TTL);
Packet::free(p);
return;
}
// Do not forward if the advertisement is for this node
if((iph->daddr() >> 8) == myaddr_) {
// trace("Node %d: Received unicast advert from %d at level %d for us at time %f",myaddr_,iph->saddr(),level,now);
Packet::free(p);
return;
}
if(action == UNICAST_ADVERT_CHILD) {
hdrc->next_hop() = get_next_hop(iph->daddr(),level);
// if(myaddr_ == 153)
// trace("Node %d: Received child unicast advert from %d to %d at level %d at time %f, next hop %d",myaddr_,iph->saddr(),iph->daddr(),level,now,hdrc->next_hop());
}
else if(action == UNICAST_ADVERT_PARENT) {
// trace("Node %d: Received parent unicast advert from %d to %d at level %d at time %f",myaddr_,iph->saddr(),iph->daddr(),level,now);
hdrc->next_hop() = get_next_hop(iph->daddr(),level+2);
}
assert(hdrc->next_hop() != NO_NEXT_HOP);
// if(now > 412.5) {
// purify_printf("ProcessHierUpdate2\n");
// purify_new_leaks();
// }
// Need to send the packet down the stack
hdrc->direction() = hdr_cmn::DOWN;
// if(debug_) printf("Node %d: Forwarding Hierarchy Update Packet", myaddr_);
s.schedule(target_, p, 0);
}
void
LandmarkAgent::assign_lmaddress(int64_t *lmaddr, int num_lm_addrs, int root_level)
{
ParentChildrenList *tmp_pcl, *cur_pcl = NULL, *child_pcl = NULL;
ParentChildrenList *parent_pcl = NULL;
LMNode *pchild;
int i;
int level = root_level;
// assert(root_level != 0);
while(level >= 0) {
tmp_pcl = parent_children_list_;
while(tmp_pcl) {
if(tmp_pcl->level_ == level)
cur_pcl = tmp_pcl;
if(tmp_pcl->level_ == (level-1))
child_pcl = tmp_pcl;
if(tmp_pcl->level_ == (level+1))
parent_pcl = tmp_pcl;
tmp_pcl = tmp_pcl->next_;
}
assert(cur_pcl);
if(level) assert(child_pcl);
assert(parent_pcl);
// Update LM address at the appropriate level
if(level == root_level) {
(cur_pcl->mylmaddrs_)->delete_lm_addrs();
if(num_lm_addrs) {
for(i = 0; i < num_lm_addrs; ++i) {
(cur_pcl->mylmaddrs_)->add_lm_addr(lmaddr[i]);
}
parent_pcl->UpdateChildLMAddr(myaddr_,num_lm_addrs,lmaddr);
}
}
int num_addrs = 0;
int64_t *assigned_addrs = NULL;
(cur_pcl->mylmaddrs_)->get_lm_addrs(&num_addrs,&assigned_addrs);
if(num_addrs == 0) {
pchild = cur_pcl->pchildren_;
while(pchild) {
if(pchild->child_flag_ == IS_CHILD) {
(pchild->lmaddr_)->delete_lm_addrs();
if(pchild->id_ == myaddr_ && level)
(child_pcl->mylmaddrs_)->delete_lm_addrs();
}
pchild = pchild->next_;
}
}
else if(cur_pcl->num_children_) {
// ID at a particular level starts from 1
for(i = 0; i < num_addrs; ++i) {
int64_t j = 1;
int64_t addr = assigned_addrs[i] + (j << ((cur_pcl->level_-1)*8));
// Assign addresses to child nodes
// while( j <= MAX_CHILDREN) {
pchild = cur_pcl->pchildren_;
assert(cur_pcl->num_children_ <= MAX_CHILDREN);
while(pchild) {
if(pchild->child_flag_ == IS_CHILD) {
(pchild->lmaddr_)->delete_lm_addrs();
(pchild->lmaddr_)->add_lm_addr(addr);
if(pchild->id_ == myaddr_ && level) {
(child_pcl->mylmaddrs_)->delete_lm_addrs();
(child_pcl->mylmaddrs_)->add_lm_addr(addr);
}
++j;
addr = assigned_addrs[i] + (j << ((cur_pcl->level_-1)*8));
// if(j > MAX_CHILDREN) break;
assert(j <= MAX_CHILDREN);
} /* if */
pchild = pchild->next_;
// }/* while */
}
}
}
if(num_addrs) delete[] assigned_addrs;
--level;
}
}
void
LandmarkAgent::periodic_callback(Event *e, int level)
{
// if(debug_) printf("Periodic Callback for level %d", level);
Scheduler &s = Scheduler::instance();
double now = Scheduler::instance().clock(), next_update_delay;
int energy = (int)(node_->energy());
int unicast_flag = FALSE, suppress_flag = FALSE;
Packet *newp;
hdr_ip *iph, *new_iph;
hdr_cmn *cmh, *new_cmh;
int action = PERIODIC_ADVERTS, parent_changed = 0, child_changed = 0;
int upd_time = (int) now;
// if(now > 412.5) {
// purify_printf("periodic_callback1: %f,%d\n",now,myaddr_);
// purify_new_leaks();
// }
// if(myaddr_ == 12 && now > 402)
// purify_new_leaks();
// Should always have atleast the level 0 object
assert(parent_children_list_ != NULL);
ParentChildrenList *pcl = parent_children_list_;
ParentChildrenList *cur_pcl = NULL;
ParentChildrenList *new_pcl = NULL;
ParentChildrenList *pcl1 = NULL;
ParentChildrenList *pcl2 = NULL;
// return if we have been demoted from this level
if(highest_level_ < level)
return;
while(pcl != NULL) {
new_pcl = pcl;
if(pcl->level_ == level){
cur_pcl = pcl;
}
if(pcl->level_ == (level - 1)){
pcl1 = pcl;
}
if(pcl->level_ == (level + 1)){
pcl2 = pcl;
}
pcl = pcl->next_;
}
assert(cur_pcl);
if(level)
assert(pcl1);
// Create level+1 object if it doesnt exist
if(!pcl2) {
new_pcl->next_ = new ParentChildrenList(level+1, this);
pcl2 = new_pcl->next_;
}
assert(pcl2);
// Delete stale potential parent entries
LMNode *lmnode = cur_pcl->pparent_;
LMNode *tmp_lmnode;
int delete_flag = TRUE;
LMNode *old_parent = cur_pcl->parent_;
while(lmnode) {
// Record next entry in linked list incase the current element is deleted
tmp_lmnode = lmnode->next_;
if(((now - lmnode->last_update_rcvd_) > cur_pcl->update_timeout_)) {
// if(debug_) trace("Node %d: Deleting stale entry for %d at time %d",myaddr_,lmnode->id_,(int)now);
upd_time = (int) now;
upd_time = (upd_time << 16) | ((lmnode->last_upd_seqnum_ + 1) & 0xFFFF);
cur_pcl->UpdatePotlParent(lmnode->id_, 0, 0, 0, 0, 0, upd_time, delete_flag);
}
lmnode = tmp_lmnode;
}
// The first condition may arise if the old parent obj is deleted ... (?)
if(cur_pcl->parent_ == old_parent && old_parent) {
if((cur_pcl->parent_)->id_ != old_parent->id_)
parent_changed = 1;
}
else if(cur_pcl->parent_ != old_parent && cur_pcl->parent_ && old_parent) {
if((cur_pcl->parent_)->id_ != old_parent->id_)
parent_changed = 1;
}
else if(cur_pcl->parent_ != old_parent)
parent_changed = 1;
// Delete stale potential children entries
lmnode = cur_pcl->pchildren_;
delete_flag = TRUE;
int demotion = FALSE;
while(lmnode) {
// Record next entry in linked list incase the current element is deleted
tmp_lmnode = lmnode->next_;
if((now - lmnode->last_update_rcvd_) > cur_pcl->update_timeout_) {
if(lmnode->child_flag_ == IS_CHILD)
child_changed = 1;
assert(level && lmnode->id_ != myaddr_);
upd_time = (int) now;
upd_time = (upd_time << 16) | ((lmnode->last_upd_seqnum_ + 1) & 0xFFFF);
cur_pcl->UpdatePotlChild(lmnode->id_, 0, 0, 0, 0, 0, upd_time, lmnode->child_flag_, delete_flag,NULL);
}
lmnode = tmp_lmnode;
}
// Send updates if tag list has changed i.e., when a child has changed
if(child_changed)
SendChangedTagListUpdate(0,level-1);
// Demote ourself if any child's energy > 30 % of our energy
if(demotion) {
trace("Node %d: Demotion due to lesser energy than child",myaddr_);
highest_level_ = level - 1;
Packet *p = makeUpdate(cur_pcl, HIER_ADVS, DEMOTION);
s.schedule(target_, p, 0);
}
// Check if a parent exists after updating potl parents. If not, start
// promotion timer.
// A LM at level 3 is also at levels 0, 1 and 2. For each of these levels,
// the LM designates itself as parent. At any given instant, only the
// level 3 (i.e., highest_level_) LM may not have a parent and may need to
// promote itself. But if the promotion timer is running, then the election
// process for the next level has already begun.
if(parent_changed && (cur_pcl->parent_ == NULL) && !demotion) {
// Cancel any promotion timer that is running for promotion from a higher
// level and send out demotion messages
if(promo_timer_running_ && level <= highest_level_) {
wait_state_ = 0;
total_wait_time_ = 0;
promo_timer_running_ = 0;
promo_timer_->cancel();
}
num_resched_ = pcl2->num_heard_ - 1;
if(num_resched_) {
promo_timer_running_ = 1;
wait_state_ = 0;
total_wait_time_ = 0;
promo_timeout_ = random_timer(double(CONST_TIMEOUT + PROMO_TIMEOUT_MULTIPLES * radius(level+1) + MAX_TIMEOUT/((num_resched_+1) * pow(2,highest_level_+1))), be_random_);
trace("Node %d: Promotion timeout after wait period in periodic_callback: %f", myaddr_,promo_timeout_);
num_resched_ = 0;
promo_start_time_ = s.clock();
promo_timer_->resched(promo_timeout_);
}
else {
double wait_time = PERIODIC_WAIT_TIME;
promo_timer_running_ = 1;
wait_state_ = 1;
total_wait_time_ += wait_time;
// trace("Node %d: Entering wait period in periodic_callback at time %f", myaddr_,now);
promo_timer_->resched(wait_time);
}
}
// Update ourself as potential child and parent for appropriate levels
// in our hierarchy tables
if(!demotion) {
if(level) {
upd_time = (int) now;
upd_time = (upd_time << 16) | (pcl1->seqnum_ & 0xFFFF);
++(pcl1->seqnum_);
pcl1->UpdatePotlParent(myaddr_, myaddr_, 0, level, cur_pcl->num_children_, energy, upd_time, FALSE);
}
upd_time = (int) now;
upd_time = (upd_time << 16) | (pcl2->seqnum_ & 0xFFFF);
++(pcl2->seqnum_);
pcl2->UpdatePotlChild(myaddr_, myaddr_, 0, level, cur_pcl->num_children_, energy, upd_time, IS_CHILD, FALSE,cur_pcl->tag_list_);
}
// Check if this is the root node. If so, set the unicast flag or suppress
// flag when no changes occur for 3 times the update period
// If this is a lower level node that has a parent, either suppress
// (for hard-state case) or unicast maintenance messages
if(!(cur_pcl->parent_) && (total_wait_time_ >= (2*PERIODIC_WAIT_TIME)) && wait_state_) {
if(adverts_type_ == UNICAST) {
unicast_flag = TRUE;
}
else if(adverts_type_ == SUPPRESS) {
suppress_flag = TRUE;
}
// Start assigning landmark addresses to child nodes;
// Shift 1, number of levels * 8 times to left for address of root node
int64_t *lmaddr = new int64_t[1];
lmaddr[0] = 1;
lmaddr[0] = (lmaddr[0] << (cur_pcl->level_ * 8));
int num_lm_addrs = 1;
assign_lmaddress(lmaddr, num_lm_addrs, cur_pcl->level_);
// The advertisements from the root LM need to be broadcast in the hash
// scheme
delete[] lmaddr;
if(global_lm_)
action = GLOBAL_ADVERT;
}
else if(cur_pcl->parent_) {
if(adverts_type_ == UNICAST) {
unicast_flag = TRUE;
}
else if(adverts_type_ == SUPPRESS) {
suppress_flag = TRUE;
}
}
// if(!demotion && (now - cur_pcl->last_update_sent_ >= cur_pcl->update_period_) && !suppress_flag)
if(!demotion && !suppress_flag) {
// trace("Node %d: Sending update at time %f",myaddr_,now);
Packet *p = makeUpdate(cur_pcl, HIER_ADVS, action);
unsigned char *walk;
if(unicast_flag) {
if(level) {
// Unicast updates to parent and children for level > 0
lmnode = cur_pcl->pchildren_;
while(lmnode) {
if(lmnode->id_ != myaddr_) {
newp = p->copy();
new_iph = (hdr_ip *) newp->access(off_ip_);
new_cmh = (hdr_cmn *) newp->access (off_cmn_);
walk = newp->accessdata();
// trace("Node %d: Generating unicast advert to child %d at time %f with next hop %d",myaddr_,lmnode->id_,now,lmnode->next_hop_);
new_iph->daddr() = lmnode->id_ << 8;
new_iph->dport() = ROUTER_PORT;
new_cmh->next_hop() = lmnode->next_hop_;
new_cmh->addr_type() = NS_AF_INET;
if(cur_pcl->level_)
new_cmh->size() = new_cmh->size() - 4 * (cur_pcl->num_potl_children_ - 1);
(*walk) = (UNICAST_ADVERT_CHILD) & 0xFF;
walk++;
int num_addrs = (*walk);
walk += (10 + 8*num_addrs);
// Update seqnum field for each packet; Otherwise subsequent
// (to first) messages would be dropped by intermediate nodes
(*walk++) = (cur_pcl->seqnum_ >> 24) & 0xFF;
(*walk++) = (cur_pcl->seqnum_ >> 16) & 0xFF;
(*walk++) = (cur_pcl->seqnum_ >> 8) & 0xFF;
(*walk++) = (cur_pcl->seqnum_) & 0xFF;
++(cur_pcl->seqnum_);
s.schedule(target_,newp,0);
}
lmnode = lmnode->next_;
}
}
if(cur_pcl->parent_) {
if((cur_pcl->parent_)->id_ != myaddr_) {
iph = (hdr_ip *) p->access(off_ip_);
cmh = (hdr_cmn *) p->access (off_cmn_);
walk = p->accessdata();
// trace("Node %d: Generating unicast advert to parent %d at time %f with next hop %d",myaddr_,cur_pcl->parent_->id_,now,(cur_pcl->parent_)->next_hop_);
iph->daddr() = (cur_pcl->parent_)->id_;
iph->dport() = ROUTER_PORT;
cmh->next_hop() = (cur_pcl->parent_)->next_hop_;
cmh->addr_type() = NS_AF_INET;
cmh->size() = cmh->size() - 4 * (cur_pcl->num_potl_children_);
(*walk) = (UNICAST_ADVERT_PARENT) & 0xFF;
walk++;
int num_addrs = (*walk);
walk += (10 + 8*num_addrs);
// Update seqnum field for each packet; Otherwise subsequent
// (to first) messages would be dropped by intermediate nodes
(*walk++) = (cur_pcl->seqnum_ >> 24) & 0xFF;
(*walk++) = (cur_pcl->seqnum_ >> 16) & 0xFF;
(*walk++) = (cur_pcl->seqnum_ >> 8) & 0xFF;
(*walk++) = (cur_pcl->seqnum_) & 0xFF;
++(cur_pcl->seqnum_);
s.schedule(target_,p,0);
}
else
Packet::free(p);
}
else
Packet::free(p);
}
else {
// trace("Node %d: Generating update msg at time %f",myaddr_,now);
s.schedule(target_, p, 0);
}
cur_pcl->last_update_sent_ = now;
}
// Schedule next update
if(cur_pcl->last_update_sent_ == now || suppress_flag)
next_update_delay = cur_pcl->update_period_ + jitter(LM_STARTUP_JITTER, be_random_);
else
next_update_delay = cur_pcl->update_period_ - (now - cur_pcl->last_update_sent_) + jitter(LM_STARTUP_JITTER, be_random_);
if(!demotion)
s.schedule(cur_pcl->periodic_handler_, cur_pcl->periodic_update_event_, next_update_delay);
// if(now > 412.5) {
// purify_printf("periodic_callback2: %f,%d\n",now,myaddr_);
// purify_new_leaks();
// }
// if(myaddr_ == 12 && now > 402)
// purify_new_leaks();
// Update entries for levels greater than our highest level in our
// highest_level_ periodic_callback event
if(level == highest_level_) {
cur_pcl = parent_children_list_;
while(cur_pcl) {
if(cur_pcl->level_ > highest_level_) {
lmnode = cur_pcl->pparent_;
delete_flag = TRUE;
while(lmnode) {
// Update potential parent list
// Record next entry in list incase current element is deleted
tmp_lmnode = lmnode->next_;
if(((now - lmnode->last_update_rcvd_) > cur_pcl->update_timeout_)) {
// if(debug_) trace("Node %d: Deleting stale entry for %d at time %d",myaddr_,lmnode->id_,(int)now);
upd_time = (int) now;
upd_time = (upd_time << 16) | ((lmnode->last_upd_seqnum_+1) & 0xFFFF);
cur_pcl->UpdatePotlParent(lmnode->id_, 0, 0, 0, 0, 0, upd_time, delete_flag);
}
lmnode = tmp_lmnode;
}
// Update children list
lmnode = cur_pcl->pchildren_;
while(lmnode) {
// Record next entry in list incase current element is deleted
tmp_lmnode = lmnode->next_;
if((now - lmnode->last_update_rcvd_) > cur_pcl->update_timeout_) {
upd_time = (int) now;
upd_time = (upd_time << 16) | ((lmnode->last_upd_seqnum_+1) & 0xFFFF);
// Check if global LM entry is being deleted; Global LM at level i
// will have entry in level i+1 pcl object
if(cur_pcl->level_ == global_lm_level_+1 && lmnode->id_ == global_lm_id_) {
global_lm_level_ = -1;
global_lm_id_ = NO_GLOBAL_LM;
}
cur_pcl->UpdatePotlChild(lmnode->id_, 0, 0, 0, 0, 0, upd_time, lmnode->child_flag_, delete_flag,NULL);
}
lmnode = tmp_lmnode;
}
}
cur_pcl = cur_pcl->next_;
}
}
}
Packet *
LandmarkAgent::makeUpdate(ParentChildrenList *pcl, int pkt_type, int action)
{
Packet *p = allocpkt();
hdr_ip *iph = (hdr_ip *) p->access(off_ip_);
hdr_cmn *hdrc = HDR_CMN(p);
unsigned char *walk;
compr_taglist *adv_tags = NULL;
double now = Scheduler::instance().clock();
int64_t *lmaddrs;
int num_lm_addrs = 0;
hdrc->next_hop_ = IP_BROADCAST; // need to broadcast packet
hdrc->addr_type_ = NS_AF_INET;
iph->daddr() = IP_BROADCAST; // packet needs to be broadcast
iph->dport() = ROUTER_PORT;
iph->ttl_ = radius(pcl->level_);
iph->saddr() = myaddr_;
iph->sport() = ROUTER_PORT;
if(action == GLOBAL_ADVERT)
trace("Node %d: Generating global LM message at time %f",myaddr_,now);
assert(pcl->num_tags_ >= 0);
if(pkt_type == HIER_ADVS) {
if(pcl->level_ == 0) {
// A level 0 node cannot be demoted!
assert(action != DEMOTION);
// No children for level 0 LM
// totally 12 bytes in packet for now; need to add our energy level later
// each tag name is 4 bytes; 2 bytes for num_tags info
// Landmark address; 1 byte to indicate #addrs and 8 bytes for each addr
lmaddrs = NULL;
num_lm_addrs = 0;
(pcl->mylmaddrs_)->get_lm_addrs(&num_lm_addrs, &lmaddrs);
p->allocdata(12 + (4 * pcl->num_tags_) + 2 + 4 + 1 + (8*num_lm_addrs));
walk = p->accessdata();
// Packet type; 1 byte
(*walk++) = (action) & 0xFF;
// Landmark address; 1 byte to indicate #addrs and 8 bytes for each addr
(*walk++) = (num_lm_addrs) & 0xFF;
// if(num_lm_addrs)
// trace("Num_lm_addrs = %d",num_lm_addrs);
for(int i = 0; i < num_lm_addrs; ++i) {
// Landmark address of node
(*walk++) = (lmaddrs[i] >> 56) & 0xFF;
(*walk++) = (lmaddrs[i] >> 48) & 0xFF;
(*walk++) = (lmaddrs[i] >> 40) & 0xFF;
(*walk++) = (lmaddrs[i] >> 32) & 0xFF;
(*walk++) = (lmaddrs[i] >> 24) & 0xFF;
(*walk++) = (lmaddrs[i] >> 16) & 0xFF;
(*walk++) = (lmaddrs[i] >> 8) & 0xFF;
(*walk++) = (lmaddrs[i]) & 0xFF;
}
if(num_lm_addrs)
delete[] lmaddrs;
// level of LM advertisement; 1 byte
(*walk++) = (pcl->level_) & 0xFF;
// Our energy level; 4 bytes (just integer portion)
int energy = (int)(node_->energy());
(*walk++) = (energy >> 24) & 0xFF;
(*walk++) = (energy >> 16) & 0xFF;
(*walk++) = (energy >> 8) & 0xFF;
(*walk++) = (energy) & 0xFF;
// make ourselves as next hop; 2 bytes
(*walk++) = (myaddr_ >> 8) & 0xFF;
(*walk++) = (myaddr_) & 0xFF;
// Vicinity size in number of hops; Carrying this information allows
// different LMs at same level to have different vicinity radii; 2 bytes
(*walk++) = (radius(pcl->level_) >> 8) & 0xFF;
(*walk++) = (radius(pcl->level_)) & 0xFF;
// Time at which packet was originated;
// 3 bytes for integer portion of time and 1 byte for fraction
// Note that we probably need both an origin_time and sequence number
// field to distinguish between msgs generated at same time.
// (origin_time required to discard old state when net dynamics present)
int origin_time = (int)now;
(*walk++) = (origin_time >> 8) & 0xFF;
(*walk++) = (origin_time) & 0xFF;
(*walk++) = (pcl->seqnum_ >> 8) & 0xFF;
(*walk++) = (pcl->seqnum_) & 0xFF;
++(pcl->seqnum_);
// Parent ID; 2 bytes
if(pcl->parent_ == NULL) {
(*walk++) = (NO_PARENT >> 8) & 0xFF;
(*walk++) = (NO_PARENT) & 0xFF;
}
else {
(*walk++) = ((pcl->parent_)->id_ >> 8) & 0xFF;
(*walk++) = ((pcl->parent_)->id_) & 0xFF;
}
(*walk++) = (pcl->num_tags_ >> 8) & 0xFF;
(*walk++) = (pcl->num_tags_) & 0xFF;
if(pcl->num_tags_) {
adv_tags = pcl->tag_list_;
while(adv_tags) {
(*walk++) = (adv_tags->obj_name_ >> 24) & 0xFF;
(*walk++) = (adv_tags->obj_name_ >> 16) & 0xFF;
(*walk++) = (adv_tags->obj_name_ >> 8) & 0xFF;
(*walk++) = (adv_tags->obj_name_) & 0xFF;
adv_tags = adv_tags->next_;
}
}
// In real life each of the above fields would be
// 4 byte integers; 20 bytes for IP addr + 2 bytes for num_children
// 4 byte for number of tags; 4 byte for each tag name; 4 byte for energy
hdrc->size_ = 20 + 20 + 4 + (4 * pcl->num_tags_) + 4 + 1 + (8*num_lm_addrs);
}
else {
// Need to list all the potential children LMs
// Pkt size: 12 bytes (as before); 2 each for number of children
// and potl_children;
// 2 byte for each child's id + 8 bytes for landmark address
// 4 bytes for each tag name; 2 bytes for num_tags info
int pkt_size = 0;
lmaddrs = NULL;
num_lm_addrs = 0;
if(action == PERIODIC_ADVERTS || action == GLOBAL_ADVERT) {
LMNode *pch = pcl->pchildren_;
// Compute number of landmark addrs of children for pkt size calc
int size = 0;
while(pch) {
int64_t *addrs;
int num_addrs = 0;
(pch->lmaddr_)->get_lm_addrs(&num_addrs,&addrs);
if(num_addrs) delete[] addrs;
size += (1 + num_addrs*8);
pch = pch->next_;
}
// Compute number of landmark addrs of this node for pkt size calc
// Landmark address; 1 byte to indicate #addrs and 8 bytes for each
// addr
(pcl->mylmaddrs_)->get_lm_addrs(&num_lm_addrs, &lmaddrs);
pkt_size = 12 + 4 + (2 * pcl->num_potl_children_) + size + (4 * pcl->num_tags_) + 2 + 4 + 1 + (8*num_lm_addrs);
}
else
pkt_size = 17; // Demotion message
p->allocdata(pkt_size);
walk = p->accessdata();
// Packet type; 1 byte
(*walk++) = (action) & 0xFF;
// if(myaddr_ == 0)
// trace("Node 0: Generating update message for level %d at time %f, num_lm_addrs %d",pcl->level_,now,num_lm_addrs);
// Landmark address; 1 byte to indicate #addrs and 8 bytes for each addr
(*walk++) = (num_lm_addrs) & 0xFF;
for(int i = 0; i < num_lm_addrs; ++i) {
// Landmark address of node
(*walk++) = (lmaddrs[i] >> 56) & 0xFF;
(*walk++) = (lmaddrs[i] >> 48) & 0xFF;
(*walk++) = (lmaddrs[i] >> 40) & 0xFF;
(*walk++) = (lmaddrs[i] >> 32) & 0xFF;
(*walk++) = (lmaddrs[i] >> 24) & 0xFF;
(*walk++) = (lmaddrs[i] >> 16) & 0xFF;
(*walk++) = (lmaddrs[i] >> 8) & 0xFF;
(*walk++) = (lmaddrs[i]) & 0xFF;
}
if(num_lm_addrs)
delete[] lmaddrs;
// level of LM advertisement; 1 byte
(*walk++) = (pcl->level_) & 0xFF;
// Our energy level; 4 bytes (just integer portion)
int energy = (int)(node_->energy());
(*walk++) = (energy >> 24) & 0xFF;
(*walk++) = (energy >> 16) & 0xFF;
(*walk++) = (energy >> 8) & 0xFF;
(*walk++) = (energy) & 0xFF;
// make ourselves as next hop; 2 bytes
(*walk++) = (myaddr_ >> 8) & 0xFF;
(*walk++) = (myaddr_) & 0xFF;
// Vicinity size in number of hops; 2 bytes
(*walk++) = (radius(pcl->level_) >> 8) & 0xFF;
(*walk++) = (radius(pcl->level_)) & 0xFF;
// Time at which packet was originated;
// 3 bytes for integer portion of time and 1 byte for fraction
int origin_time = (int)now;
(*walk++) = (origin_time >> 8) & 0xFF;
(*walk++) = (origin_time) & 0xFF;
(*walk++) = (pcl->seqnum_ >> 8) & 0xFF;
(*walk++) = (pcl->seqnum_) & 0xFF;
++(pcl->seqnum_);
if(origin_time > now) {
printf("Node %d: id %d, level %d, vicinity_radius %d",myaddr_,myaddr_,pcl->level_,radius(pcl->level_));
assert(origin_time < now);
}
// Parent's id; 2 bytes
if(pcl->parent_ == NULL) {
(*walk++) = (NO_PARENT >> 8) & 0xFF;
(*walk++) = (NO_PARENT) & 0xFF;
}
else {
(*walk++) = ((pcl->parent_)->id_ >> 8) & 0xFF;
(*walk++) = ((pcl->parent_)->id_) & 0xFF;
}
if(action == PERIODIC_ADVERTS || action == GLOBAL_ADVERT) {
// Number of children; 2 bytes
(*walk++) = (pcl->num_children_ >> 8) & 0xFF;
(*walk++) = (pcl->num_children_) & 0xFF;
// Number of potential children; 2 bytes
(*walk++) = (pcl->num_potl_children_ >> 8) & 0xFF;
(*walk++) = (pcl->num_potl_children_) & 0xFF;
LMNode *potl_ch = pcl->pchildren_;
while(potl_ch) {
if(potl_ch->child_flag_ != NOT_POTL_CHILD) {
(*walk++) = (potl_ch->id_ >> 8) & 0xFF;
(*walk++) = (potl_ch->id_) & 0xFF;
int64_t *addrs = NULL;
int num_addrs = 0;
((potl_ch)->lmaddr_)->get_lm_addrs(&num_addrs, &addrs);
// if(myaddr_ == 0 && now > 1000)
// trace("Node 0: Child %d, num_addrs: %d at time %f",potl_ch->id_,num_addrs,now);
// Number of landmark addrs
(*walk++) = (num_addrs) & 0xFF;
for(int i = 0; i < num_addrs; ++i) {
// Landmark address of node
(*walk++) = (addrs[i] >> 56) & 0xFF;
(*walk++) = (addrs[i] >> 48) & 0xFF;
(*walk++) = (addrs[i] >> 40) & 0xFF;
(*walk++) = (addrs[i] >> 32) & 0xFF;
(*walk++) = (addrs[i] >> 24) & 0xFF;
(*walk++) = (addrs[i] >> 16) & 0xFF;
(*walk++) = (addrs[i] >> 8) & 0xFF;
(*walk++) = (addrs[i]) & 0xFF;
}
if(num_addrs)
delete[] addrs;
}
potl_ch = potl_ch->next_;
}
(*walk++) = (pcl->num_tags_ >> 8) & 0xFF;
(*walk++) = (pcl->num_tags_) & 0xFF;
if(pcl->num_tags_) {
adv_tags = pcl->tag_list_;
while(adv_tags) {
(*walk++) = (adv_tags->obj_name_ >> 24) & 0xFF;
(*walk++) = (adv_tags->obj_name_ >> 16) & 0xFF;
(*walk++) = (adv_tags->obj_name_ >> 8) & 0xFF;
(*walk++) = (adv_tags->obj_name_) & 0xFF;
adv_tags = adv_tags->next_;
}
}
// 8 addl bytes for num_children and num_potl_children info; Assuming
// worst case of 8 levels in computing packet size
// SHOULD DISABLE SENDING TAG INFO IN THE HASH SCHEME
// Landmark address; 1 byte to indicate #addrs and 8 bytes
// for each addr
hdrc->size_ = 20 + 8 + ((4+8) * pcl->num_potl_children_) + 20 + 4 + (4 * pcl->num_tags_) + 4 + 1 + (8 * num_lm_addrs);
// In real life each of the above fields would be
// 4 byte integers; 20 bytes for IP addr
// if(myaddr_ == 11)
// trace("Node 11: Packet size: %d",hdrc->size_);
}
else if(action == DEMOTION) {
hdrc->size_ = 20 + 20;
}
}
}
// Optimization for reducing energy consumption; Just advertise
// sequence number in steady state
// if(pcl->parent_ == NULL && action != DEMOTION)
// hdrc->size_ = 20 + 4;
// Cancel periodic_callback event if node is being demoted
if(action == DEMOTION && pcl->periodic_update_event_->uid_)
Scheduler::instance().cancel(pcl->periodic_update_event_);
hdrc->direction() = hdr_cmn::DOWN;
return p;
}
int
LandmarkAgent::radius(int level)
{
// level i's radius >= (2 *level i-1's radius) + 1
return((int(pow(2,level+1) + pow(2,level) - 1)));
// return((level + 1)*2 + 1);
// return(int(pow(2,level+1)) + 1);
}
ParentChildrenList::ParentChildrenList(int level, LandmarkAgent *a) : parent_(NULL), num_heard_(0), num_children_(0), num_potl_children_(0), num_pparent_(0), pchildren_(NULL), pparent_(NULL) , seqnum_(0) ,last_update_sent_(-(a->update_period_)), update_period_(a->update_period_), update_timeout_(a->update_timeout_), next_(NULL)
{
level_ = level;
periodic_update_event_ = new Event;
periodic_handler_ = new LMPeriodicAdvtHandler(this);
a_ = a;
tag_list_ = NULL;
num_tags_ = 0;
adverts_type_ = FLOOD; // default is to flood adverts
mylmaddrs_ = new LMAddrs;
}
void
PromotionTimer::expire(Event *e) {
ParentChildrenList *pcl = a_->parent_children_list_;
ParentChildrenList *new_pcl, *higher_level_pcl = NULL, *lower_level_pcl;
ParentChildrenList *pcl1 = NULL; // Pointer to new highest_level_-1 object
ParentChildrenList *pcl2 = NULL; // Pointer to new highest_level_+1 object
ParentChildrenList *cur_pcl = NULL;
int found = FALSE, has_parent = FALSE;
int64_t *my_lm_addrs = NULL;
int num_my_lm_addrs = 0;
int num_potl_ch = 0;
int addr_changed = 0;
Scheduler &s = Scheduler::instance();
double now = s.clock();
Packet *p, *newp;
// if(now > 412.5) {
// purify_printf("expire1: %f,%d\n",now,a_->myaddr_);
// purify_new_leaks();
// }
while(pcl != NULL) {
if(pcl->level_ == (a_->highest_level_ + 1)) {
// Exclude ourself from the count of the lower level nodes heard
higher_level_pcl = pcl;
a_->num_resched_ = pcl->num_heard_ - 1;
num_potl_ch = pcl->num_potl_children_;
}
else if(pcl->level_ == a_->highest_level_) {
cur_pcl = pcl;
if(pcl->parent_) {
has_parent = TRUE;
}
}
else if(pcl->level_ == (a_->highest_level_-1)) {
lower_level_pcl = pcl;
}
pcl = pcl->next_;
}
assert(higher_level_pcl);
if(a_->highest_level_)
assert(lower_level_pcl);
assert(cur_pcl);
if(a_->wait_state_) {
if(a_->num_resched_ && !has_parent) {
a_->wait_state_ = 0;
a_->total_wait_time_ = 0;
// Promotion timeout is num_resched_ times the estimated time for
// a message to reach other nodes in its vicinity
// PROM0_TIMEOUT_MULTIPLE is an estimate of time for adv to reach
// all nodes in vicinity
a_->promo_timeout_ = a_->random_timer(double(CONST_TIMEOUT + PROMO_TIMEOUT_MULTIPLES * a_->radius(a_->highest_level_) + MAX_TIMEOUT/((a_->num_resched_+1) * pow(2,a_->highest_level_))), a_->be_random_);
// a_->promo_timeout_ = a_->random_timer(double(CONST_TIMEOUT + PROMO_TIMEOUT_MULTIPLES * a_->radius(a_->highest_level_) + MAX_TIMEOUT/((a_->num_resched_+1) * pow(2,a_->highest_level_))), a_->be_random_) + (MAX_ENERGY - (a_->node_)->energy())*200/MAX_ENERGY;
a_->trace("Node %d: Promotion timeout after wait period in expire1: %f at time %f", a_->myaddr_,a_->promo_timeout_,s.clock());
a_->num_resched_ = 0;
a_->promo_start_time_ = s.clock();
a_->promo_timer_->resched(a_->promo_timeout_);
}
else {
double wait_time = PERIODIC_WAIT_TIME;
a_->total_wait_time_ += wait_time;
// a_->trace("Node %d: Entering wait period in expire1 at time %f, highest level %d", a_->myaddr_,now,a_->highest_level_);
a_->promo_timer_->resched(wait_time);
// Demote ourself we do not have any children (excluding ourself) after
// waiting for 1.5 times the update period
if(a_->highest_level_ && (a_->total_wait_time_ > (a_->update_period_*1.5))) {
// a_->trace("Node %d: cur_pcl's number of children %d",a_->myaddr_,cur_pcl->num_children_);
// Demote ourself from this level if we have only one children
// and we have more than one potential parent at lower level
// If we dont have more than one potl parent at lower level,
// this node will oscillate between the two levels
if(cur_pcl->num_children_ == 1 && lower_level_pcl->num_pparent_ > 1) {
a_->trace("Node %d: Demoting from level %d because of no children at time %f",a_->myaddr_,a_->highest_level_,s.clock());
// Update appropriate lists
int delete_flag = TRUE;
int upd_time = (int) now;
upd_time = (upd_time << 16) | (lower_level_pcl->seqnum_ & 0xFFFF);
++(lower_level_pcl->seqnum_);
lower_level_pcl->UpdatePotlParent(a_->myaddr_, 0, 0, 0, 0, 0, upd_time, delete_flag);
upd_time = (int) now;
upd_time = (upd_time << 16) | (higher_level_pcl->seqnum_ & 0xFFFF);
++(higher_level_pcl->seqnum_);
higher_level_pcl->UpdatePotlChild(a_->myaddr_, 0, 0, 0, 0, 0, upd_time, IS_CHILD, delete_flag,NULL);
--(a_->highest_level_);
Packet *p = a_->makeUpdate(cur_pcl, HIER_ADVS, DEMOTION);
s.schedule(a_->target_,p,0);
}
}
else if(!(cur_pcl->parent_) && (a_->total_wait_time_ >= (2*PERIODIC_WAIT_TIME))) {
// We must be the global LM
a_->global_lm_id_ = a_->myaddr_;
a_->global_lm_level_ = a_->highest_level_;
// Get LM addresses if any
(cur_pcl->mylmaddrs_)->get_lm_addrs(&num_my_lm_addrs,&my_lm_addrs);
// Start assigning landmark addresses to child nodes;
// Shift 1, number of levels * 8 times to left for address of root node
int64_t *lmaddr = new int64_t[1];
lmaddr[0] = 1;
lmaddr[0] = (lmaddr[0] << (cur_pcl->level_ * 8));
int num_lm_addrs = 1;
assert(num_my_lm_addrs <= 1);
if(num_my_lm_addrs == 0) {
addr_changed = 1;
}
else {
if(my_lm_addrs[0] != lmaddr[0])
addr_changed = 1;
}
if(num_my_lm_addrs) delete[] my_lm_addrs;
if(addr_changed) {
a_->trace("Node %d: LM addrs being assigned by global LM at time %f, level %d",a_->myaddr_,now,a_->highest_level_);
a_->assign_lmaddress(lmaddr, num_lm_addrs, cur_pcl->level_);
if(a_->global_lm_)
p = a_->makeUpdate(cur_pcl, HIER_ADVS, GLOBAL_ADVERT);
else
p = a_->makeUpdate(cur_pcl, HIER_ADVS, PERIODIC_ADVERTS);
a_->trace("Node %d: Generating ReHash msg at time %f",a_->myaddr_,NOW);
a_->GenerateReHashMsg(lmaddr[0],NOW);
// Generate updates for LM at lower levels as well since their LM
// addresses have also changed
ParentChildrenList *tmp_pcl = a_->parent_children_list_;
while(tmp_pcl) {
if(tmp_pcl->level_ < cur_pcl->level_) {
a_->trace("Node %d: Generating level %d update at time %f",a_->myaddr_,tmp_pcl->level_,now);
newp = a_->makeUpdate(tmp_pcl, HIER_ADVS, PERIODIC_ADVERTS);
s.schedule(a_->target_,newp,0);
tmp_pcl->last_update_sent_ = now;
}
tmp_pcl = tmp_pcl->next_;
}
s.schedule(a_->target_, p, 0);
cur_pcl->last_update_sent_ = now;
}
// The advertisements from the root LM need to be broadcast in the hash
// scheme
if(num_lm_addrs) delete[] lmaddr;
}
}
return;
}
// Promotion timer is off
a_->promo_timer_running_ = 0;
// Only one promotion timer can be running at a node at a given instant.
// On expiry, the node will be promoted one level higher to highest_level_+1
// Add a parentchildrenlist object for the higher level if one doesnt already
// exist
higher_level_pcl = NULL;
pcl = a_->parent_children_list_;
while(pcl != NULL) {
new_pcl = pcl;
if(pcl->level_ == a_->highest_level_+1){
found = TRUE;
higher_level_pcl = pcl;
}
if(pcl->level_ == (a_->highest_level_)){
pcl1 = pcl;
}
if(pcl->level_ == (a_->highest_level_+2)){
pcl2 = pcl;
}
pcl = pcl->next_;
}
// highest_level_-1 object should exist
assert(pcl1);
if(pcl1->parent_ != NULL) {
a_->trace("Node %d: Not promoted to higher level %d\n", a_->myaddr_, a_->highest_level_+1);
return;
}
++(a_->highest_level_);
assert(a_->highest_level_ < MAX_LEVELS);
if(!found) {
new_pcl->next_ = new ParentChildrenList(a_->highest_level_, a_);
higher_level_pcl = new_pcl->next_;
new_pcl = new_pcl->next_;
}
// Create highest_level_+1 object if it doesnt exist
if(!pcl2) {
new_pcl->next_ = new ParentChildrenList((a_->highest_level_)+1, a_);
pcl2 = new_pcl->next_;
}
assert(pcl2);
if(a_->debug_) {
a_->trace("Node %d: Promoted to level %d, num_potl_children %d at time %f", a_->myaddr_, a_->highest_level_, num_potl_ch, now);
// LMNode *lm = higher_level_pcl->pchildren_;
// a_->trace("Potential Children:");
// while(lm) {
// a_->trace("%d (level %d) Number of hops: %d", lm->id_,lm->level_,lm->num_hops_);
// lm = lm->next_;
// }
// lm = higher_level_pcl->pparent_;
// a_->trace("Potential Parent:");
// while(lm) {
// a_->trace("%d (level %d)", lm->id_,lm->level_);
// lm = lm->next_;
// }
}
// Update tag lists and send out corresponding advertisements
a_->SendChangedTagListUpdate(0,a_->highest_level_-1);
// start off periodic advertisements for this higher level
s.schedule(higher_level_pcl->periodic_handler_, higher_level_pcl->periodic_update_event_, 0);
// add myself as potential parent for highest_level-1 and child for
// highest_level+1
int num_hops = 0;
int energy = (int)((a_->node_)->energy());
int child_flag = IS_CHILD;
int delete_flag = FALSE;
int upd_time = (int) now;
upd_time = (upd_time << 16) | (pcl1->seqnum_ & 0xFFFF);
++(pcl1->seqnum_);
pcl1->UpdatePotlParent(a_->myaddr_, a_->myaddr_, num_hops, a_->highest_level_, higher_level_pcl->num_children_, energy, upd_time, delete_flag);
// tag_list == NULL doesnt matter because we're at a smaller level than
// pcl2->level_ at this point. periodic_callback_ will update this field
// correctly
upd_time = (int) now;
upd_time = (upd_time << 16) | (pcl2->seqnum_ & 0xFFFF);
++(pcl2->seqnum_);
pcl2->UpdatePotlChild(a_->myaddr_, a_->myaddr_, num_hops, a_->highest_level_,higher_level_pcl->num_children_, energy, upd_time, child_flag, delete_flag, higher_level_pcl->tag_list_);
// If we havent seen a LM that can be our parent at this higher level, start
// promotion timer for promotion to next level
a_->num_resched_ = pcl2->num_heard_ - 1;
if(higher_level_pcl->parent_ == NULL && a_->num_resched_) {
// if (a_->debug_) printf("PromotionTimer's expire method: Scheduling timer for promo to level %d\n",a_->highest_level_);
// Timer's status is TIMER_HANDLING in handle; so we just reschedule to
// avoid "cant start timer" abort if sched is called
a_->promo_timer_running_ = 1;
a_->wait_state_ = 0;
a_->total_wait_time_ = 0;
a_->promo_timeout_ = a_->random_timer(double(CONST_TIMEOUT + PROMO_TIMEOUT_MULTIPLES * a_->radius(a_->highest_level_+1) + MAX_TIMEOUT/((a_->num_resched_+1) * pow(2,a_->highest_level_+1))), a_->be_random_);
a_->trace("Node %d: Promotion timeout after wait period in expire2: %f at time %f, num_resched_ %d, energy %f", a_->myaddr_,a_->promo_timeout_,s.clock(),a_->num_resched_,(a_->node_)->energy());
a_->num_resched_ = 0;
a_->promo_start_time_ = s.clock();
a_->promo_timer_->resched(a_->promo_timeout_);
}
else {
double wait_time = PERIODIC_WAIT_TIME;
a_->promo_timer_running_ = 1;
a_->total_wait_time_ = 0;
a_->wait_state_ = 1;
a_->total_wait_time_ += wait_time;
// a_->trace("Node %d: Entering wait period in expire1 at time %f", a_->myaddr_,now);
a_->promo_timer_->resched(wait_time);
}
// if(now > 412.5) {
// purify_printf("expire2: %f,%d\n",now,a_->myaddr_);
// purify_new_leaks();
// }
}
int
LandmarkAgent::command (int argc, const char *const *argv)
{
if (argc == 2)
{
if (strcmp (argv[1], "start") == 0)
{
startUp();
return (TCL_OK);
}
if (strcmp (argv[1], "stop") == 0)
{
stop();
return (TCL_OK);
}
if (strcmp (argv[1], "print-nbrs") == 0)
{
get_nbrinfo();
return (TCL_OK);
}
if (strcmp (argv[1], "enable-caching") == 0)
{
cache_ = 1;
return (TCL_OK);
}
if (strcmp (argv[1], "unicast-adverts") == 0)
{
adverts_type_ = UNICAST;
return (TCL_OK);
}
if (strcmp (argv[1], "hard-state-adverts") == 0)
{
adverts_type_ = SUPPRESS;
// Entries should never timeout in a hard-state scheme
update_timeout_ = 1000000;
return (TCL_OK);
}
if (strcmp (argv[1], "enable-global-landmark") == 0)
{
global_lm_ = 1;
return (TCL_OK);
}
else if (strcmp (argv[1], "dumprtab") == 0)
{
Packet *p2 = allocpkt ();
hdr_ip *iph2 = (hdr_ip *) p2->access (off_ip_);
// rtable_ent *prte;
trace ("Table Dump %d[%d]\n----------------------------------\n",
myaddr_, iph2->sport());
trace ("VTD %.5f %d:%d\n", Scheduler::instance ().clock (),
myaddr_, iph2->sport());
trace ("Remaining energy: %f", node_->energy());
// trace ("Energy consumed by queries: %f", node_->qry_energy());
/*
* Freeing a routing layer packet --> don't need to
* call drop here.
*/
trace("Highest Level: %d", highest_level_);
Packet::free (p2);
ParentChildrenList *pcl = parent_children_list_;
LMNode *pch;
while(pcl) {
trace("Level %d:", pcl->level_);
if(pcl->parent_)
trace("Parent: %d", (pcl->parent_)->id_);
else
trace("Parent: NULL");
int num_lm_addrs = 0;
int64_t *lmaddrs = NULL;
(pcl->mylmaddrs_)->get_lm_addrs(&num_lm_addrs,&lmaddrs);
for( int i = 0; i < num_lm_addrs; ++i) {
int i1,i2,i3,i4,i5,i6,i7,i8;
i1 = (lmaddrs[i] >> 56) & 0xFF;
i2 = (lmaddrs[i] >> 48) & 0xFF;
i3 = (lmaddrs[i] >> 40) & 0xFF;
i4 = (lmaddrs[i] >> 32) & 0xFF;
i5 = (lmaddrs[i] >> 24) & 0xFF;
i6 = (lmaddrs[i] >> 16) & 0xFF;
i7 = (lmaddrs[i] >> 8) & 0xFF;
i8 = (lmaddrs[i]) & 0xFF;
trace("Landmark Address: %d.%d.%d.%d.%d.%d.%d.%d",i1,i2,i3,i4,i5,i6,i7,i8);
}
if(num_lm_addrs) delete[] lmaddrs;
if(myaddr_ == 134) {
pch = pcl->pchildren_;
while(pch) {
int num_addrs = 0;
int64_t *addrs = NULL;
(pch->lmaddr_)->get_lm_addrs(&num_addrs,&addrs);
int j1=0,j2=0,j3=0,j4=0,j5=0,j6=0,j7=0,j8=0;
if(num_addrs) {
j1 = (addrs[0] >> 56) & 0xFF;
j2 = (addrs[0] >> 48) & 0xFF;
j3 = (addrs[0] >> 40) & 0xFF;
j4 = (addrs[0] >> 32) & 0xFF;
j5 = (addrs[0] >> 24) & 0xFF;
j6 = (addrs[0] >> 16) & 0xFF;
j7 = (addrs[0] >> 8) & 0xFF;
j8 = (addrs[0]) & 0xFF;
}
trace("Node %d: Potl Child id %d, LM addr %d.%d.%d.%d.%d.%d.%d.%d, next_hop %d, num_children %d",myaddr_,pch->id_,j1,j2,j3,j4,j5,j6,j7,j8,pch->next_hop_,pch->num_children_);
if(num_addrs) delete[] addrs;
pch = pch->next_;
}
}
trace("Number of potl children: %d\n", pcl->num_potl_children_);
if(myaddr_ == 166) {
trace("Number of children: %d\n", pcl->num_children_);
trace("Number of level %d nodes heard: %d\n", (pcl->level_)-1, pcl->num_heard_);
trace("Number of potl parent: %d\n", pcl->num_pparent_);
if(pcl->level_ >= 1 && highest_level_ >= 1) {
pch = pcl->pchildren_;
trace("Potential Children (radius %d):",radius(pcl->level_));
while(pch) {
if(pch->child_flag_ != NOT_POTL_CHILD)
trace("Node %d (%d hops away)",pch->id_,pch->num_hops_);
pch = pch->next_;
}
pch = pcl->pparent_;
trace("Potential parent:");
while(pch) {
trace("Node %d (%d hops away)",pch->id_,pch->num_hops_);
pch = pch->next_;
}
}
}
pcl = pcl->next_;
}
Packet::free(p2);
return (TCL_OK);
}
}
else if (argc == 3)
{
if (strcasecmp (argv[1], "tracetarget") == 0)
{
TclObject *obj;
if ((obj = TclObject::lookup (argv[2])) == 0)
{
fprintf (stderr, "%s: %s lookup of %s failed\n", __FILE__, argv[1],
argv[2]);
return TCL_ERROR;
}
tracetarget_ = (Trace *) obj;
return TCL_OK;
}
else if (strcasecmp (argv[1], "addr") == 0) {
int temp;
temp = Address::instance().str2addr(argv[2]);
myaddr_ = temp;
return TCL_OK;
}
else if (strcasecmp (argv[1], "set-update-period") == 0) {
update_period_ = atof(argv[2]);
if(adverts_type_ != SUPPRESS)
update_timeout_ = update_period_ + 4 * LM_STARTUP_JITTER;
return TCL_OK;
}
else if (strcasecmp (argv[1], "set-update-timeout") == 0) {
update_timeout_ = atof(argv[2]);
return TCL_OK;
}
else if (strcasecmp (argv[1], "start-tag-motion") == 0) {
mobility_period_ = atof(argv[2]);
Scheduler::instance().schedule(tag_mobility_,tag_mobility_event_,Random::uniform(mobility_period_));
return (TCL_OK);
}
else if (strcasecmp (argv[1], "attach-tag-dbase") == 0)
{
TclObject *obj;
if ((obj = TclObject::lookup (argv[2])) == 0)
{
fprintf (stderr, "%s: %s lookup of %s failed\n", __FILE__, argv[1],
argv[2]);
return TCL_ERROR;
}
tag_dbase_ = (tags_database *) obj;
return TCL_OK;
}
else if (strcasecmp (argv[1], "node") == 0)
{
assert(node_ == NULL);
TclObject *obj;
if ((obj = TclObject::lookup (argv[2])) == 0)
{
fprintf (stderr, "%s: %s lookup of %s failed\n", __FILE__, argv[1],
argv[2]);
return TCL_ERROR;
}
node_ = (MobileNode *) obj;
return TCL_OK;
}
else if (strcmp (argv[1], "query-debug") == 0)
{
qry_debug_ = atoi(argv[2]);
return (TCL_OK);
}
else if (strcasecmp (argv[1], "ll-queue") == 0)
{
if (!(ll_queue = (PriQueue *) TclObject::lookup (argv[2])))
{
fprintf (stderr, "Landmark_Agent: ll-queue lookup of %s failed\n", argv[2]);
return TCL_ERROR;
}
return TCL_OK;
}
}
return (Agent::command (argc, argv));
}
void
LandmarkAgent::startUp()
{
int i,ntags, j = 0, read_new_mobile_tags = 0;
Scheduler &s = Scheduler::instance();
compr_taglist *local_tags0, *local_tags1, *local_tags2, *t_ptr;
compr_taglist *tag_ptr1, *tag_ptr2;
God *gd = God::instance();
// AgentList *alist = AgentList::instance();
int *nbrs;
int num_nbrs = 0, num_nodes = 0;
// Adding ourself to global tag agent database
// alist->AddAgent(myaddr_,this);
// num_nodes = gd->numNodes()-1;
// nbrs = new int[num_nodes];
// for(i = 1; i <= num_nodes; ++i) {
// God sees node id as id+1 ...
// if(gd->hops(myaddr_+1,i) == 1) {
// nbrs[num_nbrs++] = i-1;
// }
// }
// trace("Node %d: Number of nbrs %d, Neighbours:",myaddr_,num_nbrs);
// num_nbrs_ = num_nbrs;
// nbrs_ = new int[num_nbrs_];
// for(i = 0; i < num_nbrs_; ++i) {
// nbrs_[i] = nbrs[i];
// trace("%d",nbrs_[i]);
// }
// if(nbrs) delete[] nbrs;
trace("Node %d: LM Agent starting up at time %f",myaddr_,NOW);
// Set node to be alive (this method might be called after a call to reset
node_dead_ = 0;
double x,y,z;
node_->getLoc(&x,&y,&z);
// printf("Node %d position: (%f,%f,%f)\n",myaddr_,x,y,z);
// Detection range smaller than transmission range. This is because, if
// the tags are passive, they may not have sufficient energy to re-radiate
// information to the sensor
double r = 60;
local_tags0 = tag_dbase_->Gettags(x,y,r);
// trace("Node %d's at (%f,%f,%f) senses tags:",myaddr_,x,y,z);
t_ptr = local_tags0;
ntags = 0;
while(t_ptr) {
// trace("tag name: %d.%d.%d",(t_ptr->obj_name_ >> 24) & 0xFF,(t_ptr->obj_name_ >> 16) & 0xFF,(t_ptr->obj_name_) & 0xFFFF);
++ntags;
if(!(t_ptr->next_) && mobile_tags_ && !read_new_mobile_tags) {
// Update our tag list with any new tags that have come into our range
// while we were dead
read_new_mobile_tags = 1;
t_ptr->next_ = mobile_tags_;
mobile_tags_ = NULL;
}
t_ptr = t_ptr->next_;
}
// trace("Number of tags: %d",ntags);
/*
int agg_level = 1;
int num_tags = 0;
local_tags1 = aggregate_tags(local_tags0,agg_level,&num_tags);
trace("Level 1 aggregates, num = %d",num_tags);
t_ptr = local_tags1;
while(t_ptr) {
trace("tag name: %d.%d.%d",(t_ptr->obj_name_ >> 24) & 0xFF,(t_ptr->obj_name_ >> 16) & 0xFF,(t_ptr->obj_name_) & 0xFFFF);
t_ptr = t_ptr->next_;
}
agg_level = 2;
num_tags = 0;
local_tags2 = aggregate_tags(local_tags1,agg_level,&num_tags);
trace("Level 2 aggregates, num = %d",num_tags);
t_ptr = local_tags2;
while(t_ptr) {
trace("tag name: %d.%d.%d",(t_ptr->obj_name_ >> 24) & 0xFF,(t_ptr->obj_name_ >> 16) & 0xFF,(t_ptr->obj_name_) & 0xFFFF);
t_ptr = t_ptr->next_;
}
// Delete local_tags1
tag_ptr1 = local_tags1;
while(tag_ptr1) {
tag_ptr2 = tag_ptr1;
tag_ptr1 = tag_ptr1->next_;
delete tag_ptr2;
}
// Delete local_tags2
tag_ptr1 = local_tags2;
while(tag_ptr1) {
tag_ptr2 = tag_ptr1;
tag_ptr1 = tag_ptr1->next_;
delete tag_ptr2;
}
*/
assert(highest_level_ == 0);
assert(parent_children_list_ == NULL);
parent_children_list_ = new ParentChildrenList(highest_level_, this);
ParentChildrenList **pcl = &parent_children_list_;
// Start off periodic LM advertisements
assert(highest_level_ == 0);
s.schedule((*pcl)->periodic_handler_, (*pcl)->periodic_update_event_, INITIAL_WAIT_TIME + jitter(LM_STARTUP_JITTER, be_random_));
(*pcl)->tag_list_ = local_tags0;
(*pcl)->num_tags_ = ntags;
// Start off promotion timer
// if (debug_) printf("startUp: Scheduling timer\n");
promo_timer_running_ = 1;
num_resched_ = 0;
// Node enters "wait" state where it waits to receive other node's
// advertisements; Wait for 5 * radius(level) seconds; Should be
// atleast the same as the period of LM advts (10s)
total_wait_time_ = 0;
wait_state_ = 1;
double wait_time = WAIT_TIME * radius(highest_level_) + INITIAL_WAIT_TIME + LM_STARTUP_JITTER;
total_wait_time_ += wait_time;
// trace("Node %d: Wait time at startUp: %f",myaddr_,wait_time);
promo_timer_->sched(wait_time);
// promo_timer_->sched(promo_timeout_);
}
compr_taglist *
LandmarkAgent::aggregate_taginfo(compr_taglist *unagg_tags, int agg_level, int *num_tags)
{
compr_taglist *agg_tags, *agg_ptr1, *agg_ptr2, *last_agg_ptr;
int found;
*num_tags = 0;
// agg_level is 1 implies ignore the last field
// agg_level >= 2 implies ignore the last two fields
agg_ptr1 = unagg_tags;
agg_tags = NULL;
while(agg_ptr1) {
if(agg_level == 1) {
found = FALSE;
if(agg_tags) {
agg_ptr2 = agg_tags;
while(agg_ptr2) {
if((((agg_ptr2->obj_name_ >> 24) & 0xFF) == ((agg_ptr1->obj_name_ >> 24) & 0xFF)) && (((agg_ptr2->obj_name_ >> 16) & 0xFF) == ((agg_ptr1->obj_name_ >> 16) & 0xFF))) {
found = TRUE;
break;
}
last_agg_ptr = agg_ptr2;
agg_ptr2 = agg_ptr2->next_;
}
}
if(!found) {
++(*num_tags);
if(!agg_tags) {
agg_tags = new compr_taglist;
last_agg_ptr = agg_tags;
}
else {
last_agg_ptr->next_ = new compr_taglist;
last_agg_ptr = last_agg_ptr->next_;
}
last_agg_ptr->obj_name_ = (agg_ptr1->obj_name_ & 0xFFFF0000);
}
}
else if(agg_level >= 2) {
found = FALSE;
if(agg_tags) {
agg_ptr2 = agg_tags;
while(agg_ptr2) {
if(((agg_ptr2->obj_name_ >> 24) & 0xFF) == ((agg_ptr1->obj_name_ >> 24) & 0xFF)) {
found = TRUE;
break;
}
last_agg_ptr = agg_ptr2;
agg_ptr2 = agg_ptr2->next_;
}
}
if(!found) {
++(*num_tags);
if(!agg_tags) {
agg_tags = new compr_taglist;
last_agg_ptr = agg_tags;
}
else {
last_agg_ptr->next_ = new compr_taglist;
last_agg_ptr = last_agg_ptr->next_;
}
last_agg_ptr->obj_name_ = (agg_ptr1->obj_name_ & 0xFF000000);
}
}
agg_ptr1 = agg_ptr1->next_;
}
return(agg_tags);
}
compr_taglist *
LandmarkAgent::aggregate_tags(compr_taglist *unagg_tags, int agg_level, int *num_tags)
{
compr_taglist *agg_tags = NULL, *tag_ptr;
aggreg_taglist *t1, *t2, *t3, *tmp_ptr;
aggreg_taglist *list1 = NULL, *list2 = NULL, *list3 = NULL, *list = NULL;
aggreg_taglist *prev_tag, *next_tag, *old_list;
int found;
int p1,p2,p3,q1,q2,q3,object_name;
// Tag names have 3 fields
// List 1 is list of tags with first field > 0, last 2 fields = 0
// List 2 is list of tags with first two fields > 0 and last field = 0
// List 3 is list of tags with all three fields > 0
tag_ptr = unagg_tags;
while(tag_ptr) {
p1 = (tag_ptr->obj_name_ >> 24) & 0xFF;
p2 = (tag_ptr->obj_name_ >> 16) & 0xFF;
p3 = tag_ptr->obj_name_ & 0xFFFF;
found = 0;
if(p1 && p2 && p3) {
// Check if p1.p2.0 is already in list2; If so, goto next object
object_name = (int)((p1 * pow(2,24)) + (p2 * pow(2,16))) ;
old_list = list2;
while(old_list) {
if(old_list->obj_name_ == object_name) {
found = TRUE;
break;
}
old_list = old_list->next_;
}
// Check if p1.0.0 is already in list1; If so, goto next object
old_list = list1;
while(old_list) {
q1 = (old_list->obj_name_ >> 24) & 0xFF;
if(p1 == q1) {
found = TRUE;
break;
}
old_list = old_list->next_;
}
tmp_ptr = list3;
while(tmp_ptr && !found) {
q1 = (tmp_ptr->obj_name_ >> 24) & 0xFF;
q2 = (tmp_ptr->obj_name_ >> 16) & 0xFF;
q3 = tmp_ptr->obj_name_ & 0xFFFF;
// If 2 objects have same value for first two fields, store the
// aggregate p1.p2.0 in list2; We have already checked if p1.p2.0
// is already in list2 or not
if(p1 == q1 && p2 == q2 && p3 != q3) {
if(!list2) {
list2 = new aggreg_taglist;
t2 = list2;
}
else {
t2->next_ = new aggreg_taglist;
t2 = t2->next_;
}
t2->obj_name_ = object_name;
// Indicate that this is a new aggregate
t2->marked_ = 1;
// Remove this object from list3; We simply set the obj_name_ to 1
// to indicate that this tag object is not valid
tmp_ptr->obj_name_ = -1;
found = TRUE;
break;
}
else if(p1 == q1 && p2 == q2 && p3 == q3) {
found = TRUE;
break;
}
tmp_ptr = tmp_ptr->next_;
}
if(found) {
tag_ptr = tag_ptr->next_;
continue;
}
if(!list3) {
list3 = new aggreg_taglist;
t3 = list3;
}
else {
t3->next_ = new aggreg_taglist;
t3 = t3->next_;
}
t3->obj_name_ = tag_ptr->obj_name_;
}
else if(p1 && p2 && !p3) {
// Check if p1.0.0 is already in list1; If so, goto next object
object_name = (int)(p1 * pow(2,24)) ;
if(list1) {
old_list = list1;
while(old_list) {
if(old_list->obj_name_ == object_name) {
found = TRUE;
break;
}
old_list = old_list->next_;
}
}
tmp_ptr = list2;
while(tmp_ptr && !found) {
q1 = (tmp_ptr->obj_name_ >> 24) & 0xFF;
q2 = (tmp_ptr->obj_name_ >> 16) & 0xFF;
// If 2 objects have same value for the first field, store the
// aggregate in list1 provided the other object is not a new aggregate
if(p1 == q1 && p2 != q2 && !tmp_ptr->marked_) {
if(!list1) {
list1 = new aggreg_taglist;
t1 = list1;
}
else {
t1->next_ = new aggreg_taglist;
t1 = t1->next_;
}
t1->obj_name_ = object_name;
// Indicate that this is a new aggregate
t1->marked_ = 1;
// Remove this object from list3; We simply set the obj_name_ to 1
// to indicate that this tag object is not valid
tmp_ptr->obj_name_ = -1;
// Remove any elements p1.*.* from list3 i.e., set obj_name_ to -1
old_list = list3;
while(old_list) {
q1 = (old_list->obj_name_ >> 24) & 0xFF;
if(p1 == q1)
old_list->obj_name_ = -1;
old_list = old_list->next_;
}
found = TRUE;
break;
}
else if(p1 == q1 && p2 == q2) {
found = TRUE;
break;
}
tmp_ptr = tmp_ptr->next_;
}
if(found) {
tag_ptr = tag_ptr->next_;
continue;
}
if(!list2) {
list2 = new aggreg_taglist;
t2 = list2;
}
else {
t2->next_ = new aggreg_taglist;
t2 = t2->next_;
}
t2->obj_name_ = tag_ptr->obj_name_;
// Remove any elements p1.p2.* from list3 i.e., set obj_name_ to -1
old_list = list3;
while(old_list) {
q1 = (old_list->obj_name_ >> 24) & 0xFF;
q2 = (old_list->obj_name_ >> 16) & 0xFF;
if(p1 == q1 && p2 == q2)
old_list->obj_name_ = -1;
old_list = old_list->next_;
}
}
else if(p1 && !p2 && !p3) {
// Check if object p1.0.0 already in list; If so, goto next object
tmp_ptr = list1;
while(tmp_ptr) {
if(tmp_ptr->obj_name_ == tag_ptr->obj_name_) {
found = TRUE;
break;
}
tmp_ptr = tmp_ptr->next_;
}
// Add object to list1
if(!found) {
if(!list1) {
list1 = new aggreg_taglist;
t1 = list1;
}
else {
t1->next_ = new aggreg_taglist;
t1 = t1->next_;
}
t1->obj_name_ = tag_ptr->obj_name_;
}
// Remove any elements p1.*.* from list2 i.e., set obj_name_ to -1
old_list = list2;
while(old_list) {
q1 = (old_list->obj_name_ >> 24) & 0xFF;
if(p1 == q1)
old_list->obj_name_ = -1;
old_list = old_list->next_;
}
// Remove any elements p1.*.* from list3 i.e., set obj_name_ to -1
old_list = list3;
while(old_list) {
q1 = (old_list->obj_name_ >> 24) & 0xFF;
if(p1 == q1)
old_list->obj_name_ = -1;
old_list = old_list->next_;
}
}
else
assert(0);
tag_ptr = tag_ptr->next_;
}
// Make list1, list2, list3 into one list
list = NULL;
if(list3) {
list = list3;
if(list2) {
t3->next_ = list2;
if(list1) {
t2->next_ = list1;
}
}
else if(list1)
t3->next_ = list1;
}
else if(list2) {
list = list2;
if(list1)
t2->next_ = list1;
}
else if(list1)
list = list1;
// Return the list of aggregated tags
*num_tags = 0;
agg_tags = NULL;
tmp_ptr = list;
while(tmp_ptr) {
if(tmp_ptr->obj_name_ != -1) {
if(!agg_tags) {
agg_tags = new compr_taglist;
tag_ptr = agg_tags;
}
else {
tag_ptr->next_ = new compr_taglist;
tag_ptr = tag_ptr->next_;
}
++(*num_tags);
tag_ptr->obj_name_ = tmp_ptr->obj_name_;
}
tmp_ptr = tmp_ptr->next_;
}
// Delete list
list1 = NULL;
list2 = NULL;
list1 = list;
while(list1) {
list2 = list1;
list1 = list1->next_;
delete list2;
}
return(agg_tags);
}
void
LandmarkAgent::recv(Packet *p, Handler *)
{
hdr_ip *iph = (hdr_ip *) p->access (off_ip_);
hdr_cmn *cmh = (hdr_cmn *) p->access (off_cmn_);
/*
* Must be a packet being originated by the query agent at my node?
*/
if(node_dead_) {
Packet::free(p);
return;
}
if(iph->saddr() == myaddr_ && iph->sport() == 0) {
/*
* Add the IP Header
*/
cmh->size() += IP_HDR_LEN;
}
/*
* I received a packet that I sent. Probably
* a routing loop.
*/
else if(iph->saddr() == myaddr_) {
Packet::free(p);
// drop(p, DROP_RTR_ROUTE_LOOP);
return;
}
/*
* Packet I'm forwarding...
*/
// Move the ttl check to the following methods?
// if(--iph->ttl_ == 0) {
// drop(p, DROP_RTR_TTL);
// return;
// }
// Packet will be forwarded down (if it's not dropped)
cmh->direction_ = hdr_cmn::DOWN;
unsigned char *walk = p->accessdata();
int action = *walk++;
int data_pkt = 0;
if(action == QUERY_PKT || action == HASH_PKT || action == HASH_ACK_PKT || action == REHASH_PKT || action == DIR_QUERY_PKT || action == DIR_RESPONSE_PKT || action == OBJECT_QUERY_PKT || action == OBJECT_RESPONSE_PKT)
data_pkt = 1;
if ((iph->saddr() != myaddr_) && (iph->dport() == ROUTER_PORT) && !data_pkt)
{
ProcessHierUpdate(p);
}
else
{
ForwardPacket(p);
}
}
void
LandmarkAgent::ForwardPacket(Packet *p)
{
hdr_ip *iph = (hdr_ip *) p->access (off_ip_);
hdr_cmn *cmh = (hdr_cmn *) p->access (off_cmn_);
Packet *newp;
hdr_ip *new_iph;
hdr_cmn *new_cmh;
unsigned char *walk, *X_ptr, *Y_ptr, *level_ptr, *num_src_hops_ptr;
unsigned char *last_hop_ptr, *pkt_end_ptr;
int X, Y, next_hop_level, prev_hop_level, obj_name, num_src_hops;
double local_x, local_y, local_z;
int num_dst = 0, action, origin_time;
NodeIDList *dst_nodes = NULL, *dst_ptr = NULL;
int query_for_us = FALSE;
Scheduler &s = Scheduler::instance();
double now = s.clock();
nsaddr_t last_hop_id;
int cache_index = -1; // index into cache if object is found
int found = FALSE; // whether object has been found in cache
walk = p->accessdata();
// Type of advertisement
action = *walk++;
X = 0;
X_ptr = walk;
X = *walk++;
X = (X << 8) | *walk++;
Y_ptr = walk;
Y = *walk++;
Y = (Y << 8) | *walk++;
// level of our parent/child node that forwarded the query to us
level_ptr = walk;
next_hop_level = *walk++;
obj_name = *walk++;
obj_name = (obj_name << 8) | *walk++;
obj_name = (obj_name << 8) | *walk++;
obj_name = (obj_name << 8) | *walk++;
// origin time of advertisement
origin_time = *walk++;
origin_time = (origin_time << 8) | *walk++;
origin_time = (origin_time << 8) | *walk++;
origin_time = (origin_time << 8) | *walk++;
num_src_hops_ptr = walk;
num_src_hops = *walk++;
num_src_hops = (num_src_hops << 8) | *walk++;
assert(num_src_hops <= 30);
last_hop_ptr = NULL;
for(int i = 0; i < num_src_hops; ++i) {
last_hop_ptr = walk;
walk += 3;
}
if(last_hop_ptr) {
prev_hop_level = *(last_hop_ptr+2);
last_hop_id = *last_hop_ptr;
last_hop_id = (last_hop_id << 8) | *(last_hop_ptr+1);
}
else {
prev_hop_level = 0;
last_hop_id = NO_NEXT_HOP;
}
// Used to add source route to packet
pkt_end_ptr = walk;
// If this is a response pkt, cache this information
if(cache_) {
if(X != 65000 && Y != 65000) {
if(num_cached_items_ < MAX_CACHE_ITEMS) {
int replace_index = num_cached_items_;
// If object already exists in cache, update info if necessary
for(int i = 0; i < num_cached_items_; ++i) {
if(tag_cache_[i].obj_name_ == obj_name && tag_cache_[i].origin_time_ < origin_time) {
replace_index = i;
break;
}
}
tag_cache_[replace_index].obj_name_ = obj_name;
tag_cache_[replace_index].origin_time_ = origin_time;
tag_cache_[replace_index].X_ = X;
tag_cache_[replace_index].Y_ = Y;
++num_cached_items_;
}
else {
// Use LRU cache replacement
int replace_index = 0;
int least_time = tag_cache_[replace_index].origin_time_;
for(int i = 0; i < MAX_CACHE_ITEMS; ++i) {
if(tag_cache_[i].origin_time_ < least_time)
replace_index = i;
}
tag_cache_[replace_index].obj_name_ = obj_name;
tag_cache_[replace_index].origin_time_ = origin_time;
tag_cache_[replace_index].X_ = X;
tag_cache_[replace_index].Y_ = Y;
}
}
else {
// If this is a query pkt; check if we have the relevant information
// cached. TTL = 600 seconds for the cache entries
found = FALSE;
for(int i = 0; i < num_cached_items_; ++i) {
if(tag_cache_[i].obj_name_ == obj_name && tag_cache_[i].origin_time_ > origin_time - 600) {
found = TRUE;
cache_index = i;
break;
}
}
}
}
// Loop check i.e., if response to our query agent has looped back
// Following not the correct condition to detect a loop!
// assert(!(iph->daddr() == myaddr_ && iph->dport() == 0));
// Reduce the source route to just parent-children (O(#levels))
// This is possible since parent and child in each others vicinity
cmh->direction() = hdr_cmn::DOWN;
if(iph->daddr() == myaddr_)
query_for_us = TRUE;
// Query pkt if X and Y are 65000
if(X == 65000 && Y == 65000) {
if(query_for_us || found) {
if(qry_debug_)
trace("Node %d: Rcved qry for us from node %d at time %f",myaddr_,last_hop_id,s.clock());
if(!found)
dst_nodes = search_tag(obj_name,prev_hop_level,next_hop_level,last_hop_id,&num_dst);
if((num_dst == 0 && dst_nodes) || found) {
delete dst_nodes;
// if num_dst = 0 but dst_nodes is not NULL, we sense the
// requested tag; add X,Y info and send response
// if found is true, we have the cached information
if(found) {
(*X_ptr++) = ((int)tag_cache_[cache_index].X_ >> 8) & 0xFF;
(*X_ptr) = ((int)tag_cache_[cache_index].X_) & 0xFF;
(*Y_ptr++) = ((int)tag_cache_[cache_index].Y_ >> 8) & 0xFF;
(*Y_ptr) = ((int)tag_cache_[cache_index].Y_) & 0xFF;
}
else {
node_->getLoc(&local_x, &local_y, &local_z);
(*X_ptr++) = ((int)local_x >> 8) & 0xFF;
(*X_ptr) = ((int)local_x) & 0xFF;
(*Y_ptr++) = ((int)local_y >> 8) & 0xFF;
(*Y_ptr) = ((int)local_y) & 0xFF;
}
// Send response
iph->ttl_ = 1000;
// Add 50 bytes to response
cmh->size() += 50;
// Query from an agent at our node
if(!num_src_hops) {
iph->daddr() = myaddr_;
iph->dport() = 0;
cmh->next_hop_ = myaddr_;
}
else {
--num_src_hops;
*num_src_hops_ptr = (num_src_hops >> 8) & 0xFF;
*(num_src_hops_ptr + 1) = num_src_hops & 0xFF;
// Decr pkt size
cmh->size() -= 4;
iph->daddr() = *last_hop_ptr++;
iph->daddr() = (iph->daddr() << 8) | *last_hop_ptr++;
if(!num_src_hops)
iph->dport() = 0;
else
iph->dport() = ROUTER_PORT;
int relevant_level = *last_hop_ptr;
cmh->next_hop_ = get_next_hop(iph->daddr(),relevant_level);
// assert(cmh->next_hop_ != NO_NEXT_HOP);
if(cmh->next_hop_ == NO_NEXT_HOP) {
Packet::free(p);
trace("Node %d: Packet dropped because of no next hop info",myaddr_);
return;
}
*level_ptr = *last_hop_ptr;
}
// if(found)
// trace("Node %d: Gen response from cache at time %f to node %d",myaddr_,s.clock(),iph->daddr());
// else
// trace("Node %d: Gen response at time %f to node %d",myaddr_,s.clock(),iph->daddr() >> 8);
if(!num_src_hops && iph->daddr() == myaddr_) {
// TEMPORARY HACK! Cant forward from routing agent to some other
// agent on our node!
Packet::free(p);
trace("Node %d: Found object %d.%d.%d at (%d,%d) at time %f",myaddr_, (obj_name >> 24) & 0xFF, (obj_name >> 16) & 0xFF, obj_name & 0xFFFF,X,Y,s.clock());
return;
}
else if(iph->daddr() == myaddr_) {
ForwardPacket(p);
}
else {
s.schedule(target_,p,0);
}
}
else if(num_dst >= 1) {
if(--iph->ttl_ == 0) {
drop(p, DROP_RTR_TTL);
return;
}
// Add ourself to source route and increase the number of src hops
// if(last_hop_id != myaddr_) {
++num_src_hops;
*num_src_hops_ptr = (num_src_hops >> 8) & 0xFF;
*(num_src_hops_ptr+1) = num_src_hops & 0xFF;
*pkt_end_ptr++ = (myaddr_ >> 8) & 0xFF;
*pkt_end_ptr++ = myaddr_ & 0xFF;
// Indicate the level of the pcl object that a node should look-up
// to find the relevant routing table entry
*pkt_end_ptr = (next_hop_level+1) & 0xFF;
// Incr pkt size
cmh->size() += 4;
dst_ptr = dst_nodes;
// Replicate pkt to each destination
iph->daddr() = dst_ptr->dst_node_;
iph->dport() = ROUTER_PORT;
cmh->next_hop_ = dst_ptr->dst_next_hop_;
cmh->addr_type_ = NS_AF_INET;
// Copy next hop variable to this variable temporarily
// Copy it back into packet before sending the packet
int tmp_next_hop_level = dst_ptr->next_hop_level_;
if(qry_debug_)
trace("Node %d: Forwarding qry to node %d at time %f",myaddr_,dst_ptr->dst_node_,s.clock());
dst_ptr = dst_ptr->next_;
delete dst_nodes;
dst_nodes = dst_ptr;
for(int i = 1; i < num_dst; ++i) {
if(qry_debug_)
trace("Node %d: Forwarding qry to node %d at time %f",myaddr_,dst_ptr->dst_node_,s.clock());
// Change level and copy the packet
*level_ptr = dst_ptr->next_hop_level_;
newp = p->copy();
new_iph = (hdr_ip *) newp->access(off_ip_);
new_cmh = (hdr_cmn *) newp->access (off_cmn_);
new_iph->daddr() = dst_ptr->dst_node_;
new_iph->dport() = ROUTER_PORT;
new_cmh->next_hop_ = dst_ptr->dst_next_hop_;
new_cmh->addr_type_ = NS_AF_INET;
if(new_iph->daddr() == myaddr_)
ForwardPacket(newp);
else
s.schedule(target_,newp,0);
dst_ptr = dst_ptr->next_;
delete dst_nodes;
dst_nodes = dst_ptr;
}
*level_ptr = tmp_next_hop_level;
if(iph->daddr() == myaddr_) {
ForwardPacket(p);
}
else
s.schedule(target_,p,0);
}
else if(num_dst == 0) {
// Free packet if we dont have any dst to forward packet
if(qry_debug_)
trace("Node %d: Dropping query from %d at time %f,num_src_hops %d",myaddr_,iph->saddr(),s.clock(),num_src_hops);
Packet::free(p);
return;
}
}
else {
// simply forward to next hop
if(qry_debug_)
trace("Node %d: Forwarding query to node %d at time %f,num_src_hops %d",myaddr_,iph->daddr(),s.clock(),num_src_hops);
if(--iph->ttl_ == 0) {
drop(p, DROP_RTR_TTL);
return;
}
cmh->next_hop_ = get_next_hop(iph->daddr(),next_hop_level+1);
// assert(cmh->next_hop_ != NO_NEXT_HOP);
if(cmh->next_hop_ == NO_NEXT_HOP) {
Packet::free(p);
trace("Node %d: Packet dropped because of no next hop info",myaddr_);
return;
}
s.schedule(target_,p,0);
}
}
else {
// Forward the response packet
if(qry_debug_)
trace("Node %d: Forwarding response to node %d at time %f,num_src_hops %d",myaddr_,iph->daddr(),s.clock(),num_src_hops);
if(--iph->ttl_ == 0) {
drop(p, DROP_RTR_TTL);
return;
}
// Check if query from an agent at our node
if(query_for_us) {
if(!num_src_hops) {
iph->daddr() = myaddr_;
iph->dport() = 0;
cmh->next_hop_ = myaddr_;
}
else {
--num_src_hops;
*num_src_hops_ptr = (num_src_hops >> 8) & 0xFF;
*(num_src_hops_ptr + 1) = num_src_hops & 0xFF;
// Decr pkt size
cmh->size() -= 4;
iph->daddr() = *last_hop_ptr++;
iph->daddr() = (iph->daddr() << 8) | *last_hop_ptr++;
if(!num_src_hops)
iph->dport() = 0;
else
iph->dport() = ROUTER_PORT;
int relevant_level = *last_hop_ptr;
cmh->next_hop_ = get_next_hop(iph->daddr(),relevant_level);
// assert(cmh->next_hop_ != NO_NEXT_HOP);
if(cmh->next_hop_ == NO_NEXT_HOP) {
Packet::free(p);
trace("Node %d: Packet dropped because of no next hop info",myaddr_);
return;
}
*level_ptr = *last_hop_ptr;
}
if(!num_src_hops && iph->daddr() == myaddr_) {
// TEMPORARY HACK! Cant forward from routing agent to some other
// agent on our node!
Packet::free(p);
trace("Node %d: Found object %d.%d.%d at (%d,%d) at time %f",myaddr_, (obj_name >> 24) & 0xFF, (obj_name >> 16) & 0xFF, obj_name & 0xFFFF,X,Y,s.clock());
return;
}
else if(iph->daddr() == myaddr_)
ForwardPacket(p);
else
s.schedule(target_,p,0);
}
else {
cmh->next_hop_ = get_next_hop(iph->daddr(),next_hop_level);
// assert(cmh->next_hop_ != NO_NEXT_HOP);
if(cmh->next_hop_ == NO_NEXT_HOP) {
Packet::free(p);
trace("Node %d: Packet dropped because of no next hop info",myaddr_);
return;
}
s.schedule(target_,p,0);
}
}
}
NodeIDList *
LandmarkAgent::search_tag(int obj_name, int prev_hop_level, int next_hop_level,nsaddr_t last_hop_id, int *num_dst)
{
ParentChildrenList *pcl = parent_children_list_;
LMNode *child;
compr_taglist *tag_ptr;
int forward = FALSE;
NodeIDList *nlist = NULL, *nlist_ptr = NULL;
int p1, p2, p3, q1, q2, q3;
int match = 0, exact_match = 0;
*num_dst = 0;
// Check if our node senses the requested tag
while(pcl) {
if(pcl->level_ == 0)
break;
pcl = pcl->next_;
}
if(!pcl)
return(NULL);
// if our node senses the tag, add the node to nlist but do not increase
// num_dst
tag_ptr = pcl->tag_list_;
while(tag_ptr) {
if(tag_ptr->obj_name_ == obj_name) {
nlist = new NodeIDList;
nlist->dst_node_ = myaddr_;
nlist->dst_next_hop_ = myaddr_;
return(nlist);
}
tag_ptr = tag_ptr->next_;
}
// If next_hop_level = 2, lookup would be done in the level 2 object
// that would have level 1 tag aggregates
// if(next_hop_level == 2)
// obj_name = obj_name & 0xFFFF0000;
// else if(next_hop_level >= 3)
// obj_name = obj_name & 0xFF000000;
p1 = (obj_name >> 24) & 0xFF;
p2 = (obj_name >> 16) & 0xFF;
p3 = obj_name & 0xFFFF;
pcl = parent_children_list_;
while(pcl) {
if(pcl->level_ == next_hop_level)
break;
pcl = pcl->next_;
}
if(!pcl)
return(NULL);
// assert(pcl);
child = pcl->pchildren_;
while(child) {
// Dont forward back to child if child forwarded this query to us
// We should forward to all children though if the message is going
// down the hierarchy
forward = FALSE;
if(next_hop_level < prev_hop_level || (child->id_ != last_hop_id && next_hop_level >= prev_hop_level))
forward = TRUE;
if(child->child_flag_ == IS_CHILD && forward) {
tag_ptr = child->tag_list_;
match = 0;
exact_match = 0;
while(tag_ptr) {
q1 = (tag_ptr->obj_name_ >> 24) & 0xFF;
q2 = (tag_ptr->obj_name_ >> 16) & 0xFF;
q3 = tag_ptr->obj_name_ & 0xFFFF;
if(p1 == q1 && p2 == q2 && p3 == q3)
exact_match = 1;
else if((p1 == q1 && p2 == q2 && !q3) || (p1 == q1 && !q2 && !q3))
match = 1;
if(match) {
if(!nlist) {
nlist = new NodeIDList;
nlist_ptr = nlist;
}
else {
nlist_ptr->next_ = new NodeIDList;
nlist_ptr = nlist_ptr->next_;
}
nlist_ptr->dst_node_ = child->id_;
nlist_ptr->dst_next_hop_ = child->next_hop_;
nlist_ptr->next_hop_level_= next_hop_level - 1;
++(*num_dst);
break;
}
else if(exact_match) {
// Delete all old elements
NodeIDList *n1, *n2;
n1 = nlist;
while(n1) {
n2 = n1;
n1 = n1->next_;
delete n2;
}
// Return just single element i.e., the ID of the child with an
// exact match for the object name
nlist = new NodeIDList;
nlist->dst_node_ = child->id_;
nlist->dst_next_hop_ = child->next_hop_;
nlist->next_hop_level_= next_hop_level - 1;
(*num_dst) = 1;
return(nlist);
}
tag_ptr = tag_ptr->next_;
}
}
child = child->next_;
}
// Add parent if query is travelling up the hierarchy
if(next_hop_level >= prev_hop_level && pcl->parent_) {
if(!nlist) {
nlist = new NodeIDList;
nlist_ptr = nlist;
}
else {
nlist_ptr->next_ = new NodeIDList;
nlist_ptr = nlist_ptr->next_;
}
nlist_ptr->dst_node_ = (pcl->parent_)->id_;
nlist_ptr->dst_next_hop_ = (pcl->parent_)->next_hop_;
nlist_ptr->next_hop_level_= next_hop_level + 1;
++(*num_dst);
}
return(nlist);
}
nsaddr_t
LandmarkAgent::get_next_hop(nsaddr_t dst, int next_hop_level)
{
ParentChildrenList *pcl = parent_children_list_;
LMNode *pchild;
while(pcl->level_ != next_hop_level) {
pcl = pcl->next_;
}
assert(pcl);
pchild = pcl->pchildren_;
while(pchild) {
if(pchild->id_ == dst)
return(pchild->next_hop_);
pchild = pchild->next_;
}
return(NO_NEXT_HOP);
}
void
LandmarkAgent::get_nbrinfo()
{
ParentChildrenList *pcl;
LMNode *pchild;
int num_nbrs = 0;
pcl = parent_children_list_;
if(!pcl) {
trace("Node %d: Neighbour info not available; perhaps the node is down");
return;
}
while(pcl) {
if(pcl->level_ == 1)
break;
pcl = pcl->next_;
}
// assert(pcl);
if(!pcl) {
trace("Node %d: Neighbour info not available; perhaps the node is down");
return;
}
pchild = pcl->pchildren_;
// assert(pchild);
while(pchild) {
if(pchild->num_hops_ == 1)
++num_nbrs;
pchild = pchild->next_;
}
trace("Node %d: Number of neighbours: %d",myaddr_,num_nbrs);
}
void
LandmarkAgent::MoveTags() {
ParentChildrenList *pcl = parent_children_list_;
compr_taglist *tag = NULL, *prev_tag, *next_tag;
int removed_tag = 0, our_tags_changed = 0;
trace("Node %d: Moving tags at time %f", myaddr_,NOW);
if(!pcl && !mobile_tags_) {
Scheduler::instance().schedule(tag_mobility_,tag_mobility_event_,tag_rng_->uniform(mobility_period_));
return;
}
if(pcl) {
// Get level 0 pcl object
while(pcl) {
if(pcl->level_ == 0)
break;
pcl = pcl->next_;
}
assert(pcl);
}
// Pick tag(s) at random and move them to one of the neighbours
// Only tags with the last field > 5 are mobile.
if(pcl)
tag = pcl->tag_list_;
else
tag = mobile_tags_;
prev_tag = tag;
while(tag) {
removed_tag = 0;
// Tags with last field < 30 are not mobile
if((tag->obj_name_ & 0xFFFF) > 30) {
// Move tag to neighbouring node with probability of 0.3
int n = tag_rng_->uniform(10);
if(n <= 2) {
assert(nbrs_);
int nbr_index = tag_rng_->uniform(num_nbrs_);
LandmarkAgent *nbr_agent = (LandmarkAgent *)AgentList::instance()->GetAgent(nbrs_[nbr_index]);
assert(nbr_agent);
trace("Node %d: Moving tag %d.%d.%d at time %f to nbr %d",myaddr_,(tag->obj_name_ >> 24) & 0xFF,(tag->obj_name_ >> 16) & 0xFF,tag->obj_name_ & 0xFFFF,NOW,nbrs_[nbr_index]);
// Remove tag from our list
removed_tag = 1;
our_tags_changed = 1;
if(prev_tag == tag) {
if(pcl)
pcl->tag_list_ = tag->next_;
else if(mobile_tags_)
mobile_tags_ = tag->next_;
prev_tag = tag->next_;
}
else
prev_tag->next_ = tag->next_;
next_tag = tag->next_;
if(pcl)
--(pcl->num_tags_);
// Add this tag to neighbouring node
nbr_agent->AddMobileTag(tag);
}
}
if(!removed_tag) {
prev_tag = tag;
tag = tag->next_;
}
else {
tag = next_tag;
}
}
// Trigger hierarchy advertisement if our taglist has changed
if(our_tags_changed)
SendChangedTagListUpdate(our_tags_changed,0);
Scheduler::instance().schedule(tag_mobility_,tag_mobility_event_,tag_rng_->uniform(mobility_period_));
}
void
LandmarkAgent::AddMobileTag(void *mobile_tag)
{
ParentChildrenList *pcl = parent_children_list_;
compr_taglist *tag = NULL, *new_tag = (compr_taglist *)mobile_tag;
// Make sure that this tag object is not pointing to next member on
// the previous list that this tag was part of
new_tag->next_ = NULL;
if(pcl) {
// Get level 0 pcl object
while(pcl) {
if(pcl->level_ == 0)
break;
pcl = pcl->next_;
}
assert(pcl);
++(pcl->num_tags_);
if(!pcl->tag_list_) {
pcl->tag_list_ = new_tag;
}
else {
tag = pcl->tag_list_;
while(tag->next_) {
tag = tag->next_;
}
tag->next_ = new_tag;
}
}
else {
if(!mobile_tags_) {
mobile_tags_ = new_tag;
}
else {
tag = mobile_tags_;
while(tag->next_) {
tag = tag->next_;
}
tag->next_ = new_tag;
}
}
// Trigger hierarchy advertisements after a mean of 5 seconds if
// the advt event has not already been scheduled
if(tag_advt_event_->uid_ < 0)
Scheduler::instance().schedule(tag_advt_handler_, tag_advt_event_, Random::uniform(10));
}
// new tag info received for specified level; Send updates if necessary
// i.e., if aggregates have changed etc.
void
LandmarkAgent::SendChangedTagListUpdate(int our_tag_changed, int level)
{
ParentChildrenList *pcl = parent_children_list_;
ParentChildrenList *child_pcl, *parent_pcl;
compr_taglist *tag_ptr1 = NULL, *tag_ptr2 = NULL, *tag_list = NULL;
compr_taglist *agg_tags = NULL;
LMNode *lmnode;
int upd_time, num_tags = 0;
Scheduler &s = Scheduler::instance();
double now = s.clock();
if(node_dead_ || !pcl || level >= highest_level_)
return;
if(myaddr_ == 45)
trace("Node %d: SendChangedTagListUpdate, level %d at time %f",myaddr_,level,now);
while(pcl) {
if(pcl->level_ == level)
child_pcl = pcl;
else if(pcl->level_ == level + 1)
parent_pcl = pcl;
pcl = pcl->next_;
}
if(our_tag_changed) {
assert(level == 0);
upd_time = (int) now;
upd_time = (upd_time << 16) | (parent_pcl->seqnum_ & 0xFFFF);
++(parent_pcl->seqnum_);
parent_pcl->UpdatePotlChild(myaddr_, myaddr_,0,0,0,(int) node_->energy(),upd_time,IS_CHILD,FALSE,child_pcl->tag_list_);
// Send out hierarchy advertisement since the tag list has changed
Packet *newp = makeUpdate(child_pcl,HIER_ADVS,PERIODIC_ADVERTS);
child_pcl->last_update_sent_ = now;
s.schedule(target_,newp,0);
}
while(level < highest_level_) {
if(myaddr_ == 45)
trace("Node %d: Updating tag lists, level %d",myaddr_,level);
lmnode = parent_pcl->pchildren_;
tag_list = NULL;
// Loop through all the children and add tags to tag_list
while(lmnode) {
if(lmnode->child_flag_ == IS_CHILD) {
tag_ptr1 = lmnode->tag_list_;
while(tag_ptr1) {
if(!tag_list) {
tag_list = new compr_taglist;
tag_ptr2 = tag_list;
}
else {
tag_ptr2->next_ = new compr_taglist;
tag_ptr2 = tag_ptr2->next_;
}
// trace("tag name: %d.%d.%d",(tag_ptr1->obj_name_ >> 24) & 0xFF,(tag_ptr1->obj_name_ >> 16) & 0xFF,(tag_ptr1->obj_name_) & 0xFFFF);
tag_ptr2->obj_name_ = tag_ptr1->obj_name_;
tag_ptr1 = tag_ptr1->next_;
}
}
lmnode = lmnode->next_;
}
// Aggregate tag_list
agg_tags = aggregate_taginfo(tag_list,parent_pcl->level_,&num_tags);
// Delete tag_list
tag_ptr1 = tag_list;
while(tag_ptr1) {
tag_ptr2 = tag_ptr1;
tag_ptr1 = tag_ptr1->next_;
delete tag_ptr2;
}
if(!compare_tag_lists(parent_pcl->tag_list_,parent_pcl->num_tags_,agg_tags,num_tags)) {
// Delete parent_pcl's tag_list and update with new tag_list
tag_ptr1 = parent_pcl->tag_list_;
while(tag_ptr1) {
tag_ptr2 = tag_ptr1;
tag_ptr1 = tag_ptr1->next_;
delete tag_ptr2;
}
parent_pcl->tag_list_ = agg_tags;
parent_pcl->num_tags_ = num_tags;
// Send out hierarchy advertisement since the tag list has changed
Packet *newp = makeUpdate(parent_pcl,HIER_ADVS,PERIODIC_ADVERTS);
parent_pcl->last_update_sent_ = now;
s.schedule(target_,newp,0);
++level;
// Update our tag list in the higher level pcl object
pcl = parent_children_list_;
parent_pcl = NULL;
child_pcl = NULL;
while(pcl) {
if(pcl->level_ == level)
child_pcl = pcl;
else if(pcl->level_ == level + 1)
parent_pcl = pcl;
pcl = pcl->next_;
}
upd_time = (int) now;
upd_time = (upd_time << 16) | (parent_pcl->seqnum_ & 0xFFFF);
++(parent_pcl->seqnum_);
parent_pcl->UpdatePotlChild(myaddr_, myaddr_,0,0,0,(int) node_->energy(),upd_time,IS_CHILD,FALSE,child_pcl->tag_list_);
}
else {
// Delete agg_tags
tag_ptr1 = agg_tags;
while(tag_ptr1) {
tag_ptr2 = tag_ptr1;
tag_ptr1 = tag_ptr1->next_;
delete tag_ptr2;
}
break;
}
}
}
int
LandmarkAgent::compare_tag_lists(compr_taglist *tag_list1, int num_tags1, compr_taglist *tag_list2, int num_tags2)
{
compr_taglist *tag1 = tag_list1, *tag2 = tag_list2;
int found;
// if num_tags == -1, it means that the number of tags was not computed
if(num_tags1 == -1) {
num_tags1 = 0;
while(tag1) {
++num_tags1;
tag1 = tag1->next_;
}
tag1 = tag_list1;
}
if(num_tags2 == -1) {
num_tags2 = 0;
while(tag2) {
++num_tags2;
tag2 = tag2->next_;
}
tag2 = tag_list2;
}
if(num_tags1 != num_tags2)
return(FALSE);
while(tag1) {
found = 0;
while(tag2) {
if(tag1->obj_name_ == tag2->obj_name_) {
found = 1;
break;
}
tag2 = tag2->next_;
}
if(!found)
return(FALSE);
tag1 = tag1->next_;
}
return(TRUE);
}
sensor-query.cc
extern "C" {
#include
#include
};
#include "sensor-query.h"
#include "landmark.h"
#include
#define CONST_INTERVAL 30
static class SensorQueryClass:public TclClass
{
public:
SensorQueryClass ():TclClass ("Agent/SensorQuery")
{
}
TclObject *create (int, const char *const *)
{
return (new SensorQueryAgent ());
}
} class_sensor_query;
void SensorQueryAgent::
trace (char *fmt,...)
{
va_list ap; // Define a variable ap that will refer to each argument in turn
if (!tracetarget_)
return;
// Initializes ap to first argument
va_start (ap, fmt);
// Prints the elements in turn
vsprintf (tracetarget_->buffer (), fmt, ap);
tracetarget_->dump ();
// Does the necessary clean-up before returning
va_end (ap);
}
void
SensorQueryAgent::stop()
{
trace("Node %d: SensorQueryAgent going down at time %f",myaddr_,NOW);
// Event id > 0 implies presence on the event queue
if(gen_query_event_->uid_) {
Scheduler &s = Scheduler::instance();
s.cancel(gen_query_event_);
}
node_dead_ = 1;
}
int
SensorQueryAgent::command (int argc, const char *const *argv)
{
if (argc == 2)
{
if (strcmp (argv[1], "start") == 0)
{
startUp();
return (TCL_OK);
}
if (strcmp (argv[1], "stop") == 0)
{
stop();
return (TCL_OK);
}
if (strcmp (argv[1], "generate-query") == 0)
{
generate_query(-1,-1,-1);
return (TCL_OK);
}
}
else if (argc == 3)
{
if (strcasecmp (argv[1], "tracetarget") == 0)
{
TclObject *obj;
if ((obj = TclObject::lookup (argv[2])) == 0)
{
fprintf (stderr, "%s: %s lookup of %s failed\n", __FILE__, argv[1],
argv[2]);
return TCL_ERROR;
}
tracetarget_ = (Trace *) obj;
return TCL_OK;
}
else if (strcasecmp (argv[1], "addr") == 0) {
int temp;
temp = Address::instance().str2addr(argv[2]);
myaddr_ = temp;
return TCL_OK;
}
else if (strcasecmp (argv[1], "attach-tag-dbase") == 0)
{
TclObject *obj;
if ((obj = TclObject::lookup (argv[2])) == 0)
{
fprintf (stderr, "%s: %s lookup of %s failed\n", __FILE__, argv[1],
argv[2]);
return TCL_ERROR;
}
tag_dbase_ = (tags_database *) obj;
return TCL_OK;
}
}
else if (argc == 5) {
if (strcmp (argv[1], "generate-query") == 0)
{
int p1 = atoi(argv[2]);
int p2 = atoi(argv[3]);
int p3 = atoi(argv[4]);
generate_query(p1,p2,p3);
return (TCL_OK);
}
}
return (Agent::command (argc, argv));
}
void
SensorQueryAgent::startUp()
{
// Scheduler &s = Scheduler::instance();
// double sched_time = CONST_INTERVAL + Random::uniform(query_interval_);
// trace("Node %d: Scheduling query gen at %f from now %f",myaddr_,sched_time,s.clock());
// s.schedule(query_handler_, gen_query_event_, Random::uniform(query_interval_));
trace("Node %d: SensorQueryAgent starting up at time %f",myaddr_,NOW);
node_dead_ = 0;
}
//void
//SensorQueryAgent::handle(Event *) {
// Scheduler &s = Scheduler::instance();
// generate_query();
// double sched_time = Random::uniform(query_interval_);
// trace("Node %d: Scheduling query gen at %f from now %f",myaddr_,sched_time,s.clock());
// s.schedule(this, gen_query_event_, Random::uniform(query_interval_));
//}
void
SensorQueryAgent::generate_query(int p1, int p2, int p3)
{
Packet *p = allocpkt();
hdr_ip *iph = (hdr_ip *) p->access(off_ip_);
hdr_cmn *hdrc = HDR_CMN(p);
unsigned char *walk;
Scheduler &s = Scheduler::instance();
int obj_name;
int next_hop_level = 0, num_src_hops = 0;
int X = 65000, Y = 65000;
int action = QUERY_PKT;
if(node_dead_) {
trace("Node %d: node failed, cannot generate query");
return;
}
// Need to ask our routing module to direct the packet
hdrc->next_hop_ = myaddr_;
hdrc->addr_type_ = NS_AF_INET;
iph->daddr() = myaddr_;
iph->dport() = ROUTER_PORT;
iph->ttl_ = 300; // since only 300 ids in source route in packet
iph->saddr() = myaddr_;
iph->sport() = 0;
// LM agent checks if the source port is 0 to identify a query packet
// 2 bytes each for X and Y co-ords
// 1 byte for level - used in next hop lookup
// 4 bytes for object name; 2 byte for number of hops in src route
// 4 bytes for origin_time
// 90 bytes to store 30 source route ids and their levels (1 byte each)
// (assuming max_levels = 15)
p->allocdata(105);
walk = p->accessdata();
(*walk++) = action & 0xFF;
// X coord = 65000 initially
(*walk++) = (X >> 8) & 0xFF;
(*walk++) = X & 0xFF;
// Y coord = 65000 initially
(*walk++) = (Y >> 8) & 0xFF;
(*walk++) = Y & 0xFF;
// Indicates next hop level
(*walk++) = next_hop_level & 0xFF;
if(p1 != -1 && p2 != -1 && p3 != -1)
obj_name = (int) ((p1 * pow(2,24)) + (p2 * pow(2,16)) + p3) ;
else
obj_name = tag_dbase_->get_random_tag();
// p1 = Random::integer(10);
// p2 = Random::integer(10);
// p3 = Random::integer(50);
(*walk++) = (obj_name >> 24) & 0xFF;
(*walk++) = (obj_name >> 16) & 0xFF;
(*walk++) = (obj_name >> 8) & 0xFF;
(*walk++) = (obj_name) & 0xFF;
double now = Scheduler::instance().clock();
trace("Node %d: Generating query for object %d.%d.%d at time %f",myaddr_,(obj_name >> 24) & 0xFF,(obj_name >> 16) & 0xFF, obj_name & 0xFFFF,now);
int origin_time = (int) now;
(*walk++) = (origin_time >> 24) & 0xFF;
(*walk++) = (origin_time >> 16) & 0xFF;
(*walk++) = (origin_time >> 8) & 0xFF;
(*walk++) = (origin_time) & 0xFF;
// Number of source route hops in packet (= 0); 2 bytes
(*walk++) = (num_src_hops >> 8) & 0xFF;
(*walk++) = (num_src_hops) & 0xFF;
// Above fields will be 4 bytes each. 20 bytes for the IP header will be
// added in LM agent. No source route hops on query creation.
hdrc->size_ = 24;
hdrc->direction() = hdr_cmn::DOWN;
s.schedule(target_,p,0);
// double sched_time = CONST_INTERVAL + Random::uniform(query_interval_);
// trace("Node %d: Scheduling query gen at %f from now %f",myaddr_,sched_time,s.clock());
// s.schedule(query_handler_, gen_query_event_, sched_time);
// s.schedule(this, gen_query_event_, Random::uniform(query_interval_));
}
void
SensorQueryAgent::recv(Packet *p, Handler *)
{
unsigned char *walk = p->accessdata();
int X = 0, Y = 0, obj_name = -1;
if(node_dead_) {
Packet::free(p);
return;
}
++walk;
X = *walk++;
X = (X << 8) | *walk++;
Y = *walk++;
Y = (Y << 8) | *walk++;
++walk;
obj_name = *walk++;
obj_name = (obj_name << 8) | *walk++;
obj_name = (obj_name << 8) | *walk++;
obj_name = (obj_name << 8) | *walk++;
double now = Scheduler::instance().clock();
trace("Node %d: SensorQueryAgent Found object %d.%d.%d at (%d,%d) at time %f", myaddr_, (obj_name >> 24) & 0xFF, (obj_name >> 16) & 0xFF, obj_name & 0xFFFF,X,Y,now);
Packet::free(p);
}
SensorQueryAgent::SensorQueryAgent() : Agent(PT_MESSAGE) {
query_interval_ = 120;
gen_query_event_ = new Event;
query_handler_ = new SensorQueryHandler(this);
node_dead_ = 0;
}
tags.cc
// Author: Satish Kumar, kkumar@isi.edu
extern "C" {
#include
#include
};
#include "tags.h"
#include "random.h"
#include
#define MAX_P1 10
#define MAX_P2 10
// Split into 10 rectangles at each level. Have two levels for now.
static class TagDbaseClass:public TclClass
{
public:
TagDbaseClass ():TclClass ("TagDbase")
{
}
TclObject *create (int, const char *const *)
{
return (new tags_database ());
}
} class_tags_database;
void tags_database::
trace (char *fmt,...)
{
va_list ap; // Define a variable ap that will refer to each argument in turn
if (!tracetarget_)
return;
// Initializes ap to first argument
va_start (ap, fmt);
// Prints the elements in turn
vsprintf (tracetarget_->buffer (), fmt, ap);
tracetarget_->dump ();
// Does the necessary clean-up before returning
va_end (ap);
}
int
tags_database::command (int argc, const char *const *argv)
{
if (argc == 7)
{
if (strcmp (argv[1], "create_database") == 0)
{
double x_min = atof(argv[2]);
double x_max = atof(argv[3]);
double y_min = atof(argv[4]);
double y_max = atof(argv[5]);
int num_tags = atoi(argv[6]);
num_tags_ = num_tags;
sensed_tag_list_ = new int[num_tags];
freq_qry_tag_list_ = new int[num_tags];
create_tags_database(x_min,x_max,y_min,y_max,num_tags);
return (TCL_OK);
}
}
else if (argc == 3)
{
if (strcasecmp (argv[1], "tracetarget") == 0)
{
TclObject *obj;
if ((obj = TclObject::lookup (argv[2])) == 0)
{
fprintf (stderr, "%s: %s lookup of %s failed\n", __FILE__, argv[1],
argv[2]);
return TCL_ERROR;
}
tracetarget_ = (Trace *) obj;
return TCL_OK;
}
}
return (TclObject::command(argc, argv));
}
void
tags_database::create_tags_database(double x_min, double x_max, double y_min, double y_max, int num_tags)
{
dbase_node *dbnode;
int i;
assert((x_min <= x_max) && (y_min <= y_max));
// Creating the data structures for storing the tags information
tags_db_ = new dbase_node(x_min, x_max, y_min, y_max);
// Creates child nodes for tags_db_ and partitions x and y ranges
add_level(x_min, x_max, y_min, y_max, tags_db_);
// Creates child nodes for each of tags_db_'s children
for(i = 0; i < NUM_RECTANGLES; ++i) {
dbnode = tags_db_->list_node_[i];
assert((dbnode->x_min_ <= dbnode->x_max_) && (dbnode->y_min_ <= dbnode->y_max_));
add_level(dbnode->x_min_, dbnode->x_max_, dbnode->y_min_, dbnode->y_max_, dbnode);
}
// Creating the tags and adding them to the database
tag *newtag = new tag();
i = 0;
int p1 = 1, p2 = 1, p3 = 1;
int max_p1 = MAX_P1, max_p2 = MAX_P2;
int max_p3 = num_tags/(MAX_P1 * MAX_P2);
while(i < num_tags) {
newtag->x_ = x_min + rn_->uniform(x_max-x_min);
newtag->y_ = y_min + rn_->uniform(y_max-y_min);
newtag->obj_name_ = (int)((p1 * pow(2,24)) + (p2 * pow(2,16)) + p3) ;
++p3;
if(p3 == max_p3) {
p3 = 1;
++p2;
if(p2 == max_p2) {
p2 = 1;
++p1;
if(p1 == max_p1) {
break;
}
}
}
Addtag(newtag);
// printf("Added object %d.%d.%d at (%f,%f)\n",(newtag->obj_name_ >> 24) & 0xFF,(newtag->obj_name_ >> 16) & 0xFF,(newtag->obj_name_) & 0xFFFF,newtag->x_,newtag->y_);
++i;
}
delete newtag;
}
void
tags_database::add_level(double x_min, double x_max, double y_min, double y_max, dbase_node *dbnode)
{
double x1, x2, y1, y2, x_partition_size;
int i;
// Just partitioning along x-range for now
x_partition_size = (x_max-x_min)/NUM_RECTANGLES;
x2 = x_min;
y1 = y_min;
y2 = y_max;
for(i = 0; i < NUM_RECTANGLES; ++i) {
x1 = x2; // x_min for partition
x2 = x1 + x_partition_size; // x_max for partition
// The last partition should cover the remaining ranges
if (i == (NUM_RECTANGLES - 1)) {
x2 = x_max;
}
dbnode->list_node_[i] = new dbase_node(x1,x2,y1,y2);
}
}
void
tags_database::Addtag(const tag* tag_)
{
dbase_node *dbnode = tags_db_;
int i, found = FALSE;
tag *new_tag;
while(dbnode->list_node_[0] != NULL) {
for(i = 0; i < NUM_RECTANGLES; ++i) {
if(((dbnode->list_node_[i])->x_min_ <= tag_->x_) && ((dbnode->list_node_[i])->x_max_ >= tag_->x_)) {
found = TRUE;
break;
}
}
assert(found);
dbnode = dbnode->list_node_[i];
}
if(dbnode->tags_list_ == NULL) {
dbnode->tags_list_ = new tag;
new_tag = dbnode->tags_list_;
}
else {
new_tag = dbnode->tags_list_;
while(new_tag->next_ != NULL) {
new_tag = new_tag->next_;
}
new_tag->next_ = new tag;
new_tag = new_tag->next_;
}
// tags do not have any attributes for now
new_tag->x_ = tag_->x_;
new_tag->y_ = tag_->y_;
new_tag->obj_name_ = tag_->obj_name_;
}
void
tags_database::Deletetag(const tag *tag_)
{
dbase_node *dbnode = tags_db_;
int i, found = FALSE;
tag *old_tag;
while(dbnode->list_node_[0] != NULL) {
for(i = 0; i < NUM_RECTANGLES; ++i) {
if(((dbnode->list_node_[i])->x_min_ <= tag_->x_) && ((dbnode->list_node_[i])->x_max_ >= tag_->x_)) {
found = TRUE;
break;
}
}
assert(found);
dbnode = dbnode->list_node_[i];
}
assert(dbnode->tags_list_ != NULL);
//old_tag will point to the tag entry that is to be deleted
found = FALSE;
old_tag = dbnode->tags_list_;
tag *prev_tag = old_tag; // Should have previous tag in list
while(old_tag != NULL) {
if ((old_tag->x_== tag_->x_) && (old_tag->y_ == tag_->y_) && (old_tag->obj_name_ == tag_->obj_name_)) {
found = TRUE;
break;
}
prev_tag = old_tag;
old_tag = old_tag->next_;
}
assert(found);
prev_tag->next_ = old_tag->next_;
delete old_tag;
}
compr_taglist *
tags_database::Gettags(double x, double y, double r)
{
dbase_node *dbnode = tags_db_;
compr_taglist *tptr;
int i, found = FALSE;
vtags_ = NULL;
// This interior node should have child nodes
// assert(dbnode->list_node_[0] != NULL);
search_tags_dbase(x,y,r,dbnode);
tptr = vtags_;
while(tptr) {
found = FALSE;
for(i = 0; i < num_sensed_tags_; ++i) {
if(tptr->obj_name_ == sensed_tag_list_[i]) {
found = TRUE;
break;
}
}
if(!found) {
// int r = Random::uniform(4);
// 20 % of objects stored in frequently queried objects list; others
// in sensed_tag_list_;
// if(r == 0) {
// freq_qry_tag_list_[num_freq_qry_tags_] = tptr->obj_name_;
// ++num_freq_qry_tags_;
// }
// else {
sensed_tag_list_[num_sensed_tags_] = tptr->obj_name_;
++num_sensed_tags_;
// }
}
tptr = tptr->next_;
}
assert(num_sensed_tags_ <= num_tags_);
return(vtags_);
}
int
tags_database::get_random_tag()
{
// Objects in freq_qry_tags_ are queried 10 times as often as those in
// sensed_tag_list_
// int r = Random::uniform(10);
// if(r == 0) {
int i = rn_->uniform(num_sensed_tags_);
return(sensed_tag_list_[i]);
// }
// else {
// int i = rn_->uniform(num_freq_qry_tags_);
// return(freq_qry_tag_list_[i]);
// }
}
void
tags_database::search_tags_dbase(double x, double y, double r, dbase_node *dbnode)
{
int i, found = FALSE, removed_tag = 0;
compr_taglist **apt_tags;
tag *prev_tag, *next_tag;
tag *dbase_tags;
dbase_node *child_dbnode;
// If this is a leaf interior node, lookup the taglist for appropriate tags
if(dbnode->list_node_[0] == NULL) {
apt_tags = &vtags_;
while((*apt_tags) != NULL)
apt_tags = &((*apt_tags)->next_);
dbase_tags = dbnode->tags_list_;
prev_tag = dbase_tags;
while(dbase_tags) {
removed_tag = 0;
double xpos = (dbase_tags->x_ - x) * (dbase_tags->x_ - x);
double ypos = (dbase_tags->y_ - y) * (dbase_tags->y_ - y);
if((xpos + ypos) < (r*r)) {
*apt_tags = new compr_taglist;
(*apt_tags)->obj_name_ = dbase_tags->obj_name_;
apt_tags = &((*apt_tags)->next_);
// Delete tag from the list so that only one sensor observes
// a particular tag
removed_tag = 1;
if(prev_tag == dbase_tags) {
dbnode->tags_list_ = dbase_tags->next_;
prev_tag = dbase_tags->next_;
}
else
prev_tag->next_ = dbase_tags->next_;
next_tag = dbase_tags->next_;
delete dbase_tags;
}
if(!removed_tag) {
prev_tag = dbase_tags;
dbase_tags = dbase_tags->next_;
}
else {
dbase_tags = next_tag;
}
}
return;
}
for(i = 0; i < NUM_RECTANGLES; ++i) {
found = FALSE;
// Check if x-r is in the x-range of this node
if(((dbnode->list_node_[i])->x_min_ <= (x - r)) && ((dbnode->list_node_[i])->x_max_ >= (x - r)))
found = TRUE;
// Check if x+r is in the x-range of this node
if(((dbnode->list_node_[i])->x_min_ <= (x + r)) && ((dbnode->list_node_[i])->x_max_ >= (x + r)))
found = TRUE;
// Check if the range (x-r,x+r) covers the x-range of this node
if(((dbnode->list_node_[i])->x_min_ >= (x - r)) && ((dbnode->list_node_[i])->x_max_ <= (x + r)))
found = TRUE;
if(found) {
child_dbnode = dbnode->list_node_[i];
search_tags_dbase(x,y,r,child_dbnode);
}
}
}
tora.cc
/* -*- 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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*/
/* Ported from CMU/Monarch's code*/
/*
tora.cc
$Id: AllCode__.html 980 2002-12-12 09:36:58Z ffilali $
*/
#include
#include
#include
#include
#include
#include
#include
#define LOG(s) \
fprintf(stdout, "%s --- %s (index: %d, time: %f)\n", \
__PRETTY_FUNCTION__, (s), index, Scheduler::instance().clock())
// #define DEBUG
#define CURRENT_TIME Scheduler::instance().clock()
/* sec of spacing inserted between pkts when a bunch of packets are
dumped into the link layer all at once. Allows arp time to resolve
dst, preventing the dumping of all but the last pkt on the floor */
#define ARP_SEPARATION_DELAY 0.030
/* ======================================================================
TCL Hooks
====================================================================== */
static class TORAHeaderClass : public PacketHeaderClass {
public:
TORAHeaderClass() : PacketHeaderClass("PacketHeader/TORA",
TORA_HDR_LEN) { }
} class_toraAgent_hdr;
static class toraAgentclass : public TclClass {
public:
toraAgentclass() : TclClass("Agent/TORA") {}
TclObject* create(int argc, const char*const* argv) {
assert(argc == 5);
return (new toraAgent((nsaddr_t) atoi(argv[4])));
}
} class_toraAgent;
/* ======================================================================
toraAgent Class Functions
====================================================================== */
toraAgent::toraAgent(nsaddr_t id) :
rtAgent(id, PT_TORA),
rqueue()
{
bind("off_TORA_", &off_TORA_);
LIST_INIT(&dstlist);
imepagent = 0;
logtarget = 0;
ifqueue = 0;
}
void
toraAgent::reset()
{
Packet *p;
while((p = rqueue.deque()))
{
drop(p,DROP_END_OF_SIMULATION);
}
}
int
toraAgent::command(int argc, const char*const* argv)
{
if(argc == 2) {
Tcl& tcl = Tcl::instance();
if(strncasecmp(argv[1], "id", 2) == 0) {
tcl.resultf("%d", index);
return TCL_OK;
}
}
else if(argc == 3) {
if(strcmp(argv[1], "log-target") == 0 || strcmp(argv[1], "tracetarget") == 0 ) {
logtarget = (Trace*) TclObject::lookup(argv[2]);
if(logtarget == 0)
return TCL_ERROR;
return TCL_OK;
}
else if(strcmp(argv[1], "drop-target") == 0) {
int stat = rqueue.command(argc,argv);
if (stat != TCL_OK) return stat;
return Agent::command(argc, argv);
}
else if(strcmp(argv[1], "if-queue") == 0) {
ifqueue = (PriQueue*) TclObject::lookup(argv[2]);
if(ifqueue == 0)
return TCL_ERROR;
return TCL_OK;
}
else if(strcmp(argv[1], "imep-agent") == 0) {
imepagent = (imepAgent*) TclObject::lookup(argv[2]);
if(imepagent == 0)
return TCL_ERROR;
imepagent->imepRegister((rtAgent*) this);
return TCL_OK;
}
}
return Agent::command(argc, argv);
}
/* ======================================================================
Destination Management Functions
====================================================================== */
TORADest*
toraAgent::dst_find(nsaddr_t id)
{
TORADest* td = dstlist.lh_first;
for( ; td; td = td->link.le_next) {
if(td->index == id)
return td;
}
return 0;
}
TORADest*
toraAgent::dst_add(nsaddr_t id)
{
TORADest *td = new TORADest(id, this);
assert(td);
LIST_INSERT_HEAD(&dstlist, td, link);
int *nblist = 0, nbcnt = 0;
imepagent->imepGetBiLinks(nblist, nbcnt);
for(int i = 0; i < nbcnt; i++)
(void) td->nb_add(nblist[i]);
if(nblist) delete[] nblist;
return td;
}
void
toraAgent::dst_dump()
{
TORADest *td = dstlist.lh_first;
for( ; td; td = td = td->link.le_next)
td->dump();
}
/* ======================================================================
Route Resolution
====================================================================== */
void
toraAgent::forward(Packet *p, nsaddr_t nexthop, Time delay)
{
struct hdr_cmn *ch = HDR_CMN(p);
#ifdef TORA_DISALLOW_ROUTE_LOOP
if(nexthop == ch->prev_hop_) {
log_route_loop(ch->prev_hop_, nexthop);
drop(p, DROP_RTR_ROUTE_LOOP);
return;
}
#endif
ch->next_hop() = nexthop;
ch->prev_hop_ = ipaddr();
ch->addr_type() = NS_AF_INET;
// change the packet direction to DOWN
ch->direction() = hdr_cmn::DOWN;
if (0.0 == delay) {
tora_output(p);
} else {
Scheduler::instance().schedule(target_, p, delay);
}
}
void
toraAgent::rt_resolve(Packet *p)
{
struct hdr_ip *ih = HDR_IP(p);
TORADest *td;
TORANeighbor *tn;
td = dst_find(ih->daddr());
if(td == 0) {
td = dst_add(ih->daddr());
}
tn = td->nb_find_next_hop();
if(tn == 0) {
rqueue.enque(p);
trace("T %.9f _%d_ tora enq %d->%d",
Scheduler::instance().clock(), ipaddr(),
ih->src(), ih->dst());
if(!td->rt_req)
{ // if no QRY pending, then send one
sendQRY(ih->daddr());
td->time_tx_qry = CURRENT_TIME;
td->rt_req = 1;
}
}
else {
forward(p, tn->index);
}
}
/* ======================================================================
Incoming Packets
====================================================================== */
void
toraAgent::recv(Packet *p, Handler *)
{
struct hdr_cmn *ch = HDR_CMN(p);
struct hdr_ip *ih = HDR_IP(p);
assert(initialized());
//assert(p->incoming == 0);
if(ch->ptype() == PT_TORA) {
recvTORA(p);
return;
}
/*
* Must be a packet I'm originating...
*/
if(ih->saddr() == ipaddr() && ch->num_forwards() == 0) {
/*
* Add the IP Header.
*/
ch->size() += IP_HDR_LEN;
ih->ttl_ = IP_DEF_TTL;
}
#ifdef TORA_DISALLOW_ROUTE_LOOP
/*
* I received a packet that I sent. Probably
* a routing loop.
*/
else if(ih->saddr() == ipaddr()) {
drop(p, DROP_RTR_ROUTE_LOOP);
return;
}
#endif
/*
* Packet I'm forwarding...
*/
else {
/*
* Check the TTL. If it is zero, then discard.
*/
if(--ih->ttl_ == 0) {
drop(p, DROP_RTR_TTL);
return;
}
}
rt_resolve(p);
}
void
toraAgent::recvTORA(Packet *p)
{
struct hdr_ip *ih = HDR_IP(p);
struct hdr_tora *th = HDR_TORA(p);
TORADest *td;
TORANeighbor *tn;
/*
* Fix the source IP address.
*/
assert(ih->sport() == RT_PORT);
assert(ih->dport() == RT_PORT);
/*
* Incoming Packets.
*/
switch(th->th_type) {
case TORATYPE_QRY:
recvQRY(p);
Packet::free(p);
return; // don't add/change routing state
case TORATYPE_UPD:
log_recv_upd(p);
recvUPD(p);
break;
case TORATYPE_CLR:
log_recv_clr(p);
recvCLR(p);
break;
default:
fprintf(stderr,
"%s: Invalid TORA type (%x)\n",
__PRETTY_FUNCTION__, th->th_type);
exit(1);
}
if((td = dst_find(th->th_dst)) == 0) {
Packet::free(p);
return;
}
logNextHopChange(td);
if((tn = td->nb_find_next_hop())) {
Packet *p0;
Time delay = 0.0;
while((p0 = rqueue.deque(td->index))) {
forward(p0, tn->index, delay);
delay += ARP_SEPARATION_DELAY;
}
}
Packet::free(p);
}
/*
* IETF Draft - TORA Specification, section 3.7.6
*/
void
toraAgent::recvQRY(Packet *p)
{
struct hdr_ip *ih = HDR_IP(p);
struct hdr_tora_qry *qh = HDR_TORA_QRY(p);
TORADest *td;
TORANeighbor *tn;
if(qh->tq_dst == ipaddr()) {
#ifdef DEBUG
fprintf(stderr, "node %d received `QRY` for itself.\n", index);
#endif
return;
}
td = dst_find(qh->tq_dst);
if(td == 0)
td = dst_add(qh->tq_dst);
if(td->rt_req) {
return;
}
if(td->height.r == 0) { // II, A
tn = td->nb_find(ih->saddr());
if(tn && tn->time_act > td->time_upd) { // II, A, 1
td->time_upd = Scheduler::instance().clock();
sendUPD(td->index);
}
else { // II, A, 2
}
}
else {
tn = td->nb_find_min_height(0);
if(tn) { // II, B, 1
td->update_height(tn->height.tau,
tn->height.oid,
tn->height.r,
tn->height.delta + 1,
ipaddr());
td->time_upd = Scheduler::instance().clock();
sendUPD(td->index);
}
else {
td->rt_req = 1;
td->time_rt_req = CURRENT_TIME;
if(td->num_active > 1) { // II, B, 1, a
sendQRY(td->index);
}
else { // II, B, 1, b
}
}
}
}
/*
* IETF Draft - TORA Specification, section 3.7.7
*/
void
toraAgent::recvUPD(Packet *p)
{
struct hdr_ip *ih = HDR_IP(p);
struct hdr_tora_upd *uh = HDR_TORA_UPD(p);
TORADest *td;
TORANeighbor *tn;
if(uh->tu_dst == ipaddr()) {
return;
}
td = dst_find(uh->tu_dst);
if(td == 0)
td = dst_add(uh->tu_dst);
tn = td->nb_find(ih->saddr());
if(tn == 0) {
/*
* update link status? -josh
*/
// No, don't update linkstatus: it may be an update
// that was delayed in the IMEP layer for sequencing -dam
// no way at the TORA level to tell if we're connected...
trace("T %.9f _%d_ received `UPD` from non-neighbor %d",
CURRENT_TIME, ipaddr(), ih->src_);
#ifdef DEBUG
fprintf(stderr,
"node %d received `UPD` from non-neighbor %d\n",
index, ih->src_);
#endif
return;
}
/*
* Update height and link status for neighbor [j][k].
*/
td->update_height_nb(tn, uh);
if(td->rt_req && tn->height.r == 0) { // I
td->update_height(tn->height.tau,
tn->height.oid,
tn->height.r,
tn->height.delta + 1,
ipaddr());
td->rt_req = 0;
td->time_upd = Scheduler::instance().clock();
sendUPD(td->index);
}
else if(td->num_down == 0) { // II
if(td->num_up == 0) { // II, A
if(td->height.isNull()) // II, A, 1
return; // II, A, 1, a
else {
td->height.Null(); // II, A, 1, b
td->time_upd = Scheduler::instance().clock();
sendUPD(td->index);
}
}
else {
if(td->nb_check_same_ref()) { // II, A, 2
TORANeighbor *tn;
if( (tn = td->nb_find_min_height(0)) ) { // II, A, 2, a
td->update_height(tn->height.tau, // II, A, 2, a, i
tn->height.oid,
1,
0,
ipaddr());
td->time_upd = Scheduler::instance().clock();
sendUPD(td->index);
}
else {
if(td->height.oid == ipaddr()) { // II, A, 2, a, ii
double temp_tau = td->height.tau; // II, A, 2, a, ii, x
nsaddr_t temp_oid = td->height.oid;
td->height.Null();
td->num_down = 0;
td->num_up = 0;
/*
* For every active link n, if the neighbor connected
* via link n is the destination j, set HT_NEIGH[j][n]=ZERO
* and LNK_STAT[j][n] = DN.
* Otherwise, set HT_NEIGH[j][n] = NULL and LNK_STAT[j][n] = UN.
*/
for(tn = td->nblist.lh_first; tn; tn = tn->link.le_next) {
if(tn->index == td->index) {
tn->height.Zero();
tn->lnk_stat = LINK_DN;
}
else {
tn->height.Null();
tn->lnk_stat = LINK_UN;
}
}
sendCLR(td->index, temp_tau, temp_oid);
}
else {
td->update_height(Scheduler::instance().clock(), // II, A, 2, a, ii, y
ipaddr(),
0,
0,
ipaddr());
td->rt_req = 0;
td->time_upd = Scheduler::instance().clock();
#ifdef DEBUG
// under what circumstances does this rule fire?
// seems like it will prevent the detection of
// partitions...??? -dam 8/24/98
if (logtarget)
{
sprintf(logtarget->buffer(), "T %.9f _%d_ rule IIA2a(ii)x fires %d",
Scheduler::instance().clock(), ipaddr(), td->index);
logtarget->dump();
}
#endif
sendUPD(td->index);
}
}
}
else {
TORANeighbor *n = td->nb_find_max_height(); // II, A, 2, b
assert(n);
TORANeighbor *m = td->nb_find_min_nonnull_height(&n->height);
assert(m);
td->update_height(m->height.tau,
m->height.oid,
m->height.r,
m->height.delta - 1,
ipaddr());
td->time_upd = Scheduler::instance().clock();
sendUPD(td->index);
}
}
}
else { // II, B
}
}
/*
* IETF Draft - TORA Specification, section 3.7.8
*/
void
toraAgent::recvCLR(Packet *p)
{
struct hdr_ip *ih = HDR_IP(p);
struct hdr_tora_clr *th = HDR_TORA_CLR(p);
TORADest *td;
TORANeighbor *tn;
if(th->tc_dst == ipaddr()) {
return;
}
td = dst_find(th->tc_dst);
if(td == 0)
td = dst_add(th->tc_dst);
assert(td);
if(td->height.tau == th->tc_tau &&
td->height.oid == th->tc_oid &&
td->height.r == 1) { // I
double temp_tau = td->height.tau;
nsaddr_t temp_oid = td->height.oid;
td->height.Null();
td->num_up = 0;
td->num_down = 0;
for(tn = td->nblist.lh_first; tn; tn = tn->link.le_next) {
if(tn->index == td->index) {
tn->height.Zero();
tn->lnk_stat = LINK_DN;
}
else {
tn->height.Null();
tn->lnk_stat = LINK_UN;
}
}
if(td->num_active > 1) { // I, A
sendCLR(td->index, temp_tau, temp_oid);
}
else { // I, B
}
}
else {
tn = td->nb_find(ih->saddr()); // II
if(tn == 0) {
/*
* XXX - update link status?
*/
trace("T %.9f _%d_ received `CLR` from non-neighbor %d",
CURRENT_TIME, index, ih->src_);
#ifdef DEBUG
fprintf(stderr,
"node %d received `CLR` from non-neighbor %d\n",
index, ih->src_);
#endif
return;
}
tn->height.Null();
tn->lnk_stat = LINK_UN;
for(tn = td->nblist.lh_first; tn; tn = tn->link.le_next) {
if(tn->height.tau == th->tc_tau &&
tn->height.oid == th->tc_oid &&
tn->height.r == 1) {
tn->height.Null();
tn->lnk_stat = LINK_UN;
}
}
if(td->num_down == 0) { // II, A
if(td->num_up == 0) { // II, A, 1
if(td->height.isNull()) { // II, A, 1, a
}
else {
td->height.Null();
td->time_upd = Scheduler::instance().clock();
sendUPD(td->index);
}
}
else {
td->update_height(Scheduler::instance().clock(),
ipaddr(),
0,
0,
ipaddr());
td->rt_req = 0;
td->time_upd = Scheduler::instance().clock();
sendUPD(td->index);
}
}
else {
// II, B
}
}
}
void
toraAgent::trace(char* fmt, ...)
{
va_list ap;
if (!logtarget) return;
va_start(ap, fmt);
vsprintf(logtarget->buffer(), fmt, ap);
logtarget->dump();
va_end(ap);
}
tora_api.cc
/* -*- 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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*/
/* Ported from CMU/Monarch's code*/
/*
tora_api.cc
$Id: AllCode__.html 980 2002-12-12 09:36:58Z ffilali $
Implement the API used by IMEP
*/
#include
#define CURRENT_TIME Scheduler::instance().clock()
void
toraAgent::rtNotifyLinkUP(nsaddr_t index)
{
TORADest *td = dstlist.lh_first;
/*
* Update the destination lists...
*/
for( ; td; td = td->link.le_next) {
if(td->nb_find(index) == 0) {
(void) td->nb_add(index);
}
if (td->rt_req)
{ // must send a new query for this dest so the new
// neighbor can hear it
// IMEP will take care of aggregating all these into
// one physical pkt
trace("T %.9f _%d_ QRY %d for %d (rtreq set)",
Scheduler::instance().clock(), ipaddr(),
td->index, index);
sendQRY(td->index);
td->time_tx_qry = CURRENT_TIME;
}
}
}
void
toraAgent::rtNotifyLinkDN(nsaddr_t index)
{
TORADest *td = dstlist.lh_first;
/*
* Purge each Destination's Neighbor List
*/
for( ; td; td = td->link.le_next) {
if(td->nb_del(index)) {
/*
* Send an UPD packet if you've lost the last
* downstream link.
*/
sendUPD(td->index);
}
}
/*
* Now purge the Network Interface queues that
* may have packets destined for this broken
* neighbor.
*/
{
Packet *head = 0;
Packet *p;
Packet *np = 0;
while((p = ifqueue->filter(index))) {
p->next_ = head;
head = p;
}
for(p = head; p; p = np) {
np = p->next_;
/*
* This make a lot of sense for TORA since we
* will almost always have multiple routes to
* a destination.
*/
log_link_layer_recycle(p);
rt_resolve(p);
}
}
}
void
toraAgent::rtNotifyLinkStatus(nsaddr_t /* index */, u_int32_t /* status */)
{
abort();
}
void
toraAgent::rtRoutePacket(Packet *p)
{
struct hdr_cmn *ch = HDR_CMN(p);
struct hdr_ip *ip = HDR_IP(p);
// Non-data packets and BROADCAST packets can be dropped.
if(DATA_PACKET(ch->ptype()) == 0 ||
ip->daddr() == (nsaddr_t) IP_BROADCAST) {
drop(p, DROP_RTR_MAC_CALLBACK);
return;
}
rt_resolve(p);
}
tora_dest.cc
/* -*- 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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*/
/* Ported from CMU/Monarch's code */
/*
$Id: AllCode__.html 980 2002-12-12 09:36:58Z ffilali $
*/
#include
#include
#include
#include
#include
#include
#include
#define CURRENT_TIME Scheduler::instance().clock()
/* ======================================================================
TORADest Class Functions
====================================================================== */
TORADest::TORADest(nsaddr_t id, Agent *a) : height(id)
{
index = id;
rt_req = 0;
time_upd = 0.0;
time_rt_req = 0.0;
time_tx_qry = 0.0;
LIST_INIT(&nblist);
num_active = num_down = num_up = 0;
agent = (toraAgent*) a;
}
void
TORADest::dump()
{
TORANeighbor *tn = nb_find_next_hop();
fprintf(stdout, "\tDEST: %d, RT Req: %x, Time UPD: %f\n",
index, rt_req, time_upd);
fprintf(stdout, "\t\ttau: %f, oid: %d, r: %d, delta: %d, id: %d\n",
height.tau, height.oid, height.r, height.delta, height.id);
fprintf(stdout, "\t\tActive: %d, Down: %d, Up: %d, Next Hop: %d\n\n",
num_active, num_down, num_up, tn ? tn->index : -1);
for(tn = nblist.lh_first; tn; tn = tn->link.le_next)
tn->dump();
}
/* ====================================================================== */
TORANeighbor*
TORADest::nb_add(nsaddr_t id)
{
TORANeighbor *tn = new TORANeighbor(id, agent);
assert(tn);
LIST_INSERT_HEAD(&nblist, tn, link);
num_active += 1;
if(tn->index == index) {
tn->height.Zero();
tn->lnk_stat = LINK_DN;
num_down += 1;
}
return tn;
}
int
TORADest::nb_del(nsaddr_t id)
{
TORANeighbor *tn = nblist.lh_first;
for( ; tn; tn = tn->link.le_next) {
if(tn->index == id) {
num_active -= 1;
if(tn->lnk_stat == LINK_DN)
num_down -=1;
if(tn->lnk_stat == LINK_UP)
num_up -= 1;
LIST_REMOVE(tn, link);
delete tn;
/*
* Here's were we decide whether or not to
* do route maintenance.
*/
if(num_down == 0) {
if(num_up > 0) {
update_height(CURRENT_TIME,
index,
0,
0,
index);
} else {
update_height(-1,
-1,
-1,
-1,
index);
// set height to NULL
}
agent->logNbDeletedLastDN(this);
return 1;
// send an UPDATE packet
}
return 0;
}
}
return 0;
}
TORANeighbor*
TORADest::nb_find(nsaddr_t id)
{
TORANeighbor *tn = nblist.lh_first;
for( ; tn; tn = tn->link.le_next) {
if(tn->index == id)
return tn;
}
return 0;
}
TORANeighbor*
TORADest::nb_find_next_hop()
{
TORANeighbor *tn = nblist.lh_first;
TORANeighbor *tn_min = 0;
for( ; tn; tn = tn->link.le_next) {
if(tn->height.isNull())
continue;
if(tn->lnk_stat != LINK_DN)
continue;
if(tn_min == 0 || tn_min->height.compare(&tn->height) < 0) {
tn_min = tn;
}
}
assert(tn_min == 0 || tn_min->lnk_stat == LINK_DN);
return tn_min;
}
/*
* Find a neighbor of minimum height, subject to the constraint
* that height.r == R.
*/
TORANeighbor*
TORADest::nb_find_min_height(int R)
{
TORANeighbor *tn = nblist.lh_first;
TORANeighbor *tn_min = 0;
for( ; tn; tn = tn->link.le_next) {
if(tn->height.r == R) {
if(tn_min == 0 ||
tn_min->height.compare(&tn->height) < 0)
tn_min = tn;
}
}
return tn_min;
}
/*
* Find a neighbor of minimum height, subject to the constraint
* that height.(tau,oid,r) == (tau,oid,r) and height != NULL
*/
TORANeighbor*
TORADest::nb_find_min_nonnull_height(Height *h)
{
TORANeighbor *tn = nblist.lh_first;
TORANeighbor *tn_min = 0;
assert(h);
for( ; tn; tn = tn->link.le_next) {
if(tn->height.isNull() == 0 &&
tn->height.tau == h->tau &&
tn->height.oid == h->oid &&
tn->height.r == h->r) {
if(tn_min == 0 ||
tn_min->height.compare(&tn->height) < 0)
tn_min = tn;
}
}
return tn_min;
}
/*
* Find a neighbor of maximum height, subject to the constraint
* that height != NULL.
*/
TORANeighbor*
TORADest::nb_find_max_height()
{
TORANeighbor *tn = nblist.lh_first;
TORANeighbor *tn_max = 0;
for( ; tn; tn = tn->link.le_next) {
if(tn->height.isNull() == 0) {
if(tn_max == 0 ||
tn_max->height.compare(&tn->height) > 0)
tn_max = tn;
}
}
return tn_max;
}
// Verify that all neighbors of non-null height have the same reference
// level.
int
TORADest::nb_check_same_ref()
{
TORANeighbor *tn = nblist.lh_first;
TORANeighbor *tref = 0;
for( ; tn; tn = tn->link.le_next) {
if(tn->height.isNull() == 0) {
if(tref == 0)
tref = tn;
else if(tref->height.tau != tn->height.tau ||
tref->height.oid != tn->height.oid ||
tref->height.r != tn->height.r)
return 0;
}
}
return 1;
}
void
TORADest::update_height_nb(TORANeighbor *tn, struct hdr_tora_upd *uh)
{
Height h(uh->tu_id);
h.tau = uh->tu_tau;
h.oid = uh->tu_oid;
h.r = uh->tu_r;
h.delta = uh->tu_delta;
tn->height.update(&h);
/*
* Update num_down/num_up
*/
if(tn->lnk_stat == LINK_DN)
num_down -= 1;
if(tn->lnk_stat == LINK_UP)
num_up -= 1;
tn->update_link_status(&height);
/*
* Update num_down/num_up
*/
if(tn->lnk_stat == LINK_DN)
num_down += 1;
if(tn->lnk_stat == LINK_UP)
num_up += 1;
}
/*
* Updates the height of a destination and then recomputes
* all of the link status information for the neighbors.
*/
void
TORADest::update_height(double TAU, nsaddr_t OID, int R, int DELTA, nsaddr_t ID)
{
TORANeighbor *tn = nblist.lh_first;
height.tau = TAU;
height.oid = OID;
height.r = R;
height.delta = DELTA;
height.id = ID;
#ifdef LOGGING
agent->log_dst_state_change(this);
#endif
num_active = num_down = num_up = 0;
for( ; tn; tn = tn->link.le_next) {
tn->update_link_status(&height);
num_active += 1;
if(tn->lnk_stat == LINK_DN)
num_down += 1;
if(tn->lnk_stat == LINK_UP)
num_up += 1;
}
}
tora_io.cc
/* -*- 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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*/
/* Ported from CMU/Monarch's code*/
/*
$Id: AllCode__.html 980 2002-12-12 09:36:58Z ffilali $
marshall TORA packets
*/
#include
#include
/* ======================================================================
Outgoing Packets
====================================================================== */
void
toraAgent::sendQRY(nsaddr_t id)
{
Packet *p = Packet::alloc();
struct hdr_cmn *ch = HDR_CMN(p);
struct hdr_ip *ih = HDR_IP(p);
struct hdr_tora_qry *th = HDR_TORA_QRY(p);
ch->uid() = uidcnt_++;
ch->ptype() = PT_TORA;
ch->size() = QRY_HDR_LEN;
ch->iface() = -2;
ch->error() = 0;
ch->addr_type() = NS_AF_NONE;
ch->prev_hop_ = ipaddr();
ih->saddr() = ipaddr();
ih->daddr() = IP_BROADCAST;
ih->sport() = RT_PORT;
ih->dport() = RT_PORT;
ih->ttl_ = 1;
th->tq_type = TORATYPE_QRY;
th->tq_dst = id;
trace("T %.9f _%d_ tora sendQRY %d",
Scheduler::instance().clock(), ipaddr(), id);
tora_output(p);
}
void
toraAgent::sendUPD(nsaddr_t id)
{
Packet *p = Packet::alloc();
struct hdr_cmn *ch = HDR_CMN(p);
struct hdr_ip *ih = HDR_IP(p);
struct hdr_tora_upd *th = HDR_TORA_UPD(p);
TORADest *td;
td = dst_find(id);
assert(td);
ch->uid() = uidcnt_++;
ch->ptype() = PT_TORA;
ch->size() = UPD_HDR_LEN;
ch->iface() = -2;
ch->error() = 0;
ch->addr_type() = NS_AF_NONE;
ch->prev_hop_ = ipaddr();
ih->saddr() = ipaddr();
ih->daddr() = IP_BROADCAST;
ih->sport() = RT_PORT;
ih->dport() = RT_PORT;
ih->ttl_ = 1;
th->tu_type = TORATYPE_UPD;
th->tu_dst = id;
th->tu_tau = td->height.tau;
th->tu_oid = td->height.oid;
th->tu_r = td->height.r;
th->tu_delta = td->height.delta;
th->tu_id = td->height.id;
tora_output(p);
}
void
toraAgent::sendCLR(nsaddr_t id, double tau, nsaddr_t oid)
{
Packet *p = Packet::alloc();
struct hdr_cmn *ch = HDR_CMN(p);
struct hdr_ip *ih = HDR_IP(p);
struct hdr_tora_clr *th = HDR_TORA_CLR(p);
ch->uid() = uidcnt_++;
ch->ptype() = PT_TORA;
ch->size() = CLR_HDR_LEN;
ch->iface() = -2;
ch->error() = 0;
ch->addr_type() = NS_AF_NONE;
ch->prev_hop_ = ipaddr();
ih->saddr() = ipaddr();
ih->daddr() = IP_BROADCAST;
ih->sport() = RT_PORT;
ih->dport() = RT_PORT;
ih->ttl_ = 1;
th->tc_type = TORATYPE_CLR;
th->tc_dst = id;
th->tc_tau = tau;
th->tc_oid = oid;
tora_output(p);
}
void
toraAgent::tora_output(Packet *p)
{
target_->recv(p, (Handler*) 0);
}
tora_logs.cc
/*
$Id: AllCode__.html 980 2002-12-12 09:36:58Z ffilali $
*/
#include
#include
#include
#include
#include
#include
#include
#define CURRENT_TIME Scheduler::instance().clock()
static const int verbose = 0;
/* ======================================================================
Logging Functions
====================================================================== */
void
toraAgent::log_route_loop(nsaddr_t prev, nsaddr_t next)
{
if(! logtarget || ! verbose ) return;
sprintf(logtarget->buffer(),
"T %.9f _%d_ routing loop (%d --> %d --> %d)",
CURRENT_TIME, ipaddr(), prev, ipaddr(), next);
logtarget->dump();
}
void
toraAgent::log_link_layer_feedback(Packet *p)
{
static int link_layer_feedback = 0;
struct hdr_cmn *ch = HDR_CMN(p);
if(! logtarget || ! verbose) return;
sprintf(logtarget->buffer(),
"T %.9f _%d_ LL unable to deliver packet %d to %d (%d) (reason = %d, ifqlen = %d)",
CURRENT_TIME,
ipaddr(),
ch->uid_,
ch->next_hop_,
++link_layer_feedback,
ch->xmit_reason_,
ifqueue->length());
logtarget->dump();
}
void
toraAgent::log_link_layer_recycle(Packet *p)
{
struct hdr_cmn *ch = HDR_CMN(p);
struct hdr_ip *ih = HDR_IP(p);
if(! logtarget || ! verbose) return;
sprintf(logtarget->buffer(),
"T %.9f _%d_ recycling packet %d (src = %d, dst = %d, prev = %d, next = %d)",
CURRENT_TIME,
ipaddr(),
ch->uid_,
// ih->src_, ih->dst_,
ih->saddr(),ih->daddr(),
ch->prev_hop_, ch->next_hop_);
logtarget->dump();
}
void
toraAgent::log_lnk_del(nsaddr_t dst)
{
static int link_del = 0;
if(! logtarget || ! verbose) return;
/*
* If "god" thinks that these two nodes are still
* reachable then this is an erroneous deletion.
*/
sprintf(logtarget->buffer(),
"T %.9f _%d_ deleting LL hop to %d (delete %d is %s)",
CURRENT_TIME,
ipaddr(),
dst,
++link_del,
God::instance()->hops(ipaddr(), dst) != 1 ? "VALID" : "INVALID");
logtarget->dump();
}
void
toraAgent::log_lnk_kept(nsaddr_t dst)
{
static int link_kept = 0;
if(! logtarget || ! verbose) return;
/*
* If "god" thinks that these two nodes are now
* unreachable, then we are erroneously keeping
* a bad route.
*/
sprintf(logtarget->buffer(),
"T %.9f _%d_ keeping LL hop to %d (keep %d is %s)",
CURRENT_TIME,
ipaddr(),
dst,
++link_kept,
God::instance()->hops(ipaddr(), dst) == 1 ? "VALID" : "INVALID");
logtarget->dump();
}
void
toraAgent::log_nb_del(nsaddr_t dst, nsaddr_t id)
{
if(! logtarget || ! verbose) return;
sprintf(logtarget->buffer(),
"T %.9f _%d_ destination %d removing neighbor %d",
CURRENT_TIME,
ipaddr(),
dst, id);
logtarget->dump();
}
void
toraAgent::log_recv_qry(Packet *p)
{
struct hdr_ip *ih = HDR_IP(p);
struct hdr_tora_qry *qh = HDR_TORA_QRY(p);
if(! logtarget || ! verbose) return;
sprintf(logtarget->buffer(),
"T %.9f %d received `QRY` from %d --- %d",
CURRENT_TIME, ipaddr(), ih->saddr(), qh->tq_dst);
logtarget->dump();
}
void
toraAgent::log_recv_upd(Packet *p)
{
struct hdr_ip *ih = HDR_IP(p);
struct hdr_tora_upd *uh = HDR_TORA_UPD(p);
if(! logtarget || ! verbose) return;
sprintf(logtarget->buffer(),
"T %.9f _%d_ received `UPD` from %d --- %d (%f %d %d %d %d)",
CURRENT_TIME,
ipaddr(),
// ih->src_, uh->tu_dst,
ih->saddr(), uh->tu_dst,
uh->tu_tau, uh->tu_oid, uh->tu_r, uh->tu_delta, uh->tu_id);
logtarget->dump();
}
void
toraAgent::log_recv_clr(Packet *p)
{
struct hdr_ip *ih = HDR_IP(p);
struct hdr_tora_clr *ch = HDR_TORA_CLR(p);
if(! logtarget || ! verbose) return;
sprintf(logtarget->buffer(),
"T %.9f _%d_ received `CLR` from %d --- %d (%f %d)",
CURRENT_TIME,
ipaddr(),
// ih->src_,
ih->saddr(),
ch->tc_dst, ch->tc_tau, ch->tc_oid);
logtarget->dump();
}
void
toraAgent::log_route_table()
{
TORADest *td;
TORANeighbor *tn;
if (!logtarget || ! verbose) return;
for(td = dstlist.lh_first; td; td = td->link.le_next) {
tn = td->nb_find_next_hop();
sprintf(logtarget->buffer(),
"T %.9f _%d_ %2d (%9f %2d %2d %2d %2d) ---> %2d (%9f %2d %2d %2d %2d) %d %.9f --- (%2d a, %2d d, %2d u) %d %9f",
CURRENT_TIME,
ipaddr(),
td->index,
td->height.tau, td->height.oid, td->height.r,
td->height.delta, td->height.id,
tn ? tn->index : -1,
tn ? tn->height.tau : -1.0,
tn ? tn->height.oid : -1,
tn ? tn->height.r : -1,
tn ? tn->height.delta : -1,
tn ? tn->height.id: -1,
tn ? tn->lnk_stat : -1,
tn ? tn->time_act : -1.0,
td->num_active, td->num_down, td->num_up,
td->rt_req, td->time_upd);
logtarget->dump();
}
sprintf(logtarget->buffer(),
"T --------------------------------------------------");
logtarget->dump();
}
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
void
toraAgent::logToraDest(TORADest *td)
{
if(! verbose) return;
assert(td);
sprintf(logtarget->buffer(),
"T %.9f _%d_ TD %2d (%9f %2d %2d %2d %2d) --- (%2d a, %2d d, %2d u) %d %9f",
CURRENT_TIME,
ipaddr(),
td->index,
td->height.tau, td->height.oid, td->height.r,
td->height.delta, td->height.id,
td->num_active, td->num_down, td->num_up,
td->rt_req, td->time_upd);
logtarget->dump();
}
void
toraAgent::logToraNeighbor(TORANeighbor *tn)
{
if(! verbose) return;
assert(tn);
sprintf(logtarget->buffer(),
"T %.9f _%d_ TN %2d (%.9f %2d %2d %2d %2d) %d %.9f",
CURRENT_TIME,
ipaddr(),
tn->index,
tn->height.tau,
tn->height.oid,
tn->height.r,
tn->height.delta,
tn->height.id,
tn->lnk_stat,
tn->time_act);
logtarget->dump();
}
void
toraAgent::logNextHopChange(TORADest *td)
{
if(! verbose) return;
TORANeighbor *n;
assert(td);
logToraDest(td);
for(n = td->nblist.lh_first; n; n = n->link.le_next)
logToraNeighbor(n);
n = td->nb_find_next_hop();
if(n) {
sprintf(logtarget->buffer(), "T %.9f _%d_ nexthop for %d is %d",
CURRENT_TIME, ipaddr(), td->index, n->index);
logtarget->dump();
}
sprintf(logtarget->buffer(),
"T %.9f _%d_ --------------------------------------------------",
CURRENT_TIME,
ipaddr());
logtarget->dump();
}
void
toraAgent::logNbDeletedLastDN(TORADest *td)
{
if(! verbose) return;
sprintf(logtarget->buffer(), "T %.9f _%d_ lost last downstream link for destination %d",
CURRENT_TIME,
ipaddr(),
td->index);
logtarget->dump();
logNextHopChange(td);
}
tora_neighbor.cc
/* -*- 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 Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 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.
*/
/* Ported from CMU/Monarch's code*/
/*
$Id: AllCode__.html 980 2002-12-12 09:36:58Z ffilali $
*/
#include
#include
#include
#include
#include
#include
#include
#define CURRENT_TIME Scheduler::instance().clock()
/* ======================================================================
TORANeighbor Class Functions
====================================================================== */
TORANeighbor::TORANeighbor(nsaddr_t id, Agent *a) : height(id)
{
index = id;
lnk_stat = LINK_UN;
time_act = Scheduler::instance().clock();
agent = (toraAgent*) a;
}
void
TORANeighbor::update_link_status(Height *h)
{
#ifdef LOGGING
int t = lnk_stat;
#endif
if(height.isNull())
lnk_stat = LINK_UN;
else if(h->isNull())
lnk_stat = LINK_DN;
else if(height.compare(h) > 0)
lnk_stat = LINK_DN;
else if(height.compare(h) < 0)
lnk_stat = LINK_UP;
#ifdef LOGGING
if(t != lnk_stat) {
agent->log_nb_state_change(this);
}
#endif
}
void
TORANeighbor::dump()
{
fprintf(stdout, "\t\tNEIG: %d, Link Status: %d, Time Active: %f\n",
index, lnk_stat, time_act);
fprintf(stdout, "\t\t\ttau: %f, oid: %d, r: %d, delta: %d, id: %d\n",
height.tau, height.oid, height.r, height.delta, height.id);
}
http-aux.cc
// Copyright (c) Xerox Corporation 1998. All rights reserved.
//
// License is granted to copy, to use, and to make and to use derivative
// works for research and evaluation purposes, provided that Xerox is
// acknowledged in all documentation pertaining to any such copy or
// derivative work. Xerox grants no other licenses expressed or
// implied. The Xerox trade name should not be used in any advertising
// without its written permission.
//
// XEROX CORPORATION MAKES NO REPRESENTATIONS CONCERNING EITHER THE
// MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE
// FOR ANY PARTICULAR PURPOSE. The software is provided "as is" without
// express or implied warranty of any kind.
//
// These notices must be retained in any copies of any part of this
// software.
//
// Auxiliary classes for Http multicast invalidation proxy cache
//
// $Headers$
#include "http-aux.h"
#include "http.h"
void PushTimer::expire(Event *)
{
a_->timeout(HTTP_UPDATE);
}
void HBTimer::expire(Event *)
{
a_->timeout(HTTP_INVALIDATION);
}
void LivenessTimer::expire(Event *)
{
a_->handle_node_failure(nbr_);
}
void HttpHbData::extract(InvalidationRec*& head)
{
// XXX head must be passed in from outside, because the
// InvalidationRec list will obtain the address of head
head = NULL;
InvalidationRec *q = NULL;
// reconstruct a list of invalidation records.
// We keep the updating record
for (int i = 0; i < num_inv(); i++) {
// Used only to mark that this page will be send in
// the next multicast update. The updating field in
// this agent will only be set after it gets the real
// update. Here we set this field temporarily so that
// recv_inv can find out the
q = inv_rec()[i].copyto();
q->insert(&head);
}
}
NeighborCache::~NeighborCache()
{
timer_->cancel();
delete timer_;
ServerEntry *s = sl_.gethead(), *p;
while (s != NULL) {
p = s;
s = s->next();
delete p;
}
}
int NeighborCache::is_server_down(int sid)
{
ServerEntry *s = sl_.gethead();
while (s != NULL) {
if (s->server() == sid)
return s->is_down();
s = s->next();
}
return 0;
}
void NeighborCache::server_down(int sid)
{
ServerEntry *s = sl_.gethead();
while (s != NULL) {
if (s->server() == sid)
s->down();
s = s->next();
}
}
void NeighborCache::server_up(int sid)
{
ServerEntry *s = sl_.gethead();
while (s != NULL) {
if (s->server() == sid)
s->up();
s = s->next();
}
}
void NeighborCache::invalidate(HttpMInvalCache *c)
{
ServerEntry *s = sl_.gethead();
while (s != NULL) {
c->invalidate_server(s->server());
s = s->next();
}
}
void NeighborCache::pack_leave(HttpLeaveData& data)
{
int i;
ServerEntry *s;
for (i = 0, s = sl_.gethead(); s != NULL; s = s->next(), i++)
data.add(i, s->server());
}
void NeighborCache::add_server(int sid)
{
ServerEntry *s = new ServerEntry(sid);
sl_.insert(s);
}
http.cc
// Copyright (c) Xerox Corporation 1998. All rights reserved.
//
// License is granted to copy, to use, and to make and to use derivative
// works for research and evaluation purposes, provided that Xerox is
// acknowledged in all documentation pertaining to any such copy or
// derivative work. Xerox grants no other licenses expressed or
// implied. The Xerox trade name should not be used in any advertising
// without its written permission.
//
// XEROX CORPORATION MAKES NO REPRESENTATIONS CONCERNING EITHER THE
// MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE
// FOR ANY PARTICULAR PURPOSE. The software is provided "as is" without
// express or implied warranty of any kind.
//
// These notices must be retained in any copies of any part of this
// software.
//
// Implementation of the HTTP agent. We want a separate agent for HTTP because
// we are interested in (detailed) HTTP headers, instead of just request and
// response patterns.
//
// $Header$
#include
#include
#include
#include
#include "tclcl.h"
#include "agent.h"
#include "app.h"
#include "tcp-simple.h"
#include "http.h"
#include "http-aux.h"
#include "trace.h"
#include "tcpapp.h"
#include "mcache.h"
//----------------------------------------------------------------------
// Http Application
//
// Allows multiple concurrent HTTP connections
//----------------------------------------------------------------------
static class HttpAppClass : public TclClass {
public:
HttpAppClass() : TclClass("Http") {}
TclObject* create(int, const char*const*) {
return (new HttpApp());
}
} class_http_app;
// What states should be in a http agent?
HttpApp::HttpApp() : log_(0)
{
bind("id_", &id_);
// Map a client address to a particular TCP agent
tpa_ = new Tcl_HashTable;
Tcl_InitHashTable(tpa_, TCL_ONE_WORD_KEYS);
}
HttpApp::~HttpApp()
{
if (tpa_ != NULL) {
Tcl_DeleteHashTable(tpa_);
delete tpa_;
}
}
int HttpApp::add_cnc(HttpApp* client, TcpApp *agt)
{
int newEntry = 1;
Tcl_HashEntry *he = Tcl_CreateHashEntry(tpa_,
(const char *)client->id(),
&newEntry);
if (he == NULL)
return -1;
if (newEntry)
Tcl_SetHashValue(he, (ClientData)agt);
return 0;
}
void HttpApp::delete_cnc(HttpApp* client)
{
Tcl_HashEntry *he = Tcl_FindHashEntry(tpa_,(const char *)client->id());
if (he != NULL) {
TcpApp *cnc = (TcpApp *)Tcl_GetHashValue(he);
Tcl_DeleteHashEntry(he);
delete cnc;
}
}
TcpApp* HttpApp::lookup_cnc(HttpApp* client)
{
Tcl_HashEntry *he =
Tcl_FindHashEntry(tpa_, (const char *)client->id());
if (he == NULL)
return NULL;
return (TcpApp *)Tcl_GetHashValue(he);
}
// Basic functionalities:
int HttpApp::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
if (argc == 2) {
if (strcmp(argv[1], "id") == 0) {
if (argc == 3) {
id_ = atoi(argv[2]);
tcl.resultf("%d", id_);
} else
tcl.resultf("%d", id_);
return TCL_OK;
} else if (strcmp(argv[1], "log") == 0) {
// Return the name of the log channel
if (log_ != NULL)
tcl.resultf("%s", Tcl_GetChannelName(log_));
else
tcl.result("");
return TCL_OK;
}
} else if (argc == 3) {
if (strcmp(argv[1], "get-modtime") == 0) {
double mt;
if (pool_->get_mtime(argv[2], mt) != -1) {
tcl.resultf("%.17g", mt);
return TCL_OK;
} else
return TCL_ERROR;
} else if (strcmp(argv[1], "exist-page") == 0) {
tcl.resultf("%d", pool_->exist_page(argv[2]));
return TCL_OK;
} else if (strcmp(argv[1], "get-size") == 0) {
int size;
if (pool_->get_size(argv[2], size) != -1) {
tcl.resultf("%d", size);
return TCL_OK;
} else
return TCL_ERROR;
} else if (strcmp(argv[1], "get-age") == 0) {
double age;
if (pool_->get_age(argv[2], age) != -1) {
tcl.resultf("%.17g", age);
return TCL_OK;
} else
return TCL_ERROR;
} else if (strcmp(argv[1], "get-cachetime") == 0) {
double et;
if (pool_->get_etime(argv[2], et) != -1) {
tcl.resultf("%.17g", et);
return TCL_OK;
} else
return TCL_ERROR;
} else if (strcmp(argv[1], "get-page") == 0) {
char buf[4096];
if (pool_->get_pageinfo(argv[2], buf) != -1) {
tcl.resultf("%s", buf);
return TCL_OK;
} else
return TCL_ERROR;
} else if (strcmp(argv[1], "get-cnc") == 0) {
/*
* get-cnc
*
* Given the communication party, get the tcp agent
* connected to it.
*/
HttpApp *client =
(HttpApp *)TclObject::lookup(argv[2]);
TcpApp *cnc = (TcpApp *)lookup_cnc(client);
if (cnc == NULL)
tcl.result("");
else
tcl.resultf("%s", cnc->name());
return TCL_OK;
} else if (strcmp(argv[1], "set-pagepool") == 0) {
pool_ = (ClientPagePool*)TclObject::lookup(argv[2]);
if (pool_ != NULL)
return TCL_OK;
else
return TCL_ERROR;
} else if (strcmp(argv[1], "is-connected") == 0) {
/*
* is-connected
*/
HttpApp *a = (HttpApp*)TclObject::lookup(argv[2]);
TcpApp *cnc = (TcpApp*)lookup_cnc(a);
if (cnc == NULL)
tcl.result("0");
else
tcl.result("1");
return TCL_OK;
} else if (strcmp(argv[1], "is-valid") == 0) {
ClientPage *pg =
(ClientPage *)pool_->get_page(argv[2]);
if (pg == NULL) {
tcl.resultf("%d is-valid: No page %s",
id_, argv[2]);
return TCL_ERROR;
}
tcl.resultf("%d", pg->is_valid());
return TCL_OK;
} else if (strcmp(argv[1], "log") == 0) {
int mode;
log_ = Tcl_GetChannel(tcl.interp(),
(char*)argv[2], &mode);
if (log_ == 0) {
tcl.resultf("%d: invalid log file handle %s\n",
id_, argv[2]);
return TCL_ERROR;
}
return TCL_OK;
} else if (strcmp(argv[1], "disconnect") == 0) {
/*
* disconnect
* Delete the association of source and sink TCP.
*/
HttpApp *client =
(HttpApp *)TclObject::lookup(argv[2]);
delete_cnc(client);
return TCL_OK;
} else if (strcmp(argv[1], "get-pagetype") == 0) {
/*
* get-pagetype
* return the page type
*/
ClientPage *pg =
(ClientPage*)pool_->get_page(argv[2]);
if (pg == NULL) {
tcl.resultf("%d get-pagetype: No page %s",
id_, argv[2]);
return TCL_ERROR;
}
switch (pg->type()) {
case HTML:
tcl.result("HTML");
break;
case MEDIA:
tcl.result("MEDIA");
break;
default:
fprintf(stderr, "Unknown page type %d",
pg->type());
return TCL_ERROR;
}
return TCL_OK;
} else if (strcmp(argv[1], "get-layer") == 0) {
// Assume the page is a MediaPage
MediaPage *pg = (MediaPage *)pool_->get_page(argv[2]);
if (pg == NULL) {
tcl.resultf("%d get-layer: No page %s",
id_, argv[2]);
return TCL_ERROR;
}
if (pg->type() != MEDIA) {
tcl.resultf("%d get-layer %s not a media page",
id_, argv[2]);
return TCL_ERROR;
}
tcl.resultf("%d", pg->num_layer());
return TCL_OK;
}
} else if (argc == 4) {
if (strcmp(argv[1], "connect") == 0) {
/*
* connect
*
* Associate a TCP agent with the given client.
* is the agent used to send packets out.
* We assume two-way TCP connection, therefore we
* only need one agent.
*/
HttpApp *client =
(HttpApp *)TclObject::lookup(argv[2]);
TcpApp *cnc = (TcpApp *)TclObject::lookup(argv[3]);
if (add_cnc(client, cnc)) {
tcl.resultf("%s: failed to connect to %s",
name_, argv[2]);
return TCL_ERROR;
}
// Set data delivery target
cnc->target() = (Process*)this;
return TCL_OK;
} else if (strcmp(argv[1], "set-modtime") == 0) {
double mt = strtod(argv[3], NULL);
if (pool_->set_mtime(argv[2], mt) != -1)
return TCL_OK;
else
return TCL_ERROR;
} else if (strcmp(argv[1], "set-cachetime") == 0) {
double et = Scheduler::instance().clock();
if (pool_->set_etime(argv[2], et) != -1)
return TCL_OK;
else
return TCL_ERROR;
}
} else {
if (strcmp(argv[1], "send") == 0) {
/*
* send
*/
HttpApp *client =
(HttpApp *)TclObject::lookup(argv[2]);
if (client == NULL) {
tcl.add_errorf("%s: bad client name %s",
name_, argv[2]);
return TCL_ERROR;
}
int bytes = atoi(argv[3]);
TcpApp *cnc = (TcpApp *)lookup_cnc(client);
if (cnc == NULL) {
//tcl.resultf("%s: no connection to client %s",
// name_, argv[2]);
// Tolerate it
return TCL_OK;
}
char *buf = strdup(argv[4]);
HttpNormalData *d =
new HttpNormalData(id_, bytes, buf);
cnc->send(bytes, d);
// delete d;
free(buf);
return TCL_OK;
} else if (strcmp(argv[1], "enter-page") == 0) {
ClientPage* pg = pool_->enter_page(argc, argv);
if (pg == NULL)
return TCL_ERROR;
else
return TCL_OK;
} else if (strcmp(argv[1], "evTrace") == 0) {
char buf[1024], *p;
if (log_ != 0) {
sprintf(buf, TIME_FORMAT" i %d ",
Trace::round(Scheduler::instance().clock()),
id_);
p = &(buf[strlen(buf)]);
for (int i = 2; i < argc; i++) {
strcpy(p, argv[i]);
p += strlen(argv[i]);
*(p++) = ' ';
}
// Stick in a newline.
*(p++) = '\n', *p = 0;
Tcl_Write(log_, buf, p-buf);
}
return TCL_OK;
}
}
return TclObject::command(argc, argv);
}
void HttpApp::log(const char* fmt, ...)
{
// Don't do anything if we don't have a log file.
if (log_ == 0)
return;
char buf[10240], *p;
sprintf(buf, TIME_FORMAT" i %d ",
Trace::round(Scheduler::instance().clock()), id_);
p = &(buf[strlen(buf)]);
va_list ap;
va_start(ap, fmt);
vsprintf(p, fmt, ap);
Tcl_Write(log_, buf, strlen(buf));
}
void HttpApp::process_data(int, AppData* data)
{
if (data == NULL)
return;
switch (data->type()) {
case HTTP_NORMAL: {
HttpNormalData *tmp = (HttpNormalData*)data;
Tcl::instance().eval(tmp->str());
break;
}
default:
fprintf(stderr, "Bad http invalidation data type %d\n",
data->type());
abort();
break;
}
}
//----------------------------------------------------------------------
// Clients
//----------------------------------------------------------------------
static class HttpClientClass : public TclClass {
public:
HttpClientClass() : TclClass("Http/Client") {}
TclObject* create(int, const char*const*) {
return (new HttpClient());
}
} class_httpclient_app;
//----------------------------------------------------------------------
// Servers
//----------------------------------------------------------------------
static class HttpServerClass : public TclClass {
public:
HttpServerClass() : TclClass("Http/Server") {}
TclObject* create(int, const char*const*) {
return (new HttpServer());
}
} class_httpserver_app;
static class HttpInvalServerClass : public TclClass {
public:
HttpInvalServerClass() : TclClass("Http/Server/Inval") {}
TclObject* create(int, const char*const*) {
return (new HttpInvalServer());
}
} class_httpinvalserver_app;
static class HttpYucInvalServerClass : public TclClass {
public:
HttpYucInvalServerClass() : TclClass("Http/Server/Inval/Yuc") {}
TclObject* create(int, const char*const*) {
return (new HttpYucInvalServer());
}
} class_httpyucinvalserver_app;
HttpYucInvalServer::HttpYucInvalServer() :
inv_sender_(0), invlist_(0), num_inv_(0)
{
bind("hb_interval_", &hb_interval_);
bind("enable_upd_", &enable_upd_);
bind("Ca_", &Ca_);
bind("Cb_", &Cb_);
bind("push_thresh_", &push_thresh_);
bind("push_high_bound_", &push_high_bound_);
bind("push_low_bound_", &push_low_bound_);
}
int HttpYucInvalServer::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
switch (argv[1][0]) {
case 'a':
if (strcmp(argv[1], "add-inval-sender") == 0) {
HttpUInvalAgent *tmp =
(HttpUInvalAgent *)TclObject::lookup(argv[2]);
if (tmp == NULL) {
tcl.resultf("Non-existent agent %s", argv[2]);
return TCL_ERROR;
}
inv_sender_ = tmp;
return TCL_OK;
} if (strcmp(argv[1], "add-inv") == 0) {
/*
* add-inv
*/
double mtime = strtod(argv[3], NULL);
add_inv(argv[2], mtime);
return TCL_OK;
}
break;
case 'c':
if (strcmp(argv[1], "count-request") == 0) {
ClientPage *pg =
(ClientPage *)pool_->get_page(argv[2]);
if (pg == NULL) {
tcl.resultf("%d count-request: No page %s",
id_, argv[2]);
return TCL_ERROR;
}
pg->count_request(Cb_, push_high_bound_);
log("S NTF p %s v %d\n", argv[2], pg->counter());
return TCL_OK;
} else if (strcmp(argv[1], "count-inval") == 0) {
ClientPage *pg =
(ClientPage *)pool_->get_page(argv[2]);
if (pg == NULL) {
tcl.resultf("%d count-inval: No page %s",
id_, argv[2]);
return TCL_ERROR;
}
pg->count_inval(Ca_, push_low_bound_);
log("S NTF p %s v %d\n", argv[2], pg->counter());
return TCL_OK;
}
break;
case 'i':
if (strcmp(argv[1], "is-pushable") == 0) {
ClientPage *pg =
(ClientPage *)pool_->get_page(argv[2]);
if (pg == NULL) {
tcl.resultf("%d is-pushable: No page %s",
id_, argv[2]);
return TCL_ERROR;
}
if (pg->is_mpush() &&
(Scheduler::instance().clock() - pg->mpush_time() >
HTTP_HBEXPIRE_COUNT*hb_interval_)) {
// If mandatory push timer expires, stop push
pg->clear_mpush();
fprintf(stderr,
"server %d timeout mpush\n", id_);
}
tcl.resultf("%d", (enable_upd_ &&
(pg->counter() >= push_thresh_) ||
pg->is_mpush()));
return TCL_OK;
}
break;
case 'r':
if ((strcmp(argv[1], "request-mpush") == 0) ||
(strcmp(argv[1], "refresh-mpush") == 0)) {
ClientPage *pg =
(ClientPage *)pool_->get_page(argv[2]);
if (pg == NULL) {
tcl.resultf("%d is-valid: No page %s",
id_, argv[2]);
return TCL_ERROR;
}
pg->set_mpush(Scheduler::instance().clock());
return TCL_OK;
}
break;
case 's':
if (strcmp(argv[1], "send-hb") == 0) {
send_heartbeat();
return TCL_OK;
} else if (strcmp(argv[1], "stop-mpush") == 0) {
ClientPage *pg =
(ClientPage *)pool_->get_page(argv[2]);
if (pg == NULL) {
tcl.resultf("%d is-valid: No page %s",
id_, argv[2]);
return TCL_ERROR;
}
pg->clear_mpush();
fprintf(stderr, "server %d stopped mpush\n", id_);
return TCL_OK;
}
break;
}
return HttpApp::command(argc, argv);
}
void HttpYucInvalServer::add_inv(const char *name, double mtime)
{
InvalidationRec *p = get_invrec(name);
if ((p != NULL) && (p->mtime() < mtime)) {
p->detach();
delete p;
p = NULL;
num_inv_--;
}
if (p == NULL) {
p = new InvalidationRec(name, mtime);
p->insert(&invlist_);
num_inv_++;
}
}
InvalidationRec* HttpYucInvalServer::get_invrec(const char *name)
{
// XXX What should we do if we already have an
// invalidation record of this page in our
// invlist_? --> We should replace it with the new one
InvalidationRec *r = invlist_;
for (r = invlist_; r != NULL; r = r->next())
if (strcmp(name, r->pg()) == 0)
return r;
return NULL;
}
HttpHbData* HttpYucInvalServer::pack_heartbeat()
{
HttpHbData *data = new HttpHbData(id_, num_inv_);
InvalidationRec *p = invlist_, *q;
int i = 0;
while (p != NULL) {
data->add(i++, p);
// Clearing up invalidation sending list
if (!p->dec_scount()) {
// Each invalidation is sent to its children
// for at most HTTP_HBEXPIRE times. After that
// the invalidation record is removed from
// the list
q = p;
p = p->next();
q->detach();
delete q;
num_inv_--;
} else
p = p->next();
}
return data;
}
void HttpYucInvalServer::send_hb_helper(int size, AppData *data)
{
inv_sender_->send(size, data);
}
void HttpYucInvalServer::send_heartbeat()
{
if (inv_sender_ == NULL)
return;
HttpHbData* d = pack_heartbeat();
send_hb_helper(d->cost(), d);
}
//----------------------------------------------------------------------
// Http cache with invalidation protocols. Http/Cache and Http/Cache/Inval
// are used as base classes and provide common TCL methods. Http/Cache
// derives Http/Cache/TTL and Http/Cache/TTL/Old. Http/Cache/Inval derives
// unicast invalidation and multicast invalidation.
//----------------------------------------------------------------------
static class HttpCacheClass : public TclClass {
public:
HttpCacheClass() : TclClass("Http/Cache") {}
TclObject* create(int, const char*const*) {
return (new HttpCache());
}
} class_httpcache_app;
static class HttpInvalCacheClass : public TclClass {
public:
HttpInvalCacheClass() : TclClass("Http/Cache/Inval") {}
TclObject* create(int, const char*const*) {
return (new HttpInvalCache());
}
} class_httpinvalcache_app;
static class HttpMInvalCacheClass : public TclClass {
public:
HttpMInvalCacheClass() : TclClass("Http/Cache/Inval/Mcast") {}
TclObject* create(int, const char*const*) {
return (new HttpMInvalCache());
}
} class_HttpMInvalCache_app;
// Static members and functions
HttpMInvalCache** HttpMInvalCache::CacheRepository_ = NULL;
int HttpMInvalCache::NumCache_ = 0;
void HttpMInvalCache::add_cache(HttpMInvalCache *c)
{
if (CacheRepository_ == NULL) {
CacheRepository_ = new HttpMInvalCache* [c->id() + 1];
CacheRepository_[c->id()] = c;
NumCache_ = c->id();
} else if (NumCache_ < c->id()) {
HttpMInvalCache** p = new HttpMInvalCache* [c->id()+1];
memcpy(p, CacheRepository_,
(c->id()+1)*sizeof(HttpMInvalCache*));
delete[]CacheRepository_;
CacheRepository_ = p;
NumCache_ = c->id();
p[c->id()] = c;
} else
CacheRepository_[c->id()] = c;
}
HttpMInvalCache::HttpMInvalCache() :
hb_timer_(this, HTTP_HBINTERVAL),
inv_sender_(0), num_sender_(0), size_sender_(0),
invlist_(0), num_inv_(0), inv_parent_(NULL),
upd_sender_(NULL), num_updater_(0), size_updater_(0)
{
bind("hb_interval_", &hb_interval_);
bind("enable_upd_", &enable_upd_); // If we allow push
bind("Ca_", &Ca_);
bind("Cb_", &Cb_);
bind("push_thresh_", &push_thresh_);
bind("push_high_bound_", &push_high_bound_);
bind("push_low_bound_", &push_low_bound_);
hb_timer_.set_interval(hb_interval_);
Tcl_InitHashTable(&sstate_, TCL_ONE_WORD_KEYS);
Tcl_InitHashTable(&nbr_, TCL_ONE_WORD_KEYS);
}
HttpMInvalCache::~HttpMInvalCache()
{
if (num_sender_ > 0)
delete []inv_sender_;
Tcl_DeleteHashTable(&sstate_);
Tcl_DeleteHashTable(&nbr_);
}
int HttpMInvalCache::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
if (argc < 2)
return HttpInvalCache::command(argc, argv);
switch (argv[1][0]) {
case 'a':
if ((strcmp(argv[1], "add-inval-listener") == 0) ||
(strcmp(argv[1], "add-upd-listener") == 0)) {
HttpInvalAgent *tmp =
(HttpInvalAgent *)TclObject::lookup(argv[2]);
tmp->attachApp((Application *)this);
return TCL_OK;
} else if (strcmp(argv[1], "add-inval-sender") == 0) {
HttpInvalAgent *tmp =
(HttpInvalAgent *)TclObject::lookup(argv[2]);
if (tmp == NULL) {
tcl.resultf("Non-existent agent %s", argv[2]);
return TCL_ERROR;
}
if (num_sender_ == size_sender_) {
HttpInvalAgent **tt =
new HttpInvalAgent*[size_sender_+5];
memcpy(tt, inv_sender_,
sizeof(HttpInvalAgent*)*size_sender_);
delete []inv_sender_;
size_sender_ += 5;
inv_sender_ = tt;
}
inv_sender_[num_sender_++] = tmp;
return TCL_OK;
} else if (strcmp(argv[1], "add-to-map") == 0) {
add_cache(this);
return TCL_OK;
} else if (strcmp(argv[1], "add-upd-sender") == 0) {
HttpInvalAgent *tmp =
(HttpInvalAgent *)TclObject::lookup(argv[2]);
if (tmp == NULL) {
tcl.resultf("Non-existent agent %s", argv[2]);
return TCL_ERROR;
}
if (num_updater_ == size_updater_) {
HttpInvalAgent **tt =
new HttpInvalAgent*[size_updater_+5];
memcpy(tt, upd_sender_,
sizeof(HttpInvalAgent*)*size_updater_);
delete []upd_sender_;
size_updater_ += 5;
upd_sender_ = tt;
}
upd_sender_[num_updater_++] = tmp;
return TCL_OK;
}
break;
case 'c':
if (strcmp(argv[1], "count-request") == 0) {
ClientPage *pg =
(ClientPage *)pool_->get_page(argv[2]);
if (pg == NULL) {
tcl.resultf("%d count-request: No page %s",
id_, argv[2]);
return TCL_ERROR;
}
pg->count_request(Cb_, push_high_bound_);
log("E NTF p %s v %d\n", argv[2], pg->counter());
return TCL_OK;
} else if (strcmp(argv[1], "check-sstate") == 0) {
/*
* check-sstate
* If server is re-connected, reinstate it
*/
int sid = atoi(argv[2]);
int cid = atoi(argv[3]);
check_sstate(sid, cid);
return TCL_OK;
}
break;
case 'i':
// XXX We don't need a "is-pushable" for cache!
if (strcmp(argv[1], "is-unread") == 0) {
ClientPage *pg =
(ClientPage *)pool_->get_page(argv[2]);
if (pg == NULL) {
tcl.resultf("%d is-unread: No page %s",
id_, argv[2]);
return TCL_ERROR;
}
tcl.resultf("%d", pg->is_unread());
return TCL_OK;
}
break;
case 'j':
if (strcmp(argv[1], "join") == 0) {
/*
* join
*
* join via . If they are the same,
* it means we are the primary cache for .
*/
int sid = atoi(argv[2]);
HttpMInvalCache *cache =
(HttpMInvalCache*)TclObject::lookup(argv[3]);
if (cache == NULL) {
tcl.add_errorf("Non-existent cache %s", argv[3]);
return TCL_ERROR;
}
// Add neighbor cache if necessary
NeighborCache *c = lookup_nbr(cache->id());
if (c == NULL)
add_nbr(cache);
// Establish server invalidation contract
check_sstate(sid, cache->id());
return TCL_OK;
}
break;
case 'p':
if (strcmp(argv[1], "parent-cache") == 0) {
/*
* parent-cache
* Return the parent cache of in the
* virtual distribution tree.
*/
int sid = atoi(argv[2]);
SState *sst = lookup_sstate(sid);
if (sst == NULL)
tcl.result("");
else {
// Bad hack... :(
NeighborCache *c = lookup_nbr(sst->cache()->cache()->id());
tcl.resultf("%s", c->cache()->name());
}
return TCL_OK;
} else if (strcmp(argv[1], "push-children") == 0) {
// Multicast the pushed page to all children
ClientPage *pg =
(ClientPage *)pool_->get_page(argv[2]);
if (pg == NULL) {
tcl.resultf("%d is-valid: No page %s",
id_, argv[2]);
return TCL_ERROR;
}
send_upd(pg);
return TCL_OK;
}
break;
case 'r':
if (strcmp(argv[1], "recv-inv") == 0) {
/*
* recv-inv
* This should be called only by a web server,
* therefore we do not check the validity of the
* invalidation
*/
// Pack it into a HttpHbData, and process it
HttpHbData *d = new HttpHbData(id_, 1);
strcpy(d->rec_pg(0), argv[2]);
d->rec_mtime(0) = strtod(argv[3], NULL);
//int old_inv = num_inv_;
tcl.resultf("%d", recv_inv(d));
delete d;
return TCL_OK;
} else if (strcmp(argv[1], "recv-push") == 0) {
/*
* recv-push args
*/
HttpUpdateData *d = new HttpUpdateData(id_, 1);
strcpy(d->rec_page(0), argv[2]);
for (int i = 3; i < argc; i+=2) {
if (strcmp(argv[i], "modtime") == 0)
d->rec_mtime(0) = strtod(argv[i+1], NULL);
else if (strcmp(argv[i], "size") == 0) {
d->rec_size(0) = atoi(argv[i+1]);
// XXX need to set total update page size
d->set_pgsize(d->rec_size(0));
} else if (strcmp(argv[i], "age") == 0)
d->rec_age(0) = strtod(argv[i+1], NULL);
}
tcl.resultf("%d", recv_upd(d));
delete d;
return TCL_OK;
} else if (strcmp(argv[1], "register-server") == 0) {
/*
* register-server
* We get a GET response about a page from ,
* which we hear from
*/
int cid = atoi(argv[2]);
int sid = atoi(argv[3]);
// Assuming we've already known the cache
check_sstate(sid, cid);
return TCL_OK;
}
break;
case 's':
if (strcmp(argv[1], "start-hbtimer") == 0) {
if (hb_timer_.status() == TIMER_IDLE)
hb_timer_.sched();
return TCL_OK;
} else if (strcmp(argv[1], "server-hb") == 0) {
int id = atoi(argv[2]);
recv_heartbeat(id);
return TCL_OK;
} else if (strcmp(argv[1], "set-pinv-agent") == 0) {
inv_parent_ =
(HttpUInvalAgent*)TclObject::lookup(argv[2]);
return TCL_OK;
} else if (strcmp(argv[1], "set-parent") == 0) {
HttpMInvalCache *c =
(HttpMInvalCache*)TclObject::lookup(argv[2]);
if (c == NULL) {
tcl.add_errorf("Non-existent cache %s", argv[2]);
return TCL_ERROR;
}
// Add parent cache into known cache list
add_nbr(c);
return TCL_OK;
} else if (strcmp(argv[1], "set-unread") == 0) {
ClientPage *pg =
(ClientPage *)pool_->get_page(argv[2]);
if (pg == NULL) {
tcl.resultf("%d is-valid: No page %s",
id_, argv[2]);
return TCL_ERROR;
}
pg->set_unread();
return TCL_OK;
} else if (strcmp(argv[1], "set-read") == 0) {
ClientPage *pg =
(ClientPage *)pool_->get_page(argv[2]);
if (pg == NULL) {
tcl.resultf("%d is-valid: No page %s",
id_, argv[2]);
return TCL_ERROR;
}
pg->set_read();
return TCL_OK;
} else if (strcmp(argv[1], "set-mandatory-push") == 0) {
ClientPage *pg =
(ClientPage *)pool_->get_page(argv[2]);
if (pg == NULL) {
tcl.resultf("%d is-valid: No page %s",
id_, argv[2]);
return TCL_ERROR;
}
pg->set_mpush(Scheduler::instance().clock());
return TCL_OK;
} else if (strcmp(argv[1], "stop-mpush") == 0) {
ClientPage *pg =
(ClientPage *)pool_->get_page(argv[2]);
if (pg == NULL) {
tcl.resultf("%d is-valid: No page %s",
id_, argv[2]);
return TCL_ERROR;
}
pg->clear_mpush();
return TCL_OK;
}
break;
default:
break;
}
return HttpInvalCache::command(argc, argv);
}
void HttpMInvalCache::check_sstate(int sid, int cid)
{
if ((sid == cid) && (cid == id_))
// How come?
return;
SState *sst = lookup_sstate(sid);
NeighborCache *c = lookup_nbr(cid);
if (sst == NULL) {
if (c == NULL) {
fprintf(stderr,
"%g: cache %d: No neighbor cache for received invalidation from %d via %d\n",
Scheduler::instance().clock(), id_, sid, cid);
abort();
}
#ifdef WEBCACHE_DEBUG
fprintf(stderr,
"%g: cache %d: registered server %d via cache %d\n",
Scheduler::instance().clock(), id_, sid, cid);
#endif
sst = new SState(c);
add_sstate(sid, sst);
c->add_server(sid);
} else if (sst->is_down()) {
sst->up();
if (cid != id_) {
if (c == NULL) {
fprintf(stderr,
"[%g]: Cache %d has an invalid neighbor cache %d\n",
Scheduler::instance().clock(), id_, cid);
abort();
}
c->server_up(sid);
}
#ifdef WEBCACHE_DEBUG
fprintf(stderr,
"[%g] Cache %d reconnected to server %d via cache %d\n",
Scheduler::instance().clock(), id_,
sid, cid);
#endif
Tcl::instance().evalf("%s mark-rejoin", name_);
}
}
void HttpMInvalCache::add_sstate(int sid, SState *sst)
{
int newEntry = 1;
Tcl_HashEntry *he =
Tcl_CreateHashEntry(&sstate_, (const char *)sid, &newEntry);
if (he == NULL)
return;
if (newEntry)
Tcl_SetHashValue(he, (ClientData)sst);
}
HttpMInvalCache::SState* HttpMInvalCache::lookup_sstate(int sid)
{
Tcl_HashEntry *he = Tcl_FindHashEntry(&sstate_, (const char *)sid);
if (he == NULL)
return NULL;
return (SState *)Tcl_GetHashValue(he);
}
NeighborCache* HttpMInvalCache::lookup_nbr(int id)
{
Tcl_HashEntry *he = Tcl_FindHashEntry(&nbr_, (const char *)id);
if (he == NULL)
return NULL;
return (NeighborCache *)Tcl_GetHashValue(he);
}
// Add a new neighbor cache
void HttpMInvalCache::add_nbr(HttpMInvalCache *cache)
{
int newEntry = 1;
Tcl_HashEntry *he =
Tcl_CreateHashEntry(&nbr_, (const char *)cache->id(),
&newEntry);
if (he == NULL)
return;
// If this cache already exists, don't do anything
if (!newEntry)
return;
// Start a timer for the neighbor
LivenessTimer *timer =
new LivenessTimer(this,HTTP_HBEXPIRE_COUNT*hb_interval_,
cache->id());
double time = Scheduler::instance().clock();
NeighborCache *c = new NeighborCache(cache, time, timer);
Tcl_SetHashValue(he, (ClientData)c);
}
// Two ways to receive a heartbeat: (1) via HttpInvalAgent; (2) via TCP
// connection between a server and a primary cache. (See "server-hb" handling
// in command().
void HttpMInvalCache::recv_heartbeat(int id)
{
// Receive time of the heartbeat
double time = Scheduler::instance().clock();
NeighborCache *c = lookup_nbr(id);
if (c == NULL) {
// XXX
// The only possible place for this to happen is in the TLC
// group, where no JOIN could ever reach. Moreover,
// we don't even have an entry for that cache yet, so here
// we add that cache into our entry, and later on we'll add
// corresponding servers there.
if (id == id_)
return;
add_nbr(map_cache(id));
#ifdef WEBCACHE_DEBUG
fprintf(stderr, "TLC %d discovered TLC %d\n", id_, id);
#endif
return;
} else if (c->is_down()) {
// Neighbor cache recovers. Don't do anything special and
// let invalid entries recover themselves
c->up();
#ifdef WEBCACHE_DEBUG
fprintf(stderr, "[%g] Cache %d reconnected to cache %d\n",
Scheduler::instance().clock(), id_, id);
#endif
Tcl::instance().evalf("%s mark-rejoin", name_);
} else
// Update heartbeat time
c->reset_timer(time);
}
void HttpMInvalCache::invalidate_server(int sid)
{
SState *sst = lookup_sstate(sid);
if (sst->is_down())
// If this server is already marked down, return
return;
sst->down();
pool_->invalidate_server(sid);
}
void HttpMInvalCache::handle_node_failure(int cid)
{
#ifdef WEBCACHE_DEBUG
fprintf(stderr, "[%g] Cache %d disconnected from cache %d\n",
Scheduler::instance().clock(), id_, cid);
#endif
Tcl::instance().evalf("%s mark-leave", name_);
NeighborCache *c = lookup_nbr(cid);
if (c == NULL) {
fprintf(stderr, "%s: An unknown neighbor cache %d failed.\n",
name_, cid);
}
// Mark the cache down
c->down();
// Invalidate entries of all servers related to that cache
// XXX We don't have an iterator for all servers in NeighborCache!
c->invalidate(this);
// Send leave message to all children
HttpLeaveData* data = new HttpLeaveData(id_, c->num());
c->pack_leave(*data);
send_leave(data);
}
void HttpMInvalCache::recv_leave(HttpLeaveData *d)
{
#ifdef WEBCACHE_DEBUG
fprintf(stderr, "[%g] Cache %d gets a LEAVE from cache %d\n",
Scheduler::instance().clock(), id_, d->id());
#endif
if (d->num() == 0) {
fprintf(stderr,
"%s (%g) gets a leave from cache without server!\n",
name_, Scheduler::instance().clock());
return;
}
SState *sst;
HttpLeaveData* data = new HttpLeaveData(id_, d->num());
NeighborCache *c = lookup_nbr(d->id());
int i, j;
for (i = 0, j = 0; i < d->num(); i++) {
sst = lookup_sstate(d->rec_id(i));
// If we haven't heard of that server, which means we don't
// have any page of that server, ignore the leave message.
if (sst == NULL)
continue;
// If it's already marked down, don't bother again.
if (sst->is_down())
continue;
// If we hear a LEAVE about a server from one of
// our child in the virtual distribution tree
// of the server, ignore it.
if (c != sst->cache())
continue;
// We have the page, and we hold inval contract. Invalidate
// the page and inform our children of it.
sst->down();
data->add(j++, d->rec_id(i));
pool_->invalidate_server(d->rec_id(i));
Tcl::instance().evalf("%s mark-leave", name_);
}
// Delete it if it's not sent out
if (j > 0)
send_leave(data);
delete data;
}
void HttpMInvalCache::send_leave(HttpLeaveData *d)
{
send_hb_helper(d->cost(), d);
}
void HttpMInvalCache::timeout(int reason)
{
switch (reason) {
case HTTP_INVALIDATION:
// Send an invalidation message
send_heartbeat();
break;
case HTTP_UPDATE:
// XXX do nothing. May put client selective joining update
// group here.
break;
default:
fprintf(stderr, "%s: Unknown reason %d", name_, reason);
break;
}
}
void HttpMInvalCache::process_data(int size, AppData* data)
{
if (data == NULL)
return;
switch (data->type()) {
case HTTP_INVALIDATION: {
// Update timer for the source of the heartbeat
HttpHbData *inv = (HttpHbData*)data;
recv_heartbeat(inv->id());
recv_inv(inv);
break;
}
case HTTP_UPDATE: {
// Replace all updated pages
HttpUpdateData *pg = (HttpUpdateData*)data;
recv_upd(pg);
break;
}
// JOIN messages are sent via TCP and direct TCL callback.
case HTTP_LEAVE: {
HttpLeaveData *l = (HttpLeaveData*)data;
recv_leave(l);
break;
}
default:
HttpApp::process_data(size, data);
return;
}
}
void HttpMInvalCache::add_inv(const char *name, double mtime)
{
InvalidationRec *p = get_invrec(name);
if ((p != NULL) && (p->mtime() < mtime)) {
p->detach();
delete p;
p = NULL;
num_inv_--;
}
if (p == NULL) {
p = new InvalidationRec(name, mtime);
p->insert(&invlist_);
num_inv_++;
}
}
InvalidationRec* HttpMInvalCache::get_invrec(const char *name)
{
// XXX What should we do if we already have an
// invalidation record of this page in our
// invlist_? --> We should replace it with the new one
InvalidationRec *r = invlist_;
for (r = invlist_; r != NULL; r = r->next())
if (strcmp(name, r->pg()) == 0)
return r;
return NULL;
}
HttpHbData* HttpMInvalCache::pack_heartbeat()
{
HttpHbData *data = new HttpHbData(id_, num_inv_);
InvalidationRec *p = invlist_, *q;
int i = 0;
while (p != NULL) {
data->add(i++, p);
// Clearing up invalidation sending list
if (!p->dec_scount()) {
// Each invalidation is sent to its children
// for at most HTTP_HBEXPIRE times. After that
// the invalidation record is removed from
// the list
q = p;
p = p->next();
q->detach();
delete q;
num_inv_--;
} else
p = p->next();
}
return data;
}
int HttpMInvalCache::recv_inv(HttpHbData *data)
{
if (data->num_inv() == 0)
return 0;
InvalidationRec *head;
data->extract(head);
int old_inv = num_inv_;
process_inv(data->num_inv(), head, data->id());
//log("E GINV z %d\n", data->size());
if (old_inv < num_inv_)
// This invalidation is valid
return 1;
else
return 0;
}
// Get an invalidation, check invalidation modtimes, then setup
// invalidation forwarding entries
// The input invalidation record list is destroyed.
void HttpMInvalCache::process_inv(int, InvalidationRec *ivlist, int cache)
{
InvalidationRec *p = ivlist, *q, *r;
//int upd = 0;
while (p != NULL) {
ClientPage* pg = (ClientPage *)pool_->get_page(p->pg());
// XXX Establish server states. Server states only gets
// established when we have a page (no matter if we have its
// content), and we have got an invalidation for the page.
// Then we know we've got an invalidation contract for the
// page.
if (pg != NULL) {
check_sstate(pg->server()->id(), cache);
// Count this invalidation no matter whether we're
// going to drop it. But if we doesn't get it
// from our virtual parent, don't count it
SState *sst = lookup_sstate(pg->server()->id());
if (sst == NULL) {
// How come we doesn't know the server???
fprintf(stderr,
"%s %d: couldn't find the server.\n",
__FILE__, __LINE__);
abort();
}
if ((sst->cache()->cache()->id() == cache) &&
(pg->mtime() > p->mtime())) {
// Don't count repeated invalidations.
pg->count_inval(Ca_, push_low_bound_);
log("E NTF p %s v %d\n",p->pg(),pg->counter());
}
}
// Hook for filters of derived classes
if (recv_inv_filter(pg, p) == HTTP_INVALCACHE_FILTERED) {
// If we do not have the page, or we have (or know
// about) a newer page, ignore this invalidation
// record and keep going.
//
// If we have this version of the page, and it's
// already invalid, ignore this extra invalidation
q = p;
p = p->next();
q->detach();
delete q;
} else {
// Otherwise we invalidate our page and setup a
// invalidation sending record for the page
pg->invalidate(p->mtime());
// Delete existing record for that page if any
q = get_invrec(p->pg());
if ((q != NULL) && (q->mtime() < p->mtime())) {
q->detach();
delete q;
q = NULL;
num_inv_--;
}
r = p;
p = p->next();
r->detach();
// Insert it if necessary
if (q == NULL) {
r->insert(&invlist_);
num_inv_++;
// XXX
Tcl::instance().evalf("%s mark-invalid",name_);
log("E GINV p %s m %.17g\n", r->pg(), r->mtime());
} else
delete r;
}
}
}
void HttpMInvalCache::send_hb_helper(int size, AppData *data)
{
if (inv_parent_ != NULL)
inv_parent_->send(size, data->copy());
for (int i = 0; i < num_sender_; i++)
inv_sender_[i]->send(size, data->copy());
}
void HttpMInvalCache::send_heartbeat()
{
if ((num_sender_ == 0) && (inv_parent_ == NULL))
return;
HttpHbData* d = pack_heartbeat();
send_hb_helper(d->cost(), d);
delete d;
}
int HttpMInvalCache::recv_upd(HttpUpdateData *d)
{
if (d->num() != 1) {
fprintf(stderr,
"%d gets an update which contain !=1 pages.\n", id_);
abort();
}
ClientPage *pg = pool_->get_page(d->rec_page(0));
if (pg != NULL)
if (pg->mtime() >= d->rec_mtime(0)) {
// If we've already had this version, or a newer
// version, ignore this old push
// fprintf(stderr, "[%g] %d gets an old push\n",
// Scheduler::instance().clock(), id_);
// log("E OLD m %g p %g\n", d->rec_mtime(0), pg->mtime());
return 0;
} else {
// Our old page is invalidated by this new push,
// set up invalidation records for our children
add_inv(d->rec_page(0), d->rec_mtime(0));
pg->count_inval(Ca_, push_low_bound_);
log("E NTF p %s v %d\n", d->rec_page(0),pg->counter());
}
// Add the new page into our pool
ClientPage *q = pool_->enter_page(d->rec_page(0), d->rec_size(0),
d->rec_mtime(0),
Scheduler::instance().clock(),
d->rec_age(0));
// By default the page is valid and read. Set it as unread
q->set_unread();
log("E GUPD m %.17g z %d\n", d->rec_mtime(0), d->pgsize());
Tcl::instance().evalf("%s mark-valid", name_);
// XXX If the page was previously marked as MandatoryPush, then
// we need to check if it's timed out
if (q->is_mpush() && (Scheduler::instance().clock() - q->mpush_time()
> HTTP_HBEXPIRE_COUNT*hb_interval_)) {
// If mandatory push timer expires, stop push
q->clear_mpush();
Tcl::instance().evalf("%s cancel-mpush-refresh %s",
name_, d->rec_page(0));
}
if (enable_upd_ && (q->counter() >= push_thresh_) || q->is_mpush())
// XXX Continue pushing if we either select to push, or
// were instructed to do so.
return 1;
else
return 0;
}
HttpUpdateData* HttpMInvalCache::pack_upd(ClientPage* page)
{
HttpUpdateData *data = new HttpUpdateData(id_, 1);
data->add(0, page);
return data;
}
void HttpMInvalCache::send_upd_helper(int pgsize, AppData* data)
{
for (int i = 0; i < num_updater_; i++)
upd_sender_[i]->send(pgsize, data->copy());
}
void HttpMInvalCache::send_upd(ClientPage *page)
{
if ((num_updater_ == 0) || !enable_upd_)
return;
HttpUpdateData* d = pack_upd(page);
send_upd_helper(d->pgsize(), d);
delete d;
}
//----------------------------------------------------------------------
// Multicast invalidation + two way liveness messages +
// invalidation filtering.
//----------------------------------------------------------------------
static class HttpPercInvalCacheClass : public TclClass {
public:
HttpPercInvalCacheClass() : TclClass("Http/Cache/Inval/Mcast/Perc") {}
TclObject* create(int, const char*const*) {
return (new HttpPercInvalCache());
}
} class_HttpPercInvalCache_app;
HttpPercInvalCache::HttpPercInvalCache()
{
bind("direct_request_", &direct_request_);
}
int HttpPercInvalCache::command(int argc, const char*const* argv)
{
Tcl& tcl = Tcl::instance();
if (strcmp(argv[1], "is-header-valid") == 0) {
ClientPage *pg =
(ClientPage *)pool_->get_page(argv[2]);
if (pg == NULL) {
tcl.resultf("%d is-valid: No page %s",
id_, argv[2]);
return TCL_ERROR;
}
tcl.resultf("%d", pg->is_header_valid());
return TCL_OK;
} else if (strcmp(argv[1], "enter-metadata") == 0) {
/*
* enter-metadata
* The same arguments as enter-page, but set the page status
* as HTTP_VALID_HEADER, i.e., if we get a request, we need
* to fetch the actual valid page content
*/
ClientPage *pg = pool_->enter_metadata(argc, argv);
if (pg == NULL)
return TCL_ERROR;
else
return TCL_OK;
}
return HttpMInvalCache::command(argc, argv);
}
inval-agent.cc
// Copyright (c) Xerox Corporation 1998. All rights reserved.
//
// License is granted to copy, to use, and to make and to use derivative
// works for research and evaluation purposes, provided that Xerox is
// acknowledged in all documentation pertaining to any such copy or
// derivative work. Xerox grants no other licenses expressed or
// implied. The Xerox trade name should not be used in any advertising
// without its written permission.
//
// XEROX CORPORATION MAKES NO REPRESENTATIONS CONCERNING EITHER THE
// MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE
// FOR ANY PARTICULAR PURPOSE. The software is provided "as is" without
// express or implied warranty of any kind.
//
// These notices must be retained in any copies of any part of this
// software.
//
// Agents used to send and receive invalidation records
//
// $Header$
#include "inval-agent.h"
#include "ip.h"
#include "http.h"
// Implementation 1: Invalidation via multicast heartbeat
static class HttpInvalHeaderClass : public PacketHeaderClass {
public:
HttpInvalHeaderClass() : PacketHeaderClass("PacketHeader/HttpInval",
sizeof(hdr_inval)) {}
} class_httpinvalhdr;
static class HttpInvalClass : public TclClass {
public:
HttpInvalClass() : TclClass("Agent/HttpInval") {}
TclObject* create(int, const char*const*) {
return (new HttpInvalAgent());
}
} class_httpinval_agent;
HttpInvalAgent::HttpInvalAgent() : Agent(PT_INVAL)
{
bind("off_inv_", &off_inv_);
// It should be initialized to the same as tcpip_base_hdr_size_
bind("inval_hdr_size_", &inval_hdr_size_);
}
void HttpInvalAgent::recv(Packet *pkt, Handler*)
{
hdr_ip *ip = (hdr_ip *)pkt->access(off_ip_);
if ((ip->saddr() == addr()) && (ip->sport() == port()))
// XXX Why do we need this?
return;
if (app_ == 0)
return;
hdr_inval *ih = (hdr_inval *)pkt->access(off_inv_);
((HttpApp*)app_)->process_data(ih->size(), pkt->userdata());
Packet::free(pkt);
}
// Send a list of invalidation records in user data area
// realsize: the claimed size
// datasize: the actual size of user data, used to allocate packet
void HttpInvalAgent::send(int realsize, AppData* data)
{
Packet *pkt = allocpkt(data->size());
hdr_inval *ih = (hdr_inval *)pkt->access(off_inv_);
ih->size() = data->size();
pkt->setdata(data);
// Set packet size proportional to the number of invalidations
hdr_cmn *ch = (hdr_cmn*) pkt->access(off_cmn_);
ch->size() = inval_hdr_size_ + realsize;
Agent::send(pkt, 0);
}
// Implementation 2: Invalidation via TCP.
static class HttpUInvalClass : public TclClass {
public:
HttpUInvalClass() : TclClass("Application/TcpApp/HttpInval") {}
TclObject* create(int argc, const char*const* argv) {
if (argc != 5)
return NULL;
Agent *a = (Agent *)TclObject::lookup(argv[4]);
a->set_pkttype(PT_INVAL); // It's TCP but used for invalidation
if (a == NULL)
return NULL;
return (new HttpUInvalAgent(a));
}
} class_httpuinval_agent;
void HttpUInvalAgent::process_data(int size, AppData* data)
{
target_->process_data(size, data);
}
int HttpUInvalAgent::command(int argc, const char*const* argv)
{
if (strcmp(argv[1], "set-app") == 0) {
// Compatibility interface
HttpApp* c =
(HttpApp*)TclObject::lookup(argv[2]);
target_ = (Process *)c;
return TCL_OK;
}
return TcpApp::command(argc, argv);
}
mcache.cc
/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
//
// Copyright (c) 1997 by the University of Southern California
// All rights reserved.
//
// Permission to use, copy, modify, and distribute this software and its
// documentation in source and binary forms for non-commercial purposes
// and without fee is hereby granted, provided that the above copyright
// notice appear in all copies and that both the copyright notice and
// this permission notice appear in supporting documentation. and that
// any documentation, advertising materials, and other materials related
// to such distribution and use acknowledge that the software was
// developed by the University of Southern California, Information
// Sciences Institute. The name of the University may not be used to
// endorse or promote products derived from this software without
// specific prior written permission.
//
// THE UNIVERSITY OF SOUTHERN CALIFORNIA makes no representations about
// the suitability of this software for any purpose. THIS SOFTWARE IS
// PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
// INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
//
// Other copyrights might apply to parts of this software and are so
// noted when applicable.
//
// Multimedia cache implementation
//
// $Header$
#include
#include
#include "rap/media-app.h"
#include "mcache.h"
MediaPage::MediaPage(const char *n, int s, double mt, double et,
double a, int l) :
ClientPage(n, s, mt, et, a), num_layer_(l), locked_(0), realsize_(0)
{
for (int i = 0; i < num_layer_; i++) {
hc_[i] = new HitCount(this, i);
flags_[i] = 0;
}
}
MediaPage::~MediaPage()
{
int i;
for (i = 0; i < num_layer_; i++) {
// Delete hit count list
// These hit count records should have already been removed
// from the cache's hit count list.
assert((hc_[i]->prev() == NULL) && (hc_[i]->next() == NULL));
delete hc_[i];
// Delete media segment list
layer_[i].destroy();
}
}
void MediaPage::print_info(char *buf)
{
ClientPage::print_info(buf);
buf += strlen(buf);
sprintf(buf, " pgtype MEDIA layer %d", num_layer_);
}
// Make the page full with stream data
void MediaPage::create()
{
assert((num_layer_ >= 0) && (num_layer_ < MAX_LAYER));
int i, sz = size_ / num_layer_;
for (i = 0; i < num_layer_; i++) {
// Delete whatever that was there.
layer_[i].destroy();
add_segment(i, MediaSegment(0, sz));
set_complete_layer(i);
}
realsize_ = size_;
}
void MediaPage::add_segment(int layer, const MediaSegment& s)
{
assert((layer >= 0) && (layer < MAX_LAYER));
layer_[layer].add(s);
realsize_ += s.datasize();
if (s.is_last())
set_complete_layer(layer);
}
int MediaPage::is_complete()
{
// Consider a page finished when all NON-EMPTY layers are
// marked as "completed"
for (int i = 0; i < num_layer_; i++)
if (!is_complete_layer(i) && (layer_[i].length() > 0))
return 0;
return 1;
}
void MediaPage::set_complete()
{
for (int i = 0; i < num_layer_; i++)
set_complete_layer(i);
}
// Used for cache replacement
int MediaPage::evict_tail_segment(int layer, int size)
{
if (is_locked() || is_tlocked())
return 0;
assert((layer >= 0) && (layer < MAX_LAYER));
//#ifdef MCACHE_DEBUG
#if 0
char buf[20];
name(buf);
fprintf(stderr, "Page %s evicted layer %d: ", buf, layer);
#endif
int sz = layer_[layer].evict_tail(size);
realsize_ -= sz;
//#ifdef MCACHE_DEBUG
#if 0
fprintf(stderr, "\n");
#endif
return sz;
}
//----------------------------------------------------------------------
// Classes related to a multimedia client page pool
//
// HitCountList and MClientPagePool
//----------------------------------------------------------------------
void HitCountList::update(HitCount *h)
{
HitCount *tmp = h->prev();
if ((tmp != NULL) && (tmp->hc() < h->hc())) {
// Hit count increased, need to move this one up
detach(h);
while ((tmp != NULL) && (tmp->hc() < h->hc())) {
if ((tmp->page() == h->page()) &&
(tmp->layer() < h->layer()))
// XXX Don't violate layer encoding within the
// same page!
break;
tmp = tmp->prev();
}
if (tmp == NULL)
// Insert it at the head
insert(h, head_);
else
append(h, tmp);
} else if ((h->next() != NULL) && (h->hc() < h->next()->hc())) {
// Hit count decreased, need to move this one down
tmp = h->next();
detach(h);
while ((tmp != NULL) && (h->hc() < tmp->hc())) {
if ((h->page() == tmp->page()) &&
(h->layer() < tmp->layer()))
// XXX Don't violate layer encoding within
// the same page!
break;
tmp = tmp->next();
}
if (tmp == NULL)
// At the tail
append(h, tail_);
else
insert(h, tmp);
}
// We may end up with two cases here:
//
// (1) tmp->hc()>h->hc() && tmp->layer()layer(). This is
// the normal case, where both hit count ordering and layer
// ordering are preserved;
//
// (2) tmp->hc()>h->hc() && tmp->layer()>h->layer(). In this
// case, we should move h BEFORE tmp so that the layer
// ordering is not violated. We basically order the list using
// layer number as primary key, and use hit count as secondary
// key.
// Note that the hit count ordering is only violated when more packets
// in layer i are dropped than those in layer i+1.
}
// Check the integrity of the resulting hit count list
void HitCountList::check_integrity()
{
HitCount *p = (HitCount*)head_, *q;
while (p != NULL) {
q = p->next();
while (q != NULL) {
// Check layer ordering
if ((p->page() == q->page()) &&
(p->layer() > q->layer())) {
fprintf(stderr, "Wrong hit count list.\n");
abort();
}
q = q->next();
}
p = p->next();
}
}
void HitCountList::add(HitCount *h)
{
HitCount *tmp = (HitCount*)head_;
// XXX First, ensure that the layer ordering within the same page
// is not violated!!
while ((tmp != NULL) && (tmp->hc() > h->hc())) {
if ((tmp->page() == h->page()) && (tmp->layer() > h->layer()))
break;
tmp = tmp->next();
}
// Then order according to layer number
while ((tmp != NULL) && (tmp->hc() == h->hc()) &&
(tmp->layer() < h->layer()))
tmp = tmp->next();
if (tmp == NULL) {
if (head_ == NULL)
head_ = tail_ = h;
else
append(h, tail_);
return;
} else if ((tmp == head_) &&
((tmp->hc() < h->hc()) || (tmp->layer() > h->layer()))) {
insert(h, head_);
return;
}
// Now tmp->hc()<=h->hc(), or tmp->hc()>h->hc() but
// tmp->layer()>h->layer(), insert h BEFORE tmp
insert(h, tmp);
}
// Debug only
void HitCountList::print()
{
HitCount *p = (HitCount *)head_;
int i = 0;
char buf[20];
while (p != NULL) {
p->page()->name(buf);
fprintf(stderr, "(%s %d %f) ", buf, p->layer(), p->hc());
if (++i % 4 == 0)
printf("\n");
p = p->next();
}
if (i % 4 != 0)
fprintf(stderr, "\n");
}
//------------------------------
// Multimedia client page pool
//------------------------------
static class MClientPagePoolClass : public TclClass {
public:
MClientPagePoolClass() : TclClass("PagePool/Client/Media") {}
TclObject* create(int, const char*const*) {
return (new MClientPagePool());
}
} class_mclientpagepool_agent;
MClientPagePool::MClientPagePool() :
used_size_(0), repl_style_(FINEGRAIN)
{
bind("max_size_", &max_size_);
used_size_ = 0;
}
int MClientPagePool::command(int argc, const char*const* argv)
{
if (argc == 3)
if (strcmp(argv[1], "set-repl-style") == 0) {
// Set replacement style
// set-repl-style