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 <brezak@apollo.hp.com>. * January 31, 1996 * * @(#) $Header$ (LBL) */ #ifdef WIN32 #include <assert.h> #include <io.h> #include <process.h> #include <fcntl.h> #include <windows.h> #include <malloc.h> #include <string.h> #include <stdio.h> #include <time.h> #include <winsock.h> #include <tcl.h> #include "config.h" #include <locale.h> /* 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 <stdlib.h> #include <math.h> 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 <stdlib.h> #include <math.h> 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 <math.h> #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 <stdlib.h> 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 <stdio.h> #include <stdlib.h> #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) { /* * <address> add-hier <level> <mask> <shift> */ 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 <assert.h> #include <stdlib.h> #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 <stdlib.h> #include <assert.h> #include "config.h" #include <tclcl.h> 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 <antenna.h> 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 <errno.h> #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 <address.h> // #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 <stdlib.h> #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 <float.h> #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<IntTcpAgent> (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<Segment> 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 <iostream.h> #include <assert.h> #include <stdlib.h> #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 <tcl.h> } #include <stdlib.h> #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 <stdlib.h> #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) <number> // (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 <x,G> entry: give 0 as src, but can be anything set_hash(ht_star_, 0, dst, slot, iface); } else { //install a <S,G> 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 <tcl.h> } #include "config.h" #include "packet.h" #include "ip.h" #include "classifier.h" #include "route.h" #include "object.h" #include "address.h" #include <iostream.h> 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 <stdlib.h> #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 <packet.h> #include <ip.h> #include <tcp.h> #include <rtp.h> #include <arp.h> #include <dsr/hdr_sr.h> // DSR #include <mac.h> #include <mac-802_11.h> #include <address.h> #include <tora/tora_packet.h> //TORA #include <imep/imep_spec.h> // IMEP #include <aodv/aodv_packet.h> //AODV #include <cmu-trace.h> #include <mobilenode.h> //#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 <assert.h> #include <stdlib.h> // due to definition of NULL #include <stdio.h> // 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 <sys/time.h> #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 <ctype.h> #include <errno.h> #include <stdlib.h> #include <stdio.h> #include <dem.h> 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 <bajaj@parc.xerox.com>, Mar 1997. */ #ifndef lint static const char rcsid[] = "@(#) $Header$ (Xerox)"; #endif #include "config.h" // for string.h #include <stdlib.h> #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 <kannan@isi.edu> // 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 <kkumar@isi.edu> extern "C" { #include <stdarg.h> #include <float.h> }; #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 <stdio.h> #include <ctype.h> #include "packet.h" #include "flags.h" #include "mcast_ctrl.h" #include "errmodel.h" #include "srm-headers.h" // to get the hdr_srm structure #include "classifier.h" static class ErrorModelClass : public TclClass { public: ErrorModelClass() : TclClass("ErrorModel") {} TclObject* create(int, const char*const*) { return (new ErrorModel); } } class_errormodel; static class TwoStateErrorModelClass : public TclClass { public: TwoStateErrorModelClass() : TclClass("ErrorModel/TwoState") {} TclObject* create(int, const char*const*) { return (new TwoStateErrorModel); } } class_errormodel_twostate; static class 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 <math.h> #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 <stdlib.h> #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 <stdlib.h> #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 <stdlib.h> #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 <assert.h> #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 <object.h> #include <packet.h> #include <ip.h> #include <god.h> 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 <stdlib.h> #include <math.h> 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 <stdlib.h> #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 <stdlib.h> #include <math.h> #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 <tcl.h> #include <iostream.h> #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 <addr> 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 <switch> if (strcmp(argv[1], "switch") == 0) { switch_ = (Classifier*) TclObject::lookup(argv[2]); return (TCL_OK); } // cmd routelogic <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 <errmodel.h> #include <mac.h> #include <ll.h> #include <address.h> #include <dsr/hdr_sr.h> 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 <debug.h> #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 <packet.h> #include <random.h> #include <arp.h> #include <ll.h> #include <mac-802_3.h> //#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 <delay.h> #include <connector.h> #include <packet.h> #include <random.h> // #define DEBUG //#include <debug.h> #include <arp.h> #include <ll.h> #include <mac.h> #include <mac-timers.h> #include <mac-802_11.h> /* * 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 <channel.h> #include <mac.h> #include <address.h> 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 <assert.h> #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 <stdlib.h> // strtoul, etc. #include <assert.h> #include <stdio.h> #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 <template.h> #include <mip.h> #include <random.h> #include <address.h> #include <mobilenode.h> #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_", &reg_rtx_); //bind("shift_", &shift_); //bind("mask_", &mask_); bind("reg_lifetime_", &reglftm_); 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 <address.h> #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 <stdlib.h> #include <math.h> #ifndef WIN32 #include <sys/time.h> #endif #include <ctype.h> #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 <math.h> #include <stdlib.h> #include <connector.h> #include <delay.h> #include <packet.h> #include <random.h> //#include <debug.h> #include <arp.h> #include <topography.h> #include <trace.h> #include <address.h> #include <ll.h> #include <mac.h> #include <propagation.h> #include <mobilenode.h> // 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) { /* <mobilenode> setdest <X> <Y> <speed> */ #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 <math.h> #include <stdlib.h> #include <random.h> //#include <debug.h> #include <modulation.h> /* ====================================================================== 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 <stdlib.h> 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 <stdlib.h> /*#include <unistd.h>*/ #include "nilist.h" template<class T> T Slist<T>::get() { Tlink<T>* lnk = (Tlink<T>*) 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 <stdio.h> 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<class T> T* Slist_iter<T>::operator() () { Tlink<T> *lnk = (Tlink<T> *) 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 <phy.h> #include <wired-phy.h> #include <address.h> #include <node.h> #include <random.h> 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 <stdio.h> #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 <stdlib.h> #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 <stdarg.h> #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 <omni-antenna.h> 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 <stdlib.h> 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 <math.h> #include "config.h" #include <packet.h> #include <phy.h> #include <dsr/hdr_sr.h> 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 <stdlib.h> #include <math.h> 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 <object.h> #include <queue.h> #include <drop-tail.h> #include <packet.h> //#include <cmu/cmu-trace.h> #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 <stdio.h> #include <topography.h> #include <propagation.h> #include <wireless-phy.h> 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 <stdio.h> #include <ctype.h> #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 <stdio.h> 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 <sys/time.h> #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 <stdio.h> #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 <math.h> #include <sys/types.h> #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, <phill@ece.ubc.ca> 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 <sys/time.h> // for gettimeofday #include <unistd.h> #endif #include <stdio.h> #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 <stdlib.h> #include <assert.h> #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 <stdlib.h> #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 <stdlib.h> #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 <assert.h> #include <rtqueue.h> #include <cmu-trace.h> #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 <rttable.h> /* ===================================================================== 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;i<NFLOWS;i++) { pending_[i].flowid=-1; pending_[i].status=0; } bind("off_resv_",&off_resv_); bind("off_ip_",&off_ip_); bind("src_", &src_); bind("dst_", &dst_); numfl_.tracer(this); numfl_.name("\"Admitted Flows\""); } void
SALink::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;i<NFLOWS;i++) if (pending_[i].flowid==flowid) return i; return(-1); } int SALink::get_nxt() { int i; for (i=0;i<NFLOWS;i++) { if (pending_[i].flowid==-1) return i; } printf("Ran out of pending space \n"); exit(1); return i; } void SALink::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 <math.h> 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 <stdio.h> #include <stdlib.h> #include <math.h> 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 <address.h> 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 <stdio.h> #include <stdlib.h> #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<SatNode*>(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 <stdlib.h> #include <limits.h> #include "config.h" #include "scheduler.h" #include "packet.h" #ifdef MEMDEBUG_SIMULATIONS #include "mem-trace.h" #endif #include <iostream.h> 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 <key, elem> 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 <djw@juniper.lcs.mit.edu> * 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 <sys/time.h> #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 <stdlib.h> #include <stdio.h> #include <sys/types.h> #include <math.h> #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<sack_right; i++) { SBNI.seq_no_ = i; SBNI.ack_flag_ = 0; SBNI.sack_flag_ = 0; SBNI.retran_ = 0; SBNI.snd_nxt_ = 0; length_++; if (length_ >= 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_right; i++) { // Check to see if this segment is now covered by the sack block if (SBNI.seq_no_ >= 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_; i<sack_right; i++) { // Check to see if this segment's snd_nxt_ is now covered by the sack block if (SBNI.retran_ && SBNI.snd_nxt_ < sack_right) { // the packet was lost again SBNI.retran_ = 0; SBNI.snd_nxt_ = 0; force_timeout = 1; } } } return (force_timeout); } void ScoreBoard::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_; i<SBN[(first_)%SBSIZE].seq_no_+length_; i++) { if (!SBNI.ack_flag_ && !SBNI.sack_flag_ && !SBNI.retran_) { return (i); } } } return (-1); } void ScoreBoard::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 <stdlib.h> #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 <stdlib.h> #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;i<CLASSES;i++) { q_[i] = new PacketQueue; qlimit_[i] = 0; sprintf(buf,"qlimit%d_",i); bind(buf,&qlimit_[i]); } bind("off_ip_",&off_ip_); } protected : void enque(Packet *); Packet *deque(); PacketQueue *q_[CLASSES]; int qlimit_[CLASSES]; int off_ip_; }; static class SimpleIntServClass : public TclClass { public: SimpleIntServClass() : TclClass("Queue/SimpleIntServ") {} TclObject* create(int, const char*const*) { return (new SimpleIntServ); } } class_simple_intserv; void
SimpleIntServ::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()<atoi(argv[2]) && sh->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 <stdlib.h> #include <assert.h> #include <stdio.h> #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 <sraman@parc.xerox.com>, June 1997. */ #include <stdlib.h> #include <math.h> #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 <kannan@isi.edu> // Version Date: Tue Jul 22 15:41:16 PDT 1997 // #ifndef lint static const char rcsid[] = "@(#) $Header$ (USC/ISI)"; #endif #include <stdlib.h> #include <assert.h> #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; i<current_->batch_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<ackcount; i++) opencwnd(); /* if the connection is done, call finish() */ if ((highest_ack_ >= 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 <stdio.h> #include <stdlib.h> #include <sys/types.h> #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<ackcount; i++) opencwnd(); /* check if we are out of fast start mode */ if (fs_mode_ && (highest_ack_ >= 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 <Richard.Mortier@cl.cam.ac.uk> * * 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: * <SEQ=ISS><ACK=RCV_NXT><CTL=SYN,ACK> * 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 <SYN,ACK> 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; ip<sacks; ip+=2, dp++) { if (q->time_ >= *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 <stdio.h> #include <stdlib.h> #include <sys/types.h> #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> 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<TcpSessionAgent> 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 <stdio.h> #include <stdlib.h> #include <sys/types.h> #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 <johnh@isi.edu> * and Vikram Visweswaraiah <visweswa@isi.edu>. * The original SunOS implementation was by Vikram Visweswaraiah * and Ashish Savla <asavla@usc.edu>. * * Rate-based pacing is an experimental addition to TCP * to address the slow-start restart problem. * See <http://www.isi.edu/lsam/publications/rate_based_pacing/index.html> * 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 <stdio.h> #include <stdlib.h> #include <sys/types.h> #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 <stdio.h> #include <stdlib.h> #include <sys/types.h> #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 <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <math.h> #include "ip.h" #include "tcp-rfc793edu.h" #include "flags.h" // Original code contributed by Fernando Cela Diaz, // <fcela@ce.chalmers.se>. // 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 <stdio.h> #include <stdlib.h> #include <sys/types.h> #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 <stdlib.h> #include <math.h> #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<IntTcpAgent> 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<Segment> 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<Segment> seg_iter(seglist_); Segment *curseg; Islist_iter<IntTcpAgent> 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<Segment> seg_iter(seglist_); Segment *curseg; Islist_iter<IntTcpAgent> 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<IntTcpAgent> 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<Segment> 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<Segment> 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<Segment> 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 <stdio.h> #include <stdlib.h> #include <sys/types.h> #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_baseRTT_ || rttLen<=1) v_baseRTT_ = rtt; double expect; // in pkt/sec // actual = (# in transit)/(current rtt) v_actual_ = double(rttLen)/rtt; // expect = (current window size)/baseRTT expect = double(t_seqno_-last_ack_)/v_baseRTT_; // calc actual and expect thruput diff, delta int delta=int((expect-v_actual_)*v_baseRTT_+0.5); if(cwnd_ < ssthresh_) { // slow-start // adj cwnd every other rtt v_inc_flag_ = !v_inc_flag_; if(!v_inc_flag_) v_incr_ = 0; else { if(delta > 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<v_alpha_) // delta<alpha, faster.... v_incr_ = 1/cwnd_; else // current rate is cool. v_incr_ = 0; } } // end of if(rtt > 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;i<v_maxwnd_;i++) { v_sendtime_[i] = -1.; v_transmits_[i] = 0; } } // record a find grained send time and # of transmits int index = seqno % v_maxwnd_; v_sendtime_[index] = vegastime(); ++v_transmits_[index]; /* support ndatabytes_ in output - Lloyd Wood 14 March 2000 */ int bytes = hdr_cmn::access(p)->size(); 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 <stdlib.h> #include <math.h> #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 * <ftp://ftp.ee.lbl.gov/papers/congavoid.ps.Z> * (*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 <stdio.h> #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 <stdlib.h> #include <string.h> #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 <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <math.h> #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 <stdlib.h> #include <sys/types.h> #include <math.h> #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 <stdlib.h> // 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 <stdlib.h> 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 <tk.h> #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 <math.h> #include <stdlib.h> #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 <stdio.h> #include <stdlib.h> #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 <sys/types.h> #include <sys/stat.h> #include <stdio.h> #include "config.h" #ifdef HAVE_NETINET_IN_H #include <netinet/in.h> #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 <math.h> #include <delay.h> #include <packet.h> #include <packet-stamp.h> #include <antenna.h> #include <mobilenode.h> #include <propagation.h> #include <wireless-phy.h> #include <tworayground.h> 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;i<osize;i++) maddr_[i] = temp[i]; for (i=osize;i<size_;i++) maddr_[i] = -1; delete [] temp; } int VARPTable::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 <sys/time.h> #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 <wired-phy.h> 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 <math.h> #include <packet.h> #include <mobilenode.h> #include <phy.h> #include <propagation.h> #include <modulation.h> #include <omni-antenna.h> #include <wireless-phy.h> #include <packet.h> #include <ip.h> #include <agent.h> #include <trace.h> /* ====================================================================== 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 <stdlib.h> #include <string.h> #include <ctype.h> #ifdef WIN32 #include <windows.h> #include <winsock.h> #else #include <sys/param.h> #include <netdb.h> #include <sys/socket.h> #include <unistd.h> #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 <netinet/in_systm.h> #include <netinet/ip.h> #include <stdio.h> 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 <aodv/aodv.h> #include <aodv/aodv_packet.h> #include <ip.h> #include <random.h> #include <cmu-trace.h> #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 <aodv/aodv.h> #include <aodv/aodv_packet.h> #include <ip.h> #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 <stdarg.h> #include <float.h> }; #include "dsdv.h" #include "priqueue.h" #include <random.h> #include <cmu-trace.h> #include <address.h> #include <mobilenode.h> #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 <string.h> #include <stdlib.h> #include <stdio.h> #include <assert.h> #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<elts;max++) { if (ent.dst < rtab[max].dst) { break; } } // Copy all the further out guys out yet another. // bcopy does not seem to quite work on sunos??? //bcopy(&rtab[max], &rtab[max+1], sizeof(rtable_ent)*(elts-max)); //if (elts) { int i = elts-1; while (i >= 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 <assert.h> #include <math.h> #include <stdio.h> #include <signal.h> #include <float.h> #include <object.h> #include <agent.h> #include <trace.h> #include <packet.h> #include <scheduler.h> #include <random.h> #include <mac.h> #include <ll.h> #include <cmu-trace.h> #include <address.h> #include <mobilenode.h> #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_BUF_SIZE ; c++) { if (send_buf[c].p.pkt == NULL) continue; if (Scheduler::instance().clock() - send_buf[c].t > 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 <RTREP_HOLDOFF_SIZE ; c ++) { RtRepHoldoff *entry = &(rtrep_holdoff[c]); // there is no point in doing this first check if we're always // going to send our route reply unless we hear the requester use one // better or equal to ours if (entry->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 <stdio.h> #include <dsr/hdr_sr.h> 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 <stdio.h> #include <stdarg.h> }; #undef DEBUG #include <god.h> #include "path.h" #include "routecache.h" #ifdef DSR_CACHE_STATS #include <dsr/cache_stats.h> #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 <assert.h> #include <stdio.h> } #include <packet.h> #include <ip.h> #include <dsr/hdr_sr.h> #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,"<empty path>"); 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,"[<empty path>]"); 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 <stdio.h> #include <stdarg.h> }; #undef DEBUG #include "path.h" #include "routecache.h" #include <god.h> 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 <sys/types.h> #include <sys/param.h> #include <sys/socket.h> #include <sys/ioctl.h> #include <netinet/in.h> #include <netinet/in_systm.h> #include <netinet/ip.h> #include <net/if.h> #include <net/ethernet.h> #include <net/if_arp.h> #include <netinet/if_ether.h> #include <arpa/inet.h> #include <memory.h> #include <stdio.h> #include <errno.h> #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 <stdio.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/ioctl.h> #include <net/ethernet.h> #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 <net/if.h> 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 <stdio.h> #include <sys/types.h> #include <netinet/in.h> #include <netinet/in_systm.h> #include <netinet/ip.h> #include <netinet/ip_icmp.h> #include <netinet/ip_icmp.h> #include <arpa/inet.h> #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 <stdio.h> #include <sys/types.h> #include <netinet/in.h> #include <netinet/in_systm.h> #include <netinet/ip.h> #include <netinet/ip_icmp.h> #include <netinet/ip_icmp.h> #include <arpa/inet.h> #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 <netinet/in_systm.h> #include <netinet/ip.h> 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 <stdio.h> #include <sys/types.h> #include <netinet/in.h> #include <netinet/in_systm.h> #include <netinet/ip.h> #ifndef __FAVOR_BSD #define __FAVOR_BSD #endif; #include <netinet/tcp.h> #include <arpa/inet.h> #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 <ipaddr> 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 <ipaddr> 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 <ipaddr> 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 <ipaddr> 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 <stdio.h> #ifndef WIN32 #include <unistd.h> #endif #include <time.h> #include <errno.h> #include <string.h> #ifdef WIN32 #include <io.h> #define close closesocket #else #include <sys/param.h> #include <sys/socket.h> #include <sys/ioctl.h> #include <netinet/in.h> #include <netinet/in_systm.h> #include <netinet/ip.h> typedef int Socket; #endif #if defined(sun) && defined(__svr4__) #include <sys/systeminfo.h> #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 <stdio.h> #ifndef WIN32 #include <unistd.h> #endif #include <time.h> #include <errno.h> #include <string.h> #ifdef WIN32 #include <io.h> #define close closesocket #else #include <sys/param.h> #include <sys/socket.h> #include <sys/ioctl.h> #endif #if defined(sun) && defined(__svr4__) #include <sys/systeminfo.h> #endif #ifdef __cplusplus extern "C" { #include <pcap.h> } #else #include <pcap.h> #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 <fcntl.h> #include <net/if.h> 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 <netinet/in.h> 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 <net/if.h> #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 <stdlib.h> #include <math.h> #ifndef WIN32 #include <unistd.h> #endif #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <errno.h> #include <string.h> #ifdef WIN32 #include <windows.h> #include <winsock.h> #else #include <sys/socket.h> #include <sys/uio.h> #include <sys/time.h> #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 <stdio.h> #include <sys/types.h> #include <netinet/in.h> #include <netinet/in_systm.h> #include <netinet/ip.h> #include <netinet/ip_icmp.h> #include <netinet/ip_icmp.h> #include <arpa/inet.h> #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 <errno.h> 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 <imep/dest_queue.h> #ifdef TEST_ONLY #include <stdio.h> #else #include <imep/imep.h> #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 <packet.h> #include <ip.h> #include <random.h> #include <cmu-trace.h> #include <imep/imep.h> #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 <imep/imep.h> #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 <random.h> #include <packet.h> #include <imep/imep.h> // ====================================================================== // ====================================================================== // 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 <imep/imep.h> #include <tora/tora_packet.h> #include <packet.h> // ====================================================================== // 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 <imep/imep.h> #include <packet.h> #include <ip.h> #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 <imep/imep.h> #include <packet.h> #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 <assert.h> #include <packet.h> #include <list.h> #include <imep/rxmit_queue.h>
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 <stdlib.h> #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 * <j.krinke@gmx.de> */ void operator delete[](void *pnt) { char *file; GET_RET_ADDR(file); _free_leap(file, 0, pnt); } #else /* DMALLOC_MAJOR_VERSION == 3 */ extern "C" { #include <stdlib.h> #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 <string.h> // 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 <stream.h> #include <stdlib.h> #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 <stdarg.h> #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 <tmp> 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 <tmp> 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 <size> 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 <offset> 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<layers;i++) { if (buffer_[i] < MWM(srtt)) { if ((buffer_[i-1] <= buffer_[i]+seg_size_) && (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<MAX_LAYER; l++) { optbufs1[l] = 0.0; optbufs2[l] = 0.0; } // XXX Note: when per-layer BW is low, and srtt is very small // (e.g., in a LAN), the following code will result in that // one buffered // segment will produce a abort() of "maximum backoff reached". /* next scenario 1 state */ while ((totbufs1 <= TotalBuf(layers, buffer_)) && (bs1 <= MAXBKOFF_)) { totbufs1 = 0.0; bs1++; for (l=0; l<layers;l++) { optbufs1[l] = bufOptScen1(l,layers,rate,slope, bs1)+MWM(srtt); totbufs1 += optbufs1[l]; } } // bs1 is the min no of back off that we can not handle for // s1 now /* next secenario 2 state */ while ((totbufs2 <= TotalBuf(layers, buffer_)) && (bs2 <= MAXBKOFF_)) { totbufs2 = 0.0; bs2++; for (l=0; l<layers;l++) { optbufs2[l] = bufOptScen2(l,layers,rate,slope, bs2)+MWM(srtt); totbufs2 += optbufs2[l]; } } /* * NOTE: at this point, totbufs1 could be less than total * buffering * when it is enough for recovery from rate = 0; * so totbufs1 <= TotalBuf(layers, buffer) is OK * HOWEVER, in this case, we MUST shoot for scenario 2 */ /* debug */ /* if ((totbufs2 <= TotalBuf(layers, buffer_)) && (bs2 <= MAXBKOFF_)) { panic("# ERROR: totbufs1: %.2f,tot bufs2: %.2f, \ totbuf: %.2f, bs1: %d, bs2: %d, totneededbuf1: %.2f, totneededbuf2: %2f\n", totbufs1, totbufs2, TotalBuf(layers, buffer_), bs1, bs2, TotalBuf(layers, optbufs1), TotalBuf(layers, optbufs2)); } */ /* debug */ if (bs2 >= 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<layers; l++) { if (buffer_[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<layers) && (!done)){ if (TotalBuf(layers, buffer_) >= totbufs2) { done ++; } else { if (buffer_[l]<min(optbufs2[l], optbufs1[l])) { if((buffer_[l-1] <= buffer_[l]+ seg_size_) && (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;i<layers;i++) { if (lowest>buffer_[i]) { lowest=buffer_[i]; lowix=i; } } if (lowest<MWM(srtt)) { last_depart = now; // debug("A':sending layer %d, below MWM in Drain t: %.2f\n", // lowix, now); return output(size, lowix); } if((nextAdjPoint < 0) || /* first draining phase */ (flag >= 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<MAX_LAYER; l++){ tosend[l] = 0; } for(l=0; l<layers; l++){ tosend[l] = srtt*LAYERBW_ - FinalDrainArray[l]; // Correct for numerical error if (fabs(tosend[l]) < QA_EPSILON) tosend[l] = 0.0; } /* * XXX, not sure if this is the best thing * we might only increase it */ tosendPtr = 0; /* debug only */ if ((bufToDrain <= 0) || AllZero(FinalDrainArray, layers) || AllZero(tosend, layers)) { debug("# Error: bufToDrain: %.2f, %d layers, " "srtt: %.2f\n", bufToDrain, layers, srtt); for (l=0; l<layers; l++) debug("# FinalDrainArray[%d]: %.2f, " "tosend[%d]: %.2f\n", l, FinalDrainArray[l],l, tosend[l]); /* Tcl::instance().eval("[Test instance] flush-trace"); abort(); */ } /*******/ } flag = -1; last_rate = rate; done = 0; cnt = 1; while ((!done) && (cnt <= layers)) { if (tosend[tosendPtr] > 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; i<len; i++) if (arr[i] != 0.0) // debug("-- arr[%d}: %f\n", i, arr[i]); return 0; return 1; } // // Calculate accumulative amount of buffering for the lowest "n" layers // double QA::TotalBuf(int n, double *buffer) { double totbuf = 0.0; int i; for(i=0; i<n; i++) totbuf += buffer[i]; return totbuf; } // Update buffer_ information for a given layer // Get an output data packet from applications above AppData* QA::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<layers; l++){ bufReq1 += bufOptScen1(l, layers, rate, slope, bs1); bufReq2 += bufOptScen2(l, layers, rate, slope, bs2); } for(l=0; l<MAX_LAYER; l++){ bufs1[l] = 0; bufs2[l] = 0; DrainArr[l] = 0.0; } while(bufReq1 > TotalBuf(layers, bufAvail)){ bufReq1 = 0; bs1--; for(l=0; l<layers; l++){ bufs1[l] = bufOptScen1(l, layers, rate, slope, bs1); bufReq1 += bufs1[l]; } } while(bufReq2 > TotalBuf(layers, bufAvail)){ bufReq2 = 0; bs2--; for(l=0; l<layers; l++){ bufs2[l] = bufOptScen2(l, layers, rate, slope, bs2); bufReq2 += bufs2[l]; } } if (bufReq1 >= 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<layers; l++) { if(bufs1[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<MAX_LAYER; l++){ FinalDrainArray[l] = 0.0; bufAvail[l] = buffer_[l]; } TotBufAvail = TotalBuf(layers, bufAvail); cnt = 0; while ((bufToDrain > 0) && (cnt < 10)) { // debug("bufToDrain%d: %.2f\n", cnt, bufToDrain); drain_buf(DrainArr, bufToDrain, FinalDrainArray, bufAvail, layers, rate, srtt); for(l=0; l<layers; l++){ bufToDrain -= DrainArr[l]; TotBufAvail -= DrainArr[l]; FinalDrainArray[l] += DrainArr[l]; bufAvail[l] -= DrainArr[l]; FinalBuffer[l] = buffer_[l] - FinalDrainArray[l]; } cnt++; } } void QA::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;i<MAX_LAYER;i++) { sprintf(tmp, " %.2f", buffer_[i]+i*10000); strcat(s1,tmp); strcat(s2,tmp); } log("QA %s \n", s1); log("QA %s \n", s2); fflush(stdout); } // This routine models draining of buffers at the recv // it periodically updates state of buffers // Ir must be called once and then it reschedules itself // it is first called after playout is started! void QA::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 <stdarg.h> #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 <stdarg.h> #include <float.h> }; #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 <stdarg.h> #include <float.h> }; #include "flood-agent.h" #include <random.h> #include <cmu-trace.h> #include <address.h> #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 <stdarg.h> #include <float.h> }; #include "landmark.h" #include <random.h> #include <cmu-trace.h> #include <address.h> 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 <stdarg.h> #include <float.h> }; #include "sensor-query.h" #include "landmark.h" #include <random.h> #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 <stdarg.h> #include <float.h> }; #include "tags.h" #include "random.h" #include <string.h> #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 <agent.h> #include <random.h> #include <trace.h> #include <ll.h> #include <priqueue.h> #include <tora/tora_packet.h> #include <tora/tora.h> #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 <tora/tora.h> #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 <agent.h> #include <random.h> #include <trace.h> #include <ll.h> #include <priqueue.h> #include <tora/tora_packet.h> #include <tora/tora.h> #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 <tora/tora_packet.h> #include <tora/tora.h> /* ====================================================================== 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 <agent.h> #include <random.h> #include <trace.h> #include <ll.h> #include <priqueue.h> #include <tora/tora_packet.h> #include <tora/tora.h> #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 <agent.h> #include <random.h> #include <trace.h> #include <ll.h> #include <priqueue.h> #include <tora/tora_packet.h> #include <tora/tora.h> #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 <stdlib.h> #include <assert.h> #include <string.h> #include <stdarg.h> #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) { /* * <http> get-cnc <client> * * 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) { /* * <http> is-connected <server> */ 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) { /* * <http> disconnect <client> * 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) { /* * <http> get-pagetype <pageid> * 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) { /* * <http> connect <client> <ts> * * Associate a TCP agent with the given client. * <ts> 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) { /* * <http> send <client> <bytes> <callback> */ 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) { /* * <server> add-inv <pageid> <modtime> */ 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) { /* * <cache> check-sstate <sid> <cid> * 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) { /* * <cache> join <server_id> <cache> * * <server> join via <cache>. If they are the same, * it means we are the primary cache for <server>. */ 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) { /* * <cache> parent-cache <web_server_id> * Return the parent cache of <web_server_id> 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) { /* * <cache> recv-inv <pageid> <modtime> * 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) { /* * <cache> recv-push <pageid> 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) { /* * <self> register-server <cache_id> <server_id> * We get a GET response about a page from <server>, * which we hear from <cache> */ 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) { /* * <cache> enter-metadata <args...> * 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 <assert.h> #include <stdio.h> #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()<h->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 // <obj> set-repl-style <style> if (strcmp(argv[2], "FINEGRAIN") == 0) repl_style_ = FINEGRAIN; else if (strcmp(argv[2], "ATOMIC") == 0) repl_style_ = ATOMIC; else { fprintf(stderr, "Unknown style %s", argv[3]); return (TCL_ERROR); } return (TCL_OK); } return ClientPagePool::command(argc, argv); } void MClientPagePool::hc_update(const char *name, int max_layer) { MediaPage *pg = (MediaPage*)get_page(name); assert(pg != NULL); int i; HitCount *h; // First we update the hit count of each layer of the given page for (i = 0; i <= max_layer; i++) pg->hit_layer(i); // Then we update the position of these hit count records for (i = 0; i <= max_layer; i++) { h = pg->get_hit_count(i); hclist_.update(h); } #if 1 hclist_.check_integrity(); #endif } // Add a segment to an object, and adjust hit counts accordingly // XXX Call cache replacement algorithm if necessary int MClientPagePool::add_segment(const char* name, int layer, const MediaSegment& s) { MediaPage* pg = (MediaPage *)get_page(name); if (pg == NULL) return -1; if (layer >= pg->num_layer()) { if (s.datasize() == 0) return 0; else { fprintf(stderr, "MClientPagePool: cannot add a new layer.\n"); abort(); } } // Check space availability if (used_size_ + s.datasize() > max_size_) { // If atomic replacement is used, page size is deducted in // remove_page(). If fine-grain is used, evicted size is // deducted in repl_finegrain(). cache_replace(pg, s.datasize()); //#ifdef MCACHE_DEBUG #if 0 fprintf(stderr, "Replaced for page %s segment (%d %d) layer %d\n", name, s.start(), s.end(), layer); #endif } // Add new page. When we are doing atomic replacement, the size that // we evicted may be larger than what we add. used_size_ += s.datasize(); // If this layer was not 'in' before, add its hit count block if (pg->layer_size(layer) == 0) hclist_.add(pg->get_hit_count(layer)); // Add new segment pg->add_segment(layer, s); return 0; } void MClientPagePool::fill_page(const char* pgname) { MediaPage *pg = (MediaPage*)get_page(pgname); used_size_ -= pg->realsize(); // Lock this page before we do any replacement. pg->lock(); pg->create(); // If we cannot hold the nominal size of the page, do replacement if (used_size_ + pg->size() > max_size_) // Size deduction has already been done in remove_page() cache_replace(pg, pg->size()); used_size_ += pg->size(); pg->unlock(); } ClientPage* MClientPagePool::enter_page(int argc, const char*const* argv) { double mt = -1, et, age = -1, noc = 0; int size = -1, media_page = 0, layer = -1; for (int i = 3; i < argc; i+=2) { if (strcmp(argv[i], "modtime") == 0) mt = strtod(argv[i+1], NULL); else if (strcmp(argv[i], "size") == 0) size = atoi(argv[i+1]); else if (strcmp(argv[i], "age") == 0) age = strtod(argv[i+1], NULL); else if (strcmp(argv[i], "noc") == 0) // non-cacheable flag noc = 1; else if (strcmp(argv[i], "pgtype") == 0) { if (strcmp(argv[i+1], "MEDIA") == 0) media_page = 1; } else if (strcmp(argv[i], "layer") == 0) layer = atoi(argv[i+1]); } // XXX allow mod time < 0 and age < 0! if ((size < 0) || (media_page && (layer <= 0))) { fprintf(stderr, "%s: wrong page information %s\n", name_, argv[2]); return NULL; } et = Scheduler::instance().clock(); ClientPage *pg; if (media_page) pg = new MediaPage(argv[2], size, mt, et, age, layer); else pg = new ClientPage(argv[2], size, mt, et, age); if (add_page(pg) < 0) { delete pg; return NULL; } if (noc) pg->set_uncacheable(); if (media_page) ((MediaPage *)pg)->lock(); return pg; } int MClientPagePool::cache_replace(ClientPage *pg, int size) { switch (repl_style_) { case FINEGRAIN: return repl_finegrain(pg, size); case ATOMIC: #if 0 char tmp[128]; pg->name(tmp); fprintf(stderr, "Replaced for page %s size %d\n", tmp, size); fprintf(stderr, "Used size %d, max size %d\n", used_size_, max_size_); #endif return repl_atomic(pg, size); default: fprintf(stderr, "Corrupted replacement style.\n"); abort(); } // To make msvc happy return -1; } int MClientPagePool::repl_atomic(ClientPage*, int size) { // XXX We use standard LRU to determine the stream to be kicked out. // The major problem is that we do not keep discrete hit counts. // We solve the problem by using hit counts of the base layer as // a close approximate. Because whenever a stream is accessed, // it's assumed that the client bw can always afford the base layer, // this should be a fairly good approximation. HitCount *h, *p; int sz, totalsz = 0; // Repeatedly get rid of streams until get enough space h = (HitCount*)hclist_.tail(); while (h != NULL) { if (h->layer() != 0) { // We only look for the base layer h = h->prev(); continue; } MediaPage *pg = (MediaPage *)h->page(); // Don't touch locked pages if (pg->is_tlocked() || pg->is_locked()) { h = h->prev(); continue; } sz = pg->realsize(); totalsz += sz; char tmp[HTTP_MAXURLLEN]; pg->name(tmp); // Before we delete, find the previous hit count record that // does not belong to this page. p = h->prev(); while ((p != NULL) && (p->page() == h->page())) p = p->prev(); h = p; // XXX Manually remove hit count before deleting it for (int i = 0; i < pg->num_layer(); i++) { p = pg->get_hit_count(i); hclist_.detach(p); } // Delete the page, together with its media segment list #if 0 fprintf(stderr, "At time %g, atomic replacement evicted page %s\n", Scheduler::instance().clock(), tmp); fprintf(stderr, "Hit count list: \n"); hclist_.print(); fprintf(stderr,"----------------------------------------\n\n"); #endif remove_page(tmp); if (sz >= size) return totalsz; // Continue to evict to meet the space requirement size -= sz; } fprintf(stderr, "Cache replacement cannot get enough space.\n"); abort(); return 0; // Make msvc happy } int MClientPagePool::repl_finegrain(ClientPage *, int size) { // Traverse through hit count table, evict segments from the tail // of a layer with minimum hit counts HitCount *h, *p; int sz, totalsz = 0; // Repeatedly evict pages/segments until get enough space h = (HitCount*)hclist_.tail(); while (h != NULL) { MediaPage *pg = (MediaPage *)h->page(); // XXX Don't touch locked pages if (pg->is_tlocked() || pg->is_locked()) { h = h->prev(); continue; } // Try to get "size" space by evicting other segments sz = pg->evict_tail_segment(h->layer(), size); // Decrease the cache used space used_size_ -= sz; totalsz += sz; // If we have not got enough space, we must have got rid of // the entire layer assert((sz == size) || ((sz < size) && (pg->layer_size(h->layer()) == 0))); // If we don't have anything of this layer left, get rid of // the hit count record. // XXX Must do this BEFORE removing the page p = h; h = h->prev(); if (pg->layer_size(p->layer()) == 0) { // XXX Should NEVER delete a hit count record!! // A hit count record is ONLY deleted when the page // is deleted (evicted from cache: ~MediaPage()) hclist_.detach(p); p->reset(); } // Furthermore, if the page has nothing left, get rid of it if (pg->realsize() == 0) { // NOTE: we do not manually remove hit counts of // this page because if its realsize is 0, all // hit count records must have already been // detached from the page. char tmp[HTTP_MAXURLLEN]; pg->name(tmp); #if 0 fprintf(stderr, "At time %g, fine-grain evicted page %s\n", Scheduler::instance().clock(), tmp); fprintf(stderr, "Hit count list: \n"); hclist_.print(); fprintf(stderr, "---------------------------------------\n\n"); #endif // Then the hit count record will be deleted in here remove_page(tmp); } // If we've got enough space, return; otherwise continue if (sz >= size) return totalsz; size -= sz; // Evict to fill the rest } fprintf(stderr, "Cache replacement cannot get enough space.\n"); abort(); return 0; // Make msvc happy } // Clean all hit count record of a page regardless of whether it's in the // hit count list. Used when hclist_ is not used at all, e.g., by MediaClient. int MClientPagePool::force_remove(const char *name) { // XXX Bad hack. Needs to integrate this into ClientPagePool. ClientPage *pg = (ClientPage*)get_page(name); // We should not remove a non-existent page!! assert(pg != NULL); if (pg->type() == MEDIA) { HitCount *p; MediaPage *q = (MediaPage*)pg; used_size_ -= q->realsize(); for (int i = 0; i < q->num_layer(); i++) { p = q->get_hit_count(i); hclist_.detach(p); } } else if (pg->type() == HTML) used_size_ -= pg->size(); return ClientPagePool::remove_page(name); } int MClientPagePool::remove_page(const char *name) { // XXX Bad hack. Needs to integrate this into ClientPagePool. ClientPage *pg = (ClientPage*)get_page(name); // We should not remove a non-existent page!! assert(pg != NULL); if (pg->type() == MEDIA) used_size_ -= ((MediaPage *)pg)->realsize(); else if (pg->type() == HTML) used_size_ -= pg->size(); return ClientPagePool::remove_page(name); } //------------------------------------------------------------ // MediaPagePool // Generate requests and pages for clients and servers //------------------------------------------------------------ static class MediaPagePoolClass : public TclClass { public: MediaPagePoolClass() : TclClass("PagePool/Media") {} TclObject* create(int, const char*const*) { return (new MediaPagePool()); } } class_mediapagepool_agent; MediaPagePool::MediaPagePool() : PagePool() { size_ = NULL; duration_ = 0; layer_ = 1; } // For now, only one page, fixed size, fixed layer int MediaPagePool::command(int argc, const char*const* argv) { Tcl& tcl = Tcl::instance(); if (argc == 2) { if (strcmp(argv[1], "get-poolsize") == 0) { tcl.resultf("%d", num_pages_); return TCL_OK; } else if (strcmp(argv[1], "get-start-time") == 0) { tcl.resultf("%.17g", start_time_); return TCL_OK; } else if (strcmp(argv[1], "get-duration") == 0) { tcl.resultf("%d", duration_); return TCL_OK; } } else if (argc == 3) { if (strcmp(argv[1], "gen-pageid") == 0) { // Generating requested page id if (rvReq_ == NULL) { tcl.add_errorf("no page id ranvar."); return TCL_ERROR; } int p = (int)rvReq_->value(); assert((p >= 0) && (p < num_pages_)); tcl.resultf("%d", p); return TCL_OK; } else if (strcmp(argv[1], "is-media-page") == 0) { // XXX Currently all pages are media pages. Should // be able to allow both normal pages and media pages // in the future tcl.result("1"); return TCL_OK; } else if (strcmp(argv[1], "get-layer") == 0) { // XXX Currently all pages have the same number of // layers. Should be able to change this in future. tcl.resultf("%d", layer_); return TCL_OK; } else if (strcmp(argv[1], "set-start-time") == 0) { double st = strtod(argv[2], NULL); start_time_ = st; end_time_ = st + duration_; return TCL_OK; } else if (strcmp(argv[1], "set-duration") == 0) { // XXX Need this info to set page mod time!! duration_ = atoi(argv[2]); end_time_ = start_time_ + duration_; return TCL_OK; } else if (strcmp(argv[1], "gen-init-modtime") == 0) { // XXX We are not interested in page consistency here, // so never change this page. tcl.resultf("%d", -1); return TCL_OK; } else if (strcmp(argv[1], "gen-size") == 0) { int pagenum = atoi(argv[2]); if (pagenum >= num_pages_) { tcl.add_errorf("Invalid page id %d", pagenum); return TCL_ERROR; } tcl.resultf("%d", size_[pagenum]); return TCL_OK; } else if (strcmp(argv[1], "set-layer") == 0) { layer_ = atoi(argv[2]); return TCL_OK; } else if (strcmp(argv[1], "set-num-pages") == 0) { if (size_ != NULL) { tcl.add_errorf("can't change number of pages"); return TCL_ERROR; } num_pages_ = atoi(argv[2]); size_ = new int[num_pages_]; return TCL_OK; } else if (strcmp(argv[1], "ranvar-req") == 0) { rvReq_ = (RandomVariable*)TclObject::lookup(argv[2]); return TCL_OK; } } else if (argc == 4) { if (strcmp(argv[1], "gen-modtime") == 0) { // This should never be called, because we never // deals with page modifications!! fprintf(stderr, "%s: gen-modtime called!\n", name()); abort(); } else if (strcmp(argv[1], "set-pagesize") == 0) { // <pagepool> set-pagesize <pagenum> <size> int pagenum = atoi(argv[2]); if (pagenum >= num_pages_) { tcl.add_errorf("Invalid page id %d", pagenum); return TCL_ERROR; } size_[pagenum] = atoi(argv[3]); return TCL_OK; } } return PagePool::command(argc, argv); } //---------------------------------------------------------------------- // PagePool that generates requests using the SURGE model // // Part of the code by Paul Barford (barford@cs.bu.edu). // Copyright (c) 1997 Trustees of Boston University // // Allow two options: (1) setting if all pages are media page or normal // HTTP pages; (2) average page size //---------------------------------------------------------------------- // static class SurgePagePoolClass : public TclClass { // public: // SurgePagePoolClass() : TclClass("PagePool/Surge") {} // TclObject* create(int, const char*const*) { // return (new SurgePagePool()); // } // } class_surgepagepool_agent; // SurgePagePool::SurgePagePool() : PagePool() // { // } //---------------------------------------------------------------------- // Multimedia web applications: cache, etc. //---------------------------------------------------------------------- static class MediaCacheClass : public TclClass { public: MediaCacheClass() : TclClass("Http/Cache/Media") {} TclObject* create(int, const char*const*) { return (new MediaCache()); } } class_mediacache; // By default we use online prefetching MediaCache::MediaCache() : pref_style_(ONLINE_PREF) { cmap_ = new Tcl_HashTable; Tcl_InitHashTable(cmap_, TCL_ONE_WORD_KEYS); } MediaCache::~MediaCache() { Tcl_HashEntry *he; Tcl_HashSearch hs; if (cmap_) { for (he = Tcl_FirstHashEntry(cmap_, &hs); he != NULL; he = Tcl_NextHashEntry(&hs)) delete (RegInfo*)Tcl_GetHashValue(he); Tcl_DeleteHashTable(cmap_); delete cmap_; } } AppData* MediaCache::get_data(int& size, AppData* req) { assert(req != NULL); if (req->type() != MEDIA_REQUEST) { return HttpApp::get_data(size, req); } MediaRequest *r = (MediaRequest *)req; // Get statistics block for the requestor Tcl_HashEntry *he = Tcl_FindHashEntry(cmap_, (const char *)(r->app())); assert(he != NULL); RegInfo *ri = (RegInfo *)Tcl_GetHashValue(he); // Process request if (r->request() == MEDIAREQ_GETSEG) { // Get a new data segment MediaPage* pg = (MediaPage*)pool_->get_page(r->name()); assert(pg != NULL); MediaSegment s1(r->st(), r->et()); MediaSegment s2 = pg->next_overlap(r->layer(), s1); HttpMediaData *p; if (s2.datasize() == 0) { // No more data available for this layer, allocate // an ADU with data size 0 to signal the end // of transmission for this layer size = 0; p = new HttpMediaData(name(), r->name(), r->layer(), 0, 0); } else { size = s2.datasize(); p = new HttpMediaData(name(), r->name(), r->layer(), s2.start(), s2.end()); } // XXX If we are still receiving the stream, don't // ever say that this is the last segment. If the // page is not locked, it's still possible that we // return a NULL segment because the requested one // is not available. Don't set the 'LAST' flag in this // case. if (s2.is_last()) { p->set_last(); if (!pg->is_locked() && (s2.datasize() == 0) && (r->layer() == 0)) p->set_finish(); } //---------------------------------------- // Update statistics of this connection //---------------------------------------- // Update the highest layer that this client has requested if (ri->hl_ < r->layer()) ri->hl_ = r->layer(); if (size > 0) { // Update total delivered bytes ri->db_[r->layer()] += size; // Update prefetched bytes that've been delivered ri->eb_[r->layer()] += ri->pref_size(r->layer(), s2); } return p; } else if (r->request() == MEDIAREQ_CHECKSEG) { // If we are not doing online prefetching, return nothing if (pref_style_ != ONLINE_PREF) return NULL; // Check the availability of a new data segment // And refetch if it is not available MediaPage* pg = (MediaPage*)pool_->get_page(r->name()); assert(pg != NULL); if (pg->is_locked()) // If we are during the first retrieval, don't prefetch return NULL; MediaSegmentList ul = pg->is_available(r->layer(), MediaSegment(r->st(),r->et())); if (ul.length() == 0) // All segments are available return NULL; // Otherwise do prefetching on these "holes" char *buf = ul.dump2buf(); Tcl::instance().evalf("%s pref-segment %s %s %d %s", name(), r->app()->name(), r->name(), r->layer(), buf); // log("E PREF p %s l %d %s\n", r->name(), r->layer(), buf); delete []buf; ul.destroy(); // Update the highest layer that this client has requested Tcl_HashEntry *he = Tcl_FindHashEntry(cmap_, (const char *)(r->app())); assert(he != NULL); RegInfo *ri = (RegInfo *)Tcl_GetHashValue(he); if (ri->hl_ < r->layer()) ri->hl_ = r->layer(); return NULL; } fprintf(stderr, "MediaCache %s gets an unknown MediaRequest type %d\n", name(), r->request()); abort(); return NULL; // Make msvc happy } // Add received media segment into page pool void MediaCache::process_data(int size, AppData* data) { switch (data->type()) { case MEDIA_DATA: { HttpMediaData* d = (HttpMediaData*)data; // Cache this segment, do replacement if necessary if (mpool()->add_segment(d->page(), d->layer(), MediaSegment(*d)) == -1) { fprintf(stderr, "MediaCache %s gets a segment for an " "unknown page %s\n", name(), d->page()); abort(); } if (d->is_pref()) { // Update total prefetched bytes Tcl_HashEntry *he = Tcl_FindHashEntry(cmap_, (const char*)(d->conid())); // Client-cache-server disconnection procedure: // (1) client disconnects from cache, then // (2) cache disconnects from server and shuts down // prefetching channel. // Therefore, after client disconnects, the cache // may still receive a few prefetched segments. // Ignore those because we no longer keep statistics // about the torn-down connection. if (he != NULL) { RegInfo *ri = (RegInfo *)Tcl_GetHashValue(he); ri->add_pref(d->layer(), MediaSegment(*d)); ri->pb_[d->layer()] += d->datasize(); } } // XXX debugging only #if 1 log("E RSEG p %s l %d s %d e %d z %d f %d\n", d->page(), d->layer(), d->st(), d->et(), d->datasize(), d->is_pref()); #endif break; } default: HttpCache::process_data(size, data); } } int MediaCache::command(int argc, const char*const* argv) { Tcl& tcl = Tcl::instance(); if (argc == 2) { if (strcmp(argv[1], "get-pref-style") == 0) { switch (pref_style_) { case NOPREF: tcl.result("NOPREF"); break; case ONLINE_PREF: tcl.result("ONLINE_PREF"); break; case OFFLINE_PREF: tcl.result("OFFLINE_PREF"); break; default: fprintf(stderr, "Corrupted prefetching style %d", pref_style_); return TCL_ERROR; } return TCL_OK; } } else if (argc == 3) { if (strcmp(argv[1], "offline-complete") == 0) { // Delete whatever segments in the given page, // make it complete. Used by offline prefetching ClientPage *pg = mpool()->get_page(argv[2]); if (pg == NULL) // XXX It's possible that we've already kicked // it out of the cache. Do nothing. return TCL_OK; assert(pg->type() == MEDIA); assert(!((MediaPage*)pg)->is_locked()); mpool()->fill_page(argv[2]); return TCL_OK; } else if (strcmp(argv[1], "set-pref-style") == 0) { // Set prefetching style // <obj> set-pref-style <style> // // style can be: NOPREF, ONLINE_PREF, OFFLINE_PREF if (strcmp(argv[2], "NOPREF") == 0) pref_style_ = NOPREF; else if (strcmp(argv[2], "ONLINE_PREF") == 0) pref_style_ = ONLINE_PREF; else if (strcmp(argv[2], "OFFLINE_PREF") == 0) pref_style_ = OFFLINE_PREF; else { fprintf(stderr, "Wrong prefetching style %s", argv[2]); return TCL_ERROR; } return TCL_OK; } else if (strcmp(argv[1], "dump-page") == 0) { // Dump segments of a given page ClientPage *p=(ClientPage*)mpool()->get_page(argv[2]); if (p->type() != MEDIA) // Do nothing for non-media pages return TCL_OK; MediaPage *pg = (MediaPage *)p; char *buf; for (int i = 0; i < pg->num_layer(); i++) { buf = pg->print_layer(i); if (strlen(buf) > 0) log("E SEGS p %s l %d %s\n", argv[2], i, buf); delete []buf; } return TCL_OK; } else if (strcmp(argv[1], "stream-received") == 0) { // We've got the entire page, unlock it MediaPage *pg = (MediaPage*)mpool()->get_page(argv[2]); assert(pg != NULL); pg->unlock(); // XXX Should we clear all "last" flag of segments?? #ifdef MCACHE_DEBUG // Printing out current buffer status of the page char *buf; for (int i = 0; i < pg->num_layer(); i++) { buf = pg->print_layer(i); log("E SEGS p %s l %d %s\n", argv[2], i, buf); delete []buf; } #endif // Show cache free size log("E SIZ n %d z %d t %d\n", mpool()->num_pages(), mpool()->usedsize(), mpool()->maxsize()); return TCL_OK; } } else if (argc == 5) { if (strcmp(argv[1], "register-client") == 0) { // <server> register-client <app> <client> <pageid> TclObject *a = TclObject::lookup(argv[2]); assert(a != NULL); int newEntry; Tcl_HashEntry *he = Tcl_CreateHashEntry(cmap_, (const char *)a, &newEntry); if (he == NULL) { tcl.add_errorf("cannot create hash entry"); return TCL_ERROR; } if (!newEntry) { tcl.add_errorf("duplicate connection"); return TCL_ERROR; } RegInfo *p = new RegInfo; p->client_ = (HttpApp*)TclObject::lookup(argv[3]); assert(p->client_ != NULL); strcpy(p->name_, argv[4]); Tcl_SetHashValue(he, (ClientData)p); // Lock the page while transmitting it to a client MediaPage *pg = (MediaPage*)mpool()->get_page(argv[4]); assert((pg != NULL) && (pg->type() == MEDIA)); pg->tlock(); return TCL_OK; } else if (strcmp(argv[1], "unregister-client") == 0) { // <server> unregister-client <app> <client> <pageid> TclObject *a = TclObject::lookup(argv[2]); assert(a != NULL); Tcl_HashEntry *he = Tcl_FindHashEntry(cmap_, (const char*)a); if (he == NULL) { tcl.add_errorf("cannot find hash entry"); return TCL_ERROR; } RegInfo *ri = (RegInfo*)Tcl_GetHashValue(he); // Update hit count mpool()->hc_update(argv[4], ri->hl_); #ifdef MCACHE_DEBUG printf("Cache %d hit counts: \n", id_); mpool()->dump_hclist(); #endif // Dump per-connection statistics for (int i = 0; i <= ri->hl_; i++) log("E STAT p %s l %d d %d e %d p %d\n", ri->name_, i, ri->db_[i], ri->eb_[i], ri->pb_[i]); delete ri; Tcl_DeleteHashEntry(he); // Lock the page while transmitting it to a client MediaPage *pg = (MediaPage*)mpool()->get_page(argv[4]); assert((pg != NULL) && (pg->type() == MEDIA)); pg->tunlock(); return TCL_OK; } } return HttpCache::command(argc, argv); } //---------------------------------------------------------------------- // Media web client // Use C++ interface to records quality of received stream. // NOTE: // It has OTcl inheritance, but no C++ inheritance! //---------------------------------------------------------------------- static class HttpMediaClientClass : public TclClass { public: HttpMediaClientClass() : TclClass("Http/Client/Media") {} TclObject* create(int, const char*const*) { return (new MediaClient()); } } class_httpmediaclient; // Records the quality of stream received void MediaClient::process_data(int size, AppData* data) { assert(data != NULL); switch (data->type()) { case MEDIA_DATA: { HttpMediaData* d = (HttpMediaData*)data; // XXX Don't pass any data to page pool!! if (mpool()->add_segment(d->page(), d->layer(), MediaSegment(*d)) == -1) { fprintf(stderr, "MediaCache %s gets a segment for an unknown page %s\n", name(), d->page()); // abort(); } // Note: we store the page only to produce some statistics // later so that we need not do postprocessing of traces. #if 1 log("C RSEG p %s l %d s %d e %d z %d\n", d->page(), d->layer(), d->st(), d->et(), d->datasize()); #endif break; } default: HttpClient::process_data(size, data); } } int MediaClient::command(int argc, const char*const* argv) { if (argc == 3) { if (strcmp(argv[1], "stream-received") == 0) { // XXX This is the place to do statistics collection // about quality of received stream. // // Dump delivered quality log MediaPage *pg = (MediaPage*)mpool()->get_page(argv[2]); assert(pg != NULL); // Printing out current buffer status of the page char *buf; for (int i = 0; i < pg->num_layer(); i++) { buf = pg->print_layer(i); if (strlen(buf) > 0) log("C SEGS p %s l %d %s\n", argv[2], i, buf); delete []buf; } // then delete the stream from buffer mpool()->force_remove(argv[2]); return TCL_OK; } } return HttpClient::command(argc, argv); } //---------------------------------------------------------------------- // Multimedia web server //---------------------------------------------------------------------- static class MediaServerClass : public TclClass { public: MediaServerClass() : TclClass("Http/Server/Media") {} TclObject* create(int, const char*const*) { return (new MediaServer()); } } class_mediaserver; MediaServer::MediaServer() : HttpServer() { pref_ = new Tcl_HashTable; Tcl_InitHashTable(pref_, 2); cmap_ = new Tcl_HashTable; Tcl_InitHashTable(cmap_, TCL_ONE_WORD_KEYS); } MediaServer::~MediaServer() { Tcl_HashEntry *he; Tcl_HashSearch hs; if (pref_ != NULL) { for (he = Tcl_FirstHashEntry(pref_, &hs); he != NULL; he = Tcl_NextHashEntry(&hs)) { PrefInfo *pi = (PrefInfo*)Tcl_GetHashValue(he); pi->sl_->destroy(); delete pi->sl_; } Tcl_DeleteHashTable(pref_); delete pref_; } if (cmap_ != NULL) { for (he = Tcl_FirstHashEntry(cmap_, &hs); he != NULL; he = Tcl_NextHashEntry(&hs)) delete (RegInfo*)Tcl_GetHashValue(he); Tcl_DeleteHashTable(cmap_); delete cmap_; } } // Return the next segment to be sent to a particular application MediaSegment MediaServer::get_next_segment(MediaRequest *r, Application*& ci) { MediaPage* pg = (MediaPage*)pool_->get_page(r->name()); assert(pg != NULL); // XXX Extremely hacky way to map media app names to // HTTP connections. Should maintain another hash table for this. RegInfo *ri = get_reginfo(r->app()); assert(ri != NULL); PrefInfoQ* q = get_piq(r->name(), ri->client_); // We are not on the prefetching list, send a normal data segment if ((q == NULL) || (q->is_empty())) { MediaSegment s1(r->st(), r->et()); return pg->next_overlap(r->layer(), s1); } // Cycle through the prefetched segments that we need to send int found = 0; int searched = 0; PrefInfo *pi; while (!found) { PrefInfoE *pe = q->dequeue(); pi = pe->data(); q->enqueue(pe); // If there's a pending segment in any layer, send it for (int i = 0; i < pg->num_layer(); i++) if (pi->sl_[i].length() > 0) found = 1; // If no pending prefetched segments, return empty if (searched++ == q->size()) return MediaSegment(0, 0); } // Send a segment from the prefetching list. Only use the data size // included in the request. MediaSegmentList *p = pi->sl_; // Set return conid ci = pi->conid_; // Find one available segment in prefetching list if there is none // in the given layer int l = r->layer(), i = 0; MediaSegment res; while ((res.datasize() == 0) && (i < pg->num_layer())) { // next() doesn't work. Need a method that returns the // *FIRST* non-empty segment which satisfies the size // constraint. res = p[l].get_nextseg(MediaSegment(0, r->datasize())); i++; l = (l+1) % pg->num_layer(); } // XXX We must do boundary check of the prefetched segments to make // sure that the start and end offsets are valid! if (res.start() < 0) res.set_start(0); if (res.end() > pg->layer_size(l)) res.set_end(pg->layer_size(l)); if (res.datasize() > 0) { // XXX We may end up getting data from another layer!! l = (l-1+pg->num_layer()) % pg->num_layer(); if (l != r->layer()) r->set_layer(l); // We may not be able to get the specified data size, due // to arbitrary stream lengths //assert(res.datasize() == r->datasize()); p[r->layer()].evict_head(r->datasize()); } // Set the prefetching flag of this segment res.set_pref(); return res; } // Similar to MediaCache::get_data(), but ignore segment availability checking AppData* MediaServer::get_data(int& size, AppData *req) { assert((req != NULL) && (req->type() == MEDIA_REQUEST)); MediaRequest *r = (MediaRequest *)req; Application* conid = NULL; if (r->request() == MEDIAREQ_GETSEG) { // Get a new data segment MediaSegment s2 = get_next_segment(r, conid); HttpMediaData *p; if (s2.datasize() == 0) { // No more data available for this layer, most likely // it's because this layer is finished. size = 0; p = new HttpMediaData(name(), r->name(), r->layer(), 0, 0); } else { size = s2.datasize(); p = new HttpMediaData(name(), r->name(), r->layer(), s2.start(), s2.end()); } if (s2.is_last()) { p->set_last(); // Tear down the connection after we've sent the last // segment of the base layer and are requested again. if ((s2.datasize() == 0) && (r->layer() == 0)) p->set_finish(); } if (s2.is_pref()) { // Add connection id into returned data p->set_conid(conid); p->set_pref(); } return p; } else if (r->request() == MEDIAREQ_CHECKSEG) // We don't need to return anything, so just NULL return NULL; else { fprintf(stderr, "MediaServer %s gets an unknown MediaRequest type %d\n", name(), r->request()); abort(); } /*NOTREACHED*/ return NULL; // Make msvc happy } int MediaServer::command(int argc, const char*const* argv) { Tcl& tcl = Tcl::instance(); if (argc == 3) { if (strcmp(argv[1], "is-media-page") == 0) { ClientPage *pg = pool_->get_page(argv[2]); if (pg && (pg->type() == MEDIA)) tcl.result("1"); else tcl.result("0"); return TCL_OK; } } else if (argc == 5) { if (strcmp(argv[1], "stop-prefetching") == 0) { /* * <server> stop-prefetching <Client> <conid> <pagenum> */ TclObject *a = TclObject::lookup(argv[2]); assert(a != NULL); int tmp[2]; tmp[0] = (int)a; tmp[1] = atoi(argv[4]); Tcl_HashEntry *he = Tcl_FindHashEntry(pref_, (const char*)tmp); if (he == NULL) { tcl.add_errorf( "Server %d cannot stop prefetching!\n", id_); return TCL_ERROR; } a = TclObject::lookup(argv[3]); assert(a != NULL); PrefInfoQ *q = (PrefInfoQ*)Tcl_GetHashValue(he); PrefInfoE *pe = find_prefinfo(q, (Application*)a); assert(pe != NULL); PrefInfo *pi = pe->data(); MediaSegmentList *p = pi->sl_; assert(p != NULL); for (int i = 0; i < MAX_LAYER; i++) p[i].destroy(); delete []p; delete pi; q->detach(pe); delete pe; // If no more prefetching streams left for this client, // delete all the information. // Return 0 means that we still have prefetching // clients left, don't tear down the channel yet. // Otherwise return 1. int res = 0; if (q->is_empty()) { delete q; Tcl_DeleteHashEntry(he); res = 1; } tcl.resultf("%d", res); return (TCL_OK); } else if (strcmp(argv[1], "register-client") == 0) { // <cache> register-client <app> <client> <pageid> TclObject *a = TclObject::lookup(argv[2]); assert(a != NULL); int newEntry; Tcl_HashEntry *he = Tcl_CreateHashEntry(cmap_, (const char *)a, &newEntry); if (he == NULL) { tcl.add_errorf("cannot create hash entry"); return TCL_ERROR; } if (!newEntry) { tcl.add_errorf("duplicate connection"); return TCL_ERROR; } RegInfo *p = new RegInfo; p->client_ = (HttpApp*)TclObject::lookup(argv[3]); assert(p->client_ != NULL); strcpy(p->name_, argv[4]); Tcl_SetHashValue(he, (ClientData)p); return TCL_OK; } else if (strcmp(argv[1], "unregister-client") == 0) { // <cache> unregister-client <app> <client> <pageid> TclObject *a = TclObject::lookup(argv[2]); assert(a != NULL); Tcl_HashEntry *he = Tcl_FindHashEntry(cmap_, (const char*)a); if (he == NULL) { tcl.add_errorf("cannot find hash entry"); return TCL_ERROR; } RegInfo *p = (RegInfo*)Tcl_GetHashValue(he); delete p; Tcl_DeleteHashEntry(he); return TCL_OK; } } else { if (strcmp(argv[1], "enter-page") == 0) { ClientPage *pg = pool_->enter_page(argc, argv); if (pg == NULL) return TCL_ERROR; if (pg->type() == MEDIA) ((MediaPage*)pg)->create(); // Unlock the page after creation ((MediaPage*)pg)->unlock(); return TCL_OK; } else if (strcmp(argv[1], "register-prefetch") == 0) { /* * <server> register-prefetch <client> <pagenum> * <conid> <layer> {<segments>} * Registers a list of segments to be prefetched by * <client>, where each <segment> is a pair of * (start, end). <pagenum> should be pageid without * preceding [server:] prefix. * * <conid> is the OTcl name of the original client * who requested the page. This is used for the cache * to get statistics about a particular connection. * * <client> is the requestor of the stream. */ TclObject *a = TclObject::lookup(argv[2]); assert(a != NULL); int newEntry = 1; int tmp[2]; tmp[0] = (int)a; tmp[1] = atoi(argv[3]); // Map <cache_ptr><conid> to a pref entry Tcl_HashEntry *he = Tcl_CreateHashEntry(pref_, (const char*)tmp, &newEntry); if (he == NULL) { fprintf(stderr, "Cannot create entry.\n"); return TCL_ERROR; } PrefInfo *pi; PrefInfoE *pe; PrefInfoQ *q; MediaSegmentList *p; a = TclObject::lookup(argv[4]); if (newEntry) { q = new PrefInfoQ; Tcl_SetHashValue(he, (ClientData)q); pe = NULL; } else { q = (PrefInfoQ *)Tcl_GetHashValue(he); pe = find_prefinfo(q, (Application*)a); } if (pe == NULL) { pi = new PrefInfo; pi->conid_ = (Application*)a; p = pi->sl_ = new MediaSegmentList[MAX_LAYER]; q->enqueue(new PrefInfoE(pi)); } else { pi = pe->data(); p = pi->sl_; } assert((pi != NULL) && (p != NULL)); // Preempt all old requests because they // cannot reach the cache "in time" int layer = atoi(argv[5]); p[layer].destroy(); // Add segments into prefetching list assert(argc % 2 == 0); for (int i = 6; i < argc; i+=2) p[layer].add(MediaSegment(atoi(argv[i]), atoi(argv[i+1]))); return TCL_OK; } } return HttpServer::command(argc, argv); }

pagepool.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. // // $Header$ #include <stdlib.h> #include <sys/types.h> #include <fcntl.h> #ifdef WIN32 #include <windows.h> #include <io.h> #else #include <unistd.h> #include <sys/file.h> #endif #include <sys/stat.h> #include <stdio.h> #include <limits.h> #include <ctype.h> extern "C" { #include <otcl.h> } #include "pagepool.h" #include "http.h" // Static/global variables int ClientPage::PUSHALL_ = 0; // Initialized to selective push void
ServerPage::set_mtime(int *mt, int n) { if (mtime_ != NULL) delete []mtime_; mtime_ = new int[n]; memcpy(mtime_, mt, sizeof(int)*n); } ClientPage::ClientPage(const char *n, int s, double mt, double et, double a) : Page(s), age_(a), mtime_(mt), etime_(et), status_(HTTP_VALID_PAGE), counter_(0), mpushTime_(0) { // Parse name to get server and page id char *buf = new char[strlen(n) + 1]; strcpy(buf, n); char *tmp = strtok(buf, ":"); server_ = (HttpApp*)TclObject::lookup(tmp); if (server_ == NULL) { fprintf(stderr, "Non-exitent server name for page %s", n); abort(); } tmp = strtok(NULL, ":"); id_ = atol(tmp); delete []buf; } void ClientPage::print_name(char* name, PageID& id) { sprintf(name, "%s:%-d", id.s_->name(), id.id_); } void ClientPage::split_name(const char* name, PageID& id) { char *buf = new char[strlen(name)+1]; strcpy(buf, name); char *tmp = strtok(buf, ":"); id.s_ = (HttpApp*)TclObject::lookup(tmp); if (id.s_ == NULL) { fprintf(stderr, "Non-exitent server name for page %s\n", name); abort(); } tmp = strtok(NULL, ":"); id.id_ = atol(tmp); delete []buf; } void ClientPage::print_info(char *buf) { sprintf(buf, "size %d modtime %.17g time %.17g age %.17g", size(), mtime(), etime(), age()); if (is_uncacheable()) strcat(buf, " noc 1"); } void ClientPage::name(char* buf) { sprintf(buf, "%s:%d", server_->name(), id()); } static class PagePoolClass : public TclClass { public: PagePoolClass() : TclClass("PagePool") {} TclObject* create(int, const char*const*) { return (new PagePool()); } } class_pagepool_agent; int PagePool::command(int argc, const char*const* argv) { if (argc == 2) { // XXX Should be static class variables... if (strcmp(argv[1], "set-allpush") == 0) { ClientPage::PUSHALL_ = 1; return (TCL_OK); } if (strcmp(argv[1], "set-selpush") == 0) { ClientPage::PUSHALL_ = 0; return (TCL_OK); } } return TclObject::command(argc, argv); } // TracePagePool // Used for Worrell's filtered server traces only. For handling general // web server traces and proxy traces, have a look at ProxyTracePagePool below. // // Load a trace statistics file, and randomly generate requests and // page lifetimes from the trace. // // Trace statistics file format: // <URL> <size> {<modification time>} static class TracePagePoolClass : public TclClass { public: TracePagePoolClass() : TclClass("PagePool/Trace") {} TclObject* create(int argc, const char*const* argv) { if (argc >= 5) return (new TracePagePool(argv[4])); return 0; } } class_tracepagepool_agent; TracePagePool::TracePagePool(const char *fn) : PagePool(), ranvar_(0) { FILE *fp = fopen(fn, "r"); if (fp == NULL) { fprintf(stderr, "TracePagePool: couldn't open trace file %s\n", fn); abort(); // What else can we do? } namemap_ = new Tcl_HashTable; Tcl_InitHashTable(namemap_, TCL_STRING_KEYS); idmap_ = new Tcl_HashTable; Tcl_InitHashTable(idmap_, TCL_ONE_WORD_KEYS); while (load_page(fp)); change_time(); } TracePagePool::~TracePagePool() { if (namemap_ != NULL) { Tcl_DeleteHashTable(namemap_); delete namemap_; } if (idmap_ != NULL) { Tcl_DeleteHashTable(idmap_); delete idmap_; } } void TracePagePool::change_time() { Tcl_HashEntry *he; Tcl_HashSearch hs; ServerPage *pg; int i, j; for (i = 0, he = Tcl_FirstHashEntry(idmap_, &hs); he != NULL; he = Tcl_NextHashEntry(&hs), i++) { pg = (ServerPage *) Tcl_GetHashValue(he); for (j = 0; j < pg->num_mtime(); j++) pg->mtime(j) -= (int)start_time_; } end_time_ -= start_time_; start_time_ = 0; duration_ = (int)end_time_; } ServerPage* TracePagePool::load_page(FILE *fp) { static char buf[TRACEPAGEPOOL_MAXBUF]; char *delim = " \t\n"; char *tmp1, *tmp2; ServerPage *pg; // XXX Use internal variables of struct Page if (!fgets(buf, TRACEPAGEPOOL_MAXBUF, fp)) return NULL; // URL tmp1 = strtok(buf, delim); // Size tmp2 = strtok(NULL, delim); pg = new ServerPage(atoi(tmp2), num_pages_++); if (add_page(tmp1, pg)) { delete pg; return NULL; } // Modtimes, assuming they are in ascending time order int num = 0; int *nmd = new int[5]; while ((tmp1 = strtok(NULL, delim)) != NULL) { if (num >= 5) { int *tt = new int[num+5]; memcpy(tt, nmd, sizeof(int)*num); delete []nmd; nmd = tt; } nmd[num] = atoi(tmp1); if (nmd[num] < start_time_) start_time_ = nmd[num]; if (nmd[num] > end_time_) end_time_ = nmd[num]; num++; } pg->num_mtime() = num; pg->set_mtime(nmd, num); delete []nmd; return pg; } int TracePagePool::add_page(const char* name, ServerPage *pg) { int newEntry = 1; Tcl_HashEntry *he = Tcl_CreateHashEntry(namemap_, (const char *)name, &newEntry); if (he == NULL) return -1; if (newEntry) Tcl_SetHashValue(he, (ClientData)pg); else fprintf(stderr, "TracePagePool: Duplicate entry %s\n", name); Tcl_HashEntry *hf = Tcl_CreateHashEntry(idmap_, (const char *)pg->id(), &newEntry); if (hf == NULL) { Tcl_DeleteHashEntry(he); return -1; } if (newEntry) Tcl_SetHashValue(hf, (ClientData)pg); else fprintf(stderr, "TracePagePool: Duplicate entry %d\n", pg->id()); return 0; } ServerPage* TracePagePool::get_page(int id) { if ((id < 0) || (id >= num_pages_)) return NULL; Tcl_HashEntry *he = Tcl_FindHashEntry(idmap_, (const char *)id); if (he == NULL) return NULL; return (ServerPage *)Tcl_GetHashValue(he); } int TracePagePool::command(int argc, const char *const* argv) { Tcl &tcl = Tcl::instance(); if (argc == 2) { if (strcmp(argv[1], "get-poolsize") == 0) { /* * <pgpool> get-poolsize * Get the number of pages currently in pool */ tcl.resultf("%d", num_pages_); return TCL_OK; } else if (strcmp(argv[1], "get-start-time") == 0) { tcl.resultf("%.17g", start_time_); return TCL_OK; } else if (strcmp(argv[1], "get-duration") == 0) { tcl.resultf("%d", duration_); return TCL_OK; } } else if (argc == 3) { if (strcmp(argv[1], "gen-pageid") == 0) { /* * <pgpool> gen-pageid <client_id> * Randomly generate a page id from page pool */ double tmp = ranvar_ ? ranvar_->value() : Random::uniform(); // tmp should be in [0, num_pages_-1] tmp = (tmp < 0) ? 0 : (tmp >= num_pages_) ? (num_pages_-1):tmp; if ((int)tmp >= num_pages_) abort(); tcl.resultf("%d", (int)tmp); return TCL_OK; } else if (strcmp(argv[1], "gen-size") == 0) { /* * <pgpool> gen-size <pageid> */ int id = atoi(argv[2]); ServerPage *pg = get_page(id); if (pg == NULL) { tcl.add_errorf("TracePagePool %s: page %d doesn't exists.\n", name_, id); return TCL_ERROR; } tcl.resultf("%d", pg->size()); return TCL_OK; } else if (strcmp(argv[1], "ranvar") == 0) { /* * <pgpool> ranvar <ranvar> * Set a random var which is used to randomly pick * a page from the page pool. */ ranvar_ = (RandomVariable *)TclObject::lookup(argv[2]); return TCL_OK; } else if (strcmp(argv[1], "set-start-time") == 0) { double st = strtod(argv[2], NULL); start_time_ = st; end_time_ += st; } else if (strcmp(argv[1], "gen-init-modtime") == 0) { tcl.resultf("%.17g", Scheduler::instance().clock()); return TCL_OK; } } else { if (strcmp(argv[1], "gen-modtime") == 0) { /* * <pgpool> get-modtime <pageid> <modtime> * * Return next modtime that is larger than modtime * To retrieve the first modtime (creation time), set * <modtime> to -1 in the request. */ int id = atoi(argv[2]); double mt = strtod(argv[3], NULL); ServerPage *pg = get_page(id); if (pg == NULL) { tcl.add_errorf("TracePagePool %s: page %d doesn't exists.\n", name_, id); return TCL_ERROR; } for (int i = 0; i < pg->num_mtime(); i++) if (pg->mtime(i) > mt) { tcl.resultf("%.17g", pg->mtime(i)+start_time_); return TCL_OK; } // When get to the last modtime, return -1 tcl.resultf("%d", INT_MAX); return TCL_OK; } } return PagePool::command(argc, argv); } static class MathPagePoolClass : public TclClass { public: MathPagePoolClass() : TclClass("PagePool/Math") {} TclObject* create(int, const char*const*) { return (new MathPagePool()); } } class_mathpagepool_agent; // Use 3 ranvars to generate requests, mod times and page size int MathPagePool::command(int argc, const char *const* argv) { Tcl& tcl = Tcl::instance(); // Keep the same tcl interface as PagePool/Trace if (argc == 2) { if (strcmp(argv[1], "get-poolsize") == 0) { tcl.result("1"); return TCL_OK; } else if (strcmp(argv[1], "get-start-time") == 0) { tcl.resultf("%.17g", start_time_); return TCL_OK; } else if (strcmp(argv[1], "get-duration") == 0) { tcl.resultf("%d", duration_); return TCL_OK; } } else if (argc == 3) { if (strcmp(argv[1], "gen-pageid") == 0) { // Single page tcl.result("0"); return TCL_OK; } else if (strcmp(argv[1], "gen-size") == 0) { if (rvSize_ == 0) { tcl.add_errorf("%s: no page size generator", name_); return TCL_ERROR; } int size = (int) rvSize_->value(); if (size == 0) // XXX do not allow page size 0, because TcpApp // doesn't behave correctly when sending 0 byte size = 1; tcl.resultf("%d", size); return TCL_OK; } else if (strcmp(argv[1], "ranvar-size") == 0) { rvSize_ = (RandomVariable*)TclObject::lookup(argv[2]); return TCL_OK; } else if (strcmp(argv[1], "ranvar-age") == 0) { rvAge_ = (RandomVariable*)TclObject::lookup(argv[2]); return TCL_OK; } else if (strcmp(argv[1], "set-start-time") == 0) { double st = strtod(argv[2], NULL); start_time_ = st; end_time_ += st; return TCL_OK; } else if (strcmp(argv[1], "gen-init-modtime") == 0) { tcl.resultf("%.17g", Scheduler::instance().clock()); return TCL_OK; } } else { if (strcmp(argv[1], "gen-modtime") == 0) { if (rvAge_ == 0) { tcl.add_errorf("%s: no page age generator", name_); return TCL_ERROR; } double mt = strtod(argv[3], NULL); tcl.resultf("%.17g", mt + rvAge_->value()); return TCL_OK; } } return PagePool::command(argc, argv); } // Assume one main page, which changes often, and multiple component pages static class CompMathPagePoolClass : public TclClass { public: CompMathPagePoolClass() : TclClass("PagePool/CompMath") {} TclObject* create(int, const char*const*) { return (new CompMathPagePool()); } } class_compmathpagepool_agent; CompMathPagePool::CompMathPagePool() { bind("num_pages_", &num_pages_); bind("main_size_", &main_size_); bind("comp_size_", &comp_size_); } int CompMathPagePool::command(int argc, const char *const* argv) { Tcl& tcl = Tcl::instance(); // Keep the same tcl interface as PagePool/Trace if (argc == 2) { if (strcmp(argv[1], "get-poolsize") == 0) { tcl.result("1"); return TCL_OK; } else if (strcmp(argv[1], "get-start-time") == 0) { tcl.resultf("%.17g", start_time_); return TCL_OK; } else if (strcmp(argv[1], "get-duration") == 0) { tcl.resultf("%d", duration_); return TCL_OK; } } else if (argc == 3) { if (strcmp(argv[1], "gen-pageid") == 0) { // Main pageid, never return id of component pages tcl.result("0"); return TCL_OK; } else if (strcmp(argv[1], "gen-size") == 0) { int id = atoi(argv[2]); if (id == 0) tcl.resultf("%d", main_size_); else tcl.resultf("%d", comp_size_); return TCL_OK; } else if (strcmp(argv[1], "gen-obj-size") == 0) { tcl.resultf("%d", comp_size_); return (TCL_OK); } else if (strcmp(argv[1], "get-next-objs") == 0) { PageID id; ClientPage::split_name(argv[2], id); // If we want simultaneous requests of multiple // objects, return a list; otherwise return a single // pageid. for (int i = id.id_+1; i < num_pages_; i++) { tcl.resultf("%s %s:%d", tcl.result(), id.s_->name(), i); } return TCL_OK; } else if (strcmp(argv[1], "ranvar-main-age") == 0) { rvMainAge_ = (RandomVariable*)TclObject::lookup(argv[2]); return TCL_OK; } else if (strcmp(argv[1], "ranvar-obj-age") == 0) { rvCompAge_ = (RandomVariable*)TclObject::lookup(argv[2]); return TCL_OK; } else if (strcmp(argv[1], "set-start-time") == 0) { double st = strtod(argv[2], NULL); start_time_ = st; end_time_ += st; return TCL_OK; } else if (strcmp(argv[1], "gen-init-modtime") == 0) { tcl.resultf("%.17g", Scheduler::instance().clock()); return TCL_OK; } else if (strcmp(argv[1], "is-mainpage") == 0) { // Tell if the given page is a main page or an // embedded object. // XXX Here because we have only one page, so only // page id 0 is the main page. If we have multiple // pages, we need something else to do this. PageID t1; ClientPage::split_name(argv[2], t1); if (t1.id_ == 0) tcl.result("1"); else tcl.result("0"); return TCL_OK; } else if (strcmp(argv[1], "get-mainpage") == 0) { // Get the main page of an embedded object // XXX Should maintain a mapping between embedded // objects and main pages. It can be an algorithmic // one, e.g., using page id intervals. It's simple // here because we have only one page. PageID t1; ClientPage::split_name(argv[2], t1); tcl.resultf("%s:0", t1.s_->name()); return TCL_OK; } else if (strcmp(argv[1], "get-obj-num") == 0) { // Returns the number of embedded objects of the page // given in argv[1]. Here because we have only one // page, we return a fixed value. tcl.resultf("%d", num_pages_-1); return TCL_OK; } } else { // argc > 3 if (strcmp(argv[1], "gen-modtime") == 0) { int id = atoi(argv[2]); if (id == 0) { if (rvMainAge_ == 0) { tcl.add_errorf("%s: no page age generator", name_); return TCL_ERROR; } double mt = strtod(argv[3], NULL); tcl.resultf("%.17g", mt + rvMainAge_->value()); } else { if (rvCompAge_ == 0) { tcl.add_errorf("%s: no page age generator", name_); return TCL_ERROR; } double mt = atoi(argv[3]); tcl.resultf("%.17g", mt + rvCompAge_->value()); } return TCL_OK; } else if (strcmp(argv[1], "gen-obj-modtime") == 0) { if (rvCompAge_ == 0) { tcl.add_errorf("%s: no page age generator", name_); return TCL_ERROR; } double mt = atoi(argv[3]); tcl.resultf("%.17g", mt + rvCompAge_->value()); return TCL_OK; } } return PagePool::command(argc, argv); } static class ClientPagePoolClass : public TclClass { public: ClientPagePoolClass() : TclClass("PagePool/Client") {} TclObject* create(int, const char*const*) { return (new ClientPagePool()); } } class_clientpagepool_agent; ClientPagePool::ClientPagePool() { namemap_ = new Tcl_HashTable; Tcl_InitHashTable(namemap_, 2); } ClientPagePool::~ClientPagePool() { if (namemap_ != NULL) { Tcl_DeleteHashTable(namemap_); delete namemap_; } } // In case client/cache/server needs details, e.g., page listing int ClientPagePool::command(int argc, const char*const* argv) { Tcl& tcl = Tcl::instance(); if (argc == 2) { if (strcmp(argv[1], "list-pages") == 0) { Tcl_HashEntry *he; Tcl_HashSearch hs; char *buf = new char[num_pages_*20]; char *p = buf; for (he = Tcl_FirstHashEntry(namemap_, &hs); he != NULL; he = Tcl_NextHashEntry(&hs)) { int* t2 = (int*)Tcl_GetHashKey(namemap_, he); PageID t1(t2); #ifdef NEED_SUNOS_PROTOS sprintf(p, "%s:%-d ", t1.s_->name(),t1.id_); p += strlen(p); #else p += sprintf(p,"%s:%-d ",t1.s_->name(),t1.id_); #endif } tcl.resultf("%s", buf); delete []buf; return TCL_OK; } } return PagePool::command(argc, argv); } ClientPage* ClientPagePool::get_page(const char *name) { PageID t1; void* t2[2]; ClientPage::split_name(name, t1); t2[0] = (void *)t1.s_; t2[1] = (void *)t1.id_; Tcl_HashEntry *he = Tcl_FindHashEntry(namemap_, (const char *)t2); if (he == NULL) return NULL; return (ClientPage *)Tcl_GetHashValue(he); } int ClientPagePool::get_pageinfo(const char *name, char *buf) { ClientPage *pg = get_page(name); if (pg == NULL) return -1; pg->print_info(buf); return 0; } ClientPage* ClientPagePool::enter_page(int argc, const char*const* argv) { double mt = -1, et, age = -1, noc = 0; int size = -1; for (int i = 3; i < argc; i+=2) { if (strcmp(argv[i], "modtime") == 0) mt = strtod(argv[i+1], NULL); else if (strcmp(argv[i], "size") == 0) size = atoi(argv[i+1]); else if (strcmp(argv[i], "age") == 0) age = strtod(argv[i+1], NULL); else if (strcmp(argv[i], "noc") == 0) // non-cacheable flag noc = 1; } // XXX allow mod time < 0 and age < 0!! if (size < 0) { fprintf(stderr, "PagePool %s: wrong information for page %s\n", name_, argv[2]); return NULL; } et = Scheduler::instance().clock(); ClientPage* pg = new ClientPage(argv[2], size, mt, et, age); if (add_page(pg) < 0) { delete pg; return NULL; } if (noc) pg->set_uncacheable(); return pg; } ClientPage* ClientPagePool::enter_page(const char *name, int size, double mt, double et, double age) { ClientPage* pg = new ClientPage(name, size, mt, et, age); if (add_page(pg) < 0) { delete pg; return NULL; } return pg; } // XXX We don't need parsing "noc" here because a non-cacheable // page won't be processed by a cache. ClientPage* ClientPagePool::enter_metadata(int argc, const char*const* argv) { ClientPage *pg = enter_page(argc, argv); if (pg != NULL) pg->set_valid_hdr(); return pg; } ClientPage* ClientPagePool::enter_metadata(const char *name, int size, double mt, double et, double age) { ClientPage *pg = enter_page(name, size, mt, et, age); if (pg != NULL) pg->set_valid_hdr(); return pg; } int ClientPagePool::add_page(ClientPage* pg) { if (pg == NULL) return -1; char buf[HTTP_MAXURLLEN]; pg->name(buf); PageID t1; void* t2[2]; ClientPage::split_name(buf, t1); t2[0] = (void *)t1.s_; t2[1] = (void *)t1.id_; int newEntry = 1; Tcl_HashEntry *he = Tcl_CreateHashEntry(namemap_, (const char *)t2, &newEntry); if (he == NULL) return -1; // XXX If cache replacement algorithm is added, should change // cache size here!! if (newEntry) { Tcl_SetHashValue(he, (ClientData)pg); num_pages_++; } else { // Replace the old one ClientPage *q = (ClientPage *)Tcl_GetHashValue(he); // XXX must copy the counter value pg->counter() = q->counter(); // XXX must copy the mpush values if (q->is_mpush()) pg->set_mpush(q->mpush_time()); Tcl_SetHashValue(he, (ClientData)pg); delete q; } return 0; } int ClientPagePool::remove_page(const char *name) { PageID t1; void* t2[2]; ClientPage::split_name(name, t1); t2[0] = (void *)t1.s_; t2[1] = (void *)t1.id_; // Find out which client we are seeking Tcl_HashEntry *he = Tcl_FindHashEntry(namemap_, (const char *)t2); if (he == NULL) return -1; ClientPage *pg = (ClientPage *)Tcl_GetHashValue(he); Tcl_DeleteHashEntry(he); delete pg; num_pages_--; // XXX If cache replacement algorithm is added, should change // cache size here!! return 0; } int ClientPagePool::set_mtime(const char *name, double mt) { ClientPage *pg = (ClientPage *)get_page(name); if (pg == NULL) return -1; pg->mtime() = mt; return 0; } int ClientPagePool::get_mtime(const char *name, double& mt) { ClientPage *pg = (ClientPage *)get_page(name); if (pg == NULL) return -1; mt = pg->mtime(); return 0; } int ClientPagePool::set_etime(const char *name, double et) { ClientPage *pg = (ClientPage *)get_page(name); if (pg == NULL) return -1; pg->etime() = et; return 0; } int ClientPagePool::get_etime(const char *name, double& et) { ClientPage *pg = (ClientPage *)get_page(name); if (pg == NULL) return -1; et = pg->etime(); return 0; } int ClientPagePool::get_size(const char *name, int& size) { ClientPage *pg = (ClientPage *)get_page(name); if (pg == NULL) return -1; size = pg->size(); return 0; } int ClientPagePool::get_age(const char *name, double& age) { ClientPage *pg = (ClientPage *)get_page(name); if (pg == NULL) return -1; age = pg->age(); return 0; } void ClientPagePool::invalidate_server(int sid) { Tcl_HashEntry *he; Tcl_HashSearch hs; ClientPage *pg; int i; for (i = 0, he = Tcl_FirstHashEntry(namemap_, &hs); he != NULL; he = Tcl_NextHashEntry(&hs), i++) { pg = (ClientPage *) Tcl_GetHashValue(he); if (pg->server()->id() == sid) pg->server_down(); } } // Proxy traces. Request file format: // // [<time> <clientID> <serverID> <URL_ID>] // i <Duration> <Number_of_unique_URLs> // // <time> is guaranteed to start from 0. It needs to be adjusted // // Page file format (sorted by access counts) // // <serverID> <URL_ID> <PageSize> <AccessCount> static class ProxyTracePagePoolClass : public TclClass { public: ProxyTracePagePoolClass() : TclClass("PagePool/ProxyTrace") {} TclObject* create(int, const char*const*) { return (new ProxyTracePagePool()); } } class_ProxyTracepagepool_agent; ProxyTracePagePool::ProxyTracePagePool() : rvDyn_(NULL), rvStatic_(NULL), br_(0), size_(NULL), reqfile_(NULL), req_(NULL), lastseq_(0) { } ProxyTracePagePool::~ProxyTracePagePool() { if (size_ != NULL) delete []size_; if (reqfile_ != NULL) fclose(reqfile_); if (req_ != NULL) { Tcl_DeleteHashTable(req_); delete req_; } } int ProxyTracePagePool::init_req(const char *fn) { reqfile_ = fopen(fn, "r"); if (reqfile_ == NULL) { fprintf(stderr, "ProxyTracePagePool: couldn't open trace file %s\n", fn); return TCL_ERROR; } // Discover information about the trace, e.g., number of pages, // start time, end time, etc. They should be available at the // first line of the trace file. return find_info(); } int ProxyTracePagePool::find_info() { // Read the last line of the file fseek(reqfile_, -128, SEEK_END); char buf[129]; if (fread(buf, 1, 128, reqfile_) != 128) { fprintf(stderr, "ProxyTracePagePool: cannot read file information\n"); return TCL_ERROR; } int i; // ignore the last RETURN buf[128] = 0; if (buf[127] == '\n') buf[127] = 0; for (i = 127; i >= 0; i--) if (buf[i] == '\n') { i++; break; } if (buf[i] != 'i') { fprintf(stderr, "ProxyTracePagePool: trace file doesn't contain statistics.\n"); abort(); } double len; sscanf(buf+i+1, "%lf %u", &len, &num_pages_); duration_ = (int)ceil(len); #if 0 printf("ProxyTracePagePool: duration %d pages %u\n", duration_, num_pages_); #endif rewind(reqfile_); return TCL_OK; } // Load page size info. Assuming request stream has already been loaded int ProxyTracePagePool::init_page(const char *fn) { FILE *fp = fopen(fn, "r"); if (fp == NULL) { fprintf(stderr, "ProxyTracePagePool: couldn't open trace file %s\n", fn); return TCL_ERROR; } if (size_ != NULL) delete []size_; int* p = new int[num_pages_]; size_ = p; for (int i = 0; i < num_pages_; i++, p++) fscanf(fp, "%*d %*d %d %*u\n", p); fclose(fp); return TCL_OK; } ProxyTracePagePool::ClientRequest* ProxyTracePagePool::load_req(int cid) { // Find out which client we are seeking Tcl_HashEntry *he; ClientRequest *p; int dummy; if ((he = Tcl_FindHashEntry(req_, (const char*)cid)) == NULL) { // New entry p = new ClientRequest(); p->seq_ = lastseq_++; he = Tcl_CreateHashEntry(req_, (const char*)cid, &dummy); Tcl_SetHashValue(he, (const char*)p); // Search from the beginning of file for this new client fseek(reqfile_, 0, SEEK_SET); } else { p = (ClientRequest*)Tcl_GetHashValue(he); if (p->nrt_ == -1) // No more requests for this client return p; // Clear EOF status fseek(reqfile_, p->fpos_, SEEK_SET); } // Looking for the next available request for this client double nrt; int ncid = -1, nurl; char buf[256]; while (fgets(buf, 256, reqfile_)) { if (isalpha(buf[0])) { // Last line, break; ncid = -1; break; } sscanf(buf, "%lf %d %*d %d\n", &nrt, &ncid, &nurl); if ((ncid % nclient_) == p->seq_) break; } if ((ncid % nclient_) != p->seq_) // Didn't find the next request for this client p->nrt_ = -1; else { p->nrt_ = nrt, p->nurl_ = nurl; p->nrt_ += start_time_; } p->fpos_ = ftell(reqfile_); return p; } // Provide a tcl interface compatible with MathPagePool int ProxyTracePagePool::command(int argc, const char*const* argv) { Tcl& tcl = Tcl::instance(); if (argc == 2) { if (strcmp(argv[1], "get-poolsize") == 0) { tcl.resultf("%u", num_pages_); return TCL_OK; } else if (strcmp(argv[1], "get-start-time") == 0) { tcl.resultf("%.17g", start_time_); return TCL_OK; } else if (strcmp(argv[1], "get-duration") == 0) { tcl.resultf("%d", duration_); return TCL_OK; } else if (strcmp(argv[1], "bimodal-ratio") == 0) { tcl.resultf("%g", br_ / 10); return TCL_OK; } } else if (argc == 3) { if (strcmp(argv[1], "set-client-num") == 0) { // Set the number of clients it'll access // Cannot be changed once set if (req_ != NULL) return TCL_ERROR; int num = atoi(argv[2]); req_ = new Tcl_HashTable; Tcl_InitHashTable(req_, TCL_ONE_WORD_KEYS); nclient_ = num; return TCL_OK; } else if (strcmp(argv[1], "gen-request") == 0) { // Use client id to get a corresponding request int id = atoi(argv[2]); ClientRequest *p = load_req(id); if ((p->nrt_ >= 0) && (p->nrt_ < Scheduler::instance().clock())) { // XXX Do NOT treat this as an error, also // do NOT disable further requests from this // client. fprintf(stderr, "%.17g: Wrong request time %g.\n", Scheduler::instance().clock(), p->nrt_); // XXX If it's a little bit older than current // time, let it be a little bit later than now p->nrt_ = Scheduler::instance().clock()+0.001; } tcl.resultf("%lf %d", p->nrt_ - Scheduler::instance().clock(), p->nurl_); return TCL_OK; } else if (strcmp(argv[1], "gen-size") == 0) { int id = atoi(argv[2]); if ((id < 0) || (id > num_pages_)) { tcl.result("PagePool: id out of range.\n"); return TCL_ERROR; } tcl.resultf("%d", size_[id]); return TCL_OK; } else if (strcmp(argv[1], "set-start-time") == 0) { start_time_ = strtod(argv[2], NULL); return TCL_OK; } else if (strcmp(argv[1], "bimodal-ratio") == 0) { // XXX Codes in Http/Server::gen-page{} also depends // on this dyn/static page algorithm. If this is // changed, that instproc must be changed too. // // percentage of dynamic pages. E.g., // if this ratio is 5, then page 0-4 is // dynamic, and page 4-99 is static, and so on. double ratio = strtod(argv[2], NULL); //br_ = (int)ceil(ratio*100); br_ = (int)ceil(ratio*10); return TCL_OK; } else if (strcmp(argv[1], "ranvar-dp") == 0) { // Page mod ranvar for dynamic pages rvDyn_ = (RandomVariable*)TclObject::lookup(argv[2]); return TCL_OK; } else if (strcmp(argv[1], "ranvar-sp") == 0) { // page mod ranvar for static pages rvStatic_= (RandomVariable*)TclObject::lookup(argv[2]); return TCL_OK; } else if (strcmp(argv[1], "set-reqfile") == 0) { return init_req(argv[2]); } else if (strcmp(argv[1], "set-pagefile") == 0) { return init_page(argv[2]); } else if (strcmp(argv[1], "gen-init-modtime") == 0) { int id = atoi(argv[2]) % 10; if (id >= br_) // Static page tcl.result("0"); else // Dynamic page tcl.resultf("%.17g", Scheduler::instance().clock()); return TCL_OK; } } else { if (strcmp(argv[1], "gen-modtime") == 0) { if ((rvDyn_ == 0) || (rvStatic_ == 0)) { tcl.add_errorf("%s: no page age generator", name_); return TCL_ERROR; } // int id = atoi(argv[2]) % 100; int id = atoi(argv[2]) % 10; double mt = strtod(argv[3], NULL); if (id >= br_) tcl.resultf("%.17g", mt + rvStatic_->value()); else tcl.resultf("%.17g", mt + rvDyn_->value()); return TCL_OK; } } return PagePool::command(argc, argv); } // Proxy trace with special method for page modification static class EPAPagePoolClass : public TclClass { public: EPAPagePoolClass() : TclClass("PagePool/ProxyTrace/epa") {} TclObject* create(int, const char*const*) { return (new EPATracePagePool()); } } class_epapagepool_agent; int EPATracePagePool::command(int argc, const char*const* argv) { Tcl& tcl = Tcl::instance(); if (argc == 2) { if (strcmp(argv[1], "pick-pagemod") == 0) { if (rvDyn_ == 0) { tcl.add_errorf("%s: no page age generator", name_); return (TCL_ERROR); } int j = (int)floor(rvDyn_->value()); //fprintf(stderr, "mod id = %d\n", j/br_*10 + j % br_); tcl.resultf("%d", j/br_*10 + j % br_); return TCL_OK; } } else if (argc == 3) { if (strcmp(argv[1], "ranvar-dp") == 0) { rvDyn_ = (RandomVariable*)TclObject::lookup(argv[2]); if (rvDyn_ == 0) { tcl.add_errorf("%s: no page age generator", name_); return (TCL_ERROR); } ((UniformRandomVariable*)rvDyn_)->setmin(0); ((UniformRandomVariable*)rvDyn_)->setmax(num_pages_/10*br_ + num_pages_%br_ - 1); return TCL_OK; } } else { if (strcmp(argv[1], "gen-modtime") == 0) { // Return a very large number tcl.resultf("%d", INT_MAX); return TCL_OK; } } return ProxyTracePagePool::command(argc, argv); }

tcp-simple.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. // // SimpleTcp: Only share the same interface as FullTcp. // It's inherited from FullTcp solely for interface reason... :( // // If we have interface declaration independent from class type definition, // we'll be better off. // // $Header$ #include <stdlib.h> #include "tclcl.h" #include "packet.h" #include "ip.h" #include "app.h" #include "tcp-simple.h" static class SimpleTcpClass : public TclClass { public: SimpleTcpClass() : TclClass("Agent/TCP/SimpleTcp") {} TclObject* create(int, const char*const*) { return (new SimpleTcpAgent()); } } class_simple_tcp_agent; SimpleTcpAgent::SimpleTcpAgent() : TcpAgent(), seqno_(0) { } // XXX Do *NOT* support infinite send of TCP (bytes == -1). void
SimpleTcpAgent::sendmsg(int bytes, const char* /*flags*/) { if (bytes == -1) { fprintf(stderr, "SimpleTcp doesn't support infinite send. Do not use FTP::start(), etc.\n"); return; } // Simply sending out bytes out to target_ curseq_ += bytes; seqno_ ++; Packet *p = allocpkt(); hdr_tcp *tcph = (hdr_tcp*)p->access(off_tcp_); tcph->seqno() = seqno_; tcph->ts() = Scheduler::instance().clock(); tcph->ts_echo() = ts_peer_; hdr_cmn *th = (hdr_cmn*)p->access(off_cmn_); th->size() = bytes + tcpip_base_hdr_size_; send(p, 0); } void SimpleTcpAgent::recv(Packet *pkt, Handler *) { hdr_cmn *th = (hdr_cmn*)pkt->access(off_cmn_); int datalen = th->size() - tcpip_base_hdr_size_; if (app_) app_->recv(datalen); // No lastbyte_ callback, because no packet fragmentation. Packet::free(pkt); } int SimpleTcpAgent::command(int argc, const char*const* argv) { // Copy FullTcp's tcl interface if (argc == 2) { if (strcmp(argv[1], "listen") == 0) { // Do nothing return (TCL_OK); } if (strcmp(argv[1], "close") == 0) { // Call done{} to match tcp-full's syntax Tcl::instance().evalf("%s done", name()); return (TCL_OK); } } return (TcpAgent::command(argc, argv)); }

tcpapp.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. // // $Header$ // // Tcp application: transmitting real application data // // Allows only one connection. Model underlying TCP connection as a // FIFO byte stream, use this to deliver user data #include "agent.h" #include "app.h" #include "tcpapp.h" // Buffer management stuff.
CBuf::CBuf(AppData *c, int nbytes) { nbytes_ = nbytes; size_ = c->size(); if (size_ > 0) data_ = c; else data_ = NULL; next_ = NULL; } CBufList::~CBufList() { while (head_ != NULL) { tail_ = head_; head_ = head_->next_; delete tail_; } } void CBufList::insert(CBuf *cbuf) { if (tail_ == NULL) head_ = tail_ = cbuf; else { tail_->next_ = cbuf; tail_ = cbuf; } #ifdef TCPAPP_DEBUG num_++; #endif } CBuf* CBufList::detach() { if (head_ == NULL) return NULL; CBuf *p = head_; if ((head_ = head_->next_) == NULL) tail_ = NULL; #ifdef TCPAPP_DEBUG num_--; #endif return p; } // ADU for plain TcpApp, which is by default a string of otcl script // XXX Local to this file class TcpAppString : public AppData { private: int size_; char* str_; public: TcpAppString() : AppData(TCPAPP_STRING), size_(0), str_(NULL) {} TcpAppString(TcpAppString& d) : AppData(d) { size_ = d.size_; if (size_ > 0) { str_ = new char[size_]; strcpy(str_, d.str_); } else str_ = NULL; } virtual ~TcpAppString() { if (str_ != NULL) delete []str_; } char* str() { return str_; } virtual int size() const { return AppData::size() + size_; } // Insert string-contents into the ADU void set_string(const char* s) { if ((s == NULL) || (*s == 0)) str_ = NULL, size_ = 0; else { size_ = strlen(s) + 1; str_ = new char[size_]; assert(str_ != NULL); strcpy(str_, s); } } virtual AppData* copy() { return new TcpAppString(*this); } }; // TcpApp static class TcpCncClass : public TclClass { public: TcpCncClass() : TclClass("Application/TcpApp") {} TclObject* create(int argc, const char*const* argv) { if (argc != 5) return NULL; Agent *tcp = (Agent *)TclObject::lookup(argv[4]); if (tcp == NULL) return NULL; return (new TcpApp(tcp)); } } class_tcpcnc_app; TcpApp::TcpApp(Agent *tcp) : Application(), curdata_(0), curbytes_(0) { agent_ = tcp; agent_->attachApp(this); } TcpApp::~TcpApp() { // XXX Before we quit, let our agent know what we no longer exist // so that it won't give us a call later... agent_->attachApp(NULL); } // Send with callbacks to transfer application data void TcpApp::send(int nbytes, AppData *cbk) { CBuf *p = new CBuf(cbk, nbytes); #ifdef TCPAPP_DEBUG p->time() = Scheduler::instance().clock(); #endif cbuf_.insert(p); Application::send(nbytes); } // All we need to know is that our sink has received one message void TcpApp::recv(int size) { // If it's the start of a new transmission, grab info from dest, // and execute callback if (curdata_ == 0) curdata_ = dst_->rcvr_retrieve_data(); if (curdata_ == 0) { fprintf(stderr, "[%g] %s receives a packet but no callback!\n", Scheduler::instance().clock(), name_); return; } curbytes_ += size; #ifdef TCPAPP_DEBUG fprintf(stderr, "[%g] %s gets data size %d, %s\n", Scheduler::instance().clock(), name(), curbytes_, curdata_->data()); #endif if (curbytes_ == curdata_->bytes()) { // We've got exactly the data we want // If we've received all data, execute the callback process_data(curdata_->size(), curdata_->data()); // Then cleanup this data transmission delete curdata_; curdata_ = NULL; curbytes_ = 0; } else if (curbytes_ > curdata_->bytes()) { // We've got more than we expected. Must contain other data. // Continue process callbacks until the unfinished callback while (curbytes_ >= curdata_->bytes()) { process_data(curdata_->size(), curdata_->data()); curbytes_ -= curdata_->bytes(); #ifdef TCPAPP_DEBUG fprintf(stderr, "[%g] %s gets data size %d(left %d)\n", Scheduler::instance().clock(), name(), curdata_->bytes(), curbytes_); //curdata_->data()); #endif delete curdata_; curdata_ = dst_->rcvr_retrieve_data(); if (curdata_ != 0) continue; if ((curdata_ == 0) && (curbytes_ > 0)) { fprintf(stderr, "[%g] %s gets extra data!\n", Scheduler::instance().clock(), name_); // XXX Remove this before commit!!! Tcl::instance().eval("[Test instance] flush-trace"); abort(); } else // Get out of the look without doing a check break; } } // else if (curbytes_ < curdata_->bytes()) { // fprintf(stderr, "[%g] %s gets less data than expected!!\n", // Scheduler::instance().clock(), name_); // // XXX Remove this before commit!!! // Tcl::instance().eval("[Test instance] flush-trace"); // abort(); // } } void TcpApp::resume() { // Do nothing } int TcpApp::command(int argc, const char*const* argv) { Tcl& tcl = Tcl::instance(); if (strcmp(argv[1], "connect") == 0) { dst_ = (TcpApp *)TclObject::lookup(argv[2]); if (dst_ == NULL) { tcl.resultf("%s: connected to null object.", name_); return (TCL_ERROR); } dst_->connect(this); return (TCL_OK); } else if (strcmp(argv[1], "send") == 0) { /* * <app> send <size> <tcl_script> */ int size = atoi(argv[2]); if (argc == 3) send(size, NULL); else { TcpAppString *tmp = new TcpAppString(); tmp->set_string(argv[3]); send(size, tmp); } return (TCL_OK); } else if (strcmp(argv[1], "dst") == 0) { tcl.resultf("%s", dst_->name()); return TCL_OK; } return Application::command(argc, argv); } void TcpApp::process_data(int size, AppData* data) { if (data == NULL) return; // XXX Default behavior: // If there isn't a target, use tcl to evaluate the data if (target()) send_data(size, data); else if (data->type() == TCPAPP_STRING) { TcpAppString *tmp = (TcpAppString*)data; Tcl::instance().eval(tmp->str()); } }

webtraf.cc


/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */ // // Copyright (c) 1999 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. // // Incorporation Polly's web traffic module into the PagePool framework // // $Header$ #include "config.h" #include <tclcl.h> #include "node.h" #include "pagepool.h" #include "webtraf.h" // Data structures that are specific to this web traffic model and // should not be used outside this file. // // - WebTrafPage // - WebTrafObject class WebPage : public TimerHandler { public: WebPage(int id, WebTrafSession* sess, int nObj, Node* dst) : id_(id), sess_(sess), nObj_(nObj), curObj_(0), dst_(dst) {} virtual ~WebPage() {} inline void start() { // Call expire() and schedule the next one if needed status_ = TIMER_PENDING; handle(&event_); } inline int id() const { return id_; } Node* dst() { return dst_; } private: virtual void expire(Event* = 0) { // Launch a request. Make sure size is not 0! sess_->launchReq(this, LASTOBJ_++, (int)ceil(sess_->objSize()->value())); } virtual void handle(Event *e) { // XXX Note when curObj_ == nObj_, we still schedule the timer // once, but we do not actually send out requests. This extra // schedule is only meant to be a hint to wait for the last // request to finish, then we will ask our parent to delete // this page. #if 0 fprintf(stderr, "Session %d handling page %d obj %d\n", sess_->id(), id_, curObj_); #endif if (curObj_ <= nObj_) { // If this is not the last object, schedule the next // one. Otherwise stop and tell session to delete me. TimerHandler::handle(e); curObj_++; sched(sess_->interObj()->value()); } else sess_->donePage((void*)this); } int id_; WebTrafSession* sess_; int nObj_, curObj_; Node* dst_; static int LASTOBJ_; }; int WebPage::LASTOBJ_ = 1; int WebTrafSession::LASTPAGE_ = 1; // XXX Must delete this after all pages are done!! WebTrafSession::~WebTrafSession() { if (donePage_ != curPage_) { fprintf(stderr, "done pages %d != all pages %d\n", donePage_, curPage_); abort(); } if (status_ != TIMER_IDLE) { fprintf(stderr, "WebTrafSession must be idle when deleted.\n"); abort(); } if (rvInterPage_ != NULL) Tcl::instance().evalf("delete %s", rvInterPage_->name()); if (rvPageSize_ != NULL) Tcl::instance().evalf("delete %s", rvPageSize_->name()); if (rvInterObj_ != NULL) Tcl::instance().evalf("delete %s", rvInterObj_->name()); if (rvObjSize_ != NULL) Tcl::instance().evalf("delete %s", rvObjSize_->name()); } void
WebTrafSession::donePage(void* ClntData) { #if 0 fprintf(stderr, "Session %d done page %d\n", id_, ((WebPage*)ClntData)->id()); #endif delete (WebPage*)ClntData; // If all pages are done, tell my parent to delete myself if (++donePage_ >= nPage_) mgr_->doneSession(id_); } // Launch the current page void WebTrafSession::expire(Event *) { // Pick destination for this page Node* dst = mgr_->pickdst(); // Make sure page size is not 0! WebPage* pg = new WebPage(LASTPAGE_++, this, (int)ceil(rvPageSize_->value()), dst); #if 0 printf("Session %d starting page %d, curpage %d \n", id_, LASTPAGE_-1, curPage_); #endif pg->start(); } void WebTrafSession::handle(Event *e) { // If I haven't scheduled all my pages, do the next one TimerHandler::handle(e); // XXX Notice before each page is done, it will schedule itself // one more time, this makes sure that this session will not be // deleted after the above call. Thus the following code will not // be executed in the context of a deleted object. if (++curPage_ < nPage_) sched(rvInterPage_->value()); } // Launch a request for a particular object void WebTrafSession::launchReq(void* ClntData, int obj, int size) { WebPage* pg = (WebPage*)ClntData; // Choose source and dest TCP agents for both source and destination TcpAgent* ctcp = mgr_->picktcp(); TcpAgent* stcp = mgr_->picktcp(); TcpSink* csnk = mgr_->picksink(); TcpSink* ssnk = mgr_->picksink(); // Setup TCP connection and done Tcl::instance().evalf("%s launch-req %d %s %s %s %s %s %s %d", mgr_->name(), obj, src_->name(), pg->dst()->name(), ctcp->name(), csnk->name(), stcp->name(), ssnk->name(), size); // Debug only // $numPacket_ $objectId_ $pageId_ $sessionId_ [$ns_ now] src dst #if 0 printf("%d \t %d \t %d \t %d \t %g %d %d\n", size, obj, pg->id(), id_, Scheduler::instance().clock(), src_->address(), pg->dst()->address()); printf("** Tcp agents %d, Tcp sinks %d\n", mgr_->nTcp(),mgr_->nSink()); #endif } static class WebTrafPoolClass : public TclClass { public: WebTrafPoolClass() : TclClass("PagePool/WebTraf") {} TclObject* create(int, const char*const*) { return (new WebTrafPool()); } } class_webtrafpool; // By default we use constant request interval and page size WebTrafPool::WebTrafPool() : session_(NULL), nSrc_(0), server_(NULL), nClient_(0), client_(NULL), nTcp_(0), nSink_(0) { LIST_INIT(&tcpPool_); LIST_INIT(&sinkPool_); } WebTrafPool::~WebTrafPool() { if (session_ != NULL) { for (int i = 0; i < nSession_; i++) delete session_[i]; delete []session_; } if (server_ != NULL) delete []server_; if (client_ != NULL) delete []client_; // XXX Destroy tcpPool_ and sinkPool_!! } TcpAgent* WebTrafPool::picktcp() { TcpAgent* a = (TcpAgent*)detachHead(&tcpPool_); if (a == NULL) { Tcl& tcl = Tcl::instance(); tcl.evalf("%s alloc-tcp", name()); a = (TcpAgent*)lookup_obj(tcl.result()); if (a == NULL) { fprintf(stderr, "Failed to allocate a TCP agent\n"); abort(); } } else nTcp_--; return a; } TcpSink* WebTrafPool::picksink() { TcpSink* a = (TcpSink*)detachHead(&sinkPool_); if (a == NULL) { Tcl& tcl = Tcl::instance(); tcl.evalf("%s alloc-tcp-sink", name()); a = (TcpSink*)lookup_obj(tcl.result()); if (a == NULL) { fprintf(stderr, "Failed to allocate a TCP sink\n"); abort(); } } else nSink_--; return a; } int WebTrafPool::command(int argc, const char*const* argv) { if (argc == 3) { if (strcmp(argv[1], "set-num-session") == 0) { if (session_ != NULL) { for (int i = 0; i < nSession_; i++) delete session_[i]; delete []session_; } nSession_ = atoi(argv[2]); session_ = new WebTrafSession*[nSession_]; memset(session_, 0, sizeof(WebTrafSession*)*nSession_); return (TCL_OK); } else if (strcmp(argv[1], "set-num-server") == 0) { nSrc_ = atoi(argv[2]); if (server_ != NULL) delete []server_; server_ = new Node*[nSrc_]; return (TCL_OK); } else if (strcmp(argv[1], "set-num-client") == 0) { nClient_ = atoi(argv[2]); if (client_ != NULL) delete []client_; client_ = new Node*[nClient_]; return (TCL_OK); } } else if (argc == 4) { if (strcmp(argv[1], "set-server") == 0) { Node* cli = (Node*)lookup_obj(argv[3]); if (cli == NULL) return (TCL_ERROR); int nc = atoi(argv[2]); if (nc >= nSrc_) { fprintf(stderr, "Wrong server index %d\n", nc); return TCL_ERROR; } server_[nc] = cli; return (TCL_OK); } else if (strcmp(argv[1], "set-client") == 0) { Node* s = (Node*)lookup_obj(argv[3]); if (s == NULL) return (TCL_ERROR); int n = atoi(argv[2]); if (n >= nClient_) { fprintf(stderr, "Wrong client index %d\n", n); return TCL_ERROR; } client_[n] = s; return (TCL_OK); } else if (strcmp(argv[1], "recycle") == 0) { // <obj> recycle <tcp> <sink> // // Recycle a TCP source/sink pair Agent* tcp = (Agent*)lookup_obj(argv[2]); Agent* snk = (Agent*)lookup_obj(argv[3]); nTcp_++, nSink_++; if ((tcp == NULL) || (snk == NULL)) return (TCL_ERROR); // XXX TBA: recycle tcp agents insertAgent(&tcpPool_, tcp); insertAgent(&sinkPool_, snk); return (TCL_OK); } } else if (argc == 9) { if (strcmp(argv[1], "create-session") == 0) { // <obj> create-session <session_index> // <pages_per_sess> <launch_time> // <inter_page_rv> <page_size_rv> // <inter_obj_rv> <obj_size_rv> int n = atoi(argv[2]); if ((n < 0)||(n >= nSession_)||(session_[n] != NULL)) { fprintf(stderr,"Invalid session index %d\n",n); return (TCL_ERROR); } int npg = (int)strtod(argv[3], NULL); double lt = strtod(argv[4], NULL); WebTrafSession* p = new WebTrafSession(this, picksrc(), npg, n); int res = lookup_rv(p->interPage(), argv[5]); res = (res == TCL_OK) ? lookup_rv(p->pageSize(), argv[6]) : TCL_ERROR; res = (res == TCL_OK) ? lookup_rv(p->interObj(), argv[7]) : TCL_ERROR; res = (res == TCL_OK) ? lookup_rv(p->objSize(), argv[8]) : TCL_ERROR; if (res == TCL_ERROR) { delete p; fprintf(stderr, "Invalid random variable\n"); return (TCL_ERROR); } p->sched(lt); session_[n] = p; return (TCL_OK); } } return PagePool::command(argc, argv); }

global.tcl


# # 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. # # @(#) $Header$ # # # Word of warning to developers: # this code (and all it sources) is compiled into the # ns executable. You need to rebuild ns or explicitly # source this code to see changes take effect. # proc warn {msg} { global warned_ if {![info exists warned_($msg)]} { puts stderr "warning: $msg" set warned_($msg) 1 } } if {[info commands debug] == ""} { proc debug args { warn {Script debugging disabled. Reconfigure with --with-tcldebug, and recompile.} } } proc assert args { if [catch "expr $args" ret] { set ret [eval $args] } if {! $ret} { error "assertion failed: $args" } } proc find-max list { set max 0 foreach val $list { if {$val > $max} { set max $val } } return $max } # # Create the core OTcl class called "Simulator". # This is the principal interface to the simulation engine. # Class Simulator # -*- Mode:tcl -*- # # 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. # # # This file should contain variables changed only by autoconf. proc checkout_executable {exe_var best alternate text} { global $exe_var set $exe_var $best if {"$best" == "" || ![file executable $best]} { puts stderr $text set $exe_var $alternate } } # # Keep track of where the good perl is. # checkout_executable PERL "/usr/bin/perl" perl "\ When configured, ns found the right version of perl in\ /usr/bin/perl but it doesn't seem to be there anymore, so\ ns will fall back on running the first perl in your path.\ The wrong version of perl may break the test suites.\ Reconfigure and rebuild ns if this is a problem.\ " checkout_executable TCLSH "/usr/local/bin/tclsh8.0" tclsh "\ When configured, ns found the right version of tclsh in\ /usr/local/bin/tclsh8.0 but it doesn't seem to be there anymore, so\ ns will fall back on running the first tclsh in your path.\ The wrong version of tclsh may break the test suites.\ Reconfigure and rebuild ns if this is a problem.\ " # # 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. # ##################################################################################### # API's for allocating bits to ns addressing structure # These routines should be used for setting bits for portid (agent), for nodeid # (flat or hierarchical). # DEFADDRSIZE_ and MAXADDRSIZE_ defined in ns-default.tcl # Name : set-address-format () # Options : default, expanded, hierarchical (default) and hierarchical (with # specific allocations) # Synopsis : set-address-format <option> <additional info, if required by option> # # Option:1 def (default settings) # Synopsis : set-address-format def # Description: This allows default settings in the following manner: # * 8bits portid # * 1 bit for mcast and 7 bits nodeid (mcast should be enabled # before Simulator is inited (new Simulator is called)--> leads to # unnecessary wasting of 1 bit even if mcast is not set. # # # Option :2 expanded # Synopsis : set-address-format expanded # Description: This allows to expand the address space from default size 16 to 32 # with bit allocated to the field in the foll manner: # * 8bits portid # * 1 bit for mcast and 21 bits nodeid (and same comments regarding # setting mcast as for default option above) # # # Option :3 hierarchical (default) # Synopsis : set-address-format hierarchical # Description: This allows setting of hierarchical address as follows: # * Sets 8bits portid # * Sets mcast bit (if specified) # * Sets default hierchical levels with: # * 3 levels of hierarchy and # * 8 bits in each level (looks like 8 8 8) # * 8 8 7 if mcast is enabled. # # # Option :4 hierarchical (specified) # Synopsis : set-address-format hierarchical <#n hierarchy levels> <#bits for # level1> <#bits for level 2> ....<#bits for nth level> # e.g # set-address-format hierarchical 4 2 2 2 10 # Description: This allows setting of hierarchical address as specified for each # level. # * Sets 8bits portid # * Sets mcast bit (if specified) # * Sets hierchical levels with bits specified for each level. # Name : expand-port-field-bits () # Synopsis : expand-port-field-bits <#bits for portid> # Status: Obsolete. It is no longer needed in the 32-bit addressing # scheme # Description : This should be used incase of need to extend portid. # This commnad may be used in conjuction with # set-address-format command explained above. # expand-port-field-bits checks and raises error in the foll. cases # * if the requested portsize cannot be accomodated (i.e # if sufficient num.of free bits are not available) # * if requested portsize is less than equal to existing portsize. # and incase of no errors sets port field with bits as specified. #2 # # # # Errors returned : * if # of bits specified less than 0. # * if bit positions clash (contiguous # of requested free bits not # found) # * if total # of bits exceed MAXADDRSIZE_ # * if expand-port-field-bits is attempted with portbits less or equal # to the existing portsize. # * if # hierarchy levels donot match with #bits specified (for # each level). # ###################################################################################### Class AddrParams Class AllocAddrBits
Simulator instproc get-AllocAddrBits {prog} { $self instvar allocAddr_ if ![info exists allocAddr_] { set allocAddr_ [new AllocAddrBits] } elseif ![string compare $prog "new"] { # puts "Warning: existing Address Space was destroyed\n" set allocAddr_ [new AllocAddrBits] } return $allocAddr_ } Simulator instproc set-address-format {opt args} { set len [llength $args] if {[string compare $opt "def"] == 0} { $self set-address 32 set mcastshift [AddrParams set McastShift_] Simulator set McastAddr_ [expr 1 << $mcastshift] mrtObject expandaddr } elseif {[string compare $opt "expanded"] == 0} { #$self expand-address puts "set-address-format expanded is obsoleted by 32-bit addressing." } elseif {[string compare $opt "hierarchical"] == 0 && $len == 0} { if [$self multicast?] { $self set-hieraddress 3 9 11 11 } else { $self set-hieraddress 3 10 11 11 } } else { if {[string compare $opt "hierarchical"] == 0 && $len > 0} { eval $self set-hieraddress [lindex $args 0] [lrange $args 1 [expr $len - 1]] } } } Simulator instproc expand-port-field-bits nbits { # The function is obsolete, given that ports are now 32 bits wide puts "Warning: Simulator::expand-port-field-bits is obsolete. Ports are 32 bits wide" return } # sets address for nodeid and port fields # The order of setting bits for different fields does matter # and should be as follows: # mcast # idbits # and finally portbits # this is true for both set-address and set-hieraddress Simulator instproc set-address {node} { set a [$self get-AllocAddrBits "new"] $a set size_ [AllocAddrBits set DEFADDRSIZE_] if {[expr $node] > [$a set size_]} { $a set size_ [AllocAddrBits set MAXADDRSIZE_] } # one bit is set aside for mcast as default :: this waste of 1 bit may be avoided, if # mcast option is enabled before the initialization of Simulator. $a set-mcastbits 1 set lastbit [expr $node - [$a set mcastsize_]] $a set-idbits 1 $lastbit } Simulator instproc expand-address {} { puts "Warning: Simulator::expand-address is obsolete. The node address is 32 bits wides" return } #sets hierarchical bits Simulator instproc set-hieraddress {hlevel args} { set a [$self get-AllocAddrBits "new"] $a set size_ [AllocAddrBits set MAXADDRSIZE_] if { ![Simulator set EnableHierRt_] && $hlevel > 1} { ### By default, setting hierarchical addressing also turns on hier rtg, ### provided the level is greater than 1 Simulator set EnableHierRt_ 1 Simulator set node_factory_ HierNode } if [$self multicast?] { $a set-mcastbits 1 } eval $a set-idbits $hlevel $args } AllocAddrBits instproc init {} { eval $self next $self instvar size_ portsize_ idsize_ mcastsize_ set size_ 0 set portsize_ 0 set idsize_ 0 set mcastsize_ 0 } AllocAddrBits instproc get-AllocAddr {} { $self instvar addrStruct_ if ![info exists addrStruct_] { set addrStruct_ [new AllocAddr] } return $addrStruct_ } AllocAddrBits instproc get-Address {} { $self instvar address_ if ![info exists address_] { set address_ [new Address] } return $address_ } AllocAddrBits instproc chksize {bit_num prog} { $self instvar size_ portsize_ idsize_ mcastsize_ if {$bit_num <= 0 } { error "$prog : \# bits less than 1" } set totsize [expr $portsize_ + $idsize_ + $mcastsize_] if {$totsize > [AllocAddrBits set MAXADDRSIZE_]} { error "$prog : Total \# bits exceed MAXADDRSIZE" } if { $size_ < [AllocAddrBits set MAXADDRSIZE_]} { if {$totsize > [AllocAddrBits set DEFADDRSIZE_]} { set size_ [AllocAddrBits set MAXADDRSIZE_] return 1 } } return 0 } AllocAddrBits instproc set-portbits {bit_num} { # The function is obsolete, given that ports are now 32 bits wide puts "Warning: AllocAddrBits::set-portbits is obsolete. Ports are 32 bits wide." return } AllocAddrBits instproc expand-portbits nbits { # The function is obsolete, given that ports are now 32 bits wide puts "Warning: AllocAddrBits::expand-portbits is obsolete. Ports are 32 bits wide." return } AllocAddrBits instproc set-mcastbits {bit_num} { $self instvar size_ mcastsize_ if {$bit_num != 1} { error "setmcast : mcastbit > 1" } set mcastsize_ $bit_num #chk to ensure there;s no change in size if [$self chksize mcastsize_ "setmcast"] { # assert {$chk == 0} --> assert doesn't seem to work error "set-mcastbits: size_ has been changed." } set a [$self get-AllocAddr] set v [$a setbit $bit_num $size_] AddrParams set McastMask_ [lindex $v 0] AddrParams set McastShift_ [lindex $v 1] ### TESTING # set mask [lindex $v 0] # set shift [lindex $v 1] # puts "Mcastshift = $shift \n McastMask = $mask\n" set ad [$self get-Address] $ad mcastbits-are [AddrParams set McastShift_] [AddrParams set McastMask_] } AllocAddrBits instproc set-idbits {nlevel args} { $self instvar size_ portsize_ idsize_ hlevel_ hbits_ if {$nlevel != [llength $args]} { error "setid: hlevel < 1 OR nlevel and \# args donot match" } set a [$self get-AllocAddr] set old 0 set idsize_ 0 set nodebits 0 AddrParams set hlevel_ $nlevel set hlevel_ $nlevel for {set i 0} {$i < $nlevel} {incr i} { set bpl($i) [lindex $args $i] set idsize_ [expr $idsize_ + $bpl($i)] # check to ensure there's no change in size set chk [$self chksize $bpl($i) "setid"] # assert {$chk ==0} if {$chk > 0} { error "set-idbits: size_ has been changed." } set v [$a setbit $bpl($i) $size_] AddrParams set NodeMask_([expr $i+1]) [lindex $v 0] set m([expr $i+1]) [lindex $v 0] AddrParams set NodeShift_([expr $i+1]) [lindex $v 1] set s([expr $i+1]) [lindex $v 1] lappend hbits_ $bpl($i) } AddrParams set nodebits_ $idsize_ set ad [$self get-Address] eval $ad idsbits-are [array get s] eval $ad idmbits-are [array get m] ### TESTING # set mask [lindex $v 0] # set shift [lindex $v 1] # puts "Nodeshift = $shift \n NodeMask = $mask\n" } ### Hierarchical routing support # # create a real hier address from addr string # AddrParams proc set-hieraddr addrstr { set ns [Simulator instance] set addressObj [[$ns get-AllocAddrBits ""] get-Address] set ip [$addressObj str2addr $addrstr] return $ip } # # returns address string from address :reverse of set-hieraddr. # AddrParams proc get-hieraddr addr { AddrParams instvar hlevel_ NodeMask_ NodeShift_ for {set i 1} {$i <= $hlevel_} {incr i} { set a [expr [expr $addr >> $NodeShift_($i)] & $NodeMask_($i)] lappend str $a } #puts "get-hieraddr: string=$str\n" return $str } # # Returns number of elements at a given hierarchical level, that is visible to # a node. # AddrParams proc elements-in-level? {nodeaddr level} { AddrParams instvar hlevel_ domain_num_ cluster_num_ nodes_num_ def_ set L [split $nodeaddr .] set level [expr $level + 1] # # if no topology info found for last level, set default values # ### for now, assuming only 3 levels of hierarchy if { $level == 1} { return $domain_num_ } if { $level == 2} { return [lindex $cluster_num_ [lindex $L 0]] } if { $level == 3} { set C 0 set index 0 while {$C < [lindex $L 0]} { set index [expr $index + [lindex $cluster_num_ $C]] incr C } return [lindex $nodes_num_ [expr $index + [lindex $L 1]]] } } # # Given an node's address, Return the node # Simulator instproc get-node-by-addr address { $self instvar Node_ set n [Node set nn_] for {set q 0} {$q < $n} {incr q} { set nq $Node_($q) if {[string compare [$nq node-addr] $address] == 0} { return $nq } } error "get-node-by-addr:Cannot find node with given address" } # # Given an node's address, Return the node-id # Simulator instproc get-node-id-by-addr address { $self instvar Node_ set n [Node set nn_] for {set q 0} {$q < $n} {incr q} { set nq $Node_($q) if {[string compare [$nq node-addr] $address] == 0} { return $q } } error "get-node-id-by-addr:Cannot find node with given address" } # # Copyright (c) 1996-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. # # @(#) $Header$ # # for MobileIP # Class Classifier/Port/MIP -superclass Classifier/Port Classifier/Port/Reserve instproc init args { eval $self next $self reserve-port 2 } #Class Node Node set nn_ 0 Node proc getid {} { set id [Node set nn_] Node set nn_ [expr $id + 1] return $id } Node instproc init args { eval $self next $args $self instvar np_ id_ agents_ dmux_ neighbor_ rtsize_ address_ $self instvar nodetype_ $self instvar multiPath_ set nodetype_ [[Simulator instance] get-nodetype] set neighbor_ "" set agents_ "" set dmux_ "" set np_ 0 set id_ [Node getid] set rtsize_ 0 # set address_ $args $self set-node-address$nodetype_ $args $self mk-default-classifier$nodetype_ # $self mk-default-classifier $self cmd addr $address_; # new by tomh set multiPath_ [$class set multiPath_] } Node instproc set-node-address { args } { } Node instproc set-node-addressMIPBS { args } { } Node instproc set-node-addressMIPMH { args } { } Node instproc set-node-addressMobile { args } { $self instvar nifs_ arptable_ $self instvar netif_ mac_ ifq_ ll_ $self instvar X_ Y_ Z_ set X_ 0.0 set Y_ 0.0 set Z_ 0.0 set arptable_ "" ;# no ARP table yet set nifs_ 0 ;# number of network interfaces } Node instproc set-node-addressHier {args} { $self instvar address_ set address_ $args } Node instproc set-node-addressBase {args} { } Node instproc mk-default-classifierMIPMH {} { $self mk-default-classifierBase } Node instproc mk-default-classifierMIPBS {} { $self mk-default-classifierBase } Node instproc mk-default-classifierBase {} { $self mk-default-classifierHier } Node instproc mk-default-classifierHier {} { $self instvar np_ id_ classifiers_ agents_ dmux_ neighbor_ address_ # puts "id=$id_" set levels [AddrParams set hlevel_] for {set n 1} {$n <= $levels} {incr n} { set classifiers_($n) [new Classifier/Addr] $classifiers_($n) set mask_ [AddrParams set NodeMask_($n)] $classifiers_($n) set shift_ [AddrParams set NodeShift_($n)] } } # mobileNode Node instproc mk-default-classifierMobile {} { $self mk-default-classifier } ## splitting up address str: used by other non-hier classes Node instproc split-addrstr addrstr { set L [split $addrstr .] return $L } Node instproc mk-default-classifier {} { $self instvar address_ classifier_ id_ set classifier_ [new Classifier/Hash/Dest 32] # set up classifer as a router (default value 8 bit of addr and 8 bit port) $classifier_ set mask_ [AddrParams set NodeMask_(1)] $classifier_ set shift_ [AddrParams set NodeShift_(1)] if ![info exists address_] { set address_ $id_ } } Node instproc enable-mcast sim { $self instvar classifier_ multiclassifier_ ns_ switch_ mcastproto_ $self set ns_ $sim $self set switch_ [new Classifier/Addr] # # set up switch to route unicast packet to slot 0 and # multicast packets to slot 1 # [$self set switch_] set mask_ [AddrParams set McastMask_] [$self set switch_] set shift_ [AddrParams set McastShift_] # # create a classifier for multicast routing # $self set multiclassifier_ [new Classifier/Multicast/Replicator] [$self set multiclassifier_] set node_ $self $self set mrtObject_ [new mrtObject $self] $switch_ install 0 $classifier_ $switch_ install 1 $multiclassifier_ } Node instproc add-neighbor p { $self instvar neighbor_ lappend neighbor_ $p } # # increase the routing table size counter - keeps track of rtg table # size for each node # Node instproc incr-rtgtable-size {} { $self instvar rtsize_ set rtsize_ [expr $rtsize_ + 1] } Node instproc entry {} { #set nodetype [[Simulator instance] get-nodetype] $self instvar nodetype_ return [$self entry-New$nodetype_] } Node instproc entry-NewMIPBS {} { return [$self entry-NewBase] } Node instproc entry-NewMIPMH {} { return [$self entry-NewBase] } Node instproc entry-NewBase {} { $self instvar ns_ if ![info exist ns_] { set ns_ [Simulator instance] } if [$ns_ multicast?] { $self instvar switch_ return $switch_ } $self instvar classifiers_ return $classifiers_(1) } Node instproc entry-New {} { if [info exists router_supp_] { return $router_supp_ } if ![info exist ns_] { set ns_ [Simulator instance] } if [$ns_ multicast?] { $self instvar switch_ return $switch_ } $self instvar classifier_ return $classifier_ } Node instproc entry-NewMobile {} { return [$self entry-New] } Node instproc add-route { dst target } { $self instvar classifier_ $classifier_ install $dst $target $self incr-rtgtable-size } Node instproc get-nam-traceall {args} { $self instvar namtraceFile_ set file [lindex $args 0] set namtraceFile_ $file } Node instproc id {} { $self instvar id_ return $id_ } Node instproc node-addr {} { $self instvar address_ return $address_ } Node instproc alloc-port { nullagent } { $self instvar dmux_ np_ set p [$dmux_ alloc-port $nullagent] if { $np_ < $p } { set np_ $p } if {$np_ > [$dmux_ set mask_] && [$dmux_ set mask_] > 0 } { puts stderr "No of ports($np_) attached to $self node is greater than allowed" } return $p } # # Attach an agent to a node. Pick a port and # bind the agent to the port number. # Node instproc attach { agent { port "" } } { $self instvar agents_ address_ dmux_ classifier_ $self instvar classifiers_ # # assign port number (i.e., this agent receives # traffic addressed to this host and port) # lappend agents_ $agent # # Check if number of agents exceeds length of port-address-field size # set mask [AddrParams set ALL_BITS_SET] set shift 0 # The following code is no longer needed. It is unlikely that # a node holds billions of agents # if {[expr [llength $agents_] - 1] > $mask} { # error "\# of agents attached to node $self exceeds port-field length of $mask bits\n" # } # # Attach agents to this node (i.e., the classifier inside). # We call the entry method on ourself to find the front door # (e.g., for multicast the first object is not the unicast # classifier) # Also, stash the node in the agent and set the # local addr of this agent. # $agent set node_ $self if [Simulator set EnableHierRt_] { $agent set agent_addr_ [AddrParams set-hieraddr $address_] } else { $agent set agent_addr_ [expr ($address_ & \ [AddrParams set NodeMask_(1)]) \ << [AddrParams set NodeShift_(1) ]] } # # If a port demuxer doesn't exist, create it. # if { $dmux_ == "" } { set dmux_ [new Classifier/Port] $dmux_ set mask_ $mask $dmux_ set shift_ $shift # # point the node's routing entry to itself # at the port demuxer (if there is one) # if {[Simulator set EnableHierRt_]} { $self add-hroute $address_ $dmux_ } else { $self add-route $address_ $dmux_ } } if {$port == ""} { set ns_ [Simulator instance] $ns_ instvar nullAgent_ set port [$self alloc-port $nullAgent_] } $agent set agent_port_ $port $self add-target $agent $port } # # add target to agent and add entry for port-id in port-dmux # Node instproc add-target {agent port} { #set nodetype [[Simulator instance] get-nodetype] $self instvar nodetype_ $self add-target-New$nodetype_ $agent $port } Node instproc add-target-New {agent port} { $self instvar dmux_ # # Send Target # $agent target [$self entry] # # Recv Target # $dmux_ install $port $agent } Node instproc add-target-NewBase {agent port} { $self add-target-NewMobile $agent $port } Node instproc add-target-NewHier {agent port} { $self add-target-New $agent $port } # # Detach an agent from a node. # Node instproc detach { agent nullagent } { $self instvar agents_ dmux_ # # remove agent from list # set k [lsearch -exact $agents_ $agent] if { $k >= 0 } { set agents_ [lreplace $agents_ $k $k] } # # sanity -- clear out any potential linkage # $agent set node_ "" $agent set agent_addr_ 0 $agent target $nullagent set port [$agent set agent_port_] #Install nullagent to sink transit packets # $dmux_ clear $port $dmux_ install $port $nullagent } Node instproc agent port { $self instvar agents_ foreach a $agents_ { if { [$a set agent_port_] == $port } { return $a } } return "" } # # reset all agents attached to this node # Node instproc reset {} { #set nodetype [[Simulator instance] get-nodetype] $self instvar nodetype_ $self do-reset$nodetype_ # $self instvar agents_ # foreach a $agents_ { # $a reset # } } Node instproc do-reset { } { $self instvar agents_ foreach a $agents_ { $a reset } } Node instproc do-resetMobile {} { $self instvar arptable_ nifs_ $self instvar netif_ mac_ ifq_ ll_ $self instvar imep_ for {set i 0} {$i < $nifs_} {incr i} { $netif_($i) reset $mac_($i) reset $ll_($i) reset $ifq_($i) reset if { [info exists opt(imep)] && $opt(imep) == "ON" } { $imep_($i) reset } } if { $arptable_ != "" } { $arptable_ reset } } Node instproc do-resetBase {} { $self do-resetMobile } Node instproc do-resetHier {} { $self do-reset } # # Some helpers # Node instproc neighbors {} { $self instvar neighbor_ return [lsort $neighbor_] } # # Helpers for interface stuff # Node instproc attachInterfaces ifs { $self instvar ifaces_ set ifaces_ $ifs foreach ifc $ifaces_ { $ifc setNode $self } } Node instproc addInterface { iface } { $self instvar ifaces_ lappend ifaces_ $iface # $iface setNode $self } Node instproc createInterface { num } { $self instvar ifaces_ set newInterface [new NetInterface] if { $num < 0 } { return 0 } $newInterface set-label $num return 1 } Node instproc getInterfaces {} { $self instvar ifaces_ return $ifaces_ } Node instproc getNode {} { return $self } # # helpers for PIM stuff # Node instproc get-vif {} { $self instvar vif_ if [info exists vif_] { return $vif_ } set vif_ [new NetInterface] $self addInterface $vif_ return $vif_ } # List of corresponding peer TCP hosts from this node, used in IntTcp Node instproc addCorresHost {addr cw mtu maxcw wndopt } { $self instvar chaddrs_ if { ![info exists chaddrs_($addr)] } { set chost [new Agent/Chost $addr $cw $mtu $maxcw $wndopt] set chaddrs_($addr) $chost } return $chaddrs_($addr) } Node instproc createTcpSession {dst} { $self instvar tcpSession_ if { ![info exists tcpSession_($dst)] } { set tcpsession [new Agent/TCP/Session] $self attach $tcpsession $tcpsession set dst_ $dst set tcpSession_($dst) $tcpsession } return $tcpSession_($dst) } Node instproc getTcpSession {dst} { $self instvar tcpSession_ if { [info exists tcpSession_($dst)] } { return $tcpSession_($dst) } else { puts "In getTcpSession(): no session exists for destination [$dst set addr_]" return "" } } Node instproc existsTcpSession {dst} { $self instvar tcpSession_ if { [info exists tcpSession_($dst)] } { return 1 } else { return 0 } } # # Node support for detailed dynamic routing # Node instproc init-routing rtObject { $self instvar multiPath_ routes_ rtObject_ set multiPath_ [$class set multiPath_] set nn [$class set nn_] for {set i 0} {$i < $nn} {incr i} { set routes_($i) 0 } if ![info exists rtObject_] { $self set rtObject_ $rtObject } $self set rtObject_ } Node instproc rtObject? {} { $self instvar rtObject_ if ![info exists rtObject_] { return "" } else { return $rtObject_ } } # # Node support for equal cost multi path routing # Node instproc add-routes {id ifs} { $self instvar classifier_ multiPath_ routes_ mpathClsfr_ if !$multiPath_ { if {[llength $ifs] > 1} { warn "$class::$proc cannot install multiple routes" set ifs [lindex $ifs 0] } $self add-route $id [$ifs head] set routes_($id) 1 return } if {$routes_($id) <= 0 && [llength $ifs] == 1 && \ ![info exists mpathClsfr_($id)]} { $self add-route $id [$ifs head] set routes_($id) 1 } else { if ![info exists mpathClsfr_($id)] { # 1. get new MultiPathClassifier, # 2. migrate existing routes to that mclassifier # 3. install the mclassifier in the node classifier_ # set mpathClsfr_($id) [new Classifier/MultiPath] if {$routes_($id) > 0} { assert {$routes_($id) == 1} $mpathClsfr_($id) installNext [$classifier_ in-slot? $id] } $classifier_ install $id $mpathClsfr_($id) } foreach L $ifs { $mpathClsfr_($id) installNext [$L head] incr routes_($id) } } } Node instproc delete-routes {id ifs nullagent} { $self instvar mpathClsfr_ routes_ if [info exists mpathClsfr_($id)] { foreach L $ifs { set nonLink([$L head]) 1 } foreach {slot link} [$mpathClsfr_($id) adjacents] { if [info exists nonLink($link)] { $mpathClsfr_($id) clear $slot incr routes_($id) -1 } } } else { $self add-route $id $nullagent incr routes_($id) -1 } } Node instproc intf-changed { } { $self instvar rtObject_ if [info exists rtObject_] { ;# i.e. detailed dynamic routing $rtObject_ intf-changed } } ### PGM Router support ### # Simulator instproc attach-PgmNE (args) { # foreach node $args { # $node attach-PgmNEAgent # } # Node instproc attach-PgmNEAgent {} { # $self instvar switch_ router_supp_ ns_ # # if![$ns_ multicast?] { # # error "Error :Attaching PGM without Mcast option!" # # } # set router_supp_ [new Agent/NE/Pgm $switch_] # [Simulator instance] attach-agent $self $router-supp_ # $switch_ install 1 $router-supp_ # } # # Manual Routing Nodes: # like normal nodes, but with a hash classifier. # Class ManualRtNode -superclass Node ManualRtNode instproc mk-default-classifier {} { $self instvar address_ classifier_ id_ dmux_ # Note the very small hash size--- # you're supposed to resize it if you want more. set classifier_ [new Classifier/Hash/Dest 2] $classifier_ set mask_ [AddrParams set NodeMask_(1)] $classifier_ set shift_ [AddrParams set NodeShift_(1)] set address_ $id_ # # When an agent is created, # $self add-route $address_ $dmux_ is called # which will do this. # } ManualRtNode instproc add-route {dst_address target} { $self instvar classifier_ set slot [$classifier_ installNext $target] if {$dst_address == "default"} { $classifier_ set default_ $slot } else { # don't encode the address here, set-hash bypasses that for us set encoded_dst_address [expr $dst_address << [AddrParams set NodeShift_(1)]] $classifier_ set-hash auto 0 $encoded_dst_address 0 $slot # $classifier_ set-hash auto 0 $dst_address 0 $slot } # puts "ManualRtNode::add-route: $dst $target, classifier=$classifier_ slot=$slot" # puts "\t*slot=[$classifier_ slot $slot]" } ManualRtNode instproc add-route-to-adj-node args { $self instvar classifier_ address_ set dst "" if {[lindex $args 0] == "-default"} { set dst default set args [lrange $args 1 end] } if {[llength $args] != 1} { error "ManualRtNode::add-route-to-adj-node [-default] node" } set target_node $args if {$dst == ""} { set dst [$target_node set address_] } set ns [Simulator instance] set link [$ns link $self $target_node] set target [$link head] # puts "ManualRtNode::add-route-to-adj-node: in $self for addr $dst to target $target" return [$self add-route $dst $target] } # # Virtual Classifier Nodes: # like normal nodes, but with a virtual unicast classifier. # Class VirtualClassifierNode -superclass Node VirtualClassifierNode instproc mk-default-classifier {} { $self instvar address_ classifier_ id_ set classifier_ [new Classifier/Virtual] $classifier_ set node_ $self $classifier_ set mask_ [AddrParams set NodeMask_(1)] $classifier_ set shift_ [AddrParams set NodeShift_(1)] set address_ $id_ $classifier_ nodeaddr $address_ } VirtualClassifierNode instproc add-route { dst target } { } Classifier/Virtual instproc find dst { $self instvar node_ ns_ routingTable_ if ![info exist ns_] { set ns_ [Simulator instance] } if {[$node_ id] == $dst} { return [$node_ set dmux_] } else { return [[$ns_ link $node_ [$ns_ set Node_($dst)]] head] } } Classifier/Virtual instproc install {dst target} { } # # Broadcast Nodes: # accept limited broadcast packets # Class Node/Broadcast -superclass Node Node/Broadcast instproc mk-default-classifier {} { $self instvar address_ classifier_ id_ dmux_ set classifier_ [new Classifier/Hash/Dest/Bcast 32] $classifier_ set mask_ [AddrParams set NodeMask_(1)] $classifier_ set shift_ [AddrParams set NodeShift_(1)] set address_ $id_ if { $dmux_ == "" } { set dmux_ [new Classifier/Port/Reserve] $dmux_ set mask_ [AddrParams set ALL_BITS_SET] $dmux_ set shift_ 0 if [Simulator set EnableHierRt_] { $self add-hroute $address_ $dmux_ } else { $self add-route $address_ $dmux_ } } $classifier_ bcast-receiver $dmux_ } # New node structure Node instproc add-target-NewMobile {agent port} { #global opt $self instvar dmux_ classifier_ $self instvar imep_ toraDebug_ set ns_ [Simulator instance] set newapi [$ns_ imep-support] $agent set sport_ $port #special processing for TORA/IMEP node set toraonly [string first "TORA" [$agent info class]] if {$toraonly != -1 } { $agent if-queue [$self set ifq_(0)] ;# ifq between LL and MAC # # XXX: The routing protocol and the IMEP agents needs handles # to each other. # $agent imep-agent [$self set imep_(0)] [$self set imep_(0)] rtagent $agent } if { $port == 255 } { # non-routing agents if { [Simulator set RouterTrace_] == "ON" } { # # Send Target # if {$newapi != ""} { set sndT [$ns_ mobility-trace Send "RTR" $self] } else { set sndT [cmu-trace Send "RTR" $self] } if [info exists namtraceFile_] { $sndT namattach $namtraceFile_ } if { $newapi == "ON" } { $agent target $imep_(0) $imep_(0) sendtarget $sndT # second tracer to see the actual # types of tora packets before imep packs them #if { [info exists opt(debug)] && $opt(debug) == "ON" } { if { [info exists toraDebug_] && $toraDebug_ == "ON"} { set sndT2 [$ns_ mobility-trace Send "TRP" $self] $sndT2 target $imep_(0) $agent target $sndT2 } } else { ;# no IMEP $agent target $sndT } $sndT target [$self set ll_(0)] # $agent target $sndT # # Recv Target # if {$newapi != ""} { set rcvT [$ns_ mobility-trace Recv "RTR" $self] } else { set rcvT [cmu-trace Recv "RTR" $self] } if [info exists namtraceFile_] { $rcvT namattach $namtraceFile_ } if {$newapi == "ON" } { # puts "Hacked for tora20 runs!! No RTR revc trace" [$self set ll_(0)] up-target $imep_(0) $classifier_ defaulttarget $agent # need a second tracer to see the actual # types of tora packets after imep unpacks them #if { [info exists opt(debug)] && $opt(debug) == "ON" } { # no need to support any hier node if {[info exists toraDebug_] && $toraDebug_ == "ON" } { set rcvT2 [$ns_ mobility-trace Recv "TRP" $self] $rcvT2 target $agent [$self set classifier_] defaulttarget $rcvT2 } } else { $rcvT target $agent $self install-defaulttarget $rcvT # [$self set classifier_] defaulttarget $rcvT # $classifier_ defaulttarget $rcvT $dmux_ install $port $rcvT } # $rcvT target $agent # $classifier_ defaulttarget $rcvT # $dmux_ install $port $rcvT } else { # # Send Target # $agent target [$self set ll_(0)] # # Recv Target # $self install-defaulttarget $agent #$classifier_ defaulttarget $agent $dmux_ install $port $agent } } else { if { [Simulator set AgentTrace_] == "ON" } { # # Send Target # if {$newapi != ""} { set sndT [$ns_ mobility-trace Send AGT $self] } else { set sndT [cmu-trace Send AGT $self] } $sndT target [$self entry] $agent target $sndT # # Recv Target # if {$newapi != ""} { set rcvT [$ns_ mobility-trace Recv AGT $self] } else { set rcvT [cmu-trace Recv AGT $self] } $rcvT target $agent $dmux_ install $port $rcvT } else { # # Send Target # $agent target [$self entry] # # Recv Target # $dmux_ install $port $agent } } } Node instproc add-interface { channel pmodel \ lltype mactype qtype qlen iftype anttype} { $self instvar arptable_ nifs_ $self instvar netif_ mac_ ifq_ ll_ $self instvar imep_ #global ns_ opt #set MacTrace [Simulator set MacTrace_] set ns_ [Simulator instance] set imepflag [$ns_ imep-support] set t $nifs_ incr nifs_ set netif_($t) [new $iftype] ;# interface set mac_($t) [new $mactype] ;# mac layer set ifq_($t) [new $qtype] ;# interface queue set ll_($t) [new $lltype] ;# link layer set ant_($t) [new $anttype] if {$imepflag == "ON" } { ;# IMEP layer set imep_($t) [new Agent/IMEP [$self id]] set imep $imep_($t) set drpT [$ns_ mobility-trace Drop "RTR" $self] $imep drop-target $drpT if [info exists namtraceFile_] { $drpT namattach $namtraceFile_ } $ns_ at 0.[$self id] "$imep_($t) start" ;# start beacon timer } # # Local Variables # set nullAgent_ [$ns_ set nullAgent_] set netif $netif_($t) set mac $mac_($t) set ifq $ifq_($t) set ll $ll_($t) # # Initialize ARP table only once. # if { $arptable_ == "" } { set arptable_ [new ARPTable $self $mac] # FOR backward compatibility sake, hack only if {$imepflag != ""} { set drpT [$ns_ mobility-trace Drop "IFQ" $self] } else { set drpT [cmu-trace Drop "IFQ" $self] } $arptable_ drop-target $drpT } # # Link Layer # $ll arptable $arptable_ $ll mac $mac # $ll up-target [$self entry] $ll down-target $ifq if {$imepflag == "ON" } { $imep recvtarget [$self entry] $imep sendtarget $ll $ll up-target $imep } else { $ll up-target [$self entry] } # # Interface Queue # $ifq target $mac $ifq set qlim_ $qlen if {$imepflag != ""} { set drpT [$ns_ mobility-trace Drop "IFQ" $self] } else { set drpT [cmu-trace Drop "IFQ" $self] } $ifq drop-target $drpT # # Mac Layer # $mac netif $netif $mac up-target $ll $mac down-target $netif #$mac nodes $opt(nn) set god_ [God instance] $mac nodes [$god_ num_nodes] # # Network Interface # $netif channel $channel $netif up-target $mac $netif propagation $pmodel ;# Propagation Model $netif node $self ;# Bind node <---> interface $netif antenna $ant_($t) # # Physical Channel` # $channel addif $netif # ============================================================ if { [Simulator set MacTrace_] == "ON" } { # # Trace RTS/CTS/ACK Packets # if {$imepflag != ""} { set rcvT [$ns_ mobility-trace Recv "MAC" $self] } else { set rcvT [cmu-trace Recv "MAC" $self] } $mac log-target $rcvT if [info exists namtraceFile_] { $rcvT namattach $namtraceFile_ } # # Trace Sent Packets # if {$imepflag != ""} { set sndT [$ns_ mobility-trace Send "MAC" $self] } else { set sndT [cmu-trace Send "MAC" $self] } $sndT target [$mac down-target] $mac down-target $sndT if [info exists namtraceFile_] { $sndT namattach $namtraceFile_ } # # Trace Received Packets # if {$imepflag != ""} { set rcvT [$ns_ mobility-trace Recv "MAC" $self] } else { set rcvT [cmu-trace Recv "MAC" $self] } $rcvT target [$mac up-target] $mac up-target $rcvT if [info exists namtraceFile_] { $rcvT namattach $namtraceFile_ } # # Trace Dropped Packets # if {$imepflag != ""} { set drpT [$ns_ mobility-trace Drop "MAC" $self] } else { set drpT [cmu-trace Drop "MAC" $self]` } $mac drop-target $drpT if [info exists namtraceFile_] { $drpT namattach $namtraceFile_ } } else { $mac log-target [$ns_ set nullAgent_] $mac drop-target [$ns_ set nullAgent_] } # ============================================================ $self addif $netif } Node instproc agenttrace {tracefd} { set ns_ [Simulator instance] set ragent [$self set ragent_] # # Drop Target (always on regardless of other tracing) # set drpT [$ns_ mobility-trace Drop "RTR" $self] $ragent drop-target $drpT # # Log Target # set T [new Trace/Generic] $T target [$ns_ set nullAgent_] $T attach $tracefd $T set src_ [$self id] #$ragent log-target $T $ragent tracetarget $T # # XXX: let the IMEP agent use the same log target. # set imepflag [$ns_ imep-support] if {$imepflag == "ON"} { [$self set imep_(0)] log-target $T } if [info exists namtraceFile_] { $drpT namattach $namtraceFile_ } } Node instproc nodetrace { tracefd } { set ns_ [Simulator instance] # # This Trace Target is used to log changes in direction # and velocity for the mobile node. # set T [new Trace/Generic] $T target [$ns_ set nullAgent_] $T attach $tracefd $T set src_ [$self id] $self log-target $T } Node instproc install-defaulttarget {rcvT} { #set nodetype [[Simulator instance] get-nodetype] $self instvar nodetype_ $self install-defaulttarget-New$nodetype_ $rcvT } Node instproc install-defaulttarget-New {rcvT} { [$self set classifier_] defaulttarget $rcvT } Node instproc install-defaulttarget-NewMobile {rcvT} { [$self set classifier_] defaulttarget $rcvT } Node instproc install-defaulttarget-NewBase {rcvT} { $self instvar classifiers_ set level [AddrParams set hlevel_] for {set i 1} {$i <= $level} {incr i} { $classifiers_($i) defaulttarget $rcvT # $classifiers_($i) bcast-receiver $rcvT } } Node instproc install-defaulttarget-NewMIPMH {rcvT} { $self install-defaulttarget-NewBase $rcvT } Node instproc install-defaulttarget-NewMIPBS {rcvT} { $self install-defaulttarget-NewBase $rcvT } # set receiving power Node instproc setPt { val } { $self instvar netif_ $netif_(0) setTxPower $val } # set transmission power Node instproc setPr { val } { $self instvar netif_ $netif_(0) setRxPower $val } Node instproc add-hroute { dst target } { $self instvar classifiers_ rtsize_ set al [$self split-addrstr $dst] set l [llength $al] for {set i 1} {$i <= $l} {incr i} { set d [lindex $al [expr $i-1]] if {$i == $l} { $classifiers_($i) install $d $target } else { $classifiers_($i) install $d $classifiers_([expr $i + 1]) } } # # increase the routing table size counter - keeps track of rtg table size for # each node set rtsize_ [expr $rtsize_ + 1] } # # 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. # # # THE CODE INCLUDED IN THIS FILE IS USED FOR BACKWARD COMPATIBILITY ONLY # for now creating a new class for hier-node for the sake of minimum overlap. # However this may be combined with ns-node at a later date when hierarchical routing # gets to become stable & operational. Class HierNode -superclass Node HierNode instproc init {args} { $self instvar address_ set haddress $args set address_ $args set args [lreplace $args 0 1] $self next $args set address_ $haddress } HierNode instproc mk-default-classifier {} { $self instvar np_ id_ classifiers_ agents_ dmux_ neighbor_ address_ # puts "id=$id_" set levels [AddrParams set hlevel_] for {set n 1} {$n <= $levels} {incr n} { set classifiers_($n) [new Classifier/Addr] $classifiers_($n) set mask_ [AddrParams set NodeMask_($n)] $classifiers_($n) set shift_ [AddrParams set NodeShift_($n)] } } HierNode instproc entry {} { $self instvar ns_ if ![info exist ns_] { set ns_ [Simulator instance] } if [$ns_ multicast?] { $self instvar switch_ return $switch_ } $self instvar classifiers_ return $classifiers_(1) } HierNode instproc enable-mcast sim { $self instvar classifiers_ multiclassifier_ ns_ switch_ mcastproto_ $self set ns_ $sim $self set switch_ [new Classifier/Addr] # # set up switch to route unicast packet to slot 0 and # multicast packets to slot 1 # [$self set switch_] set mask_ [AddrParams set McastMask_] [$self set switch_] set shift_ [AddrParams set McastShift_] # # create a classifier for multicast routing # $self set multiclassifier_ [new Classifier/Multicast/Replicator] [$self set multiclassifier_] set node_ $self [$self set switch_] install 0 $classifiers_(1) [$self set switch_] install 1 $multiclassifier_ # # Create a prune agent. Each multicast routing node # has a private prune agent that sends and receives # prune/graft messages and dispatches them to the # appropriate replicator object. # $self set mrtObject_ [new mrtObject $self] } HierNode instproc add-hroute { dst target } { $self instvar classifiers_ rtsize_ set al [$self split-addrstr $dst] set l [llength $al] for {set i 1} {$i <= $l} {incr i} { set d [lindex $al [expr $i-1]] if {$i == $l} { $classifiers_($i) install $d $target } else { $classifiers_($i) install $d $classifiers_([expr $i + 1]) } } # # increase the routing table size counter - keeps track of rtg table size for # each node set rtsize_ [expr $rtsize_ + 1] } # # split up hier address string # HierNode instproc split-addrstr addrstr { set L [split $addrstr .] return $L } #HierNode instproc node-addr {} { # $self instvar address_ # puts "H $address_" # return $address_ #} # as of now, hierarchical routing support not extended for equal cost multi path routing ### feature may be added in future # # Copyright (c) 1996-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. # # @(#) $Header: # # Ported from CMU-Monarch project's mobility extensions -Padma, 10/98. # #IT IS NOT ENCOURAGED TO SUBCLASSS MOBILNODE CLASS DEFINED IN THIS FILE #Class ARPTable # ====================================================================== # # The ARPTable class # # ====================================================================== ARPTable instproc init args { eval $self next $args ;# parent class constructor } ARPTable set bandwidth_ 0 ARPTable set delay_ 5us ARPTable set off_prune_ 0 ARPTable set off_CtrMcast_ 0 ARPTable set off_arp_ 0 # ====================================================================== # # The Node/MobileNode class # # ====================================================================== Node/MobileNode instproc init args { $self instvar nifs_ arptable_ $self instvar netif_ mac_ ifq_ ll_ $self instvar X_ Y_ Z_ $self instvar address_ $self instvar nodetype_ if {[llength $args] != 0} { set address_ $args set args [lreplace $args 0 1] } eval $self next $args ;# parent class constructor set X_ 0.0 set Y_ 0.0 set Z_ 0.0 # set netif_ "" ;# network interfaces # set mac_ "" ;# MAC layers # set ifq_ "" ;# interface queues # set ll_ "" ;# link layers set arptable_ "" ;# no ARP table yet set nifs_ 0 ;# number of network interfaces # MIP node processing $self makemip-New$nodetype_ } Node/MobileNode instproc makemip-New {} { } Node/MobileNode instproc makemip-NewBase {} { } Node/MobileNode instproc makemip-NewMobile {} { } Node/MobileNode instproc makemip-NewMIPBS {} { $self instvar regagent_ encap_ decap_ agents_ address_ dmux_ id_ if { $dmux_ == "" } { set dmux_ [new Classifier/Port/Reserve] $dmux_ set mask_ 0x7fffffff $dmux_ set shift_ 0 if [Simulator set EnableHierRt_] { $self add-hroute $address_ $dmux_ } else { $self add-route $address_ $dmux_ } } set regagent_ [new Agent/MIPBS $self] $self attach $regagent_ [Node/MobileNode set REGAGENT_PORT] $self attach-encap $self attach-decap } Node/MobileNode instproc makemip-NewMIPMH {} { $self instvar regagent_ dmux_ address_ if { $dmux_ == "" } { set dmux_ [new Classifier/Port/Reserve] $dmux_ set mask_ 0x7fffffff $dmux_ set shift_ 0 if [Simulator set EnableHierRt_] { $self add-hroute $address_ $dmux_ } else { $self add-route $address_ $dmux_ } } set regagent_ [new Agent/MIPMH $self] $self attach $regagent_ [Node/MobileNode set REGAGENT_PORT] $regagent_ node $self } Node/MobileNode instproc attach-encap {} { $self instvar encap_ address_ set encap_ [new MIPEncapsulator] set mask 0x7fffffff set shift 0 if [Simulator set EnableHierRt_] { set nodeaddr [AddrParams set-hieraddr $address_] } else { set nodeaddr [expr ( $address_ & \ [AddrParams set NodeMask_(1)] ) << \ [AddrParams set NodeShift_(1) ]] } $encap_ set addr_ [expr ( ~($mask << $shift) & $nodeaddr)] $encap_ set port_ 1 $encap_ target [$self entry] $encap_ set node_ $self #$encap_ set mask_ [AddrParams set NodeMask_(1)] #$encap_ set shift_ [AddrParams set NodeShift_(1)] } Node/MobileNode instproc attach-decap {} { $self instvar decap_ dmux_ agents_ set decap_ [new Classifier/Addr/MIPDecapsulator] lappend agents_ $decap_ set mask 0x7fffffff set shift 0 if {[expr [llength $agents_] - 1] > $mask} { error "\# of agents attached to node $self exceeds port-field length of $mask bits\n" } $dmux_ install [Node/MobileNode set DECAP_PORT] $decap_ #$decap_ set mask_ [AddrParams set NodeMask_(1)] #$decap_ set shift_ [AddrParams set NodeShift_(1)] #$decap_ def-target [$self entry] } Node/MobileNode instproc reset {} { $self instvar arptable_ nifs_ $self instvar netif_ mac_ ifq_ ll_ $self instvar imep_ for {set i 0} {$i < $nifs_} {incr i} { $netif_($i) reset $mac_($i) reset $ll_($i) reset $ifq_($i) reset if { [info exists opt(imep)] && $opt(imep) == "ON" } { $imep_($i) reset } } if { $arptable_ != "" } { $arptable_ reset } } # # Attach an agent to a node. Pick a port and # bind the agent to the port number. # if portnumber is 255, default target is set to the routing agent # Node/MobileNode instproc add-target {agent port } { #global opt $self instvar dmux_ classifier_ $self instvar imep_ toraDebug_ $self instvar namtraceFile_ set ns_ [Simulator instance] set newapi [$ns_ imep-support] $agent set sport_ $port #special processing for TORA/IMEP node set toraonly [string first "TORA" [$agent info class]] if {$toraonly != -1 } { $agent if-queue [$self set ifq_(0)] ;# ifq between LL and MAC # # XXX: The routing protocol and the IMEP agents needs handles # to each other. # $agent imep-agent [$self set imep_(0)] [$self set imep_(0)] rtagent $agent } # speciall processing for AODV set aodvonly [string first "AODV" [$agent info class]] if {$aodvonly != -1 } { $agent if-queue [$self set ifq_(0)] ;# ifq between LL and MAC } if { $port == 255 } { # routing agents if { [Simulator set RouterTrace_] == "ON" } { # # Send Target # if {$newapi != ""} { set sndT [$ns_ mobility-trace Send "RTR" $self] } else { set sndT [cmu-trace Send "RTR" $self] } if [info exists namtraceFile_] { $sndT namattach $namtraceFile_ } if { $newapi == "ON" } { $agent target $imep_(0) $imep_(0) sendtarget $sndT # second tracer to see the actual # types of tora packets before imep packs them #if { [info exists opt(debug)] && $opt(debug) == "ON" } { if { [info exists toraDebug_] && $toraDebug_ == "ON"} { set sndT2 [$ns_ mobility-trace Send "TRP" $self] $sndT2 target $imep_(0) $agent target $sndT2 } } else { ;# no IMEP $agent target $sndT } $sndT target [$self set ll_(0)] # $agent target $sndT # # Recv Target # if {$newapi != ""} { set rcvT [$ns_ mobility-trace Recv "RTR" $self] } else { set rcvT [cmu-trace Recv "RTR" $self] } if [info exists namtraceFile_] { $rcvT namattach $namtraceFile_ } if {$newapi == "ON" } { # puts "Hacked for tora20 runs!! No RTR revc trace" [$self set ll_(0)] up-target $imep_(0) $classifier_ defaulttarget $agent # need a second tracer to see the actual # types of tora packets after imep unpacks them #if { [info exists opt(debug)] && $opt(debug) == "ON" } { # no need to support any hier node if {[info exists toraDebug_] && $toraDebug_ == "ON" } { set rcvT2 [$ns_ mobility-trace Recv "TRP" $self] $rcvT2 target $agent [$self set classifier_] defaulttarget $rcvT2 } } else { $rcvT target $agent $self install-defaulttarget $rcvT # [$self set classifier_] defaulttarget $rcvT # $classifier_ defaulttarget $rcvT $dmux_ install $port $rcvT } # $rcvT target $agent # $classifier_ defaulttarget $rcvT # $dmux_ install $port $rcvT } else { # # Send Target # # if tora is used if { $newapi == "ON" } { $agent target $imep_(0) # $imep_(0) sendtarget $sndT # second tracer to see the actual # types of tora packets before imep packs them #if { [info exists opt(debug)] && $opt(debug) == "ON" } { if { [info exists toraDebug_] && $toraDebug_ == "ON"} { set sndT2 [$ns_ mobility-trace Send "TRP" $self] $sndT2 target $imep_(0) $agent target $sndT2 } $imep_(0) sendtarget [$self set ll_(0)] } else { ;# no IMEP $agent target [$self set ll_(0)] # $agent target $sndT } #$agent target [$self set ll_(0)] # # Recv Target # if {$newapi == "ON" } { # puts "Hacked for tora20 runs!! No RTR revc trace" [$self set ll_(0)] up-target $imep_(0) $classifier_ defaulttarget $agent # need a second tracer to see the actual # types of tora packets after imep unpacks them #if { [info exists opt(debug)] && $opt(debug) == "ON" } # no need to support any hier node if {[info exists toraDebug_] && $toraDebug_ == "ON" } { set rcvT2 [$ns_ mobility-trace Recv "TRP" $self] $rcvT2 target $agent [$self set classifier_] defaulttarget $rcvT2 } } else { $self install-defaulttarget $agent #$classifier_ defaulttarget $agent $dmux_ install $port $agent } } } else { if { [Simulator set AgentTrace_] == "ON" } { # # Send Target # if {$newapi != ""} { set sndT [$ns_ mobility-trace Send AGT $self] } else { set sndT [cmu-trace Send AGT $self] } if [info exists namtraceFile_] { $sndT namattach $namtraceFile_ } $sndT target [$self entry] $agent target $sndT # # Recv Target # if {$newapi != ""} { set rcvT [$ns_ mobility-trace Recv AGT $self] } else { set rcvT [cmu-trace Recv AGT $self] } if [info exists namtraceFile_] { $rcvT namattach $namtraceFile_ } $rcvT target $agent $dmux_ install $port $rcvT } else { # # Send Target # $agent target [$self entry] # # Recv Target # $dmux_ install $port $agent } } } #Node/MobileNode instproc install-defaulttarget {rcvT} { # [$self set classifier_] defaulttarget $rcvT #} # set receiving power Node/MobileNode instproc setPt { val } { $self instvar netif_ $netif_(0) setTxPower $val } # set transmission power Node/MobileNode instproc setPr { val } { $self instvar netif_ $netif_(0) setRxPower $val } # # The following setups up link layer, mac layer, network interface # and physical layer structures for the mobile node. # Node/MobileNode instproc add-interface { channel pmodel \ lltype mactype qtype qlen iftype anttype} { $self instvar arptable_ nifs_ $self instvar netif_ mac_ ifq_ ll_ $self instvar imep_ $self instvar namtraceFile_ #global ns_ opt #set MacTrace [Simulator set MacTrace_] set ns_ [Simulator instance] set imepflag [$ns_ imep-support] set t $nifs_ incr nifs_ set netif_($t) [new $iftype] ;# interface set mac_($t) [new $mactype] ;# mac layer set ifq_($t) [new $qtype] ;# interface queue set ll_($t) [new $lltype] ;# link layer set ant_($t) [new $anttype] if {$imepflag == "ON" } { ;# IMEP layer set imep_($t) [new Agent/IMEP [$self id]] set imep $imep_($t) set drpT [$ns_ mobility-trace Drop "RTR" $self] if [info exists namtraceFile_] { $drpT namattach $namtraceFile_ } $imep drop-target $drpT $ns_ at 0.[$self id] "$imep_($t) start" ;# start beacon timer } # # Local Variables # set nullAgent_ [$ns_ set nullAgent_] set netif $netif_($t) set mac $mac_($t) set ifq $ifq_($t) set ll $ll_($t) # # Initialize ARP table only once. # if { $arptable_ == "" } { set arptable_ [new ARPTable $self $mac] # FOR backward compatibility sake, hack only if {$imepflag != ""} { set drpT [$ns_ mobility-trace Drop "IFQ" $self] } else { set drpT [cmu-trace Drop "IFQ" $self] } $arptable_ drop-target $drpT if [info exists namtraceFile_] { $drpT namattach $namtraceFile_ } } # # Link Layer # $ll arptable $arptable_ $ll mac $mac # $ll up-target [$self entry] $ll down-target $ifq if {$imepflag == "ON" } { $imep recvtarget [$self entry] $imep sendtarget $ll $ll up-target $imep } else { $ll up-target [$self entry] } # # Interface Queue # $ifq target $mac $ifq set qlim_ $qlen if {$imepflag != ""} { set drpT [$ns_ mobility-trace Drop "IFQ" $self] } else { set drpT [cmu-trace Drop "IFQ" $self] } $ifq drop-target $drpT if [info exists namtraceFile_] { $drpT namattach $namtraceFile_ } # # Mac Layer # $mac netif $netif $mac up-target $ll $mac down-target $netif #$mac nodes $opt(nn) set god_ [God instance] if {$mactype == "Mac/802_11"} { $mac nodes [$god_ num_nodes] } # # Network Interface # $netif channel $channel $netif up-target $mac $netif propagation $pmodel ;# Propagation Model $netif node $self ;# Bind node <---> interface $netif antenna $ant_($t) # # Physical Channel` # $channel addif $netif # ============================================================ if { [Simulator set MacTrace_] == "ON" } { # # Trace RTS/CTS/ACK Packets # if {$imepflag != ""} { set rcvT [$ns_ mobility-trace Recv "MAC" $self] } else { set rcvT [cmu-trace Recv "MAC" $self] } $mac log-target $rcvT if [info exists namtraceFile_] { $rcvT namattach $namtraceFile_ } # # Trace Sent Packets # if {$imepflag != ""} { set sndT [$ns_ mobility-trace Send "MAC" $self] } else { set sndT [cmu-trace Send "MAC" $self] } $sndT target [$mac down-target] $mac down-target $sndT if [info exists namtraceFile_] { $sndT namattach $namtraceFile_ } # # Trace Received Packets # if {$imepflag != ""} { set rcvT [$ns_ mobility-trace Recv "MAC" $self] } else { set rcvT [cmu-trace Recv "MAC" $self] } $rcvT target [$mac up-target] $mac up-target $rcvT if [info exists namtraceFile_] { $rcvT namattach $namtraceFile_ } # # Trace Dropped Packets # if {$imepflag != ""} { set drpT [$ns_ mobility-trace Drop "MAC" $self] } else { set drpT [cmu-trace Drop "MAC" $self]` } $mac drop-target $drpT if [info exists namtraceFile_] { $drpT namattach $namtraceFile_ } } else { $mac log-target [$ns_ set nullAgent_] $mac drop-target [$ns_ set nullAgent_] } # ============================================================ $self addif $netif } Node/MobileNode instproc nodetrace { tracefd } { set ns_ [Simulator instance] # # This Trace Target is used to log changes in direction # and velocity for the mobile node. # set T [new Trace/Generic] $T target [$ns_ set nullAgent_] $T attach $tracefd $T set src_ [$self id] $self log-target $T } Node/MobileNode instproc agenttrace {tracefd} { $self instvar namtraceFile_ set ns_ [Simulator instance] set ragent [$self set ragent_] # # Drop Target (always on regardless of other tracing) # set drpT [$ns_ mobility-trace Drop "RTR" $self] if [info exists namtraceFile_] { $drpT namattach $namtraceFile_ } $ragent drop-target $drpT # # Log Target # set T [new Trace/Generic] $T target [$ns_ set nullAgent_] $T attach $tracefd $T set src_ [$self id] #$ragent log-target $T $ragent tracetarget $T # # XXX: let the IMEP agent use the same log target. # set imepflag [$ns_ imep-support] if {$imepflag == "ON"} { [$self set imep_(0)] log-target $T } } ## method to remove an entry from the hier classifiers Node/MobileNode instproc clear-hroute args { $self instvar classifiers_ set a [split $args] set l [llength $a] $classifiers_($l) clear [lindex $a [expr $l-1]] } Node/MobileNode instproc mip-call {ragent} { $self instvar regagent_ if [info exists regagent_] { $regagent_ ragent $ragent } } Node instproc mip-call {ragent} { } #----------------------------------------------------------------- Agent/DSRAgent set sport_ 255 Agent/DSRAgent set dport_ 255 Agent/DSRAgent set rt_port 255 Class SRNodeNew -superclass Node/MobileNode SRNodeNew instproc init {args} { #global ns ns_ opt tracefd RouterTrace $self instvar dsr_agent_ dmux_ entry_point_ address_ set ns_ [Simulator instance] eval $self next $args ;# parent class constructor if {$dmux_ == "" } { set dmux_ [new Classifier/Port] $dmux_ set mask_ [AddrParams set PortMask_] $dmux_ set shift_ [AddrParams set PortShift_] # # point the node's routing entry to itself # at the port demuxer (if there is one) # #if [Simulator set EnableHierRt_] { #$self add-hroute $address_ $dmux_ #} else { #$self add-route $address_ $dmux_ #} } # puts "making dsragent for node [$self id]" set dsr_agent_ [new Agent/DSRAgent] # setup address (supports hier-address) for dsragent $dsr_agent_ addr $address_ $dsr_agent_ node $self if [Simulator set mobile_ip_] { $dsr_agent_ port-dmux [$self set dmux_] } # set up IP address $self addr $address_ if { [Simulator set RouterTrace_] == "ON" } { # Recv Target set rcvT [$ns_ mobility-trace Recv "RTR" $self] set namtraceFile_ [$ns_ set namtraceAllFile_] if { $namtraceFile_ != "" } { $rcvT namattach $namtraceFile_ } $rcvT target $dsr_agent_ set entry_point_ $rcvT } else { # Recv Target set entry_point_ $dsr_agent_ } # # Drop Target (always on regardless of other tracing) # #set drpT [$ns_ mobility-trace Drop "RTR" $self] #$dsr_agent_ drop-target $drpT # # Log Target # #set tracefd [$ns_ get-ns-traceall] #if {$tracefd != "" } { # set T [new Trace/Generic] # $T target [$ns_ set nullAgent_] # $T attach $tracefd # $T set src_ [$self id] # $dsr_agent_ log-target $T #} $self set ragent_ $dsr_agent_ $dsr_agent_ target $dmux_ # packets to the DSR port should be dropped, since we've # already handled them in the DSRAgent at the entry. set nullAgent_ [$ns_ set nullAgent_] $dmux_ install [Agent/DSRAgent set rt_port] $nullAgent_ # SRNodeNews don't use the IP addr classifier. The DSRAgent should # be the entry point $self instvar classifier_ set classifier_ "srnode made illegal use of classifier_" #$ns_ at 0.0 "$node start-dsr" return $self } SRNodeNew instproc start-dsr {} { $self instvar dsr_agent_ #global opt; $dsr_agent_ startdsr #if {$opt(cc) == "on"} {checkcache $dsr_agent_} } SRNodeNew instproc entry {} { $self instvar entry_point_ return $entry_point_ } SRNodeNew instproc add-interface {args} { set ns_ [Simulator instance] eval $self next $args $self instvar dsr_agent_ ll_ mac_ ifq_ $dsr_agent_ mac-addr [$mac_(0) id] if { [Simulator set RouterTrace_] == "ON" } { # Send Target set sndT [$ns_ mobility-trace Send "RTR" $self] set namtraceFile_ [$ns_ set namtraceAllFile_] if {$namtraceFile_ != "" } { $sndT namattach $namtraceFile_ } $sndT target $ll_(0) $dsr_agent_ add-ll $sndT $ifq_(0) } else { # Send Target $dsr_agent_ add-ll $ll_(0) $ifq_(0) } # setup promiscuous tap into mac layer $dsr_agent_ install-tap $mac_(0) } SRNodeNew instproc reset args { $self instvar dsr_agent_ eval $self next $args $dsr_agent_ reset } #new base station node Class BaseNode -superclass {HierNode Node/MobileNode} BaseNode instproc init {args} { $self instvar address_ $self next $args set address_ $args } BaseNode instproc install-defaulttarget {rcvT} { $self instvar classifiers_ set level [AddrParams set hlevel_] for {set i 1} {$i <= $level} {incr i} { $classifiers_($i) defaulttarget $rcvT # $classifiers_($i) bcast-receiver $rcvT } } # # Global Defaults - avoids those annoying warnings generated by bind() # Node/MobileNode set X_ 0 Node/MobileNode set Y_ 0 Node/MobileNode set Z_ 0 Node/MobileNode set speed_ 0 Node/MobileNode set position_update_interval_ 0 Node/MobileNode set bandwidth_ 0 ;# not used Node/MobileNode set delay_ 0 ;# not used Node/MobileNode set off_prune_ 0 ;# not used Node/MobileNode set off_CtrMcast_ 0 ;# not used # # Copyright (c) 1996-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. # # @(#) $Header: # #THE CODE INCLUDED IN THIS FILE IS USED FOR BACKWARD COMPATIBILITY ONLY # # # Special Base station nodes for communicating between wired and # wireless topologies in ns. Base stations are a hybrid form between # mobilenodes and hierarchical nodes. # XXXX does otcl support multiple inheritence? then maybe could # inheriting from hiernode and mobilenode. # # # The Node/MobileNode/BaseStationNode class # # ====================================================================== Class Node/MobileNode/BaseStationNode -superclass Node/MobileNode Node/MobileNode/BaseStationNode instproc init args { $self next $args } Node/MobileNode/BaseStationNode instproc mk-default-classifier {} { $self instvar classifiers_ set levels [AddrParams set hlevel_] for {set n 1} {$n <= $levels} {incr n} { set classifiers_($n) [new Classifier/Hash/Dest/Bcast 32] $classifiers_($n) set mask_ [AddrParams set NodeMask_($n)] $classifiers_($n) set shift_ [AddrParams set NodeShift_($n)] } } Node/MobileNode/BaseStationNode instproc entry {} { #XXX although mcast is not supported with wireless networking currently $self instvar ns_ if ![info exist ns_] { set ns_ [Simulator instance] } if [$ns_ multicast?] { $self instvar switch_ return $switch_ } $self instvar classifiers_ return $classifiers_(1) } Node/MobileNode/BaseStationNode instproc add-hroute { dst target } { $self instvar classifiers_ rtsize_ set al [$self split-addrstr $dst] set l [llength $al] for {set i 1} {$i <= $l} {incr i} { set d [lindex $al [expr $i-1]] if {$i == $l} { $classifiers_($i) install $d $target } else { $classifiers_($i) install $d $classifiers_([expr $i + 1]) } } # # increase the routing table size counter - keeps track of rtg table size for # each node set rtsize_ [expr $rtsize_ + 1] } ## method to remove an entry from the hier classifiers Node/MobileNode/BaseStationNode instproc clear-hroute args { $self instvar classifiers_ set a [split $args] set l [llength $a] $classifiers_($l) clear [lindex $a [expr $l-1]] } Node/MobileNode/BaseStationNode instproc node-addr {} { $self instvar address_ return $address_ } Node/MobileNode/BaseStationNode instproc split-addrstr addrstr { set L [split $addrstr .] return $L } Node/MobileNode/BaseStationNode instproc add-target {agent port } { #global RouterTrace AgentTrace $self instvar dmux_ classifiers_ $agent set sport_ $port set level [AddrParams set hlevel_] if { $port == 255 } { if { [Simulator set RouterTrace_] == "ON" } { # # Send Target # set sndT [cmu-trace Send "RTR" $self] $sndT target [$self set ll_(0)] $agent target $sndT # # Recv Target # set rcvT [cmu-trace Recv "RTR" $self] $rcvT target $agent for {set i 1} {$i <= $level} {incr i} { $classifiers_($i) defaulttarget $rcvT $classifiers_($i) bcast-receiver $rcvT } $dmux_ install $port $rcvT } else { $agent target [$self set ll_(0)] for {set i 1} {$i <= $level} {incr i} { $classifiers_($i) bcast-receiver $agent $classifiers_($i) defaulttarget $agent } $dmux_ install $port $agent ##$classifiers_($level) defaulttarget $agent } } else { if { [Simulator set AgentTrace_] == "ON" } { # # Send Target # set sndT [cmu-trace Send AGT $self] $sndT target [$self entry] $agent target $sndT # # Recv Target # set rcvT [cmu-trace Recv AGT $self] $rcvT target $agent $dmux_ install $port $rcvT } else { $agent target [$self entry] #for {set i 1} {$i <= $level} {incr i} { #$classifiers_($i) bcast-receiver $dmux_ #} $dmux_ install $port $agent } } } # # 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. # # @(#) $Header$ # Class Link Link instproc init { src dst } { $self next #modified for interface code $self instvar trace_ fromNode_ toNode_ color_ oldColor_ set fromNode_ $src set toNode_ $dst set color_ "black" set oldColor_ "black" set trace_ "" } Link instproc head {} { $self instvar head_ return $head_ } Link instproc add-to-head { connector } { $self instvar head_ $connector target [$head_ target] $head_ target $connector } Link instproc queue {} { $self instvar queue_ return $queue_ } Link instproc link {} { $self instvar link_ return $link_ } Link instproc src {} { $self set fromNode_ } Link instproc dst {} { $self set toNode_ } Link instproc cost c { $self set cost_ $c } Link instproc cost? {} { $self instvar cost_ if ![info exists cost_] { set cost_ 1 } set cost_ } Link instproc if-label? {} { $self instvar iif_ $iif_ label } Link instproc up { } { $self instvar dynamics_ dynT_ if ![info exists dynamics_] return $dynamics_ set status_ 1 if [info exists dynT_] { foreach tr $dynT_ { $tr format link-up {$src_} {$dst_} set ns [Simulator instance] $self instvar fromNode_ toNode_ $tr ntrace "l -t [$ns now] -s [$fromNode_ id] -d [$toNode_ id] -S UP" $tr ntrace "v -t [$ns now] link-up [$ns now] [$fromNode_ id] [$toNode_ id]" } } } Link instproc down { } { $self instvar dynamics_ dynT_ if ![info exists dynamics_] { puts stderr "$class::$proc Link $self was not declared dynamic, and cannot be taken down. ignored" return } $dynamics_ set status_ 0 $self all-connectors reset if [info exists dynT_] { foreach tr $dynT_ { $tr format link-down {$src_} {$dst_} set ns [Simulator instance] $self instvar fromNode_ toNode_ $tr ntrace "l -t [$ns now] -s [$fromNode_ id] -d [$toNode_ id] -S DOWN" $tr ntrace "v -t [$ns now] link-down [$ns now] [$fromNode_ id] [$toNode_ id]" } } } Link instproc up? {} { $self instvar dynamics_ if [info exists dynamics_] { return [$dynamics_ status?] } else { return "up" } } Link instproc all-connectors op { foreach c [$self info vars] { $self instvar $c if ![info exists $c] continue if [array size $c] continue foreach var [$self set $c] { if [catch "$var info class"] { continue } if ![$var info class Node] { ;# $op on most everything catch "$var $op";# in case var isn't a connector } } } } Link instproc install-error {em} { $self instvar link_ $em target [$link_ target] $link_ target $em } Class SimpleLink -superclass Link SimpleLink instproc init { src dst bw delay q {lltype "DelayLink"} } { $self next $src $dst $self instvar link_ queue_ head_ toNode_ ttl_ $self instvar drophead_ set ns [Simulator instance] set drophead_ [new Connector] $drophead_ target [$ns set nullAgent_] set head_ [new Connector] $head_ set link_ $self #set head_ $queue_ -> replace by the following # xxx this is hacky if { [[$q info class] info heritage ErrModule] == "ErrorModule" } { $head_ target [$q classifier] } else { $head_ target $q } set queue_ $q set link_ [new $lltype] $link_ set bandwidth_ $bw $link_ set delay_ $delay $queue_ target $link_ $link_ target [$dst entry] $queue_ drop-target $drophead_ # XXX # put the ttl checker after the delay # so we don't have to worry about accounting # for ttl-drops within the trace and/or monitor # fabric # set ttl_ [new TTLChecker] $ttl_ target [$link_ target] $self ttl-drop-trace $link_ target $ttl_ # Finally, if running a multicast simulation, # put the iif for the neighbor node... if { [$ns multicast?] } { $self enable-mcast $src $dst } } SimpleLink instproc enable-mcast {src dst} { $self instvar iif_ ttl_ set iif_ [new NetworkInterface] $iif_ target [$ttl_ target] $ttl_ target $iif_ $src add-oif [$self head] $self $dst add-iif [$iif_ label] $self } # # should be called after SimpleLink::trace # SimpleLink instproc nam-trace { ns f } { $self instvar enqT_ deqT_ drpT_ rcvT_ dynT_ #XXX # we use enqT_ as a flag of whether tracing has been # initialized if [info exists enqT_] { $enqT_ namattach $f if [info exists deqT_] { $deqT_ namattach $f } if [info exists drpT_] { $drpT_ namattach $f } if [info exists rcvT_] { $rcvT_ namattach $f } if [info exists dynT_] { foreach tr $dynT_ { $tr namattach $f } } } else { $self trace $ns $f "nam" } } # # Build trace objects for this link and # update the object linkage # # create nam trace files if op == "nam" # SimpleLink instproc trace { ns f {op ""} } { $self instvar enqT_ deqT_ drpT_ queue_ link_ fromNode_ toNode_ $self instvar rcvT_ ttl_ trace_ $self instvar drophead_ ;# idea stolen from CBQ and Kevin set trace_ $f set enqT_ [$ns create-trace Enque $f $fromNode_ $toNode_ $op] set deqT_ [$ns create-trace Deque $f $fromNode_ $toNode_ $op] set drpT_ [$ns create-trace Drop $f $fromNode_ $toNode_ $op] set rcvT_ [$ns create-trace Recv $f $fromNode_ $toNode_ $op] $self instvar drpT_ drophead_ set nxt [$drophead_ target] $drophead_ target $drpT_ $drpT_ target $nxt $queue_ drop-target $drophead_ # $drpT_ target [$queue_ drop-target] # $queue_ drop-target $drpT_ $deqT_ target [$queue_ target] $queue_ target $deqT_ # head is, like the drop-head_ a special connector. # mess not with it. $self add-to-head $enqT_ # put recv trace after ttl checking, so that only actually # received packets are recorded $rcvT_ target [$ttl_ target] $ttl_ target $rcvT_ $self instvar dynamics_ if [info exists dynamics_] { $self trace-dynamics $ns $f $op } } SimpleLink instproc trace-dynamics { ns f {op ""}} { $self instvar dynT_ fromNode_ toNode_ lappend dynT_ [$ns create-trace Generic $f $fromNode_ $toNode_ $op] $self transit-drop-trace $self linkfail-drop-trace } SimpleLink instproc ttl-drop-trace args { $self instvar ttl_ if ![info exists ttl_] return if {[llength $args] != 0} { $ttl_ drop-target [lindex $args 0] } else { $self instvar drophead_ $ttl_ drop-target $drophead_ } } SimpleLink instproc transit-drop-trace args { $self instvar link_ if {[llength $args] != 0} { $link_ drop-target [lindex $args 0] } else { $self instvar drophead_ $link_ drop-target $drophead_ } } SimpleLink instproc linkfail-drop-trace args { $self instvar dynamics_ if ![info exists dynamics_] return if {[llength $args] != 0} { $dynamics_ drop-target [lindex $args 0] } else { $self instvar drophead_ $dynamics_ drop-target $drophead_ } } # # Trace to a callback function rather than a file. # SimpleLink instproc trace-callback {ns cmd} { $self trace $ns {} foreach part {enqT_ deqT_ drpT_ rcvT_} { $self instvar $part set to [$self set $part] $to set callback_ 1 $to proc handle a "$cmd \$a" } } # # like init-monitor, but allows for specification of more of the items # attach-monitors $insnoop $inqm $outsnoop $outqm $dropsnoop $dropqm # SimpleLink instproc attach-monitors { insnoop outsnoop dropsnoop qmon } { $self instvar drpT_ queue_ snoopIn_ snoopOut_ snoopDrop_ $self instvar qMonitor_ drophead_ set snoopIn_ $insnoop set snoopOut_ $outsnoop set snoopDrop_ $dropsnoop $self add-to-head $snoopIn_ $snoopOut_ target [$queue_ target] $queue_ target $snoopOut_ set nxt [$drophead_ target] $drophead_ target $snoopDrop_ $snoopDrop_ target $nxt # if [info exists drpT_] { # $snoopDrop_ target [$drpT_ target] # $drpT_ target $snoopDrop_ # $queue_ drop-target $drpT_ # } else { # $snoopDrop_ target [[Simulator instance] set nullAgent_] # $queue_ drop-target $snoopDrop_ # } $snoopIn_ set-monitor $qmon $snoopOut_ set-monitor $qmon $snoopDrop_ set-monitor $qmon set qMonitor_ $qmon } # # Insert objects that allow us to monitor the queue size # of this link. Return the name of the object that # can be queried to determine the average queue size. # SimpleLink instproc init-monitor { ns qtrace sampleInterval} { $self instvar qMonitor_ ns_ qtrace_ sampleInterval_ set ns_ $ns set qtrace_ $qtrace set sampleInterval_ $sampleInterval set qMonitor_ [new QueueMonitor] $self attach-monitors [new SnoopQueue/In] \ [new SnoopQueue/Out] [new SnoopQueue/Drop] $qMonitor_ set bytesInt_ [new Integrator] $qMonitor_ set-bytes-integrator $bytesInt_ set pktsInt_ [new Integrator] $qMonitor_ set-pkts-integrator $pktsInt_ return $qMonitor_ } SimpleLink instproc start-tracing { } { $self instvar qMonitor_ ns_ qtrace_ sampleInterval_ $self instvar fromNode_ toNode_ if {$qtrace_ != 0} { $qMonitor_ trace $qtrace_ } $qMonitor_ set-src-dst [$fromNode_ id] [$toNode_ id] } SimpleLink instproc queue-sample-timeout { } { $self instvar qMonitor_ ns_ qtrace_ sampleInterval_ $self instvar fromNode_ toNode_ set qavg [$self sample-queue-size] if {$qtrace_ != 0} { puts $qtrace_ "[$ns_ now] [$fromNode_ id] [$toNode_ id] $qavg" } $ns_ at [expr [$ns_ now] + $sampleInterval_] "$self queue-sample-timeout" } SimpleLink instproc sample-queue-size { } { $self instvar qMonitor_ ns_ qtrace_ sampleInterval_ lastSample_ set now [$ns_ now] set qBytesMonitor_ [$qMonitor_ get-bytes-integrator] set qPktsMonitor_ [$qMonitor_ get-pkts-integrator] $qBytesMonitor_ newpoint $now [$qBytesMonitor_ set lasty_] set bsum [$qBytesMonitor_ set sum_] $qPktsMonitor_ newpoint $now [$qPktsMonitor_ set lasty_] set psum [$qPktsMonitor_ set sum_] if ![info exists lastSample_] { set lastSample_ 0 } set dur [expr $now - $lastSample_] if { $dur != 0 } { set meanBytesQ [expr $bsum / $dur] set meanPktsQ [expr $psum / $dur] } else { set meanBytesQ 0 set meanPktsQ 0 } $qBytesMonitor_ set sum_ 0.0 $qPktsMonitor_ set sum_ 0.0 set lastSample_ $now return "$meanBytesQ $meanPktsQ" } SimpleLink instproc dynamic {} { $self instvar dynamics_ if [info exists dynamics_] return set dynamics_ [new DynamicLink] $self add-to-head $dynamics_ $self transit-drop-trace $self all-connectors isDynamic } # # insert an "error module" BEFORE the queue # point the em's drop-target to the drophead # SimpleLink instproc errormodule args { $self instvar errmodule_ queue_ drophead_ if { $args == "" } { return $errmodule_ } set em [lindex $args 0] set errmodule_ $em $self add-to-head $em $em drop-target $drophead_ } # # Insert a loss module AFTER the queue. # # Must be inserted *RIGHT AFTER* the deqT_ (if present) or queue_, because # nam can only visualize a packet drop if and only if it is on the link or # in the queue # SimpleLink instproc insert-linkloss args { $self instvar link_errmodule_ queue_ drophead_ deqT_ if { $args == "" } { return $link_errmodule_ } set em [lindex $args 0] if [info exists link_errmodule_] { delete link_errmodule_ } set link_errmodule_ $em if [info exists deqT_] { $em target [$deqT_ target] $deqT_ target $em } else { $em target [$queue_ target] $queue_ target $em } $em drop-target $drophead_ } # # 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. # # @(#) $Header$ # # NOTE: Could consider renaming this file to ns-app.tcl and moving the # backward compatible code to ns-compat.tcl #set ns_telnet(interval) 1000ms #set ns_bursty(interval) 0 #set ns_bursty(burst-size) 2 #set ns_message(packet-size) 40 Class Application/FTP -superclass Application Application/FTP instproc init {} { $self next } Application/FTP instproc start {} { [$self agent] send -1 } # Causes TCP to send no more new data. Application/FTP instproc stop {} { [$self agent] advance 0 [$self agent] close } Application/FTP instproc send {nbytes} { [$self agent] send $nbytes } # For sending packets. Sends $pktcnt packets. Application/FTP instproc produce { pktcnt } { [$self agent] advance $pktcnt } # For sending packets. Sends $pktcnt more packets. Application/FTP instproc producemore { pktcnt } { [$self agent] advanceby $pktcnt } # Backward compatibility Application/Traffic instproc set args { $self instvar packetSize_ rate_ if { [lindex $args 0] == "packet_size_" } { if { [llength $args] == 2 } { set packetSize_ [lindex $args 1] return } elseif { [llength $args] == 1 } { return $packetSize_ } } eval $self next $args } # Helper function to convert rate_ into an interval_ Application/Traffic/CBR instproc set args { $self instvar packetSize_ rate_ if { [lindex $args 0] == "interval_" } { if { [llength $args] == 2 } { set ns_ [Simulator instance] set interval_ [$ns_ delay_parse [lindex $args 1]] $self set rate_ [expr $packetSize_ * 8.0/$interval_] return } elseif { [llength $args] == 1 } { return [expr $packetSize_ * 8.0/$rate_] } } eval $self next $args } # # Below are backward compatible components # Class Agent/CBR -superclass Agent/UDP Class Agent/CBR/UDP -superclass Agent/UDP Class Agent/CBR/RTP -superclass Agent/RTP Class Agent/CBR/UDP/SA -superclass Agent/SA Agent/SA instproc attach-traffic tg { $tg attach-agent $self eval $self cmd attach-traffic $tg } Agent/CBR/UDP instproc attach-traffic tg { $self instvar trafgen_ $tg attach-agent $self set trafgen_ $tg } Agent/CBR/UDP instproc done {} { } Agent/CBR/UDP instproc start {} { $self instvar trafgen_ $trafgen_ start } Agent/CBR/UDP instproc stop {} { $self instvar trafgen_ $trafgen_ stop } Agent/CBR/UDP instproc advance args { $self instvar trafgen_ eval $trafgen_ advance $args } Agent/CBR/UDP instproc advanceby args { $self instvar trafgen_ eval $trafgen_ advanceby $args } Agent/CBR instproc init {} { $self next $self instvar trafgen_ interval_ random_ packetSize_ maxpkts_ # The following used to be in ns-default.tcl set packetSize_ 210 set random_ 0 set maxpkts_ 268435456 set interval_ 0.00375 set trafgen_ [new Application/Traffic/CBR] $trafgen_ attach-agent $self # Convert packetSize_ and interval_ to trafgen_ rate_ $trafgen_ set rate_ [expr $packetSize_ * 8.0/ $interval_] $trafgen_ set random_ [$self set random_] $trafgen_ set maxpkts_ [$self set maxpkts_] $trafgen_ set packetSize_ [$self set packetSize_] # The line below is needed for backward compat with v1 test scripts if {[Simulator set nsv1flag] == 0} { puts "using backward compatible Agent/CBR; use Application/Traffic/CBR instead" } } Agent/CBR instproc done {} { } Agent/CBR instproc start {} { $self instvar trafgen_ $trafgen_ start } Agent/CBR instproc stop {} { $self instvar trafgen_ $trafgen_ stop } Agent/CBR instproc advance args { $self instvar trafgen_ eval $trafgen_ advance $args } Agent/CBR instproc advanceby args { $self instvar trafgen_ eval $trafgen_ advanceby $args } # Catches parameter settings for overlying traffic generator object Agent/CBR instproc set args { $self instvar interval_ random_ packetSize_ maxpkts_ trafgen_ if { [info exists trafgen_] } { if { [lindex $args 0] == "packetSize_" } { if { [llength $args] == 2 } { $trafgen_ set packetSize_ [lindex $args 1] set packetSize_ [lindex $args 1] # Recompute rate $trafgen_ set rate_ [expr $packetSize_ * 8.0/ $interval_] return } elseif { [llength $args] == 1 } { return $packetSize_ } } elseif { [lindex $args 0] == "random_" } { if { [llength $args] == 2 } { $trafgen_ set random_ [lindex $args 1] set random_ [lindex $args 1] return } elseif { [llength $args] == 1 } { return $random_ } } elseif { [lindex $args 0] == "maxpkts_" } { if { [llength $args] == 2 } { $trafgen_ set maxpkts_ [lindex $args 1] set maxpkts_ [lindex $args 1] return } elseif { [llength $args] == 1 } { return $maxpkts_ } } elseif { [lindex $args 0] == "interval_" } { if { [llength $args] == 2 } { set ns_ [Simulator instance] set interval_ [$ns_ delay_parse [lindex $args 1]] # Convert interval_ to rate for trafgen_ $trafgen_ set rate_ [expr $packetSize_ * 8.0/ $interval_] return } elseif { [llength $args] == 1 } { return $interval_ } } } eval $self next $args } Class Traffic/Expoo -superclass Application/Traffic/Exponential Class Traffic/Pareto -superclass Application/Traffic/Pareto Class Traffic/Trace -superclass Application/Traffic/Trace # These instprocs are needed to map old Traffic/* type variables Traffic/Expoo instproc set args { $self instvar packetSize_ burst_time_ idle_time_ rate_ if { [lindex $args 0] == "packet-size" } { if { [llength $args] == 2 } { $self set packetSize_ [lindex $args 1] return } elseif { [llength $args] == 1 } { return $packetSize_ } } elseif { [lindex $args 0] == "burst-time" } { if { [llength $args] == 2 } { $self set burst_time_ [lindex $args 1] return } elseif { [llength $args] == 1 } { return $burst_time_ } } elseif { [lindex $args 0] == "idle-time" } { if { [llength $args] == 2 } { $self set idle_time_ [lindex $args 1] return } elseif { [llength $args] == 1 } { return $idle_time_ } } elseif { [lindex $args 0] == "rate" } { if { [llength $args] == 2 } { $self set rate_ [lindex $args 1] return } elseif { [llength $args] == 1 } { return $rate_ } } eval $self next $args } Traffic/Pareto instproc set args { $self instvar packetSize_ burst_time_ idle_time_ rate_ shape_ if { [lindex $args 0] == "packet-size" } { if { [llength $args] == 2 } { $self set packetSize_ [lindex $args 1] return } elseif { [llength $args] == 1 } { return $packetSize_ } } elseif { [lindex $args 0] == "burst-time" } { if { [llength $args] == 2 } { $self set burst_time_ [lindex $args 1] return } elseif { [llength $args] == 1 } { return $burst_time_ } } elseif { [lindex $args 0] == "idle-time" } { if { [llength $args] == 2 } { $self set idle_time_ [lindex $args 1] return } elseif { [llength $args] == 1 } { return $idle_time_ } } elseif { [lindex $args 0] == "rate" } { if { [llength $args] == 2 } { $self set rate_ [lindex $args 1] return } elseif { [llength $args] == 1 } { return $rate_ } } elseif { [lindex $args 0] == "shape" } { if { [llength $args] == 2 } { $self set shape_ [lindex $args 1] return } elseif { [llength $args] == 1 } { return $shape_ } } eval $self next $args } Class Source/FTP -superclass Application Source/FTP set maxpkts_ 268435456 Source/FTP instproc attach o { $self instvar agent_ set agent_ $o $self attach-agent $o } Source/FTP instproc init {} { $self next $self instvar maxpkts_ agent_ set maxpkts_ 268435456 } Source/FTP instproc start {} { $self instvar agent_ maxpkts_ $agent_ advance $maxpkts_ } Source/FTP instproc stop {} { $self instvar agent_ $agent_ advance 0 } Source/FTP instproc produce { pktcnt } { $self instvar agent_ $agent_ advance $pktcnt } Source/FTP instproc producemore { pktcnt } { $self instvar agent_ $agent_ advanceby $pktcnt } # # For consistency with other applications # Class Source/Telnet -superclass Application/Telnet Source/Telnet set maxpkts_ 268435456 Source/Telnet instproc attach o { $self instvar agent_ set agent_ $o $self attach-agent $o } # # 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. # # @(#) $Header$ # Class OldSim -superclass Simulator # # If the "ns" command is called, set up the simulator # class to assume backward compat. This creates an instance # of a backward-compat simulator API with the name "ns" # (which in turn overrides this proc) # proc ns args { OldSim ns eval ns $args } OldSim instproc default_catch { varName index op } { if { $index == "" } { error "ns-1 compat: default change caught, but not a default! (varName: $varName)" exit 1 } if { $op == "r" || $op == "u" } { error "ns-1 compat: default change caught a $op operation" exit 1 } set vname ${varName}($index) upvar $vname var $self default_assign $varName $index $var } OldSim instproc default_assign {aname index newval} { $self instvar classMap_ queueMap_ if { $index == "" } { puts "something funny with default traces" exit 1 } set obj [string trimleft $aname ns_] # # special case the link array # if { $obj == "link" } { if { $index == "queue-limit" } { Queue set limit_ $newval return } set ivar "$index\_" if { [lsearch [DelayLink info vars] $ivar] >= 0 } { DelayLink set $ivar $newval return } error "warning: ns-1 compatibility library cannot set link default ${aname}($index)" return } # # now everyone else # if ![info exists classMap_($obj)] { if ![info exists queueMap_($obj)] { puts "error: ns-2 compatibility library cannot set ns-v1 default ${aname}($index)" exit 1 } else { set ns2obj "Queue/$queueMap_($obj)" } } else { set ns2obj $classMap_($obj) } SplitObject instvar varMap_ if ![info exists varMap_($index)] { puts "error: ns-2 compatibility library cannot map instvar $index in class $ns2obj" exit 1 } $ns2obj set $varMap_($index) $newval } # # see if this array has any elements already set # if so, arrange for the value to be set in ns-2 # also, add a trace hook so that future changes get # reflected into ns-2 # OldSim instproc map_ns_defaults old_arr { global $old_arr ; # these were all globals in ns-1 SplitObject instvar varMap_ foreach el [array names $old_arr] { set val [expr "$${old_arr}($el)"] $self default_assign $old_arr $el $val } # arrange to trace any read/write/unset op trace variable $old_arr rwu "$self default_catch" } OldSim instproc trace_old_defaults {} { # all ns-v1 defaults as of 1.4 $self map_ns_defaults ns_tcp $self map_ns_defaults ns_tcpnewreno $self map_ns_defaults ns_trace $self map_ns_defaults ns_fulltcp $self map_ns_defaults ns_red $self map_ns_defaults ns_cbq $self map_ns_defaults ns_class $self map_ns_defaults ns_sink $self map_ns_defaults ns_delsink $self map_ns_defaults ns_sacksink $self map_ns_defaults ns_cbr $self map_ns_defaults ns_rlm $self map_ns_defaults ns_ivs $self map_ns_defaults ns_source $self map_ns_defaults ns_telnet $self map_ns_defaults ns_bursty $self map_ns_defaults ns_message $self map_ns_defaults ns_facktcp $self map_ns_defaults ns_link $self map_ns_defaults ns_lossy_uniform $self map_ns_defaults ns_lossy_patt $self map_ns_defaults ns_queue $self map_ns_defaults ns_srm } OldSim instproc init args { eval $self next $args puts stderr "warning: using backward compatibility mode" $self instvar classMap_ Simulator set nsv1flag 1 # # Always use the list scheduler. $self instvar scheduler_ set scheduler_ [new Scheduler/List] # # in CBQ, setting the algorithm_ variable becomes invoking # the algorithm method # # also, there really isn't a limit_ for CBQ, as each queue # has its own. # Queue/CBQ instproc set args { $self instvar compat_qlim_ if { [lindex $args 0] == "queue-limit" || \ [lindex $args 0] == "limit_" } { if { [llength $args] == 2 } { set val [lindex $args 1] set compat_qlim_ $val return $val } return $compat_qlim_ } elseif { [lindex $args 0] == "algorithm_" } { $self algorithm [lindex $args 1] # note: no return here } eval $self next $args } # # Catch queue-limit variable which is now "$q limit" # Queue/DropTail instproc set args { if { [llength $args] == 2 && [lindex $args 0] == "queue-limit" } { # this will recursively call ourself $self set limit_ [lindex $args 1] return } eval $self next $args } Queue/RED instproc set args { if { [llength $args] == 2 && [lindex $args 0] == "queue-limit" } { # this will recursively call ourself $self set limit_ [lindex $args 1] return } eval $self next $args } Queue/RED instproc enable-vartrace file { $self trace ave_ $self trace prob_ $self trace curq_ $self attach $file } # # Catch set maxpkts for FTP sources, (needed because Source objects are # not derived from TclObject, and hence can't use varMap method below) # Source/FTP instproc set args { if { [llength $args] == 2 && [lindex $args 0] == "maxpkts" } { $self set maxpkts_ [lindex $args 1] return } eval $self next $args } Source/Telnet instproc set args { if { [llength $args] == 2 && [lindex $args 0] == "interval" } { $self set interval_ [lindex $args 1] return } eval $self next $args } # # Support for things like "set ftp [$tcp source ftp]" # Agent/TCP instproc source type { if { $type == "ftp" } { set type FTP } if { $type == "telnet" } { set type Telnet } set src [new Source/$type] $src attach $self return $src } Agent/TCP set restart_bugfix_ false # # support for new variable names # it'd be nice to set up mappings on a per-class # basis, but this is too painful. Just do the # mapping across all objects and hope this # doesn't cause any collisions... # SplitObject instproc set args { SplitObject instvar varMap_ set var [lindex $args 0] if [info exists varMap_($var)] { set var $varMap_($var) set args "$var [lrange $args 1 end]" } # xxx: re-implement the code from tcl-object.tcl $self instvar -parse-part1 $var if {[llength $args] == 1} { return [subst $[subst $var]] } else { return [set $var [lrange $args 1 end]] } } SplitObject instproc get {var} { SplitObject instvar varMap_ if [info exists varMap_($var)] { # puts stderr "TclObject::get $var -> $varMap_($var)." return [$self set $varMap_($var)] } else { return [$self next $var] } } # Agent TclObject set varMap_(addr) addr_ TclObject set varMap_(dst) dst_ ## now gone ###TclObject set varMap_(seqno) seqno_ ###TclObject set varMap_(cls) class_ ## class -> flow id TclObject set varMap_(cls) fid_ # Trace TclObject set varMap_(src) src_ TclObject set varMap_(show_tcphdr) show_tcphdr_ # TCP TclObject set varMap_(window) window_ TclObject set varMap_(window-init) windowInit_ TclObject set varMap_(window-option) windowOption_ TclObject set varMap_(window-constant) windowConstant_ TclObject set varMap_(window-thresh) windowThresh_ TclObject set varMap_(overhead) overhead_ TclObject set varMap_(tcp-tick) tcpTick_ TclObject set varMap_(ecn) ecn_ TclObject set varMap_(bug-fix) bugFix_ TclObject set varMap_(maxburst) maxburst_ TclObject set varMap_(maxcwnd) maxcwnd_ TclObject set varMap_(dupacks) dupacks_ TclObject set varMap_(seqno) seqno_ TclObject set varMap_(ack) ack_ TclObject set varMap_(cwnd) cwnd_ TclObject set varMap_(awnd) awnd_ TclObject set varMap_(ssthresh) ssthresh_ TclObject set varMap_(rtt) rtt_ TclObject set varMap_(srtt) srtt_ TclObject set varMap_(rttvar) rttvar_ TclObject set varMap_(backoff) backoff_ TclObject set varMap_(v-alpha) v_alpha_ TclObject set varMap_(v-beta) v_beta_ TclObject set varMap_(v-gamma) v_gamma_ # Agent/TCP/NewReno TclObject set varMap_(changes) newreno_changes_ # Agent/TCP/Fack TclObject set varMap_(rampdown) rampdown_ TclObject set varMap_(ss-div4) ss-div4_ # Queue TclObject set varMap_(limit) limit_ # Queue/SFQ TclObject set varMap_(limit) maxqueue_ TclObject set varMap_(buckets) buckets_ # Queue/RED TclObject set varMap_(bytes) bytes_ TclObject set varMap_(thresh) thresh_ TclObject set varMap_(maxthresh) maxthresh_ TclObject set varMap_(mean_pktsize) meanPacketSize_ TclObject set varMap_(q_weight) queueWeight_ TclObject set varMap_(wait) wait_ TclObject set varMap_(linterm) linterm_ TclObject set varMap_(setbit) setbit_ TclObject set varMap_(drop-tail) dropTail_ TclObject set varMap_(doubleq) doubleq_ TclObject set varMap_(dqthresh) dqthresh_ TclObject set varMap_(subclasses) subclasses_ # CBQClass TclObject set varMap_(algorithm) algorithm_ TclObject set varMap_(max-pktsize) maxpkt_ TclObject set varMap_(priority) priority_ TclObject set varMap_(maxidle) maxidle_ TclObject set varMap_(extradelay) extradelay_ # Agent/TCPSinnk, Agent/CBR TclObject set varMap_(packet-size) packetSize_ TclObject set varMap_(interval) interval_ # Agent/CBR TclObject set varMap_(random) random_ # IVS TclObject set varMap_(S) S_ TclObject set varMap_(R) R_ TclObject set varMap_(state) state_ TclObject set varMap_(rttShift) rttShift_ TclObject set varMap_(keyShift) keyShift_ TclObject set varMap_(key) key_ TclObject set varMap_(maxrtt) maxrtt_ Class traceHelper traceHelper instproc attach f { $self instvar file_ set file_ $f } # # linkHelper # backward compat for "[ns link $n1 $n2] set linkVar $value" # # unfortunately, 'linkVar' in ns-1 can be associated # with a link (delay, bandwidth, generic queue requests) or # can be specific to a particular queue (e.g. RED) which # has a bunch of variables (see above). # Class linkHelper linkHelper instproc init args { $self instvar node1_ node2_ linkref_ queue_ set node1_ [lindex $args 0] set node2_ [lindex $args 1] set lid [$node1_ id]:[$node2_ id] set linkref_ [ns set link_($lid)] set queue_ [$linkref_ queue] # these will be used in support of link stats set sqi [new SnoopQueue/In] set sqo [new SnoopQueue/Out] set sqd [new SnoopQueue/Drop] set dsamples [new Samples] set qmon [new QueueMonitor/Compat] $qmon set-delay-samples $dsamples $linkref_ attach-monitors $sqi $sqo $sqd $qmon $linkref_ set bytesInt_ [new Integrator] $linkref_ set pktsInt_ [new Integrator] $qmon set-bytes-integrator [$linkref_ set bytesInt_] $qmon set-pkts-integrator [$linkref_ set pktsInt_] } linkHelper instproc trace traceObj { $self instvar node1_ node2_ $self instvar queue_ set tfile [$traceObj set file_] ns trace-queue $node1_ $node2_ $tfile # XXX: special-case RED queue for var tracing if { [string first Queue/RED [$queue_ info class]] == 0 } { $queue_ enable-vartrace $tfile } } linkHelper instproc callback {fn} { # Reach deep into the guts of the link and twist... # (This code makes assumptions about how # SimpleLink instproc trace works.) # NEEDSWORK: should this be done with attach-monitors? $self instvar linkref_ foreach part {enqT_ deqT_ drpT_} { set to [$linkref_ set $part] $to set callback_ 1 $to proc handle {args} "$fn \$args" } } linkHelper instproc set { var val } { $self instvar linkref_ queue_ set qvars [$queue_ info vars] set linkvars [$linkref_ info vars] set linkdelayvars [[$linkref_ link] info vars] # # adjust the string to have a trailing '_' # because all instvars are constructed that way # if { [string last _ $var] != ( [string length $var] - 1) } { set var ${var}_ } if { $var == "queue-limit_" } { set var "limit_" } if { [lsearch $qvars $var] >= 0 } { # set a queue var $queue_ set $var $val } elseif { [lsearch $linkvars $var] >= 0 } { # set a link OTcl var $linkref_ set $var $val } elseif { [lsearch $linkdelayvars $var] >= 0 } { # set a linkdelay object var [$linkref_ link] set $var $val } else { puts stderr "linkHelper warning: couldn't set unknown variable $var" } } linkHelper instproc get var { $self instvar linkref_ queue_ set qvars [$queue_ info vars] set linkvars [$linkref_ info vars] set linkdelayvars [[$linkref_ link] info vars] # # adjust the string to have a trailing '_' # because all instvars are constructed that way # if { [string last _ $var] != ( [string length $var] - 1) } { set var ${var}_ } if { $var == "queue-limit_" } { set var "limit_" } if { [lsearch $qvars $var] >= 0 } { # set a queue var return [$queue_ set $var] } elseif { [lsearch $linkvars $var] >= 0 } { # set a link OTcl var return [$linkref_ set $var] } elseif { [lsearch $linkdelayvars $var] >= 0 } { # set a linkdelay object var return [[$linkref_ link] set $var] } else { puts stderr "linkHelper warning: couldn't set unknown variable $var" return "" } return "" } # # gross, but works: # # In ns-1 queues were a sublass of link, and this compat # code carries around a 'linkHelper' as the returned object # when you do a [ns link $r1 $r2] or a [ns link $r1 $r2 $qtype] # command. So, operations on this object could have been # either link ops or queue ops in ns-1. It is possible to see # whether an Otcl class or object supports certain commands # but it isn't possible to look inside a C++ implemented object # (i.e. into it's cmd function) to see what it supports. Instead, # arrange to catch the exception generated while trying into a # not-implemented method in a C++ object. # linkHelper instproc try { obj operation argv } { set op [eval list $obj $operation $argv] set ocl [$obj info class] set iprocs [$ocl info instcommands] set oprocs [$obj info commands] # if it's a OTcl-implemented method we see it in info # and thus don't need to catch it if { $operation != "cmd" } { if { [lsearch $iprocs $operation] >= 0 } { return [eval $op] } if { [lsearch $oprocs $operation] >= 0 } { return [eval $op] } } #catch the c++-implemented method in case it's not there #ret will contain error string or return string # value of catch operation will be 1 on error if [catch $op ret] { return -1 } return $ret } # so, try to invoke the op on a queue and if that causes # an exception (a missing function hopefully) try it on # the link instead # # we need to override 'TclObject instproc unknown args' # (well, at least we did), because it was coded such that # if a command() function didn't exist, an exit 1 happened # linkHelper instproc unknown { m args } { # method could be in: queue, link, linkdelay # or any of its command procedures # note that if any of those have errors in them # we can get a general error by ending up at the end here $self instvar linkref_ queue_ set oldbody [TclObject info instbody unknown] TclObject instproc unknown args { if { [lindex $args 0] == "cmd" } { puts stderr "Can't dispatch $args" exit 1 } eval $self cmd $args } # try an OTcl queue then the underlying queue object set rval [$self try $queue_ $m $args] if { $rval != -1 } { TclObject instproc unknown args $oldbody return $rval } set rval [$self try $queue_ cmd [list $m $args]] if { $rval != -1 } { TclObject instproc unknown args $oldbody return $rval } set rval [$self try $linkref_ $m $args] if { $rval != -1 } { TclObject instproc unknown args $oldbody return $rval } set rval [$self try $linkref_ cmd [list $m $args]] if { $rval != -1 } { TclObject instproc unknown args $oldbody return $rval } set dlink [$linkref_ link] set rval [$self try $dlink $m $args] if { $rval != -1 } { TclObject instproc unknown args $oldbody return $rval } set rval [$self try $dlink cmd [list $m $args]] if { $rval != -1 } { TclObject instproc unknown args $oldbody return $rval } TclObject instproc unknown args $oldbody puts stderr "Unknown operation $m or subbordinate operation failed" exit 1 } linkHelper instproc stat { classid item } { $self instvar linkref_ set qmon [$linkref_ set qMonitor_] # note: in ns-1 the packets/bytes stats are counts # of the number of *departures* at a link/queue # if { $item == "packets" } { return [$qmon pkts $classid] } elseif { $item == "bytes" } { return [$qmon bytes $classid] } elseif { $item == "drops"} { return [$qmon drops $classid] } elseif { $item == "mean-qdelay" } { set dsamp [$qmon get-class-delay-samples $classid] if { [$dsamp cnt] > 0 } { return [$dsamp mean] } else { return NaN } } else { puts stderr "linkHelper: unknown stat op $item" exit 1 } } linkHelper instproc integral { itype } { $self instvar linkref_ if { $itype == "qsize" } { set integ [$linkref_ set bytesInt_] } elseif { $itype == "qlen" } { set integ [$linkref_ set pktsInt_] } return [$integ set sum_] } # # end linkHelper # set classMap_(tcp) Agent/TCP set classMap_(tcp-reno) Agent/TCP/Reno set classMap_(tcp-vegas) Agent/TCP/Vegas set classMap_(tcp-full) Agent/TCP/FullTcp set classMap_(fulltcp) Agent/TCP/FullTcp set classMap_(tcp-fack) Agent/TCP/Fack set classMap_(facktcp) Agent/TCP/Fack set classMap_(tcp-newreno) Agent/TCP/Newreno set classMap_(tcpnewreno) Agent/TCP/Newreno set classMap_(cbr) Agent/CBR set classMap_(tcp-sink) Agent/TCPSink set classMap_(tcp-sack1) Agent/TCP/Sack1 set classMap_(sack1-tcp-sink) Agent/TCPSink/Sack1 set classMap_(tcp-sink-da) Agent/TCPSink/DelAck set classMap_(sack1-tcp-sink-da) Agent/TCPSink/Sack1/DelAck set classMap_(sink) Agent/TCPSink set classMap_(delsink) Agent/TCPSink/DelAck set classMap_(sacksink) Agent/TCPSink ; # sacksink becomes TCPSink here set classMap_(loss-monitor) Agent/LossMonitor set classMap_(class) CBQClass set classMap_(ivs) Agent/IVS/Source set classMap_(trace) Trace set classMap_(srm) Agent/SRM $self instvar queueMap_ set queueMap_(drop-tail) DropTail set queueMap_(sfq) SFQ set queueMap_(red) RED set queueMap_(cbq) CBQ set queueMap_(wrr-cbq) CBQ/WRR $self trace_old_defaults # # this is a hack to deal with the unfortunate name # of a CBQ class chosen in ns-1 (i.e. "class"). # # the "new" procedure in Tcl/tcl-object.tcl will end # up calling: # eval class create id "" # so, catch this here... yuck global tcl_version if {$tcl_version < 8} { set class_name "class" } else { set class_name "::class" } proc $class_name args { set arglen [llength $args] if { $arglen < 2 } { return } set op [lindex $args 0] set id [lindex $args 1] if { $op != "create" } { error "ns-v1 compat: malformed class operation: op $op" return } # # we need to prevent a "phantom" argument from # showing up in the argument list to [CBQClass create], # so, don't pass an empty string if we weren't # called with one! # # by calling through [eval], we suppress any {} that # might result from the [lrange ...] below # eval CBQClass create $id [lrange $args 2 [expr $arglen - 1]] } } # # links in ns-1 had support for statistics collection... # $link stat packets/bytes/drops # OldSim instproc simplex-link-compat { n1 n2 bw delay qtype } { set linkhelp [$self link-threeargs $n1 $n2 $qtype] $linkhelp set bandwidth_ $bw $linkhelp set delay_ $delay } OldSim instproc duplex-link-compat { n1 n2 bw delay type } { ns simplex-link-compat $n1 $n2 $bw $delay $type ns simplex-link-compat $n2 $n1 $bw $delay $type } OldSim instproc get-queues { n1 n2 } { $self instvar link_ set n1 [$n1 id] set n2 [$n2 id] return "[$link_($n1:$n2) queue] [$link_($n2:$n1) queue]" } OldSim instproc create-agent { node type pktClass } { $self instvar classMap_ if ![info exists classMap_($type)] { puts stderr \ "backward compat bug: need to update classMap for $type" exit 1 } set agent [new $classMap_($type)] # new mapping old class -> flowid $agent set fid_ $pktClass $self attach-agent $node $agent # This has been replaced by TclObject instproc get. -johnh, 10-Sep-97 # # $agent proc get var { # return [$self set $var] # } return $agent } OldSim instproc agent { type node } { return [$self create-agent $node $type 0] } OldSim instproc create-connection \ { srcType srcNode sinkType sinkNode pktClass } { set src [$self create-agent $srcNode $srcType $pktClass] set sink [$self create-agent $sinkNode $sinkType $pktClass] $self connect $src $sink return $src } proc ns_connect { src sink } { return [ns connect $src $sink] } # # return helper object for backward compat of "ns link" command # OldSim instproc link args { set nargs [llength $args] set arg0 [lindex $args 0] set arg1 [lindex $args 1] if { $nargs == 2 } { return [$self link-twoargs $arg0 $arg1] } elseif { $nargs == 3 } { return [$self link-threeargs $arg0 $arg1 [lindex $args 2]] } } OldSim instproc link-twoargs { n1 n2 } { $self instvar LH_ if ![info exists LH_($n1:$n2)] { set LH_($n1:$n2) 1 linkHelper LH_:$n1:$n2 $n1 $n2 } return LH_:$n1:$n2 } OldSim instproc link-threeargs { n1 n2 qtype } { # new link with 0 bandwidth and 0 delay $self simplex-link $n1 $n2 0 0 $qtype return [$self link-twoargs $n1 $n2] } OldSim instproc trace {} { return [new traceHelper] } OldSim instproc random { seed } { return [ns-random $seed] } proc ns_simplex { n1 n2 bw delay type } { # this was never used in ns-1 puts stderr "ns_simplex: no backward compat" exit 1 } proc ns_duplex { n1 n2 bw delay type } { ns duplex-link-compat $n1 $n2 $bw $delay $type return [ns get-queues $n1 $n2] } # # Create a source/sink connection pair and return the source agent. # proc ns_create_connection { srcType srcNode sinkType sinkNode pktClass } { ns create-connection $srcType $srcNode $sinkType \ $sinkNode $pktClass } # # Create a source/sink CBR pair and return the source agent. # proc ns_create_cbr { srcNode sinkNode pktSize interval fid } { set s [ns create-connection cbr $srcNode loss-monitor \ $sinkNode $fid] $s set interval_ $interval $s set packetSize_ $pktSize return $s } # # compat code for CBQ # proc ns_create_class { parent borrow allot maxidle notused prio depth xdelay } { set cl [new CBQClass] # # major hack: if the prio is 8 (the highest in ns-1) it's # an internal node, hence no queue disc if { $prio < 8 } { set qtype [CBQClass set def_qtype_] set q [new Queue/$qtype] $cl install-queue $q } set depth [expr $depth + 1] if { $borrow == "none" } { set borrowok false } elseif { $borrow == $parent } { set borrowok true } else { puts stderr "CBQ: borrowing from non-parent not supported" exit 1 } $cl setparams $parent $borrowok $allot $maxidle $prio $depth $xdelay return $cl } proc ns_create_class1 { parent borrow allot maxidle notused prio depth xdelay Mb } { set cl [ns_create_class $parent $borrow $allot $maxidle $notused $prio $depth $xdelay] ns_class_maxIdle $cl $allot $maxidle $prio $Mb return $cl } proc ns_class_params { cl parent borrow allot maxidle notused prio depth xdelay Mb } { set depth [expr $depth + 1] if { $borrow == "none" } { set borrowok false } elseif { $borrow == $parent } { set borrowok true } else { puts stderr "CBQ: borrowing from non-parent not supported" exit 1 } $cl setparams $parent $borrowok $allot $maxidle $prio $depth $xdelay ns_class_maxIdle $cl $allot $maxidle $prio $Mb return $cl } # # If $maxIdle is "auto", set maxIdle to Max[t(1/p-1)(1-g^n)/g^n, t(1-g)]. # For p = allotment, t = packet transmission time, g = weight for EWMA. # The parameter t is calculated for a medium-sized 1000-byte packet. # proc ns_class_maxIdle { cl allot maxIdle priority Mbps } { if { $maxIdle == "auto" } { set g 0.9375 set n [expr 8 * $priority] set gTOn [expr pow($g, $n)] set first [expr ((1/$allot) - 1) * (1-$gTOn) / $gTOn ] set second [expr (1 - $g)] set packetsize 1000 set t [expr ($packetsize * 8)/($Mbps * 1000000) ] if { $first > $second } { $cl set maxidle_ [expr $t * $first] } else { $cl set maxidle_ [expr $t * $second] } } else { $cl set maxidle_ $maxIdle } return $cl } # # backward compat for agent methods that were replaced # by OTcl instance variables # Agent instproc connect d { $self set dst_ $d } # XXX changed call from "handle" to "recv" Agent/Message instproc recv msg { $self handle $msg } #Renamed variables in Queue/RED and Queue/DropTail Queue/RED proc set { var {arg ""} } { if { $var == "queue-in-bytes_" } { warn "Warning: use `queue_in_bytes_' rather than `queue-in-bytes_'" set var "queue_in_bytes_" } elseif { $var == "drop-tail_" } { warn "Warning: use `drop_tail_' rather than `drop-tail_'" set var "drop_tail_" } elseif { $var == "drop-front_" } { warn "Warning: use `drop_front_' rather than `drop-front_'" set var "drop_front_" } elseif { $var == "drop-rand_" } { warn "Warning: use `drop_rand_' rather than `drop-rand_'" set var "drop_rand_" } elseif { $var == "ns1-compat_" } { warn "Warning: use `ns1_compat_' rather than `ns1-compat_'" set var "ns1_compat_" } eval $self next $var $arg } Queue/DropTail proc set { var {arg ""} } { if { $var == "drop-front_" } { warn "Warning: use `drop_front_' rather than `drop-front_'" set var "drop_front_" } eval $self next $var $arg } # # 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. # # @(#) $Header$ # Class NamSimulator -superclass Simulator NamSimulator instproc init { outfile } { $self next $self set chan_ [open $outfile w] $self set nam_config_ "proc nam_config {net} \{\n" } NamSimulator instproc node {} { set n [$self next] set namcmd "\t\$net node [$n id] circle\n" $self instvar nam_config_ set nam_config_ $nam_config_$namcmd return $n } NamSimulator instproc simplex-link { n1 n2 bw delay type } { $self next $n1 $n2 $bw $delay $type set id1 [$n1 id] set id2 [$n2 id] $self instvar nam_config_ link_mark_ if { ![info exists link_mark_($id2,$id1)] } { set namcmd "\tauto_mklink \$net $id1 $id2 $bw $delay \n" set nam_config_ $nam_config_$namcmd } set link_mark_($id1,$id2) 1 set namcmd "\t\$net queue $id1 $id2 0.5 \n" set nam_config_ $nam_config_$namcmd } NamSimulator instproc config_dump {} { $self instvar nam_config_ set nam_config_ $nam_config_\}\n $self instvar chan_ puts $chan_ $nam_config_ close $chan_ } # # 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. # # @(#) $Header$ # # # set up the packet format for the simulation # (initial version) # PacketHeaderManager set hdrlen_ 0 #XXX could potentially get rid of this by searching having a more # uniform offset concept... ## CMU extension foreach cl [PacketHeader info subclass] { PacketHeaderManager set vartab_($cl) "" } # If you need to save some memory, you can disable unneeded packet headers # by commenting them out from the list below foreach pair { { Common off_cmn_ } { Mac off_mac_ } { Mac802_11 off_mac802_11_ } { LL off_ll_ } { ARP off_arp_ } { Snoop off_snoop_ } { SR off_SR_ } { IP off_ip_ } { TCP off_tcp_ } { TCPA off_tcpasym_ } { Flags off_flags_ } { TORA off_TORA_ } { AODV off_AODV_ } { IMEP off_IMEP_ } { RTP off_rtp_ } { Message off_msg_ } { IVS off_ivs_ } { rtProtoDV off_DV_ } { CtrMcast off_CtrMcast_ } { mcastCtrl off_mcast_ctrl_ } { aSRM off_asrm_ } { SRM off_srm_ } { SRMEXT off_srm_ext_} { Resv off_resv_} { HttpInval off_inv_} { IPinIP off_ipinip_} { MIP off_mip_} { MFTP off_mftp_ } { Encap off_encap_ } { RAP off_rap_ } { UMP off_ump_ } { TFRC off_tfrm_} { Ping off_ping_} } { set cl PacketHeader/[lindex $pair 0] set var [lindex $pair 1] PacketHeaderManager set vartab_($cl) $var } proc PktHdr_offset {hdrName {field ""}} { set var [PacketHeaderManager set vartab_($hdrName)] set offset [TclObject set $var] if {$field != ""} { incr offset [$hdrName set offset_($field)] } return $offset } Simulator instproc create_packetformat { } { PacketHeaderManager instvar vartab_ set pm [new PacketHeaderManager] foreach cl [PacketHeader info subclass] { if {[info exists vartab_($cl)] && $vartab_($cl) != ""} { set off [$pm allochdr [lindex [split $cl /] 1]] set var [PacketHeaderManager set vartab_($cl)] TclObject set $var $off $cl offset $off } } $self set packetManager_ $pm } PacketHeaderManager instproc allochdr cl { set size [PacketHeader/$cl set hdrlen_] $self instvar hdrlen_ set NS_ALIGN 8 # round up to nearest NS_ALIGN bytes # (needed on sparc/solaris) set incr [expr ($size + ($NS_ALIGN-1)) & ~($NS_ALIGN-1)] set base $hdrlen_ incr hdrlen_ $incr return $base } # # 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. # # @(#) $Header$ # # # This file contains auxillary support for CBQ and CBQ links, and # some embryonic stuff for Queue Monitors. -KF # # # CBQ # # # set up the baseline CBQ or CBQ/WRR object # baseline object contains only an empty classifier # and the scheduler (CBQ or CBQ/WRR) object itself # # After initialized, the structure is as follows: # # # head_-> (classifier) # queue_-> (cbq) ==> link_ # drophead_ -> (connector) ==> nullAgent # # == is data flow # -- is a pointer reference # Class CBQLink -superclass SimpleLink CBQLink instproc init { src dst bw delay q cl {lltype "DelayLink"} } { $self next $src $dst $bw $delay $q $lltype ; # SimpleLink ctor $self instvar head_ queue_ link_ $self instvar classifier_ ; # not found in a SimpleLink # $self instvar drophead_ ; # not found in a SimpleLink $queue_ link $link_ ; # queue_ set by SimpleLink ctor, CBQ needs $link_ set classifier_ $cl $head_ target $classifier_ # set drophead_ [new Connector] # $drophead_ target [[Simulator instance] set nullAgent_] # # the following is merely to inject 'algorithm_' in the # $queue_ class' name space. It is not used in ns-2, but # is needed here for the compat code to recognize that # 'set algorithm_ foo' commands should work # set defalg [Queue/CBQ set algorithm_] $queue_ set algorithm_ $defalg # this part actually sets the default $queue_ algorithm $defalg } # # set up a trace. Calling the SimpleLink version is harmless, but doesn't # do exactly what we need # #CBQLink instproc trace { ns f } { # $self next $ns $f # $self instvar drpT_ drophead_ # set nxt [$drophead_ target] # $drophead_ target $drpT_ # $drpT_ target $nxt #} # # set up monitors. Once again, the base version is close, but not # exactly what's needed # #CBQLink instproc attach-monitors { isnoop osnoop dsnoop qmon } { # $self next $isnoop $osnoop $dsnoop $qmon # $self instvar drophead_ # set nxt [$drophead_ target] # $drophead_ target $dsnoop # $dsnoop target $nxt #} CBQLink instproc classifier {} { $self instvar classifier_ return $classifier_ } # # # for flow-id based classification, bind c # CBQClass to a given flow id # (or range) # # OTcl usage: # bind $cbqclass id # or # bind $cbqclass idstart idend # # these use flow id's as id's # CBQLink instproc bind args { # this is to perform '$cbqlink bind $cbqclass id' # and '$cbqlink insert $cbqclass bind $cbqclass idstart idend' $self instvar classifier_ set nargs [llength $args] set cbqcl [lindex $args 0] set a [lindex $args 1] if { $nargs == 3 } { set b [lindex $args 2] } else { set b $a } # bind the class to the flow id's [a..b] while { $a <= $b } { # first install the class to get its slot number # use the flow id as the hash bucket set slot [$classifier_ installNext $cbqcl] $classifier_ set-hash auto 0 0 $a $slot incr a } } # # insert the class into the link # each class will have an associated queue # we must create a set of snoop qs with an associated qmon # which cbq uses to monitor demand. The qmon may either be # given, or defaults to just a QueueMonitor type. # # Otcl usage: # insert $cbqclass # insert $cbqclass $qmon # # the two different usages are used to make backward compat with # ns-1 easier, since in ns-2, insert was in c++ # # general idea: # pkt--> Classifier --> CBQClass --> snoopin --> qdisc --> snoopout --> CBQ # CBQLink instproc insert args { # queue_ refers to the cbq object $self instvar queue_ drophead_ link_ set nargs [llength $args] set cbqcl [lindex $args 0] set qdisc [$cbqcl qdisc] if { $nargs == 1 } { set qmon [new QueueMonitor] } else { set qmon [lindex $args 1] } # qdisc can be null for internal classes if { $qmon == "" } { error "CBQ requires a q-monitor for class $cbqcl" } if { $qdisc != "" } { # create in, out, and drop snoop queues # and attach them to the same monitor # this is used by CBQ to assess demand # (we don't need bytes/pkt integrator or stats here) set in [new SnoopQueue/In] set out [new SnoopQueue/Out] set drop [new SnoopQueue/Drop] $in set-monitor $qmon $out set-monitor $qmon $drop set-monitor $qmon # output of cbqclass -> snoopy inq $in target $qdisc $cbqcl target $in # drop from qdisc -> snoopy dropq # snoopy dropq's target is overall cbq drop target $qdisc drop-target $drop $drop target $drophead_ # output of queue -> snoopy outq # output of snoopy outq is cbq $qdisc target $out $out target $queue_ # tell this class about its new queue monitor $cbqcl qmon $qmon } $cbqcl instvar maxidle_ if { $maxidle_ == "auto" } { $cbqcl automaxidle [$link_ set bandwidth_] \ [$queue_ set maxpkt_] set maxidle_ [$cbqcl set maxidle_] } $cbqcl maxidle $maxidle_ # tell cbq about this class # (this also tells the cbqclass about the cbq) $queue_ insert-class $cbqcl } # # procedures on a cbq class # CBQClass instproc init {} { $self next $self instvar automaxidle_gain_ set automaxidle_gain_ [$class set automaxidle_gain_] } CBQClass instproc automaxidle { linkbw maxpkt } { $self instvar automaxidle_gain_ maxidle_ $self instvar priority_ set allot [$self allot] set g $automaxidle_gain_ set n [expr 8 * $priority_] if { $g == 0 || $allot == 0 || $linkbw == 0 } { set maxidle_ 0.0 return } set gTOn [expr pow($g, $n)] set first [expr ((1/$allot) - 1) * (1-$gTOn) / $gTOn ] set second [expr (1 - $g)] set t [expr ($maxpkt * 8.0)/$linkbw] if { $first > $second } { set maxidle_ [expr $t * $first] } else { set maxidle_ [expr $t * $second] } return $maxidle_ } CBQClass instproc setparams { parent okborrow allot maxidle prio level xdelay } { $self allot $allot $self parent $parent $self set okborrow_ $okborrow $self set maxidle_ $maxidle $self set priority_ $prio $self set level_ $level $self set extradelay_ $xdelay return $self } # # insert a queue into a CBQ class: # arrange for the queue to initally be blocked # and for it to block when resumed # (provides flow control on the queue) CBQClass instproc install-queue q { $q set blocked_ true $q set unblock_on_resume_ false $self qdisc $q } # # QueueMonitor # QueueMonitor instproc reset {} { $self instvar size_ pkts_ $self instvar parrivals_ barrivals_ $self instvar pdepartures_ bdepartures_ $self instvar pdrops_ bdrops_ # don't reset size_ and pkts_ here # because they are not cumulative measurements # the way the following values are set parrivals_ 0 set barrivals_ 0 set pdepartures_ 0 set bdepartures_ 0 set pdrops_ 0 set bdrops_ 0 set bint [$self get-bytes-integrator] if { $bint != "" } { $bint reset } set pint [$self get-pkts-integrator] if { $pint != "" } { $pint reset } set samp [$self get-delay-samples] if { $samp != "" } { $samp reset } } QueueMonitor/ED instproc reset {} { $self next $self instvar epdrops_ ebdrops_ set epdrops_ 0 set ebdrops_ 0 } Class AckReconsClass -superclass Agent AckReconsControllerClass instproc demux { src dst } { $self instvar reconslist_ queue_ set addr $src:$dst if { ![info exists reconslist_($addr)] } { set recons [new Agent/AckReconsClass $src $dst] $recons target $queue_ set reconslist_($addr) $recons } # return an ack reconstructor object return $reconslist_($addr) } # Calculate number and spacing of acks to be sent # deltaAckThresh_ = threshold after which reconstructor kicks in # ackInterArr_ = estimate of arrival rate of acks ("counting process") # ackSpacing_ = duration in time between acks sent by reconstructor # delack_ = generate an ack at least every delack_ acks at reconstructor Agent/AckReconsClass instproc spacing { ack } { $self instvar ackInterArr_ ackSpacing_ delack_ \ lastAck_ lastRealAck_ lastRealTime_ adaptive_ size_ global ns set deltaTime [expr [$ns now] - $lastRealTime_] set deltaAck [expr $ack - $lastAck_] if {$adaptive_} { set bw [expr $deltaAck*$size_/$deltaTime] set ackSpacing_ $ackInterArr_ if { $deltaAck > 0 } { # set ackSpacing_ [expr $ackInterArr_*$delack_/$deltaAck] } } else { set deltaT [expr $deltaTime / ($deltaAck/$delack_ +1)] set ackSpacing_ $deltaT } } # Estimate rate at which acks are arriving Agent/AckReconsClass instproc ackbw {ack time} { $self instvar ackInterArr_ lastRealTime_ lastRealAck_ alpha_ set sample [expr $time - $lastRealTime_] # EWMA set ackInterArr_ [expr $alpha_*$sample + (1-$alpha_)*$ackInterArr_] } Class Classifier/Hash/Fid/FQ -superclass Classifier/Hash/Fid Classifier/Hash/Fid/FQ instproc unknown-flow { src dst fid } { $self instvar fq_ $fq_ new-flow $src $dst $fid } Class FQLink -superclass SimpleLink FQLink instproc init { src dst bw delay q } { $self next $src $dst $bw $delay $q $self instvar link_ queue_ head_ toNode_ ttl_ classifier_ \ nactive_ $self instvar drophead_ ;# idea stolen from CBQ and Kevin set nactive_ 0 set classifier_ [new Classifier/Hash/Fid/FQ 33] $classifier_ set fq_ $self #$self add-to-head $classifier_ $head_ target $classifier_ # XXX # put the ttl checker after the delay # so we don't have to worry about accounting # for ttl-drops within the trace and/or monitor # fabric # $queue_ set secsPerByte_ [expr 8.0 / [$link_ set bandwidth_]] } FQLink instproc new-flow { src dst fid } { $self instvar classifier_ nactive_ queue_ link_ drpT_ incr nactive_ set type [$class set queueManagement_] set q [new Queue/$type] #XXX yuck if { $type == "RED" } { set bw [$link_ set bandwidth_] $q set ptc_ [expr $bw / (8. * [$q set mean_pktsize_])] } $q drop-target $drpT_ set slot [$classifier_ installNext $q] $classifier_ set-hash auto $src $dst $fid $slot $q target $queue_ $queue_ install $fid $q } #XXX ask Kannan why this isn't in otcl base class. FQLink instproc up? { } { return up } # # 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. # # @(#) $Header$ # Trace instproc init type { $self next $type $self instvar type_ set type_ $type } Trace instproc format args { # The strange puts construction below helps us write formats such as # $traceObject format {$src_} {$dst_} # that will then put the source or destination id in the desired place. $self instvar type_ fp_ src_ dst_ if [info exists fp_] { set ns [Simulator instance] puts $fp_ [eval list $type_ [$ns now] [eval concat $args]] } } Trace instproc attach fp { $self instvar fp_ set fp_ $fp $self cmd attach $fp_ } Class Trace/Hop -superclass Trace Trace/Hop instproc init {} { $self next "h" } Class Trace/Enque -superclass Trace Trace/Enque instproc init {} { $self next "+" } Trace/Deque instproc init {} { $self next "-" } # Next two are for SessionSim's packet traces Class Trace/SessEnque -superclass Trace Trace/SessEnque instproc init {} { $self next "E" ;# Should use '='? :) } Class Trace/SessDeque -superclass Trace Trace/SessDeque instproc init {} { $self next "D" ;# Should use '_'? } Class Trace/Recv -superclass Trace Trace/Recv instproc init {} { $self next "r" } Class Trace/Drop -superclass Trace Trace/Drop instproc init {} { $self next "d" } Class Trace/Generic -superclass Trace Trace/Generic instproc init {} { $self next "v" } # var trace shouldn't be derived here because it shouldn't be a connector # it's here only for backward compatibility Class Trace/Var -superclass Trace Trace/Var instproc init {} { $self next "f" } # Some pretty printing routines for generic use... proc f-time t { # format time format "%7.4f" $t } proc f-node n { # format node id... set node [expr $n >> 8] set port [expr $n & 0xff] return "$node.$port" } proc gc o { set ret "NULL_OBJECT" if { $o != "" } { set ret "" foreach i $o { if ![catch "$i info class" val] { lappend ret $val } } } set ret } Node instproc tn {} { $self instvar id_ return "${self}(id $id_)" } Simulator instproc gen-map {} { # Did you ever see such uglier code? duh? # $self instvar Node_ link_ MobileNode_ set nn [Node set nn_] for {set i 0} {$i < $nn} {incr i} { if ![info exists Node($i)] { incr i continue } puts "Node [$n tn]" foreach nc [$n info vars] { switch $nc { ns_ continue id_ continue neighbor_ continue agents_ continue routes_ continue np_ continue default { if [$n array exists $nc] { puts "\t\t$nc\t[$n array get $nc]" } else { set v [$n set $nc] puts "\t\t$nc${v}([gc $v])" } } } } # Would be nice to dump agents attached to the dmux here? if {[llength [$n set agents_]] > 0} { puts "\n\tAgents at node (possibly in order of creation):" foreach a [$n set agents_] { puts "\t\t$a\t[gc $a]\t\tportID: [$a set portID_]([$a set addr_])" } } puts "" foreach li [array names link_ [$n id]:*] { set L [split $li :] set nbr [[$self get-node-by-id [lindex $L 1]] entry] set ln $link_($li) puts "\tLink $ln, fromNode_ [[$ln set fromNode_] tn] -> toNode_ [[$ln set toNode_] tn]" puts "\tComponents (in order) head first" for {set c [$ln head]} {$c != $nbr} {set c [$c target]} { puts "\t\t$c\t[gc $c]" } } puts "---" } } Simulator instproc maybeEnableTraceAll {obj args} { foreach {file tag} { traceAllFile_ {} namtraceAllFile_ nam } { $self instvar $file if [info exists $file] { $obj trace [set $file] $args $tag } } } # # 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. # # @(#) $Header$ # #Code to generate random numbers here proc exponential {args} { global defaultRNG eval [list $defaultRNG exponential] $args } proc uniform {args} { global defaultRNG eval [list $defaultRNG uniform] $args } proc integer {args} { global defaultRNG eval [list $defaultRNG integer] $args } RNG instproc init {} { $self next #z2 used to create a normal distribution from a uniform distribution #using the polar method $self instvar z2 set z2 0 } RNG instproc uniform {a b} { expr $a + (($b - $a) * ([$self next-random] * 1.0 / 0x7fffffff)) } RNG instproc integer k { expr [$self next-random] % abs($k) } RNG instproc exponential {{mu 1.0}} { expr - $mu * log(([$self next-random] + 1.0) / (0x7fffffff + 1.0)) } #RNG instproc normal {a1 a2 } { # $self instvar z2 # if {$z2 !=0 } { # set z1 $z2 # set z2 0 # } else { # set w 1 # while { $w >= 1.0 } { # set v1 [expr 2.0 * ([$self next-random] *1.0/0x7fffffff) - 1.0] # set v2 [expr 2.0 * ([$self next-random] *1.0/0x7fffffff) - 1.0] # set w [expr ($v1*$v1+$v2*$v2)] # } # set w [expr sqrt((-2.0*log($w))/$w)] # set z1 [expr $v1*$w] # set z2 [expr $v2*$w] # } # expr $a1 + $z1 *$a2 #} #RNG instproc lognormal {med stddev } { # expr $med *exp($stddev*[$self normal 0.0 1.0]) #} RandomVariable instproc test count { for {set i 0} {$i < $count} {incr i} { puts stdout [$self value] } } set defaultRNG [new RNG] $defaultRNG seed 1 $defaultRNG default # # Because defaultRNG is not a bound variable but is instead # just copied into C++, we enforce this. # (A long-term solution be to make defaultRNG bound.) # --johnh, 30-Dec-97 # trace variable defaultRNG w { abort "cannot update defaultRNG once assigned"; } # Trace driven random variable. # Polly Huang, March 4 1999 Class RandomVariable/TraceDriven -superclass RandomVariable RandomVariable/TraceDriven instproc init {} { $self instvar filename_ file_ } RandomVariable/TraceDriven instproc value {} { $self instvar file_ filename_ if ![info exist file_] { if [info exist filename_] { set file_ [open $filename_ r] } else { puts "RandomVariable/TraceDriven: Filename is not given" exit 0 } } if ![eof $file_] { gets $file_ tmp return $tmp } else { close $file_ puts "Error: RandomVariable/TraceDriven: Reached the end of the trace fi le " exit 0 } } # # 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. # # @(#) $Header$ # # # OTcl methods for the Agent base class # # # The following overload was added to inform users of the backward # compatibility issues resulted from having a 32-bit addressing space. # Agent instproc set args { if { [lindex $args 0] == "dst_" } { puts "Warning dst_ is no longer being supported in NS. $args" puts "Use dst_addr_ and dst_port_ instead" $self instvar dst_addr_ dst_port_ set addr [lindex $args 1] set baseAddr [Simulator set McastBaseAddr_] if { $addr >= $baseAddr } { $self set dst_addr_ $addr $self set dst_port_ 0 } else { $self set dst_addr_ [expr ($addr >> 8) ] $self set dst_port_ [expr ($addr % 256) ] exit } return } eval $self next $args } Agent instproc port {} { $self instvar agent_port_ return $agent_port_ } # # Lower 8 bits of dst_ are portID_. this proc supports setting the interval # for delayed acks # Agent instproc dst-port {} { $self instvar dst_port_ return [expr $dst_port_] } # # Add source of type s_type to agent and return the source # Source objects are obsolete; use attach-app instead # Agent instproc attach-source {s_type} { set source [new Source/$s_type] $source attach $self $self set type_ $s_type return $source } # # Add application of type s_type to agent and return the app # Note that s_type must be defined as a packet type in packet.h # Agent instproc attach-app {s_type} { set app_ [new Application/$s_type] $app_ attach-agent $self $self set type_ $s_type return $app_ } # # Attach tbf to an agent # Agent instproc attach-tbf { tbf } { $tbf target [$self target] $self target $tbf } # # OTcl support for classes derived from Agent # Class Agent/Null -superclass Agent Agent/Null instproc init args { eval $self next $args } Agent/LossMonitor instproc log-loss {} { } #Signalling agent attaches tbf differently as none of its signalling mesages #go via the tbf Agent/CBR/UDP/SA instproc attach-tbf { tbf } { $tbf target [$self target] $self target $tbf $self ctrl-target [$tbf target] } # # A lot of agents want to store the maxttl locally. However, # setting a class variable based on the Agent::ttl_ variable # does not help if the user redefines Agent::ttl_. Therefore, # Agents interested in the maxttl_ should call this function # with the name of their class variable, and it is set to the # maximum of the current/previous value. # # The function itself returns the value of ttl_ set. # # I use this function from agent constructors to set appropriate vars: # for instance to set Agent/rtProto/DV::INFINITY, or # Agent/SRM/SSM::ttlGroupScope_ # Agent proc set-maxttl {objectOrClass var} { if { [catch "$objectOrClass set $var" value] || \ ($value < [Agent set ttl_]) } { $objectOrClass set $var [Agent set ttl_] } $objectOrClass set $var } # # Full Tcp constructors for other than the baseline Reno # implementation # Agent/TCP/FullTcp/Tahoe instproc init {} { $self next $self instvar reno_fastrecov_ set reno_fastrecov_ false } Agent/TCP/FullTcp/Sack instproc init {} { $self next $self instvar reno_fastrecov_ maxburst_ open_cwnd_on_pack_ set reno_fastrecov_ false set maxburst_ 5 set open_cwnd_on_pack_ false } Agent/TCP/FullTcp/Newreno instproc init {} { $self next $self instvar open_cwnd_on_pack_ set open_cwnd_on_pack_ false } #Agent instproc init args { # $self next $args #} #Agent/rtProto instproc init args { # puts "DOWN HERE 2" # $self next $args #} #Agent/rtProto/TORA -superclass Agent Agent/TORA instproc init args { $self next $args } Agent/TORA set sport_ 0 Agent/TORA set dport_ 0 Agent/AODV instproc init args { $self next $args } Agent/AODV set sport_ 0 Agent/AODV set dport_ 0 # # 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. # # @(#) $Header$ # Simulator instproc rtproto {proto args} { $self instvar routingTable_ if {$proto == "Algorithmic"} { set routingTable_ [new RouteLogic/Algorithmic] } eval [$self get-routelogic] register $proto $args } Simulator instproc get-routelogic {} { $self instvar routingTable_ if ![info exists routingTable_] { set routingTable_ [new RouteLogic] } return $routingTable_ } Simulator instproc dump-routelogic-nh {} { $self instvar routingTable_ Node_ link_ if ![info exists routingTable_] { puts "error: routing table is not computed yet!" return 0 } puts "Dumping Routing Table: Next Hop Information" set n [Node set nn_] set i 0 puts -nonewline "\t" while { $i < $n } { if ![info exists Node_($i)] { incr i continue } puts -nonewline "$i\t" incr i } set i 0 while { $i < $n } { if ![info exists Node_($i)] { incr i continue } puts -nonewline "\n$i\t" set n1 $Node_($i) set j 0 while { $j < $n } { if { $i != $j } { # shortened nexthop to nh, to fit add-route in # a single line set nh [$routingTable_ lookup $i $j] if { $nh >= 0 } { puts -nonewline "$nh\t" } } else { puts -nonewline "--\t" } incr j } incr i } puts "" } Simulator instproc dump-routelogic-distance {} { $self instvar routingTable_ Node_ link_ if ![info exists routingTable_] { puts "error: routing table is not computed yet!" return 0 } # puts "Dumping Routing Table: Distance Information" set n [Node set nn_] set i 0 puts -nonewline "\t" while { $i < $n } { if ![info exists Node_($i)] { incr i continue } puts -nonewline "$i\t" incr i } set i 0 while { $i < $n } { if ![info exists Node_($i)] { incr i continue } puts -nonewline "\n$i\t" set n1 $Node_($i) set j 0 while { $j < $n } { if { $i != $j } { set nh [$routingTable_ lookup $i $j] if { $nh >= 0 } { set distance 0 set tmpfrom $i set tmpto $j while {$tmpfrom != $tmpto} { set tmpnext [$routingTable_ lookup $tmpfrom $tmpto] set distance [expr $distance + [$link_($tmpfrom:$tmpnext) cost?]] set tmpfrom $tmpnext } puts -nonewline "$distance\t" } else { puts -nonewline "0\t" } } else { puts -nonewline "0\t" } incr j } incr i } puts "" } Simulator instproc compute-routes {} { # # call hierarchical routing, if applicable # if [Simulator set EnableHierRt_] { $self compute-hier-routes } else { $self compute-flat-routes } } Simulator instproc compute-flat-routes {} { $self instvar Node_ link_ # # Compute all the routes using the route-logic helper object. # set r [$self get-routelogic] #set r [new RouteLogic] ;# must not create multiple instances foreach ln [array names link_] { set L [split $ln :] set srcID [lindex $L 0] set dstID [lindex $L 1] if { [$link_($ln) up?] == "up" } { $r insert $srcID $dstID [$link_($ln) cost?] } else { $r reset $srcID $dstID } } $r compute #$r dump $nn # # Set up each classifer (aka node) to act as a router. # Point each classifer table to the link object that # in turns points to the right node. # set i 0 set n [Node set nn_] #puts "total = $n" while { $i < $n } { if ![info exists Node_($i)] { incr i continue } set n1 $Node_($i) set j 0 while { $j < $n } { if { $i != $j } { # shortened nexthop to nh, to fit add-route in # a single line set nh [$r lookup $i $j] if { $nh >= 0 } { $n1 add-route $j [$link_($i:$nh) head] ###$n1 incr-rtgtable-size } } incr j } incr i } # delete $r #XXX used by multicast router #set routingTable_ $r ;# already set by get-routelogic } RouteLogic instproc register {proto args} { $self instvar rtprotos_ node_rtprotos_ default_node_rtprotos_ if [info exists rtprotos_($proto)] { eval lappend rtprotos_($proto) $args } else { set rtprotos_($proto) $args } if { [Agent/rtProto/$proto info procs pre-init-all] != "" } { Agent/rtProto/$proto pre-init-all $args } # # keep node to rtproto mapping # foreach $n $args < # set node_rtprotos_($n) $proto # > # if <$args == ""> < # set default_node_rtprotos_ $proto # > } # # map a node (object) to it's routing protocol # RouteLogic instproc node-to-rtproto <node> < # $self instvar node_rtprotos_ default_node_rtprotos_ # if <[info exists node_rtprotos_]> < # if <[info exists node_rtprotos_($node)]> < # return $node_rtprotos_($node) # > # > # if <[info exists default_node_rtprotos_]> < # return $default_node_rtprotos_ # > # return Static # > RouteLogic instproc configure {} { $self instvar rtprotos_ if [info exists rtprotos_] { foreach proto [array names rtprotos_] { eval Agent/rtProto/$proto init-all $rtprotos_($proto) } } else { Agent/rtProto/Static init-all # set rtprotos_(Static) 1 } } RouteLogic instproc lookup { nodeid destid } { if { $nodeid == $destid } { return $nodeid } set ns [Simulator instance] set node [$ns get-node-by-id $nodeid] if [Simulator set EnableHierRt_] { set dest [$ns get-node-by-id $destid] set nh [$self hier-lookup [$node node-addr] [$dest node-addr]] return [$ns get-node-id-by-addr $nh] } set rtobj [$node rtObject?] if { $rtobj != "" } { $rtobj lookup [$ns get-node-by-id $destid] } else { $self cmd lookup $nodeid $destid } } RouteLogic instproc notify {} { $self instvar rtprotos_ foreach i [array names rtprotos_] { Agent/rtProto/$i compute-all } foreach i [CtrMcastComp info instances] { $i notify } if { [rtObject info instances] == ""} { foreach node [[Simulator instance] all-nodes-list] { # XXX using dummy 0 for 'changes' $node notify-mcast 0 } } } # # routine to create address for hier-route-lookup at each level # i.e at level 2, address to lookup should be 0.1 and not just 1 # RouteLogic instproc append-addr {level addrstr} { if {$level != 0} { set str [lindex $addrstr 0] for {set i 1} {$i < $level} {incr i} { append str . [lindex $addrstr [expr $i]] } return $str } } # # Hierarchical routing support # Simulator instproc hier-topo {rl} { # # if topo info not found, use default values # AddrParams instvar domain_num_ cluster_num_ nodes_num_ hlevel_ ### this is bad hack..should be removed when changed to n-levels if ![info exists cluster_num_] { if {$hlevel_ > 1} { ### set default value of clusters/domain set def [AddrParams set def_clusters] puts "Default value for cluster_num set to $def\n" for {set i 0} {$i < $domain_num_} {incr i} { lappend clusters $def } } else { ### how did we reach here instead of flat routing? puts stderr "hierarchy level = 1; should use flat-rtg instead of hier-rtg" exit 1 } AddrParams set cluster_num_ $clusters } ### set default value of nodes/cluster if ![info exists nodes_num_ ] { set total_node 0 set def [AddrParams set def_nodes] puts "Default value for nodes_num set to $def\n" for {set i 0} {$i < $domain_num_} {incr i} { set total_node [expr $total_node + \ [lindex $clusters $i]] } for {set i 0} {$i < $total_node} {incr i} { lappend nodes $def } AddrParams set nodes_num_ $nodes } eval $rl send-num-of-domains $domain_num_ eval $rl send-num-of-clusters $cluster_num_ eval $rl send-num-of-nodes $nodes_num_ } Simulator instproc compute-hier-routes {} { $self instvar Node_ link_ set r [$self get-routelogic] # # send hierarchical data : # array of cluster size, #clusters, #domains # assuming 3 levels of hierarchy --> this should be extended to support # n-levels of hierarchy # #puts "Computing Hierarchical routes\n" if ![info exists link_] { return } set level [AddrParams set hlevel_] $r hlevel-is $level $self hier-topo $r foreach ln [array names link_] { set L [split $ln :] set srcID [[$self get-node-by-id [lindex $L 0]] node-addr] set dstID [[$self get-node-by-id [lindex $L 1]] node-addr] if { [$link_($ln) up?] == "up" } { $r hier-insert $srcID $dstID [$link_($ln) cost?] } else { $r hier-reset $srcID $dstID } } $r hier-compute # # Set up each classifier (n for n levels of hierarchy) for every node # set i 0 set n [Node set nn_] # #for each node # while { $i < $n } { if ![info exists Node_($i)] { incr i continue } set n1 $Node_($i) set addr [$n1 node-addr] set L [$n1 split-addrstr $addr] # # for each hierarchy level, populate classifier for that level # for {set k 0} {$k < $level} {incr k} { set csize [AddrParams elements-in-level? $addr $k] # # for each element in that level (elements maybe nodes or clusters or domains # if {$k > 0} { set prefix [$r append-addr $k $L] } for {set m 0} {$m < $csize} {incr m} { if { $m != [lindex $L $k]} { if {$k > 0} { set str $prefix append str . $m } else { set str $m } set nh [$r hier-lookup $addr $str] # add entry in clasifier only if hier-lookup # returns a value. if {$nh != -1} { set node [$self get-node-id-by-addr $nh] if { $node >= 0 } { $n1 add-hroute $str [$link_($i:$node) head] ###$n1 incr-rtgtable-size } } } } } incr i } #$self clearMemTrace; } # # debugging method to dump table (see route.cc for C++ methods) # RouteLogic instproc dump nn { set i 0 while { $i < $nn } { set j 0 while { $j < $nn } { puts "$i -> $j via [$self lookup $i $j]" incr j } incr i } } ##### # Now source the dynamic routing protocol methods # # # 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@isi.edu>. # # This file only contains the methods for dynamic routing # Check ../lib/ns-route.tcl for the Simulator routing support # set rtglibRNG [new RNG] $rtglibRNG seed 1 Class rtObject rtObject set unreach_ -1 rtObject set maxpref_ 255 rtObject proc init-all args { foreach node $args { if { [$node rtObject?] == "" } { set rtobj($node) [new rtObject $node] } } foreach node $args { ;# XXX $rtobj($node) compute-routes } } rtObject instproc init node { $self next $self instvar ns_ nullAgent_ $self instvar nextHop_ rtpref_ metric_ node_ rtVia_ rtProtos_ set ns_ [Simulator instance] set nullAgent_ [$ns_ set nullAgent_] $node init-routing $self set node_ $node foreach dest [$ns_ all-nodes-list] { set nextHop_($dest) "" if {$node == $dest} { set rtpref_($dest) 0 set metric_($dest) 0 set rtVia_($dest) "Agent/rtProto/Local" ;# make dump happy } else { set rtpref_($dest) [$class set maxpref_] set metric_($dest) [$class set unreach_] set rtVia_($dest) "" $node add-route [$dest id] $nullAgent_ } } $self add-proto Direct $node $rtProtos_(Direct) compute-routes # $self compute-routes } rtObject instproc add-proto {proto node} { $self instvar ns_ rtProtos_ set rtProtos_($proto) [new Agent/rtProto/$proto $node] $ns_ attach-agent $node $rtProtos_($proto) set rtProtos_($proto) } rtObject instproc lookup dest { $self instvar nextHop_ node_ if {![info exists nextHop_($dest)] || $nextHop_($dest) == ""} { return -1 } else { return [[$nextHop_($dest) set toNode_] id] } } rtObject instproc compute-routes {} { # choose the best route to each destination from all protocols $self instvar ns_ node_ rtProtos_ nullAgent_ $self instvar nextHop_ rtpref_ metric_ rtVia_ set protos "" set changes 0 foreach p [array names rtProtos_] { if [$rtProtos_($p) set rtsChanged_] { incr changes $rtProtos_($p) set rtsChanged_ 0 } lappend protos $rtProtos_($p) } if !$changes return set changes 0 foreach dst [$ns_ all-nodes-list] { if {$dst == $node_} continue set nh "" set pf [$class set maxpref_] set mt [$class set unreach_] set rv "" foreach p $protos { set pnh [$p set nextHop_($dst)] if { $pnh == "" } continue set ppf [$p set rtpref_($dst)] set pmt [$p set metric_($dst)] if {$ppf < $pf || ($ppf == $pf && $pmt < $mt) || $mt < 0} { set nh $pnh set pf $ppf set mt $pmt set rv $p } } if { $nh == "" } { # no route... delete any existing routes if { $nextHop_($dst) != "" } { $node_ delete-routes [$dst id] $nextHop_($dst) $nullAgent_ set nextHop_($dst) $nh set rtpref_($dst) $pf set metric_($dst) $mt set rtVia_($dst) $rv incr changes } } else { if { $rv == $rtVia_($dst) } { # Current protocol still has best route. # See if changed if { $nh != $nextHop_($dst) } { $node_ delete-routes [$dst id] $nextHop_($dst) $nullAgent_ set nextHop_($dst) $nh $node_ add-routes [$dst id] $nextHop_($dst) incr changes } if { $mt != $metric_($dst) } { set metric_($dst) $mt incr changes } if { $pf != $rtpref_($dst) } { set rtpref_($dst) $pf } } else { if { $rtVia_($dst) != "" } { set nextHop_($dst) [$rtVia_($dst) set nextHop_($dst)] set rtpref_($dst) [$rtVia_($dst) set rtpref_($dst)] set metric_($dst) [$rtVia_($dst) set metric_($dst)] } if {$rtpref_($dst) != $pf || $metric_($dst) != $mt} { # Then new prefs must be better, or # new prefs are equal, and new metrics are lower $node_ delete-routes [$dst id] $nextHop_($dst) $nullAgent_ set nextHop_($dst) $nh set rtpref_($dst) $pf set metric_($dst) $mt set rtVia_($dst) $rv $node_ add-routes [$dst id] $nextHop_($dst) incr changes } } } } foreach proto [array names rtProtos_] { $rtProtos_($proto) send-updates $changes } # # XXX # detailed multicast routing hooks must come here. # My idea for the hook will be something like: # set mrtObject [$node_ mrtObject?] # if {$mrtObject != ""} { # $mrtObject recompute-mroutes $changes # } # $changes == 0 if only interfaces changed state. Look at how # Agent/rtProto/DV handles ifsUp_ # $changes > 0 if new unicast routes were installed. # $self flag-multicast $changes } rtObject instproc flag-multicast changes { $self instvar node_ $node_ notify-mcast $changes } rtObject instproc intf-changed {} { $self instvar ns_ node_ rtProtos_ rtVia_ nextHop_ rtpref_ metric_ foreach p [array names rtProtos_] { $rtProtos_($p) intf-changed $rtProtos_($p) compute-routes } $self compute-routes } rtObject instproc dump-routes chan { $self instvar ns_ node_ nextHop_ rtpref_ metric_ rtVia_ # if {[info proc SplitObjectCompare] == ""} { # # XXX: in tcl8 we need to find this in the global namespace # if {[info proc ::SplitObjectCompare] == {}} { # puts stderr "${class}::${proc} failed. Update your TclCL library" # return # } # } if {$ns_ != ""} { set time [$ns_ now] } else { set time 0.0 } puts $chan [concat "Node:\t${node_}([$node_ id])\tat t =" \ [format "%4.2f" $time]] puts $chan " Dest\t\t nextHop\tPref\tMetric\tProto" foreach dest [lsort -command SplitObjectCompare [$ns_ all-nodes-list]] { if {[llength $nextHop_($dest)] > 1} { set p [split [$rtVia_($dest) info class] /] set proto [lindex $p [expr [llength $p] - 1]] foreach rt $nextHop_($dest) { puts $chan [format "%-5s(%d)\t%-5s(%d)\t%3d\t%4d\t %s" \ $dest [$dest id] $rt [[$rt set toNode_] id] \ $rtpref_($dest) $metric_($dest) $proto] } } elseif {$nextHop_($dest) != ""} { set p [split [$rtVia_($dest) info class] /] set proto [lindex $p [expr [llength $p] - 1]] puts $chan [format "%-5s(%d)\t%-5s(%d)\t%3d\t%4d\t %s" \ $dest [$dest id] \ $nextHop_($dest) [[$nextHop_($dest) set toNode_] id] \ $rtpref_($dest) $metric_($dest) $proto] } elseif {$dest == $node_} { puts $chan [format "%-5s(%d)\t%-5s(%d)\t%03d\t%4d\t %s" \ $dest [$dest id] $dest [$dest id] 0 0 "Local"] } else { puts $chan [format "%-5s(%d)\t%-5s(%s)\t%03d\t%4d\t %s" \ $dest [$dest id] "" "-" 255 32 "Unknown"] } } } rtObject instproc rtProto? proto { $self instvar rtProtos_ if [info exists rtProtos_($proto)] { return $rtProtos_($proto) } else { return "" } } rtObject instproc nextHop? dest { $self instvar nextHop_ $self set nextHop_($dest) } rtObject instproc rtpref? dest { $self instvar rtpref_ $self set rtpref_($dest) } rtObject instproc metric? dest { $self instvar metric_ $self set metric_($dest) } # Class rtPeer rtPeer instproc init {addr port cls} { $self next $self instvar addr_ port_ metric_ rtpref_ set addr_ $addr set port_ $port foreach dest [[Simulator instance] all-nodes-list] { set metric_($dest) [$cls set INFINITY] set rtpref_($dest) [$cls set preference_] } } rtPeer instproc addr? {} { $self instvar addr_ return $addr_ } rtPeer instproc port? {} { $self instvar port_ return $port_ } rtPeer instproc metric {dest val} { $self instvar metric_ set metric_($dest) $val } rtPeer instproc metric? dest { $self instvar metric_ return $metric_($dest) } rtPeer instproc preference {dest val} { $self instvar rtpref_ set rtpref_($dest) $val } rtPeer instproc preference? dest { $self instvar rtpref_ return $rtpref_($dest) } # #Class Agent/rtProto -superclass Agent Agent/rtProto proc pre-init-all args { # By default, do nothing when a person does $ns rtproto foo. } Agent/rtProto proc init-all args { error "No initialization defined" } Agent/rtProto instproc init node { $self next $self instvar ns_ node_ rtObject_ preference_ ifs_ ifstat_ set ns_ [Simulator instance] catch "set preference_ [[$self info class] set preference_]" ret if { $ret == "" } { set preference_ [$class set preference_] } foreach nbr [$node set neighbor_] { set link [$ns_ link $node $nbr] set ifs_($nbr) $link set ifstat_($nbr) [$link up?] } set rtObject_ [$node rtObject?] } Agent/rtProto instproc compute-routes {} { error "No route computation defined" } Agent/rtProto instproc intf-changed {} { #NOTHING } Agent/rtProto instproc send-updates args { #NOTHING } Agent/rtProto proc compute-all {} { #NOTHING } # # Static routing, the default # Class Agent/rtProto/Static -superclass Agent/rtProto Agent/rtProto/Static proc init-all args { # The Simulator knows the entire topology. # Hence, the current compute-routes method in the Simulator class is # well suited. We use it as is. [Simulator instance] compute-routes } # # Session based unicast routing # Class Agent/rtProto/Session -superclass Agent/rtProto Agent/rtProto/Session proc init-all args { [Simulator instance] compute-routes } Agent/rtProto/Session proc compute-all {} { [Simulator instance] compute-routes } # ######################################################################### # # Code below this line is experimental, and should be considered work # in progress. None of this code is used in production test-suites, or # in the release yet, and hence should not be a problem to anyone. # Class Agent/rtProto/Direct -superclass Agent/rtProto Agent/rtProto/Direct instproc init node { $self next $node $self instvar ns_ rtpref_ nextHop_ metric_ ifs_ foreach node [$ns_ all-nodes-list] { set rtpref_($node) 255 set nextHop_($node) "" set metric_($node) -1 } foreach node [array names ifs_] { set rtpref_($node) [$class set preference_] } } Agent/rtProto/Direct instproc compute-routes {} { $self instvar ifs_ ifstat_ nextHop_ metric_ rtsChanged_ set rtsChanged_ 0 foreach nbr [array names ifs_] { if {$nextHop_($nbr) == "" && [$ifs_($nbr) up?] == "up"} { set ifstat_($nbr) 1 set nextHop_($nbr) $ifs_($nbr) set metric_($nbr) [$ifs_($nbr) cost?] incr rtsChanged_ } elseif {$nextHop_($nbr) != "" && [$ifs_($nbr) up?] != "up"} { set ifstat_($nbr) 0 set nextHop_($nbr) "" set metric_($nbr) -1 incr rtsChanged_ } } } # # Distance Vector Route Computation # # Class Agent/rtProto/DV -superclass Agent/rtProto Agent/rtProto/DV set UNREACHABLE [rtObject set unreach_] Agent/rtProto/DV set mid_ 0 Agent/rtProto/DV proc init-all args { if { [llength $args] == 0 } { set nodeslist [[Simulator instance] all-nodes-list] } else { eval "set nodeslist $args" } Agent set-maxttl Agent/rtProto/DV INFINITY eval rtObject init-all $nodeslist foreach node $nodeslist { set proto($node) [[$node rtObject?] add-proto DV $node] } foreach node $nodeslist { foreach nbr [$node neighbors] { set rtobj [$nbr rtObject?] if { $rtobj != "" } { set rtproto [$rtobj rtProto? DV] if { $rtproto != "" } { $proto($node) add-peer $nbr [$rtproto set agent_addr_] [$rtproto set agent_port_] } } } } } Agent/rtProto/DV instproc init node { global rtglibRNG $self next $node $self instvar ns_ rtObject_ ifsUp_ $self instvar preference_ rtpref_ nextHop_ nextHopPeer_ metric_ multiPath_ set UNREACHABLE [$class set UNREACHABLE] foreach dest [$ns_ all-nodes-list] { set rtpref_($dest) $preference_ set nextHop_($dest) "" set nextHopPeer_($dest) "" set metric_($dest) $UNREACHABLE } set ifsUp_ "" set multiPath_ [[$rtObject_ set node_] set multiPath_] set updateTime [$rtglibRNG uniform 0.0 0.5] $ns_ at $updateTime "$self send-periodic-update" } Agent/rtProto/DV instproc add-peer {nbr agentAddr agentPort} { $self instvar peers_ $self set peers_($nbr) [new rtPeer $agentAddr $agentPort $class] } Agent/rtProto/DV instproc send-periodic-update {} { global rtglibRNG $self instvar ns_ $self send-updates 1 ;# Anything but 0 set updateTime [expr [$ns_ now] + \ ([$class set advertInterval] * [$rtglibRNG uniform 0.9 1.1])] $ns_ at $updateTime "$self send-periodic-update" } Agent/rtProto/DV instproc compute-routes {} { $self instvar ns_ ifs_ rtpref_ metric_ nextHop_ nextHopPeer_ $self instvar peers_ rtsChanged_ multiPath_ set INFINITY [$class set INFINITY] set MAXPREF [rtObject set maxpref_] set UNREACH [rtObject set unreach_] set rtsChanged_ 0 foreach dst [$ns_ all-nodes-list] { set p [lindex $nextHopPeer_($dst) 0] if {$p != ""} { set metric_($dst) [$p metric? $dst] set rtpref_($dst) [$p preference? $dst] } set pf $MAXPREF set mt $INFINITY set nh(0) 0 foreach nbr [array names peers_] { set pmt [$peers_($nbr) metric? $dst] set ppf [$peers_($nbr) preference? $dst] # if peer metric not valid continue # if peer pref higher continue # if peer pref lower set to latest values # else peer pref equal # if peer metric higher continue # if peer metric lower set to latest values # else peer metrics equal append latest values if { $pmt < 0 || $pmt >= $INFINITY || $ppf > $pf || $pmt > $mt } \ continue if { $ppf < $pf || $pmt < $mt } { set pf $ppf set mt $pmt unset nh ;# because we must compute *new* next hops } set nh($ifs_($nbr)) $peers_($nbr) } catch "unset nh(0)" if { $pf == $MAXPREF && $mt == $INFINITY } continue if { $pf > $rtpref_($dst) || \ ($metric_($dst) >= 0 && $mt > $metric_($dst)) } \ continue if {$mt >= $INFINITY} { set mt $UNREACH } incr rtsChanged_ if { $pf < $rtpref_($dst) || $mt < $metric_($dst) } { set rtpref_($dst) $pf set metric_($dst) $mt set nextHop_($dst) "" set nextHopPeer_($dst) "" foreach n [array names nh] { lappend nextHop_($dst) $n lappend nextHopPeer_($dst) $nh($n) if !$multiPath_ break; } continue } set rtpref_($dst) $pf set metric_($dst) $mt set newNextHop "" set newNextHopPeer "" foreach rt $nextHop_($dst) { if [info exists nh($rt)] { lappend newNextHop $rt lappend newNextHopPeer $nh($rt) unset nh($rt) } } set nextHop_($dst) $newNextHop set nextHopPeer_($dst) $newNextHopPeer if { $multiPath_ || $nextHop_($dst) == "" } { foreach rt [array names nh] { lappend nextHop_($dst) $rt lappend nextHopPeer_($dst) $nh($rt) if !$multiPath_ break } } } set rtsChanged_ } Agent/rtProto/DV instproc intf-changed {} { $self instvar ns_ peers_ ifs_ ifstat_ ifsUp_ nextHop_ nextHopPeer_ metric_ set INFINITY [$class set INFINITY] set ifsUp_ "" foreach nbr [array names peers_] { set state [$ifs_($nbr) up?] if {$state != $ifstat_($nbr)} { set ifstat_($nbr) $state if {$state != "up"} { if ![info exists all-nodes] { set all-nodes [$ns_ all-nodes-list] } foreach dest ${all-nodes} { $peers_($nbr) metric $dest $INFINITY } } else { lappend ifsUp_ $nbr } } } } Agent/rtProto/DV proc get-next-mid {} { set ret [Agent/rtProto/DV set mid_] Agent/rtProto/DV set mid_ [expr $ret + 1] set ret } Agent/rtProto/DV proc retrieve-msg id { set ret [Agent/rtProto/DV set msg_($id)] Agent/rtProto/DV unset msg_($id) set ret } Agent/rtProto/DV instproc send-updates changes { $self instvar peers_ ifs_ ifsUp_ if $changes { set to-send-to [array names peers_] } else { set to-send-to $ifsUp_ } set ifsUp_ "" foreach nbr ${to-send-to} { if { [$ifs_($nbr) up?] == "up" } { $self send-to-peer $nbr } } } Agent/rtProto/DV instproc send-to-peer nbr { $self instvar ns_ rtObject_ ifs_ peers_ set INFINITY [$class set INFINITY] foreach dest [$ns_ all-nodes-list] { set metric [$rtObject_ metric? $dest] if {$metric < 0} { set update($dest) $INFINITY } else { set update($dest) [$rtObject_ metric? $dest] foreach nh [$rtObject_ nextHop? $dest] { if {$nh == $ifs_($nbr)} { set update($dest) $INFINITY } } } } ### modifed by Liang Guo, 11/11/99, what if there's no peer on that end? ### needed when only part of the network nodes are using DV routing if { $peers_($nbr) == "" } { return } ##################### End ########## set id [$class get-next-mid] $class set msg_($id) [array get update] # set n [$rtObject_ set node_]; \ puts stderr [concat [format ">>> %7.5f" [$ns_ now]] \ "${n}([$n id]/[$self set addr_]) send update" \ "to ${nbr}([$nbr id]/[$peers_($nbr) addr?]) id = $id"]; \ set j 0; \ foreach i [lsort -command TclObjectCompare [array names update]] { \ puts -nonewline "\t${i}([$i id]) $update($i)"; \ if {$j == 3} { \ puts ""; \ }; \ set j [expr ($j + 1) % 4]; \ }; \ if $j { puts ""; } # XXX Note the singularity below... $self send-update [$peers_($nbr) addr?] [$peers_($nbr) port?] $id [array size update] } Agent/rtProto/DV instproc recv-update {peerAddr id} { $self instvar peers_ ifs_ nextHopPeer_ metric_ $self instvar rtsChanged_ rtObject_ set INFINITY [$class set INFINITY] set UNREACHABLE [$class set UNREACHABLE] set msg [$class retrieve-msg $id] array set metrics $msg # set n [$rtObject_ set node_]; \ puts stderr [concat [format "<<< %7.5f" [[Simulator instance] now]] \ "${n}([$n id]) recv update from peer $peerAddr id = $id"] foreach nbr [array names peers_] { if {[$peers_($nbr) addr?] == $peerAddr} { set peer $peers_($nbr) if { [array size metrics] > [Node set nn_] } { error "$class::$proc update $peerAddr:$msg:$count is larger than the simulation topology" } set metricsChanged 0 foreach dest [array names metrics] { set metric [expr $metrics($dest) + [$ifs_($nbr) cost?]] if {$metric > $INFINITY} { set metric $INFINITY } if {$metric != [$peer metric? $dest]} { $peer metric $dest $metric incr metricsChanged } } if $metricsChanged { $self compute-routes incr rtsChanged_ $metricsChanged $rtObject_ compute-routes } else { # dynamicDM multicast hack. # If we get a message from a neighbour, then something # at that neighbour has changed. While this may not # cause any unicast changes on our end, dynamicDM # looks at neighbour's routing tables to compute # parent-child relationships, and has to do them # again. # $rtObject_ flag-multicast -1 } return } } error "$class::$proc update $peerAddr:$msg:$count from unknown peer" } Agent/rtProto/DV proc compute-all {} { # Because proc methods are not inherited from the parent class. } # # Manual routing # Class Agent/rtProto/Manual -superclass Agent/rtProto Agent/rtProto/Manual proc pre-init-all args { Simulator set node_factory_ ManualRtNode } Agent/rtProto/Manual proc init-all args { # The user will do all routing. } ### Local Variables: ### mode: tcl ### tcl-indent-level: 4 ### tcl-default-application: ns ### End: # # 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@isi.edu>. # # The API for this code is still somewhat fluid and subject to change. # Check the documentation for details. Class rtQueue Simulator instproc rtmodel { dist parms args } { set ret "" if { [rtModel info subclass rtModel/$dist] != "" } { $self instvar rtModel_ set ret [eval new rtModel/$dist $self] eval $ret set-elements $args eval $ret set-parms $parms set trace [$self get-ns-traceall] if {$trace != ""} { $ret trace $self $trace } set trace [$self get-nam-traceall] if {$trace != ""} { $ret trace $self $trace "nam" } if [info exists rtModel_] { lappend rtModel_ $ret } else { set rtModel_ $ret } } return $ret } Simulator instproc rtmodel-configure {} { $self instvar rtq_ rtModel_ if [info exists rtModel_] { set rtq_ [new rtQueue $self] foreach m $rtModel_ { $m configure } } } Simulator instproc rtmodel-at {at op args} { set parms [list $op $at] eval $self rtmodel Manual [list $parms] $args } Simulator instproc rtmodel-delete model { $self instvar rtModel_ set idx [lsearch -exact $rtModel_ $model] if { $idx != -1 } { delete $model set rtModel_ [lreplace $rtModel_ $idx $idx] } } # rtQueue instproc init ns { $self next $self instvar ns_ set ns_ $ns } rtQueue instproc insq-i { interval obj iproc args } { $self instvar rtq_ ns_ set time [expr $interval + [$ns_ now]] if ![info exists rtq_($time)] { $ns_ at $time "$self runq $time" } lappend rtq_($time) "$obj $iproc $args" return $time } rtQueue instproc insq { at obj iproc args } { $self instvar rtq_ ns_ if {[$ns_ now] >= $at} { puts stderr "$proc: Cannot set event in the past" set at "" } else { if ![info exists rtq_($at)] { $ns_ at $at "$self runq $at" } lappend rtq_($at) "$obj $iproc $args" } return $at } rtQueue instproc delq { time obj } { $self instvar rtq_ set ret "" set nevent "" if [info exists rtq_($time)] { foreach event $rtq_($time) { if {[lindex $event 0] != $obj} { lappend nevent $event } else { set ret $event } } set rtq_($time) $nevent ;# XXX } return ret } rtQueue instproc runq { time } { $self instvar rtq_ set objects "" foreach event $rtq_($time) { set obj [lindex $event 0] set iproc [lindex $event 1] set args [lrange $event 2 end] eval $obj $iproc $args lappend objects $obj } foreach obj $objects { $obj notify } unset rtq_($time) } # Class rtModel rtModel set rtq_ "" rtModel instproc init ns { $self next $self instvar ns_ startTime_ finishTime_ set ns_ $ns set startTime_ [$class set startTime_] set finishTime_ [$class set finishTime_] } rtModel instproc set-elements args { $self instvar ns_ links_ nodes_ if { [llength $args] == 2 } { set n0 [lindex $args 0] set n1 [lindex $args 1] set n0id [$n0 id] set n1id [$n1 id] set nodes_($n0id) $n0 set nodes_($n1id) $n1 set links_($n0id:$n1id) [$ns_ link $n0 $n1] set links_($n1id:$n0id) [$ns_ link $n1 $n0] } else { set n0 [lindex $args 0] set n0id [$n0 id] set nodes_($n0id) $n0 foreach nbr [$n0 set neighbor_] { set n1 $nbr set n1id [$n1 id] set nodes_($n1id) $n1 set links_($n0id:$n1id) [$ns_ link $n0 $n1] set links_($n1id:$n0id) [$ns_ link $n1 $n0] } } } rtModel instproc set-parms args { $self instvar startTime_ upInterval_ downInterval_ finishTime_ set cls [$self info class] foreach i {startTime_ upInterval_ downInterval_ finishTime_} { if [catch "$cls set $i" $i] { set $i [$class set $i] } } set off "-" set up "-" set dn "-" set fin "-" switch [llength $args] { 4 { set off [lindex $args 0] set up [lindex $args 1] set dn [lindex $args 2] set fin [lindex $args 3] } 3 { set off [lindex $args 0] set up [lindex $args 1] set dn [lindex $args 2] } 2 { set up [lindex $args 0] set dn [lindex $args 1] } } if {$off != "-" && $off != ""} { set startTime_ $off } if {$up != "-" && $up != ""} { set upInterval_ $up } if {$dn != "-" && $dn != ""} { set downInterval_ $dn } if {$fin != "-" && $fin != ""} { set finishTime_ $fin } } rtModel instproc configure {} { $self instvar ns_ links_ if { [rtModel set rtq_] == "" } { rtModel set rtq_ [$ns_ set rtq_] } foreach l [array names links_] { $links_($l) dynamic } $self set-first-event } rtModel instproc set-event-exact {fireTime op} { $self instvar ns_ finishTime_ if {$finishTime_ != "-" && $fireTime > $finishTime_} { if {$op == "up"} { [rtModel set rtq_] insq $finishTime_ $self $op } } else { [rtModel set rtq_] insq $fireTime $self $op } } rtModel instproc set-event {interval op} { $self instvar ns_ $self set-event-exact [expr [$ns_ now] + $interval] $op } rtModel instproc set-first-event {} { $self instvar startTime_ upInterval_ $self set-event [expr $startTime_ + $upInterval_] down } rtModel instproc up {} { $self instvar links_ foreach l [array names links_] { $links_($l) up } } rtModel instproc down {} { $self instvar links_ foreach l [array names links_] { $links_($l) down } } rtModel instproc notify {} { $self instvar nodes_ ns_ foreach n [array names nodes_] { $nodes_($n) intf-changed } [$ns_ get-routelogic] notify } rtModel instproc trace { ns f {op ""} } { $self instvar links_ foreach l [array names links_] { $links_($l) trace-dynamics $ns $f $op } } # # Exponential link failure/recovery models # Class rtModel/Exponential -superclass rtModel rtModel/Exponential instproc set-first-event {} { global rtglibRNG $self instvar startTime_ upInterval_ $self set-event [expr $startTime_ + [$rtglibRNG exponential] * $upInterval_] down } rtModel/Exponential instproc up { } { global rtglibRNG $self next $self instvar upInterval_ $self set-event [expr [$rtglibRNG exponential] * $upInterval_] down } rtModel/Exponential instproc down { } { global rtglibRNG $self next $self instvar downInterval_ $self set-event [expr [$rtglibRNG exponential] * $downInterval_] up } # # Deterministic link failure/recovery models # Class rtModel/Deterministic -superclass rtModel rtModel/Deterministic instproc up { } { $self next $self instvar upInterval_ $self set-event $upInterval_ down } rtModel/Deterministic instproc down { } { $self next $self instvar downInterval_ $self set-event $downInterval_ up } # # Route Dynamics instantiated through a trace file. # Invoked through: # # $ns_ rtmodel Trace $traceFile $node1 [$node2 ... ] # Class rtModel/Trace -superclass rtModel rtModel/Trace instproc get-next-event {} { $self instvar tracef_ links_ while {[gets $tracef_ event] >= 0} { set toks [split $event] if [info exists links_([lindex $toks 3]:[lindex $toks 4])] { return $toks } } return "" } rtModel/Trace instproc set-trace-events {} { $self instvar ns_ nextEvent_ evq_ set time [lindex $nextEvent_ 1] while {$nextEvent_ != ""} { set nextTime [lindex $nextEvent_ 1] if {$nextTime < $time} { puts stderr "event $nextEvent_ is before current time $time. ignored." continue } if {$nextTime > $time} break if ![info exists evq_($time)] { set op [string range [lindex $nextEvent_ 2] 5 end] $self set-event-exact $time $op set evq_($time) 1 } set nextEvent_ [$self get-next-event] } } rtModel/Trace instproc set-parms traceFile { $self instvar tracef_ nextEvent_ if [catch "open $traceFile r" tracef_] { puts stderr "cannot open $traceFile" } else { set nextEvent_ [$self get-next-event] if {$nextEvent_ == ""} { puts stderr "no relevant events in $traceFile" } } } rtModel/Trace instproc set-first-event {} { $self set-trace-events } rtModel/Trace instproc up {} { $self next $self set-trace-events } rtModel/Trace instproc down {} { $self next $self set-trace-events } # # One-shot route dynamics events # Invoked through: # # $ns_ link-op $op $at $node1 [$node2 ...] # or # $ns_ rtmodel Manual {$op $at} $node1 [$node2 ...] # Class rtModel/Manual -superclass rtModel rtModel/Manual instproc set-first-event {} { $self instvar op_ at_ $self set-event-exact $at_ $op_ ;# you could concievably set a finishTime_? } rtModel/Manual instproc set-parms {op at} { $self instvar op_ at_ set op_ $op set at_ $at } rtModel/Manual instproc notify {} { $self next delete $self ;# XXX wierd code alert. # If needed, this could be commented out, on the assumption that # manual settings will be very limited, and hence not a sufficient # drag on memory resources. For now, play it safe (or is it risky?) } Class Agent/rtProto/Algorithmic -superclass Agent/rtProto #Agent/rtProto/Algorithmic proc pre-init-all args { # [Simulator instance] set routingTable_ [new RouteLogic/Algorithmic] #} Agent/rtProto/Algorithmic proc init-all args { [Simulator instance] compute-algo-routes } Agent/rtProto/Algorithmic proc compute-all {} { [Simulator instance] compute-algo-routes } RouteLogic/Algorithmic instproc BFS {} { $self instvar ns_ children_ root_ rank_ set ns_ [Simulator instance] if {[$ns_ info class] == "Simulator"} { $ns_ instvar link_ foreach ln [array names link_] { set L [split $ln :] set srcID [lindex $L 0] set dstID [lindex $L 1] if ![info exist adj($srcID)] { set adj($srcID) "" } if ![info exist adj($dstID)] { set adj($dstID) "" } if {[lsearch $adj($srcID) $dstID] < 0} { lappend adj($srcID) $dstID } if {[lsearch $adj($dstID) $srcID] < 0} { lappend adj($dstID) $srcID } } } elseif {[$ns_ info class] == "SessionSim"} { $ns_ instvar delay_ foreach ln [array names delay_] { set L [split $ln :] set srcID [lindex $L 0] set dstID [lindex $L 1] if ![info exist adj($srcID)] { set adj($srcID) "" } if ![info exist adj($dstID)] { set adj($dstID) "" } if {[lsearch $adj($srcID) $dstID] < 0} { lappend adj($srcID) $dstID } if {[lsearch $adj($dstID) $srcID] < 0} { lappend adj($dstID) $srcID } } } # foreach index [array names adj] { # puts "$index: $adj($index)" # } set rank_ 0 set root_ 0 set traversed($root_) 1 set queue "$root_" while {[llength $queue] > 0} { # puts "queue: $queue, queue-length: [llength $queue]" set parent [lindex $queue 0] set queue [lreplace $queue 0 0] # puts "parent: $parent, queue: $queue, queue-length: [llength $queue]" if ![info exist children_($parent)] { set children_($parent) "" } # puts "adj: $adj($parent)" foreach nd $adj($parent) { if ![info exist traversed($nd)] { set traversed($nd) 0 } if !$traversed($nd) { set traversed($nd) 1 lappend children_($parent) $nd lappend queue $nd } } set num_children [llength $children_($parent)] if {$rank_ < $num_children} { set rank_ $num_children } # puts "rank: $rank_, queue: $queue, queue-length: [llength $queue]" } } RouteLogic/Algorithmic instproc compute {} { $self instvar root_ children_ rank_ id_ algoAdd_ # set queue "$root_" # while {[llength $queue] > 0} { # set parent [lindex $queue 0] # set queue [lreplace $queue 0 0] # puts "$parent: $children_($parent)" # foreach child $children_($parent) { # lappend queue $child # } # } set queue [list [list $root_ 0]] while {[llength $queue] > 0} { # puts $queue set parent [lindex $queue 0] set queue [lreplace $queue 0 0] set id [lindex $parent 0] set algoAdd [lindex $parent 1] set id_($algoAdd) $id set algoAdd_($id) $algoAdd set i 0 foreach child $children_($id) { incr i lappend queue [list $child [expr [expr $algoAdd * $rank_] + $i]] } } } RouteLogic/Algorithmic instproc lookup {src dst} { $self instvar id_ algoAdd_ set algosrc $algoAdd_($src) set algodst $algoAdd_($dst) set algonxt [$self algo-lookup $algosrc $algodst] # puts "lookup: $algosrc $algodst $algonxt $id_($algonxt)" return $id_($algonxt) } RouteLogic/Algorithmic instproc algo-lookup {src dst} { $self instvar rank_ if {$src == $dst} { return $src } set a $src set b $dst set offset 0 while {$b > $a} { set offset [expr $b % $rank_] set b [expr $b / $rank_] if {$offset == 0} { set offset $rank_ set b [expr $b - 1] } } if {$b == $a} { return [expr [expr $a * $rank_] + $offset] } else { return [expr [expr $a - 1] / $rank_] } } Simulator instproc compute-algo-routes {} { $self instvar Node_ link_ # # Compute all the routes using the route-logic helper object. # set r [$self get-routelogic] $r BFS $r compute # # Set up each classifer (aka node) to act as a router. # Point each classifer table to the link object that # in turns points to the right node. # set i 0 set n [Node set nn_] # puts "total = $n" while { $i < $n } { if ![info exists Node_($i)] { incr i continue } set n1 $Node_($i) $n1 set rtsize_ 1 set j 0 while { $j < $n } { if { $i != $j } { # shortened nexthop to nh, to fit add-route in # a single line set nh [$r lookup $i $j] # puts "$i $j $nh" if { $nh >= 0 } { $n1 add-route $j [$link_($i:$nh) head] ###$n1 incr-rtgtable-size } } incr j } incr i } } Simulator instproc compute-route-for-mobilenodes {} { $self instvar MobileNode_ Simulator instvar mn_ set r [$self get-routelogic] set level [AddrParams set hlevel_] $r hlevel-is $level $self hier-topo $r for {set i 0} {$i < $mn_} {incr i} { set srcID [$MobileNode_($i) node-addr] set bs [$MobileNode_($i) base-station?] set dstID [$bs node-addr] $r hier-insert $srcID $dstID 1 ## we donot setup basestn nodes as compute-hier ## takes care of them } $r hier-compute $self populate-mobilenode $level } Simulator instproc populate-mobilenode {level} { Simulator instvar mn_ $self instvar MobileNode_ set i 0 while { $i < $mn_ } { set n1 $MobileNode_($i) set addr [$n1 node-addr] set L [$n1 split-addrstr $addr] # # for each hierarchy level, populate classifier # for that level # for {set k 0} {$k < $level} {incr k} { set csize [AddrParams elements-in-level? $addr $k] # # for each element in that level # (elements maybe nodes or clusters or domains # if {$k > 0} { set prefix [$r append-addr $k $L] } for {set m 0} {$m < $csize} {incr m} { if { $m != [lindex $L $k]} { if {$k > 0} { set str $prefix append str . $m } else { set str $m } set nh [$r hier-lookup $addr \ $str] # add entry in clasifier # only if hier-lookup # returns a value. if {$nh != -1} { set node [$self get-node-by-addr $nh] if { $node > 0 } { $n1 add-hroute $str $node } } } } } incr i } } # # 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 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, UC Berkeley # (http://daedalus.cs.berkeley.edu) # # # There are three levels to error generation. # 1. Single State # rate_: uniform error rate in pkt or byte # 2. Two State # error-free (0) and error (1) states # each state has a ranvar determining the length each state # 3. Multi-State: extending Two-State in OTcl # each state has a ranvar determining the length each state # a matrix specifying transition probabilities # # Patched by Jianping Pan (jpan@bbcr.uwaterloo.ca) # # Each state is an error model (which could be 1-state or multi-state), # In addtion, the error model has a matrix of transition probabilities, # and a start state for the model. These usually corresond to # homogeneous Markov chains, but are not restricted to them, because it # is possible to change the transition probabilities on the fly and # depending on past history, if you so desire. Multi-state error models # reside entirely in Tcl and aren't split between C and Tcl. One-state # error models are split objects and ErrorModel is derived from # Connector. As an example, a 2-state Markov error model is built-in, # as ErrorModel/MultiState/TwoStateMarkov Finally, an error *module* # contains a classifier, a set of dynamically-added ErrorModels, and # enough plumbing to construct flow-based Errors. # ErrorModel/Trace instproc init {{filename ""}} { $self instvar file_ $self next set file_ "" if {$filename != ""} { $self open $filename } } ErrorModel/Trace instproc open {filename} { $self instvar file_ if {! [file readable $filename]} { puts "$class: cannot open $filename" return } if {$file_ != ""} { close $file_ } set file_ [open $filename] $self read } ErrorModel/Trace instproc read {} { $self instvar file_ good_ loss_ if {$file_ != ""} { set line [gets $file_] set good_ [lindex $line 0] set loss_ [lindex $line 1] } else { set good_ 123456789 set loss_ 0 } } ErrorModel/TwoState instproc init {rv0 rv1 {unit "pkt"}} { $self next $self unit $unit $self ranvar 0 $rv0 $self ranvar 1 $rv1 } Class ErrorModel/Uniform -superclass ErrorModel Class ErrorModel/Expo -superclass ErrorModel/TwoState Class ErrorModel/Empirical -superclass ErrorModel/TwoState ErrorModel/Uniform instproc init {rate {unit "pkt"}} { $self next $self unit $unit $self set rate_ $rate } ErrorModel/Expo instproc init {avgList {unit "pkt"}} { set rv0 [new RandomVariable/Exponential] set rv1 [new RandomVariable/Exponential] $rv0 set avg_ [lindex $avgList 0] $rv1 set avg_ [lindex $avgList 1] $self next $rv0 $rv1 $unit } ErrorModel/Empirical instproc init {fileList {unit "pkt"}} { set rv0 [new RandomVariable/Empirical] set rv1 [new RandomVariable/Empirical] $rv0 loadCDF [lindex $fileList 0] $rv1 loadCDF [lindex $fileList 1] $self next $rv0 $rv1 $unit } ErrorModel/MultiState instproc init {states periods trans transunit sttype nstates start} { # states_ is an array of states (errmodels), # periods_ is an array of state duration (sec) # transmatrix_ is the transition state model matrix, # sttype is the type of state transitions to use 'time' or 'pkt' # nstates_ is the number of states # transunit_ is pkt/byte/time, and curstate_ is the current state # start is the start state, which curstate_ is initialized to # error-model is the current error model to use # curperiod_ is the duration of the current timed-state $self instvar states_ transmatrix_ transunit_ nstates_ curstate_ eu_ periods_ $self next set states_ $states set periods_ $periods set transmatrix_ $trans set transunit_ $transunit $self sttype $sttype set nstates_ $nstates set curstate_ $start set eu_ $transunit $self error-model $start # Find current state's duration if { [$self sttype] == "time" } { for { set i 0 } { $i < $nstates_ } {incr i} { if { [lindex $states_ $i] == $curstate_ } { break } } $self set curperiod_ [lindex $periods_ $i] } } ErrorModel/MultiState instproc corrupt { } { $self instvar states_ transmatrix_ transunit_ curstate_ set cur $curstate_ # XXX # check the type of state transitions to use: 'time' or 'pkt' # defaults to pkt transitions using transmatrix_ if { [$self sttype] == "time" } { set curstate_ [$self time-transition] } else { set curstate_ [$self transition] } if { $cur != $curstate_ } { # If transitioning out, reset current state $cur reset $self reset $self error-model $curstate_ } return [$curstate_ next] } # XXX eventually want to put in expected times of staying in each state # before transition here. Punt on this for now. #ErrorModel instproc insert-error { parent } { # return [$self corrupt $parent] #} # Transition based on time spent in the current state ErrorModel/MultiState instproc time-transition { } { $self instvar states_ transmatrix_ transunit_ curstate_ nstates_ periods_ if {[$self set texpired_] != 1} { return $curstate_ } for { set i 0 } { $i < $nstates_ } {incr i} { if { [lindex $states_ $i] == $curstate_ } { break } } # get the right transition list set trans [lindex $transmatrix_ $i] set p [uniform 0 1] set total 0 for { set i 0 } { $i < $nstates_ } {incr i } { set total [expr $total + [lindex $trans $i]] if { $p <= $total } { $self set curperiod_ [lindex $periods_ $i] return [lindex $states_ $i] } } puts "Misconfigured state transition: prob $p total $total $nstates_" return $curstate_ } # Decide whom to transition to ErrorModel/MultiState instproc transition { } { $self instvar states_ transmatrix_ transunit_ curstate_ nstates_ for { set i 0 } { $i < $nstates_ } {incr i} { if { [lindex $states_ $i] == $curstate_ } { break } } # get the right transition list set trans [lindex $transmatrix_ $i] set p [uniform 0 1] set total 0 for { set i 0 } { $i < $nstates_ } {incr i } { set total [expr $total + [lindex $trans $i]] if { $p <= $total } { return [lindex $states_ $i] } } puts "Misconfigured state transition: prob $p total $total $nstates_" return $curstate_ } Class ErrorModel/TwoStateMarkov -superclass ErrorModel/TwoState ErrorModel/TwoStateMarkov instproc init {rate eu {transition}} { $self next $self unit time set rv0 [new RandomVariable/Exponential] set rv1 [new RandomVariable/Exponential] $rv0 set avg_ [lindex $rate 0] $rv1 set avg_ [lindex $rate 1] $self ranvar 0 $rv0 $self ranvar 1 $rv1 # set p01 [lindex $transition 0] # set p10 [lindex $transition 1] # set trans [list [list [expr 1 - $p01] $p01] \ # [list [expr 1 - $p01] $p01]] # # state 0 is the start state # $self next $states_ $trans $eu $i [lindex $states_ 0] } # # the following is a "ErrorModule"; # it contains a classifier, a set of dynamically-added ErrorModels, and enough # plumbing to construct flow-based Errors. # # It's derived from a connector # ErrorModule instproc init { cltype { clslots 29 } } { $self next set nullagent [[Simulator instance] set nullAgent_] set classifier [new Classifier/Hash/$cltype $clslots] $self cmd classifier $classifier $self cmd target [new Connector] $self cmd drop-target [new Connector] $classifier proc unknown-flow { src dst fid } { puts "warning: classifier $self unknown flow s:$src, d:$dst, fid:$fid" } } # # set a default behavior within the error module. # Called as follows # $errormodule default $em # where $em is the name of an error model to pass default traffic to. # note that if $em is "pass", then default just goes through untouched # ErrorModule instproc default errmodel { set cl [$self cmd classifier] if { $errmodel == "pass" } { set target [$self cmd target] set pslot [$cl installNext $target] $cl set default_ $pslot return } set emslot [$cl findslot $errmodel] if { $emslot == -1 } { puts "ErrorModule: couldn't find classifier entry for error model $errmodel" return } $cl set default_ $emslot } ErrorModule instproc insert errmodel { $self instvar models_ $errmodel target [$self cmd target] $errmodel drop-target [$self cmd drop-target] if { [info exists models_] } { set models_ [concat $models_ $errmodel] } else { set models_ $errmodel } } ErrorModule instproc errormodels {} { $self instvar models_ return $models_ } ErrorModule instproc bind args { # this is to perform '$fem bind $errmod id' # and '$fem bind $errmod idstart idend' set nargs [llength $args] set errmod [lindex $args 0] set a [lindex $args 1] if { $nargs == 3 } { set b [lindex $args 2] } else { set b $a } # bind the errmodel to the flow id's [a..b] set cls [$self cmd classifier] while { $a <= $b } { # first install the class to get its slot number # use the flow id as the hash bucket set slot [$cls installNext $errmod] $cls set-hash auto 0 0 $a $slot incr a } } ErrorModule instproc target args { if { $args == "" } { return [[$self cmd target] target] } set obj [lindex $args 0] [$self cmd target] target $obj [$self cmd target] drop-target $obj } ErrorModule instproc drop-target args { if { $args == "" } { return [[$self cmd drop-target] target] } set obj [lindex $args 0] [$self cmd drop-target] drop-target $obj [$self cmd drop-target] target $obj } # # 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. # #defaults Queue/SimpleIntServ set qlimit1_ 50 Queue/SimpleIntServ set qlimit0_ 50 Agent/SA set rate_ 0 Agent/SA set bucket_ 0 Agent/SA set packetSize_ 210 ADC set backoff_ true ADC set dobump_ true ADC/MS set backoff_ false ADC set src_ -1 ADC set dst_ -1 ADC/MS set utilization_ 0.95 ADC/MSPK set utilization_ 0.95 ADC/Param set utilization_ 1.0 ADC/HB set epsilon_ 0.7 ADC/ACTO set s_ 0.002 ADC/ACTO set dobump_ false ADC/ACTP set s_ 0.002 ADC/ACTP set dobump_ false Est/TimeWindow set T_ 3 Est/ExpAvg set w_ 0.125 Est set period_ 0.5 ADC set bandwidth_ 0 SALink set src_ -1 SALink set dst_ -1 Est set src_ -1 Est set dst_ -1 Class IntServLink -superclass SimpleLink IntServLink instproc init { src dst bw delay q arg {lltype "DelayLink"} } { $self next $src $dst $bw $delay $q $lltype ; # SimpleLink ctor $self instvar queue_ link_ $self instvar measclassifier_ signalmod_ adc_ est_ measmod_ set ns_ [Simulator instance] #Create a suitable adc unit from larg with suitable params set adctype [lindex $arg 3] set adc_ [new ADC/$adctype] $adc_ set bandwidth_ $bw $adc_ set src_ [$src id] $adc_ set dst_ [$dst id] if { [lindex $arg 5] == "CL" } { #Create a suitable est unit set esttype [lindex $arg 4] set est_ [new Est/$esttype] $est_ set src_ [$src id] $est_ set dst_ [$dst id] $adc_ attach-est $est_ 1 #Create a Measurement Module set measmod_ [new MeasureMod] $measmod_ target $queue_ $adc_ attach-measmod $measmod_ 1 } #Create the signalmodule set signaltype [lindex $arg 2] set signalmod_ [new $signaltype] $signalmod_ set src_ [$src id] $signalmod_ set dst_ [$dst id] $signalmod_ attach-adc $adc_ $self add-to-head $signalmod_ #Create a measurement classifier to decide which packets to measure $self create-meas-classifier $signalmod_ target $measclassifier_ #Schedule to start the admission control object $ns_ at 0.0 "$adc_ start" } IntServLink instproc buffersize { b } { $self instvar est_ adc_ $est_ setbuf [set b] $adc_ setbuf [set b] } #measClassifier is an instance of Classifier/Hash/Flow #for right now # FlowId 0 -> Best Effort traffic # FlowId non-zero -> Int Serv traffic IntServLink instproc create-meas-classifier {} { $self instvar measclassifier_ measmod_ link_ queue_ set measclassifier_ [new Classifier/Hash/Fid 1 ] #set slots for measclassifier set slot [$measclassifier_ installNext $queue_] $measclassifier_ set-hash auto 0 0 0 $slot #Currently measure all flows with fid.ne.0 alone set slot [$measclassifier_ installNext $measmod_] $measclassifier_ set default_ 1 } IntServLink instproc trace-sig { f } { $self instvar signalmod_ est_ adc_ $signalmod_ attach $f $est_ attach $f $adc_ attach $f set ns [Simulator instance] $ns at 0.0 "$signalmod_ add-trace" } #Helper function to output link utilization and bw estimate IntServLink instproc trace-util { interval {f ""}} { $self instvar est_ set ns [Simulator instance] if { $f != "" } { puts $f "[$ns now] [$est_ load-est] [$est_ link-utlzn]" } $ns at [expr [$ns now]+$interval] "$self trace-util $interval $f" } CMUTrace instproc init { tname type } { $self next $tname $type $self instvar type_ src_ dst_ callback_ show_tcphdr_ set type_ $type set src_ 0 set dst_ 0 set callback_ 0 set show_tcphdr_ 0 } CMUTrace instproc attach fp { $self instvar fp_ set fp_ $fp $self cmd attach $fp_ } Class CMUTrace/Send -superclass CMUTrace CMUTrace/Send instproc init { tname } { $self next $tname "s" } Class CMUTrace/Recv -superclass CMUTrace CMUTrace/Recv instproc init { tname } { $self next $tname "r" } Class CMUTrace/Drop -superclass CMUTrace CMUTrace/Drop instproc init { tname } { $self next $tname "D" } CMUTrace/Recv set src_ 0 CMUTrace/Recv set dst_ 0 CMUTrace/Recv set callback_ 0 CMUTrace/Recv set show_tcphdr_ 0 CMUTrace/Recv set off_sr_ 0 CMUTrace/Send set src_ 0 CMUTrace/Send set dst_ 0 CMUTrace/Send set callback_ 0 CMUTrace/Send set show_tcphdr_ 0 CMUTrace/Send set off_sr_ 0 CMUTrace/Drop set src_ 0 CMUTrace/Drop set dst_ 0 CMUTrace/Drop set callback_ 0 CMUTrace/Drop set show_tcphdr_ 0 CMUTrace/Drop set off_sr_ 0 CMUTrace/Drop set off_SR_ 0 CMUTrace/Drop set off_arp_ 0 # # 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. # MIPEncapsulator instproc tunnel-exit mhaddr { $self instvar node_ return [[$node_ set regagent_] set TunnelExit_($mhaddr)] } Class Node/MIPBS -superclass Node/Broadcast Node/MIPBS instproc init { args } { eval $self next $args $self instvar regagent_ encap_ decap_ agents_ address_ dmux_ id_ if { $dmux_ == "" } { error "serious internal error at Node/MIPBS\n" } set regagent_ [new Agent/MIPBS $self] $self attach $regagent_ 0 $regagent_ set mask_ [AddrParams set NodeMask_(1)] $regagent_ set shift_ [AddrParams set NodeShift_(1)] $regagent_ set dst_addr_ [expr (~0) << [AddrParams set NodeShift_(1)]] $regagent_ set dst_port_ 0 set encap_ [new MIPEncapsulator] set decap_ [new Classifier/Addr/MIPDecapsulator] # # install en/de-capsulators # lappend agents_ $decap_ # # Check if number of agents exceeds length of port-address-field size # set mask [AddrParams set ALL_BITS_SET] set shift 0 # if {[expr [llength $agents_] - 1] > $mask} { # error "\# of agents attached to node $self exceeds port-field length of $mask bits\n" # } if [Simulator set EnableHierRt_] { set nodeaddr [AddrParams set-hieraddr $address_] } else { set nodeaddr [expr ( $address_ & \ [AddrParams set NodeMask_(1)] ) << \ [AddrParams set NodeShift_(1) ]] } $encap_ set addr_ $nodeaddr $encap_ set port_ 1 $encap_ target [$self entry] $encap_ set node_ $self $dmux_ install 1 $decap_ $encap_ set mask_ [AddrParams set NodeMask_(1)] $encap_ set shift_ [AddrParams set NodeShift_(1)] $decap_ set mask_ [AddrParams set NodeMask_(1)] $decap_ set shift_ [AddrParams set NodeShift_(1)] } Class Node/MIPMH -superclass Node/Broadcast Node/MIPMH instproc init { args } { eval $self next $args $self instvar regagent_ set regagent_ [new Agent/MIPMH $self] $self attach $regagent_ 0 $regagent_ set mask_ [AddrParams set NodeMask_(1)] $regagent_ set shift_ [AddrParams set NodeShift_(1)] $regagent_ set dst_addr_ [expr (~0) << [AddrParams set NodeShift_(1)]] $regagent_ set dst_port_ 0 } Agent/MIPBS instproc init { node args } { eval $self next $args # if mobilenode, donot use bcasttarget; use target_ instead; if {[$node info class] != "MobileNode/MIPBS" && [$node info class] != "Node/MobileNode"} { $self instvar BcastTarget_ set BcastTarget_ [new Classifier/Replicator] $self bcast-target $BcastTarget_ } $self beacon-period 1.0 ;# default value } Agent/MIPBS instproc clear-reg mhaddr { $self instvar node_ OldRoute_ RegTimer_ if [info exists OldRoute_($mhaddr)] { $node_ add-route $mhaddr $OldRoute_($mhaddr) } if {[$node_ info class] == "MobileNode/MIPBS" || [$node_ info class] =="Node/MobileNode" } { eval $node_ clear-hroute [AddrParams get-hieraddr $mhaddr] } if { [info exists RegTimer_($mhaddr)] && $RegTimer_($mhaddr) != "" } { [Simulator instance] cancel $RegTimer_($mhaddr) set RegTimer_($mhaddr) "" } } Agent/MIPBS instproc encap-route { mhaddr coa lifetime } { $self instvar node_ TunnelExit_ OldRoute_ RegTimer_ set ns [Simulator instance] set encap [$node_ set encap_] if {[$node_ info class] == "MobileNode/MIPBS" || [$node_ info class] == "Node/MobileNode"} { set addr [AddrParams get-hieraddr $mhaddr] set a [split $addr] set b [join $a .] #puts "b=$b" $node_ add-hroute $b $encap } else { set or [[$node_ set classifier_] slot $mhaddr] if { $or != $encap } { set OldRoute_($mhaddr) $or $node_ add-route $mhaddr $encap } } set TunnelExit_($mhaddr) $coa if { [info exists RegTimer_($mhaddr)] && $RegTimer_($mhaddr) != "" } { $ns cancel $RegTimer_($mhaddr) } set RegTimer_($mhaddr) [$ns at [expr [$ns now] + $lifetime] \ "$self clear-reg $mhaddr"] } Agent/MIPBS instproc decap-route { mhaddr target lifetime } { $self instvar node_ RegTimer_ # decap's for mobilenodes can have a default-target; in this # case the def-target is the routing-agent. if {[$node_ info class] != "MobileNode/MIPBS" && [$node_ info class] != "Node/MobileNode" } { set ns [Simulator instance] [$node_ set decap_] install $mhaddr $target if { [info exists RegTimer_($mhaddr)] && $RegTimer_($mhaddr) != "" } { $ns cancel $RegTimer_($mhaddr) } set RegTimer_($mhaddr) [$ns at [expr [$ns now] + $lifetime] \ "$self clear-decap $mhaddr"] } else { [$node_ set decap_] defaulttarget [$node_ set ragent_] } } Agent/MIPBS instproc clear-decap mhaddr { $self instvar node_ RegTimer_ if { [info exists RegTimer_($mhaddr)] && $RegTimer_($mhaddr) != "" } { [Simulator instance] cancel $RegTimer_($mhaddr) set RegTimer_($mhaddr) "" } [$node_ set decap_] clear $mhaddr } Agent/MIPBS instproc get-link { src dst } { $self instvar node_ if {[$node_ info class] != "MobileNode/MIPBS" && [$node_ info class] != "Node/MobileNode"} { set ns [Simulator instance] return [[$ns link [$ns get-node-by-addr $src] \ [$ns get-node-by-addr $dst]] head] } else { return "" } } Agent/MIPBS instproc add-ads-bcast-link { ll } { $self instvar BcastTarget_ $BcastTarget_ installNext [$ll head] } Agent/MIPMH instproc init { node args } { eval $self next $args # if mobilenode, donot use bcasttarget; use target_ instead; if {[$node info class] != "MobileNode/MIPMH" && \ [$node info class] != "SRNode/MIPMH" && [$node info class] != "Node/MobileNode" } { $self instvar BcastTarget_ set BcastTarget_ [new Classifier/Replicator] $self bcast-target $BcastTarget_ } $self beacon-period 1.0 ;# default value } Agent/MIPMH instproc update-reg coa { $self instvar node_ ## dont need to set up routing for mobilenodes, so.. if {[$node_ info class] != "MobileNode/MIPMH" && \ [$node_ info class] != "SRNode/MIPMH" && [$node_ info class] != "Node/MobileNode" } { set n [Node set nn_] set ns [Simulator instance] set id [$node_ id] set l [[$ns link $node_ [$ns get-node-by-addr $coa]] head] for { set i 0 } { $i < $n } { incr i } { if { $i != $id } { $node_ add-route $i $l } } } } Agent/MIPMH instproc get-link { src dst } { $self instvar node_ if {[$node_ info class] != "MobileNode/MIPMH" && \ [$node_ info class] != "SRNode/MIPMH" && [$node_ info class] != "Node/MobileNode" } { set ns [Simulator instance] return [[$ns link [$ns get-node-by-addr $src] \ [$ns get-node-by-addr $dst]] head] } else { return "" } } Agent/MIPMH instproc add-sol-bcast-link { ll } { $self instvar BcastTarget_ $BcastTarget_ installNext [$ll head] } # # 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 # @(#) $Header$ # ====================================================================== # # The Node/SatNode class (a pure virtual class) # # ====================================================================== Node/SatNode instproc init args { eval $self next $args ;# parent class constructor $self instvar nifs_ $self instvar phy_tx_ phy_rx_ mac_ ifq_ ll_ pos_ hm_ set nifs_ 0 ;# number of network interfaces # Create a drop trace to log packets for which no route exists set ns_ [Simulator instance] set trace_ [$ns_ get-ns-traceall] if {$trace_ != ""} { set dropT_ [$ns_ create-trace Sat/Drop $trace_ $self $self ""] $self set_trace $dropT_ } } Node/SatNode instproc reset {} { eval $self next $self instvar hm_ instvar nifs_ phy_tx_ phy_rx_ mac_ ifq_ ll_ set ns [Simulator instance] set now_ [$ns now] for {set i 0} {$i < $nifs_} {incr i} { $phy_tx_($i) reset $phy_rx_($i) reset if {[info exists mac_($i)]} { $mac_($i) reset } if {[info exists ll_($i)]} { $ll_($i) reset } if {[info exists ifq_($i)]} { $ifq_($i) reset } } if {$now_ == 0} { if {[info exists hm_]} { $ns at 0.0 "$self start_handoff" } } } Node/SatNode instproc set_next {node_} { $self instvar pos_ $pos_ set_next [$node_ set pos_] } # # Attach an agent to a node. Pick a port and # bind the agent to the port number. # if portnumber is 255, default target is set to the routing agent # Node/SatNode instproc add-target {agent port } { $self instvar dmux_ if { $port == 255 } { # Set the default target at C++ level. [$self set classifier_] defaulttarget $agent $dmux_ install $port $agent } else { # # Send Target # $agent target [$self entry] # # Recv Target # $dmux_ install $port $agent } } # ====================================================================== # # methods for creating SatNodes # # ====================================================================== # Wrapper method for making a polar satellite node that first creates a # satnode plus two link interfaces (uplink and downlink) plus two # satellite channels (uplink and downlink) # Additional ISLs can be added later Simulator instproc satnode-polar {alt inc lon alpha plane linkargs chan} { set tmp [$self satnode polar $alt $inc $lon $alpha $plane] $self add-first-links $tmp gsl $linkargs $chan return $tmp } # Wrapper method for making a geo satellite node that first creates a # satnode plus two link interfaces (uplink and downlink) plus two # satellite channels (uplink and downlink) Simulator instproc satnode-geo {lon linkargs chan} { set tmp [$self satnode geo $lon] $self add-first-links $tmp gsl $linkargs $chan return $tmp } # Wrapper method for making a geo satellite repeater node that first creates a # satnode plus two link interfaces (uplink and downlink) plus two # satellite channels (uplink and downlink) Simulator instproc satnode-geo-repeater {lon chan} { set tmp [$self satnode geo $lon] $self add-first-links $tmp gsl-repeater "" $chan return $tmp } # Wrapper method that does nothing really but makes the API consistent Simulator instproc satnode-terminal {lat lon} { $self satnode terminal $lat $lon } Simulator instproc satnode args { $self instvar Node_ set node [new Node/SatNode] if {[lindex $args 0] == "polar" || [lindex $args 0] == "Polar"} { set args [lreplace $args 0 0] # Set up position object $node set pos_ [new Position/Sat/Polar $args] $node cmd set_position [$node set pos_] [$node set pos_] setnode $node # Set up handoff manager $node set hm_ [new HandoffManager/Sat] $node cmd set_handoff_mgr [$node set hm_] [$node set hm_] setnode $node $node create-ragent } elseif {[lindex $args 0] == "geo" || [lindex $args 0] == "Geo"} { set args [lreplace $args 0 0] $node set pos_ [new Position/Sat/Geo $args] $node cmd set_position [$node set pos_] [$node set pos_] setnode $node $node create-ragent } elseif {[lindex $args 0] == "geo-repeater" || [lindex $args 0] == "Geo-repeater"} { set args [lreplace $args 0 0] $node set pos_ [new Position/Sat/Geo $args] $node cmd set_position [$node set pos_] [$node set pos_] setnode $node } elseif {[lindex $args 0] == "terminal" || [lindex $args 0] == "Terminal"} { set args [lreplace $args 0 0] $node set pos_ [new Position/Sat/Term $args] $node cmd set_position [$node set pos_] [$node set pos_] setnode $node $node set hm_ [new HandoffManager/Term] $node cmd set_handoff_mgr [$node set hm_] [$node set hm_] setnode $node $node create-ragent } else { puts "Otcl error; satnode specified incorrectly: $args" } set Node_([$node id]) $node $node set ns_ $self if [$self multicast?] { $node enable-mcast $self } $self check-node-num return $node } # ====================================================================== # # methods for creating satellite links, channels, and error models # # ====================================================================== # Helper method for creating uplinks and downlinks for a satellite node Simulator instproc add-first-links {node_ linktype linkargs chan} { $node_ set_downlink $chan $node_ set_uplink $chan # Add the interface for these channels and then attach the channels if {$linktype == "gsl-repeater"} { $node_ add-repeater $chan } else { eval $node_ add-interface $linktype $linkargs } $node_ attach-to-outlink [$node_ set downlink_] $node_ attach-to-inlink [$node_ set uplink_] } # Add network stack and attach to the channels Node/SatNode instproc add-gsl {ltype opt_ll opt_ifq opt_qlim opt_mac \ opt_bw opt_phy opt_inlink opt_outlink} { $self add-interface $ltype $opt_ll $opt_ifq $opt_qlim $opt_mac $opt_bw \ $opt_phy $self attach-to-inlink $opt_inlink $self attach-to-outlink $opt_outlink } # Add network stacks for ISLs, and create channels for them Simulator instproc add-isl {ltype node1 node2 bw qtype qlim} { set opt_ll LL/Sat set opt_mac Mac/Sat set opt_phy Phy/Sat set opt_chan Channel/Sat set chan1 [new $opt_chan] set chan2 [new $opt_chan] $node1 add-interface $ltype $opt_ll $qtype $qlim $opt_mac $bw $opt_phy $chan1 $chan2 $node2 add-interface $ltype $opt_ll $qtype $qlim $opt_mac $bw $opt_phy $chan2 $chan1 if {$ltype == "crossseam"} { # Add another spare interface between node 1 and node 2 $node1 add-interface $ltype $opt_ll $qtype $qlim $opt_mac $bw $opt_phy $node2 add-interface $ltype $opt_ll $qtype $qlim $opt_mac $bw $opt_phy } } Node/SatNode instproc add-repeater chan { $self instvar nifs_ phy_tx_ phy_rx_ linkhead_ set t $nifs_ incr nifs_ # linkhead_($t) is a simple connector that provides an API for # accessing elements of the network interface stack set linkhead_($t) [new Connector/LinkHead/Sat] set phy_tx_($t) [new Phy/Repeater] ;# interface set phy_rx_($t) [new Phy/Repeater] # linkhead maintains a collection of pointers $linkhead_($t) setnode $self $linkhead_($t) setphytx $phy_tx_($t) $linkhead_($t) setphyrx $phy_rx_($t) $linkhead_($t) set_type "gsl-repeater" $linkhead_($t) set type_ "gsl-repeater" $phy_rx_($t) up-target $phy_tx_($t) $phy_tx_($t) linkhead $linkhead_($t) $phy_rx_($t) linkhead $linkhead_($t) $phy_tx_($t) node $self ;# Bind node <---> interface $phy_rx_($t) node $self ;# Bind node <---> interface } # # The following sets up link layer, mac layer, interface queue # and physical layer structures for the satellite nodes. # Propagation model is an optional argument # Node/SatNode instproc add-interface args { $self instvar nifs_ phy_tx_ phy_rx_ mac_ ifq_ ll_ drophead_ linkhead_ global ns_ MacTrace opt set t $nifs_ incr nifs_ # linkhead_($t) is a simple connector that provides an API for # accessing elements of the network interface stack set linkhead_($t) [new Connector/LinkHead/Sat] set linktype [lindex $args 0] set ll_($t) [new [lindex $args 1]] ;# link layer set ifq_($t) [new [lindex $args 2]] ;# interface queue set qlen [lindex $args 3] set mac_($t) [new [lindex $args 4]] ;# mac layer set mac_bw [lindex $args 5] set phy_tx_($t) [new [lindex $args 6]] ;# interface set phy_rx_($t) [new [lindex $args 6]] ;# interface set inchan [lindex $args 7] set outchan [lindex $args 8] set drophead_($t) [new Connector] ;# drop target for queue set iif_($t) [new NetworkInterface] # # Local Variables # #set nullAgent_ [$ns_ set nullAgent_] set linkhead $linkhead_($t) set phy_tx $phy_tx_($t) set phy_rx $phy_rx_($t) set mac $mac_($t) set ifq $ifq_($t) set ll $ll_($t) set drophead $drophead_($t) set iif $iif_($t) # linkhead maintains a collection of pointers $linkhead setnode $self $linkhead setll $ll $linkhead setmac $mac $linkhead setqueue $ifq $linkhead setphytx $phy_tx $linkhead setphyrx $phy_rx $linkhead setnetinf $iif $self addlinkhead $linkhead; # Add NetworkInterface to node's list $linkhead target $ll; $linkhead set_type $linktype $linkhead set type_ $linktype # # NetworkInterface # $iif target [$self entry] # # Link Layer # $ll mac $mac; # XXX is this needed? $ll up-target $iif $ll down-target $ifq $ll set delay_ 0ms; # processing delay between ll and ifq # # Interface Queue # $ifq target $mac $ifq set qlim_ $qlen $drophead target [[Simulator instance] set nullAgent_] $ifq drop-target $drophead # # Mac Layer # $mac netif $phy_tx; # Not used by satellite code at this time $mac up-target $ll $mac down-target $phy_tx $mac set bandwidth_ $mac_bw; #$mac nodes $opt(nn) # # Physical Layer # $phy_rx up-target $mac $phy_tx linkhead $linkhead $phy_rx linkhead $linkhead $phy_tx node $self ;# Bind node <---> interface $phy_rx node $self ;# Bind node <---> interface # If we have a channel to attach this link to (for an point-to-point # ISL), do so here if {$outchan != "" && $inchan != ""} { $phy_tx channel $outchan $phy_rx channel $inchan # Add to list of Phys receiving on the channel $inchan addif $phy_rx } return $t } Node/SatNode instproc set_uplink {chan} { $self instvar uplink_ set uplink_ [new $chan] $self cmd set_uplink $uplink_ } Node/SatNode instproc set_downlink {chan} { $self instvar downlink_ set downlink_ [new $chan] $self cmd set_downlink $downlink_ } # Attaches channel to the phy indexed by "index" (by default, the first one) Node/SatNode instproc attach-to-outlink {chan {index 0} } { $self instvar phy_tx_ mac_ $phy_tx_($index) channel $chan #$mac_($index) channel $chan; # For mac addr resolution (rather than ARP) } # Attaches channel to the phy indexed by "index" (by default, the first one) Node/SatNode instproc attach-to-inlink { chan {index 0}} { $self instvar phy_rx_ $phy_rx_($index) channel $chan $chan addif $phy_rx_($index) } # Attaches error model to interface "index" (by default, the first one) Node/SatNode instproc interface-errormodel { em { index 0 } } { $self instvar mac_ ll_ em_ linkhead_ $mac_($index) up-target $em $em target $ll_($index) $em drop-target [new Agent/Null]; # otherwise, packet is only marked set em_($index) $em $linkhead_($index) seterrmodel $em } # ====================================================================== # # methods for routing # # ====================================================================== # Create a network interface (for one uplink and downlink), add routing agent # At least one interface must be created before routing agent is added Node/SatNode instproc create-ragent {} { set ragent [new Agent/SatRoute] $self attach $ragent 255; # attaches to default target of classifier $ragent set myaddr_ [$self set id_] $self set_ragent $ragent; # sets pointer at C++ level $ragent set_node $self; # sets back pointer in ragent to node } # When running all routing in C++, we want a dummy OTcl routing # Class Agent/rtProto/Dummy -superclass Agent/rtProto Agent/rtProto/Dummy proc init-all args { # Nothing } # ====================================================================== # # methods for tracing # # ====================================================================== Simulator instproc trace-all-satlinks {f} { $self instvar Node_ foreach nn [array names Node_] { if {![$Node_($nn) info class Node/SatNode]} { continue; # Not a SatNode } $Node_($nn) trace-all-satlinks $f } } # All satlinks should have an interface indexed by nifs_ Node/SatNode instproc trace-all-satlinks {f} { $self instvar nifs_ enqT_ rcvT_ linkhead_ for {set i 0} {$i < $nifs_} {incr i} { if {[$linkhead_($i) set type_] == "gsl-repeater"} { continue; } if {[info exists enqT_($i)]} { puts "Tracing already exists on node [$self id]" } else { $self trace-outlink-queue $f $i } if {[info exists rcvT_($i)]} { puts "Tracing already exists on node [$self id]" } else { $self trace-inlink-queue $f $i } } } # Set up trace objects around first output queue for packets destined to node Node/SatNode instproc trace-outlink-queue {f {index_ 0} } { $self instvar id_ enqT_ deqT_ drpT_ mac_ ll_ ifq_ drophead_ set ns [Simulator instance] set fromNode_ $id_ set toNode_ -1 set enqT_($index_) [$ns create-trace Sat/Enque $f $fromNode_ $toNode_] $enqT_($index_) target $ifq_($index_) $ll_($index_) down-target $enqT_($index_) set deqT_($index_) [$ns create-trace Sat/Deque $f $fromNode_ $toNode_] $deqT_($index_) target $mac_($index_) $ifq_($index_) target $deqT_($index_) set drpT_($index_) [$ns create-trace Sat/Drop $f $fromNode_ $toNode_] $drpT_($index_) target [$drophead_($index_) target] $drophead_($index_) target $drpT_($index_) $ifq_($index_) drop-target $drpT_($index_) } # Trace element between mac and ll tracing packets between node and node Node/SatNode instproc trace-inlink-queue {f {index_ 0} } { $self instvar id_ rcvT_ mac_ ll_ phy_rx_ em_ errT_ set ns [Simulator instance] set toNode_ $id_ set fromNode_ -1 if {[info exists em_($index_)]} { # if error model, then chain mac -> em -> rcvT -> ll # First, set up an error trace on the ErrorModule set errT_($index_) [$ns create-trace Sat/Error $f $fromNode_ $toNode_] $errT_($index_) target [$em_($index_) drop-target] $em_($index_) drop-target $errT_($index_) set rcvT_($index_) [$ns create-trace Sat/Recv $f $fromNode_ $toNode_] $rcvT_($index_) target [$em_($index_) target] $em_($index_) target $rcvT_($index_) } else { # if no error model, then insert between mac and ll set rcvT_($index_) [$ns create-trace Sat/Recv $f $fromNode_ $toNode_] $rcvT_($index_) target [$mac_($index_) up-target] $mac_($index_) up-target $rcvT_($index_) } } ########### # TRACE MODIFICATIONS ########## # This creates special satellite tracing elements # See __FILE__.cc # Support for Enque, Deque, Recv, Drop, Generic (not Session) Class Trace/Sat/Hop -superclass Trace/Sat Trace/Sat/Hop instproc init {} { $self next "h" } Class Trace/Sat/Enque -superclass Trace/Sat Trace/Sat/Enque instproc init {} { $self next "+" } Trace/Sat/Deque instproc init {} { $self next "-" } Class Trace/Sat/Recv -superclass Trace/Sat Trace/Sat/Recv instproc init {} { $self next "r" } Class Trace/Sat/Drop -superclass Trace/Sat Trace/Sat/Drop instproc init {} { $self next "d" } Class Trace/Sat/Error -superclass Trace/Sat Trace/Sat/Error instproc init {} { $self next "e" } Class Trace/Sat/Generic -superclass Trace/Sat Trace/Sat/Generic instproc init {} { $self next "v" } # ====================================================================== # # Defaults for bound variables # # ====================================================================== Node/SatNode set dist_routing_ "false"; # distributed routing not yet supported Position/Sat set time_advance_ 0; # time offset to start of simulation Position/Sat/Polar set plane_ 0 HandoffManager/Term set elevation_mask_ 0 HandoffManager/Term set term_handoff_int_ 10 HandoffManager/Sat set sat_handoff_int_ 10 HandoffManager/Sat set latitude_threshold_ 70 HandoffManager/Sat set longitude_threshold_ 0 HandoffManager set handoff_randomization_ "false" SatRouteObject set metric_delay_ "true" SatRouteObject set data_driven_computation_ "false" Mac/Sat/UnslottedAloha set mean_backoff_ 1s; # mean backoff time upon collision Mac/Sat/UnslottedAloha set rtx_limit_ 3; # Retransmission limit Mac/Sat/UnslottedAloha set send_timeout_ 270ms; # Timer interval for new sends Agent/SatRoute set myaddr_ 0 ;# My address Mac/Sat set bandwidth_ 2Mb #source ns-wireless-mip.tcl # # 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. # # @(#) $Header$ # proc mvar args { upvar self _s uplevel $_s instvar $args } proc bw_parse { bspec } { if { [scan $bspec "%f%s" b unit] == 1 } { set unit bps } regsub {[/p]s(ec)?$} $unit {} unit if [string match {*B} $unit] { set b [expr $b*8] set unit "[string trimright B $unit]b" } switch $unit { b { return $b } kb { return [expr $b*1000] } Mb { return [expr $b*1000000] } Gb { return [expr $b*1000000000] } default { puts "error: bw_parse: unknown unit `$unit'" exit 1 } } } proc time_parse { spec } { if { [scan $spec "%f%s" t unit] == 1 } { set unit s } regsub {sec$} $unit {s} unit switch $unit { s { return $t } ms { return [expr $t*1e-3] } us { return [expr $t*1e-6] } ns { return [expr $t*1e-9] } ps { return [expr $t*1e-12] } default { puts "error: time_parse: unknown unit `$unit'" exit 1 } } } Session/RTP set uniq_srcid 0 Session/RTP proc alloc_srcid {} { set id [Session/RTP set uniq_srcid] Session/RTP set uniq_srcid [expr $id+1] return $id } Session/RTP instproc init {} { $self next mvar dchan_ cchan_ set cchan_ [new Agent/RTCP] set dchan_ [new Agent/CBR/RTP] $dchan_ set packetSize_ 512 $dchan_ session $self $cchan_ session $self $self set rtcp_timer_ [new RTCPTimer $self] mvar srcid_ localsrc_ set srcid_ [Session/RTP alloc_srcid] set localsrc_ [new RTPSource $srcid_] $self localsrc $localsrc_ $self set srctab_ $localsrc_ $self set stopped_ 1 } Session/RTP instproc start {} { mvar group_ if ![info exists group_] { puts "error: can't transmit before joining group!" exit 1 } mvar cchan_ $cchan_ start } Session/RTP instproc stop {} { $self instvar cchan_ dchan_ $dchan_ stop $cchan_ stop $self set stopped_ 1 } Session/RTP instproc report-interval { i } { mvar cchan_ $cchan_ set interval_ $i } Session/RTP instproc bye {} { mvar cchan_ dchan_ $dchan_ stop $cchan_ bye } Session/RTP instproc attach-node { node } { mvar dchan_ cchan_ global ns $ns attach-agent $node $dchan_ $ns attach-agent $node $cchan_ $self set node_ $node } Session/RTP instproc detach-node { node } { mvar dchan_ cchan_ global ns $ns detach-agent $node $dchan_ $ns detach-agent $node $cchan_ $self unset node_ } # Hook to enable easy syncronization with RTCP timeouts. Session/RTP instproc rtcp_timeout {} { mvar rtcp_timeout_callback_ if [info exists rtcp_timeout_callback_] { eval $rtcp_timeout_callback_ } } Session/RTP instproc join-group { g } { set g [expr $g] $self set group_ $g mvar node_ dchan_ cchan_ $dchan_ set dst_ $g $node_ join-group $dchan_ $g incr g $cchan_ set dst_ $g $node_ join-group $cchan_ $g } Session/RTP instproc leave-group { } { mvar group_ node_ cchan_ dchan_ $node_ leave-group $dchan_ $group_ $node_ leave-group $cchan_ [expr $group_+1] $self unset group_ } Session/RTP instproc session_bw { bspec } { set b [bw_parse $bspec] $self set session_bw_ $b mvar rtcp_timer_ $rtcp_timer_ session-bw $b } Session/RTP instproc transmit { bspec } { set b [bw_parse $bspec] #mvar srcid_ #global ns #puts "[$ns now] $self $srcid_ transmit $b" $self set txBW_ $b $self instvar dchan_ stopped_ if { $b == 0 } { $dchan_ stop set stopped_ 1 } set ps [$dchan_ set packetSize_] $dchan_ set interval_ [expr 8.*$ps/$b] if { $stopped_ == 1 } { $dchan_ start set stopped_ 0 } else { $dchan_ rate-change } } Session/RTP instproc sample-size { cc } { mvar rtcp_timer_ $rtcp_timer_ sample-size $cc } Session/RTP instproc adapt-timer { nsrc nrr we_sent } { mvar rtcp_timer_ $rtcp_timer_ adapt $nsrc $nrr $we_sent } Session/RTP instproc new-source { srcid } { set src [new RTPSource $srcid] $self enter $src mvar srctab_ lappend srctab_ $src return $src } Class RTCPTimer RTCPTimer instproc init { session } { $self next # Could make most of these class instvars. mvar session_bw_fraction_ min_rpt_time_ inv_sender_bw_fraction_ mvar inv_rcvr_bw_fraction_ size_gain_ avg_size_ inv_bw_ set session_bw_fraction_ 0.05 # XXX just so we see some reports in short sim's... set min_rpt_time_ 1. set sender_bw_fraction 0.25 set rcvr_bw_fraction [expr 1. - $sender_bw_fraction] set inv_sender_bw_fraction_ [expr 1. / $sender_bw_fraction] set inv_rcvr_bw_fraction_ [expr 1. / $rcvr_bw_fraction] set size_gain_ 0.125 set avg_size_ 128. set inv_bw_ 0. mvar session_ set session_ $session # Schedule a timer for our first report using half the # min ctrl interval. This gives us some time before # our first report to learn about other sources so our # next report interval will account for them. The avg # ctrl size was initialized to 128 bytes which is # conservative (it assumes everyone else is generating # SRs instead of RRs). mvar min_rtp_time_ avg_size_ inv_bw_ set rint [expr 8*$avg_size_ * $inv_bw_] set t [expr $min_rpt_time_ / 2.] if { $rint < $t } { set rint $t } $session_ report-interval $rint } RTCPTimer instproc sample-size { cc } { mvar avg_size_ size_gain_ set avg_size_ [expr $avg_size_ + $size_gain_ * ($cc + 28 - $avg_size_)] } RTCPTimer instproc adapt { nsrc nrr we_sent } { mvar inv_bw_ avg_size_ min_rpt_time_ mvar inv_sender_bw_fraction_ inv_rcvr_bw_fraction_ # Compute the time to the next report. we do this here # because we need to know if there were any active sources # during the last report period (nrr above) & if we were # a source. The bandwidth limit for ctrl traffic was set # on startup from the session bandwidth. It is the inverse # of bandwidth (ie., ms/byte) to avoid a divide below. set ibw $inv_bw_ if { $nrr > 0 } { if { $we_sent } { set ibw [expr $ibw * $inv_sender_bw_fraction_] set nsrc $nrr } else { set ibw [expr $ibw * $inv_rcvr_bw_fraction_] incr nsrc -$nrr } } set rint [expr 8*$avg_size_ * $nsrc * $ibw] if { $rint < $min_rpt_time_ } { set rint $min_rpt_time_ } mvar session_ $session_ report-interval $rint } RTCPTimer instproc session-bw { b } { $self set inv_bw_ [expr 1. / $b ] } Agent/RTCP set interval_ 0. Agent/RTCP set random_ 0 Agent/RTCP set class_ 32 RTPSource set srcid_ -1 # # tcl/interface/ns-iface.tcl # # 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 # # NetworkInterface set ifacenum_ 0 NetworkInterface proc getid {} { $self instvar ifacenum_ return [incr ifacenum_] } NetworkInterface instproc init {} { $self next $self cmd label [NetworkInterface getid] } # # 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, http://daedalus.cs.berkeley.edu # #default channel propagation delay (for a LAN) Channel set delay_ 4us Classifier/Mac set bcast_ 0 #default bandwidth setting done during mac initialisation (c++) Mac set bandwidth_ 2Mb Mac set delay_ 0us Mac set off_mac_ 0 # WaveLAN settings (also inherited by Csma/Ca, which is what WaveLAN is) #Mac/Csma set delay_ 64us #Mac/Csma set bandwidth_ 2Mb #Mac/Csma set hlen_ 20 #Mac/Csma set ifs_ 16us #Mac/Csma set slotTime_ 16us #Mac/Csma set cwmin_ 16 #Mac/Csma set cwmax_ 1024 #Mac/Csma set rtxLimit_ 16 #Mac/Csma set csense_ 1 # 10 Mbps Ethernet settings #Mac/Csma/Cd set bandwidth_ 10Mb #Mac/Csma/Cd set ifs_ 52us #Mac/Csma/Cd set slotTime_ 52us #Mac/Csma/Cd set cwmin_ 1 # IEEE 802.11 MAC settings if [TclObject is-class Mac/802_11] { Mac/802_11 set delay_ 64us Mac/802_11 set ifs_ 16us Mac/802_11 set slotTime_ 16us Mac/802_11 set cwmin_ 16 Mac/802_11 set cwmax_ 1024 Mac/802_11 set rtxLimit_ 16 Mac/802_11 set bssId_ -1 Mac/802_11 set sifs_ 8us Mac/802_11 set pifs_ 12us Mac/802_11 set difs_ 16us Mac/802_11 set rtxAckLimit_ 1 Mac/802_11 set rtxRtsLimit_ 3 } # IEEE 802.14 MAC settings if [TclObject is-class Mac/Mcns] { Mac/Mcns set bandwidth_ 10Mb Mac/Mcns set hlen_ 6 Mac/Mcns set bssId_ -1 Mac/Mcns set slotTime_ 10us } # Multihop wireless MAC modeled after Metricom's Ricochet if [TclObject is-class Mac/Multihop] { Mac/Multihop set bandwidth_ 100Kb Mac/Multihop set delay_ 10ms Mac/Multihop set tx_rx_ 11.125ms Mac/Multihop set rx_tx_ 13.25ms Mac/Multihop set rx_rx_ 10.5625 Mac/Multihop set backoffBase_ 20ms Mac/Multihop set hlen_ 16 } # The MAC classifier (to demux incoming packets to the correct LL object) Mac instproc classify-macs {peerinfo} { set peerlabel [lindex $peerinfo 0] set peerll [lindex $peerinfo 1] $self instvar mclass_ set mclass_ [new Classifier/Mac] $mclass_ install $peerlabel $peerll $self target $mclass_ } # XXX this belongs in ns-node.tcl # Hook up the MACs together at a given Node Node instproc addmac {mac} { $self instvar machead_ mactail_ if ![info exists mactail_] { set mactail_ [set machead_ $mac] $mac maclist $mactail_ } else { $mactail_ maclist $mac $mac maclist $machead_ set mactail_ $mac } } # # Copyright (c) 1997, 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 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, http://daedalus.cs.berkeley.edu # # Defaults for link-layer LL set bandwidth_ 0 ;# not used LL set delay_ 1ms LL set macDA_ 0 if [TclObject is-class LL/Arq] { LL/Arq set mode_ 2 LL/Arq set hlen_ 16 LL/Arq set slen_ 1400 LL/Arq set limit_ 8 LL/Arq set timeout_ 100ms Class LL/Rlp -superclass LL/Arq LL/Rlp set mode_ 1 LL/Rlp set hlen_ 6 LL/Rlp set slen_ 30 LL/Rlp set limit_ 63 LL/Rlp set timeout_ 500ms LL/Rlp set delay_ 70ms } # Snoop variables if [TclObject is-class Snoop] { Snoop set snoopTick_ 0.1 Snoop set snoopDisable_ 0 Snoop set srtt_ 0.1 Snoop set rttvar_ 0.25 Snoop set g_ 0.125 Snoop set tailTime_ 0 Snoop set rxmitStatus_ 0 Snoop set lru_ 0 Snoop set maxbufs_ 0 } if [TclObject is-class LL/LLSnoop] { LL/LLSnoop set integrate_ 0 LL/LLSnoop set delay_ 0ms Snoop set srtt_ 0.1 Snoop set rttvar_ 0.25 Snoop set g_ 0.125 LL/LLSnoop set snoopTick_ 0.1 } # Get snoop agent (or else create one automatically LL/LLSnoop instproc get-snoop { src dst } { $self instvar snoops_ off_ll_ delay_ if { ![info exists snoops_($src:$dst)] } { # make a new snoop agent if none exists # puts "making new snoop $src $dst" set snoops_($src:$dst) [new Snoop] } # make snoop's llsnoop_ ourself, and make it's recvtarget_ same as ours $snoops_($src:$dst) llsnoop $self $snoops_($src:$dst) set delay_ $delay_ return $snoops_($src:$dst) } # Integrate snoop processing across concurrent active connections LL/LLSnoop instproc integrate { src dst } { $self instvar snoops_ set conn $src:$dst if {![info exists snoops_($conn)]} { return } set snoop $snoops_($conn) set threshtime [$snoop set tailTime_] foreach a [array names snoops_] { # go through other conns checking for rxmit possibilities if { $a != $conn } { $snoops_($a) check-rxmit $threshtime if { [$snoops_($a) set rxmitStatus_] == 2 } { # We just did a retransmission, don't be # too aggressive on rxmissions. This follows # standard conservation of packets. break; } } } } # # vlan.tcl # # 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. # # LanNode---------------------------------------------------- # # Lan implementation as a virtual node: LanNode mimics a real # node and uses an address (id) from Node's address space # # WARNING: if used with hierarchical routing, one has to assign # a hierarchical address to the lan itself. This maybe confusing. #------------------------------------------------------------ Class LanNode LanNode set ifqType_ Queue/DropTail LanNode set llType_ LL #LanNode set macType_ Mac/Csma/Cd LanNode set macType_ Mac LanNode set chanType_ Channel LanNode set phyType_ Phy/WiredPhy LanNode set address_ "" LanNode instproc address {val} { $self set address_ $val } LanNode instproc bw {val} { $self set bw_ $val } LanNode instproc delay {val} { $self set delay_ $val } LanNode instproc ifqType {val} { $self set ifqType_ $val } LanNode instproc llType {val} { $self set llType_ $val } LanNode instproc macType {val} { $self set macType_ $val } LanNode instproc chanType {val} { $self set chanType_ $val } LanNode instproc phyType {val} { $self set phyType_ $val } LanNode instproc init {ns args} { set args [eval $self init-vars $args] $self instvar bw_ delay_ ifqType_ llType_ macType_ chanType_ $self instvar phyType_ $self instvar ns_ nodelist_ defRouter_ cost_ $self instvar id_ address_ channel_ mcl_ varp_ $ns instvar Node_ $self next set ns_ $ns set nodelist_ "" set cost_ 1 set id_ [Node getid] set Node_($id_) $self if [Simulator set EnableHierRt_] { if {$address_ == ""} { error "LanNode: use \"-address\" option \ with hierarchical routing" } } else { set address_ $id_ } set defRouter_ [new LanRouter $ns $self] if [$ns multicast?] { set switch_ [new Classifier/Hash/Dest 32] $switch_ set mask_ [AddrParams set McastMask_] $switch_ set shift_ [AddrParams set McastShift_] $defRouter_ switch $switch_ } set channel_ [new $chanType_] set varp_ [new VARPTable] #set mcl_ [new Classifier/Mac] #$mcl_ set offset_ [PktHdr_offset PacketHeader/Mac macDA_] #$channel_ target $mcl_ } LanNode instproc addNode {nodes bw delay {llType ""} {ifqType ""} \ {macType ""} {phyType ""}} { $self instvar ifqType_ llType_ macType_ chanType_ phyType_ $self instvar id_ channel_ mcl_ lanIface_ $self instvar ns_ nodelist_ cost_ varp_ $ns_ instvar link_ Node_ if {$ifqType == ""} { set ifqType $ifqType_ } if {$macType == ""} { set macType $macType_ } if {$llType == ""} { set llType $llType_ } if {$phyType == ""} { set phyType $phyType_ } set vlinkcost [expr $cost_ / 2.0] foreach src $nodes { set nif [new LanIface $src $self \ -ifqType $ifqType \ -llType $llType \ -macType $macType \ -phyType $phyType] set tr [$ns_ get-ns-traceall] if {$tr != ""} { $nif trace $ns_ $tr } set tr [$ns_ get-nam-traceall] if {$tr != ""} { $nif nam-trace $ns_ $tr } set ll [$nif set ll_] $ll set delay_ $delay $ll varp $varp_ $varp_ mac-addr [[$nif set node_] id] \ [[$nif set mac_] id] set phy [$nif set phy_] $phy node $src $phy channel $channel_ $channel_ addif $phy $phy set bandwidth_ $bw set lanIface_($src) $nif $src add-neighbor $self set sid [$src id] set link_($sid:$id_) [new Vlink $ns_ $self $src $self $bw 0] set link_($id_:$sid) [new Vlink $ns_ $self $self $src $bw 0] $src add-oif [$link_($sid:$id_) head] $link_($sid:$id_) $src add-iif [[$nif set iface_] label] $link_($id_:$sid) [$link_($sid:$id_) head] set link_ $link_($sid:$id_) $link_($sid:$id_) queue [$nif set ifq_] $link_($id_:$sid) queue [$nif set ifq_] $link_($sid:$id_) set iif_ [$nif set iface_] $link_($id_:$sid) set iif_ [$nif set iface_] $link_($sid:$id_) cost $vlinkcost $link_($id_:$sid) cost $vlinkcost } set nodelist_ [concat $nodelist_ $nodes] } LanNode instproc assign-mac {ip} { return $ip ;# use ip addresses at MAC layer } LanNode instproc cost c { $self instvar ns_ nodelist_ id_ cost_ $ns_ instvar link_ set cost_ $c set vlinkcost [expr $c / 2.0] foreach node $nodelist_ { set nid [$node id] $link_($id_:$nid) cost $vlinkcost $link_($nid:$id_) cost $vlinkcost } } LanNode instproc cost? {} { $self instvar cost_ return $cost_ } LanNode instproc rtObject? {} { # NOTHING } LanNode instproc id {} { $self set id_ } LanNode instproc node-addr {{addr ""}} { eval $self set address_ $addr } LanNode instproc reset {} { # NOTHING: needed for node processing by ns routing } LanNode instproc is-lan? {} { return 1 } LanNode instproc dump-namconfig {} { # Redefine this function if want a different lan layout $self instvar ns_ bw_ delay_ nodelist_ id_ $ns_ puts-nam-config \ "X -t * -n $id_ -r $bw_ -D $delay_ -o left" set cnt 0 set LanOrient(0) "up" set LanOrient(1) "down" foreach n $nodelist_ { $ns_ puts-nam-config \ "L -t * -s $id_ -d [$n id] -o $LanOrient($cnt)" set cnt [expr 1 - $cnt] } } LanNode instproc init-outLink {} { #NOTHING } LanNode instproc start-mcast {} { # NOTHING } LanNode instproc getArbiter {} { # NOTHING } LanNode instproc attach {agent} { # NOTHING } LanNode instproc add-route {args} { # NOTHING: use defRouter to find routes } LanNode instproc add-hroute {args} { # NOTHING: use defRouter to find routes } LanNode instproc split-addrstr addrstr { set L [split $addrstr .] return $L } # LanIface--------------------------------------------------- # # node's interface to a LanNode #------------------------------------------------------------ Class LanIface LanIface set ifqType_ Queue/DropTail #LanIface set macType_ Mac/Csma/Cd LanIface set macType_ Mac LanIface set llType_ LL LanIface set phyType_ Phy/WiredPhy LanIface instproc llType {val} { $self set llType_ $val } LanIface instproc ifqType {val} { $self set ifqType_ $val } LanIface instproc macType {val} { $self set macType_ $val } LanIface instproc phyType {val} { $self set phyType_ $val } LanIface instproc entry {} { $self set entry_ } LanIface instproc init {node lan args} { set args [eval $self init-vars $args] eval $self next $args $self instvar llType_ ifqType_ macType_ phyType_ $self instvar node_ lan_ ifq_ mac_ ll_ phy_ $self instvar iface_ entry_ drophead_ set node_ $node set lan_ $lan set ll_ [new $llType_] set ifq_ [new $ifqType_] set mac_ [new $macType_] set iface_ [new NetworkInterface] set phy_ [new $phyType_] set entry_ [new Connector] set drophead_ [new Connector] $ll_ set macDA_ -1 ;# bcast address if there is no LAN router $ll_ lanrouter [$lan set defRouter_] $ll_ up-target $iface_ $ll_ down-target $ifq_ $ll_ mac $mac_ $ifq_ target $mac_ #$mac_ target $ll_ $mac_ up-target $ll_ $mac_ down-target $phy_ $mac_ netif $phy_ $phy_ up-target $mac_ $node addInterface $iface_ $iface_ target [$node entry] $entry_ target $ll_ set ns [Simulator instance] #drophead is the same for all drops in the lan $drophead_ target [$ns set nullAgent_] $ifq_ drop-target $drophead_ $mac_ drop-target $drophead_ $ll_ drop-target $drophead_ } LanIface instproc trace {ns f {op ""}} { $self instvar hopT_ rcvT_ enqT_ deqT_ drpT_ $self instvar iface_ entry_ node_ lan_ drophead_ $self instvar ll_ ifq_ set hopT_ [$ns create-trace Hop $f $node_ $lan_ $op] set rcvT_ [$ns create-trace Recv $f $lan_ $node_ $op] set enqT_ [$ns create-trace Enque $f $node_ $lan_ $op] set deqT_ [$ns create-trace Deque $f $node_ $lan_ $op] set drpT_ [$ns create-trace Drop $f $node_ $lan_ $op] $hopT_ target [$entry_ target] $entry_ target $hopT_ $rcvT_ target [$iface_ target] $iface_ target $rcvT_ $enqT_ target [$ll_ down-target] $ll_ down-target $enqT_ $deqT_ target [$ifq_ target] $ifq_ target $deqT_ $drpT_ target [$drophead_ target] $drophead_ target $drpT_ } # should be called after LanIface::trace LanIface instproc nam-trace {ns f} { $self instvar hopT_ rcvT_ enqT_ deqT_ drpT_ if [info exists hopT_] { $hopT_ namattach $f } else { $self trace $ns $f "nam" } $rcvT_ namattach $f $enqT_ namattach $f $deqT_ namattach $f $drpT_ namattach $f } LanIface instproc add-receive-filter filter { $self instvar mac_ $filter target [$mac_ target] $mac_ target $filter } # Vlink------------------------------------------------------ # # Virtual link implementation. Mimics a Simple Link but with # zero delay and infinite bandwidth #------------------------------------------------------------ Class Vlink Vlink instproc up? {} { return "up" } Vlink instproc queue {{q ""}} { eval $self set queue_ $q } Vlink instproc init {ns lan src dst b d} { $self instvar ns_ lan_ src_ dst_ bw_ delay_ set ns_ $ns set lan_ $lan set src_ $src set dst_ $dst set bw_ $b set delay_ $d } Vlink instproc src {} { $self set src_ } Vlink instproc dst {} { $self set dst_ } Vlink instproc dump-nam-queueconfig {} { #NOTHING } Vlink instproc head {} { $self instvar lan_ dst_ src_ if {$src_ == $lan_ } { # if this is a link FROM the lan vnode, # it doesn't matter what we return, because # it's only used by $lan add-route (empty) return "" } else { # if this is a link TO the lan vnode, # return the entry to the lanIface object set src_lif [$lan_ set lanIface_($src_)] return [$src_lif entry] } } Vlink instproc cost c { $self set cost_ $c} Vlink instproc cost? {} { $self instvar cost_ if ![info exists cost_] { return 1 } return $cost_ } # LanRouter-------------------------------------------------- # # "Virtual node lan" needs to know which of the lan nodes is # the next hop towards the packet's (maybe remote) destination. #------------------------------------------------------------ LanRouter instproc init {ns lan} { $self next Simulator instvar EnableHierRt_ if {$EnableHierRt_} { $self routing hier } else { $self routing flat } $self lanaddr [$lan node-addr] $self routelogic [$ns get-routelogic] } Node instproc is-lan? {} { return 0 } # # newLan: create a LAN from a sete of nodes # Simulator instproc newLan {nodelist bw delay args} { set lan [eval new LanNode $self -bw $bw -delay $delay $args] $lan addNode $nodelist $bw $delay return $lan } # For convenience, use make-lan. For more fine-grained control, # use newLan instead of make-lan. #{macType Mac/Csma/Cd} -> for now support for only Mac Simulator instproc make-lan {nodelist bw delay \ {llType LL} \ {ifqType Queue/DropTail} \ {macType Mac} \ {chanType Channel} \ {phyType Phy/WiredPhy}} { if {[string compare $macType "Mac/Csma/Cd"] == 0} { puts "Warning: Mac/Csma/Cd is out of date" puts "Warning: Please use Mac/802_3 to replace Mac/Csma/Cd" set macType "Mac/802_3" } set lan [new LanNode $self \ -bw $bw \ -delay $delay \ -llType $llType \ -ifqType $ifqType \ -macType $macType \ -chanType $chanType \ -phyType $phyType] $lan addNode $nodelist $bw $delay $llType $ifqType $macType \ $phyType return $lan } # # A simple timer class. You can derive a subclass of Timer # to provide a simple mechanism for scheduling events: # # $self sched $delay -- causes "$self timeout" to be called # $delay seconds in the future # $self cancel -- cancels any pending scheduled callback # Class Timer Timer instproc init { ns } { $self set ns_ $ns } # sched is the same as resched; the previous setting is cancelled # and another event is scheduled. No state is kept for the timers. # This is different than the C++ timer API in timer-handler.cc,h; where a # sched aborts if the timer is already set. C++ timers maintain state # (e.g. IDLE, PENDING..etc) that is checked before the timer is scheduled. Timer instproc sched delay { $self instvar ns_ $self instvar id_ $self cancel set id_ [$ns_ after $delay "$self timeout"] } Timer instproc destroy {} { $self cancel } Timer instproc cancel {} { $self instvar ns_ $self instvar id_ if [info exists id_] { $ns_ cancel $id_ unset id_ } } # resched and expire are added to have a similar API to C++ timers. Timer instproc resched delay { $self sched $delay } # the subclass must provide the timeout function Timer instproc expire {} { $self timeout } # Interface timers Class Timer/Iface -superclass Timer Timer/Iface instproc init { protocol source group oiface sim} { $self instvar proto_ src_ grp_ oif_ $self next $sim set proto_ $protocol set src_ $source set grp_ $group set oif_ $oiface } Timer/Iface instproc schedule {} { $self sched [[$self info class] set timeout] } # # tcl/mcast/ns-mcast.tcl # # 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 # # ############### # The MultiSim stuff below is only for backward compatibility. Class MultiSim -superclass Simulator MultiSim instproc init args { eval $self next $args $self multicast on } Simulator instproc multicast args { $self set multiSim_ 1 } Simulator instproc multicast? {} { $self instvar multiSim_ if { ![info exists multiSim_] } { set multiSim_ 0 } set multiSim_ } Simulator instproc run-mcast {} { $self instvar Node_ foreach n [array names Node_] { set node $Node_($n) $node start-mcast } $self next } Simulator instproc clear-mcast {} { $self instvar Node_ foreach n [array names Node_] { $Node_($n) stop-mcast } } Simulator instproc mrtproto { mproto { nodelist "" } } { $self instvar Node_ MrtHandle_ set MrtHandle_ "" if { $mproto == "CtrMcast" } { set MrtHandle_ [new CtrMcastComp $self] $MrtHandle_ set ctrrpcomp [new CtrRPComp $self] } if { $mproto == "BST" } { foreach n [array names Node_] { if ![$Node_($n) is-lan?] { $Node_($n) instvar multiclassifier_ switch_ # delete $multiclassifier_ set multiclassifier_ [new Classifier/Multicast/Replicator/BST] [$Node_($n) set multiclassifier_] set node_ $Node_($n) $switch_ install 1 $multiclassifier_ } } } if { $nodelist == "" } { foreach n [array names Node_] { $self mrtproto-iifs $mproto $Node_($n) "" } } else { foreach node $nodelist { $self mrtproto-iifs $mproto $node "" } } $self at 0.0 "$self run-mcast" return $MrtHandle_ } #finer control than mrtproto: specify which iifs protocols owns Simulator instproc mrtproto-iifs {mproto node iiflist } { set mh [new $mproto $self $node] set arbiter [$node getArbiter] if { $arbiter != "" } { $arbiter addproto $mh $iiflist } } Node proc allocaddr {} { # return a unique mcast address set addr [Simulator set McastAddr_] Simulator set McastAddr_ [expr $addr + 1] return $addr } Node proc expandaddr {} { # reset the bit used by mcast/unicast switch to expand # number of nodes that can be used #Simulator set McastShift_ 30 #Simulator set McastAddr_ [expr 1 << 30] # calling set-address-format with expanded option (sets nodeid with 21 bits # & sets aside 1 bit for mcast) and sets portid with 8 bits # if hierarchical address format is set, just expands the McastAddr_ #if ![Simulator set EnableHierRt_] { set ns [Simulator instance] $ns set-address-format expanded puts "Backward compatibility: Use \"set-address-format expanded\" instead of \"Node expandaddr\";" #} #set mcastshift [AddrParams set McastShift_] #Simulator set McastAddr_ [expr 1 << $mcastshift] #mrtObject expandaddr } Node instproc start-mcast {} { $self instvar mrtObject_ $mrtObject_ start } Node instproc getArbiter {} { $self instvar mrtObject_ if [info exists mrtObject_] { return $mrtObject_ } return "" } Node instproc notify-mcast changes { $self instvar mrtObject_ if [info exists mrtObject_] { $mrtObject_ notify $changes } } Node instproc stop-mcast {} { $self instvar mrtObject_ $self clear-caches $mrtObject_ stop } Node instproc clear-caches {} { $self instvar Agents_ multiclassifier_ replicator_ $multiclassifier_ clearAll $multiclassifier_ set nrep_ 0 foreach var {Agents_ replicator_} { $self instvar $var if { [info exists $var] } { delete $var unset $var } } # XXX watch out for memory leaks } Node instproc dump-routes args { $self instvar mrtObject_ if { [info exists mrtObject_] } { eval $mrtObject_ dump-routes $args } } Node instproc check-local { group } { $self instvar Agents_ if [info exists Agents_($group)] { return [llength $Agents_($group)] } return 0 } Node instproc new-group { src group iface code } { $self instvar mrtObject_ $mrtObject_ upcall $code $src $group $iface } Node instproc join-group { agent group { src "" } } { $self instvar replicator_ Agents_ mrtObject_ set group [expr $group] ;# use expr to convert to decimal $mrtObject_ join-group $group $src lappend Agents_($group) $agent if { $src == "" } { set reps [$self getReps "*" $group] } else { set reps [$self getReps $src $group] } foreach rep $reps { # make sure agent is enabled in each replicator for this group $rep insert $agent } } Node instproc leave-group { agent group { src "" } } { $self instvar replicator_ Agents_ mrtObject_ set group [expr $group] ;# use expr to get rid of possible leading 0x if { $src == "" } { set reps [$self getReps "*" $group] } else { set reps [$self getReps $src $group] } foreach rep $reps { $rep disable $agent } if [info exists Agents_($group)] { set k [lsearch -exact $Agents_($group) $agent] set Agents_($group) [lreplace $Agents_($group) $k $k] $mrtObject_ leave-group $group $src } else { warn "cannot leave a group without joining it" } } #Node instproc join-group-source { agent group source } { # $self instvar Agents_ mrtObject_ replicator_ # set group [expr $group] # ## send a message for the mcastproto agent to inform the mcast protocols # $mrtObject_ join-group $group $source # lappend Agents_($source:$group) $agent # if [info exists replicator_($source:$group)] { # $replicator_($source:$group) insert $agent # } #} #Node instproc leave-group-source { agent group source } { # $self instvar replicator_ Agents_ mrtObject_ # set group [expr $group] # if [info exists replicator_($source:$group)] { # $replicator_($source:$group) disable $agent # } # $mrtObject_ leave-group $group $source #} Node instproc add-mfc { src group iif oiflist } { $self instvar multiclassifier_ \ replicator_ Agents_ if [info exists replicator_($src:$group)] { set r $replicator_($src:$group) } else { set r [new Classifier/Replicator/Demuxer] $r set srcID_ $src $r set grp_ $group set replicator_($src:$group) $r $r set node_ $self # # install each agent that has previously joined this group # if [info exists Agents_($group)] { foreach a $Agents_($group) { $r insert $a } } # we also need to check Agents($srcID:$group) if [info exists Agents_($src:$group)] { foreach a $Agents_($src:$group) { $r insert $a } } # # Install the replicator. # $multiclassifier_ add-rep $r $src $group $iif } foreach oif [lsort $oiflist] { $r insert $oif } } Node instproc del-mfc { srcID group oiflist } { $self instvar replicator_ multiclassifier_ if [info exists replicator_($srcID:$group)] { set r $replicator_($srcID:$group) foreach oif $oiflist { $r disable $oif } return 1 } return 0 } #################### Class Classifier/Multicast/Replicator -superclass Classifier/Multicast # # This method called when a new multicast group/source pair # is seen by the underlying classifier/mcast object. # We install a hash for the pair mapping it to a slot # number in the classifier table and point the slot # at a replicator object that sends each packet along # the RPF tree. # Classifier/Multicast instproc new-group { src group iface code} { $self instvar node_ $node_ new-group $src $group $iface $code } Classifier/Multicast instproc no-slot slot { # NOTHING } Classifier/Multicast/Replicator instproc init args { $self next $self instvar nrep_ set nrep_ 0 } Classifier/Multicast/Replicator instproc add-rep { rep src group iif } { $self instvar nrep_ $self set-hash $src $group $nrep_ $iif $self install $nrep_ $rep incr nrep_ } ###################### Class Classifier/Replicator/Demuxer ############## Class Classifier/Replicator/Demuxer -superclass Classifier/Replicator Classifier/Replicator/Demuxer set ignore_ 0 Classifier/Replicator/Demuxer instproc init args { eval $self next $args $self instvar nslot_ nactive_ set nactive_ 0 } Classifier/Replicator/Demuxer instproc is-active {} { $self instvar nactive_ expr $nactive_ > 0 } Classifier/Replicator/Demuxer instproc insert target { $self instvar nactive_ active_ if ![info exists active_($target)] { set active_($target) -1 } if {$active_($target) < 0} { $self enable $target } } Classifier/Replicator/Demuxer instproc dump-oifs {} { set oifs "" if [$self is-active] { $self instvar active_ foreach target [array names active_] { if { $active_($target) >= 0 } { lappend oifs [$self slot $active_($target)] } } } return [lsort $oifs] } Classifier/Replicator/Demuxer instproc disable target { $self instvar nactive_ active_ if {[info exists active_($target)] && $active_($target) >= 0} { $self clear $active_($target) set active_($target) -1 incr nactive_ -1 } } Classifier/Replicator/Demuxer instproc enable target { $self instvar nactive_ active_ ignore_ if {$active_($target) < 0} { set active_($target) [$self installNext $target] incr nactive_ set ignore_ 0 } } Classifier/Replicator/Demuxer instproc exists target { $self instvar active_ info exists active_($target) } Classifier/Replicator/Demuxer instproc is-active-target target { $self instvar active_ if { [info exists active_($target)] && $active_($target) >= 0 } { return 1 } else { return 0 } } Classifier/Replicator/Demuxer instproc drop { src dst {iface -1} } { $self instvar node_ [$node_ getArbiter] drop $self $src $dst $iface } Node instproc change-iface { src dst oldiface newiface} { $self instvar multiclassifier_ $multiclassifier_ change-iface $src $dst $oldiface $newiface } Node instproc lookup-iface { src dst } { $self instvar multiclassifier_ $multiclassifier_ lookup-iface $src $dst } Classifier/Replicator/Demuxer instproc reset {} { $self instvar nactive_ active_ foreach { target slot } [array get active_] { $self clear $slot } set nactive_ 0 unset active_ } # # XXX # # # XXX These are PIM specific? Why are they here? # #Simulator instproc getNodeIDs {} { # $self instvar Node_ # return [array names Node_] #} #Simulator instproc setPIMProto { index proto } { # $self instvar pimProtos # set pimProtos($index) $proto #} # #Simulator instproc getPIMProto { index } { # $self instvar pimProtos # if [info exists pimProtos($index)] { # return $pimProtos($index) # } # return -1 #} # Agent/Mcast/Control instproc init { protocol } { $self next $self instvar proto_ set proto_ $protocol } Agent/Mcast/Control array set messages {} Agent/Mcast/Control set mcounter 0 Agent/Mcast/Control instproc send {type from src group args} { Agent/Mcast/Control instvar mcounter messages set messages($mcounter) [concat [list $from $src $group] $args] $self cmd send $type $mcounter incr mcounter } Agent/Mcast/Control instproc recv {type iface m} { Agent/Mcast/Control instvar messages eval $self recv2 $type $iface $messages($m) #unset messages($m) } Agent/Mcast/Control instproc recv2 {type iface from src group args} { $self instvar proto_ eval $proto_ recv-$type $from $src $group $iface $args } Node instproc rpf-nbr src { $self instvar ns_ id_ if [catch "$src id" srcID] { set srcID $src } $ns_ get-node-by-id [[$ns_ get-routelogic] lookup $id_ $srcID] } LanNode instproc rpf-nbr src { $self instvar ns_ id_ if [catch "$src id" srcID] { set srcID $src } $ns_ get-node-by-id [[$ns_ get-routelogic] lookup $id_ $srcID] } Node instproc getReps { src group } { $self instvar replicator_ set reps "" foreach key [array names replicator_ "$src:$group"] { lappend reps $replicator_($key) } return [lsort $reps] } Node instproc getReps-raw { src group } { $self array get replicator_ "$src:$group" } Node instproc clearReps { src group } { $self instvar multiclassifier_ foreach {key rep} [$self getReps-raw $src $group] { $rep reset delete $rep foreach {slot val} [$multiclassifier_ adjacents] { if { $val == $rep } { $multiclassifier_ clear $slot } } $self unset replicator_($key) } } Node instproc add-oif {head link} { $self instvar outLink_ set outLink_($head) $link } Node instproc add-iif {iflbl link} { # array mapping ifnum -> link $self set inLink_($iflbl) $link } Node instproc get-all-oifs {} { $self instvar outLink_ # return a sorted list of all "heads" return [lsort [array names outLink_]] } Node instproc get-all-iifs {} { $self instvar inLink_ # return a list of "labels" return [array names inLink_] } Node instproc iif2oif ifid { $self instvar ns_ set link [$self iif2link $ifid] # assuming that there have to be a reverse link # that is, all links are duplex. set outlink [$ns_ link $self [$link src]] return [$self link2oif $outlink] } Node instproc iif2link ifid { $self set inLink_($ifid) } Node instproc link2iif link { return [[$link set iif_] label] } Node instproc link2oif link { $link head } Node instproc oif2link oif { $oif set link_ } # Find out what interface packets sent from $node will arrive at # this node. $node need not be a neighbor. $node can be a node object # or node id. Node instproc from-node-iface { node } { $self instvar ns_ catch { set node [$ns_ get-node-by-id $node] } set rpfnbr [$self rpf-nbr $node] set rpflink [$ns_ link $rpfnbr $self] if { $rpflink != "" } { return [$rpflink if-label?] } return "?" ;#unknown iface } Vlink instproc if-label? {} { $self instvar iif_ $iif_ label }# # tcl/mcast/McastProto.tcl # # 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 # # Class McastProtocol McastProtocol instproc init {sim node} { $self next $self instvar ns_ node_ status_ type_ id_ set ns_ $sim set node_ $node set status_ "down" set type_ [$self info class] set id_ [$node id] $ns_ maybeEnableTraceAll $self $node_ } McastProtocol instproc getType {} { $self set type_ } McastProtocol instproc start {} { $self set status_ "up" } McastProtocol instproc stop {} { $self set status_"down" } McastProtocol instproc getStatus {} { $self set status_ } McastProtocol instproc upcall {code args} { # currently expects to handle cache-miss and wrong-iif eval $self handle-$code $args } McastProtocol instproc handle-wrong-iif { srcID group iface } { # return values: # 0 : do not call classify on this packet again # 1 : changed iif for the corresponding mfc-entry, classify again return 0 } McastProtocol instproc handle-cache-miss { srcID group iface } { # return values: # 0 : do not call classify on this packet again # 1 : changed iif for the corresponding mfc-entry, classify again return 0 } McastProtocol instproc annotate args { $self instvar dynT_ node_ ns_ set s "[$ns_ now] [$node_ id] $args" ;#nam wants uinique first arg??? if [info exists dynT_] { foreach tr $dynT_ { $tr annotate $s } } } McastProtocol instproc join-group arg { $self annotate $proc $arg } McastProtocol instproc leave-group arg { $self annotate $proc $arg } McastProtocol instproc trace { f src {op ""} } { $self instvar ns_ dynT_ if {$op == "nam" && [info exists dynT_] > 0} { foreach tr $dynT_ { $tr namattach $f } } else { lappend dynT_ [$ns_ create-trace Generic $f $src $src $op] } } # This method is called when a change in routing occurs. McastProtocol instproc notify { dummy } { $self instvar ns_ node_ PruneTimer_ #build list of current sources foreach r [$node_ getReps "*" "*"] { set src_id [$r set srcID_] set sources($src_id) 1 } set sourceIDs [array names sources] foreach src_id $sourceIDs { set src [$ns_ get-node-by-id $src_id] if {$src != $node_} { set upstream [$node_ rpf-nbr $src] if { $upstream != ""} { set inlink [$ns_ link $upstream $node_] set newiif [$node_ link2iif $inlink] set reps [$node_ getReps $src_id "*"] foreach r $reps { set oldiif [$node_ lookup-iface $src_id [$r set grp_]] if { $oldiif != $newiif } { $node_ change-iface $src_id [$r set grp_] $oldiif $newiif } } } } #next update outgoing interfaces set oiflist "" foreach nbr [$node_ neighbors] { set nbr_id [$nbr id] set nh [$nbr rpf-nbr $src] if { $nh != $node_ } { # are we ($node_) the next hop from ($nbr) to # the source ($src) continue } set oif [$node_ link2oif [$ns_ link $node_ $nbr]] # oif to such neighbor set oifs($oif) 1 } set oiflist [array names oifs] set reps [$node_ getReps $src_id "*"] foreach r $reps { set grp [$r set grp_] set oldoifs [$r dump-oifs] set newoifs $oiflist foreach old $oldoifs { if [catch "$node_ oif2link $old" ] { # this must be a local agent, not an oif continue } set idx [lsearch $newoifs $old] if { $idx < 0} { $r disable $old if [info exists PruneTimer_($src_id:$grp:$old)] { delete $PruneTimer_($src_id:$grp:$old) unset PruneTimer_($src_id:$grp:$old) } } else { set newoifs [lreplace $newoifs $idx $idx] } } foreach new $newoifs { foreach r $reps { $r insert $new } } } } } McastProtocol instproc dump-routes {chan {grp ""} {src ""}} { $self instvar ns_ node_ if { $grp == "" } { # dump all replicator entries array set reps [$node_ getReps-raw * *] } elseif { $src == "" } { # dump entries for group array set reps [$node_ getReps-raw * $grp] ;# actually, more than *,g } else { # dump entries for src, group. array set reps [$node_ getReps-raw $src $grp] } puts $chan [concat "Node:\t${node_}([$node_ id])\tat t =" \ [format "%4.2f" [$ns_ now]]] puts $chan "\trepTag\tActive\t\tsrc\tgroup\tiifNode\t\tdest_nodes" foreach ent [lsort [array names reps]] { set sg [split $ent ":"] if { [$reps($ent) is-active] } { set active Y } else { set active N } # translate each oif to a link and then the neighbor node set dest "" foreach oif [$reps($ent) dump-oifs] { if ![catch { set nbr [[$node_ oif2link $oif] dst] } ] { set nbrid [$nbr id] if [$nbr is-lan?] { set nbrid ${nbrid}(L) } lappend dest $nbrid } } set s [lindex $sg 0] set g [lindex $sg 1] set iif [$node_ lookup-iface $s $g] set iif_node_id $iif catch { # catch: iif can be negative for senders set iif_node [[$node_ iif2link $iif] src] if [$iif_node is-lan?] { set iif_node_id [$iif_node id](L) } else { set iif_node_id [$iif_node id] } } puts $chan [format "\t%5s\t %s\t\t%d\t0x%x\t%s\t\t%s" \ $reps($ent) $active $s $g $iif_node_id $dest] } } ################################################### Class mrtObject #XXX well-known groups (WKG) with local multicast/broadcast mrtObject set mask-wkgroups 0xfff0 mrtObject set wkgroups(Allocd) [mrtObject set mask-wkgroups] mrtObject proc registerWellKnownGroups name { set newGroup [mrtObject set wkgroups(Allocd)] mrtObject set wkgroups(Allocd) [expr $newGroup + 1] mrtObject set wkgroups($name) $newGroup } mrtObject proc getWellKnownGroup name { assert "\"$name\" != \"Allocd\"" mrtObject set wkgroups($name) } mrtObject registerWellKnownGroups ALL_ROUTERS mrtObject registerWellKnownGroups ALL_PIM_ROUTERS mrtObject proc expandaddr {} { # extend the space to 32 bits mrtObject set mask-wkgroups 0x7fffffff foreach {name group} [mrtObject array get wkgroups] { mrtObject set wkgroups($name) [expr $group | 0x7fffffff] } } mrtObject instproc init { node } { $self next $self set node_ $node } mrtObject instproc addproto { proto { iiflist "" } } { $self instvar node_ protocols_ # if iiflist is empty, protocol runs on all iifs if { $iiflist == "" } { set iiflist [$node_ get-all-iifs] lappend iiflist -1 ;#for local packets } foreach iif $iiflist { set protocols_($iif) $proto } } mrtObject instproc getType { protocolType } { $self instvar protocols_ foreach iif [array names protocols_] { if { [$protocols_($iif) getType] == $protocolType } { return $protocols_($iif) } } return "" } mrtObject instproc all-mprotos {op args} { $self instvar protocols_ foreach iif [array names protocols_] { set p $protocols_($iif) if ![info exists protos($p)] { set protos($p) 1 eval $p $op $args } } } mrtObject instproc start {} { $self all-mprotos start } mrtObject instproc stop {} { $self all-mprotos stop } mrtObject instproc notify dummy { $self all-mprotos notify $dummy } mrtObject instproc dump-routes args { $self all-mprotos dump-routes $args } # similar to membership indication by igmp.. mrtObject instproc join-group { grp src } { eval $self all-mprotos join-group $grp $src } mrtObject instproc leave-group { grp src } { eval $self all-mprotos leave-group $grp $src } mrtObject instproc upcall { code source group iface } { # check if the group is local multicast to well-known group set wkgroup [expr [$class set mask-wkgroups]] if { [expr ( $group & $wkgroup ) == $wkgroup] } { $self instvar node_ $node_ add-mfc $source $group -1 {} return 1 } else { $self instvar protocols_ $protocols_($iface) upcall $code $source $group $iface } } mrtObject instproc drop { replicator src dst {iface -1} } { $self instvar protocols_ $protocols_($iface) drop $replicator $src $dst $iface } # # tcl/mcast/DM.tcl # # 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/Modified by Polly Huang (USC/ISI), http://www-scf.usc.edu/~bhuang # Class DM -superclass McastProtocol DM set PruneTimeout 0.5 DM set CacheMissMode pimdm ;#or dvmrp (lowercase) DM instproc init { sim node } { $self instvar mctrl_ set mctrl_ [new Agent/Mcast/Control $self] $node attach $mctrl_ Timer/Iface/Prune set timeout [[$self info class] set PruneTimeout] $self next $sim $node } DM instproc join-group { group } { $self instvar node_ $self next $group set listOfReps [$node_ getReps * $group] foreach r $listOfReps { if ![$r is-active] { $self send-ctrl "graft" [$r set srcID_] $group set nbr [$node_ rpf-nbr [$r set srcID_]] set nbrs($nbr) 1 } } foreach nbr [array names nbrs] { if [$nbr is-lan?] { # for each LAN we maintain an array of counters # of how many local receivers we have on the lan # for a given group $nbr instvar receivers_ if [info exists receivers_($group)] { incr receivers_($group) } else { set receivers_($group) 1 } } } } DM instproc leave-group { group } { $self next $group $self instvar node_ # lan: decrement counters set listOfReps [$node_ getReps * $group] foreach r $listOfReps { set nbr [$node_ rpf-nbr [$r set srcID_]] set nbrs($nbr) 1 } foreach nbr [array names nbrs] { if [$nbr is-lan?] { $nbr instvar receivers_ if { [info exists receivers_($group)] && \ $receivers_($group) > 0 } { incr receivers_($group) -1 } } } } DM instproc handle-wrong-iif { srcID group iface } { $self instvar node_ ns_ set inlink [$node_ iif2link $iface] set from [$inlink src] $self send-ctrl "prune" $srcID $group [$from id] return 0 ;# don't call this method two times } DM instproc handle-cache-miss { srcID group iface } { DM instvar CacheMissMode $self handle-cache-miss-$CacheMissMode $srcID $group $iface return 1 ;#call again } DM instproc handle-cache-miss-pimdm { srcID group iface } { $self instvar node_ ns_ if { $iface >= 0 } { set rpf_nbr [$node_ rpf-nbr $srcID] set inlink [$node_ iif2link $iface] set rpflink [$ns_ link $rpf_nbr $node_] if { $inlink != $rpflink } { set from [$inlink src] $self send-ctrl "prune" $srcID $group [$from id] return 0; #drop this packet } set rpfoif [$node_ iif2oif $iface] } else { set rpfoif "" } set alloifs [$node_ get-all-oifs] set oiflist "" foreach oif $alloifs { if {$oif == $rpfoif} { continue ;#exclude incoming iface } set dst [[$node_ oif2link $oif] dst] if { [$dst is-lan?] && [$dst rpf-nbr $srcID] != $node_ } { # exclude also lan oifs for which we are not forwarders # this constitutes a form of "centralized" assert mechanism continue } lappend oiflist $oif } #set idx [lsearch $oiflist $rpfoif] #set oiflist [lreplace $oiflist $idx $idx] $node_ add-mfc $srcID $group $iface $oiflist } DM instproc handle-cache-miss-dvmrp { srcID group iface } { $self instvar node_ ns_ set oiflist "" foreach nbr [$node_ neighbors] { # peek into other nodes' routing tables to simulate # child-parent relationship maintained by dvmrp set rpfnbr [$nbr rpf-nbr $srcID] if { $rpfnbr == $node_ } { set link [$ns_ link $node_ $nbr] lappend oiflist [$node_ link2oif $link] } } $node_ add-mfc $srcID $group $iface $oiflist } DM instproc drop { replicator src dst iface} { $self instvar node_ ns_ if { $iface < 0 } { # optimization for sender: if no listeners, set the ignore bit, # so this function isn't called for every packet. $replicator set ignore_ 1 } else { set from [[$node_ iif2link $iface] src] if [$from is-lan?] { $self send-ctrl "prune" $src $dst } else { $self send-ctrl "prune" $src $dst [$from id] } } } DM instproc recv-prune { from src group iface} { $self instvar node_ PruneTimer_ ns_ set r [$node_ getReps $src $group] if { $r == "" } { return 0 } set id [$node_ id] set tmpoif [$node_ iif2oif $iface] if { [$r is-active-target $tmpoif] } { $r disable $tmpoif if ![$r is-active] { if { $src != $id } { # propagate prune only if the disabled oif # was the last one $self send-ctrl prune $src $group } } } if ![info exists PruneTimer_($src:$group:$tmpoif)] { set PruneTimer_($src:$group:$tmpoif) \ [new Timer/Iface/Prune $self $src $group $tmpoif $ns_] } $PruneTimer_($src:$group:$tmpoif) schedule } DM instproc recv-graft { from src group iface} { $self instvar node_ PruneTimer_ ns_ set id [$node_ id] set r [$node_ getReps $src $group] if { $r == "" } { if { $id == $src } { set iif "?" } else { set rpfnbr [$node_ rpf_nbr $src] set rpflnk [$ns_ link $src $id] set iif [$node_ link2iif $rpflnk] } $node_ add-mfc $src $group $iif "" set r [$node_ getReps $src $group] } if { ![$r is-active] && $src != $id } { # propagate the graft $self send-ctrl graft $src $group } set tmpoif [$node_ iif2oif $iface] $r enable $tmpoif if [info exists PruneTimer_($src:$group:$tmpoif)] { delete $PruneTimer_($src:$group:$tmpoif) unset PruneTimer_($src:$group:$tmpoif) } } # send a graft/prune for src/group up to the source or towards $to DM instproc send-ctrl { which src group { to "" } } { $self instvar mctrl_ ns_ node_ if { $to != "" } { set n [$ns_ get-node-by-id $to] # we don't want to send anything to a lanNode if [$n is-lan?] return set toid $to } else { set toid $src } set nbr [$node_ rpf-nbr $toid] if [$nbr is-lan?] { # we're requested to send via a lan: $nbr $nbr instvar receivers_ # send a graft/prune only if there're no other receivers on the lan if { [info exists receivers_($group)] && \ $receivers_($group) > 0 } return # need to send: find the next hope node set nbr [$nbr rpf-nbr $toid] } $ns_ simplex-connect $mctrl_ \ [[[$nbr getArbiter] getType [$self info class]] set mctrl_] if { $which == "prune" } { $mctrl_ set class_ 30 } else { $mctrl_ set class_ 31 } $mctrl_ send $which [$node_ id] $src $group } DM instproc timeoutPrune { oif src grp } { $self instvar node_ PruneTimer_ ns_ set r [$node_ getReps $src $grp] $r insert $oif if [info exists PruneTimer_($src:$grp:$oif)] { delete $PruneTimer_($src:$grp:$oif) unset PruneTimer_($src:$grp:$oif) } return } Class Timer/Iface/Prune -superclass Timer/Iface Timer/Iface/Prune set timeout 0.5 Timer/Iface/Prune instproc timeout {} { $self instvar proto_ src_ grp_ oif_ $proto_ timeoutPrune $oif_ $src_ $grp_ } # # tcl/ctr-mcast/CtrMcast.tcl # # 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 # # ########## CtrMcast Class: Individual Node join-group, leave-group, etc ##### Class CtrMcast -superclass McastProtocol CtrMcast instproc init { sim node } { $self next $sim $node $self instvar ns_ node_ $self instvar agent_ defaultTree_ decapagent_ $self instvar c_rp_ c_bsr_ priority_ set agent_ [$ns_ set MrtHandle_] set defaultTree_ "RPT" set decapagent_ [new Agent/Decapsulator] $ns_ attach-agent $node_ $decapagent_ ### config PIM nodes set c_rp_ 1 set c_bsr_ 1 set priority_ 0 } CtrMcast instproc join-group { group } { $self next $group $self instvar node_ ns_ agent_ $self instvar defaultTree_ if { [$agent_ treetype? $group] == "" } { $agent_ treetype $group $defaultTree_ $agent_ add-new-group $group } $agent_ add-new-member $group $node_ foreach src [$agent_ sources? $group] { $agent_ compute-branch $src $group $node_ } } CtrMcast instproc leave-group { group } { $self next $group $self instvar node_ ns_ agent_ defaultTree_ $agent_ remove-member $group $node_ foreach src [$agent_ sources? $group] { $agent_ prune-branch $src $group $node_ } } CtrMcast instproc handle-cache-miss { srcID group iface } { $self instvar ns_ agent_ node_ $self instvar defaultTree_ if { [$agent_ treetype? $group] == "" } { $agent_ treetype $group $defaultTree_ #$agent_ add-new-member $group $node_ } if { [$node_ id] == $srcID } { set RP [$self get_rp $group] if {[$agent_ treetype? $group] == "RPT" && $srcID != [$RP id]} { set encapagent [new Agent/Encapsulator] $ns_ attach-agent $node_ $encapagent set ctrmcast [[$RP getArbiter] getType "CtrMcast"] $ns_ connect $encapagent [$ctrmcast set decapagent_] ### create (S,G,iif=-1) entry $node_ add-mfc-reg $srcID $group -1 $encapagent } if [$agent_ new-source? $group $node_] { $agent_ compute-tree $node_ $group } } elseif [SessionSim set MixMode_] { set srcnode [$ns_ get-node-by-id $srcID] if [$agent_ new-source? $group $srcnode] { $agent_ compute-tree $srcnode $group } } return 1 ;#call again } CtrMcast instproc drop { replicator src group iface } { #packets got dropped only due to null oiflist } CtrMcast instproc handle-wrong-iif { srcID group iface } { warn "$self: $proc for <S: $srcID, G: $group, if: $iface>?" return 0 ;#call once } CtrMcast instproc notify { dummy } { } ##### Two functions to help get RP for a group ##### ##### get_rp {group} ##### ##### hash {rp group} ##### CtrMcast instproc get_rp group { $self instvar rpset_ agent_ if ![info exists rpset_] { [$agent_ set ctrrpcomp] compute-rpset assert [info exists rpset_] } set returnrp -1 set hashval -1 foreach rp $rpset_ { if {[$self hash $rp $group] > $hashval} { set hashval [$self hash $rp $group] set returnrp $rp } } set returnrp ;# return } CtrMcast instproc hash {rp group} { $rp id } CtrMcast instproc set-rpset args { eval $self set rpset_ "$args" } CtrMcast instproc get_bsr {} { warn "$self: CtrMcast doesn't require a BSR" } CtrMcast instproc set_c_bsr { prior } { $self instvar c_bsr_ priority_ set c_bsr_ 1 set priority_ $prior } CtrMcast instproc set_c_rp {} { $self instvar c_rp_ set c_rp_ 1 } CtrMcast instproc unset_c_rp {} { $self instvar c_rp_ set c_rp_ 0 } # #################### MultiNode: add-mfc-reg ################ Node instproc add-mfc-reg { src group iif oiflist } { $self instvar multiclassifier_ Regreplicator_ #XXX node addr is in upper 24 bits if [info exists Regreplicator_($group)] { foreach oif $oiflist { $Regreplicator_($group) insert $oif } return 1 } set r [new Classifier/Replicator/Demuxer] $r set node_ $self $r set srcID_ $src set Regreplicator_($group) $r foreach oif $oiflist { $r insert $oif } # Install the replicator. We do this only once and leave # it forever. Since prunes are data driven, we want to # leave the replicator in place even when it's empty since # the replicator::drop callback triggers the prune. # $multiclassifier_ add-rep $r $src $group $iif } Node instproc getRegreplicator group { $self instvar Regreplicator_ if [info exists Regreplicator_($group)] { return $Regreplicator_($group) } else { return -1 } } # # tcl/ctr-mcast/CtrMcastComp.tcl # # 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 # # ########## CtrMcastComp Class ################################## ########## init {} ########## compute-tree {src group Ttype} ########## compute-branch {src group member Ttype} Class CtrMcastComp CtrMcastComp instproc init sim { $self instvar ns_ set ns_ $sim $self init-groups $ns_ maybeEnableTraceAll $self {} } CtrMcastComp instproc id {} { return 0 } CtrMcastComp instproc trace { f nop {op ""} } { $self instvar ns_ dynT_ if {$op == "nam" && [info exists dynT_]} { foreach tr $dynT_ { $tr namattach $f } } else { lappend dynT_ [$ns_ create-trace Generic $f $self $self $op] } } ##### Main computation functions ##### CtrMcastComp instproc reset-mroutes {} { $self instvar ns_ foreach node [$ns_ all-nodes-list] { foreach group [$self groups?] { set class_info [$node info class] if {$class_info != "LanNode"} { $node clearReps * $group } # *,G match catches all sources. # foreach src [$self sources? $group] { # $node clearReps [$src id] $group # } } } } CtrMcastComp instproc compute-mroutes {} { $self reset-mroutes foreach group [$self groups?] { foreach src [$self sources? $group] { $self compute-tree $src $group } } } CtrMcastComp instproc compute-tree { src group } { foreach mem [$self members? $group] { $self compute-branch $src $group $mem } } CtrMcastComp instproc compute-branch { src group nodeh } { $self instvar ns_ ### set (S,G) join target set tt [$self treetype? $group] if { $tt == "SPT" } { set target $src } elseif { $tt == "RPT" } { set target [$self get_rp $nodeh $group] } for { set downstreamtmp "" set tmp $nodeh } { $downstreamtmp != $target } { set downstreamtmp $tmp set tmp [$tmp rpf-nbr $target] } { if {[SessionSim set MixMode_] && $downstreamtmp != "" && ![$ns_ detailed-link? [$tmp id] [$downstreamtmp id]]} { # puts "joining into session area" break } ### set iif : RPF link interface label if {$tmp == $target} { # at src or RP set iif -1 } else { set rpfl [$ns_ link [$tmp rpf-nbr $target] $tmp] if {[SessionSim set MixMode_] && $rpfl == ""} { # in mix mode: default -1 unless find a # detailed link on the rpf path set iif -1 set ttmp $tmp while {$ttmp != $target} { set rpfl [$ns_ link [$ttmp rpf-nbr $target] $ttmp] if {$rpfl != ""} { set iif [$rpfl if-label?] break } set ttmp [$ttmp rpf-nbr $target] } } else { # in regular detailed mode set iif [$rpfl if-label?] } } ### set oif : RPF link set oiflist "" if { $downstreamtmp != "" } { set rpfnbr [$downstreamtmp rpf-nbr $target] if { $rpfnbr == $tmp } { set rpflink [$ns_ link $tmp $downstreamtmp] if {$rpflink != ""} { set oiflist [$tmp link2oif $rpflink] } } } if { [set r [$tmp getReps [$src id] $group]] != "" } { if [$r is-active] { # puts "reach merging point, $group [$src id] [$target id] [$tmp id] [$nodeh id], iif $iif, oif $oiflist" if { $oiflist != "" } { $r insert [lindex $oiflist 0] } break } else { # puts "hasn't reached merging point, $group [$src id] [$target id] [$tmp id] [$nodeh id], iif $iif, oif $oiflist" # so continue to insert the oif if { $oiflist != "" } { $r insert [lindex $oiflist 0] } } } else { # hasn't reached merging point, # puts "so keep creating (S,G) like a graft, $group [$src id] [$target id] [$tmp id] [$nodeh id], iif $iif, oif $oiflist" $tmp add-mfc [$src id] $group $iif $oiflist } } } CtrMcastComp instproc prune-branch { src group nodeh } { $self instvar ns_ ### set (S,G) prune target set tt [$self treetype? $group] if { $tt == "SPT" } { set target $src } elseif { $tt == "RPT" } { set target [$self get_rp $nodeh $group] } for { set downstreamtmp "" set tmp $nodeh } { $downstreamtmp != $target } { set downstreamtmp $tmp set tmp [$tmp rpf-nbr $target] } { set iif -1 set oif "" if { $downstreamtmp != "" } { set rpfnbr [$downstreamtmp rpf-nbr $target] if { $rpfnbr == $tmp } { set oif [$tmp link2oif [$ns_ link $tmp $downstreamtmp]] } } if { [set r [$tmp getReps [$src id] $group]] != "" } { if { $oif != "" } { $r disable $oif } if [$r is-active] { break } } else { break } } } # notify(): adapt to rtglib dynamics CtrMcastComp instproc notify {} { $self instvar ctrrpcomp ### need to add a delay before recomputation $ctrrpcomp compute-rpset $self compute-mroutes } # # utility functions to track Source, Group, and Member State # CtrMcastComp instproc init-groups {} { $self set Glist_ "" } CtrMcastComp instproc add-new-group group { $self instvar Glist_ set group [expr $group] if ![info exist Glist_] { set Glist_ "" } if {[lsearch $Glist_ $group] < 0} { lappend Glist_ $group } } CtrMcastComp instproc add-new-member {group node} { $self instvar Mlist_ set group [expr $group] $self add-new-group $group if ![info exist Mlist_($group)] { set Mlist_($group) "" } if {[lsearch $Mlist_($group) $node] < 0} { lappend Mlist_($group) $node } } CtrMcastComp instproc new-source? {group node} { $self instvar Slist_ set group [expr $group] $self add-new-group $group if ![info exist Slist_($group)] { set Slist_($group) "" } if {[lsearch $Slist_($group) $node] < 0} { lappend Slist_($group) $node return 1 } else { return 0 } } CtrMcastComp instproc groups? {} { $self set Glist_ } CtrMcastComp instproc members? group { $self instvar Mlist_ set group [expr $group] if ![info exists Mlist_($group)] { set Mlist_($group) "" } set Mlist_($group) } CtrMcastComp instproc sources? group { $self instvar Slist_ set group [expr $group] if ![info exists Slist_($group)] { set Slist_($group) "" } set Slist_($group) } CtrMcastComp instproc remove-member {group node} { $self instvar Mlist_ Glist_ set group [expr $group] set k [lsearch $Mlist_($group) $node] if {$k < 0} { puts "warning: removing non-member" } else { set Mlist_($group) [lreplace $Mlist_($group) $k $k] } if { $Mlist_($group) == "" } { set k [lsearch $Glist_ $group] if {$k < 0} { puts "warning: removing non-existing group" } else { set Glist_ [lreplace $Glist_ $k $k] } } } CtrMcastComp instproc treetype? group { $self instvar treetype_ set group [expr $group] if [info exists treetype_($group)] { return $treetype_($group) } else { return "" } } CtrMcastComp instproc treetype {group tree} { $self set treetype_([expr $group]) $tree } CtrMcastComp instproc switch-treetype group { $self instvar treetype_ dynT_ set group [expr $group] if [info exists dynT_] { foreach tr $dynT_ { $tr annotate "$group switch tree type" } } set treetype_($group) "SPT" $self add-new-group $group $self compute-mroutes } CtrMcastComp instproc set_c_rp args { $self instvar ns_ foreach n [$ns_ all-nodes-list] { set arbiter [$n getArbiter] #NS creates a virtual node per LAN. This node does #not have any the multicast features set. if {$arbiter != ""} { set ctrmcast [$arbiter getType "CtrMcast"] $ctrmcast instvar c_rp_ $ctrmcast unset_c_rp } } foreach node $args { set arbiter [$node getArbiter] set ctrmcast [$arbiter getType "CtrMcast"] $ctrmcast set_c_rp } } CtrMcastComp instproc set_c_bsr args { foreach node $args { set tmp [split $node :] set node [lindex $tmp 0] set prior [lindex $tmp 1] set arbiter [$node getArbiter] set ctrmcast [$arbiter getType "CtrMcast"] $ctrmcast set_c_bsr $prior } } CtrMcastComp instproc get_rp { node group } { set ctrmcast [[$node getArbiter] getType "CtrMcast"] $ctrmcast get_rp $group } CtrMcastComp instproc get_bsr { node } { set arbiter [$node getArbiter] set ctrmcast [$arbiter getType "CtrMcast"] $ctrmcast get_bsr } # # tcl/ctr-mcast/CtrRPComp.tcl # # 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 # Class CtrRPComp CtrRPComp instproc init sim { $self set ns_ $sim $self next } CtrRPComp instproc compute-rpset {} { $self instvar ns_ ### initialization foreach node [$ns_ all-nodes-list] { set connected($node) 0 } set urtl [$ns_ get-routelogic] ### connected region algorithm foreach node [$ns_ all-nodes-list] { foreach {vertix lvertix} [array get ldomain] { if {[$urtl lookup [$node id] [$vertix id]] >= 0} { lappend ldomain($vertix) $node set connected($node) 1 break } } if {!$connected($node)} { set ldomain($node) $node set connected($node) 1 } } ### for each region, set rpset foreach {vnode lvertix} [array get ldomain] { set hasbsr 0 set rpset "" ### find rpset for the region foreach vertix $lvertix { set class_info [$vertix info class] if {$class_info != "LanNode"} { set ctrdm [[$vertix getArbiter] getType "CtrMcast"] if [$ctrdm set c_bsr_] {set hasbsr 1} if [$ctrdm set c_rp_] { lappend rpset $vertix } } } foreach vertix $lvertix { set class_info [$vertix info class] if {$class_info != "LanNode"} { set ctrdm [[$vertix getArbiter] getType "CtrMcast"] if $hasbsr { $ctrdm set-rpset $rpset } else { $ctrdm set-rpset "" puts "no c_bsr" } } } } } # # tcl/mcast/BST.tcl # # # 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. # # # # Written by Yuri Pryadkin # Modified by Nader Saelhi # ############### # Implementation of a simple shared bi-directional tree protocol. No # timers. Nodes send grafts/prunes toward the RP to join/leave the # group. The user needs to set two protocol variables: # # "BST set RP_($group) $node" - indicates that $node # acts as an RP for the $group Class BST -superclass McastProtocol BST instproc init { sim node } { $self instvar mctrl_ oiflist_ BST instvar RP_ set mctrl_ [new Agent/Mcast/Control $self] $node attach $mctrl_ $self next $sim $node } BST instproc start {} { $self instvar node_ oiflist_ BST instvar RP_ # need to do it in start when unicast routing is computed foreach grpx [array names RP_] { set grp [expr $grpx] # $self dbg "BST: grp $grp, node [$node_ id]" if { [string compare $grp $grpx] } { set RP_($grp) $RP_(grpx) unset RP_($grpx) } set rpfiif [$node_ from-node-iface $RP_($grp)] # $self dbg "BST: rpfiif $rpfiif" if { $rpfiif != "?" } { set rpfoif [$node_ iif2oif $rpfiif] } else { set rpfoif "" } # initialize with the value of rpfoif set oiflist_($grp) $rpfoif set neighbors [$node_ set neighbor_] if [info exists neighbors] { for {set i 0} {$i < [llength $neighbors]} {incr i} { set neighbor [lindex $neighbors $i] set class_info [$neighbor info class] if {$class_info == "LanNode"} { # This node is on a LAN, we # must designate a router for # the mcast group $neighbor designate-ump-router $grp \ $RP_($grp) } } } } } BST instproc join-group { group {src "x"} } { $self instvar node_ ns_ oiflist_ BST instvar RP_ set nbr [$node_ rpf-nbr $RP_($group)] set nbrs($nbr) 1 $node_ add-mark m1 blue "[$node_ get-shape]" foreach nbr [array names nbrs] { if [$nbr is-lan?] { $nbr instvar receivers_ if [info exists receivers_($group)] { incr receivers_($group) } else { $self send-ctrl "graft" $RP_($group) $group set receivers_($group) 1 } } $self next $group ; #annotate } if { ![$node_ check-local $group] || [$node_ getReps "x" \ $group] == ""} { # $self dbg "Sending join-group" $self send-ctrl "graft" $RP_($group) $group } } BST instproc leave-group { group {src "x"} } { BST instvar RP_ $self next $group ;#annotate $self instvar node_ oiflist_ set nbr [$node_ rpf-nbr $RP_($group)] if [$nbr is-lan?] { $nbr instvar receivers_ if [info exists receivers_($group)] { if {$receivers_($group) > 0} { incr receivers_($group) -1 if {$receivers_($group) == 0} { $node_ delete-mark m1 $self send-ctrl "prune" $RP_($group) $group } } } else { # Nobody has joined yet return } } else { set rpfiif [$node_ from-node-iface $RP_($group)] if { $rpfiif != "?" } { set rpfoif [$node_ iif2oif $rpfiif] } else { set rpfoif "" } if { $oiflist_($group) == \ $rpfoif && ![$node_ check-local $group] } { # propagate $self send-ctrl "prune" $RP_($group) $group $node_ delete-mark m1 } } } # handle-wrong-iif # This function does nothing BST instproc handle-wrong-iif { srcID group iface } { $self instvar node_ oiflist_ BST instvar RP_ # $self dbg "BST: wrong iif $iface, src $srcID, grp $group" # $self dbg "\t oiflist: $oiflist_($group)" set rep [$node_ getReps "x" $group] $node_ add-mfc "x" $group $iface $oiflist_($group) set iif [$node_ lookup-iface "x" $group] if { $iface >= 0 } { set oif [$node_ iif2oif $iface] set rpfiif [$node_ from-node-iface $RP_($group)] if { $iface == $rpfiif } { # forward direction: disable oif to RP $rep disable [$node_ iif2oif $rpfiif] } else { # reverse direction: disable where it came from $rep disable $oif if { $node_ != $RP_($group) } { $rep insert [$node_ iif2oif $rpfiif] } } } $node_ change-iface "x" $group $iif $iface return 1 ;#classify packet again } # handle-cache-miss # Creates a (*, G) entry for a group. BST instproc handle-cache-miss { srcID group iface } { $self instvar node_ ns_ oiflist_ BST instvar RP_ if { [$node_ getReps "x" $group] != "" } { debug 1 } # $self dbg \ # "handle-cache-miss, src: $srcID, group: $group, iface: $iface" # $self dbg \ # "********* miss: adding <x, $group, $iface, $oiflist_($group)>" # Check if the node is on a LAN. If so we should NOT resume # if: # 1) The node is not the next upstream router, and # 2) The incoming interface is to a LanNode, and # 3) The node is not a source. if {$iface != -1} { # if the node is not a source (3) set neighbors [$node_ set neighbor_] if [info exists neighbors] { for {set i 0} {$i < [llength $neighbors]} {incr i} { set neighbor [lindex $neighbors $i] set nbr [$node_ rpf-nbr $RP_($group)] if {[$neighbor is-lan?] && \ [$nbr info class] != "LanNode"} { # The node is directly # connected to RP --or at # least not via LanNode $neighbor instvar up_ set up [$neighbor set up_($group)] if {$node_ != $up} { # If not the upstream # router (1) if [$self link2lan? $neighbor \ $iface] { # The interface is to # the LAN return 0 } } } } } } $node_ add-mfc "x" $group $iface $oiflist_($group) if { $iface > 0 } { #disable reverse iface set rep [$node_ getReps "x" $group] $rep disable [$node_ iif2oif $iface] } return 1 ;# classify the packet again. } BST instproc drop { replicator src dst iface} { $self instvar node_ ns_ BST instvar RP_ # No downstream listeners? Just drop the packet. No purning is # necessary # $self dbg "drops src: $src, dst: $dst, replicator: [$replicator set srcID_]" if {$iface >= 0} { # so, this packet came from outside of the node. # Since PIM works based on explicit join mechanism, # the only action is to drop unwanted packets. # $self dbg "drops the unwanted packet" } } BST instproc recv-prune { from src group iface} { $self instvar node_ ns_ oiflist_ BST instvar RP_ # $self dbg "received a prune from: $from, src: $src, grp: $group, if: $iface" set rep [$node_ getReps "x" $group] if {$rep != ""} { set oif [$node_ iif2oif $iface] set idx [lsearch $oiflist_($group) $oif] if { $idx >= 0 } { set oiflist_($group) [lreplace $oiflist_($group) $idx $idx] $rep disable $oif set rpfiif [$node_ from-node-iface $RP_($group)] if { $rpfiif != "?" } { set rpfoif [$node_ iif2oif $rpfiif] } else { set rpfoif "" } if { $oiflist_($group) == $rpfoif && ![$node_ check-local $group] } { # propagate $node_ delete-mark m2 $self send-ctrl "prune" $RP_($group) $group } } } } BST instproc recv-graft { from to group iface } { $self instvar node_ ns_ oiflist_ BST instvar RP_ # $self dbg "received a graft from: $from, to: $to, if: $iface" set oif [$node_ iif2oif $iface] set rpfiif [$node_ from-node-iface $RP_($group)] if { $rpfiif != "?" } { set rpfoif [$node_ iif2oif $rpfiif] } else { set rpfoif "" } if { $oiflist_($group) == $rpfoif && ![$node_ check-local $group] } { # propagate $node_ add-mark m2 red circle $self send-ctrl "graft" $RP_($group) $group } if { [lsearch $oiflist_($group) $oif] < 0 } { lappend oiflist_($group) $oif if { [$node_ lookup-iface "x" $group] != $iface } { set rep [$node_ getReps "x" $group] if { $rep != "" } { $rep insert $oif } } } } # # send a graft/prune for src/group up the RPF tree towards dst # BST instproc send-ctrl { which dst group } { $self instvar mctrl_ ns_ node_ if {$node_ != $dst} { set nbr [$node_ rpf-nbr $dst] if [$nbr is-lan?] { # we're requested to send via a lan $nbr instvar receivers_ # send graft/prune only if there's no other receiver. if { [info exists receivers_($group)] && \ $receivers_($group) > 0 } return set nbr [$nbr rpf-nbr $dst] # $self dbg "BST::send-ctrl The first hop router to LAN is node [$nbr id]" } # $self dbg "BST::send-ctrl $ns_ simplex-connect \[\[\[$nbr getArbiter\] getType \[$self info class\]\] set mctrl_\]" $ns_ simplex-connect $mctrl_ \ [[[$nbr getArbiter] getType [$self info class]] set mctrl_] if { $which == "prune" } { $mctrl_ set fid_ 2 } else { $mctrl_ set fid_ 3 } # $self dbg "BST::send-ctrl $mctrl_ ([$mctrl_ info class]) send $which [$node_ id] $dst $group" $mctrl_ send $which [$node_ id] $dst $group } } ################ Helpers BST instproc dbg arg { $self instvar ns_ node_ puts [format "At %.4f : node [$node_ id] $arg" [$ns_ now]] } # designate-ump-router # Designates a router on a LAN in order to avoid transmission of # duplicate and redundant messages. The node with the highest ID is # chosen as the designated router. LanNode instproc designate-ump-router {group dst} { $self instvar nodelist_ $self instvar up_ set nbr [$self rpf-nbr $dst] set up_($group) $nbr return } # Returns the next hop router to a node BST instproc next-hop-router {node group} { BST instvar RP_ set nbr [$node rpf-nbr $RP_($group)] if [$nbr is-lan?] { set nbr [$nbr rpf-nbr $RP_($group)] } return $nbr } # Checks if an mcast group is BST BST instproc is-group-bidir? {group} { BST instvar RP_ foreach grp [array names RP_] { if {$grp == $group} { return 1 } } return 0 } # Finds out which Connector or Vlink corresponds to (link) BST instproc match-oif {group link} { $self instvar oiflist_ set oiflist $oiflist_($group) if {$oiflist != ""} { foreach oif $oiflist { set oiflink [$oif set link_] if {$oiflink == $link} { return $oiflink } } } return } # Finds the outgoing link to the next node (dst) BST instproc find-oif {dst group} { $self instvar node_ ns_ if {$node_ != $dst} { set ns [$self set ns_] $ns instvar link_ set link [$ns set link_([$node_ id]:[$dst id])] # Now find which Connector or VLink corresponds to this link return [$self match-oif $group $link] } else { return "" } } # Finds if the interface (iface) is toward the LAN represented by (neighbor) # or not. BST instproc link2lan? {neighbor iface} { $self instvar node_ ns_ set link1 [[[$node_ iif2oif $iface] set link_] set iif_] set link2 [[$ns_ link $node_ $neighbor] set iif_] if {$link1 == $link2} { return 1 } else { return 0 } } #################### Class Classifier/Multicast/Replicator/BST -superclass Classifier/Multicast/BST # # This method called when a new multicast group/source pair # is seen by the underlying classifier/mcast object. # We install a hash for the pair mapping it to a slot # number in the classifier table and point the slot # at a replicator object that sends each packet along # the RPF tree. # Classifier/Multicast/BST instproc new-group { src group iface code} { $self instvar node_ $node_ new-group $src $group $iface $code } Classifier/Multicast/BST instproc no-slot slot { # NOTHING } Classifier/Multicast/Replicator/BST instproc init args { $self next $self instvar nrep_ set nrep_ 0 } Classifier/Multicast/Replicator/BST instproc add-rep { rep src group iif } { $self instvar nrep_ $self set-hash $src $group $nrep_ $iif $self install $nrep_ $rep incr nrep_ } #################### # match-BST-iif # Performs the RPF Classifier/Multicast/Replicator/BST instproc match-BST-iif {iface group} { $self instvar node_ list retval_ set agents [$node_ set agents_] for {set i 0} {$i < [llength $agents]} {incr i} { # Check if you can find a BST agent set agent [lindex $agents $i] $agent instvar proto_ if [info exists proto_] { set protocol [$agent set proto_] if {[$protocol info class] == "BST"} { # See if the group is a BST group BST instvar RP_ $protocol instvar oiflist_ set bidir [$protocol is-group-bidir? $group] if {$bidir == 1} { if {$node_ == $RP_($group)} { return 1 } # Find the incoming interface # from RP. set iif [$node_ from-node-iface \ $RP_($group)] if {$iif == $iface} { return 1 } else { return 0 } } } } } return -1 } # Classifier/Multicast/Replicator upstream-link # Finds the link from the next hop upstream router to the current # node. This function is called from C++ to provide the classifier # with appropriate information to set the UMP option. Classifier/Multicast/Replicator/BST instproc upstream-link {group} { $self instvar node_ list retval_ set agents [$node_ set agents_] for {set i 0} {$i < [llength $agents]} {incr i} { # Check if you can find a BST agent set agent [lindex $agents $i] $agent instvar proto_ if [info exists proto_] { set protocol [$agent set proto_] if {[$protocol info class] == "BST"} { # See if the group is a BST group BST instvar RP_ $protocol instvar oiflist_ set bidir [$protocol is-group-bidir? $group] if {$bidir == 1} { # Get the next node, REAL or VIRTUAL! set nbr [$node_ rpf-nbr $RP_($group)] # Find the outgoing link to the next # node set oif [$protocol find-oif $nbr \ $group] if {$oif == ""} { set oif "self" } lappend retval_ $oif # Now attach the next node's ID # If the neighbor is a virtual # name, we need to find the # designated router on the # LAN. Currently, the # designated router is the one # which is the closest to the RP. if [$nbr is-lan?] { set nbr [$nbr rpf-nbr $RP_($group)] } lappend retval_ [$nbr id] return $retval_ } } } } # $self dbg "There is no agent for this node!" return {} } # check-rpf-link # Finds out the RPF link for a node Classifier/Multicast/Replicator/BST instproc check-rpf-link {node group} { $self instvar node_ set agents [$node_ set agents_] for {set i 0} {$i < [llength $agents]} {incr i} { # Check if you can find a BST agent set agent [lindex $agents $i] $agent instvar proto_ if [info exists proto_] { set protocol [$agent set proto_] set classInfo [$protocol info class] if {$classInfo == "BST"} { # See if the group is a BST group BST instvar RP_ set rpfiif [$node_ from-node-iface \ $RP_($group)] return $rpfiif } } } return -1 } # # 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. # # Source srm-debug.tcl if you want to turn on tracing of events. # Do not insert probes directly in this code unless you are sure # that what you want is not available there. # # Author: Kannan Varadhan <kannan@isi.edu> # Version Date: Mon Jun 30 15:51:33 PDT 1997 # # @(#) $Header$ (USC/ISI) # # THis routine is a temporary hack. It is likely to dissappear # at some point in time. # Agent instproc traffic-source agent { $self instvar tg_ set tg_ $agent $tg_ target $self $tg_ set agent_addr_ [$self set agent_addr_] $tg_ set agent_port_ [$self set agent_port_] } Agent/SRM set packetSize_ 1024 ;# assume default message size for repair is 1K Agent/SRM set groupSize_ 0 Agent/SRM set app_fid_ 0 Agent/SRM set distanceCompute_ ewma Agent/SRM set C1_ 2.0 Agent/SRM set C2_ 2.0 Agent/SRM set requestFunction_ "SRM/request" Agent/SRM set requestBackoffLimit_ 5 Agent/SRM set D1_ 1.0 Agent/SRM set D2_ 1.0 Agent/SRM set repairFunction_ "SRM/repair" Agent/SRM set sessionDelay_ 1.0 Agent/SRM set sessionFunction_ "SRM/session" Class Agent/SRM/Deterministic -superclass Agent/SRM Agent/SRM/Deterministic set C2_ 0.0 Agent/SRM/Deterministic set D2_ 0.0 Class Agent/SRM/Probabilistic -superclass Agent/SRM Agent/SRM/Probabilistic set C1_ 0.0 Agent/SRM/Probabilistic set D1_ 0.0 Class Agent/SRM/Fixed -superclass Agent/SRM Class SRM Class SRM/request -superclass SRM Class SRM/repair -superclass SRM Class SRM/session -superclass SRM # # 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. # # Author: Kannan Varadhan <kannan@isi.edu> # Version Date: Mon Jun 30 15:51:33 PDT 1997 # # @(#) $Header$ (USC/ISI) # Agent/SRM/Adaptive set pdistance_ 0.0 ;# bound instance variables Agent/SRM/Adaptive set requestor_ 0 Agent/SRM/Adaptive set C1_ 2.0 Agent/SRM/Adaptive set MinC1_ 0.5 Agent/SRM/Adaptive set MaxC1_ 2.0 Agent/SRM/Adaptive set C2_ 2.0 Agent/SRM/Adaptive set MinC2_ 1.0 Agent/SRM/Adaptive set MaxC2_ 1.0 ;# G Agent/SRM/Adaptive set D1_ -1 ;# log10 G Agent/SRM/Adaptive set MinD1_ 0.5 Agent/SRM/Adaptive set MaxD1_ 0.0 ;# log10 G Agent/SRM/Adaptive set D2_ -1 ;# log10 G XXX Agent/SRM/Adaptive set MinD2_ 1.0 Agent/SRM/Adaptive set MaxD2_ 1.0 ;# G Agent/SRM/Adaptive set requestFunction_ "SRM/request/Adaptive" Agent/SRM/Adaptive set repairFunction_ "SRM/repair/Adaptive" Agent/SRM/Adaptive set AveDups_ 1.0 Agent/SRM/Adaptive set AveDelay_ 1.0 Agent/SRM/Adaptive set eps_ 0.10 Agent/SRM/Adaptive set done_ 0 Agent/SRM/Adaptive instproc init args { if ![$class set done_] { set pm [[Simulator instance] set packetManager_] TclObject set off_asrm_ [$pm allochdr aSRM] $class set done_ 1 } eval $self next $args $self array set closest_ "requestor 0 repairor 0" $self set AveDups_ [$class set AveDups_] $self set AveDelay_ [$class set AveDelay_] foreach i [list MinC1_ MaxC1_ MinC2_ MaxC2_ \ MinD1_ MaxD1_ MinD2_ MaxD2_] { $self instvar $i set $i [$class set $i] } $self set eps_ [$class set eps_] } Agent/SRM/Adaptive instproc check-bounds args { set G [$self set groupSize_] $self set MaxC2_ $G $self set MaxD1_ [expr log10($G)] $self set MaxD2_ $G if {[llength $args] <= 0} { set args "C1_ C2_ D1_ D2_" } foreach i $args { $self instvar $i set val [$self set $i] ;# We do this for notational convenience set min [$self set Min$i] set max [$self set Max$i] if { $val < $min } { set $i $min } elseif { $val > $max } { set $i $max } } } Agent/SRM/Adaptive instproc recompute-request-params {} { $self instvar closest_ C1_ C2_ stats_ AveDups_ AveDelay_ eps_ if {$stats_(ave-req-delay) < 0} { # This is the very first loss this agent is seeing. $self check-bounds C1_ C2_ ;# adjust bounds to estimated size of G return } $self compute-ave dup-req if $closest_(requestor) { set C2_ [expr $C2_ - 0.1] set closest_(requestor) 0 } elseif {$stats_(ave-dup-req) >= $AveDups_} { set C1_ [expr $C1_ + 0.1] set C2_ [expr $C2_ + 0.5] } elseif {$stats_(ave-dup-req) < [expr $AveDups_ - $eps_]} { if {$stats_(ave-req-delay) > $AveDelay_} { set C2_ [expr $C2_ - 0.1] } if {$stats_(ave-dup-req) < 0.25} { set C1_ [expr $C1_ - 0.05] } } else { set C1_ [expr $C1_ + 0.05] } $self check-bounds C1_ C2_ } Agent/SRM/Adaptive instproc sending-request {} { $self set C1_ [expr [$self set C1_] - 0.1] ;# XXX SF's code uses other # value, 0.05. Why? $self set closest_(requestor) 1 $self check-bounds C1_ } Agent/SRM/Adaptive instproc recv-request {r d s m} { $self instvar pending_ closest_ if { [info exists pending_($s:$m)] && $d == 1 } { # Should we not set closeness, even if we get a late round message? set closeness [$pending_($s:$m) closest-requestor?] if {$closeness >= 0} { set closest_(requestor) $closeness } } $self next $r $d $s $m } Agent/SRM/Adaptive instproc recompute-repair-params {} { $self instvar closest_ D1_ D2_ stats_ AveDups_ AveDelay_ eps_ if {$stats_(ave-rep-delay) < 0} { # This is the very first repair this agent is doing. # if {$D1_ != -1 || $D2_ != -1} { # error "invalid repair parameters <$D1_, $D2_>" # } set logG [expr log10([$self set groupSize_])] set D1_ $logG set D2_ $logG $self check-bounds D1_ D2_ ;# adjust bounds to estimated size of G return } $self compute-ave dup-rep if $closest_(repairor) { set D2_ [expr $D2_ - 0.1] set closest_(repairor) 0 } elseif {$stats_(ave-dup-rep) >= $AveDups_} { set D1_ [expr $D1_ + 0.1] set D2_ [expr $D2_ + 0.5] } elseif {$stats_(ave-dup-rep) < [expr $AveDups_ - $eps_]} { if {$stats_(ave-rep-delay) > $AveDelay_} { set D2_ [expr $D2_ - 0.1] } if {$stats_(ave-dup-rep) < 0.25} { set D1_ [expr $D1_ - 0.05] } } else { set D1_ [expr $D1_ + 0.05] } $self check-bounds D1_ D2_ } Agent/SRM/Adaptive instproc sending-repair {} { $self set D1_ [expr [$self set D1_] - 0.1] ;# XXX SF's code uses other # value, D1 -= 0.05. Why? $self set closest_(repairor) 1 $self check-bounds D1_ } Agent/SRM/Adaptive instproc recv-repair {d s m} { $self instvar pending_ closest_ if { [info exists pending_($s:$m)] && $d == 1 } { # Should we not set closeness, even if we get a late round message? set closeness [$pending_($s:$m) closest-repairor?] if {$closeness >= 0} { set closest_(repairor) $closeness } } $self next $d $s $m } Class SRM/request/Adaptive -superclass SRM/request SRM/request/Adaptive instproc set-params args { $self instvar agent_ $agent_ recompute-request-params eval $self next $args } SRM/request/Adaptive instproc backoff? {} { $self instvar backoff_ backoffCtr_ backoffLimit_ set retval $backoff_ if {[incr backoffCtr_] <= $backoffLimit_} { set backoff_ [expr $backoff_ * 3] } set retval } SRM/request/Adaptive instproc schedule {} { $self next } SRM/request/Adaptive instproc send-request {} { $self instvar agent_ round_ if { $round_ == 1 } { $agent_ sending-request } $self next } SRM/request/Adaptive instproc closest-requestor? {} { $self instvar agent_ sender_ sent_ round_ if {$sent_ == 1 && $round_ == 1} { ;# since repairs aren't rescheduled. if {[$agent_ set pdistance_] > \ [expr 1.5 * [$self distance? $sender_]]} { return 1 } else { return 0 } } else { return -1 } } SRM/request/Adaptive instproc closest-repairor? {} { return -1 } Class SRM/repair/Adaptive -superclass SRM/repair SRM/repair/Adaptive instproc set-params args { $self instvar agent_ $agent_ recompute-repair-params eval $self next $args } SRM/repair/Adaptive instproc schedule {} { $self next } SRM/repair/Adaptive instproc send-repair {} { $self instvar round_ agent_ if { $round_ == 1 } { $agent_ sending-repair } $self next } SRM/repair/Adaptive instproc closest-requestor? {} { return -1 } SRM/repair/Adaptive instproc closest-repairor? {} { $self instvar agent_ requestor_ sent_ round_ if {$sent_ == 1 && $round_ == 1} { if {[$agent_ set pdistance_] > \ [expr 1.5 * [$self distance? $requestor_]]} { return 1 } else { return 0 } } else { return -1 } } ### Agent/SRM instproc init {} { $self next $self instvar ns_ requestFunction_ repairFunction_ set ns_ [Simulator instance] $self init-instvar sessionDelay_ foreach var {C1_ C2_ D1_ D2_} { $self init-instvar $var } $self init-instvar requestFunction_ $self init-instvar repairFunction_ $self init-instvar sessionFunction_ $self init-instvar requestBackoffLimit_ $self init-instvar distanceCompute_ $self array set stats_ [list \ dup-req -1 ave-dup-req -1 \ dup-rep -1 ave-dup-rep -1 \ req-delay 0.0 ave-req-delay -1 \ rep-delay 0.0 ave-rep-delay -1 \ ] } Agent/SRM instproc delete {} { $self instvar ns_ pending_ done_ session_ tg_ foreach i [array names pending_] { $pending_($i) cancel DELETE delete $pending_($i) } $self cleanup delete $session_ if [info exists tg_] { delete $tg_ } } Agent/SRM instproc start {} { $self instvar node_ dst_addr_ ;# defined in Agent base class set dst_addr_ [expr $dst_addr_] ; # get rid of possibly leading 0x etc. $self cmd start $node_ join-group $self $dst_addr_ $self instvar ns_ session_ sessionFunction_ set session_ [new $sessionFunction_ $ns_ $self] $session_ schedule } Agent/SRM instproc start-source {} { $self instvar tg_ if ![info exists tg_] { error "No source defined for agent $self" } $tg_ start } Agent/SRM instproc sessionFunction f { $self instvar sessionFunction_ set sessionFunction_ $f } Agent/SRM instproc requestFunction f { $self instvar requestFunction_ set requestFunction_ $f } Agent/SRM instproc repairFunction f { $self instvar repairFunction_ set repairFunction_ $f } Agent/SRM instproc groupSize? {} { $self set groupSize_ } global alpha if ![info exists alpha] { set alpha 0.25 } proc ewma {ave cur} { if {$ave < 0} { return $cur } else { global alpha return [expr (1 - $alpha) * $ave + $alpha * $cur] } } proc instantaneous {ave cur} { set cur } Agent/SRM instproc compute-ave var { $self instvar stats_ set stats_(ave-$var) [ewma $stats_(ave-$var) $stats_($var)] } #### Agent/SRM instproc recv {type args} { eval $self recv-$type $args } Agent/SRM instproc recv-data {sender msgid} { $self instvar pending_ if ![info exists pending_($sender:$msgid)] { # This is a very late data of some wierd sort. # How did we get here? error "Oy vey! How did we get here?" } if {[$pending_($sender:$msgid) set round_] == 1} { $pending_($sender:$msgid) cancel DATA $pending_($sender:$msgid) evTrace Q DATA delete $pending_($sender:$msgid) unset pending_($sender:$msgid) } else { $pending_($sender:$msgid) recv-repair } } Agent/SRM instproc mark-period period { $self compute-ave $period $self set stats_($period) 0 } Agent/SRM instproc request {sender args} { # called when agent detects a lost packet $self instvar pending_ ns_ requestFunction_ set newReq 0 foreach msgid $args { if [info exists pending_($sender:$msgid)] { error "duplicate loss detection in agent" } set pending_($sender:$msgid) [new $requestFunction_ $ns_ $self] $pending_($sender:$msgid) set-params $sender $msgid $pending_($sender:$msgid) schedule if ![info exists old_($sender:$msgid)] { incr newReq } } if $newReq { $self mark-period dup-req } } Agent/SRM instproc update-ave {type delay} { $self instvar stats_ set stats_(${type}-delay) $delay $self compute-ave ${type}-delay } Agent/SRM instproc recv-request {requestor round sender msgid} { # called when agent receives a control message for an old ADU $self instvar pending_ stats_ if [info exists pending_($sender:$msgid)] { # Either a request or a repair is pending. # Possibly mark duplicates. if { $round == 1 } { incr stats_(dup-req) [$pending_($sender:$msgid) \ dup-request?] } $pending_($sender:$msgid) recv-request } else { # We have no events pending, only if the ADU is old, and # we have received that ADU. $self repair $requestor $sender $msgid } } Agent/SRM instproc repair {requestor sender msgid} { # called whenever agent receives a request, and packet exists $self instvar pending_ ns_ repairFunction_ if [info exists pending_($sender:$msgid)] { error "duplicate repair detection in agent?? really??" } set pending_($sender:$msgid) [new $repairFunction_ $ns_ $self] $pending_($sender:$msgid) set requestor_ $requestor $pending_($sender:$msgid) set-params $sender $msgid $pending_($sender:$msgid) schedule $self mark-period dup-rep } Agent/SRM instproc recv-repair {round sender msgid} { $self instvar pending_ stats_ if ![info exists pending_($sender:$msgid)] { # 1. We didn't hear the request for the older ADU, or # 2. This is a very late repair beyond the $3 d_{S,B}$ wait # What should we do? #if { $round == 1 } { incr stats_(dup-rep) } $self instvar trace_ ns_ node_ if [info exists trace_] { # puts -nonewline $trace_ [format "%7.4f" [$ns_ now]] # puts $trace_ " n [$node_ id] m <$sender:$msgid> r $round X REPAIR late dup" } } else { if { $round == 1 } { incr stats_(dup-rep) [$pending_($sender:$msgid) \ dup-repair?] } $pending_($sender:$msgid) recv-repair } } Agent/SRM/Fixed instproc repair args { $self set D1_ [expr log10([$self set groupSize_])] $self set D2_ [expr log10([$self set groupSize_])] eval $self next $args } #### Agent/SRM instproc clear {obj s m} { $self instvar pending_ done_ old_ logfile_ set done_($s:$m) $obj set old_($s:$m) [$obj set round_] if [info exists logfile_] { $obj dump-stats $logfile_ } unset pending_($s:$m) if {[array size done_] > 32} { $self instvar ns_ $ns_ at [expr [$ns_ now] + 0.01] "$self cleanup" } } Agent/SRM instproc round? {s m} { $self instvar old_ if [info exists old_($s:$m)] { return $old_($s:$m) } else { return 0 } } Agent/SRM instproc cleanup {} { # We need to do this somewhere...now seems a good time $self instvar done_ if [info exists done_] { foreach i [array names done_] { delete $done_($i) } unset done_ } } Agent/SRM instproc trace file { $self set trace_ $file } Agent/SRM instproc log file { $self set logfile_ $file } # # # The class definition for the following classes is at the # top of this file. # Note that the SRM event handlers are not rooted as TclObjects. # SRM instproc init {ns agent} { $self next $self instvar ns_ agent_ nid_ distf_ set ns_ $ns set agent_ $agent set nid_ [[$agent_ set node_] id] set distf_ [$agent_ set distanceCompute_] if ![catch "$agent_ set trace_" traceVar] { $self set trace_ $traceVar } $self array set times_ [list \ startTime [$ns_ now] serviceTime -1 distance -1] } SRM instproc set-params {sender msgid} { $self next $self instvar agent_ sender_ msgid_ round_ sent_ set sender_ $sender set msgid_ $msgid set round_ [$agent_ round? $sender_ $msgid_] set sent_ 0 } SRM instproc cancel {} { $self instvar ns_ eventID_ if [info exists eventID_] { $ns_ cancel $eventID_ unset eventID_ } } SRM instproc schedule {} { $self instvar round_ incr round_ } SRM instproc distance? node { $self instvar agent_ times_ distf_ set times_(distance) [$distf_ $times_(distance) \ [$agent_ distance? $node]] } SRM instproc serviceTime {} { $self instvar ns_ times_ set times_(serviceTime) [expr ([$ns_ now] - $times_(startTime)) / \ ( 2 * $times_(distance))] } # Some routines to make SRM tracing standard SRM instproc logpfx fp { $self instvar ns_ nid_ sender_ msgid_ round_ puts -nonewline $fp [format "%7.4f" [$ns_ now]] puts -nonewline $fp " n $nid_ m <$sender_:$msgid_> r $round_ " } SRM instproc dump-stats fp { $self instvar times_ statistics_ $self logpfx $fp puts -nonewline $fp "type [string range [$self info class] 4 end] " puts $fp "[array get times_] [array get statistics_]" } SRM instproc evTrace {tag type args} { $self instvar trace_ if [info exists trace_] { $self logpfx $trace_ puts -nonewline $trace_ "$tag $type" foreach elem $args { puts -nonewline $trace_ " $elem" } puts $trace_ {} } } #### SRM/request instproc init args { eval $self next $args $self array set statistics_ "dupRQST 0 dupREPR 0 #sent 0 backoff 0" } SRM/request instproc set-params args { eval $self next $args $self instvar agent_ sender_ foreach var {C1_ C2_} { if ![catch "$agent_ set $var" val] { $self instvar $var set $var $val } } $self distance? $sender_ $self instvar backoff_ backoffCtr_ backoffLimit_ set backoff_ 1 set backoffCtr_ 0 set backoffLimit_ [$agent_ set requestBackoffLimit_] $self evTrace Q DETECT } SRM/request instproc dup-request? {} { $self instvar ns_ round_ ignore_ if {$round_ == 2 && [$ns_ now] <= $ignore_} { return 1 } else { return 0 } } SRM/request instproc dup-repair? {} { return 0 } SRM/request instproc backoff? {} { $self instvar backoff_ backoffCtr_ backoffLimit_ set retval $backoff_ if {[incr backoffCtr_] <= $backoffLimit_} { incr backoff_ $backoff_ } set retval } SRM/request instproc compute-delay {} { $self instvar C1_ C2_ set rancomp [expr $C1_ + $C2_ * [uniform 0 1]] $self instvar sender_ backoff_ set dist [$self distance? $sender_] $self evTrace Q INTERVALS C1 $C1_ C2 $C2_ d $dist i $backoff_ set delay [expr $rancomp * $dist] } SRM/request instproc schedule {} { $self instvar ns_ eventID_ delay_ $self next set now [$ns_ now] set delay_ [expr [$self compute-delay] * [$self backoff?]] set fireTime [expr $now + $delay_] $self evTrace Q NTIMER at $fireTime set eventID_ [$ns_ at $fireTime "$self send-request"] } SRM/request instproc cancel type { $self next if {$type == "REQUEST" || $type == "REPAIR"} { $self instvar agent_ round_ if {$round_ == 1} { $agent_ update-ave req [$self serviceTime] } } } SRM/request instproc send-request {} { $self instvar agent_ round_ sender_ msgid_ sent_ round_ $self evTrace Q SENDNACK $agent_ send request $round_ $sender_ $msgid_ $self instvar statistics_ incr statistics_(#sent) set sent_ $round_ } SRM/request instproc recv-request {} { $self instvar ns_ agent_ round_ delay_ ignore_ statistics_ if {[info exists ignore_] && [$ns_ now] < $ignore_} { incr statistics_(dupRQST) # $self evTrace Q NACK dup } else { $self cancel REQUEST $self schedule ;# or rather, reschedule-rqst set ignore_ [expr [$ns_ now] + ($delay_ / 2)] incr statistics_(backoff) $self evTrace Q NACK IGNORE-BACKOFF $ignore_ } } SRM/request instproc recv-repair {} { $self instvar ns_ agent_ sender_ msgid_ ignore_ eventID_ if [info exists eventID_] { $self serviceTime set ignore_ [expr [$ns_ now] + 3 * [$self distance? $sender_]] $ns_ at $ignore_ "$agent_ clear $self $sender_ $msgid_" $self cancel REPAIR $self evTrace Q REPAIR IGNORES $ignore_ } else { ;# we must be in the 3dS,B holdDown interval $self instvar statistics_ incr statistics_(dupREPR) # $self evTrace Q REPAIR dup } } # SRM/repair instproc init args { eval $self next $args $self array set statistics_ "dupRQST 0 dupREPR 0 #sent 0" } SRM/repair instproc set-params args { eval $self next $args $self instvar agent_ requestor_ foreach var {D1_ D2_} { if ![catch "$agent_ set $var" val] { $self instvar $var set $var $val } } $self distance? $requestor_ $self evTrace P NACK from $requestor_ } SRM/repair instproc dup-request? {} { return 0 } SRM/repair instproc dup-repair? {} { $self instvar ns_ round_ if {$round_ == 1} { ;# because repairs do not reschedule return 1 } else { return 0 } } # SRM/repair instproc compute-delay {} { $self instvar D1_ D2_ set rancomp [expr $D1_ + $D2_ * [uniform 0 1]] $self instvar requestor_ set dist [$self distance? $requestor_] $self evTrace P INTERVALS D1 $D1_ D2 $D2_ d $dist set delay [expr $rancomp * $dist] } SRM/repair instproc schedule {} { $self instvar ns_ eventID_ $self next set fireTime [expr [$ns_ now] + [$self compute-delay]] $self evTrace P RTIMER at $fireTime set eventID_ [$ns_ at $fireTime "$self send-repair"] } SRM/repair instproc cancel type { $self next if {$type == "REQUEST" || $type == "REPAIR"} { $self instvar agent_ round_ if {$round_ == 1} { $agent_ update-ave rep [$self serviceTime] } } } SRM/repair instproc send-repair {} { $self instvar ns_ agent_ round_ sender_ msgid_ requestor_ sent_ round_ $self evTrace P SENDREP $agent_ set requestor_ $requestor_ $agent_ send repair $round_ $sender_ $msgid_ $self instvar statistics_ incr statistics_(#sent) set sent_ $round_ } SRM/repair instproc recv-request {} { # duplicate (or multiple) requests $self instvar statistics_ incr statistics_(dupRQST) # $self evTrace P NACK dup } SRM/repair instproc recv-repair {} { $self instvar ns_ agent_ round_ sender_ msgid_ eventID_ requestor_ if [info exists eventID_] { # # holdDown is identical to the ignore_ parameter in # SRM/request::recv-repair. However, since recv-request # for this class is very simple, we do not need an # instance variable to record the current state, and # hence only require a simple variable. # set holdDown [expr [$ns_ now] + \ 3 * [$self distance? $requestor_]] $ns_ at $holdDown "$agent_ clear $self $sender_ $msgid_" $self cancel REPAIR $self evTrace P REPAIR IGNORES $holdDown } else { ;# we must in the 3dS,B holdDown interval $self instvar statistics_ incr statistics_(dupREPR) # $self evTrace P REPAIR dup } } # SRM/session instproc init args { eval $self next $args $self instvar agent_ sessionDelay_ round_ set sessionDelay_ [$agent_ set sessionDelay_] set round_ 1 $self array set statistics_ "#sent 0" $self set sender_ 0 $self set msgid_ 0 } SRM/session instproc delete {} { $self instvar $ns_ eventID_ $ns_ cancel $eventID_ $self next } SRM/session instproc schedule {} { $self instvar ns_ agent_ sessionDelay_ eventID_ $self next # What is a reasonable interval to schedule session messages? set fireTime [expr $sessionDelay_ * [uniform 0.9 1.1]] # set fireTime [expr $sessionDelay_ * [uniform 0.9 1.1] * \ (1 + log([$agent_ set groupSize_])) ] set eventID_ [$ns_ at [expr [$ns_ now] + $fireTime] \ "$self send-session"] } SRM/session instproc send-session {} { $self instvar agent_ statistics_ $agent_ send session $self evTrace S SESSION incr statistics_(#sent) $self schedule } SRM/session instproc evTrace args {} ;# because I don't want to trace # session messages. Class SRM/session/log-scaled -superclass SRM/session SRM/session/log-scaled instproc schedule {} { $self instvar ns_ agent_ sessionDelay_ eventID_ # What is a reasonable interval to schedule session messages? #set fireTime [expr $sessionDelay_ * [uniform 0.9 1.1]] set fireTime [expr $sessionDelay_ * [uniform 0.9 1.1] * \ (1 + log([$agent_ set groupSize_])) ] set eventID_ [$ns_ at [expr [$ns_ now] + $fireTime] \ "$self send-session"] } Agent/SRM/SSM set group_scope_ 32 Agent/SRM/SSM set local_scope_ 2 Agent/SRM/SSM set scope_flag_ 2 Agent/SRM/SSM set rep_id_ 0 Agent/SRM/SSM set numrep_ 0 Agent/SRM/SSM set repthresh_up_ 100 Agent/SRM/SSM set repthresh_low_ 7 Agent/SRM/SSM set Z1_ 1.5 Agent/SRM/SSM set S1_ 0.0 Agent/SRM/SSM set S2_ 3.0 Agent/SRM/SSM instproc init {} { $self next $self instvar numrep_ numloc_ repthresh_up_ repthresh_low_ Z1_ \ S1_ S2_ set numrep_ 0 set numloc_ 0 set repthresh_up_ [$class set repthresh_up_] set repthresh_low_ [$class set repthresh_low_] set Z1_ [$class set Z1_] set S1_ [$class set S1_] set S2_ [$class set S2_] } Agent/SRM/SSM instproc start {} { $self next $self instvar deactivateID_ sessionDelay_ ns_ set now [expr [$ns_ now]] set deactivateID_ [$ns_ at [expr $now + 3 * $sessionDelay_] \ "$self deactivate-reps $now"] } #Currently not used Agent/SRM/SSM instproc repid { rep} { $self instvar rep_id_ $self set rep_id_ [$rep set addr_] $self ch-rep } Agent/SRM/SSM instproc member-scope {scope } { $self instvar scope_flag_ $self set scope_flag_ $scope # $self ch-scope scope } Agent/SRM/SSM instproc local-member? {} { $self instvar scope_flag_ if {$scope_flag_ == 1 } { return 1 } else { return 0 } } Agent/SRM/SSM instproc global-member? {} { $self instvar scope_flag_ if {$scope_flag_ == 2 } { return 1 } else { return 0 } } Agent/SRM/SSM instproc local-member {} { $self member-scope 1 } Agent/SRM/SSM instproc global-rep {} { $self member-scope 2 set rep_id_ [$self set addr_] $self ch-rep } Agent/SRM/SSM instproc set-local-scope {scope} { $self instvar local_scope_ $self set local_scope_ $scope } Agent/SRM/SSM instproc set-global-scope {scope} { $self instvar global-scope $self set global-scope $scope } Agent/SRM/SSM instproc set-repid {rep} { $self instvar rep_id_ $self set rep_id_ [$rep set addr_] $self ch-rep } Agent/SRM/SSM instproc dump-reps {} { $self instvar ns_ activerep_ numrep_ puts "[ft $ns_ $self] numreps: $numrep_" if [info exists activerep_] { foreach i [array names activerep_] { set rtime [$activerep_($i) set recvTime_] set ttl [$activerep_($i) set ttl_] puts "rep: $i recvtime: [ftime $rtime] ttl: $ttl" } } } Agent/SRM/SSM instproc dump-locs {} { $self instvar ns_ activeloc_ numloc_ puts "[ft $ns_ $self] numlocs: $numloc_" if [info exists activeloc_] { foreach i [array names activeloc_] { set rtime [$activeloc_($i) set recvTime_] set ttl [$activeloc_($i) set ttl_] set repid [$activeloc_($i) set repid_] puts "loc: $i recvtime: [ftime $rtime] ttl: \ $ttl repid: $repid" } } } Agent/SRM/SSM instproc send-session {} { $self instvar session_ $session_ send-session } # Called when rep change is detected as following # a) rep sends a local session message # b) do not hear from the rep for a longtime. Agent/SRM/SSM instproc repchange-action {} { $self instvar rep_id_ tentativerep_ tentativettl_ $self instvar ns_ $self cur-num-reps set rep_id_ $tentativerep_ puts "[ft $ns_ $self] chrep rep : $tentativerep_\ ttl : $tentativettl_" $self set-local-scope $tentativettl_ $self local-member $self ch-rep $self send-session } Agent/SRM/SSM instproc recv-lsess {sender repid ttl} { # If it is mychild deactivate-locs will adjust the ttl $self instvar activeloc_ ns_ numloc_ sessionDelay_ deactivatelocID_ $self instvar activerep_ numrep_ $self instvar ch_localID_ tentativerep_ addr_ rep_id_ tentativettl_ # if { $rep_id_ == $sender} { # puts "[ft $ns_ $self] lsess from myparent: $sender== $repid" # } if [info exists activeloc_($sender)] { $activeloc_($sender) recv-lsess $repid $ttl } else { set activeloc_($sender) [new SRMinfo/loc $sender] incr numloc_ $activeloc_($sender) set-params $ns_ $self $activeloc_($sender) recv-lsess $repid $ttl } # if { $repid == [$self set addr_]} { # puts "[ft $ns_ $self] lsess,mychild: $sender== $repid" # } # recvd a local session message from someone who was global if [info exists activerep_($sender)] { delete $activerep_($sender) unset activerep_($sender) incr numrep_ -1 if [info exists ch_localID_] { if {[info exists tentativerep_] && $tentativerep_ == $sender } { # find new rep $self cur-num-reps } if { $repid == $addr_} { $ns_ cancel $ch_localID_ $self unset ch_localID_ $self check-status } } # If this is my rep then if { [$self local-member?]} { if { $sender == $rep_id_} { $self repchange-action } } else { if { $sender == $rep_id_} { puts "[ft $ns_ $self] error" } } } # Currently we do this everytime session message is received set time [expr [$ns_ now] - 3 * $sessionDelay_] if [info exists deactivatelocID_] { $ns_ cancel $deactivatelocID_ unset deactivatelocID_ } $self deactivate-locs $time } Agent/SRM/SSM instproc recv-gsess {sender ttl} { $self instvar activerep_ ns_ numrep_ sessionDelay_ $self instvar deactivateID_ local_scope_ # puts "[ft $ns_ $self] gsess: $sender" $self instvar activeloc_ numloc_ if [info exists activerep_($sender)] { $activerep_($sender) recv-gsess $ttl } else { set activerep_($sender) [new SRMinfo/rep $sender] incr numrep_ $activerep_($sender) set-params $ns_ $self $activerep_($sender) recv-gsess $ttl } # Currently we do this everytime session message is received set time [expr [$ns_ now] - 3 * $sessionDelay_] if [info exists deactivateID_] { $ns_ cancel $deactivateID_ unset deactivateID_ } # recvd a global session message from someone who was local if [info exists activeloc_($sender)] { delete $activeloc_($sender) unset activeloc_($sender) incr numloc_ -1 } if { [$self local-member?]} { if {$ttl < $local_scope_} { set rep_id_ $sender puts "[ft $ns_ $self] closerrep rep : $sender \ ttl : $ttl" $self set-local-scope $ttl $self local-member $self ch-rep $self send-session } } $self deactivate-reps $time $self check-status } Agent/SRM/SSM instproc bias {} { $self instvar activerep_ ns_ sessionDelay_ set now [expr [$ns_ now]] set biasfactor 0 set time [expr $now - 1.5 * $sessionDelay_] if [info exists activerep_] { foreach i [array names activerep_] { set rtime [$activerep_($i) set recvTime_] if { $rtime >= $time} { incr biasfactor } } } return $biasfactor } Agent/SRM/SSM instproc my-loc {} { $self instvar activeloc_ set num 0 if [info exists activeloc_] { foreach i [array names activeloc_] { set repid [$activeloc_($i) set repid_] if { $repid == [$self set addr_]} { incr num } } } return $num } Agent/SRM/SSM instproc cur-num-reps {} { $self instvar activerep_ ns_ sessionDelay_ tentativerep_ tentativettl_ $self instvar Z1_ set now [expr [$ns_ now]] set num 0 set min_ttl 32 set time [expr $now - $Z1_ * $sessionDelay_] if [info exists activerep_] { foreach i [array names activerep_] { set rtime [$activerep_($i) set recvTime_] set ttl [$activerep_($i) set ttl_] if { $rtime >= $time} { if {$min_ttl > $ttl} { set tentativerep_ $i set min_ttl $ttl } incr num } } } set tentativettl_ $min_ttl return $num } Agent/SRM/SSM instproc compute-localdelay {} { $self instvar S1_ S2_ sessionDelay_ set num [$self my-loc] if {$num > 0} { set rancomp [expr $S1_+ 1 + $S2_ * [uniform 0 1]] } else { set rancomp [expr $S1_+ $S2_ * [uniform 0 1]] } # set delay [expr $rancomp * [$self bias] * $sessionDelay_ / \ # $repthresh_up_ ] set delay [expr $rancomp * $sessionDelay_] return $delay } Agent/SRM/SSM instproc compute-globaldelay {} { $self instvar S1_ S2_ sessionDelay_ set rancomp [expr $S1_ + $S2_ * [uniform 0 1]] set delay [expr $rancomp * $sessionDelay_] return $delay } Agent/SRM/SSM instproc schedule-ch-local {} { $self instvar ns_ ch_localID_ set now [$ns_ now] set delay [$self compute-localdelay] set fireTime [expr $now + $delay] if [info exists ch_localID_] { puts "[new_ft $ns_ $self] scheduled called without cancel" $ns_ cancel $ch_localID_ unset ch_localID_ } set ch_localID_ [$ns_ at $fireTime "$self ch-local"] puts "[ft $ns_ $self] schlocal [ftime $fireTime] evid : $ch_localID_" } Agent/SRM/SSM instproc schedule-ch-global {} { $self instvar ns_ ch_globalID_ set now [$ns_ now] set delay [$self compute-globaldelay] set fireTime [expr $now + $delay] if [info exists ch_globalID_] { puts "[ft $ns_ $self] glbscheduled called without cancel" $ns_ cancel $ch_globalID_ unset ch_globalID_ } set ch_globalID_ [$ns_ at $fireTime "$self ch-global"] puts "[ft $ns_ $self] schglobal [ftime $fireTime] evid : $ch_globalID_" } Agent/SRM/SSM instproc check-status {} { $self instvar ns_ numrep_ repthresh_up_ ch_localID_ $self instvar ch_globalID_ repthresh_low_ if { $numrep_ > $repthresh_up_ } { if [info exists ch_localID_] { # Already scheduled.. return; } if { [$self local-member?]} { # Already a local member, cancel changing to # global if scheduled if [info exists ch_globalID_] { $ns_ cancel $ch_globalID_ unset ch_globalID_ } return; } $self schedule-ch-local return; } if {$numrep_ < $repthresh_low_} { if [info exists ch_globalID_] { # Already scheduled.. return; } if { [$self global-member?]} { # Already a global member, cancel changing to # local if scheduled if [info exists ch_localID_] { $ns_ cancel $ch_localID_ unset ch_localID_ } return; } $self schedule-ch-global return; } if [info exists ch_localID_] { $ns_ cancel $ch_localID_ unset ch_localID_ } if [info exists ch_globalID_] { $ns_ cancel $ch_globalID_ unset ch_globalID_ } } Agent/SRM/SSM instproc ch-local {} { $self instvar repthresh_up_ tentativerep_ tentativettl_ ns_ rep_id_ if {[$self cur-num-reps] > $repthresh_up_} { #change scope, scope status and rep set rep_id_ $tentativerep_ puts "[ft $ns_ $self] chlocal rep : $tentativerep_\ ttl : $tentativettl_" $self local-member $self ch-rep $self send-session # Moved this so that the first local message reaches # all the children for faster detection $self set-local-scope $tentativettl_ } if [info exists ch_localID_] { $ns_ cancel ch_localID_ unset ch_localID_ } } Agent/SRM/SSM instproc ch-global {} { $self instvar repthresh_low_ tentativerep_ tentativettl_ ns_ rep_id_ if {[$self cur-num-reps] < $repthresh_low_} { #change scope, scope status and rep set rep_id_ [$self set addr_] puts "[ft $ns_ $self] chglobal rep : $rep_id_\ ttl : $tentativettl_" # Here currently setting the localscope to reach # nearest rep, change later.. # $self set-local-scope $tentativettl_ # Set to zero and modify if get lsess $self set-local-scope 0 $self global-rep $self ch-rep $self send-session } if [info exists ch_globalID_] { $ns_ cancel ch_globalID_ unset ch_globalID_ } } Agent/SRM/SSM instproc deactivate-reps {time} { $self instvar numrep_ activerep_ deactivateID_ ns_ $self instvar sessionDelay_ rep_id_ if [info exists activerep_] { foreach i [array names activerep_] { set rtime [$activerep_($i) set recvTime_] if { $rtime < $time} { delete $activerep_($i) unset activerep_($i) incr numrep_ -1 # have not heard from the rep for a longtime # Currently just find a new rep if { $i == $rep_id_ } { puts "[ft $ns_ $self] $i == $rep_id_" $self repchange-action } } } if {$numrep_ <= 0} { unset activerep_ } } set now [expr [$ns_ now]] set deactivateID_ [$ns_ at [expr $now + 3 * $sessionDelay_] \ "$self deactivate-reps $now"] # $self dump-reps } Agent/SRM/SSM instproc deactivate-locs {time} { $self instvar numloc_ activeloc_ deactivatelocID_ ns_ $self instvar sessionDelay_ local_scope_ set maxttl 0 if [info exists activeloc_] { foreach i [array names activeloc_] { set rtime [$activeloc_($i) set recvTime_] if { $rtime < $time} { delete $activeloc_($i) unset activeloc_($i) incr numloc_ -1 } else { # Might want to set it for own # children only if { [$self global-member?] } { set ttl [$activeloc_($i) set ttl_] if {$maxttl < $ttl} { set maxttl $ttl } set local_scope_ $maxttl } } } if {$numloc_ <= 0} { unset activeloc_ } } set now [expr [$ns_ now]] set deactivatelocID_ [$ns_ at [expr $now + 3 * $sessionDelay_] \ "$self deactivate-locs $now"] # $self dump-locs } Class SRMinfo SRMinfo set recvTime_ 0 SRMinfo instproc init {sender} { $self next $self instvar sender_ set sender_ $sender } SRMinfo instproc set-params {ns agent} { $self instvar ns_ agent_ set ns_ $ns set agent_ $agent } Class SRMinfo/rep -superclass SRMinfo SRMinfo/rep instproc recv-gsess {ttl} { $self instvar recvTime_ ns_ ttl_ set now [$ns_ now] set recvTime_ [expr $now] set ttl_ [expr $ttl] } Class SRMinfo/loc -superclass SRMinfo SRMinfo/loc instproc recv-lsess {repid ttl} { $self instvar recvTime_ ns_ ttl_ repid_ set now [$ns_ now] set recvTime_ [expr $now] set ttl_ [expr $ttl] set repid_ [expr $repid] } # # (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.tcl # Last change: Nov. 17, 1998 # # This software may freely be used only for non-commercial purposes # Agent/MFTP/Snd set dtuSize_ 1424 ;# default size of DTUs (in bytes) Agent/MFTP/Snd set dtusPerBlock_ 1472 ;# default number of DTUs per block Agent/MFTP/Snd set dtusPerGroup_ 8 ;# default group size Agent/MFTP/Snd set fileSize_ 1000000 ;# default file size in bytes Agent/MFTP/Snd set readAheadBufsize_ 2097152;# default size of read-ahead buffer in bytes Agent/MFTP/Snd set interval_ 512000 ;# default transmission rate is 512kbps Agent/MFTP/Snd set txStatusLimit_ 100 ;# default max. number of consecutive status requests without NAK Agent/MFTP/Snd set txStatusDelay_ 2 ;# default time to wait for status responses after a request before polling again Agent/MFTP/Snd set rspBackoffWindow_ 1 ;# default max. time for receivers to wait before replying with nak(s) after a request Agent/MFTP/Snd set reply_addr_ undefined ; # application _must_ specify the sender address (i.e. the one ;# to which NAKs are unicast to). Default is "undefined" Agent/MFTP/Snd set reply_port_ undefined Agent/MFTP/Snd set nakCount_ 0 Agent/MFTP/Snd set seekCount_ 0 ;# number of disk seeks performed Agent/MFTP/Snd instproc init {} { $self next $self instvar ns_ dtuSize_ dtusPerBlock_ dtusPerGroup_ fileSize_ $self instvar reply_addr_ reply_port_ readAheadBufsize_ interval_ $self instvar txStatusLimit_ txStatusDelay_ rspBackoffWindow_ nakCount_ $self instvar seekCount_ set ns_ [Simulator instance] foreach var { dtuSize_ dtusPerBlock_ dtusPerGroup_ fileSize_ \ readAheadBufsize_ interval_ txStatusLimit_ \ txStatusDelay_ rspBackoffWindow_ nakCount_ seekCount_ } { $self init-instvar $var } } # send-data does not get logged here. Rather, we get a callback-call "send-notify" from # the C++ function se_data Agent/MFTP/Snd instproc send-data { } { $self instvar ns_ interval_ if { [$self cmd send data] != -1 } { # if not end-of-pass, re-schedule new "send-data" event $ns_ at [expr [$ns_ now] + $interval_] "$self send-data" } } Agent/MFTP/Snd instproc start {} { $self instvar node_ dst_addr_ set dst_addr_ [expr $dst_addr_] ;# get rid of possibly leading 0x etc. $self cmd start $node_ join-group $self $dst_addr_ $self send-data } Agent/MFTP/Snd instproc pass-finished { CurrentPass NbBlocks } { $self instvar ns_ dtusPerGroup_ interval_ tx_status_requests_ rspBackoffWindow_ set tx_status_requests_ 0 ;# number of consecutively sent status requests if { $CurrentPass >= $dtusPerGroup_ - 1 } { $self send status-req $CurrentPass 0 [expr $NbBlocks-1] $rspBackoffWindow_ } else { # the first dtusPerGroup_ passes are sent without waiting for NAKs: # re-schedule new "send-data" event $ns_ at [expr [$ns_ now] + $interval_] "$self send-data" } } # send-status-req is called at end-of-pass, but only after the dtusPerGroup-th pass. Agent/MFTP/Snd instproc send-status-req { CurrentPass blockLo blockHi rspBackoffWindow } { $self instvar ns_ tx_status_requests_ txStatusDelay_ $self cmd send statreq $CurrentPass $blockLo $blockHi $rspBackoffWindow incr tx_status_requests_ $ns_ at [expr [$ns_ now] + $txStatusDelay_] \ "$self status-rsp-pending $CurrentPass $blockLo $blockHi" } # status-rsp-pending is called after the status-request packet is sent and # if the status-rsp-pending-timer expires. Agent/MFTP/Snd instproc status-rsp-pending { CurrentPass blockLo blockHi } { $self instvar nakCount_ tx_status_requests_ txStatusLimit_ rspBackoffWindow_ # see if we have at least received 1 NAK # if yes, then start over with new pass, i.e. $self send-data # if no, send another status request, i.e. $self send status-req if { $nakCount_ > 0 } { # start over with next pass: set nakCount_ 0 $self send-data } elseif { $tx_status_requests_ < $txStatusLimit_ } { # if we have not reached the maximum number of status-requests: $self send status-req $CurrentPass $blockLo $blockHi $rspBackoffWindow_ } else { # Reached the maximum number of status requests $self done } } Agent/MFTP/Snd instproc recv { type args } { eval $self evTrace $proc $type $args eval $self $proc-$type $args } Agent/MFTP/Snd instproc send { type args } { eval $self evTrace $proc $type $args eval $self $proc-$type $args } # we get here whenever the sender sends a new data packet (callback from send_data) Agent/MFTP/Snd instproc send-notify { args } { } Agent/MFTP/Snd instproc recv-nak { passNb block_nb nak_count} { } Agent/MFTP/Snd instproc done {} { # no further registering of events => end of transmission } Agent/MFTP/Snd instproc trace fd { $self instvar trace_ set trace_ $fd } Agent/MFTP/Snd instproc delete {} { } Agent/MFTP/Snd instproc evTrace { op type args } { $self instvar trace_ ns_ if [info exists trace_] { puts $trace_ [format "%7.4f [[$self set node_] id] $op-$type $args" [$ns_ now]] } } # # (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.tcl # Last change: Dec. 14, 1998 # This software may freely be used only for non-commercial purposes # # standard settings: Agent/MFTP/Rcv set dtuSize_ 1424 Agent/MFTP/Rcv set dtusPerBlock_ 1472 Agent/MFTP/Rcv set dtusPerGroup_ 8 Agent/MFTP/Rcv set fileSize_ 1000000 Agent/MFTP/Rcv set nakCount_ 0 Agent/MFTP/Rcv set seekCount_ 0 Agent/MFTP/Rcv set reply_addr_ 0 ; # unicast reply addr (=address of server) Agent/MFTP/Rcv set reply_port_ 0 ; # unicast reply addr (=address of server) Agent/MFTP/Rcv instproc init {} { $self next $self instvar ns_ dtuSize_ dtusPerBlock_ dtusPerGroup_ fileSize_ $self instvar reply_addr_ reply_port_ nakCount_ seekCount_ set ns_ [Simulator instance] foreach var { dtuSize_ dtusPerBlock_ dtusPerGroup_ fileSize_ reply_addr_ reply_port_ nakCount_ seekCount_} { $self init-instvar $var } } Agent/MFTP/Rcv instproc start {} { $self instvar node_ dst_addr_ set dst_addr_ [expr $dst_addr_] ;# get rid of possibly leading 0x etc. $self cmd start $node_ join-group $self $dst_addr_ } Agent/MFTP/Rcv instproc delete {} { } Agent/MFTP/Rcv instproc done-notify { args } { # File reception completed $self instvar node_ dst_addr_ eval $self evTrace done notify $args $node_ leave-group $self $dst_addr_ } Agent/MFTP/Rcv instproc recv { type args } { eval $self evTrace $proc $type $args eval $self recv-$type $args } #Agent/MFTP/Rcv instproc send { type args } { # eval $self evTrace $proc $type $args # eval $self send-$type $args #} Agent/MFTP/Rcv instproc recv-dependent { CurrentPass CurrentGroup CwPat } { # discard packet due to a linear dependency (no action) } Agent/MFTP/Rcv instproc recv-group-full { CurrentPass CurrentGroup CwPat } { # discard packet due to a full group (no action) } Agent/MFTP/Rcv instproc recv-useful { CurrentPass CurrentGroup CwPat } { # receive useful packet } Agent/MFTP/Rcv instproc recv-status-req { passNb blockLo blockHi txStatusDelay } { $self instvar ns_ set backoff [uniform 0 $txStatusDelay] $ns_ at [expr [$ns_ now] + $backoff] "$self send-nak [list $passNb $blockLo $blockHi]" } Agent/MFTP/Rcv instproc send-nak { passNb blockLo blockHi } { while { $blockLo <= $blockHi } { set bit_count [$self cmd send nak $passNb $blockLo] $self evTrace send nak $passNb $blockLo $bit_count incr blockLo } } Agent/MFTP/Rcv instproc trace fd { $self instvar trace_ set trace_ $fd } Agent/MFTP/Rcv instproc evTrace { op type args } { $self instvar trace_ ns_ if [info exists trace_] { puts $trace_ [format "%7.4f [[$self set node_] id] $op-$type $args" [$ns_ now]] } } # mftp_rcv_stat.tcl # Author: Christoph Haenle <haenle@telematik.informatik.uni-karlsruhe.de> # Version Date: ... # Class Agent/MFTP/Rcv/Stat -superclass Agent/MFTP/Rcv Agent/MFTP/Rcv/Stat instproc init { } { $self instvar nb_useful_recv nb_full_disc nb_lin_dep_disc $self next foreach var [list nb_useful_recv nb_full_disc nb_lin_dep_disc] { set $var 0 } } Agent/MFTP/Rcv/Stat instproc recv-useful { CurrentPass CurrentGroup CwPat } { $self instvar nb_useful_recv puts stdout "recv-useful!" $self next $CurrentPass $CurrentGroup $CwPat incr nb_useful_recv } Agent/MFTP/Rcv/Stat instproc recv-group-full { CurrentPass CurrentGroup CwPat } { $self instvar nb_full_disc puts stdout "recv-group-full!" $self next $CurrentPass $CurrentGroup $CwPat incr nb_full_disc } Agent/MFTP/Rcv/Stat instproc recv-dependent { CurrentPass CurrentGroup CwPat } { $self instvar nb_lin_dep_disc puts stdout "recv-dependent!" $self next $CurrentPass $CurrentGroup $CwPat incr nb_lin_dep_disc } Agent/MFTP/Rcv/Stat instproc done-notify { args } { $self instvar nb_useful_recv nb_full_disc nb_lin_dep_disc eval $self next $args $nb_useful_recv $nb_full_disc $nb_lin_dep_disc } Class McastMonitor McastMonitor instproc init {} { $self instvar period_ ns_ set ns_ [Simulator instance] set period_ 0.03 } McastMonitor instproc trace-topo {} { $self instvar ns_ period_ $self trace-links [$ns_ all-links-list] } McastMonitor instproc trace-links links { $self instvar pktmon_ foreach l $links { set pktmon_($l) [new PktInTranMonitor] $pktmon_($l) attach-link $l $l add-pktmon $pktmon_($l) } } McastMonitor instproc filter {header field value} { $self instvar pktmon_ foreach index [array name pktmon_] { $pktmon_($index) filter $header $field $value } } McastMonitor instproc pktintran {} { $self instvar ns_ pktmon_ set total 0 foreach index [array name pktmon_] { if {[$index up?] == "up"} { incr total [$pktmon_($index) pktintran] } } return $total } McastMonitor instproc print-trace {} { $self instvar ns_ period_ file_ if [info exists file_] { puts $file_ "[$ns_ now] [$self pktintran]" } else { puts "[$ns_ now] [$self pktintran]" } $ns_ at [expr [$ns_ now] + $period_] "$self print-trace" } McastMonitor instproc attach file { $self instvar file_ set file_ $file } ############### ###############################Pkt In Transit Monitor################### ### constructed by ### front filter and front counter: keep track of #pkts passed into link ### rear filter and rear counter: keep track of #pkts passed out of link ### front count - rear count = #pkt in transit ### Class PktInTranMonitor PktInTranMonitor instproc init {} { $self instvar period_ ns_ front_counter_ rear_counter_ front_filter_ rear_filter_ set ns_ [Simulator instance] set period_ 0.03 set front_counter_ [new PktCounter] $front_counter_ set pktInTranMonitor_ $self set front_filter_ [new Filter/MultiField] $front_filter_ filter-target $front_counter_ set rear_counter_ [new PktCounter] $rear_counter_ set pktInTranMonitor_ $self set rear_filter_ [new Filter/MultiField] $rear_filter_ filter-target $rear_counter_ } PktInTranMonitor instproc reset {} { $self instvar front_counter_ rear_counter_ ns_ next_ $front_counter_ reset $rear_counter_ reset if {[info exist next_] && $next_ != 0} { $next_ reset } } PktInTranMonitor instproc filter {header field value} { $self instvar front_filter_ rear_filter_ $front_filter_ filter-field [PktHdr_offset PacketHeader/$header $field] $value $rear_filter_ filter-field [PktHdr_offset PacketHeader/$header $field] $value } PktInTranMonitor instproc attach-link link { $self instvar front_filter_ rear_filter_ front_counter_ rear_counter_ set tmp [$link head] while {[$tmp target] != [$link link]} { set tmp [$tmp target] } $tmp target $front_filter_ $front_filter_ target [$link link] $front_counter_ target [$link link] $rear_filter_ target [[$link link] target] $rear_counter_ target [[$link link] target] [$link link] target $rear_filter_ } PktInTranMonitor instproc attach file { $self instvar file_ set file_ $file } PktInTranMonitor instproc pktintran {} { $self instvar front_counter_ rear_counter_ return [expr [$front_counter_ value] - [$rear_counter_ value]] } PktInTranMonitor instproc output {} { $self instvar front_counter_ rear_counter_ ns_ file_ puts $file_ "[$ns_ now] [expr [$front_counter_ value] - [$rear_counter_ value]]" } PktInTranMonitor instproc periodical-output {} { $self instvar period_ ns_ $self output $ns_ at [expr [$ns_ now] + $period_] "$self periodical-output" } ################ Simulator instproc all-links-list {} { $self instvar link_ set links "" foreach n [array names link_] { lappend links $link_($n) } set links } Link instproc add-pktmon pktmon { $self instvar pktmon_ if [info exists pktmon_] { $pktmon set next_ $pktmon_ } else { $pktmon set next_ 0 } set pktmon_ $pktmon } # # 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. # # @(#) $Header$ # # # exponential factor for backing off join-timer # set rlm_param(alpha) 4 set rlm_param(alpha) 2 set rlm_param(beta) 0.75 set rlm_param(init-tj) 1.5 set rlm_param(init-tj) 10 set rlm_param(init-tj) 5 set rlm_param(init-td) 5 #XXX #set rlm_param(init-td) 10 set rlm_param(init-td-var) 2 set rlm_param(max) 600 #XXX set rlm_param(max) 60 set rlm_param(g1) 0.25 set rlm_param(g2) 0.25 #XXX #set rlm_param(target-exp-time) 5 #puts stderr "rlm: scaling min-rate by M=$M" #set rlm_param(max) [expr $rlm_param(target-exp-time) * 60 * $M] #XXX #puts stderr "rlm: scaling alpha by M=$M" #set rlm_param(alpha) [expr $rlm_param(alpha) * $M] # # The MMG (Multiple Multicast Groups) class implements the RLM # protocol (Receiver-driven Layered Multicast). See # <a href=http://www.cs.berkeley.edu/~mccanne/phd-work/>McCanne's # thesis</a> for a detailed description of RLM.<p> # # This class implements only the basic protocol machinery, it # does not know anything about either ns or mash. MMG is an # abstract class -- you should not instantiate it directly. # Instead, to use RLM a subclass needs to be created that # actually joins and leaves groups, makes upcalls on packet # losses, etc...<p> # # Two such subclasses are implemented at the moment, one for # ns and one for mash. Note that since all code in the MMG # base class is shared between ns and mash, you should not # change anything in this file without being certain that the # changes will work properly in both ns and mash.<p> # # See documentation for the appropriate subclass (i.e., MMG/ns # or MMG/mash) for details about RLM in different environments. Class MMG MMG instproc init { levels } { $self next $self instvar debug_ env_ maxlevel_ set debug_ 0 set env_ [lindex [split [$self info class] /] 1] set maxlevel_ $levels #XXX global rlm_debug_flag if [info exists rlm_debug_flag] { set debug_ $rlm_debug_flag } $self instvar TD TDVAR state_ subscription_ #XXX global rlm_param set TD $rlm_param(init-td) set TDVAR $rlm_param(init-td-var) set state_ /S # # we number the subscription level starting at 1. # level 0 means no groups are subscribed to. # $self instvar layer_ layers_ set i 1 while { $i <= $maxlevel_ } { set layer_($i) [$self create-layer [expr $i - 1]] lappend layers_ $layer_($i) incr i } # # set the subscription level to 0 and call add_layer # to start out with at least one group # set subscription_ 0 $self add-layer set state_ /S # # Schedule the initial join-timer. # $self set_TJ_timer } MMG instproc set-state s { $self instvar state_ set old $state_ set state_ $s $self debug "FSM: $old -> $s" } MMG instproc drop-layer {} { $self dumpLevel $self instvar subscription_ layer_ set n $subscription_ # # if we have an active layer, drop it # if { $n > 0 } { $self debug "DRP-LAYER $n" $layer_($n) leave-group incr n -1 set subscription_ $n } $self dumpLevel } MMG instproc add-layer {} { $self dumpLevel $self instvar maxlevel_ subscription_ layer_ set n $subscription_ if { $n < $maxlevel_ } { $self debug "ADD-LAYER" incr n set subscription_ $n $layer_($n) join-group } $self dumpLevel } MMG instproc current_layer_getting_packets {} { $self instvar subscription_ layer_ TD set n $subscription_ if { $n == 0 } { return 0 } set l $layer_($subscription_) $self debug "npkts [$l npkts]" if [$l getting-pkts] { return 1 } #XXX hack to adjust TD for large latency case set delta [expr [$self now] - [$l last-add]] if { $delta > $TD } { set TD [expr 1.2 * $delta] } return 0 } # # return the amount of loss across all the groups of the given mmg # MMG instproc mmg_loss {} { $self instvar layers_ set loss 0 foreach l $layers_ { incr loss [$l nlost] } return $loss } # # return the number of packets received across all the groups of the given mmg # MMG instproc mmg_pkts {} { $self instvar layers_ set npkts 0 foreach l $layers_ { incr npkts [$l npkts] } return $npkts } #XXX what is this for? # deleted some code that didn't seem to be used... MMG instproc check-equilibrium {} { global rlm_param $self instvar subscription_ maxlevel_ layer_ # see if the next higher-level is maxed out set n [expr $subscription_ + 1] if { $n >= $maxlevel_ || [$layer_($n) timer] >= $rlm_param(max) } { set eq 1 } else { set eq 0 } $self debug "EQ $eq" } MMG instproc backoff-one { n alpha } { $self debug "BACKOFF $n by $alpha" $self instvar layer_ $layer_($n) backoff $alpha } MMG instproc backoff n { $self debug "BACKOFF $n" global rlm_param $self instvar maxlevel_ layer_ set alpha $rlm_param(alpha) set L $layer_($n) $L backoff $alpha incr n while { $n <= $maxlevel_ } { $layer_($n) peg-backoff $L incr n } $self check-equilibrium } MMG instproc highest_level_pending {} { $self instvar maxlevel_ set m "" set n 0 incr n while { $n <= $maxlevel_ } { if [$self level_pending $n] { set m $n } incr n } return $m } MMG instproc rlm_update_D D { # # update detection time estimate # global rlm_param $self instvar TD TDVAR set v [expr abs($D - $TD)] set TD [expr $TD * (1 - $rlm_param(g1)) \ + $rlm_param(g1) * $D] set TDVAR [expr $TDVAR * (1 - $rlm_param(g2)) \ + $rlm_param(g2) * $v] } MMG instproc exceed_loss_thresh {} { $self instvar h_npkts h_nlost set npkts [expr [$self mmg_pkts] - $h_npkts] if { $npkts >= 10 } { set nloss [expr [$self mmg_loss] - $h_nlost] #XXX 0.4 set loss [expr double($nloss) / ($nloss + $npkts)] $self debug "H-THRESH $nloss $npkts $loss" if { $loss > 0.25 } { return 1 } } return 0 } MMG instproc enter_M {} { $self set-state /M $self set_TD_timer_wait $self instvar h_npkts h_nlost set h_npkts [$self mmg_pkts] set h_nlost [$self mmg_loss] } MMG instproc enter_D {} { $self set-state /D $self set_TD_timer_conservative } MMG instproc enter_H {} { $self set_TD_timer_conservative $self set-state /H } MMG instproc log-loss {} { $self debug "LOSS [$self mmg_loss]" $self instvar state_ subscription_ pending_ts_ if { $state_ == "/M" } { if [$self exceed_loss_thresh] { $self cancel_timer TD $self drop-layer $self check-equilibrium $self enter_D } return } if { $state_ == "/S" } { $self cancel_timer TD set n [$self highest_level_pending] if { $n != "" } { # # there is a join-experiment in progress -- # back off the join-experiment rate for the # layer that was doing the experiment # if we're at that layer, drop it, and # update the detection time estimator. # $self backoff $n if { $n == $subscription_ } { set ts $pending_ts_($subscription_) $self rlm_update_D [expr [$self now] - $ts] $self drop-layer $self check-equilibrium $self enter_D return } # # If we're at the level just below the experimental # layer that cause a problem, reset our join timer. # The logic is that we just effectively ran an # experiment, so we might as well reset our timer. # This improves the scalability of the algorithm # by limiting the frequency of experiments. # if { $n == [expr $subscription_ + 1] } { $self cancel_timer TJ $self set_TJ_timer } } if [$self our_level_recently_added] { $self enter_M return } $self enter_H return } if { $state_ == "/H" || $state_ == "/D" } { return } puts stderr "rlm state machine botched" exit -1 } MMG instproc relax_TJ {} { $self instvar subscription_ layer_ if { $subscription_ > 0 } { $layer_($subscription_) relax $self check-equilibrium } } MMG instproc trigger_TD {} { $self instvar state_ if { $state_ == "/H" } { $self enter_M return } if { $state_ == "/D" || $state_ == "/M" } { $self set-state /S $self set_TD_timer_conservative return } if { $state_ == "/S" } { $self relax_TJ $self set_TD_timer_conservative return } puts stderr "trigger_TD: rlm state machine botched $state)" exit -1 } MMG instproc set_TJ_timer {} { global rlm_param $self instvar subscription_ layer_ set n [expr $subscription_ + 1] if ![info exists layer_($n)] { # # no timer -- means we're maximally subscribed # return } set I [$layer_($n) timer] set d [expr $I / 2.0 + [trunc_exponential $I]] $self debug "TJ $d" $self set_timer TJ $d } MMG instproc set_TD_timer_conservative {} { $self instvar TD TDVAR set delay [expr $TD + 1.5 * $TDVAR] $self set_timer TD $delay } MMG instproc set_TD_timer_wait {} { $self instvar TD TDVAR #XXX factor of 2? $self instvar subscription_ set k [expr $subscription_ / 2. + 1.5] # set k 2 $self set_timer TD [expr $TD + $k * $TDVAR] } # # Return true iff the time given by $ts is recent enough # such that any action taken since then is likely to influence the # present or future # MMG instproc is-recent { ts } { $self instvar TD TDVAR set ts [expr $ts + ($TD + 2 * $TDVAR)] if { $ts > [$self now] } { return 1 } return 0 } MMG instproc level_pending n { $self instvar pending_ts_ if { [info exists pending_ts_($n)] && \ [$self is-recent $pending_ts_($n)] } { return 1 } return 0 } MMG instproc level_recently_joined n { $self instvar join_ts_ if { [info exists join_ts_($n)] && \ [$self is-recent $join_ts_($n)] } { return 1 } return 0 } MMG instproc pending_inferior_jexps {} { set n 0 $self instvar subscription_ while { $n <= $subscription_ } { if [$self level_recently_joined $n] { return 1 } incr n } $self debug "NO-PEND-INF" return 0 } # # join the next higher layer when in /S # MMG instproc trigger_TJ {} { $self debug "trigger-TJ" $self instvar state_ ctrl_ subscription_ if { ($state_ == "/S" && ![$self pending_inferior_jexps] && \ [$self current_layer_getting_packets]) } { $self add-layer $self check-equilibrium set msg "add $subscription_" $ctrl_ send $msg #XXX loop back message $self local-join } $self set_TJ_timer } MMG instproc our_level_recently_added {} { $self instvar subscription_ layer_ return [$self is-recent [$layer_($subscription_) last-add]] } MMG instproc recv-ctrl msg { $self instvar join_ts_ pending_ts_ subscription_ $self debug "X-JOIN $msg" set what [lindex $msg 0] if { $what != "add" } { #puts RECV/$msg return } set level [lindex $msg 1] # #XXX # only set the join-ts if the outside J.E. is greater # than our level. if not, then we do not want to falsely # increase the ts of our levels.XXX say this better. # set join_ts_($level) [$self now] if { $level > $subscription_ } { set pending_ts_($level) [$self now] } } MMG instproc local-join {} { $self instvar subscription_ pending_ts_ join_ts_ set join_ts_($subscription_) [$self now] set pending_ts_($subscription_) [$self now] } MMG instproc debug { msg } { $self instvar debug_ subscription_ state_ if {$debug_} { puts stderr "[gettimeofday] layer $subscription_ $state_ $msg" } } #XXX MMG instproc dumpLevel {} { # global rlmTraceFile rates # if [info exists rlmTraceFile] { # $self instvar subscription node rateMap # #XXX # if ![info exists rateMap] { # set s 0 # set rateMap "0" # foreach r $rates { # set s [expr $s + $r] # lappend rateMap $s # } # } # set r [lindex $rateMap $subscription] # puts $rlmTraceFile "[$node id] [ns-now] $r" # } } Class Layer Layer instproc init { mmg } { $self next $self instvar mmg_ TJ npkts_ global rlm_param set mmg_ $mmg set TJ $rlm_param(init-tj) set npkts_ 0 # loss trace created in constructor of derived class } #Layer should relax by beta and not alpha Layer instproc relax {} { global rlm_param $self instvar TJ set TJ [expr $TJ * $rlm_param(beta)] if { $TJ <= $rlm_param(init-tj) } { set TJ $rlm_param(init-tj) } } Layer instproc backoff alpha { global rlm_param $self instvar TJ set TJ [expr $TJ * $alpha] if { $TJ >= $rlm_param(max) } { set TJ $rlm_param(max) } } Layer instproc peg-backoff L { $self instvar TJ set t [$L set TJ] if { $t >= $TJ } { set TJ $t } } Layer instproc timer {} { $self instvar TJ return $TJ } Layer instproc last-add {} { $self instvar add_time_ return $add_time_ } Layer instproc join-group {} { $self instvar npkts_ add_time_ mmg_ set npkts_ [$self npkts] set add_time_ [$mmg_ now] # derived class actually joins group } Layer instproc leave-group {} { # derived class actually leaves group } Layer instproc getting-pkts {} { $self instvar npkts_ return [expr [$self npkts] != $npkts_] } # # 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. # # @(#) $Header$ #XXX goes in ns-lib.tcl Agent/LossMonitor set npkts_ 0 Agent/LossMonitor set bytes_ 0 Agent/LossMonitor set nlost_ 0 Agent/LossMonitor set lastPktTime_ 0 Class LossTrace -superclass Agent/LossMonitor LossTrace set expected_ -1 LossTrace instproc init {} { $self next $self instvar lastTime set lastTime 0 } LossTrace instproc log-loss {} { $self instvar mmg_ $mmg_ log-loss global lossTraceFile lossNode if [info exists lossTraceFile] { set id [[$mmg_ node] id] if { [info exists lossNode] && $lossNode != $id } { return } # # compute intervals of arrived and lost pkts # this code assumes no pkt reordering # (which is true in ns as of 11/96, but this # will change in the future) # set f $lossTraceFile $self instvar layerNo seqno_ expected_ lastPktTime_ \ lastSeqno lastTime if [info exists lastSeqno] { set npkt [expr $expected_ - $lastSeqno] puts $f "p $id $layerNo $lastTime $lastPktTime_ $npkt" set lastTime $lastPktTime_ } set lost [expr $seqno_ - $expected_] set t [ns-now] puts $f "d $id $layerNo $lastPktTime_ $t $lost" set lastSeqno $seqno_ set lastTime $t } } LossTrace instproc flush {} { global lossTraceFile $self instvar lastSeqno expected_ layerNo lastTime \ lastPktTime_ mmg_ seqno_ if [info exists lastSeqno] { set id [[$mmg_ node] id] set npkt [expr $seqno_ - $lastSeqno] if { $npkt != 0 } { puts $lossTraceFile \ "p $id $layerNo $lastTime $lastPktTime_ $npkt" } unset lastSeqno } } Class Layer/ns -superclass Layer Layer/ns instproc init {ns mmg addr layerNo} { $self next $mmg $self instvar ns_ addr_ mon_ set ns_ $ns set addr_ $addr set mon_ [$ns_ create-agent [$mmg node] LossTrace 0] $mon_ set layerNo $layerNo $mon_ set mmg_ $mmg $mon_ set dst_ $addr } Layer/ns instproc join-group {} { $self instvar mon_ mmg_ addr_ $mon_ clear [$mmg_ node] join-group $mon_ $addr_ $self next } Layer/ns instproc leave-group {} { $self instvar mon_ mmg_ addr_ [$mmg_ node] leave-group $mon_ $addr_ $self next } Layer/ns instproc npkts {} { $self instvar mon_ return [$mon_ set npkts_] } Layer/ns instproc nlost {} { $self instvar mon_ return [$mon_ set nlost_] } #XXX get rid of this method! Layer/ns instproc mon {} { $self instvar mon_ return $mon_ } # # This class serves as an interface between the MMG class which # implements the RLM protocol machinery, and the objects in ns # that are involved in the RLm protocol (i.e., Node objects # join/leave multicast groups, LossMonitor objects report packet # loss, etc...).<p> # # See tcl/ex/test-rlm.tcl for an example of how to create a # simulation script that uses RLM # Class MMG/ns -superclass MMG MMG/ns instproc init {ns localNode caddr addrs} { $self instvar ns_ node_ addrs_ set ns_ $ns set node_ $localNode set addrs_ $addrs $self next [llength $addrs] $self instvar ctrl_ set ctrl_ [$ns create-agent $node_ Agent/Message 0] $ctrl_ set dst_ $caddr $ctrl_ proc handle msg "$self recv-ctrl \$msg" $node_ join-group $ctrl_ $caddr } MMG/ns instproc create-layer {layerNo} { $self instvar ns_ addrs_ return [new Layer/ns $ns_ $self [lindex $addrs_ $layerNo] $layerNo] } MMG/ns instproc now {} { $self instvar ns_ return [$ns_ now] } MMG/ns instproc set_timer {which delay} { $self instvar ns_ timers_ if [info exists timers_($which)] { puts "timer botched ($which)" exit 1 } set time [expr [$ns_ now] + $delay] set timers_($which) [$ns_ at $time "$self trigger_timer $which"] } MMG/ns instproc trigger_timer {which} { $self instvar timers_ unset timers_($which) $self trigger_$which } MMG/ns instproc cancel_timer {which} { $self instvar ns_ timers_ if [info exists timers_($which)] { #XXX does this cancel the timer? $ns_ at $timers_($which) unset timers_($which) } } ####### MMG/ns instproc node {} { $self instvar node_ return $node_ } MMG/ns instproc debug { msg } { $self instvar debug_ if {!$debug_} { return } $self instvar subscription_ state_ node_ set time [format %.05f [ns-now]] puts stderr "$time node [$node_ id] layer $subscription_ $state_ $msg" } MMG/ns instproc trace { trace } { $self instvar layers_ foreach s $layers_ { [$s mon] trace $trace } } MMG/ns instproc total_bytes_delivered {} { $self instvar layers_ set v 0 foreach s $layers_ { incr v [[$s mon] set bytes] } return $v } Class SessionSim -superclass Simulator SessionSim set MixMode_ 0 ### Create a session helper that associates with the src agent ### SessionSim instproc create-session { srcNode srcAgent } { $self instvar session_ set nid [$srcNode id] set dst [$srcAgent set dst_addr_] set session_($nid:$dst:$nid) [new SessionHelper] $session_($nid:$dst:$nid) set-node $nid if [SessionSim set rc_] { $session_($nid:$dst:$nid) set rc_ 1 } # If exists nam-traceall, we'll insert an intermediate trace object set trace [$self get-nam-traceall] if {$trace != ""} { # This will write every packet sent and received to # the nam trace file set p [$self create-trace SessEnque $trace $nid $dst "nam"] $srcAgent target $p $p target $session_($nid:$dst:$nid) } else { $srcAgent target $session_($nid:$dst:$nid) } return $session_($nid:$dst:$nid) } SessionSim instproc update-loss-dependency { src dst owner agent group } { $self instvar session_ routingTable_ loss_ set loss_rcv 1 set tmp $dst while {$tmp != $owner} { set next [$routingTable_ lookup $tmp $owner] if {[info exists loss_($next:$tmp)] && $loss_($next:$tmp) != 0} { if {$loss_rcv} { #puts "update-loss-rcv $loss_($next:$tmp) $next $tmp $agent" set dep_loss [$session_($src:$group:$owner) update-loss-rcv $loss_($next:$tmp) $agent] } else { #puts "update-loss-rcv $loss_($next:$tmp) $next $tmp $dep_loss" set dep_loss [$session_($src:$group:$owner) update-loss-loss $loss_($next:$tmp) $dep_loss] } if {$dep_loss == 0} { return } set loss_rcv 0 } set tmp $next } if [info exists dep_loss] { $session_($src:$group:$owner) update-loss-top $dep_loss } } SessionSim instproc join-group { rcvAgent group } { $self instvar session_ routingTable_ delay_ bw_ foreach index [array names session_] { set tri [split $index :] if {[lindex $tri 1] == $group} { set src [lindex $tri 0] set dst [[$rcvAgent set node_] id] set delay 0 set accu_bw 0 set ttl 0 set tmp $dst while {$tmp != $src} { set next [$routingTable_ lookup $tmp $src] set delay [expr $delay + $delay_($tmp:$next)] if {$accu_bw} { set accu_bw [expr 1 / (1 / $accu_bw + 1 / $bw_($tmp:$next))] } else { set accu_bw $bw_($tmp:$next) } incr ttl set tmp $next } # Create nam queues for all receivers if traceall is turned on # XXX # nam will deal with the issue whether all groups share a # single queue per receiver. The simulator simply writes # this information there $self puts-nam-config "G -t [$self now] -i $group -a $dst" # And we should add a trace object before each receiver, # because only this will capture the packet before it # reaches the receiver and after it left the sender set f [$self get-nam-traceall] if {$f != ""} { set p [$self create-trace SessDeque $f $src $dst "nam"] $p target $rcvAgent $session_($index) add-dst $accu_bw $delay $ttl $dst $p $self update-loss-dependency $src $dst $src $p $group } else { #puts "add-dst $accu_bw $delay $ttl $src $dst" $session_($index) add-dst $accu_bw $delay $ttl $dst $rcvAgent $self update-loss-dependency $src $dst $src $rcvAgent $group } } } } SessionSim instproc leave-group { rcvAgent group } { $self instvar session_ foreach index [array names session_] { set tri [split $index :] if {[lindex $tri 1] == $group} { #$session_($index) delete-dst [[$rcvAgent set node_] id] $rcvAgent set dst [[$rcvAgent set node_] id] # remove the receiver from packet distribution list $self puts-nam-traceall \ "G -t [$self now] -i $group -x $dst" } } } SessionSim instproc insert-loss { lossmodule from to } { $self instvar loss_ bw_ Node_ if {[SessionSim set MixMode_] && [$self detailed-link? [$from id] [$to id]]} { $self lossmodel $lossmodule $from $to } elseif [info exists bw_([$from id]:[$to id])] { set loss_([$from id]:[$to id]) $lossmodule } } SessionSim instproc get-delay { src dst } { $self instvar routingTable_ delay_ set delay 0 set tmp $src while {$tmp != $dst} { set next [$routingTable_ lookup $tmp $dst] set delay [expr $delay + $delay_($tmp:$next)] set tmp $next } return $delay } SessionSim instproc get-bw { src dst } { $self instvar routingTable_ bw_ set accu_bw 0 set tmp $src while {$tmp != $dst} { set next [$routingTable_ lookup $tmp $dst] if {$accu_bw} { set accu_bw [expr 1 / (1 / $accu_bw + 1 / $bw_($tmp:$next))] } else { set accu_bw $bw_($tmp:$next) } set tmp $next } return $accu_bw } ### Create sessoin links and nodes SessionSim instproc bw_parse { bspec } { if { [scan $bspec "%f%s" b unit] == 1 } { set unit b } # xxx: all units should support X"ps" --johnh switch $unit { b { return $b } bps { return $b } kb { return [expr $b*1000] } Mb { return [expr $b*1000000] } Gb { return [expr $b*1000000000] } default { puts "error: bw_parse: unknown unit `$unit'" exit 1 } } } SessionSim instproc delay_parse { dspec } { if { [scan $dspec "%f%s" b unit] == 1 } { set unit s } switch $unit { s { return $b } ms { return [expr $b*0.001] } ns { return [expr $b*0.000001] } default { puts "error: bw_parse: unknown unit `$unit'" exit 1 } } } SessionSim instproc node args { $self instvar sessionNode_ if {[llength $args] == 0} { set node [new SessionNode] } else { set node [new SessionNode $args] } set sessionNode_([$node id]) $node $node set ns_ $self return $node } SessionSim instproc simplex-link { n1 n2 bw delay type } { $self instvar bw_ delay_ linkAttr_ set sid [$n1 id] set did [$n2 id] set bw_($sid:$did) [$self bw_parse $bw] set delay_($sid:$did) [$self delay_parse $delay] set linkAttr_($sid:$did:ORIENT) "" set linkAttr_($sid:$did:COLOR) "black" } SessionSim instproc duplex-link { n1 n2 bw delay type } { $self simplex-link $n1 $n2 $bw $delay $type $self simplex-link $n2 $n1 $bw $delay $type $self session-register-nam-linkconfig [$n1 id]:[$n2 id] } SessionSim instproc simplex-link-of-interfaces { n1 n2 bw delay type } { $self simplex-link $n1 $n2 $bw $delay $type } SessionSim instproc duplex-link-of-interfaces { n1 n2 bw delay type } { $self simplex-link $n1 $n2 $bw $delay $type $self simplex-link $n2 $n1 $bw $delay $type $self session-register-nam-linkconfig [$n1 id]:[$n2 id] } ### mix mode detailed link SessionSim instproc detailed-node { id address } { $self instvar Node_ if { [Simulator info vars EnableMcast_] != "" } { warn "Flag variable Simulator::EnableMcast_ discontinued.\n\t\ Use multicast methods as:\n\t\t\ % set ns \[new Simulator -multicast on]\n\t\t\ % \$ns multicast" $self multicast Simulator unset EnableMcast_ } if ![info exist Node_($id)] { set node [new [Simulator set node_factory_] $address] Node set nn_ [expr [Node set nn_] - 1] $node set id_ $id set Node_($id) $node if [$self multicast?] { $node enable-mcast $self } return $node } else { return $Node_($id) } } SessionSim instproc detailed-duplex-link { from to } { $self instvar bw_ delay_ SessionSim set MixMode_ 1 set fromNode [$self detailed-node [$from id] [$from set address_]] set toNode [$self detailed-node [$to id] [$from set address_]] $self simulator-duplex-link $fromNode $toNode $bw_([$from id]:[$to id]) $delay_([$from id]:[$to id]) DropTail } SessionSim instproc simulator-duplex-link { n1 n2 bw delay type args } { $self instvar link_ set i1 [$n1 id] set i2 [$n2 id] if [info exists link_($i1:$i2)] { $self remove-nam-linkconfig $i1 $i2 } eval $self simulator-simplex-link $n1 $n2 $bw $delay $type $args eval $self simulator-simplex-link $n2 $n1 $bw $delay $type $args } SessionSim instproc simulator-simplex-link { n1 n2 bw delay qtype args } { $self instvar link_ queueMap_ nullAgent_ set sid [$n1 id] set did [$n2 id] if [info exists queueMap_($qtype)] { set qtype $queueMap_($qtype) } # construct the queue set qtypeOrig $qtype switch -exact $qtype { ErrorModule { if { [llength $args] > 0 } { set q [eval new $qtype $args] } else { set q [new $qtype Fid] } } intserv { set qtype [lindex $args 0] set q [new Queue/$qtype] } default { set q [new Queue/$qtype] } } # Now create the link switch -exact $qtypeOrig { RTM { set c [lindex $args 1] set link_($sid:$did) [new CBQLink \ $n1 $n2 $bw $delay $q $c] } CBQ - CBQ/WRR { # assume we have a string of form "linktype linkarg" if {[llength $args] == 0} { # default classifier for cbq is just Fid type set c [new Classifier/Hash/Fid 33] } else { set c [lindex $args 1] } set link_($sid:$did) [new CBQLink \ $n1 $n2 $bw $delay $q $c] } intserv { #XX need to clean this up set link_($sid:$did) [new IntServLink \ $n1 $n2 $bw $delay $q \ [concat $qtypeOrig $args]] } default { set link_($sid:$did) [new SimpleLink \ $n1 $n2 $bw $delay $q] } } $n1 add-neighbor $n2 #XXX yuck if {[string first "RED" $qtype] != -1} { $q link [$link_($sid:$did) set link_] } set trace [$self get-ns-traceall] if {$trace != ""} { $self trace-queue $n1 $n2 $trace } set trace [$self get-nam-traceall] if {$trace != ""} { $self namtrace-queue $n1 $n2 $trace } # Register this simplex link in nam link list. Treat it as # a duplex link in nam $self register-nam-linkconfig $link_($sid:$did) } # Assume ops to be performed is 'orient' only # XXX Poor hack. What should we do without a link object?? SessionSim instproc duplex-link-op { n1 n2 op args } { $self instvar linkAttr_ bw_ set sid [$n1 id] set did [$n2 id] if ![info exists bw_($sid:$did)] { error "Non-existent link [$n1 id]:[$n2 id]" } switch $op { "orient" { set linkAttr_($sid:$did:ORIENT) $args set linkAttr_($did:$sid:ORIENT) $args } "color" { set ns [Simulator instance] $ns puts-nam-traceall \ [eval list "l -t [$self now] -s $sid -d $did \ -S COLOR -c $args -o $linkAttr_($sid:$did:COLOR)"] $ns puts-nam-traceall \ [eval list "l -t [$self now] -s $did -d $sid \ -S COLOR -c $args -o $linkAttr_($sid:$did:COLOR)"] eval set attr_($sid:$did:COLOR) $args eval set attr_($did:$sid:COLOR) $args } default { eval puts "Duplex link option $args not implemented \ in SessionSim" } } } # nam support for session sim, Contributed by Haobo Yu # Because here we don't have a link object, we need to have a new # link register method SessionSim instproc session-register-nam-linkconfig link { $self instvar sessionLinkConfigList_ bw_ linkAttr_ if [info exists sessionLinkConfigList_] { # Check whether the reverse simplex link is registered, # if so, don't register this link again. # We should have a separate object for duplex link. set tmp [split $link :] set i1 [lindex $tmp 0] set i2 [lindex $tmp 1] if [info exists bw_($i2:$i1)] { set pos [lsearch $sessionLinkConfigList_ $i2:$i1] if {$pos >= 0} { set a1 $linkAttr_($i2:$i1:ORIENT) set a2 $linkAttr_($link:ORIENT) if {$a1 == "" && $a2 != ""} { # If this duplex link has not been # assigned an orientation, do it. set sessionLinkConfigList_ [lreplace $sessionLinkConfigList_ $pos $pos] } else { return } } } # Remove $link from list if it's already there set pos [lsearch $sessionLinkConfigList_ $link] if {$pos >= 0} { set sessionLinkConfigList_ \ [lreplace $sessionLinkConfigList_ $pos $pos] } } lappend sessionLinkConfigList_ $link } # write link configurations SessionSim instproc dump-namlinks {} { $self instvar bw_ delay_ sessionLinkConfigList_ linkAttr_ set ns [Simulator instance] foreach lnk $sessionLinkConfigList_ { set tmp [split $lnk :] set i1 [lindex $tmp 0] set i2 [lindex $tmp 1] $ns puts-nam-traceall \ "l -t * -s $i1 -d $i2 -S UP -r $bw_($lnk) -D \ $delay_($lnk) -o $linkAttr_($lnk:ORIENT)" } } SessionSim instproc dump-namnodes {} { $self instvar sessionNode_ if ![$self is-started] { return } foreach nn [array names sessionNode_] { if ![$sessionNode_($nn) is-lan?] { $sessionNode_($nn) dump-namconfig } } } ### Routing support SessionSim instproc compute-routes {} { # # call hierarchical routing, if applicable # if [Simulator set EnableHierRt_] { $self compute-hier-routes } else { $self compute-flat-routes } } SessionSim instproc compute-flat-routes {} { $self instvar bw_ # # Compute all the routes using the route-logic helper object. # set r [$self get-routelogic] foreach ln [array names bw_] { set L [split $ln :] set srcID [lindex $L 0] set dstID [lindex $L 1] if {$bw_($ln) != 0} { $r insert $srcID $dstID } else { $r reset $srcID $dstID } } $r compute } SessionSim instproc compute-hier-routes {} { $self instvar bw_ set r [$self get-routelogic] # # send hierarchical data : # array of cluster size, #clusters, #domains # assuming 3 levels of hierarchy --> this should be extended to # support # n-levels of hierarchy # # puts "Computing Hierarchical routes\n" set level [AddrParams set hlevel_] $r hlevel-is $level $self hier-topo $r foreach ln [array names bw_] { set L [split $ln :] set srcID [[$self get-node-by-id [lindex $L 0]] node-addr] set dstID [[$self get-node-by-id [lindex $L 1]] node-addr] if { $bw_($ln) != 0 } { # $r hier-insert $srcID $dstID $bw_($ln) $r hier-insert $srcID $dstID } else { $r hier-reset $srcID $dstID } } $r hier-compute } SessionSim instproc compute-algo-routes {} { set r [$self get-routelogic] # puts "Computing algorithmic routes" $r BFS $r compute } ### Route length analysis helper function SessionSim instproc dump-routelogic-distance {} { $self instvar routingTable_ sessionNode_ bw_ if ![info exists routingTable_] { puts "error: routing table is not computed yet!" return 0 } # puts "Dumping Routing Table: Distance Information" set n [Node set nn_] set i 0 puts -nonewline "\t" while { $i < $n } { if ![info exists sessionNode_($i)] { incr i continue } puts -nonewline "$i\t" incr i } set i 0 while { $i < $n } { if ![info exists sessionNode_($i)] { incr i continue } puts -nonewline "\n$i\t" set n1 $sessionNode_($i) set j 0 while { $j < $n } { if { $i != $j } { set nh [$routingTable_ lookup $i $j] if { $nh >= 0 } { set distance 0 set tmpfrom $i set tmpto $j while {$tmpfrom != $tmpto} { set tmpnext [$routingTable_ lookup $tmpfrom $tmpto] set distance [expr $distance + 1] set tmpfrom $tmpnext } puts -nonewline "$distance\t" } else { puts -nonewline "0\t" } } else { puts -nonewline "0\t" } incr j } incr i } puts "" } ### SessionSim instproc run SessionSim instproc run args { $self rtmodel-configure ;# in case there are any [$self get-routelogic] configure $self instvar scheduler_ sessionNode_ started_ set started_ 1 # # Reset every node, which resets every agent # foreach nn [array names sessionNode_] { $sessionNode_($nn) reset } if [SessionSim set MixMode_] { foreach nn [array names Node_] { $Node_($nn) reset } } # We don't have queues in SessionSim $self dump-namcolors $self dump-namnodes $self dump-namlinks $self dump-namagents return [$scheduler_ run] } # Debugging mcast tree function; Contributed by Haobo Yu # Get multicast tree in session simulator: By assembling individual # (receiver, sender) paths into a SPT. # src is a Node. SessionSim instproc get-mcast-tree { src grp } { $self instvar treeLinks_ session_ if [info exists treeLinks_] { unset treeLinks_ } set sid [$src id] # get member list foreach idx [array names session_] { set tri [split $idx :] if {[lindex $tri 0] == $sid && [lindex $tri 1] == $grp} { set mbrs [$session_($idx) list-mbr] break } } foreach mbr $mbrs { # Find path from $mbr to $src while {![string match "Agent*" [$mbr info class]]} { # In case agent is at the end of the chain... set mbr [$mbr target] } set mid [[$mbr set node_] id] if {$sid == $mid} { continue } # get paths for each individual member $self merge-path $sid $mid } # generating tree link list foreach lnk [array names treeLinks_] { lappend res $lnk $treeLinks_($lnk) } return $res } # Merge the path from mbr to src # src is node id. SessionSim instproc merge-path { src mbr } { $self instvar routingTable_ treeLinks_ bw_ # get paths from mbr to src and merge into treeLinks_ set tmp $mbr while {$tmp != $src} { set nxt [$routingTable_ lookup $tmp $src] # XXX # Assume routingTable lookup is always successful, so # don't validate existence of bw_($tid:$sid) # Always arrange tree links in (parent, child). if ![info exists treeLinks_($nxt:$tmp)] { set treeLinks_($nxt:$tmp) $bw_($nxt:$tmp) } if [info exists treeLinks_($tmp:$nxt)] { error "Reverse links in a SPT!" } set tmp $nxt } } SessionSim instproc get-node-by-id id { $self instvar sessionNode_ Node_ if [info exists Node_($id)] { set Node_($id) } else { set sessionNode_($id) } } SessionSim instproc get-node-id-by-addr address { $self instvar sessionNode_ set n [Node set nn_] for {set q 0} {$q < $n} {incr q} { set nq $sessionNode_($q) if {[string compare [$nq node-addr] $address] == 0} { return $q } } error "get-node-id-by-addr:Cannot find node with given address" } ############## SessionNode ############## Class SessionNode -superclass Node SessionNode instproc init args { $self instvar id_ np_ address_ set id_ [Node getid] set np_ 0 if {[llength $args] > 0} { set address_ $args } else { set address_ $id_ } } SessionNode instproc id {} { $self instvar id_ return $id_ } SessionNode instproc reset {} { } SessionNode instproc alloc-port {} { $self instvar np_ set p $np_ incr np_ return $p } SessionNode instproc attach agent { $self instvar id_ address_ $agent set node_ $self set port [$self alloc-port] set mask 0xffffffff set shift 0 if [Simulator set EnableHierRt_] { set nodeaddr [AddrParams set-hieraddr $address_] } else { set nodeaddr [expr [expr $address_ & [AddrParams set NodeMask_(1)]]\ << [AddrParams set NodeShift_(1)]] } $agent set agent_addr_ $nodeaddr $agent set agent_port_ $port } SessionNode instproc join-group { rcvAgent group } { set group [expr $group] if [SessionSim set MixMode_] { [Simulator instance] join-intermediate-session $rcvAgent $group } else { [Simulator instance] join-group $rcvAgent $group } } SessionNode instproc leave-group { rcvAgent group } { set group [expr $group] [Simulator instance] leave-group $rcvAgent $group } Agent/LossMonitor instproc show-delay { seqno delay } { $self instvar node_ puts "[$node_ id] $seqno $delay" } ####################### Mix Mode Stuff ################################## ### Create a session helper that does not associates with a src agent ### ### I.e., Create an intermediate session for mix mode operation ### ### Return the obj to perform detailed join ### SessionSim instproc RPF-link { src from to } { $self instvar routingTable_ link_ # # If this link is on the RPF tree, return the link object. # if [info exists routingTable_] { set tmp $to while {$tmp != $src} { set reverse [$routingTable_ lookup $tmp $src] if [info exists link_($reverse:$tmp)] { return $link_($reverse:$tmp) } set tmp $reverse } } return "" } SessionSim instproc detailed-link? { from to } { $self instvar link_ return [info exist link_($from:$to)] } SessionSim instproc create-intermediate-session { src group nid } { $self instvar session_ set session_($src:$group:$nid) [new SessionHelper] $session_($src:$group:$nid) set-node $nid if [SessionSim set rc_] { $session_($src:$group:$nid) set rc_ 1 } # If exists nam-traceall, we'll insert an intermediate trace object set trace [$self get-nam-traceall] if {$trace != ""} { # This will write every packet sent and received to # the nam trace file set p [$self create-trace SessEnque $trace $nid $dst "nam"] $p target $session_($src:$group:$nid) return $p } else { return $session_($src:$group:$nid) } } SessionSim instproc join-intermediate-session { rcvAgent group } { $self instvar session_ routingTable_ delay_ bw_ link_ Node_ dlist_ foreach index [array names session_] { set tri [split $index :] set src [lindex $tri 0] set grp [lindex $tri 1] set owner [lindex $tri 2] if {$grp == $group && $src == $owner} { set session_area 1 set dst [[$rcvAgent set node_] id] set delay 0 set accu_bw 0 set ttl 0 set tmp $dst while {$tmp != $src} { set next [$routingTable_ lookup $tmp $src] # Conditions to perform session/detailed join if {$session_area} { if [info exist link_($tmp:$next)] { # walking into detailed area from session area set session_area 0 if ![info exist session_($src:$grp:$tmp)] { set inter_session [$self create-intermediate-session $src $grp $tmp] } else { set inter_session $session_($src:$grp:$tmp) } if {![info exist dlist_($src:$grp:$tmp)] || [lsearch $dlist_($src:$grp:$tmp) $rcvAgent] < 0 } { $inter_session add-dst $accu_bw $delay $ttl $dst $rcvAgent $self update-loss-dependency $src $dst $tmp $rcvAgent $group lappend dlist_($src:$grp:$tmp) $rcvAgent } $Node_($tmp) join-group $inter_session $group # puts "s->d: $dst, $rcvAgent, [$rcvAgent info class], join session $inter_session which detailed-joined the group $group, $delay, $accu_bw, $ttl" } else { # stay in session area, keep track of accumulative # delay, bw, ttl set delay [expr $delay + $delay_($tmp:$next)] if {$accu_bw} { set accu_bw [expr 1 / (1 / $accu_bw + 1 / $bw_($tmp:$next))] } else { set accu_bw $bw_($tmp:$next) } incr ttl # puts "s->s: $dst, $rcvAgent, [$rcvAgent info class], $group, $delay, $accu_bw, $ttl" } } else { if [info exist link_($tmp:$next)] { # stay in detailed area, do nothing # puts "d->d" } else { # walking into session area from detailed area set session_area 1 set accu_bw $bw_($tmp:$next) set delay $delay_($tmp:$next) set ttl 1 set dst $tmp set rcvAgent [$Node_($tmp) entry] # puts "d->s: $dst, $rcvAgent, [$rcvAgent info class], $group, $delay, $accu_bw, $ttl" } } set tmp $next } # Create nam queues for all receivers if traceall is turned on # XXX # nam will deal with the issue whether all groups share a # single queue per receiver. The simulator simply writes # this information there $self puts-nam-config "G -t [$self now] -i $group -a $dst" # And we should add a trace object before each receiver, # because only this will capture the packet before it # reaches the receiver and after it left the sender set f [$self get-nam-traceall] if {$session_area} { if {$f != ""} { set p [$self create-trace SessDeque $f $src $dst "nam"] $p target $rcvAgent if {![info exist dlist_($index)] || [lsearch $dlist_($index) $rcvAgent] < 0 } { $session_($index) add-dst $accu_bw $delay $ttl $dst $p $self update-loss-dependency $src $dst $src $p $group lappend dlist_($index) $rcvAgent } } else { # puts "session area: add-dst $accu_bw $delay $ttl $src $dst $rcvAgent [$rcvAgent info class]" if {![info exist dlist_($index)] || [lsearch $dlist_($index) $rcvAgent] < 0 } { $session_($index) add-dst $accu_bw $delay $ttl $dst $rcvAgent $self update-loss-dependency $src $dst $src $rcvAgent $group lappend dlist_($index) $rcvAgent } } } else { if {$f != ""} { set p [$self create-trace SessDeque $f $src $src "nam"] $p target [$Node_($tmp) entry] if {![info exist dlist_($index)] || [lsearch $dlist_($index) [$Node_($tmp) entry]] < 0 } { $session_($index) add-dst 0 0 0 $src $p $self update-loss-dependency $src $src $src $p $group lappend dlist_($index) [$Node_($tmp) entry] } } else { # puts "detailed area: add-dst $accu_bw $delay $ttl $src $dst[$Node_($tmp) entry] [[$Node_($tmp) entry] info class]" if {![info exist dlist_($index)] || [lsearch $dlist_($index) [$Node_($tmp) entry]] < 0 } { $session_($index) add-dst 0 0 0 $src [$Node_($tmp) entry] $self update-loss-dependency $src $src $src [$Node_($tmp) entry] $group lappend dlist_($index) [$Node_($tmp) entry] } } } } } } # 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 an HTTP server # # $Header$ # # PagePool # # Generage a new page, including size, age, and flags. Do NOT generate # modification time. That's the job of web servers. PagePool instproc gen-page { pageid thismod } { set size [$self gen-size $pageid] # If $thismod == -1, we set age to -1, which means this page # never changes if {$thismod >= 0} { set age [expr [$self gen-modtime $pageid $thismod] - $thismod] } else { set age -1 } return "size $size age $age modtime $thismod" } # # Compound pagepool with a non-cacheable main page # Class PagePool/CompMath/noc -superclass PagePool/CompMath PagePool/CompMath/noc instproc gen-page { pageid thismod } { set res [eval $self next $pageid $thismod] if {$pageid == 0} { return "$res noc 1" } else { return $res } } # # web server codes # Http/Server instproc init args { eval $self next $args $self instvar node_ stat_ $node_ color "HotPink" array set stat_ [list hit-num 0 mod-num 0 barrival 0] } Http/Server instproc set-page-generator { pagepool } { $self instvar pgtr_ set pgtr_ $pagepool } Http/Server instproc gen-init-modtime { id } { $self instvar pgtr_ ns_ if [info exists pgtr_] { return [$pgtr_ gen-init-modtime $id] } else { return [$ns_ now] } } # XXX # This method to calculate staleness time isn't scalable!!! We have to have # a garbage collection method to release unused portion of modtimes_ and # modseq_. That's not implemented yet because it requires the server to know # the oldest version held by all other clients. Http/Server instproc stale-time { pageid modtime } { $self instvar modseq_ modtimes_ ns_ for {set i $modseq_($pageid)} {$i >= 0} {incr i -1} { if {$modtimes_($pageid:$i) <= $modtime} { break } } if {$i < 0} { error "Non-existent modtime $modtime for page $pageid" } set ii [expr $i + 1] set t1 [expr abs($modtimes_($pageid:$i) - $modtime)] set t2 [expr abs($modtimes_($pageid:$ii) - $modtime)] if {$t1 > $t2} { incr ii } return [expr [$ns_ now] - $modtimes_($pageid:$ii)] } Http/Server instproc modify-page { pageid } { # Set Last-Modified-Time to current time $self instvar ns_ id_ stat_ pgtr_ incr stat_(mod-num) set id [lindex [split $pageid :] end] # Change modtime and lifetime only, do not change page size set modtime [$ns_ now] if [info exists pgtr_] { set pginfo [$pgtr_ gen-page $id $modtime] } else { set pginfo "size 2000 age 50 modtime $modtime" } array set data $pginfo set age $data(age) $self schedule-nextmod [expr [$ns_ now] + $age] $pageid eval $self enter-page $pageid $pginfo $ns_ trace-annotate "S $id_ INV $pageid" $self evTrace S MOD p $pageid m [$ns_ now] n [expr [$ns_ now] + $age] $self instvar modtimes_ modseq_ incr modseq_($pageid) set modtimes_($pageid:$modseq_($pageid)) $modtime } Http/Server instproc schedule-nextmod { time pageid } { $self instvar ns_ $ns_ at $time "$self modify-page $pageid" } Http/Server instproc gen-page { pageid } { set pginfo [$self gen-pageinfo $pageid] eval $self enter-page $pageid $pginfo return $pginfo } # XXX Assumes page doesn't exists before. Http/Server instproc gen-pageinfo { pageid } { $self instvar ns_ pgtr_ if [$self exist-page $pageid] { error "$self: shouldn't use gen-page for existing pages" } set id [lindex [split $pageid :] end] # XXX If a page never changes, set modtime to -1 here!! set modtime [$self gen-init-modtime $id] if [info exists pgtr_] { set pginfo [$pgtr_ gen-page $id $modtime] } else { set pginfo "size 2000 age 50 modtime $modtime" } array set data $pginfo set age $data(age) if {$modtime >= 0} { $self schedule-nextmod [expr [$ns_ now] + $age] $pageid } $self evTrace S MOD p $pageid m [$ns_ now] n [expr [$ns_ now] + $age] $self instvar modtimes_ modseq_ set modseq_($pageid) 0 set modtimes_($pageid:0) $modtime return [join $pginfo] } Http/Server instproc disconnect { client } { $self instvar ns_ clist_ node_ set pos [lsearch $clist_ $client] if {$pos >= 0} { lreplace $clist_ $pos $pos } else { error "Http/Server::disconnect: not connected to $server" } set tcp [[$self get-cnc $client] agent] $self cmd disconnect $client $tcp proc done {} "$ns_ detach-agent $node_ $tcp; delete $tcp" $tcp close #puts "server [$self id] disconnect" } Http/Server instproc alloc-connection { client fid } { Http instvar TRANSPORT_ $self instvar ns_ clist_ node_ fid_ lappend clist_ $client set snk [new Agent/TCP/$TRANSPORT_] $snk set fid_ $fid $ns_ attach-agent $node_ $snk $snk listen set wrapper [new Application/TcpApp $snk] $self cmd connect $client $wrapper return $wrapper } Http/Server instproc handle-request-GET { pageid args } { $self instvar ns_ if [$self exist-page $pageid] { set pageinfo [$self get-page $pageid] } else { set pageinfo [$self gen-page $pageid] } lappend res [$self get-size $pageid] eval lappend res $pageinfo } Http/Server instproc handle-request-IMS { pageid args } { array set data $args set mt [$self get-modtime $pageid] if {$mt <= $data(modtime)} { # Send a not-modified since set size [$self get-invsize] # We don't need other information for a IMS of a # valid page set pageinfo \ "size $size modtime $mt time [$self get-cachetime $pageid]" $self evTrace S SND p $pageid m $mt z $size t IMS-NM } else { # Page modified, send the new one set size [$self get-size $pageid] set pageinfo [$self get-page $pageid] $self evTrace S SND p $pageid m $mt z $size t IMS-M } lappend res $size eval lappend res $pageinfo return $res } Http/Server instproc get-request { client type pageid args } { $self instvar ns_ id_ stat_ incr stat_(hit-num) array set data $args incr stat_(barrival) $data(size) unset data # XXX Here maybe we want to wait for a random time to model # server response delay, it could be easily added in a derived class. set res [eval $self handle-request-$type $pageid $args] set size [lindex $res 0] set pageinfo [lrange $res 1 end] $self send $client $size \ "$client get-response-$type $self $pageid $pageinfo" } Http/Server instproc set-parent-cache { cache } { # Dummy proc } #---------------------------------------------------------------------- # Http server modifying pages in the way as described in Pei Cao et al's # ICDCS'97 paper. Used to test the simulator #---------------------------------------------------------------------- Class Http/Server/epa -superclass Http/Server Http/Server/epa instproc start-update { interval } { $self instvar pm_itv_ ns_ set pm_itv_ $interval $ns_ at [expr [$ns_ now] + $pm_itv_] "$self modify-page" } # Schedule next page modification using another way Http/Server/epa instproc schedule-nextmod { time pageid } { $self instvar ns_ pm_itv_ $ns_ at [expr [$ns_ now]+$pm_itv_] "$self modify-page $pageid" } # Change the page id to be modified. The pageid given in argument makes # no sense at all. Http/Server/epa instproc modify-page args { $self instvar pgtr_ set pageid $self:[$pgtr_ pick-pagemod] eval $self next $pageid } # Do not schedule modification during page generation. Http/Server/epa instproc gen-pageinfo { pageid } { $self instvar ns_ pgtr_ if [$self exist-page $pageid] { error "$self: shouldn't use gen-page for existing pages" } set id [lindex [split $pageid :] end] set modtime [$self gen-init-modtime $id] if [info exists pgtr_] { set pginfo [$pgtr_ gen-page $id $modtime] } else { set pginfo "size 2000 age 50 modtime $modtime" } array set data $pginfo set age $data(age) $self instvar modtimes_ modseq_ set modseq_($pageid) 0 set modtimes_($pageid:0) $modtime return [join $pginfo] } #---------------------------------------------------------------------- # Base Http invalidation server #---------------------------------------------------------------------- Http/Server/Inval instproc modify-page { pageid } { $self next $pageid $self instvar ns_ id_ $self invalidate $pageid [$ns_ now] } Http/Server/Inval instproc handle-request-REF { pageid args } { return [eval $self handle-request-GET $pageid $args] } #---------------------------------------------------------------------- # Old unicast invalidation Http server. For compatibility # Server with single unicast invalidation #---------------------------------------------------------------------- Class Http/Server/Inval/Ucast -superclass Http/Server/Inval # We need to maintain a list of all caches who have gotten a page from this # server. Http/Server/Inval/Ucast instproc get-request { client type pageid args } { eval $self next $client $type $pageid $args # XXX more efficient representation? $self instvar cacheList_ if [info exists cacheList_($pageid)] { set pos [lsearch $cacheList_($pageid) $client] } else { set pos -1 } # If it's a new cache, put it there # XXX we should eventually have a timer for each cache entry, so # we can get rid of old cache entries if {$pos < 0 && [regexp "Cache" [$client info class]]} { lappend cacheList_($pageid) $client } } Http/Server/Inval/Ucast instproc invalidate { pageid modtime } { $self instvar cacheList_ if ![info exists cacheList_($pageid)] { return } foreach c $cacheList_($pageid) { # Send invalidation to every cache, assuming a connection # exists between the server and the cache set size [$self get-invsize] # Mark invalidation packet as another fid set agent [[$self get-cnc $c] agent] set fid [$agent set fid_] $agent_ set fid_ [Http set PINV_FID_] $self send $c $size \ "$c invalidate $pageid $modtime" $agent_ set fid_ $fid $self evTrace S INV p $pageid m $modtime z $size } } #---------------------------------------------------------------------- # (Y)et another (U)ni(C)ast invalidation server # # It has a single parent cache. Whenever a page is updated in this server # it informs the parent cache, which will in turn propagate the update # (or invalidation) to the whole cache hierarchy. #---------------------------------------------------------------------- Http/Server/Inval/Yuc instproc set-tlc { tlc } { $self instvar tlc_ set tlc_ $tlc } Http/Server/Inval/Yuc instproc get-tlc { tlc } { $self instvar tlc_ return $tlc_ } Http/Server/Inval/Yuc instproc next-hb {} { Http/Server/Inval/Yuc instvar hb_interval_ return [expr $hb_interval_ * [uniform 0.9 1.1]] } # XXX Must do this when the caching hierachy is ready Http/Server/Inval/Yuc instproc set-parent-cache { cache } { $self instvar pcache_ set pcache_ $cache # Send JOIN #puts "[$self id] joins cache [$pcache_ id]" $self send $pcache_ [$self get-joinsize] \ "$pcache_ server-join $self $self" # Establish an invalidation connection using TCP Http instvar TRANSPORT_ $self instvar ns_ node_ set tcp [new Agent/TCP/$TRANSPORT_] $tcp set fid_ [Http set HB_FID_] $ns_ attach-agent $node_ $tcp set dst [$pcache_ setup-unicast-hb] set snk [$dst agent] $ns_ connect $tcp $snk #$tcp set dst_ [$snk set addr_] $tcp set window_ 100 set wrapper [new Application/TcpApp/HttpInval $tcp] $wrapper connect $dst $wrapper set-app $self $self add-inval-sender $wrapper # Start heartbeat after some time, otherwise TCP connection may # not be well established... $self instvar ns_ $ns_ at [expr [$ns_ now] + [$self next-hb]] "$self heartbeat" } Http/Server/Inval/Yuc instproc heartbeat {} { $self instvar pcache_ ns_ $self cmd send-hb $ns_ at [expr [$ns_ now] + [$self next-hb]] \ "$self heartbeat" } Http/Server/Inval/Yuc instproc get-request { cl type pageid args } { eval $self next $cl $type $pageid $args if {($type == "GET") || ($type == "REF")} { $self count-request $pageid } } Http/Server/Inval/Yuc instproc invalidate { pageid modtime } { $self instvar pcache_ id_ enable_upd_ if ![info exists pcache_] { error "Server $id_ doesn't have a parent cache!" } # One more invalidation $self count-inval $pageid if [$self is-pushable $pageid] { $self push-page $pageid $modtime return } # Send invalidation to every cache, assuming a connection # exists between the server and the cache # set size [$self get-invsize] # Mark invalidation packet as another fid # set agent [[$self get-cnc $pcache_] agent] # set fid [$agent set fid_] # $agent set fid_ [Http set PINV_FID_] # $self send $pcache_ $size "$pcache_ invalidate $pageid $modtime" # $agent set fid_ $fid $self cmd add-inv $pageid $modtime $self evTrace S INV p $pageid m $modtime } Http/Server/Inval/Yuc instproc push-page { pageid modtime } { $self instvar pcache_ id_ if ![info exists pcache_] { error "Server $id_ doesn't have a parent cache!" } # Do not send invalidation, instead send the new page to # parent cache set size [$self get-size $pageid] set pageinfo [$self get-page $pageid] # Mark invalidation packet as another fid set agent [[$self get-cnc $pcache_] agent] set fid [$agent set fid_] $agent set fid_ [Http set PINV_FID_] $self send $pcache_ $size \ "$pcache_ push-update $pageid $pageinfo" $agent set fid_ $fid $self evTrace S UPD p $pageid m $modtime z $size } Http/Server/Inval/Yuc instproc get-req-notify { pageid } { $self count-request $pageid } Http/Server/Inval/Yuc instproc handle-request-TLC { pageid args } { $self instvar tlc_ array set data $args lappend res $data(size) ;# Same size of queries lappend res $tlc_ return $res } #---------------------------------------------------------------------- # server + support for compound pages. # # A compound page is considered to be a frequently changing main page # and several component pages which are usually big static images. # # XXX This is a naive implementation, which assumes single page and # fixed page size for all pages #---------------------------------------------------------------------- Class Http/Server/Compound -superclass Http/Server # Invalidation server for compound pages Class Http/Server/Inval/MYuc -superclass \ { Http/Server/Inval/Yuc Http/Server/Compound} # 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 web cache # # $Header$ Http/Cache instproc init args { eval $self next $args $self instvar node_ stat_ $node_ color "yellow" ;# no page array set stat_ [list hit-num 0 barrival 0 ims-num 0] } Http instproc set-cachesize { size } { $self instvar pool_ $pool_ set max_size_ $size } Http instproc get-cachesize {} { $self instvar pool_ return [$pool_ set max_size_] } # It's the user's responsibility to connect clients to caches, and caches to # servers. Note that a cache may connect to many other caches and servers, # but it has only one parent cache Http/Cache instproc connect { server } { $self next $server } Http/Cache instproc disconnect { http } { $self instvar slist_ clist_ if [$http info class Http/Cache] { error "Cannot disconnect a cache from another cache" } if {[lsearch $slist_ $http] >= 0} { $self disconnect-server $http } else { $self disconnect-client $http } } # XXX Should add pending_ handling into disconnect Http/Cache instproc disconnect-server { server } { $self instvar ns_ slist_ node_ set pos [lsearch $slist_ $server] if {$pos >= 0} { lreplace $slist_ $pos $pos } else { error "Http::disconnect: not connected to $server" } set tcp [[$self get-cnc $server] agent] $self cmd disconnect $server $server disconnect $self $tcp proc done {} "$ns_ detach-agent $node_ $tcp; delete $tcp" $tcp close #puts "cache [$self id] disconnect from server [$server id]" # Clear all states related to the server. # XXX Assume the server isn't a cache! $self instvar pending_ foreach p [array names pending_] { if {$server == [lindex [split $p :] 0]} { unset pending_($p) } } } # XXX Should clean up client request states Http/Cache instproc disconnect-client { client } { $self instvar ns_ clist_ node_ set pos [lsearch $clist_ $client] if {$pos >= 0} { lreplace $clist_ $pos $pos } else { error "Http/Cache::disconnect: not connected to $server" } set tcp [[$self get-cnc $client] agent] $self cmd disconnect $client $tcp proc done {} "$ns_ detach-agent $node_ $tcp; delete $tcp" $tcp close #puts "cache [$self id] disconnect from client [$client id]" # Clear all pending requests associated with the client $self instvar creq_ foreach p [array names creq_] { set res {} for {set i 0} {$i < [llength $creq_($p)]} {incr i} { set clt [lindex $creq_($p) $i] if {$client != [lindex [split clt /] 0]} { lappend res $clt } } if {[llength $res] == 0} { unset creq_($p) } else { set creq_($p) $res } } } # Use this function to construct a cache hierarchy Http/Cache instproc set-parent { server } { $self instvar parent_ set parent_ $server } # Copied from Http/Server # Let the client side to do the actual connection ($ns connect) Http/Cache instproc alloc-connection { client fid } { Http instvar TRANSPORT_ $self instvar ns_ clist_ node_ id_ fid_ lappend clist_ $client set snk [new Agent/TCP/$TRANSPORT_] $snk set fid_ $fid $ns_ attach-agent $node_ $snk $snk listen set wrapper [new Application/TcpApp $snk] $self cmd connect $client $wrapper #puts "Cache $id_ connected to client [$client id]" return $wrapper } # Parameters different from Http/Client::send-request. This one needs # size of the request because it may need to forward a client's request to # a server. Http/Cache instproc send-request { server type pageid size args } { $self instvar ns_ pending_ ;# pending requests, includes those ;# from itself # Don't bother sending a request to a not-connected server if ![$self is-connected $server] { return } set pending_($pageid) [$ns_ now] $self send $server $size \ "$server get-request $self $type $pageid size $size [join $args]" } # By constructing page id as tuple (server name, page id) we build in # support for multiple web servers Http/Cache instproc get-request { cl type pageid args } { $self instvar slist_ clist_ ns_ id_ pending_ stat_ incr stat_(hit-num) array set data $args if ![info exists data(size)] { error "Http/Cache $id_: client [$cl id] must include request size in its request" } if [$self exist-page $pageid] { $self cache-hit $cl $type $pageid } else { $self cache-miss $cl $type $pageid } } # Cache miss, get it from the server Http/Cache instproc cache-miss { cl type pageid } { $self instvar parent_ pending_ \ creq_ ;# pending client requests # Another client requests for the page lappend creq_($pageid) $cl/$type # XXX If there's a previous requests going on we won't send another # request for the same page. if [info exists pending_($pageid)] { return } # Page not found, contact parent and get the page. If parent_ == 0, # which means this is a root cache, directly contact the server set server [lindex [split $pageid :] 0] if [info exists parent_] { set server $parent_ } set size [$self get-reqsize] $self evTrace E MISS p $pageid c [$cl id] s [$server id] z $size $self send-request $server $type $pageid $size } # Check if page $pageid is consistent. If not, refetch the page from server. Http/Cache instproc is-consistent { cl type pageid } { return 1 } Http/Cache instproc refetch-pending { cl type pageid } { return 0 } Http/Cache instproc refetch args { # Do nothing } Http/Cache instproc cache-hit { cl type pageid } { # page found in cache, return it to client if ![$self is-consistent $cl $type $pageid] { # Page expired and is being refetched, waiting... if ![$self refetch-pending $cl $type $pageid] { $self refetch $cl $type $pageid } return } set server [lindex [split $pageid :] 0] $self evTrace E HIT p $pageid c [$cl id] s [$server id] # XXX don't send any response here. Classify responses according # to request type. eval $self answer-request-$type $cl $pageid [$self get-page $pageid] } # A response may come from: # (1) a missed client request, Http/Cache instproc get-response-GET { server pageid args } { array set data $args if ![info exists data(noc)] { # Cacheable page, continue... if ![$self exist-page $pageid] { # Cache the page if it's not in the pool eval $self enter-page $pageid $args $self evTrace E ENT p $pageid m $data(modtime) \ z $data(size) s [$server id] } else { $self instvar id_ ns_ # A pushed page may come before a response! puts stderr "At [$ns_ now], cache $id_ has requested a page which it already has." } } # If non-cacheable page, don't cache the page. However, still need to # answer all pending requests eval $self answer-pending-requests $pageid $args $self instvar stat_ incr stat_(barrival) $data(size) $self instvar node_ $node_ color "blue" ;# valid page } Http/Cache instproc answer-pending-requests { pageid args } { $self instvar creq_ pending_ array set data $args if [info exists creq_($pageid)] { # Forward the new page to every client that has requested it foreach clt $creq_($pageid) { set tmp [split $clt /] set cl [lindex $tmp 0] set type [lindex $tmp 1] eval $self answer-request-$type $cl $pageid $args } unset creq_($pageid) unset pending_($pageid) } else { unset pending_($pageid) } } Http/Cache instproc answer-request-GET { cl pageid args } { # In response to a GET, we should always return # our copy of the page. array set data $args $self send $cl $data(size) \ "$cl get-response-GET $self $pageid $args" $self evTrace E SND c [$cl id] p $pageid z $data(size) } #---------------------------------------------------------------------- # Cache with consistency protocol based on TTL #---------------------------------------------------------------------- Class Http/Cache/TTL -superclass Http/Cache Http/Cache/TTL set updateThreshold_ 0.1 Http/Cache/TTL instproc init args { eval $self next $args # Default value $self instvar thresh_ set thresh_ [Http/Cache/TTL set updateThreshold_] } Http/Cache/TTL instproc set-thresh { th } { $self instvar thresh_ set thresh_ $th } # XXX we should store modtime of IMS requests somewhere. Then we can check # if that modtime matches this cache's newest modtime when it gets an IMS # response back from the server Http/Cache/TTL instproc answer-request-IMS { client pageid args } { if ![$self exist-page $pageid] { error "At [$ns_ now], cache [$self id] gets an IMS of a non-cacheable page." } set mt [$self get-modtime $pageid] if ![$client exist-page $pageid] { error "client [$client id] IMS a page which it doesn't have" } if {$mt < [$client get-modtime $pageid]} { error "client [$client id] IMS a newer page" } if {$mt > [$client get-modtime $pageid]} { # We should send back the new page, even if we got a # "not-modified-since" set pginfo [$self get-page $pageid] set size [$self get-size $pageid] } else { set size [$self get-invsize] set pginfo "size $size modtime $mt time [$self get-cachetime $pageid]" } $self evTrace E SND c [$client id] t IMS z $size $self send $client $size \ "$client get-response-IMS $self $pageid $pginfo" } Http/Cache/TTL instproc get-response-IMS { server pageid args } { $self instvar ns_ # Alex cache # Invalidate when:(CurTime-LastCheckTime) > Thresh*(CurTime-CreateTime) array set data $args if {$data(modtime) > [$self get-modtime $pageid]} { # Newer page, cache it eval $self enter-page $pageid $args $self evTrace E ENT p $pageid m [$self get-modtime $pageid] \ z [$self get-size $pageid] s [$server id] # XXX Set cache entry time to server's entry time so that # we would have the same expiration time $self set-cachetime $pageid $data(time) } else { # Update entry last validation time $self set-cachetime $pageid [$ns_ now] } eval $self answer-pending-requests $pageid [$self get-page $pageid] # Compute total bytes arrived $self instvar stat_ incr stat_(barrival) $data(size) } Http/Cache/TTL instproc is-expired { pageid } { $self instvar thresh_ ns_ set cktime [expr [$ns_ now] - [$self get-cachetime $pageid]] set age [expr ([$ns_ now] - [$self get-modtime $pageid]) * $thresh_] if {$cktime <= $age} { # Not expired return 0 } return 1 } Http/Cache/TTL instproc is-consistent { cl type pageid } { return ![$self is-expired $pageid] } Http/Cache/TTL instproc refetch-pending { cl type pageid } { # Page expired, validate it $self instvar creq_ if [info exists creq_($pageid)] { if [regexp $cl:* $creq_($pageid)] { # This page already requestsed by this client return 1 } # This page is already requested by other clients. Add # the new client to the requester list, do not request it again lappend creq_($pageid) $cl/$type return 1 } # Set up a refetch pending state lappend creq_($pageid) $cl/$type return 0 } Http/Cache/TTL instproc refetch { cl type pageid } { $self instvar parent_ # Send an If-Modified-Since set server [lindex [split $pageid :] 0] set size [$self get-imssize] if [info exists parent_] { set server $parent_ } # Compute how many IMSs have been sent so far $self instvar stat_ incr stat_(ims-num) $self evTrace E IMS p $pageid c [$cl id] s [$server id] z $size \ t [$self get-cachetime $pageid] m [$self get-modtime $pageid] $self send-request $server IMS $pageid $size \ modtime [$self get-modtime $pageid] return 0 } # Old style TTL, using a single fixed threshold Class Http/Cache/TTL/Plain -superclass Http/Cache/TTL Http/Cache/TTL/Plain set updateThreshold_ 100 Http/Cache/TTL/Plain instproc init { args } { eval $self next $args $self instvar thresh_ set thresh_ [[$self info class] set updateThreshold_] } Http/Cache/TTL/Plain instproc is-expired { pageid } { $self instvar ns_ thresh_ set cktime [expr [$ns_ now] - [$self get-cachetime $pageid]] if {$cktime < $thresh_} { return 0 } return 1 } Class Http/Cache/TTL/Omniscient -superclass Http/Cache/TTL # Assume every cache has exact knowledge of when a page will change Http/Cache/TTL/Omniscient instproc is-expired { pageid } { $self instvar ns_ set nmt [expr [$self get-modtime $pageid] + [$self get-age $pageid]] if {[$ns_ now] >= $nmt} { return 1 } return 0 } #---------------------------------------------------------------------- # Http cache with invalidation -- Base Class #---------------------------------------------------------------------- Http/Cache/Inval instproc mark-invalid {} { $self instvar node_ $node_ color "red" } Http/Cache/Inval instproc mark-valid {} { $self instvar node_ $node_ color "blue" } Http/Cache/Inval instproc mark-leave {} { $self instvar node_ $node_ add-mark down "cyan" } Http/Cache/Inval instproc mark-rejoin {} { $self instvar node_ $node_ delete-mark down } Http/Cache/Inval instproc answer-request-REF { cl pageid args } { if ![$self exist-page $pageid] { error "At [$ns_ now], cache [$self id] gets a REF of a non-cacheable page." } # Send my new page back set pginfo [$self get-page $pageid] set size [$self get-size $pageid] $self evTrace E SND c [$cl id] t REF p $pageid z $size $self send $cl $size \ "$cl get-response-REF $self $pageid $pginfo" } Http/Cache/Inval instproc get-response-GET { server pageid args } { # Check sstate set sid [[lindex [split $pageid :] 0] id] set cid [$server id] $self check-sstate $sid $cid eval $self next $server $pageid $args } # Only get the new page cached, do nothing else Http/Cache/Inval instproc get-response-REF { server pageid args } { $self instvar creq_ id_ # Check sstate set sid [[lindex [split $pageid :] 0] id] set cid [$server id] $self check-sstate $sid $cid array set data $args if {[$self get-modtime $pageid] > $data(modtime)} { # XXX We may get an old page because we are doing full TCP # and an update is sent *during* a regular refetch, which is # sent through several smaller packets. #$self instvar ns_ #error "At [$ns_ now], cache $self ($id_) refetched an old page\ #$pageid ($data(modtime), new time [$self get-modtime $pageid])\ #from [$server id]" puts stderr "At [$ns_ now], cache $self ($id_) refetched an old page\ $pageid ($data(modtime), new time [$self get-modtime $pageid])\ from [$server id]" # Do nothing; send back the newer page } else { # The page is re-validated by replacing the old entry eval $self enter-page $pageid $args $self evTrace E UPD p $pageid m [$self get-modtime $pageid] \ z [$self get-size $pageid] s [$server id] } eval $self answer-pending-requests $pageid [$self get-page $pageid] $self instvar node_ marks_ ns_ set mk [lindex $marks_($pageid) 0] $node_ delete-mark $mk set marks_($pageid) [lreplace $marks_($pageid) 0 0] $node_ color "blue" } # Always consistent? Http/Cache/Inval instproc is-consistent { cl type pageid } { return [$self is-valid $pageid] } Http/Cache/Inval instproc refetch-pending { cl type pageid } { # Invalid page, prepare a refetch. $self instvar creq_ if [info exists creq_($pageid)] { if [regexp $cl:* $creq_($pageid)] { # This page already requestsed by this client return 1 } # This page already requested by other clients, add ourselves # to the returning list and return lappend creq_($pageid) $cl/$type return 1 } # Setup a refetch pending state lappend creq_($pageid) $cl/$type return 0 } # Send a refetch. Forward the request to our parent Http/Cache/Inval instproc refetch { cl type pageid } { $self instvar parent_ set size [$self get-refsize] set server [lindex [split $pageid :] 0] if [info exists parent_] { set par $parent_ } else { # We are the root cache (TLC), directly contact the # web server set par $server } $self evTrace E REF p $pageid s [$server id] z $size $self send-request $par REF $pageid $size $self instvar node_ marks_ ns_ lappend marks_($pageid) $pageid:[$ns_ now] $node_ add-mark $pageid:[$ns_ now] "brown" } #---------------------------------------------------------------------- # Invalidation cache with multicast heartbeat invalidation #---------------------------------------------------------------------- Http/Cache/Inval/Mcast instproc init args { eval $self next $args $self add-to-map } # When we enter a new page into cache, we'll have to register the server # in case we haven't know anything about it. The right place to do it # is in get-response-GET, because a cache will only enter a new page # after a cache miss, where it issues a GET. Http/Cache/Inval/Mcast instproc get-response-GET { server pageid args } { eval $self next $server $pageid $args # XXX Assume once server-neighbor cache relationship is fixed, they # never change. # debug 1 set sid [[lindex [split $pageid :] 0] id] set cid [$server id] $self register-server $cid $sid } Http/Cache/Inval/Mcast instproc set-parent { parent } { $self next $parent # Establish a cache entry in state table $self cmd set-parent $parent } # I'm a listener (child) Http/Cache/Inval/Mcast instproc join-inval-group { group } { $self instvar invalListener_ invListenGroup_ ns_ node_ if [info exists invalListener_] { return } set invalListener_ [new Agent/HttpInval] set invListenGroup_ $group $invalListener_ set dst_addr_ $group $invalListener_ set dst_port_ 0 $self add-inval-listener $invalListener_ $ns_ attach-agent $node_ $invalListener_ # XXX assuming simulator already started $node_ join-group $invalListener_ $group } # I'm a sender (parent) Http/Cache/Inval/Mcast instproc init-inval-group { group } { $self instvar invalSender_ invSndGroup_ ns_ node_ if [info exists invalSender_] { return } set invalSender_ [new Agent/HttpInval] set invSndGroup_ $group $invalSender_ set dst_addr_ $group $invalSender_ set dst_port_ 0 $self add-inval-sender $invalSender_ $ns_ attach-agent $node_ $invalSender_ $node_ join-group $invalSender_ $group # XXX We should put this somewhere else... But where??? $self start-hbtimer } # Another "breakdown" version of parent-cache() is in cache-miss() Http/Cache/Inval/Mcast instproc parent-cache { server } { $self instvar parent_ set par [$self cmd parent-cache [$server id]] if {$par == ""} { # (par == "") means parent cache in the virtual distribution # tree is the default, which is parent_ if [info exists parent_] { set par $parent_ } else { # We are the root cache (TLC), directly contact the # web server set par $server } } return $par } # Send a refetch. # # We should ask our parent in the virtual distribution tree # of the corresponding web server, instead of our parent in the # cache hierarchy. Http/Cache/Inval/Mcast instproc refetch { cl type pageid } { set size [$self get-refsize] set server [lindex [split $pageid :] 0] set par [$self parent-cache $server] $self evTrace E REF p $pageid s [$server id] z $size $self send-request $par REF $pageid $size $self instvar node_ marks_ ns_ lappend marks_($pageid) $pageid:[$ns_ now] $node_ add-mark $pageid:[$ns_ now] "brown" } # Cache miss, get it from our parent cache in the virtual distribution # tree of the web server Http/Cache/Inval/Mcast instproc cache-miss { cl type pageid } { $self instvar parent_ pending_ creq_ ;# pending client requests lappend creq_($pageid) $cl/$type # XXX If there's a previous requests going on we won't send another # request for the same page. if [info exists pending_($pageid)] { return } # Page not found, contact parent and get the page. set size [$self get-reqsize] set server [lindex [split $pageid :] 0] $self evTrace E MISS p $pageid c [$cl id] s [$server id] z $size # We directly query the server map without using TCL's version # of parent-cache() to mask details... set par [$self cmd parent-cache [$server id]] if {$par == ""} { if [info exists parent_] { # Use default server map, i.e., parent cache set par $parent_ } else { # This is a TLC, and the request is for another server # in another hierarchy (because we don't have it in our # server map, nor do we have a parent cache). Now we # need to find out what's the corresponding TLC of # the web server so as to setup invalidation path. # # Send a direct request to server to ask about TLC $self instvar ns_ id_ #puts "[$ns_ now]: $id_ send TLC" $self send-request $server TLC $pageid $size # We'll send another request to the TLC after we get # its addr return } } $self send-request $par $type $pageid $size } # This allows a server passes invalidation to any cache via unicast # XXX Whenever a node only wants to do an invalidation, call "cmd recv-inv" Http/Cache/Inval/Mcast instproc invalidate { pageid modtime } { if [$self recv-inv $pageid $modtime] { # Unicast invalidation to parent. $self instvar parent_ if ![info exists parent_] { # This must be a root cache, should we do anything? return } set size [$self get-invsize] $self evTrace E SND t INV c [$parent_ id] p $pageid z $size # Mark invalidation packet as another fid set agent [[$self get-cnc $parent_] agent] set fid [$agent set fid_] $agent set fid_ [Http set PINV_FID_] $self send $parent_ $size \ "$parent_ invalidate $pageid $modtime" $agent set fid_ $fid } } Http/Cache/Inval/Mcast instproc get-request { cl type pageid args } { eval $self next $cl $type $pageid $args if {(($type == "GET") || ($type == "REF")) && \ [$self exist-page $pageid]} { $self count-request $pageid if [$self is-unread $pageid] { $self send-req-notify $pageid $self set-read $pageid } } } # Do the same thing as if getting a request Http/Cache/Inval/Mcast instproc get-req-notify { pageid } { $self count-request $pageid if [$self is-unread $pageid] { # Continue to forward the request only if our page is # also unread $self set-read $pageid $self send-req-notify $pageid } } # Request notification goes along a single path in the virtual distribution # tree towards the web server. It's not multicast to anybody else Http/Cache/Inval/Mcast instproc send-req-notify { pageid } { set server [lindex [split $pageid :] 0] set par [$self parent-cache $server] $self send $par [$self get-ntfsize] "$par get-req-notify $pageid" } # (1) setup an invalidation record is set to invalidate my children; # (2) Unicast the new page to my parent; # (3) Update my own page records # (4) Setting up a repair group to send out the new page (once and for all) Http/Cache/Inval/Mcast instproc push-update { pageid args } { # Update page, possibly push the page to children if [eval $self recv-push $pageid $args] { # XXX We should probably check if we have pending request for # this page. If so, we should use this pushed page to answer # those pending requests, and then mark this page as read. # unicast push to parent $self instvar parent_ if [info exists parent_] { # If we are root, don't forward the data packet to # anybody. Otherwise unicast the new page to my parent set pginfo [$self get-page $pageid] set size [$self get-size $pageid] $self evTrace E UPD c [$parent_ id] p $pageid z $size $self send $parent_ $size \ "$parent_ push-update $pageid $pginfo" } $self push-children $pageid } } Http/Cache/Inval/Mcast instproc init-update-group { group } { $self instvar ns_ node_ updSender_ updSendGroup_ # Allow a cache to have multiple update groups. set snd [new Agent/HttpInval] $snd set dst_addr_ $group $snd set dst_port_ 0 $self add-upd-sender $snd $ns_ attach-agent $node_ $snd $node_ join-group $snd $group } Http/Cache/Inval/Mcast instproc join-update-group { group } { $self instvar updListener_ updListenGroup_ ns_ node_ set updListenGroup_ $group # One cache can only receive from one update group at a time if ![info exists updListener_] { set updListener_ [new Agent/HttpInval] $self add-upd-listener $updListener_ $updListener_ set dst_addr_ $updListenGroup_ $updListener_ set dst_port_ 0 $ns_ attach-agent $node_ $updListener_ } $node_ join-group $updListener_ $updListenGroup_ # $node_ add-mark "Updating" "Orange" } Http/Cache/Inval/Mcast instproc leave-update-group {} { $self instvar updListener_ updListenGroup_ ns_ node_ if ![info exists updListener_] { return } $node_ leave-group $updListener_ $updListenGroup_ $node_ delete-mark "Updating" } # Set up a unicast heartbeat connection Http/Cache/Inval/Mcast instproc setup-unicast-hb {} { Http instvar TRANSPORT_ $self instvar node_ ns_ set snk [new Agent/TCP/$TRANSPORT_] $snk set fid_ [Http set HB_FID_] $ns_ attach-agent $node_ $snk $snk listen set wrapper [new Application/TcpApp/HttpInval $snk] $wrapper set-app $self return $wrapper } # Establish state for server. Propagate until Top-Level Cache is reached # Set up heartbeat connection along the way Http/Cache/Inval/Mcast instproc server-join { server cache } { $self cmd join [$server id] $cache #puts "Server [$server id] joins cache [$self id]" $self instvar parent_ if ![info exists parent_] { return } $self send $parent_ [$self get-joinsize] \ "$parent_ server-join $server $self" # Establishing a tcp connection. Http instvar TRANSPORT_ $self instvar ns_ node_ set tcp [new Agent/TCP/$TRANSPORT_] $tcp set fid_ [Http set HB_FID_] $ns_ attach-agent $node_ $tcp set dst [$parent_ setup-unicast-hb] set snk [$dst agent] $ns_ connect $tcp $snk #$tcp set dst_ [$snk set addr_] $tcp set window_ 100 set wrapper [new Application/TcpApp/HttpInval $tcp] $wrapper connect $dst $wrapper set-app $self $self set-pinv-agent $wrapper # If we haven't started it yet, start it. $self start-hbtimer } Http/Cache/Inval/Mcast instproc request-mpush { page } { $self instvar mpush_refresh_ ns_ hb_interval_ if [info exists mpush_refresh_($page)] { # The page is already set as mandatory push, ignore it return } $self set-mandatory-push $page set server [lindex [split $page :] 0] set cache [$self parent-cache $server] set mpush_refresh_($page) [$ns_ at [expr [$ns_ now] + $hb_interval_] \ "$self send-refresh-mpush $cache $page"] # Forward the push request towards the web server $self send $cache [$self get-mpusize] "$cache request-mpush $page" } Http/Cache/Inval/Mcast instproc refresh-mpush { page } { $self cmd set-mandatory-push $page } Http/Cache/Inval/Mcast instproc send-refresh-mpush { cache page } { $self instvar mpush_refresh_ ns_ hb_interval_ $self send $cache [$self get-mpusize] "$cache refresh-mpush $page" set mpush_refresh_($page) [$ns_ at [expr [$ns_ now] + $hb_interval_] \ "$self send-refresh-mpush $cache $page"] } # XXX This is used when a mpush is timed out, where we don't need to # send explicit teardown, etc. Http/Cache/Inval/Mcast instproc cancel-mpush-refresh { page } { $self instvar mpush_refresh_ ns_ if [info exists mpush_refresh_($page)] { $ns_ cancel $mpush_refresh_($page) #puts "[$ns_ now]: Cache [$self id] stops mpush" } else { error "Cache [$self id]: No mpush to stop!" } } Http/Cache/Inval/Mcast instproc stop-mpush { page } { # Cancel refresh messages $self cancel-mpush-refresh $page # Clear page push status $self cmd stop-mpush $page # Send explicit message to stop mpush set server [lindex [split $page :] 0] set cache [$self parent-cache $server] $self send $cache [$self get-mpusize] "$cache stop-mpush $page" } # # Support for multiple hierarchies # # Top-Level Caches (TLCs) need to exchange invalidations with each other, # so they are both sender and receiver in this multicast group. Http/Cache/Inval/Mcast instproc join-tlc-group { group } { $self instvar tlcAgent_ tlcGroup_ ns_ node_ if [info exists tlcAgent_] { return } set tlcAgent_ [new Agent/HttpInval] set tlcGroup_ $group $tlcAgent_ set dst_addr_ $group $tlcAgent_ set dst_port_ 0 $self add-inval-sender $tlcAgent_ $self add-inval-listener $tlcAgent_ $ns_ attach-agent $node_ $tlcAgent_ $node_ join-group $tlcAgent_ $group } Http/Cache/Inval/Mcast instproc get-response-TLC { server pageid tlc } { # Continue query... # debug 1 $self register-server [$tlc id] [$server id] $self instvar ns_ id_ # puts "[$ns_ now]: Cache $id_ knows server [$server id] -> tlc [$tlc id]" $self send-request $tlc GET $pageid [$self get-reqsize] } #---------------------------------------------------------------------- # Http/Cache/Inval/Mcast/Perc # # Multicast invalidation + two way liveness message + invalidation # filtering. Must be used with Http/Server/Inval/Ucast/Perc # # Requires C++ support. This is why we have this long name. :( # # Procedures: # - Server's new page: the server injects it into the cache hierarchy by # sending it to its parent cache, which in turn forwards it up the tree. # - Every cache keeps a cost for each cached page. #---------------------------------------------------------------------- # XXX Do not check-sstate{} when getting a response. Because we are doing # direct request, those responses will always come from the server Http/Cache/Inval/Mcast/Perc instproc check-sstate {sid cid} { $self instvar direct_request_ if !$direct_request_ { # If not using direct request, check sstate $self cmd check-sstate $sid $cid } } # Because we are doing direct request, we'll get a lot of responses # directly from the server, and we'll have cid == sid. We don't want to # register this into our server map, because the server map is used # for forwarding pro formas. Therefore, we wrap up register-server to # direct requests to all our *UNKNOWN* servers to our parent. # # Note this won't disrupt server entries via JOIN, because they are # established before any request is sent. Http/Cache/Inval/Mcast/Perc instproc register-server {cid sid} { $self instvar parent_ direct_request_ # debug 1 if {$direct_request_ && [info exists parent_]} { $self cmd register-server [$parent_ id] $sid } } # Allows direct request Http/Cache/Inval/Mcast/Perc instproc cache-miss { cl type pageid } { $self instvar direct_request_ if !$direct_request_ { # If not use direct request, fall back to previous method $self next $cl $type $pageid return } # If use direct request, send a request to the web server to ask # for the page, and then send a pro forma when get the request $self instvar parent_ pending_ creq_ ;# pending client requests $self instvar dreq_ ;# pending direct requests lappend creq_($pageid) $cl/$type # XXX If there's a previous requests going on we won't send another # request for the same page. if [info exists pending_($pageid)] { return } $self instvar dreq_ set dreq_($pageid) 1 # Page not found, directly contact the server and get the page. set server [lindex [split $pageid :] 0] set size [$self get-reqsize] $self evTrace E MISS p $pageid c [$cl id] s [$server id] z $size $self send-request $server $type $pageid $size } # Allows direct request Http/Cache/Inval/Mcast/Perc instproc refetch { cl type pageid } { $self instvar direct_request_ if !$direct_request_ { $self next $cl $type $pageid return } $self instvar dreq_ set dreq_($pageid) 1 set size [$self get-refsize] set server [lindex [split $pageid :] 0] $self evTrace E REF p $pageid s [$server id] z $size $self send-request $server REF $pageid $size $self instvar node_ marks_ ns_ lappend marks_($pageid) $pageid:[$ns_ now] $node_ add-mark $pageid:[$ns_ now] "brown" } # Whenever get a request, send a pro forma up Http/Cache/Inval/Mcast/Perc instproc get-response-GET { server pageid args } { # First, answer children's requests, etc. eval $self next $server $pageid $args # Then send a pro forma if it's a direct request $self instvar dreq_ if [info exists dreq_($pageid)] { # If this page is result of a direct request, send a pro forma eval $self send-proforma $pageid $args unset dreq_($pageid) } } # Same treatment as get-response-GET Http/Cache/Inval/Mcast/Perc instproc get-response-REF { server pageid args } { eval $self next $server $pageid $args $self instvar dreq_ if [info exists dreq_($pageid)] { eval $self send-proforma $pageid $args unset dreq_($pageid) } } # XXX We need special handling for multiple hierarchies. If we cannot find # the server in our server map, we directly call the server's routine to # find out its TLC. This doesn't make the simulation artificial, though, # because in our previous direct response from the server, we could have # easily gotten its TLC. Http/Cache/Inval/Mcast/Perc instproc send-proforma { pageid args } { set server [lindex [split $pageid :] 0] set par [$self parent-cache $server] if {$par == $server} { # If we are the primary cache, don't send anything return } elseif {$par == ""} { # XXX # We are the TLC, and we don't have a server entry. This # means that the server resides in another hierarchy. # Query the global server-to-TLC map to unicast this # pro forma to that TLC... set par [$server get-tlc] #puts "TLC [$self id] learned about server [$server id] by pro forma" } $self send $par [$self get-pfsize] \ "$par recv-proforma $self $pageid [join $args]" $self evTrace E SPF p $pageid c [$par id] } Http/Cache/Inval/Mcast/Perc instproc get-response-IMS { server pageid args } { $self instvar ns_ array set data $args if {$data(modtime) <= [$self get-modtime $pageid]} { # The page we got from the pro forma is indeed most up-to-date return } # The server has changed the page since the pro forma is sent # We need to send invalidations to invalidate the page $self invalidate $pageid eval $self enter-page $pageid $args $self mark-valid } Http/Cache/Inval/Mcast/Perc instproc mark-valid-hdr {} { $self instvar node_ $node_ color "orange" } Http/Cache/Inval/Mcast/Perc instproc recv-proforma { cache pageid args } { $self instvar stat_ # count pro forma as one TLC hit incr stat_(hit-num) $self evTrace E RPF p $pageid c [$cache id] array set data $args if ![$self exist-page $pageid] { # Page doesn't exists. Create an entry for page header, and # forward it towards the web server eval $self enter-metadata $pageid $args $self mark-valid-hdr set server [lindex [split $pageid :] 0] set par [$self parent-cache $server] if {$par == $server} { # If we are the primary cache, validate this # pro forma by sending an IMS $self send-request $par IMS $pageid \ [$self get-imssize] modtime $data(modtime) } else { eval $self send-proforma $pageid $args } } elseif [$self is-valid $pageid] { # Valid page, check if this is a newer one set mt [$self get-modtime $pageid] if {$data(modtime) < $mt} { # If the pro forma is older, should invalidate our # children so that they'll invalidate their stuff $self recv-inv $pageid $data(modtime) return } elseif {$data(modtime) > $mt} { # If the pro forma is about a newer page, # first invalidate our page, so that we have an # invalidation record to let our children know the # page is invalid. Then enter the page metadata. # # XXX Should check for existence of page content $self recv-inv $pageid $data(modtime) eval $self enter-metadata $pageid $args $self mark-valid-hdr eval $self send-proforma $pageid $args } # Drop the pro forma if it's the same as our page. # XXX count the pro forma as a request to this page, and # send a request notification towards the web server. # Mark the page as read if it's originally unread. $self count-request $pageid if [$self is-unread $pageid] { $self set-read $pageid } } else { # Invalid page, check if we should set a valid page header # so that invalidations will be forwarded. array set data $args set mt [$self get-modtime $pageid] if {$data(modtime) < $mt} { # We already have the most up-to-date page, so are # our parents. Do nothing return } # The pro forma is newer, put in the new meta-data and # set the page as valid_header but not valid_page # Note if a page is invalid, its modtime is that of the # newest page. # # XXX Should test for the existence of page content by # looking at the size of the pro forma. eval $self enter-metadata $pageid $args $self mark-valid-hdr eval $self send-proforma $pageid $args } } # 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. # # HTTP agents: server, client, cache # # $Header$ Http set id_ 0 ;# required by TclCL # Type of Tcp agent. Can be SimpleTcp or FullTcp # Default should be set to FullTcp, in case for inadvertent bites. :( Http set TRANSPORT_ FullTcp Http set HB_FID_ 40 Http set PINV_FID_ 41 # XXX invalidation message size should be proportional to the number of # invalidations inside the message Http set INVSize_ 43 ;# unicast invalidation Http set REQSize_ 43 ;# Request Http set REFSize_ 50 ;# Refetch request Http set IMSSize_ 50 ;# If-Modified-Since Http set JOINSize_ 10 ;# Server join/leave Http set HBSize_ 1 ;# Used by Http/Server/Inval only Http set PFSize_ 1 ;# Pro forma Http set NTFSize_ 10 ;# Request Notification Http set MPUSize_ 10 ;# Mandatory push request Http/Server set id_ 0 Http/Server/Inval set id_ 0 Http/Server/Inval/Yuc set hb_interval_ 60 Http/Server/Inval/Yuc set enable_upd_ 0 Http/Server/Inval/Yuc set Ca_ 1 Http/Server/Inval/Yuc set Cb_ 4 Http/Server/Inval/Yuc set push_thresh_ 4 Http/Server/Inval/Yuc set push_low_bound_ 0 Http/Server/Inval/Yuc set push_high_bound_ 8 Http/Cache set id_ 0 Http/Cache/Inval set id_ 0 Http/Cache/Inval/Mcast set hb_interval_ 60 Http/Cache/Inval/Mcast set upd_interval_ 5 Http/Cache/Inval/Mcast set enable_upd_ 0 Http/Cache/Inval/Mcast set Ca_ 1 Http/Cache/Inval/Mcast set Cb_ 4 Http/Cache/Inval/Mcast set push_thresh_ 4 Http/Cache/Inval/Mcast set push_low_bound_ 0 Http/Cache/Inval/Mcast set push_high_bound_ 8 Http/Cache/Inval/Mcast/Perc set direct_request_ 0 PagePool/CompMath set num_pages_ 1 PagePool/CompMath set main_size_ 1024 PagePool/CompMath set comp_size_ 10240 # Transport protocol used for multimedia connections Http set MEDIA_TRANSPORT_ RAP # Application-level handler for multimedia connections. # Currently there are two available: # - MediaApp: simply extract data from packets and pass it to cache # - QA: do quality adaptation Http set MEDIA_APP_ MediaApp # 1K per multimedia segment Application/MediaApp set segmentSize_ 1024 Application/MediaApp set MAX_LAYER_ 10 # Constants related to quality adaptation Application/MediaApp/QA set LAYERBW_ 2500 ;# Byte per-second Application/MediaApp/QA set MAXACTIVELAYERS_ 10 Application/MediaApp/QA set SRTTWEIGHT_ 0.95 Application/MediaApp/QA set SMOOTHFACTOR_ 4 Application/MediaApp/QA set MAXBKOFF_ 100 Application/MediaApp/QA set debug_output_ 0 # Prefetching lookahead SRTT 200ms Application/MediaApp/QA set pref_srtt_ 0.6 # 100M buffer size at cache/server/client PagePool/Client/Media set max_size_ 104857600 Http instproc init { ns node } { $self next $self instvar ns_ node_ id_ pool_ set ns_ $ns set node_ $node $self set id_ [$node_ id] set pool_ [$self create-pagepool] } Http instproc create-pagepool {} { set pool [new PagePool/Client] $self set-pagepool $pool return $pool } Http instproc addr {} { $self instvar node_ return [$node_ node-addr] } Http set fid_ -1 Http instproc getfid {} { $self instvar fid_ set fid_ [Http set fid_] Http set fid_ [incr fid_] } Http instproc get-mpusize {} { return [Http set MPUSize_] } Http instproc get-ntfsize {} { return [Http set NTFSize_] } Http instproc get-pfsize {} { return [Http set PFSize_] } Http instproc get-hbsize {} { return [Http set HBSize_] } Http instproc get-imssize {} { return [Http set IMSSize_] } Http instproc get-invsize {} { return [Http set INVSize_] } # Generate request packet size. Should be constant because it's small Http instproc get-reqsize {} { return [Http set REQSize_] } Http instproc get-refsize {} { return [Http set REFSize_] } Http instproc get-joinsize {} { return [Http set JOINSize_] } # At startup, connect to a server, the server may be a cache Http instproc connect { server } { Http instvar TRANSPORT_ $self instvar ns_ slist_ node_ fid_ id_ lappend slist_ $server set tcp [new Agent/TCP/$TRANSPORT_] $tcp set fid_ [$self getfid] $ns_ attach-agent $node_ $tcp set ret [$server alloc-connection $self $fid_] set snk [$ret agent] $ns_ connect $tcp $snk #$tcp set dst_ [$snk set addr_] $tcp set window_ 100 # Use a wrapper to implement application data transfer set wrapper [new Application/TcpApp $tcp] $self cmd connect $server $wrapper $wrapper connect $ret #puts "HttpApp $id_ connected to server [$server id]" } Http instproc stat { name } { $self instvar stat_ return $stat_($name) } # Used for mandatory push refreshments Http/Client set hb_interval_ 60 Http/Client instproc init args { eval $self next $args $self instvar node_ stat_ $node_ color "SteelBlue" array set stat_ [list req-num 0 stale-num 0 stale-time 0 rep-time 0 \ rt-min 987654321 rt-max 0 st-min 987654321 st-max 0] } # XXX Assume that it's always client disconnects from server, not vice versa Http/Client instproc disconnect { server } { $self instvar ns_ slist_ set pos [lsearch $slist_ $server] if {$pos >= 0} { lreplace $slist_ $pos $pos } else { error "Http::disconnect: not connected to $server" } # Cleanup of all pending requests and states $self instvar ns_ node_ cache_ $self stop-session $server # XXX Is this the right behavior? Should we wait for FIN etc.? set tcp [[$self get-cnc $server] agent] $self cmd disconnect $server $server disconnect $self $tcp proc done {} "$ns_ detach-agent $node_ $tcp; delete $tcp" $tcp close } # Meta-data to be sent in a request # XXX pageid should always be given from the users, because client may # connect to a cache, hence it doesn't know the server name. Http/Client instproc send-request { server type pageid args } { $self instvar ns_ pending_ ;# unansewered requests # XXX Do not set pending states for an non-existent connection if ![$self cmd is-connected $server] { return } if ![info exists pending_($pageid)] { # XXX Actually we should use set, because only one request # is allowed for a page simultaneously lappend pending_($pageid) [$ns_ now] } else { # If the page is being requested, do not send another request return } set size [$self get-reqsize] $self send $server $size \ "$server get-request $self $type $pageid size $size [join $args]" $self evTrace C GET p $pageid s [$server id] z $size $self instvar stat_ simStartTime_ if [info exists simStartTime_] { incr stat_(req-num) } $self mark-request $pageid } Http/Client instproc mark-request { pageid } { # Nam state coloring $self instvar node_ marks_ ns_ $node_ add-mark $pageid:[$ns_ now] "purple" lappend marks_($pageid) $pageid:[$ns_ now] } # The reason that "type" is here is for Http/Cache to work. Client doesn't # check the reason of the response Http/Client instproc get-response-GET { server pageid args } { $self instvar pending_ id_ ns_ stat_ simStartTime_ if ![info exists pending_($pageid)] { error "Client $id_: Unrequested response page $pageid from server [$server id]" } array set data $args # Check stale hits set origsvr [lindex [split $pageid :] 0] set modtime [$origsvr get-modtime $pageid] set reqtime [lindex $pending_($pageid) 0] set reqrtt [expr [$ns_ now] - $reqtime] # # XXX If a stale hit occurs because a page is modified during the RTT # of the request, we should *NOT* consider it a stale hit. We # implement it by ignoring all stale hits whose modification time is # larger than the request time. # if {$modtime > $data(modtime)} { # Staleness is the time from now to the time it's last modified set tmp [$origsvr stale-time $pageid $data(modtime)] if {$tmp > $reqrtt/2} { # We have a real stale hit $self evTrace C STA p $pageid s [$origsvr id] l $tmp if [info exists simStartTime_] { incr stat_(stale-num) set stat_(stale-time) [expr \ $stat_(stale-time) + $tmp] if {$stat_(st-min) > $tmp} { set stat_(st-min) $tmp } if {$stat_(st-max) < $tmp} { set stat_(st-max) $tmp } } } } # Assume this response is for the very first request we've sent. # Because we'll average the response time at the end, which # request this response actually corresponds to doesn't matter. $self evTrace C RCV p $pageid s [$server id] l $reqrtt z $data(size) if [info exists simStartTime_] { set stat_(rep-time) [expr $stat_(rep-time) + $reqrtt] if {$stat_(rt-min) > $reqrtt} { set stat_(rt-min) $reqrtt } if {$stat_(rt-max) < $reqrtt} { set stat_(rt-max) $reqrtt } } set pending_($pageid) [lreplace $pending_($pageid) 0 0] if {[llength $pending_($pageid)] == 0} { unset pending_($pageid) } $self mark-response $pageid } Http/Client instproc mark-response { pageid } { $self instvar node_ marks_ ns_ set mk [lindex $marks_($pageid) 0] $node_ delete-mark $mk set marks_($pageid) [lreplace $marks_($pageid) 0 0] } Http/Client instproc get-response-REF { server pageid args } { eval $self get-response-GET $server $pageid $args } Http/Client instproc get-response-IMS { server pageid args } { eval $self get-response-GET $server $pageid $args } # Generate the time when next request will occur # It's either a TracePagePool or a MathPagePool # # XXX both TracePagePool and MathPagePool should share the same C++ # interface and OTcl interface Http/Client instproc set-page-generator { pagepool } { $self instvar pgtr_ ;# Page generator set pgtr_ $pagepool } Http/Client instproc set-interval-generator { ranvar } { $self instvar rvInterPage_ set rvInterPage_ $ranvar } # XXX PagePool::gen-pageid{} *MUST* precede gen-req-itvl{}, because the # former may responsible for loading a new page from the trace file if # PagePool/ProxyTrace is used Http/Client instproc gen-request {} { $self instvar pgtr_ rvInterPage_ id_ if ![info exists pgtr_] { error "Http/Client requires a page generator (pgtr_)!" } # XXX # rvInterPage_ should only be set when PagePool/ProxyTrace is # *NOT* used if [info exists rvInterPage_] { return [list [$rvInterPage_ value] [$pgtr_ gen-pageid $id_]] } else { return [$pgtr_ gen-request $id_] } } Http/Client instproc next-request { server pageid } { $self instvar ns_ cache_ nextreq_ if [info exists cache_] { $self send-request $cache_ GET $pageid } else { $self send-request $server GET $pageid } # Prepare for the next request set req [$self gen-request] set pageid $server:[lindex $req 1] set itvl [lindex $req 0] if {$itvl >= 0} { set nextreq_([$server id]) [$ns_ at [expr [$ns_ now] + $itvl] \ "$self next-request $server $pageid"] } ;# otherwise it's the end of the request stream } Http/Client instproc set-cache { cache } { $self instvar cache_ set cache_ $cache } # Assuming everything is setup, this function starts sending requests # Sending a request to $cache, the original page should come from $server # # Populate a cache with all available pages # XXX how would we distribute pages spatially when we have a hierarchy # of caches? Or, how would we distribute client requests spatially? # It should be used in single client, single cache and single server case # *ONLY*. Http/Client instproc start-session { cache server } { $self instvar ns_ cache_ simStartTime_ $self instvar simStartTime_ pgtr_ set simStartTime_ [$ns_ now] if [info exists pgtr_] { if {[$pgtr_ get-start-time] > $simStartTime_} { $pgtr_ set-start-time $simStartTime_ } } #puts "Client [$self id] starts request session at [$ns_ now]" set cache_ $cache # The first time invocation we don't send out a request # immediately. Instead, we find out when will the first # request happen, then schedule it using next-request{} # Then every time next-request{} is called later, it # sends out a request and schedule the next. set req [$self gen-request] set pageid $server:[lindex $req 1] set itvl [lindex $req 0] if {$itvl >= 0} { $ns_ at [expr [$ns_ now] + $itvl] \ "$self next-request $server $pageid" } ;# otherwise it's the end of the request stream } # Stop sending further requests, and clean all pending requests Http/Client instproc stop-session { server } { $self instvar ns_ nextreq_ pending_ cache_ set sid [$server id] # Clean up all pending requests if [info exists nextreq_($sid)] { $ns_ cancel $nextreq_($sid) } if {![info exists pending_]} { return } # XXX If a client either connects to a *SINGLE* cache # or a *SINGLE* server, then we remove all pending requests # when we are disconnected. if {[info exists cache_] && ($server == $cache_)} { unset pending_ } else { # Only remove pending requests related to $server foreach p [array names pending_] { if {$server == [lindex [split $p :] 0]} { unset pending_($p) } } } } Http/Client instproc populate { cache server } { $self instvar pgtr_ curpage_ status_ ns_ if ![info exists status_] { set status_ "POPULATE" set curpage_ 0 } if [info exists pgtr_] { # Populate cache with all pages incrementally if {$curpage_ < [$pgtr_ get-poolsize]} { $self send-request $cache GET $server:$curpage_ incr curpage_ $ns_ at [expr [$ns_ now] + 1] \ "$self populate $cache $server" # if {$curpage_ % 1000 == 0} { # puts "Client [$self id] populated $curpage_" # } return } } # Start sending requests $ns_ at [expr [$ns_ now] + 10] "$self start-session $cache $server" } Http/Client instproc start { cache server } { $self instvar cache_ set cache_ $cache $self populate $cache $server } Http/Client instproc request-mpush { page } { $self instvar mpush_refresh_ ns_ cache_ $self send $cache_ [$self get-mpusize] \ "$cache_ request-mpush $page" Http/Client instvar hb_interval_ set mpush_refresh_($page) [$ns_ at [expr [$ns_ now] + $hb_interval_] \ "$self send-refresh-mpush $page"] } Http/Client instproc send-refresh-mpush { page } { $self instvar mpush_refresh_ ns_ cache_ $self send $cache_ [$self get-mpusize] "$cache_ refresh-mpush $page" #puts "[$ns_ now]: Client [$self id] send mpush refresh" Http/Client instvar hb_interval_ set mpush_refresh_($page) [$ns_ at [expr [$ns_ now] + $hb_interval_] \ "$self send-refresh-mpush $page"] } # XXX We use explicit teardown. Http/Client instproc stop-mpush { page } { $self instvar mpush_refresh_ ns_ cache_ if [info exists mpush_refresh_($page)] { # Stop sending the periodic refreshment $ns_ cancel $mpush_refresh_($page) #puts "[$ns_ now]: Client [$self id] stops mpush" } else { error "no mpush to cancel!" } # Send explicit message up to tear down $self send $cache_ [$self get-mpusize] "$cache_ stop-mpush $page" } #---------------------------------------------------------------------- # Client which is capable of handling compound pages #---------------------------------------------------------------------- Class Http/Client/Compound -superclass Http/Client Http/Client/Compound instproc set-interobj-generator { ranvar } { $self instvar rvInterObj_ set rvInterObj_ $ranvar } # Generate next page request and the number of embedded objects for # this page. # Use rvInterPage_ to generate interval between pages (inactive OFF), and # use rvInterObj_ to generate interval between embedded objects (active OFF) Http/Client/Compound instproc next-request { server pageid } { # First schedule next page request eval $self next $server $pageid } # Request the next embedded object(s) Http/Client/Compound instproc next-obj { server args } { $self instvar pgtr_ cache_ req_objs_ ns_ rvInterObj_ if ![llength $args] { # No requests to make return } if [info exists cache_] { set dest $cache_ } else { set dest $server } set pageid [lindex $args 0] set mpgid [$pgtr_ get-mainpage $pageid] ;# main page id set max 0 set origsvr [lindex [split $pageid :] 0] foreach pageid $args { set id [lindex [split $pageid :] 1] if {$max < $id} { set max $id } incr req_objs_($mpgid) -1 $self send-request $dest GET $pageid } if {$req_objs_($mpgid) <= 0} { # We have requested all objects for this page, done. return } # Schedule next requests. Assuming we get embedded objects according # to the ascending order of their page id set objid [join [$pgtr_ get-next-objs $origsvr:$max]] puts "At [$ns_ now], client [$self id] get objs $objid" if [info exists rvInterObj_] { $ns_ at [expr [$ns_ now] + [$rvInterObj_ value]] \ "$self next-obj $server $objid" } else { $self next-obj $server $objid } } # XXX We need to override get-response-GET because we need to recompute # the RCV time, and the STA time, etc. # XXX Allow only *ONE* compound page. Http/Client/Compound instproc get-response-GET { server pageid args } { $self instvar pending_ id_ ns_ recv_objs_ max_stale_ stat_ \ simStartTime_ pgtr_ if ![info exists pending_($pageid)] { error "Client $id_: Unrequested response page $pageid from server/cache [$server id]" } # Check if this is the main page if [$pgtr_ is-mainpage $pageid] { set mpgid $pageid # Get all the embedded objects, do "active OFF" delay # if available $self instvar req_objs_ recv_objs_ rvInterObj_ # Objects that are to be received set recv_objs_($pageid) [$pgtr_ get-obj-num $pageid] # Objects that have been requested set req_objs_($pageid) $recv_objs_($pageid) set objid [join [$pgtr_ get-next-objs $pageid]] if [info exists rvInterObj_] { $ns_ at [expr [$ns_ now] + [$rvInterObj_ value]] \ "$self next-obj $server $objid" } else { eval $self next-obj $server $objid } } else { # Main page id set mpgid [$pgtr_ get-mainpage $pageid] } array set data $args # Check stale hits and record maximum stale hit time set origsvr [lindex [split $pageid :] 0] set modtime [$origsvr get-modtime $pageid] set reqtime [lindex $pending_($pageid) 0] set reqrtt [expr [$ns_ now] - $reqtime] # XXX If a stale hit occurs because a page is modified during the RTT # of the request, we should *NOT* consider it a stale hit. We # implement it by ignoring all stale hits whose modification time is # larger than the request time. # # See Http/Client::get-response-GET{} if {$modtime > $data(modtime)} { $self instvar ns_ # Staleness is the time from now to the time it's last modified set tmp [$origsvr stale-time $pageid $data(modtime)] if {$tmp > $reqrtt/2} { if ![info exists max_stale_($mpgid)] { set max_stale_($mpgid) $tmp } elseif {$max_stale_($mpgid) < $tmp} { set max_stale_($mpgid) $tmp } } } # Wait for the embedded objects if this is a main page if [$pgtr_ is-mainpage $pageid] { return } # Delete pending record of all embedded objects, but not the main page; # we need it later to compute the response time, etc. # XXX assuming only one request per object $self evTrace C RCV p $pageid s [$server id] l $reqrtt z $data(size) unset pending_($pageid) # Check if we have any pending embedded objects incr recv_objs_($mpgid) -1 if {$recv_objs_($mpgid) > 0} { # We are waiting for more objects to come return } # Now we've received all objects $self instvar pgtr_ # Record response time for the entire compound page set reqtime [lindex $pending_($mpgid) 0] $self evTrace C RCV p $mpgid s [$origsvr id] l \ [expr [$ns_ now] - $reqtime] z $data(size) # We are done with this page unset pending_($mpgid) if [info exists simStartTime_] { set tmp [expr [$ns_ now] - $reqtime] set stat_(rep-time) [expr $stat_(rep-time) + $tmp] if {$stat_(rt-min) > $tmp} { set stat_(rt-min) $tmp } if {$stat_(rt-max) < $tmp} { set stat_(rt-max) $tmp } unset tmp } if [info exists max_stale_($mpgid)] { $self evTrace C STA p $mpgid s [$origsvr id] \ l $max_stale_($mpgid) if [info exists simStartTime_] { incr stat_(stale-num) set stat_(stale-time) [expr \ $stat_(stale-time) + $max_stale_($mpgid)] if {$stat_(st-min) > $max_stale_($mpgid)} { set stat_(st-min) $max_stale_($mpgid) } if {$stat_(st-max) < $max_stale_($mpgid)} { set stat_(st-max) $max_stale_($mpgid) } } unset max_stale_($mpgid) } $self mark-response $mpgid } Http/Client/Compound instproc mark-request { pageid } { set id [lindex [split $pageid :] end] if {$id == 0} { $self next $pageid } } # # 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 web cache, client and server which support # multimedia objects. # # $Header$ # # Multimedia web client # # Override this method to change the default max size of the page pool Http/Client/Media instproc create-pagepool {} { set pool [new PagePool/Client/Media] $self set-pagepool $pool return $pool } # Connection procedure: # (1) Client ---(HTTP_REQUEST)--> Server # (2) Client <--(HTTP_RESPONSE)-- Server # (3) Client <----(RAP_DATA)---- Server Http/Client/Media instproc get-response-GET { server pageid args } { eval $self next $server $pageid $args # XXX Enter the page into page pool, so that we can keep track of # which segment has been received and know where to stop receiving. if [$self exist-page $pageid] { error "Http/Client/Media: receives an \"active\" page!" } eval $self enter-page $pageid $args array set data $args if {[info exists data(pgtype)] && ($data(pgtype) == "MEDIA")} { # Create a multimedia connection to server $self media-connect $server $pageid } } # Don't accept an request if there is one stream still on transit Http/Client/Media instproc send-request { server type pageid args } { $self instvar mmapp_ if [info exists mmapp_($pageid)] { return } eval $self next $server $type $pageid $args } # Create a RAP connection to a server Http/Client/Media instproc media-connect { server pageid } { # DEBUG ONLY #puts "Client [$self id] media-connect [$server id] $pageid" $self instvar mmapp_ ns_ node_ Http instvar MEDIA_TRANSPORT_ MEDIA_APP_ if [info exists mmapp_($pageid)] { puts "Media client [$self id] got a request for an existing stream" return } set agent [new Agent/$MEDIA_TRANSPORT_] $ns_ attach-agent $node_ $agent set app [new Application/$MEDIA_APP_ $pageid] $app attach-agent $agent $app target $self $server alloc-mcon $self $pageid $agent set mmapp_($pageid) $app $app set-layer [$self get-layer $pageid] } Http/Client/Media instproc media-disconnect { server pageid } { $self instvar mmapp_ ns_ node_ if {![info exists mmapp_($pageid)]} { error "Media client [$self id] disconnect: not connected to \ server [$server id] with page $pageid" } set app $mmapp_($pageid) set agent [$app agent] $ns_ detach-agent $node_ $agent # DEBUG ONLY # puts "Client [$self id] disconnect from server [$server id]" # Disconnect the agent and app at the server side $server media-disconnect $self $pageid delete $agent delete $app unset mmapp_($pageid) $self stream-received $pageid } # # Multimedia web server # # Create media pages instead of normal pages Http/Server/Media instproc gen-page { pageid } { $self instvar pgtr_ set pginfo [$self next $pageid] if [$pgtr_ is-media-page $pageid] { return [lappend pginfo pgtype MEDIA] } else { return $pginfo } } Http/Server/Media instproc create-pagepool {} { set pool [new PagePool/Client/Media] $self set-pagepool $pool # Set the pool size to "infinity" (2^31-1) $pool set max_size_ 2147483647 return $pool } Http/Server/Media instproc medialog-on {} { $self instvar MediaLog_ set MediaLog_ 1 } # Allocate a media connection Http/Server/Media instproc alloc-mcon { client pageid dst_agent } { $self instvar ns_ node_ mmapp_ Http instvar MEDIA_TRANSPORT_ MEDIA_APP_ set agent [new Agent/$MEDIA_TRANSPORT_] $ns_ attach-agent $node_ $agent set app [new Application/$MEDIA_APP_ $pageid] $app attach-agent $agent $app target $self set mmapp_($client/$pageid) $app # Set layers $app set-layer [$self get-layer $pageid] # Associate $app with $client and $pageid $self register-client $app $client $pageid # DEBUG ONLY # Logging MediaApps, only do it for sender-side media apps $self instvar MediaLog_ if [info exists MediaLog_] { set lf [$self log] if {$lf != ""} { $app log $lf } } # puts "Server [$self id] allocated a connection to client [$client id] using agent $agent" # Connect two RAP agents and start data transmission $ns_ connect $agent $dst_agent $agent start } Http/Server/Media instproc media-disconnect { client pageid } { $self instvar mmapp_ ns_ node_ # DEBUG ONLY # puts "At [$ns_ now] Server [$self id] disconnected from client [$client id]" if {![info exists mmapp_($client/$pageid)]} { error "Media server [$self id] disconnect: not connected to \ client [$client id] with page $pageid" } set app $mmapp_($client/$pageid) set agent [$app agent] $ns_ detach-agent $node_ $agent $self unregister-client $app $client $pageid # DEBUG ONLY # puts "Server [$self id] deleting agent $agent" delete $agent delete $app unset mmapp_($client/$pageid) } # Tell the client that a stream has been completed. Assume that there is # an open connection between server and client Http/Server/Media instproc finish-stream { app } { $self instvar mmapp_ foreach n [array names mmapp_] { if {$mmapp_($n) == $app} { set tmp [split $n /] set client [lindex $tmp 0] set pageid [lindex $tmp 1] $self send $client [$self get-reqsize] \ "$client media-disconnect $self $pageid" return } } } # If the page is a media page, set the response size to that of the request Http/Server/Media instproc handle-request-GET { pageid args } { set pginfo [eval $self next $pageid $args] if {[$self get-pagetype $pageid] == "MEDIA"} { set pginfo [lreplace $pginfo 0 0 [$self get-reqsize]] } return $pginfo } Http/Server/Media instproc gen-pageinfo { pageid } { set pginfo [eval $self next $pageid] # Create contents for media page $self instvar pgtr_ if [$pgtr_ is-media-page $pageid] { return [lappend pginfo pgtype MEDIA layer \ [$pgtr_ get-layer $pageid]] } else { return $pginfo } } # Map a media application to the HTTP application at the other end #Http/Server/Media instproc map-media-app { app } { # $self instvar mmapp_ # foreach n [array names mmapp_] { # if {$mmapp_($n) == $app} { # return [lindex [split $n /] 0] # } # } #} # Handle prefetching of designated segments Http/Server/Media instproc get-request { client type pageid args } { if {$type == "PREFSEG"} { # XXX allow only one prefetching stream from any single client # Records this client as doing prefetching. # FAKE a connection to the client without prior negotiation set pagenum [lindex [split $pageid :] 1] set conid [lindex $args 0] set layer [lindex $args 1] set seglist [lrange $args 2 end] eval $self register-prefetch $client $pagenum $conid \ $layer $seglist $client start-prefetch $self $pageid $conid $self evTrace S PREF p $pageid l $layer [join $seglist] # DEBUG only # $self instvar ns_ # puts "At [$ns_ now] server [$self id] prefetches $args" } elseif {$type == "STOPPREF"} { set pagenum [lindex [split $pageid :] 1] set conid [lindex $args 0] # DEBUG only # $self instvar ns_ # puts "At [$ns_ now] server [$self id] stops prefetching $pageid conid $conid" if [$self stop-prefetching $client $conid $pagenum] { # Tear down pref channel iff we don't have remaining # clients sharing the channel $client media-disconnect $self $pageid $conid } } elseif {$type == "OFFLPREF"} { # Simply send the page back through TCP channel if ![$self exist-page $pageid] { error "Server [$self id] offline-prefetch non-existent page $pageid!" } set size [$self get-size $pageid] $self send $client $size "$client offline-complete $pageid" } else { eval $self next $client $type $pageid $args } } # # Multimedia web cache # Http/Cache/Media instproc create-pagepool {} { set pool [new PagePool/Client/Media] $self set-pagepool $pool return $pool } Http/Cache/Media instproc start-prefetch { server pageid conid } { $self instvar pref_ ns_ if [info exists pref_($server/$pageid)] { # We are already connected to the server if {[lsearch -exact $pref_($server/$pageid) $conid] == -1} { # puts "At [$ns_ now] cache [$self id] RE-REQUESTs prefetching to server [$server id] for page $pageid" lappend pref_($server/$pageid) $conid } return } else { # Whenever there is a prefetching request to the server, # increase the "ref counter" by one. Then we delete it # when it's 0. lappend pref_($server/$pageid) $conid } # puts "At [$ns_ now] cache [$self id] starts prefetching to [$server id] for page $pageid conid $conid" # XXX Do not use QA for prefetching!! Http instvar MEDIA_APP_ set oldapp $MEDIA_APP_ # XXX Do not use the default initial RTT of RAP. We must start sending # as soon as possible, then drop our rate if necessary. Instead, set # initial ipg_ and srtt_ to 10ms. set oldipg [Agent/RAP set ipg_] set oldsrtt [Agent/RAP set srtt_] Agent/RAP set ipg_ 0.01 Agent/RAP set srtt_ 0.01 set MEDIA_APP_ MediaApp $self media-connect $server $pageid set MEDIA_APP_ $oldapp Agent/RAP set ipg_ $oldipg Agent/RAP set srtt_ $oldsrtt } Http/Cache/Media instproc media-connect { server pageid } { $self instvar s_mmapp_ ns_ node_ # DEBUG ONLY # puts "At [$ns_ now], cache [$self id] connects to [$server id] for page $pageid" Http instvar MEDIA_TRANSPORT_ MEDIA_APP_ if [info exists s_mmapp_($server/$pageid)] { error "Media client [$self id] got a request for an existing \ stream" } set agent [new Agent/$MEDIA_TRANSPORT_] $ns_ attach-agent $node_ $agent set app [new Application/$MEDIA_APP_ $pageid] $app attach-agent $agent $app target $self $server alloc-mcon $self $pageid $agent set s_mmapp_($server/$pageid) $app $app set-layer [$self get-layer $pageid] } Http/Cache/Media instproc alloc-mcon { client pageid dst_agent } { $self instvar ns_ node_ c_mmapp_ Http instvar MEDIA_TRANSPORT_ MEDIA_APP_ if [info exists c_mmapp_($client/$pageid)] { error "Media cache [$self id] got a request for an existing \ stream $pageid from client [$client id]" } set agent [new Agent/$MEDIA_TRANSPORT_] $ns_ attach-agent $node_ $agent set app [new Application/$MEDIA_APP_ $pageid] $app attach-agent $agent $app target $self set c_mmapp_($client/$pageid) $app # Set layers $app set-layer [$self get-layer $pageid] # Associate $app with $client and $pageid $self register-client $app $client $pageid # Logging MediaApps, only do it for sender-side media apps $self instvar MediaLog_ if [info exists MediaLog_] { set lf [$self log] if {$lf != ""} { $app log $lf } } # Connect two RAP agents and start data transmission $ns_ connect $agent $dst_agent $agent start } # Turn on logging of media application Http/Cache/Media instproc medialog-on {} { $self instvar MediaLog_ set MediaLog_ 1 } Http/Cache/Media instproc media-disconnect { host pageid args } { $self instvar c_mmapp_ s_mmapp_ ns_ node_ pref_ c_tbt_ # Flags: is it client disconnection or server? set cntdisco 0 set svrdisco 0 set server [lindex [split $pageid :] 0] if {($host != $server) && [info exists c_mmapp_($host/$pageid)]} { # Disconnect from a client set app $c_mmapp_($host/$pageid) set agent [$app agent] $ns_ detach-agent $node_ $agent $self unregister-client $app $host $pageid # DEBUG ONLY # puts "At [$ns_ now] Cache [$self id] disconnect from \ # client [$host id]" # Do NOT delete client-side agent and app until we've # torn down prefetching connection to the server. # XXX But first check that this client has indeed been # involved in prefetching! if {[info exists pref_($server/$pageid)] && \ [lsearch -exact $pref_($server/$pageid) $app] != -1} { # If we have concurrent requests and other clients # may still be using this prefetching channel. We # tell the server that this client has stopped so # that server will remove all related state. # puts "At [$ns_ now] Cache [$self id] stops prefetching for conid $app, page $pageid, from server [$server id]" $self send $server [$self get-reqsize] "$server get-request $self STOPPREF $pageid $app" # Stop application-related timers set c_tbt_($host/$pageid) $app $app stop } else { delete $app } delete $agent unset c_mmapp_($host/$pageid) # Dump status of all pages after serving a client $self instvar pool_ foreach p [lsort [$pool_ list-pages]] { $self dump-page $p } set cntdisco 1 } elseif [info exists s_mmapp_($host/$pageid)] { # XXX This assumes that we are NOT connecting to another cache. # Stop prefetching when we requested that at the server. # # Also, we assume that while the cache is downloading from # the server during the first retrieval, no prefetching should # be used. This is guaranteed by the way MEDIAREQ_CHECKSEG is # handled in mcache.cc:680. # # Note that (1) this always happens AFTER client disconnection, # (2) whenever there is a prefetching connection, there's # always a corresponding entry in s_mmapp_(). # # If we are disconnecting a prefetching channel, check if # we still have other clients sharing the channel. If not, # then we tear down the channel. set svrdisco 1 if [info exists pref_($server/$pageid)] { # By default we don't tear down pref channel set teardown 0 # Actually the MediaApp for the client set conid [lindex $args 0] set pos [lsearch -exact $pref_($server/$pageid) $conid] if {$pos == -1} { error "media-disconnect cannot find $conid!!" } set pref_($server/$pageid) [lreplace \ $pref_($server/$pageid) $pos $pos] if {[llength $pref_($server/$pageid)] == 0} { $self evTrace E STP s [$server id] p $pageid unset pref_($server/$pageid) # Now we can tear down the pref channel set teardown 1 } # Tear down client-side connection (i.e.,conid) # puts "At [$ns_ now] cache [$self id] stops prefetching\ # to [$server id] for client $conid" delete $conid } else { # If no pref, tear down by default set teardown 1 } if {$teardown} { # Disconnect from a server set app $s_mmapp_($host/$pageid) set agent [$app agent] $ns_ detach-agent $node_ $agent $host media-disconnect $self $pageid # DEBUG ONLY # puts "At [$ns_ now] Cache [$self id] disconnect from \ # server [$host id]" delete $agent delete $app unset s_mmapp_($host/$pageid) } # Use the TCP channel between the cache and the server to # transmit the entire file. Note since we no longer do # online prefetching, this channel is pretty much empty # and we can occupy it as long as we want. $self instvar firstreq_ if {([$self get-pref-style] == "OFFLINE_PREF") && \ [info exists firstreq_($pageid)]} { $self send $server [$self get-reqsize] \ "$server get-request $self OFFLPREF $pageid" } if [info exists firstreq_($pageid)] { unset firstreq_($pageid) } } else { error "At [$ns_ now] Media cache [$self id] tries to \ disconnect from a non-connected host [$host id]" } # XXX Only do this when we disconnect from a server. if {$svrdisco == 1} { $self stream-received $pageid } } # Tell the client that a stream has been completed. Assume that there is # an open connection between server and client Http/Cache/Media instproc finish-stream { app } { $self instvar c_mmapp_ s_mmapp_ foreach n [array names c_mmapp_] { if {$c_mmapp_($n) == $app} { set tmp [split $n /] set client [lindex $tmp 0] set pageid [lindex $tmp 1] $self send $client [$self get-reqsize] \ "$client media-disconnect $self $pageid" return } } } Http/Cache/Media instproc get-response-GET { server pageid args } { # Whether this is the first request. If offline prefetching is used, # media-disconnect{} uses this information to decide whether to # prefetch the entire stream. $self instvar firstreq_ if ![$self exist-page $pageid] { set firstreq_($pageid) 1 } eval $self next $server $pageid $args # DEBUG ONLY # puts "Cache [$self id] gets response" array set data $args if {[info exists data(pgtype)] && ($data(pgtype) == "MEDIA")} { # Create a multimedia connection to server $self media-connect $server $pageid } } # XXX If it's a media page, change page size and send back a small response. # This response is intended to establish a RAP connection from client to # cache. Http/Cache/Media instproc answer-request-GET { cl pageid args } { array set data $args if {[info exists data(pgtype)] && ($data(pgtype) == "MEDIA")} { set size [$self get-reqsize] } else { set size $data(size) } $self send $cl $size \ "$cl get-response-GET $self $pageid $args" $self evTrace E SND c [$cl id] p $pageid z $data(size) } # $args is a list of segments, each of which is a list of # two elements: {start end} Http/Cache/Media instproc pref-segment {conid pageid layer args} { # XXX directly send a request to the SERVER. Assuming that # we are not going through another cache. set server [lindex [split $pageid :] 0] set size [$self get-reqsize] $self send $server $size "$server get-request $self PREFSEG \ $pageid $conid $layer [join $args]" } Http/Cache/Media instproc set-repl-style { style } { $self instvar pool_ $pool_ set-repl-style $style } # 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. # # Re-tooled version of Polly's web traffic models (tcl/ex/web-traffic.tcl, # tcl/ex/large-scale-traffic.tcl) in order to save memory. # # The main strategy is to move everything into C++, only leave an OTcl # configuration interface. Be very careful as what is configuration and # what is functionality. # # $Header$ PagePool/WebTraf instproc launch-req { id clnt svr ctcp csnk stcp ssnk size } { # puts "launch request [$clnt id] [$svr id] size $size" # puts " client: $ctcp $csnk" # puts " server: $stcp $ssnk" set ns [Simulator instance] $ns attach-agent $clnt $ctcp $ns attach-agent $svr $csnk $ns connect $ctcp $csnk $ctcp set fid_ $id $ns attach-agent $svr $stcp $ns attach-agent $clnt $ssnk $ns connect $stcp $ssnk $stcp set fid_ $id $ctcp proc done {} "$self done-req $clnt $svr $ctcp $csnk $stcp $size" $stcp proc done {} "$self done-resp $clnt $svr $stcp $ssnk" # Send a single packet as a request $ctcp advanceby 1 } PagePool/WebTraf instproc done-req { clnt svr ctcp csnk stcp size } { # puts "done request [$clnt id] [$svr id]" set ns [Simulator instance] # Recycle client-side TCP agents $ns detach-agent $clnt $ctcp $ns detach-agent $svr $csnk $ctcp reset $csnk reset $self recycle $ctcp $csnk # puts "recycled $ctcp $csnk" # Advance $size packets $stcp advanceby $size } PagePool/WebTraf instproc done-resp { clnt svr stcp ssnk } { # puts "done response [$clnt id] [$svr id]" set ns [Simulator instance] # Recycle server-side TCP agents $ns detach-agent $clnt $ssnk $ns detach-agent $svr $stcp $stcp reset $ssnk reset $self recycle $stcp $ssnk # puts "recycled $stcp $ssnk" } # XXX Should allow customizable TCP types. Can be easily done via a # class variable PagePool/WebTraf instproc alloc-tcp {} { return [new Agent/TCP/Reno] } PagePool/WebTraf instproc alloc-tcp-sink {} { return [new Agent/TCPSink] } # # 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. # # ns trace support for nam # # Author: Haobo Yu (haoboy@isi.edu) # # $Header$ # # # Support for node tracing # # This will only work during initialization. Not possible to change shape # dynamically Node instproc shape { shape } { $self instvar attr_ set attr_(SHAPE) $shape } # Returns the current shape of the node Node instproc get-shape {} { $self instvar attr_ if [info exists attr_(SHAPE)] { return $attr_(SHAPE) } else { return "" } } Node instproc color { color } { $self instvar attr_ id_ set ns [Simulator instance] if [$ns is-started] { # color must be initialized $ns puts-nam-config \ [eval list "n -t [$ns now] -s $id_ -S COLOR -c $color -o $attr_(COLOR) -i $color -I $attr_(LCOLOR)"] set attr_(COLOR) $color set attr_(LCOLOR) $color } else { set attr_(COLOR) $color set attr_(LCOLOR) $color } } Node instproc label { str} { $self instvar attr_ id_ set ns [Simulator instance] if [info exists attr_(DLABEL)] { $ns puts-nam-config "n -t [$ns now] -s $id_ -S DLABEL -l \"$str\" -L $attr_(DLABEL)" } else { $ns puts-nam-config "n -t [$ns now] -s $id_ -S DLABEL -l \"$str\" -L \"\"" } set attr_(DLABEL) \"$str\" } Node instproc label-color { str} { $self instvar attr_ id_ set ns [Simulator instance] if [info exists attr_(DCOLOR)] { $ns puts-nam-config "n -t [$ns now] -s $id_ -S DCOLOR -e \"$str\" -E $attr_(DCOLOR)" } else { $ns puts-nam-config "n -t [$ns now] -s $id_ -S DCOLOR -e \"$str\" -E \"\"" } set attr_(DCOLOR) \"$str\" } Node instproc label-at { str } { $self instvar attr_ id_ set ns [Simulator instance] if [info exists attr_(DIRECTION)] { $ns puts-nam-config "n -t [$ns now] -s $id_ -S DIRECTION -p \"$str\" -P $attr_(DIRECTION)" } else { $ns puts-nam-config "n -t [$ns now] -s $id_ -S DIRECTION -p \"$str\" -P \"\"" } set attr_(DIRECTION) \"$str\" } Node instproc dump-namconfig {} { $self instvar attr_ id_ address_ set ns [Simulator instance] if ![info exists attr_(SHAPE)] { set attr_(SHAPE) "circle" } if ![info exists attr_(COLOR)] { set attr_(COLOR) "black" set attr_(LCOLOR) "black" } if ![info exists attr_(DCOLOR)] { set attr_(DCOLOR) "black" } $ns puts-nam-config \ [eval list "n -t * -a $address_ -s $id_ -S UP -v $attr_(SHAPE) -c $attr_(COLOR) -i $attr_(LCOLOR)"] } Node instproc change-color { color } { puts "Warning: Node::change-color is obsolete. Use Node::color instead" $self color $color } Node instproc get-attribute { name } { $self instvar attr_ if [info exists attr_($name)] { return $attr_($name) } else { return "" } } Node instproc get-color {} { puts "Warning: Node::get-color is obsolete. Please use Node::get-attribute" return [$self get-attribute "COLOR"] } Node instproc add-mark { name color {shape "circle"} } { $self instvar id_ markColor_ shape_ set ns [Simulator instance] $ns puts-nam-config "m -t [$ns now] -s $id_ -n $name -c $color -h $shape" set markColor_($name) $color set shape_($name) $shape } Node instproc delete-mark { name } { $self instvar id_ markColor_ shape_ # Ignore if the mark $name doesn't exist if ![info exists markColor_($name)] { return } set ns [Simulator instance] $ns puts-nam-config \ "m -t [$ns now] -s $id_ -n $name -c $markColor_($name) -h $shape_($name) -X" } # # Support for link tracing # XXX only SimpleLink (and its children) can dump nam config, because Link # doesn't have bandwidth and delay. # SimpleLink instproc dump-namconfig {} { # make a duplex link in nam $self instvar link_ attr_ fromNode_ toNode_ if ![info exists attr_(COLOR)] { set attr_(COLOR) "black" } if ![info exists attr_(ORIENTATION)] { set attr_(ORIENTATION) "" } set ns [Simulator instance] set bw [$link_ set bandwidth_] set delay [$link_ set delay_] $ns puts-nam-config \ "l -t * -s [$fromNode_ id] -d [$toNode_ id] -S UP -r $bw -D $delay -c $attr_(COLOR) -o $attr_(ORIENTATION)" } Link instproc dump-nam-queueconfig {} { $self instvar attr_ fromNode_ toNode_ if ![info exists attr_(COLOR)] { set attr_(COLOR) "black" } set ns [Simulator instance] if [info exists attr_(QUEUE_POS)] { $ns puts-nam-config "q -t * -s [$fromNode_ id] -d [$toNode_ id] -a $attr_(QUEUE_POS)" } else { set attr_(QUEUE_POS) "" } } # # XXX # This function should be called ONLY ONCE during initialization. # The order in which links are created in nam is determined by the calling # order of this function. # Link instproc orient { ori } { $self instvar attr_ set attr_(ORIENTATION) $ori [Simulator instance] register-nam-linkconfig $self } Link instproc get-attribute { name } { $self instvar attr_ if [info exists attr_($name)] { return $attr_($name) } else { return "" } } Link instproc queuePos { pos } { $self instvar attr_ set attr_(QUEUE_POS) $pos } Link instproc color { color } { $self instvar attr_ fromNode_ toNode_ trace_ set ns [Simulator instance] if [$ns is-started] { $ns puts-nam-config \ [eval list "l -t [$ns now] -s [$fromNode_ id] -d [$toNode_ id] -S COLOR -c $color -o $attr_(COLOR)"] set attr_(COLOR) $color } else { set attr_(COLOR) $color } } # a link doesn't have its own trace file, write it to global trace file Link instproc change-color { color } { puts "Warning: Link::change-color is obsolete. Please use Link::color." $self color $color } Link instproc get-color {} { puts "Warning: Node::get-color is obsolete. Please use Node::get-attribute" return [$self get-attribute "COLOR"] } Link instproc label { label } { $self instvar attr_ fromNode_ toNode_ trace_ set ns [Simulator instance] if [info exists attr_(DLABEL)] { $ns puts-nam-config \ "l -t [$ns now] -s [$fromNode_ id] -d [$toNode_ id] -S DLABEL -l \"$label\" -L $attr_(DLABEL)" } else { $ns puts-nam-config \ "l -t [$ns now] -s [$fromNode_ id] -d [$toNode_ id] -S DLABEL -l \"$label\" -L \"\"" } set attr_(DLABEL) \"$label\" } Link instproc label-color { str } { $self instvar attr_ fromNode_ toNode_ trace_ set ns [Simulator instance] if [info exists attr_(DCOLOR)] { $ns puts-nam-config \ "l -t [$ns now] -s [$fromNode_ id] -d [$toNode_ id] -S DCOLOR -e \"$str\" -E $attr_(DCOLOR)" } else { $ns puts-nam-config \ "l -t [$ns now] -s [$fromNode_ id] -d [$toNode_ id] -S DCOLOR -e \"$str\" -E \"\"" } set attr_(DCOLOR) \"$str\" } Link instproc label-at { str } { $self instvar attr_ fromNode_ toNode_ trace_ set ns [Simulator instance] if [info exists attr_(DIRECTION)] { $ns puts-nam-config \ "l -t [$ns now] -s [$fromNode_ id] -d [$toNode_ id] -S DIRECTION -p \"$str\" -P $attr_(DIRECTION)" } else { $ns puts-nam-config \ "l -t [$ns now] -s [$fromNode_ id] -d [$toNode_ id] -S DIRECTION -p \"$str\" -P \"\"" } set attr_(DIRECTION) \"$str\" } # # Support for nam snapshot # Simulator instproc snapshot { } { set ns [Simulator instance] $ns puts-nam-config \ "v -t [$self now] take_snapshot" } Simulator instproc rewind-nam { } { set ns [Simulator instance] $ns puts-nam-config \ "v -t [$self now] playing_backward" } Simulator instproc re-rewind-nam { } { set ns [Simulator instance] $ns puts-nam-config \ "v -t [$self now] playing_forward" } Simulator instproc terminate-nam { } { set ns [Simulator instance] $ns puts-nam-config \ "v -t [$self now] terminating_nam" } # # Support for agent tracing # # This function records agents being traced, so they will be written into nam # trace when the simulator starts Simulator instproc add-agent-trace { agent name {f ""} } { $self instvar tracedAgents_ set tracedAgents_($name) $agent set trace [$self get-nam-traceall] if {$f != ""} { $agent attach-trace $f } elseif {$trace != ""} { $agent attach-trace $trace } } Simulator instproc delete-agent-trace { agent } { $agent delete-agent-trace } Simulator instproc monitor-agent-trace { agent } { $self instvar monitoredAgents_ lappend monitoredAgents_ $agent } # # Agent trace is added when attaching to a traced node # we need to keep a file handle in tcl so that var tracing can also be # done in tcl by manual inserting update-var-trace{} # Agent instproc attach-trace { file } { $self instvar namTrace_ set namTrace_ $file # add all traced var messages $self attach $file } # # nam initialization # Simulator instproc dump-namagents {} { $self instvar tracedAgents_ monitoredAgents_ if {![$self is-started]} { return } if [info exists tracedAgents_] { foreach id [array names tracedAgents_] { $tracedAgents_($id) add-agent-trace $id $tracedAgents_($id) cmd dump-namtracedvars } unset tracedAgents_ } if [info exists monitoredAgents_] { foreach a $monitoredAgents_ { $a show-monitor } unset monitoredAgents_ } } Simulator instproc dump-namversion { v } { $self puts-nam-config "V -t * -v $v -a 0" } Simulator instproc dump-namcolors {} { $self instvar color_ if ![$self is-started] { return } foreach id [array names color_] { $self puts-nam-config "c -t * -i $id -n $color_($id)" } } Simulator instproc dump-namlans {} { if ![$self is-started] { return } $self instvar Node_ foreach nn [array names Node_] { if [$Node_($nn) is-lan?] { $Node_($nn) dump-namconfig } } } Simulator instproc dump-namlinks {} { $self instvar linkConfigList_ if ![$self is-started] { return } if [info exists linkConfigList_] { foreach lnk $linkConfigList_ { $lnk dump-namconfig } unset linkConfigList_ } } Simulator instproc dump-namnodes {} { $self instvar Node_ if ![$self is-started] { return } foreach nn [array names Node_] { if ![$Node_($nn) is-lan?] { $Node_($nn) dump-namconfig } } } Simulator instproc dump-namqueues {} { $self instvar link_ if ![$self is-started] { return } foreach qn [array names link_] { $link_($qn) dump-nam-queueconfig } } # Write hierarchical masks/shifts into trace file Simulator instproc dump-namaddress {} { AddrParams instvar hlevel_ NodeShift_ NodeMask_ McastShift_ McastMask_ # First write number of hierarchies $self puts-nam-config \ "A -t * -n $hlevel_ -p 0 -o [AddrParams set ALL_BITS_SET] -c $McastShift_ -a $McastMask_" for {set i 1} {$i <= $hlevel_} {incr i} { $self puts-nam-config \ "A -t * -h $i -m $NodeMask_($i) -s $NodeShift_($i)" } } Simulator instproc init-nam {} { $self instvar annotationSeq_ set annotationSeq_ 0 # Setting nam trace file version first $self dump-namversion 1.0a5 # Addressing scheme $self dump-namaddress # Color configuration for nam $self dump-namcolors # Node configuration for nam $self dump-namnodes # Lan and Link configurations for nam $self dump-namlinks $self dump-namlans # nam queue configurations $self dump-namqueues # Traced agents for nam $self dump-namagents } # # Other animation control support # Simulator instproc trace-annotate { str } { $self instvar annotationSeq_ $self puts-ns-traceall [format \ "v %s %s {set sim_annotation {%s}}" [$self now] eval $str] incr annotationSeq_ $self puts-nam-config \ "v -t [$self now] sim_annotation [$self now] $annotationSeq_ $str" } proc trace_annotate { str } { set ns [Simulator instance] $ns trace-annotate $str } proc flash_annotate { start duration msg } { set ns [Simulator instance] $ns at $start "trace_annotate {$msg}" $ns at [expr $start+$duration] "trace_annotate periodic_message" } # rate's unit is second Simulator instproc set-animation-rate { rate } { # time_parse defined in tcl/rtp/session-rtp.tcl set r [time_parse $rate] $self puts-nam-config "v -t [$self now] set_rate [expr 10*log10($r)] 1" } # # Copyright (c) 1996-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. # Ported from CMU-Monarch project's mobility extensions -Padma, 10/98. # @(#) $Header: # # ====================================================================== # Default Script Options # ====================================================================== Agent/DSDV set sport_ 0 Agent/DSDV set dport_ 0 Agent/DSDV set wst0_ 6 ;# As specified by Pravin Agent/DSDV set perup_ 15 ;# As given in the paper (update period) Agent/DSDV set use_mac_ 0 ;# Performance suffers with this on Agent/DSDV set be_random_ 1 ;# Flavor the performance numbers :) Agent/DSDV set alpha_ 0.875 ;# 7/8, as in RIP(?) Agent/DSDV set min_update_periods_ 3 ;# Missing perups before linkbreak ##Agent/DSDV set myaddr_ 0 ;# My address Agent/DSDV set verbose_ 0 ;# Agent/DSDV set trace_wst_ 0 ;# #Class Agent/DSDV set opt(ragent) Agent/DSDV set opt(pos) NONE ;# Box or NONE if { $opt(pos) == "Box" } { puts "*** DSDV using Box configuration..." } # ====================================================================== Agent instproc init args { eval $self next $args } Agent/DSDV instproc init args { eval $self next $args } # ===== Get rid of the warnings in bind ================================ # ====================================================================== proc create-dsdv-routing-agent { node id } { global ns_ ragent_ tracefd opt # # Create the Routing Agent and attach it to port 255. # #set ragent_($id) [new $opt(ragent) $id] set ragent_($id) [new $opt(ragent)] set ragent $ragent_($id) ## setup address (supports hier-addr) for dsdv agent ## and mobilenode set addr [$node node-addr] $ragent addr $addr $ragent node $node if [Simulator set mobile_ip_] { $ragent port-dmux [$node set dmux_] } $node addr $addr $node set ragent_ $ragent $node attach $ragent 255 ##$ragent set target_ [$node set ifq_(0)] ;# ifq between LL and MAC # XXX FIX ME XXX # Where's the DSR stuff? #$ragent ll-queue [$node get-queue 0] ;# ugly filter-queue hack $ns_ at 0.0 "$ragent_($id) start-dsdv" ;# start updates # # Drop Target (always on regardless of other tracing) # set drpT [cmu-trace Drop "RTR" $node] $ragent drop-target $drpT # # Log Target # set T [new Trace/Generic] $T target [$ns_ set nullAgent_] $T attach $tracefd $T set src_ $id $ragent tracetarget $T } proc dsdv-create-mobile-node { id args } { global ns ns_ chan prop topo tracefd opt node_ global chan prop tracefd topo opt set ns_ [Simulator instance] if {[Simulator set EnableHierRt_]} { if [Simulator set mobile_ip_] { set node_($id) [new MobileNode/MIPMH $args] } else { set node_($id) [new Node/MobileNode/BaseStationNode $args] } } else { set node_($id) [new Node/MobileNode] } set node $node_($id) $node random-motion 0 ;# disable random motion $node topography $topo # # This Trace Target is used to log changes in direction # and velocity for the mobile node. # set T [new Trace/Generic] $T target [$ns_ set nullAgent_] $T attach $tracefd $T set src_ $id $node log-target $T $node add-interface $chan $prop $opt(ll) $opt(mac) \ $opt(ifq) $opt(ifqlen) $opt(netif) $opt(ant) # # Create a Routing Agent for the Node # create-$opt(rp)-routing-agent $node $id # ============================================================ if { $opt(pos) == "Box" } { # # Box Configuration # set spacing 200 set maxrow 7 set col [expr ($id - 1) % $maxrow] set row [expr ($id - 1) / $maxrow] $node set X_ [expr $col * $spacing] $node set Y_ [expr $row * $spacing] $node set Z_ 0.0 $node set speed_ 0.0 $ns_ at 0.0 "$node_($id) start" } return $node } # # Copyright (c) 1996-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. # # Ported from CMU-Monarch project's mobility extensions -Padma, 10/98. # dsr.tcl # $Id: AllCode__.html 980 2002-12-12 09:36:58Z ffilali $ # ====================================================================== # Default Script Options # ====================================================================== set opt(rt_port) 255 set opt(cc) "off" ;# have god check the caches for bad links? Agent/DSRAgent set sport_ 255 Agent/DSRAgent set dport_ 255 # ====================================================================== # god cache monitoring #source tcl/ex/timer.tcl Class CacheTimer -superclass Timer CacheTimer instproc timeout {} { global opt node_; $self instvar agent; $agent check-cache $self sched 1.0 } proc checkcache {a} { global cachetimer ns set cachetimer [new CacheTimer] $cachetimer set agent $a $cachetimer sched 1.0 } # ====================================================================== Class SRNode -superclass Node/MobileNode SRNode instproc init {args} { global ns ns_ opt tracefd RouterTrace $self instvar dsr_agent_ dmux_ entry_point_ address_ set ns_ [Simulator instance] eval $self next $args ;# parent class constructor if {$dmux_ == "" } { set dmux_ [new Classifier/Port] $dmux_ set mask_ [AddrParams set PortMask_] $dmux_ set shift_ [AddrParams set PortShift_] # # point the node's routing entry to itself # at the port demuxer (if there is one) # #if [Simulator set EnableHierRt_] { #$self add-hroute $address_ $dmux_ #} else { #$self add-route $address_ $dmux_ #} } # puts "making dsragent for node [$self id]" set dsr_agent_ [new Agent/DSRAgent] # setup address (supports hier-address) for dsragent $dsr_agent_ addr $address_ $dsr_agent_ node $self if [Simulator set mobile_ip_] { $dsr_agent_ port-dmux [$self set dmux_] } # set up IP address $self addr $address_ if { $RouterTrace == "ON" } { # Recv Target set rcvT [cmu-trace Recv "RTR" $self] $rcvT target $dsr_agent_ set entry_point_ $rcvT } else { # Recv Target set entry_point_ $dsr_agent_ } # # Drop Target (always on regardless of other tracing) # set drpT [cmu-trace Drop "RTR" $self] $dsr_agent_ drop-target $drpT # # Log Target # set T [new Trace/Generic] $T target [$ns_ set nullAgent_] $T attach $tracefd $T set src_ [$self id] $dsr_agent_ log-target $T $dsr_agent_ target $dmux_ # packets to the DSR port should be dropped, since we've # already handled them in the DSRAgent at the entry. set nullAgent_ [$ns_ set nullAgent_] $dmux_ install $opt(rt_port) $nullAgent_ # SRNodes don't use the IP addr classifier. The DSRAgent should # be the entry point $self instvar classifier_ set classifier_ "srnode made illegal use of classifier_" } SRNode instproc start-dsr {} { $self instvar dsr_agent_ global opt; $dsr_agent_ startdsr if {$opt(cc) == "on"} {checkcache $dsr_agent_} } SRNode instproc entry {} { $self instvar entry_point_ return $entry_point_ } SRNode instproc add-interface {args} { # args are expected to be of the form # $chan $prop $tracefd $opt(ll) $opt(mac) global ns ns_ opt RouterTrace eval $self next $args $self instvar dsr_agent_ ll_ mac_ ifq_ $dsr_agent_ mac-addr [$mac_(0) id] if { $RouterTrace == "ON" } { # Send Target set sndT [cmu-trace Send "RTR" $self] $sndT target $ll_(0) $dsr_agent_ add-ll $sndT $ifq_(0) } else { # Send Target $dsr_agent_ add-ll $ll_(0) $ifq_(0) } # setup promiscuous tap into mac layer $dsr_agent_ install-tap $mac_(0) } SRNode instproc reset args { $self instvar dsr_agent_ eval $self next $args $dsr_agent_ reset } # ====================================================================== proc dsr-create-mobile-node { id args } { global ns_ chan prop topo tracefd opt node_ set ns_ [Simulator instance] if {[Simulator set EnableHierRt_]} { if [Simulator set mobile_ip_] { set node_($id) [new SRNode/MIPMH $args] } else { set node_($id) [new SRNode $args] } } else { set node_($id) [new SRNode] } set node $node_($id) $node random-motion 0 ;# disable random motion $node topography $topo # connect up the channel $node add-interface $chan $prop $opt(ll) $opt(mac) \ $opt(ifq) $opt(ifqlen) $opt(netif) $opt(ant) # # This Trace Target is used to log changes in direction # and velocity for the mobile node and log actions of the DSR agent # set T [new Trace/Generic] $T target [$ns_ set nullAgent_] $T attach $tracefd $T set src_ $id $node log-target $T $ns_ at 0.0 "$node start-dsr" return $node } # # Copyright (c) 1996-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. # @(#) $Header: # proc create-base-station-node {address } { global topo tracefd opt node node_ ns_ set ns_ [Simulator instance] if [Simulator set mobile_ip_] { Simulator set node_factory_ MobileNode/MIPBS } else { Simulator set node_factory_ Node/MobileNode/BaseStationNode } #set ns_ $ns set node [$ns_ node $address] set id [$node id] #set node_($id) $node ;#XXX does wireless code use this global # array? $node random-motion 0 ;# disable random motion $node topography $topo # # This Trace Target is used to log changes in direction # and velocity for the mobile node. # set T [new Trace/Generic] $T target [$ns_ set nullAgent_] $T attach $tracefd $T set src_ $id $node log-target $T #$node base-station $node $node base-station [AddrParams set-hieraddr [$node node-addr]] create-$opt(rp)-bs-node $node $id Simulator set node_factory_ Node ;# default value return $node } proc create-dsdv-bs-node {node id} { global ns_ chan prop opt node_ $node instvar regagent_ ragent_ $node add-interface $chan $prop $opt(ll) $opt(mac) \ $opt(ifq) $opt(ifqlen) $opt(netif) \ $opt(ant) create-$opt(rp)-routing-agent $node $id if [info exists regagent_] { $regagent_ ragent $ragent_ } if { $opt(pos) == "Box" } { # # Box Configuration # set spacing 200 set maxrow 7 set col [expr ($id - 1) % $maxrow] set row [expr ($id - 1) / $maxrow] $node set X_ [expr $col * $spacing] $node set Y_ [expr $row * $spacing] $node set Z_ 0.0 $node set speed_ 0.0 $ns_ at 0.0 "$node_($id) start" } } proc create-dsr-bs-node {node id} { global ns_ chan prop opt $node instvar regagent_ ragent_ $node add-interface $chan $prop $opt(ll) $opt(mac) \ $opt(ifq) $opt(ifqlen) $opt(netif) \ $opt(ant) create-$opt(rp)-routing-agent $node $id $node create-xtra-interface if [info exists regagent_] { $regagent_ ragent $ragent_ } $ns_ at 0.0 "$node start-dsr" } proc create-dsr-routing-agent { node id } { global ns_ ragent_ tracefd opt # # Create routing agent and attach it to port 255 # set ragent_($id) [new Agent/DSRAgent/BS_DSRAgent] set ragent $ragent_($id) # setup address (supports hier-addr) for dsdv agent set address [$node node-addr] $ragent addr $address $ragent node $node if [Simulator set mobile_ip_] { $ragent port-dmux [$node set dmux_] } $node addr $address $node set ragent_ $ragent set dmux [$node set dmux_] if {$dmux == "" } { set dmux [new Classifier/Hash/Dest 32] $dmux set mask_ [AddrParams set PortMask_] $dmux set shift_ [AddrParams set PortShift_] # # point the node's routing entry to itself # at the port demuxer (if there is one) # if [Simulator set EnableHierRt_] { $node add-hroute $address $ragent } else { $node add-route $address $ragent } $node set dmux_ $dmux } set level [AddrParams set hlevel_] if { [Simulator set RouterTrace_] == "ON" } { # # Recv Target # set rcvT [cmu-trace Recv "RTR" $node] $rcvT target $ragent for {set i 1} {$i <= $level} {incr i} { [$node set classifiers_($i)] defaulttarget $rcvT [$node set classifiers_($i)] bcast-receiver $rcvT } } else { for {set i 1} {$i <= $level} {incr i} { [$node set classifiers_($i)] defaulttarget $ragent [$node set classifiers_($i)] bcast-receiver $ragent } } # # Drop Target (always on regardless of other tracing) # set drpT [cmu-trace Drop "RTR" $node] $ragent drop-target $drpT # # Log Target # #set T [new Trace/Generic] #$T target [$ns_ set nullAgent_] #$T attach $tracefd #$T set src_ [$node id] #$ragent log-target $T $ragent target $dmux # packets to the DSR port should be handed over to ragent_ # since all pkts now donot go thru ragent $dmux install $opt(rt_port) $ragent } Node/MobileNode/BaseStationNode instproc create-xtra-interface { } { global ns_ opt $self instvar ragent_ ll_ mac_ ifq_ $ragent_ mac-addr [$mac_(0) id] if { [Simulator set RouterTrace_] == "ON" } { # Send Target set sndT [cmu-trace Send "RTR" $self] $sndT target $ll_(0) $ragent_ add-ll $sndT $ifq_(0) } else { # Send Target $ragent_ add-ll $ll_(0) $ifq_(0) } # setup promiscuous tap into mac layer $ragent_ install-tap $mac_(0) } Node/MobileNode/BaseStationNode instproc start-dsr {} { $self instvar ragent_ global opt; $ragent_ startdsr if {$opt(cc) == "on"} {checkcache $dsr_agent_} } Node/MobileNode/BaseStationNode instproc reset args { $self instvar ragent_ eval $self next $args $ragent_ reset } #Class God #God instproc init {args} { # eval $self next $args #} proc create-god { nodes } { #global ns_ god_ tracefd set god [God info instances] if { $god == "" } { set god [new God] } $god num_nodes $nodes return $god } God proc instance {} { set god [God info instances] if { $god != "" } { return $god } error "Cannot find instance of god" } proc cmu-trace { ttype atype node } { global ns_ tracefd if { $tracefd == "" } { return "" } set T [new CMUTrace/$ttype $atype] $T target [$ns_ set nullAgent_] $T attach $tracefd $T set src_ [$node id] $T node $node return $T } proc log-movement {} { global logtimer ns_ ns set ns $ns_ source ../mobility/timer.tcl Class LogTimer -superclass Timer LogTimer instproc timeout {} { global opt node_; for {set i 0} {$i < $opt(nn)} {incr i} { $node_($i) log-movement } $self sched 0.1 } set logtimer [new LogTimer] $logtimer sched 0.1 } proc set-wireless-traces { args } { set len [llength $args] if { $len <= 0 || [expr $len%2] } { error "Incorrect number of parameters" } for {set n 0} {$n < $len} {incr n 2} { if {[string compare [lindex $args $n] "-AgentTrace"] == 0 } { Simulator set AgentTrace_ [lindex $args [expr $n+1]] } elseif {[string compare [lindex $args $n] "-RouterTrace"] == 0 } { Simulator set RouterTrace_ [lindex $args [expr $n+1]] } elseif {[string compare [lindex $args $n] "-MacTrace"] == 0 } { Simulator set MacTrace_ [lindex $args [expr $n+1]] } else { error "Unknown wireless trace type: [lindex $args $n]" } } } # # 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. # # @(#) $Header$ # # # Set up all the default paramters. Each default parameter # is stored in the OTcl class template and copied from the # class into the instance when the object is created # (this happens in the Tcl/tcl-object.tcl helper library) # # Increased Floating Point Precision set tcl_precision 17 Connector set debug_ false TTLChecker set debug_ false Trace set src_ -1 Trace set dst_ -1 Trace set callback_ 0 Trace set show_tcphdr_ 0 Trace set debug_ false CMUTrace set debug_ false Agent set fid_ 0 Agent set prio_ 0 Agent set agent_addr_ -1 Agent set agent_port_ -1 Agent set dst_addr_ -1 Agent set dst_port_ -1 Agent set flags_ 0 Agent set ttl_ 32 ; # arbitrary choice here Agent set debug_ false Agent set class_ 0 Scheduler/RealTime set maxslop_ 0.010; # max allowed slop b4 error (sec) ##Agent set seqno_ 0 now is gone ##Agent set class_ 0 now is gone Agent/Ping set packetSize_ 64 Agent/UDP set packetSize_ 1000 Agent/UDP instproc done {} { } Agent/TCP set seqno_ 0 Agent/TCP set t_seqno_ 0 Agent/TCP set maxburst_ 0 Agent/TCP set maxcwnd_ 0 Agent/TCP set window_ 20 Agent/TCP set windowInit_ 1 Agent/TCP set windowInitOption_ 1 Agent/TCP set syn_ false Agent/TCP set windowOption_ 1 Agent/TCP set windowConstant_ 4 Agent/TCP set windowThresh_ 0.002 Agent/TCP set decrease_num_ 0.5 Agent/TCP set increase_num_ 1.0 Agent/TCP set overhead_ 0 Agent/TCP set ecn_ 0 Agent/TCP set old_ecn_ 0 Agent/TCP set packetSize_ 1000 Agent/TCP set tcpip_base_hdr_size_ 40 Agent/TCP set bugFix_ true Agent/TCP set timestamps_ false Agent/TCP set slow_start_restart_ true Agent/TCP set restart_bugfix_ true Agent/TCP set tcpTick_ 0.1 Agent/TCP set maxrto_ 100000 Agent/TCP set srtt_init_ 0 Agent/TCP set rttvar_init_ 12 Agent/TCP set rtxcur_init_ 6.0 Agent/TCP set T_SRTT_BITS 3 Agent/TCP set T_RTTVAR_BITS 2 Agent/TCP set rttvar_exp_ 2 Agent/TCP instproc done {} { } Agent/TCP set dupacks_ 0 Agent/TCP set ack_ 0 Agent/TCP set cwnd_ 0 Agent/TCP set awnd_ 0 Agent/TCP set ssthresh_ 0 Agent/TCP set rtt_ 0 Agent/TCP set srtt_ 0 Agent/TCP set rttvar_ 0 Agent/TCP set backoff_ 0 Agent/TCP set maxseq_ 0 Agent/TCP set singledup_ 0 Agent/TCP set ndatapack_ 0 Agent/TCP set ndatabytes_ 0 Agent/TCP set nackpack_ 0 Agent/TCP set nrexmit_ 0 Agent/TCP set nrexmitpack_ 0 Agent/TCP set nrexmitbytes_ 0 Agent/TCP set trace_all_oneline_ false Agent/TCP set QOption_ 0 Agent/TCP set EnblRTTCtr_ 0 Agent/TCP set control_increase_ 0 # XXX Generate nam trace or plain old text trace for variables. # When it's true, generate nam trace. Agent/TCP set nam_tracevar_ false Agent/TCP/Fack set ss-div4_ false Agent/TCP/Fack set rampdown_ false Agent/TCP set eln_ 0 Agent/TCP set eln_rxmit_thresh_ 1 Agent/TCP set delay_growth_ false Agent/TCP set CoarseTimer_ 0 Agent/TCPSink set sport_ 0 Agent/TCPSink set dport_ 0 #XXX other kinds of sinks -> should reparent Agent/TCPSink set packetSize_ 40 Agent/TCPSink set maxSackBlocks_ 3 Agent/TCPSink set ts_echo_bugfix_ false Agent/TCPSink set generateDSacks_ false Agent/TCPSink set RFC2581_immediate_ack_ true Agent/TCPSink/DelAck set interval_ 100ms catch { Agent/TCPSink/Asym set interval_ 100ms Agent/TCPSink/Asym set maxdelack_ 5 } Agent/TCPSink/Sack1/DelAck set interval_ 100ms # setting this to 1 implements some changes to reno # proposed by Janey Hoe (other than fixing reno's # unnecessary retransmit timeouts) Agent/TCP/Newreno set newreno_changes_ 0 # setting this to 1 allows the retransmit timer to expire for # a window with many packet drops Agent/TCP/Newreno set newreno_changes1_ 0 Agent/TCP/Newreno set partial_window_deflation_ 0 Agent/TCP/Newreno set exit_recovery_fix_ 0 Agent/TCP/Vegas set v_alpha_ 1 Agent/TCP/Vegas set v_beta_ 3 Agent/TCP/Vegas set v_gamma_ 1 Agent/TCP/Vegas set v_rtt_ 0 Agent/TCP/Vegas/RBP set rbp_scale_ 0.75 # rbp_rate_algorithm_'s are defined in tcp-rbp.cc. # 1=RBP_VEGAS_RATE_ALGORITHM (default), # 2=RBP_CWND_ALGORITHM Agent/TCP/Vegas/RBP set rbp_rate_algorithm_ 1 Agent/TCP/Vegas/RBP set rbp_segs_actually_paced_ 0 Agent/TCP/Vegas/RBP set rbp_inter_pace_delay_ 0 Agent/TCP/Reno/RBP set rbp_scale_ 0.75 Agent/TCP/Reno/RBP set rbp_segs_actually_paced_ 0 Agent/TCP/Reno/RBP set rbp_inter_pace_delay_ 0 # Reno/RBP supports only RBP_CWND_ALGORITHM # Agent/TCP/Reno/RBP set rbp_rate_algorithm_ 2 Agent/TCP/Asym set g_ 0.125 Agent/TCP/Reno/Asym set g_ 0.125 Agent/TCP/Newreno/Asym set g_ 0.125 # RFC793eduTcp -- 19990820, fcela@acm.org Agent/TCP/RFC793edu set add793expbackoff_ true Agent/TCP/RFC793edu set add793jacobsonrtt_ false Agent/TCP/RFC793edu set add793fastrtx_ false Agent/TCP/RFC793edu set add793slowstart_ false Agent/TCP/RFC793edu set add793additiveinc_ false Agent/TCP/RFC793edu set add793karnrtt_ true Agent/TCP/RFC793edu set rto_ 60 Agent/TCP/RFC793edu set syn_ true Agent/TCP/RFC793edu set add793exponinc_ false Agent/TFRC set packetSize_ 1000 Agent/TFRC set df_ 0.95 ; # decay factor for accurate RTT estimate Agent/TFRC set tcp_tick_ 0.1 ; Agent/TFRC set ndatapack_ 0 ; # Number of packets sent Agent/TFRC set srtt_init_ 0 ; # Variables for tracking RTT Agent/TFRC set rttvar_init_ 12 Agent/TFRC set rtxcur_init_ 6.0 Agent/TFRC set rttvar_exp_ 2 Agent/TFRC set T_SRTT_BITS 3 Agent/TFRC set T_RTTVAR_BITS 2 Agent/TFRC set InitRate_ 300 ; # Initial send rate Agent/TFRC set overhead_ 0 ; # If > 0, dither outgoing packets Agent/TFRC set ssmult_ 2 ; # Rate of increase during slow-start: Agent/TFRC set bval_ 1 ; # Value of B for TCP formula Agent/TFRC set ca_ 1 ; # Enable Sqrt(RTT) congestion avoidance Agent/TFRC set printStatus_ 0 Agent/TFRCSink set packetSize_ 40 Agent/TFRCSink set InitHistorySize_ 100000 Agent/TFRCSink set NumFeedback_ 1 Agent/TFRCSink set AdjustHistoryAfterSS_ 1 Agent/TFRCSink set NumSamples_ -1 Agent/TFRCSink set discount_ 1; # History Discounting Agent/TFRCSink set printLoss_ 0 Agent/TFRCSink set smooth_ 1 ; # smoother Average Loss Interval if [TclObject is-class Agent/TCP/FullTcp] { Agent/TCP/FullTcp set segsperack_ 1; # ACK frequency Agent/TCP/FullTcp set segsize_ 536; # segment size Agent/TCP/FullTcp set tcprexmtthresh_ 3; # num dupacks to enter recov Agent/TCP/FullTcp set iss_ 0; # Initial send seq# Agent/TCP/FullTcp set nodelay_ false; # Nagle disable? Agent/TCP/FullTcp set data_on_syn_ false; # allow data on 1st SYN? Agent/TCP/FullTcp set dupseg_fix_ true ; # no rexmt w/dup segs from peer Agent/TCP/FullTcp set dupack_reset_ false; # exit recov on ack < highest Agent/TCP/FullTcp set interval_ 0.1 ; # delayed ACK interval 100ms Agent/TCP/FullTcp set close_on_empty_ false; # close conn if sent all Agent/TCP/FullTcp set ts_option_size_ 10; # in bytes Agent/TCP/FullTcp set reno_fastrecov_ true; # fast recov true by default Agent/TCP/FullTcp set pipectrl_ false; # use "pipe" ctrl Agent/TCP/FullTcp set open_cwnd_on_pack_ true; # ^ win on partial acks? Agent/TCP/FullTcp set halfclose_ false; # do simplex closes (shutdown)? Agent/TCP/FullTcp/Newreno set recov_maxburst_ 2; # max burst dur recov Agent/TCP/FullTcp/Sack set sack_block_size_ 8; # bytes in a SACK block Agent/TCP/FullTcp/Sack set sack_option_size_ 2; # bytes in opt hdr Agent/TCP/FullTcp/Sack set max_sack_blocks_ 3; # max # of sack blks } # Default values used by wireless simulations Agent/Null set sport_ 0 Agent/Null set dport_ 0 Agent/CBR set sport_ 0 Agent/CBR set dport_ 0 # Http invalidation agent Agent/HttpInval set inval_hdr_size_ 40 Agent/RTP set seqno_ 0 Agent/RTP set interval_ 3.75ms Agent/RTP set random_ 0 Agent/RTP set packetSize_ 210 Agent/RTP set maxpkts_ 0x10000000 Agent/RTP instproc done {} { } Agent/RTCP set seqno_ 0 Agent/Message set packetSize_ 180 Agent/LossMonitor set nlost_ 0 Agent/LossMonitor set npkts_ 0 Agent/LossMonitor set bytes_ 0 Agent/LossMonitor set lastPktTime_ 0 Agent/LossMonitor set expected_ 0 # RAP Agent/RAP set packetSize_ 512 Agent/RAP set seqno_ 0 Agent/RAP set sessionLossCount_ 0 Agent/RAP set ipg_ 2.0 Agent/RAP set alpha_ 1.0 Agent/RAP set beta_ 0.5 Agent/RAP set srtt_ 2.0 Agent/RAP set variance_ 0.0 Agent/RAP set delta_ 0.5 Agent/RAP set mu_ 1.2 Agent/RAP set phi_ 4.0 Agent/RAP set timeout_ 2.0 Agent/RAP set overhead_ 0 Agent/RAP set useFineGrain_ 0 Agent/RAP set kfrtt_ 0.9 Agent/RAP set kxrtt_ 0.01 Agent/RAP set debugEnable_ 0 Agent/RAP set rap_base_hdr_size_ 44 Agent/RAP set dpthresh_ 50 Agent/RAP instproc done {} { } # Routing protocol agents Agent/Mcast/Control set packetSize_ 80 # Dynamic routing defaults Agent/rtProto set preference_ 200 ;# global default preference Agent/rtProto/Direct set preference_ 100 Agent/rtProto/DV set preference_ 120 Agent/rtProto/DV set INFINITY [Agent set ttl_] Agent/rtProto/DV set advertInterval 2 #Agent/Decapsulator set off_encap_ 0 #Agent/Encapsulator set off_encap_ 0 Agent/Encapsulator set status_ 1 Agent/Encapsulator set overhead_ 20 Integrator set lastx_ 0.0 Integrator set lasty_ 0.0 Integrator set sum_ 0.0 # 10->50 to be like ns-1 Queue set limit_ 50 Queue set blocked_ false Queue set unblock_on_resume_ true Queue set interleave_ false Queue set acksfirst_ false Queue set ackfromfront_ false Queue set debug_ false Queue/SFQ set maxqueue_ 40 Queue/SFQ set buckets_ 16 Queue/FQ set secsPerByte_ 0 # change DropTail to RED for RED on individual queues FQLink set queueManagement_ DropTail Queue/DropTail set drop_front_ false Queue/DropTail/PriQueue set Prefer_Routing_Protocols 1 Queue/RED set bytes_ false Queue/RED set queue_in_bytes_ false Queue/RED set thresh_ 5 Queue/RED set maxthresh_ 15 Queue/RED set mean_pktsize_ 500 Queue/RED set q_weight_ 0.002 Queue/RED set wait_ true Queue/RED set linterm_ 10 Queue/RED set setbit_ false Queue/RED set gentle_ false Queue/RED set drop_tail_ true Queue/RED set drop_front_ false Queue/RED set drop_rand_ false Queue/RED set doubleq_ false Queue/RED set ns1_compat_ false Queue/RED set dqthresh_ 50 Queue/RED set ave_ 0.0 Queue/RED set prob1_ 0.0 Queue/RED set curq_ 0 Queue/DRR set buckets_ 10 Queue/DRR set blimit_ 25000 Queue/DRR set quantum_ 250 Queue/DRR set mask_ 0 Queue/CBQ set algorithm_ 0 ;# used by compat only, not bound Queue/CBQ set maxpkt_ 1024 CBQClass set priority_ 0 CBQClass set level_ 1 CBQClass set extradelay_ 0.0 CBQClass set def_qtype_ DropTail CBQClass set okborrow_ true CBQClass set automaxidle_gain_ 0.9375 CBQClass set debug_ false SnoopQueue/In set debug_ false SnoopQueue/Out set debug_ false SnoopQueue/Drop set debug_ false SnoopQueue/EDrop set debug_ false PacketQueue/Semantic set acksfirst_ false PacketQueue/Semantic set filteracks_ false PacketQueue/Semantic set replace_head_ false PacketQueue/Semantic set priority_drop_ false PacketQueue/Semantic set random_drop_ false PacketQueue/Semantic set reconsAcks_ false PacketQueue/Semantic set random_ecn_ false DelayLink set bandwidth_ 1.5Mb DelayLink set delay_ 100ms DelayLink set debug_ false DynamicLink set status_ 1 DynamicLink set debug_ false Filter set debug_ false Filter/Field set offset_ 0 Filter/Field set match_ -1 # these are assigned when created Classifier set offset_ 0 Classifier set shift_ 0 Classifier set mask_ 0xffffffff Classifier set debug_ false Classifier/Hash set default_ -1; # none Classifier/Replicator set ignore_ 0 ErrorModule set debug_ false ErrorModel set enable_ 1 ErrorModel set markecn_ false ErrorModel set rate_ 0 ErrorModel set bandwidth_ 2Mb ErrorModel set debug_ false ErrorModel/Trace set good_ 123456789 ErrorModel/Trace set loss_ 0 ErrorModel/Periodic set period_ 1.0 ErrorModel/Periodic set offset_ 0.0 ErrorModel/Periodic set burstlen_ 0.0 ErrorModel/MultiState set curperiod_ 0.0 ErrorModel/MultiState set sttype_ pkt ErrorModel/MultiState set texpired_ 0 SelectErrorModel set enable_ 1 SelectErrorModel set markecn_ false SelectErrorModel set rate_ 0 SelectErrorModel set bandwidth_ 2Mb SelectErrorModel set pkt_type_ 2 SelectErrorModel set drop_cycle_ 10 SelectErrorModel set drop_offset_ 1 SelectErrorModel set debug_ false SRMErrorModel set enable_ 1 SRMErrorModel set markecn_ false SRMErrorModel set rate_ 0 SRMErrorModel set bandwidth_ 2Mb SRMErrorModel set pkt_type_ 2 SRMErrorModel set drop_cycle_ 10 SRMErrorModel set drop_offset_ 1 SRMErrorModel set debug_ false #MrouteErrorModel set enable_ 1 #MrouteErrorModel set rate_ 0 #MrouteErrorModel set bandwidth_ 2Mb #MrouteErrorModel set pkt_type_ 2 #MrouteErrorModel set drop_cycle_ 10 #MrouteErrorModel set drop_offset_ 1 #MrouteErrorModel set good_ 99999999 #MrouteErrorModel set loss_ 0 QueueMonitor set size_ 0 QueueMonitor set pkts_ 0 QueueMonitor set parrivals_ 0 QueueMonitor set barrivals_ 0 QueueMonitor set pdepartures_ 0 QueueMonitor set bdepartures_ 0 QueueMonitor set pdrops_ 0 QueueMonitor set bdrops_ 0 QueueMonitor/ED set epdrops_ 0 QueueMonitor/ED set ebdrops_ 0 QueueMonitor/ED/Flowmon set enable_in_ true QueueMonitor/ED/Flowmon set enable_out_ true QueueMonitor/ED/Flowmon set enable_drop_ true QueueMonitor/ED/Flowmon set enable_edrop_ true QueueMonitor/ED/Flow set src_ -1 QueueMonitor/ED/Flow set dst_ -1 QueueMonitor/ED/Flow set flowid_ -1 Application/Traffic/Exponential set burst_time_ .5 Application/Traffic/Exponential set idle_time_ .5 Application/Traffic/Exponential set rate_ 64Kb Application/Traffic/Exponential set packetSize_ 210 Application/Traffic/Pareto set burst_time_ 500ms Application/Traffic/Pareto set idle_time_ 500ms Application/Traffic/Pareto set rate_ 64Kb Application/Traffic/Pareto set packetSize_ 210 Application/Traffic/Pareto set shape_ 1.5 Application/Traffic/CBR set rate_ 448Kb ;# corresponds to interval of 3.75ms Application/Traffic/CBR set packetSize_ 210 Application/Traffic/CBR set random_ 0 Application/Traffic/CBR set maxpkts_ 268435456; # 0x10000000 Application/Telnet set interval_ 1.0 RandomVariable/Uniform set min_ 0.0 RandomVariable/Uniform set max_ 1.0 RandomVariable/Exponential set avg_ 1.0 RandomVariable/Pareto set avg_ 1.0 RandomVariable/Pareto set shape_ 1.5 RandomVariable/ParetoII set avg_ 10.0 RandomVariable/ParetoII set shape_ 1.2 RandomVariable/Constant set val_ 1.0 RandomVariable/HyperExponential set avg_ 1.0 RandomVariable/HyperExponential set cov_ 4.0 RandomVariable/Empirical set minCDF_ 0 RandomVariable/Empirical set maxCDF_ 1 RandomVariable/Empirical set interpolation_ 0 RandomVariable/Empirical set maxEntry_ 32 RandomVariable/Normal set avg_ 0.0 RandomVariable/Normal set std_ 1.0 RandomVariable/LogNormal set avg_ 1.0 RandomVariable/LogNormal set std_ 1.0 SessionHelper set rc_ 0 ;# just to eliminate warnings SessionHelper set debug_ false NetworkInterface set debug_ false ADC/MS set debug_ false ADC/HB set debug_ false ADC/Param set debug_ false ADC/ACTP set debug_ false ADC/ACTO set debug_ false Est/Null set debug_ false Est/TimeWindow set debug_ false Est/ExpAvg set debug_ false Est/PointSample set debug_ false MeasureMod set debug_ false SALink set debug_ false # # The following are defautls for objects that are not necessarily TclObjects # Node set multiPath_ 0 #### Bits are allocated for different fields like port, nodeid, mcast, hierarchical-levels #### All Mask and Shift values are stored in Class AddrParams. AddrParams set ALL_BITS_SET 0xffffffff AddrParams set PortShift_ 0 AddrParams set PortMask_ [AddrParams set ALL_BITS_SET] Agent/MIPBS set adSize_ 48 Agent/MIPBS set shift_ 0 Agent/MIPBS set mask_ [AddrParams set ALL_BITS_SET] Agent/MIPBS set ad_lifetime_ 2 Agent/MIPMH set home_agent_ 0 Agent/MIPMH set rreqSize_ 52 Agent/MIPMH set reg_rtx_ 0.5 Agent/MIPMH set shift_ 0 Agent/MIPMH set mask_ [AddrParams set ALL_BITS_SET] Agent/MIPMH set reg_lifetime_ 2 #### Default and Maximum Address space - leaving the MSB as signed bit AllocAddrBits set DEFADDRSIZE_ 32 AllocAddrBits set MAXADDRSIZE_ 32 ;# leaving the signed bit Simulator set node_factory_ Node Simulator set nsv1flag 0 #Simulator set mn_ 0 ;# counter for mobile nodes Simulator set mobile_ip_ 0 ;# flag for mobileIP Simulator set EnableHierRt_ 0 ;# is hierarchical routing on? (to turn it on, call set-hieraddress) SessionSim set rc_ 0 ;# to enable packet reference count ### Default settings for Hierarchical topology AddrParams set domain_num_ 1 AddrParams set def_clusters 4 AddrParams set def_nodes 5 # Defaults for unicast addresses # While changing these, ensure that the values are consistent in config.h # Simulator set NodeMask_ 0xffffff # Simulator set NodeShift_ 8 # Simulator set NodeMask_(1) 0xffffff # Simulator set NodeShift_(1) 8 # Simulator set PortMask_ 0xff # Defaults for multicast addresses #Simulator set McastShift_ 15 Simulator set McastBaseAddr_ 0x80000000 Simulator set McastAddr_ 0x80000000 rtModel set startTime_ 0.5 rtModel set finishTime_ "-" rtModel/Exponential set upInterval_ 10.0 rtModel/Exponential set downInterval_ 1.0 rtModel/Deterministic set upInterval_ 2.0 rtModel/Deterministic set downInterval_ 1.0 # SRM Agent defaults are in ../tcl/mcast/srm.tcl and ../mcast/srm-adaptive.tcl #IntServ Object specific defaults are in ../tcl/lib/ns-intserv.tcl # defaults for tbf TBF set rate_ 64k TBF set bucket_ 1024 TBF set qlen_ 0 #mobile Ip MIPEncapsulator set addr_ 0 MIPEncapsulator set port_ 0 MIPEncapsulator set shift_ 0 MIPEncapsulator set mask_ [AddrParams set ALL_BITS_SET] MIPEncapsulator set ttl_ 32 MIPEncapsulator set debug_ false # HTTP-related defaults are in ../tcl/webcache/http-agent.tcl Node/MobileNode set REGAGENT_PORT 0 Node/MobileNode set DECAP_PORT 1 # Default values used for wireless simulations Simulator set AgentTrace_ ON Simulator set RouterTrace_ OFF Simulator set MacTrace_ OFF Mac set debug_ false ARPTable set debug_ false God set debug_ false LL set mindelay_ 50us LL set delay_ 25us LL set bandwidth_ 0 ;# not used LL set off_prune_ 0 ;# not used LL set off_CtrMcast_ 0 ;# not used LL set debug_ false # unity gain, omni-directional antennas # set up the antennas to be centered in the node and 1.5 meters above it Antenna/OmniAntenna set X_ 0 Antenna/OmniAntenna set Y_ 0 Antenna/OmniAntenna set Z_ 1.5 Antenna/OmniAntenna set Gt_ 1.0 Antenna/OmniAntenna set Gr_ 1.0 # Initialize the SharedMedia interface with parameters to make # it work like the 914MHz Lucent WaveLAN DSSS radio interface Phy/WirelessPhy set CPThresh_ 10.0 Phy/WirelessPhy set CSThresh_ 1.559e-11 Phy/WirelessPhy set RXThresh_ 3.652e-10 Phy/WirelessPhy set Rb_ 2*1e6 Phy/WirelessPhy set Pt_ 0.2818 Phy/WirelessPhy set freq_ 914e+6 Phy/WirelessPhy set L_ 1.0 Phy/WirelessPhy set debug_ false Phy/WiredPhy set bandwidth_ 10e6 Phy/WiredPhy set debug_ false Phy/Repeater set debug_ false LanRouter set debug_ false Phy/Sat set debug_ false Mac/Sat set debug_ false LL/Sat set debug_ false # # ns-emulate.tcl # defaults for nse [ns with emulation support] # if [TclObject is-class Network/Pcap/Live] { Network/Pcap/Live set snaplen_ 4096;# bpf snap len Network/Pcap/Live set promisc_ false; Network/Pcap/Live set timeout_ 0 Network/Pcap/Live set optimize_ true;# bpf code optimizer Network/Pcap/Live set offset_ 0.0; # Network/Pcap/File set offset_ 0.0; # ts for 1st pkt in trace file } if [TclObject is-class Agent/Tap] { Agent/Tap set maxpkt_ 1600 } if [TclObject is-class Agent/IcmpAgent] { Agent/IcmpAgent set ttl_ 254 } if [TclObject is-class ArpAgent] { ArpAgent set cachesize_ 10; # entries in arp cache ArpAgent instproc init {} { $self next } ArpAgent instproc config ifname { $self instvar net_ myether_ myip_ set net_ [new Network/Pcap/Live] $net_ open readwrite $ifname set myether_ [$net_ linkaddr] set myip_ [$net_ netaddr] $net_ filter "arp and (not ether src $myether_) and \ (ether dst $myether_ \ or ether dst ff:ff:ff:ff:ff:ff)" $self cmd network $net_ $self cmd myether $myether_ $self cmd myip $myip_ } } Simulator instproc init args { $self create_packetformat #the calendar scheduler doesn't work on big mobile network runs #it dies around 240 secs... #$self use-scheduler List $self use-scheduler Calendar $self set nullAgent_ [new Agent/Null] $self set-address-format def eval $self next $args } Simulator instproc nullagent {} { $self instvar nullAgent_ return $nullAgent_ } Simulator instproc use-scheduler type { $self instvar scheduler_ if [info exists scheduler_] { if { [$scheduler_ info class] == "Scheduler/$type" } { return } else { delete $scheduler_ } } set scheduler_ [new Scheduler/$type] $scheduler_ now } # # A simple method to wrap any object around # a trace object that dumps to stdout # Simulator instproc dumper obj { set t [$self alloc-trace hop stdout] $t target $obj return $t } # New node structure # Define global node configuration # $ns_ node-config -addressType flat/hierarchical # -adhocRouting DSDV/DSR/TORA # -llType # -macType # -propType # -ifqType # -ifqLen # -phyType # -antType # -channelType # -topologyInstance # -wiredRouting ON/OFF # -mobileIP ON/OFF # -energyModel "EnergyModel" # -initialEnergy (in Joules) # -rxPower (in W) # -txPower (in W) # -agentTrace ON # -routerTrace ON # -macTrace OFF # -toraDebug OFF # -movementTrace OFF Simulator set routingAgent_ "" Simulator set addressType_ "" #Simulator set llType_ "" #Simulator set macType_ "" #Simulator set propType_ "" #Simulator set ifqType_ "" #Simulator set phyType_ "" #Simulator set antType_ "" #Simulator set ifqlen_ "" Simulator set nodefactory_ Node Simulator set MovementTrace OFF Simulator instproc addressType {val} { $self set addressType_ $val } Simulator instproc adhocRouting {val} { $self set routingAgent_ $val } Simulator instproc llType {val} { $self set llType_ $val } Simulator instproc macType {val} { $self set macType_ $val } Simulator instproc propType {val} { $self set propType_ $val } Simulator instproc ifqType {val} { $self set ifqType_ $val } Simulator instproc ifqLen {val} { $self set ifqlen_ $val } Simulator instproc phyType {val} { $self set phyType_ $val } Simulator instproc antType {val} { $self set antType_ $val } Simulator instproc channelType {val} {$self set channelType_ $val} Simulator instproc topoInstance {val} {$self set topoInstance_ $val} Simulator instproc wiredRouting {val} {$self set wiredRouting_ $val} Simulator instproc mobileIP {val} {$self set mobileIP_ $val} Simulator instproc energyModel {val} { $self set energyModel_ $val } Simulator instproc initialEnergy {val} { $self set initialEnergy_ $val } Simulator instproc txPower {val} { $self set txPower_ $val } Simulator instproc rxPower {val} { $self set rxPower_ $val } Simulator instproc agentTrace {val} { $self set agentTrace_ $val } Simulator instproc routerTrace {val} { $self set routerTrace_ $val } Simulator instproc macTrace {val} { $self set macTrace_ $val } Simulator instproc movementTrace {val} { $self set movementTrace_ $val } Simulator instproc toraDebug {val} {$self set toraDebug_ $val } Simulator instproc get-nodetype {} { $self instvar addressType_ routingAgent_ wiredRouting_ set val "" if { [info exists addressType_] && $addressType_ == "hierarchical" } { set val Hier } if { [info exists routingAgent_] && $routingAgent_ != "" } { set val Mobile } if { [info exists wiredRouting_] && $wiredRouting_ == "ON" } { set val Base } if { [info exists wiredRouting_] && $wiredRouting_ == "OFF"} { set val Base } if { [Simulator set mobile_ip_] } { if { $val == "Base" && $wiredRouting_ == "ON" } { set val MIPBS } if { $val == "Base" && $wiredRouting_ == "OFF" } { set val MIPMH } } return $val } Simulator instproc node-config args { set args [eval $self init-vars $args] $self instvar addressType_ routingAgent_ nodefactory_ propType_ $self instvar macTrace_ routerTrace_ agentTrace_ movementTrace_ $self instvar channelType_ topoInstance_ propInstance_ chan mobileIP_ $self instvar rxPower_ txPower_ if [info exists macTrace_] { Simulator set MacTrace_ $macTrace_ } if [info exists routerTrace_] { Simulator set RouterTrace_ $routerTrace_ } if [info exists agentTrace_] { Simulator set AgentTrace_ $agentTrace_ } if [info exists movementTrace_] { Simulator set MovementTrace_ $movementTrace_ } # hacking for matching old cmu add-interface # not good style, for back-compability ONLY # Only create 1 instance of prop if {[info exists propType_] && ![info exists propInstance_]} { set propInstance_ [new $propType_] } if {[info exists channelType_] && ![info exists chan]} { set chan [new $channelType_] } if [info exists topoInstance_] { $propType_ topography $topoInstance_ } # set address type, hierarchical or expanded if {[string compare $addressType_ ""] != 0} { $self set-address-format $addressType_ } # set mobileIP flag if { [info exists mobileIP_] && $mobileIP_ == "ON"} { Simulator set mobile_ip_ 1 } else { if { [info exists mobileIP_] } { Simulator set mobile_ip_ 0 } } } # Default behavior is changed: consider nam as not initialized if # no shape OR color parameter is given Simulator instproc node args { $self instvar Node_ routingAgent_ wiredRouting_ if { [Simulator info vars EnableMcast_] != "" } { warn "Flag variable Simulator::EnableMcast_ discontinued.\n\t\ Use multicast methods as:\n\t\t\ % set ns \[new Simulator -multicast on]\n\t\t\ % \$ns multicast" $self multicast Simulator unset EnableMcast_ } if { [Simulator info vars NumberInterfaces_] != "" } { warn "Flag variable Simulator::NumberInterfaces_ discontinued.\n\t\ Setting (or not) this variable will not affect the simulations." Simulator unset NumberInterfaces_ } # wireless-ready node if [info exists routingAgent_] { if {[string compare $routingAgent_ ""] != 0} { set node [$self create-wireless-node $args] # for base node if {[info exists wiredRouting_] && $wiredRouting_ == "ON"} { set Node_([$node id]) $node } return $node } } set node [new [Simulator set node_factory_] $args] set Node_([$node id]) $node $node set ns_ $self if [$self multicast?] { $node enable-mcast $self } $self check-node-num return $node } Simulator instproc imep-support {} { $self instvar imepflag_ if [info exists imepflag_] { return $imepflag_ } return "" } Simulator instproc create-wireless-node { args } { $self instvar routingAgent_ wiredRouting_ $self instvar propInstance_ llType_ macType_ ifqType_ ifqlen_ phyType_ chan $self instvar antType_ energyModel_ initialEnergy_ txPower_ rxPower_ $self instvar imepflag_ topoInstance_ $self instvar namtraceAllFile_ $self instvar level1_ level2_ set imepflag_ OFF # create node instance set node [$self create-node-instance $args] if { [info exist namtraceAllFile_] } { $node get-nam-traceall $namtraceAllFile_ $node namDefined } # basestation address setting if { [info exist wiredRouting_] && $wiredRouting_ == "ON" } { $node base-station [AddrParams set-hieraddr [$node node-addr]] } $node nodeid [$node id] switch -exact $routingAgent_ { DSDV { set ragent [$self create-dsdv-agent $node] } DSR { $self at 0.0 "$node start-dsr" } AODV { set ragent [$self create-aodv-agent $node] } TORA { set imepflag_ ON set ragent [$self create-tora-agent $node] } default { puts "Wrong node routing agent!" exit } } # add main node interface $node add-interface $chan $propInstance_ $llType_ $macType_ \ $ifqType_ $ifqlen_ $phyType_ $antType_ # attach agent if {$routingAgent_ != "DSR"} { $node attach $ragent 255 } # bind routing agent and mip agent if existing # basestation address setting if { [info exist wiredRouting_] && $wiredRouting_ == "ON" } { if { $routingAgent_ != "DSR" } { $node mip-call $ragent } } # # This Trace Target is used to log changes in direction # and velocity for the mobile node. # set tracefd [$self get-ns-traceall] if {$tracefd != "" } { $node nodetrace $tracefd $node agenttrace $tracefd } set namtracefd [$self get-nam-traceall] if {$namtracefd != "" } { $node namattach $namtracefd } if [info exists energyModel_] { if [info exists level1_] { set l1 $level1_ } else { set l1 0.5 } if [info exists level2_] { set l2 $level2_ } else { set l2 0.2 } $node addenergymodel [new $energyModel_ $initialEnergy_ $l1 $l2] } if [info exists txPower_] { $node setPt $txPower_ } if [info exists rxPower_] { $node setPr $rxPower_ } $node topography $topoInstance_ return $node } #Class BaseNode -superclass {HierNode Node/MobileNode} Simulator instproc create-node-instance { args } { $self instvar routingAgent_ set nodeclass Node/MobileNode # DSR is a special case if {$routingAgent_ == "DSR"} { set nodeclass [$self set-dsr-nodetype] } if {$args != "{}" && $args != "{{}}"} { set node [new $nodeclass $args] } else { set node [new $nodeclass] } return $node } Simulator instproc set-dsr-nodetype {} { $self instvar wiredRouting_ set nodetype SRNodeNew # MIP mobilenode if [Simulator set mobile_ip_] { set nodetype SRNodeNew/MIPMH } # basestation dsr node if { [info exists wiredRouting_] && $wiredRouting_ == "ON"} { set nodetype Node/MobileNode/BaseStationNode } return $nodetype } Simulator instproc create-tora-agent { node } { set ragent [new Agent/TORA [$node id]] #delay till after add interface # $node attach $ragent 255 #$ragent if-queue [$node set ifq_(0)] ;# ifq between LL and MAC # # XXX: The routing protocol and the IMEP agents needs handles # to each other. # #$ragent imep-agent [$node set imep_(0)] #[$node set imep_(0)] rtagent $ragent # # Drop Target (always on regardless of other tracing) # #set drpT [$self mobility-trace Drop "RTR" $node] #$ragent drop-target $drpT #set tracefd [$self get-ns-traceall] #if {$tracefd != "" } { # # Log Target # # set T [new Trace/Generic] # $T target [$ns_ set nullAgent_] # $T attach $tracefd # $T set src_ $id # $ragent log-target $T #} # # XXX: let the IMEP agent use the same log target. # #[$node set imep_(0)] log-target $T $node set ragent_ $ragent return $ragent } Simulator instproc create-dsdv-agent { node } { # Create a dsdv routing agent for this node set ragent [new Agent/DSDV] ## setup address (supports hier-addr) for dsdv agent ## and mobilenode set addr [$node node-addr] $ragent addr $addr $ragent node $node if [Simulator set mobile_ip_] { $ragent port-dmux [$node set dmux_] } $node addr $addr $node set ragent_ $ragent #delay till after add interface # $node attach $ragent 255 $self at 0.0 "$ragent start-dsdv" ;# start updates return $ragent } Simulator instproc create-aodv-agent { node } { # # Create the Routing Agent and attach it to port 255. # set ragent [new Agent/AODV [$node id]] #set ragent_($id) [new $opt(ragent) $id] #set ragent $ragent_($id) #$node attach $ragent 255 #$ragent if-queue [$node set ifq_(0)] ;# ifq between LL and MAC $self at 0.0 "$ragent start" ;# start BEACON/HELLO Messages # # Drop Target (always on regardless of other tracing) # #set drpT [cmu-trace Drop "RTR" $node] #$ragent drop-target $drpT # # Log Target # #set T [new Trace/Generic] #$T target [$ns_ set nullAgent_] #$T attach $tracefd #$T set src_ $id #$ragent log-target $T $node set ragent_ $ragent return $ragent } Simulator instproc use-newtrace {} { $self instvar newTraceFormat set newTraceFormat 1 } Simulator instproc mobility-trace {ttype atype node} { $self instvar newTraceFormat set tracefd [$self get-ns-traceall] if { $tracefd == "" } { puts "Warning: You have not defined you tracefile yet!" puts "Please use trace-all command to define it." return "" } set T [new CMUTrace/$ttype $atype] if { [info exist newTraceFormat] && $newTraceFormat == "1" } { $T newtrace 1 } $T target [$self set nullAgent_] $T attach $tracefd $T set src_ [$node id] $T node $node return $T } Simulator instproc hier-node haddr { error "now create hier-nodes with just [$ns_ node $haddr]" } Simulator instproc now {} { $self instvar scheduler_ return [$scheduler_ now] } Simulator instproc at args { $self instvar scheduler_ return [eval $scheduler_ at $args] } Simulator instproc at-now args { $self instvar scheduler_ return [eval $scheduler_ at-now $args] } Simulator instproc cancel args { $self instvar scheduler_ return [eval $scheduler_ cancel $args] } Simulator instproc after {ival args} { eval $self at [expr [$self now] + $ival] $args } # # check if total num of nodes exceed 2 to the power n # where <n=node field size in address> # Simulator instproc check-node-num {} { AddrParams instvar nodebits_ if {[Node set nn_] > [expr pow(2, $nodebits_)]} { error "Number of nodes exceeds node-field-size of $nodebits_ bits" } if [Simulator set EnableHierRt_] { # $self chk-hier-field-lengths } } # # Check if number of items at each hier level (num of nodes, or clusters or domains) # exceed size of that hier level field size (in bits). should be modified to support # n-level of hierarchies # Simulator instproc chk-hier-field-lengths {} { AddrParams instvar domain_num_ cluster_num_ nodes_num_ NodeMask_ if [info exists domain_num_] { if {[expr $domain_num_ - 1]> $NodeMask_(1)} { error "\# of domains exceed dom-field-size " } } if [info exists cluster_num_] { set maxval [expr [find-max $cluster_num_] - 1] if {$maxval > [expr pow(2, $NodeMask_(2))]} { error "\# of clusters exceed clus-field-size " } } if [info exists nodes_num_] { set maxval [expr [find-max $nodes_num_] -1] if {$maxval > [expr pow(2, $NodeMask_(3))]} { error "\# of nodess exceed node-field-size" } } } Simulator instproc run {} { #$self compute-routes $self check-node-num $self rtmodel-configure ;# in case there are any [$self get-routelogic] configure $self instvar scheduler_ Node_ link_ started_ set started_ 1 # # Reset every node, which resets every agent. # foreach nn [array names Node_] { $Node_($nn) reset } # # Also reset every queue # foreach qn [array names link_] { set q [$link_($qn) queue] $q reset } # Do all nam-related initialization here $self init-nam return [$scheduler_ run] } Simulator instproc halt {} { $self instvar scheduler_ $scheduler_ halt } Simulator instproc dumpq {} { $self instvar scheduler_ $scheduler_ dumpq } Simulator instproc is-started {} { $self instvar started_ return [info exists started_] } Simulator instproc clearMemTrace {} { $self instvar scheduler_ $scheduler_ clearMemTrace } Simulator instproc simplex-link { n1 n2 bw delay qtype args } { $self instvar link_ queueMap_ nullAgent_ set sid [$n1 id] set did [$n2 id] if [info exists queueMap_($qtype)] { set qtype $queueMap_($qtype) } # construct the queue set qtypeOrig $qtype switch -exact $qtype { ErrorModule { if { [llength $args] > 0 } { set q [eval new $qtype $args] } else { set q [new $qtype Fid] } } intserv { set qtype [lindex $args 0] set q [new Queue/$qtype] } default { set q [new Queue/$qtype] } } # Now create the link switch -exact $qtypeOrig { RTM { set c [lindex $args 1] set link_($sid:$did) [new CBQLink \ $n1 $n2 $bw $delay $q $c] } CBQ - CBQ/WRR { # assume we have a string of form "linktype linkarg" if {[llength $args] == 0} { # default classifier for cbq is just Fid type set c [new Classifier/Hash/Fid 33] } else { set c [lindex $args 1] } set link_($sid:$did) [new CBQLink \ $n1 $n2 $bw $delay $q $c] } FQ { set link_($sid:$did) [new FQLink $n1 $n2 $bw $delay $q] } intserv { #XX need to clean this up set link_($sid:$did) [new IntServLink \ $n1 $n2 $bw $delay $q \ [concat $qtypeOrig $args]] } default { set link_($sid:$did) [new SimpleLink \ $n1 $n2 $bw $delay $q] } } $n1 add-neighbor $n2 #XXX yuck if {[string first "RED" $qtype] != -1} { $q link [$link_($sid:$did) set link_] } set trace [$self get-ns-traceall] if {$trace != ""} { $self trace-queue $n1 $n2 $trace } set trace [$self get-nam-traceall] if {$trace != ""} { $self namtrace-queue $n1 $n2 $trace } # Register this simplex link in nam link list. Treat it as # a duplex link in nam $self register-nam-linkconfig $link_($sid:$did) } # # This is used by Link::orient to register/update the order in which links # should created in nam. This is important because different creation order # may result in different layout. # # A poor hack. :( Any better ideas? # Simulator instproc register-nam-linkconfig link { $self instvar linkConfigList_ link_ if [info exists linkConfigList_] { # Check whether the reverse simplex link is registered, # if so, don't register this link again. # We should have a separate object for duplex link. set i1 [[$link src] id] set i2 [[$link dst] id] if [info exists link_($i2:$i1)] { set pos [lsearch $linkConfigList_ $link_($i2:$i1)] if {$pos >= 0} { set a1 [$link_($i2:$i1) get-attribute "ORIENTATION"] set a2 [$link get-attribute "ORIENTATION"] if {$a1 == "" && $a2 != ""} { # If this duplex link has not been # assigned an orientation, do it. set linkConfigList_ \ [lreplace $linkConfigList_ $pos $pos] } else { return } } } # Remove $link from list if it's already there set pos [lsearch $linkConfigList_ $link] if {$pos >= 0} { set linkConfigList_ \ [lreplace $linkConfigList_ $pos $pos] } } lappend linkConfigList_ $link } # # GT-ITM may occasionally generate duplicate links, so we need this check # to ensure duplicated links do not appear in nam trace files. # Simulator instproc remove-nam-linkconfig {i1 i2} { $self instvar linkConfigList_ link_ if ![info exists linkConfigList_] { return } set pos [lsearch $linkConfigList_ $link_($i1:$i2)] if {$pos >= 0} { set linkConfigList_ [lreplace $linkConfigList_ $pos $pos] return } set pos [lsearch $linkConfigList_ $link_($i2:$i1)] if {$pos >= 0} { set linkConfigList_ [lreplace $linkConfigList_ $pos $pos] } } Simulator instproc duplex-link { n1 n2 bw delay type args } { $self instvar link_ set i1 [$n1 id] set i2 [$n2 id] if [info exists link_($i1:$i2)] { $self remove-nam-linkconfig $i1 $i2 } eval $self simplex-link $n1 $n2 $bw $delay $type $args eval $self simplex-link $n2 $n1 $bw $delay $type $args } Simulator instproc duplex-intserv-link { n1 n2 bw pd sched signal adc args } { eval $self duplex-link $n1 $n2 $bw $pd intserv $sched $signal $adc $args } Simulator instproc simplex-link-op { n1 n2 op args } { $self instvar link_ eval $link_([$n1 id]:[$n2 id]) $op $args } Simulator instproc duplex-link-op { n1 n2 op args } { $self instvar link_ eval $link_([$n1 id]:[$n2 id]) $op $args eval $link_([$n2 id]:[$n1 id]) $op $args } Simulator instproc flush-trace {} { $self instvar alltrace_ if [info exists alltrace_] { foreach trace $alltrace_ { $trace flush } } } Simulator instproc namtrace-all file { $self instvar namtraceAllFile_ if {$file != ""} { set namtraceAllFile_ $file } else { unset namtraceAllFile_ } } Simulator instproc energy-color-change {level1 level2} { $self instvar level1_ level2_ set level1_ $level1 set level2_ $level2 } Simulator instproc namtrace-all-wireless {file optx opty} { $self instvar namtraceAllFile_ if {$file != ""} { set namtraceAllFile_ $file } else { unset namtraceAllFile_ } if { $optx != "" && $opty != "" } { $self puts-nam-config "W -t * -x $optx -y $opty" } } Simulator instproc nam-end-wireless {stoptime} { $self instvar namtraceAllFile_ if {$namtraceAllFile_ != ""} { $self puts-nam-config "W -t $stoptime" } } #Simulator instproc initial_node_pos {nodep size} { # # $self puts-nam-config "n -t * -s [$nodep id] \ # -x [$nodep set X_] -y [$nodep set Y_] -Z [$nodep set Z_] -z $size \ # -v circle -c black" #} Simulator instproc namtrace-some file { $self instvar namtraceSomeFile_ set namtraceSomeFile_ $file } Simulator instproc namtrace-all-wireless {file optx opty} { $self instvar namtraceAllFile_ if {$file != ""} { set namtraceAllFile_ $file } else { unset namtraceAllFile_ } $self puts-nam-config "W -t * -x $optx -y $opty" } Simulator instproc initial_node_pos {nodep size} { $self instvar addressType_ $self instvar energyModel_ if [info exists energyModel_] { set nodeColor "green" } else { set nodeColor "black" } if { [info exists addressType_] && $addressType_ == "hierarchical" } { #hierarchical addressing $self puts-nam-config "n -t * -a [$nodep set address_] -s [$nodep id] \ -x [$nodep set X_] -y [$nodep set Y_] -Z [$nodep set Z_] -z $size \ -v circle -c $nodeColor" } else { #flat addressing $self puts-nam-config "n -t * -s [$nodep id] \ -x [$nodep set X_] -y [$nodep set Y_] -Z [$nodep set Z_] -z $size \ -v circle -c $nodeColor" } } Simulator instproc trace-all file { $self instvar traceAllFile_ set traceAllFile_ $file } Simulator instproc get-nam-traceall {} { $self instvar namtraceAllFile_ if [info exists namtraceAllFile_] { return $namtraceAllFile_ } else { return "" } } Simulator instproc get-ns-traceall {} { $self instvar traceAllFile_ if [info exists traceAllFile_] { return $traceAllFile_ } else { return "" } } # If exists a traceAllFile_, print $str to $traceAllFile_ Simulator instproc puts-ns-traceall { str } { $self instvar traceAllFile_ if [info exists traceAllFile_] { puts $traceAllFile_ $str } } # If exists a traceAllFile_, print $str to $traceAllFile_ Simulator instproc puts-nam-traceall { str } { $self instvar namtraceAllFile_ if [info exists namtraceAllFile_] { puts $namtraceAllFile_ $str } elseif [info exists namtraceSomeFile_] { puts $namtraceSomeFile_ $str } } # namConfigFile is used for writing color/link/node/queue/annotations. # XXX It cannot co-exist with namtraceAll. Simulator instproc namtrace-config { f } { $self instvar namConfigFile_ set namConfigFile_ $f } Simulator instproc get-nam-config {} { $self instvar namConfigFile_ if [info exists namConfigFile_] { return $namConfigFile_ } else { return "" } } # Used only for writing nam configurations to trace file(s). This is different # from puts-nam-traceall because we may want to separate configuration # informations and actual tracing information Simulator instproc puts-nam-config { str } { $self instvar namtraceAllFile_ namConfigFile_ if [info exists namConfigFile_] { puts $namConfigFile_ $str } elseif [info exists namtraceAllFile_] { puts $namtraceAllFile_ $str } elseif [info exists namtraceSomeFile_] { puts $namtraceSomeFile_ $str } } Simulator instproc color { id name } { $self instvar color_ set color_($id) $name } Simulator instproc get-color { id } { $self instvar color_ return $color_($id) } # you can pass in {} as a null file Simulator instproc create-trace { type file src dst {op ""} } { $self instvar alltrace_ set p [new Trace/$type] if [catch {$p set src_ [$src id]}] { $p set src_ $src } if [catch {$p set dst_ [$dst id]}] { $p set dst_ $dst } lappend alltrace_ $p if {$file != ""} { $p ${op}attach $file } return $p } Simulator instproc namtrace-queue { n1 n2 {file ""} } { $self instvar link_ namtraceAllFile_ if {$file == ""} { if ![info exists namtraceAllFile_] return set file $namtraceAllFile_ } $link_([$n1 id]:[$n2 id]) nam-trace $self $file } Simulator instproc trace-queue { n1 n2 {file ""} } { $self instvar link_ traceAllFile_ if {$file == ""} { if ![info exists traceAllFile_] return set file $traceAllFile_ } $link_([$n1 id]:[$n2 id]) trace $self $file } # # arrange for queue length of link between nodes n1 and n2 # to be tracked and return object that can be queried # to learn average q size etc. XXX this API still rough # Simulator instproc monitor-queue { n1 n2 qtrace { sampleInterval 0.1 } } { $self instvar link_ return [$link_([$n1 id]:[$n2 id]) init-monitor $self $qtrace $sampleInterval] } Simulator instproc queue-limit { n1 n2 limit } { $self instvar link_ [$link_([$n1 id]:[$n2 id]) queue] set limit_ $limit } Simulator instproc drop-trace { n1 n2 trace } { $self instvar link_ [$link_([$n1 id]:[$n2 id]) queue] drop-target $trace } Simulator instproc cost {n1 n2 c} { $self instvar link_ $link_([$n1 id]:[$n2 id]) cost $c } Simulator instproc attach-agent { node agent } { $node attach $agent } Simulator instproc attach-tbf-agent { node agent tbf } { $node attach $agent $agent attach-tbf $tbf } Simulator instproc detach-agent { node agent } { $self instvar nullAgent_ $node detach $agent $nullAgent_ } # # Helper proc for setting delay on an existing link # Simulator instproc delay { n1 n2 delay } { $self instvar link_ set sid [$n1 id] set did [$n2 id] if [info exists link_($sid:$did)] { set d [$link_($sid:$did) link] $d set delay_ $delay } } #XXX need to check that agents are attached to nodes already Simulator instproc connect {src dst} { $self simplex-connect $src $dst $self simplex-connect $dst $src return $src } Simulator instproc simplex-connect { src dst } { $src set dst_addr_ [$dst set agent_addr_] $src set dst_port_ [$dst set agent_port_] # Polly Huang: to support abstract TCP simulations if {[lindex [split [$src info class] "/"] 1] == "AbsTCP"} { $self at [$self now] "$self rtt $src $dst" $dst set class_ [$src set class_] } return $src } # # Here are a bunch of helper methods. # Simulator proc instance {} { set ns [Simulator info instances] if { $ns != "" } { return $ns } foreach sim [Simulator info subclass] { set ns [$sim info instances] if { $ns != "" } { return $ns } } error "Cannot find instance of simulator" } Simulator instproc get-node-by-id id { $self instvar Node_ set Node_($id) } Simulator instproc all-nodes-list {} { $self instvar Node_ set nodes "" foreach n [array names Node_] { lappend nodes $Node_($n) } set nodes } Simulator instproc link { n1 n2 } { $self instvar Node_ link_ if { ![catch "$n1 info class Node"] } { set n1 [$n1 id] } if { ![catch "$n2 info class Node"] } { set n2 [$n2 id] } if [info exists link_($n1:$n2)] { return $link_($n1:$n2) } return "" } # Creates connection. First creates a source agent of type s_type and binds # it to source. Next creates a destination agent of type d_type and binds # it to dest. Finally creates bindings for the source and destination agents, # connects them, and returns the source agent. Simulator instproc create-connection {s_type source d_type dest pktClass} { set s_agent [new Agent/$s_type] set d_agent [new Agent/$d_type] $s_agent set fid_ $pktClass $d_agent set fid_ $pktClass $self attach-agent $source $s_agent $self attach-agent $dest $d_agent $self connect $s_agent $d_agent return $s_agent } # Creates connection. First creates a source agent of type s_type and binds # it to source. Next creates a destination agent of type d_type and binds # it to dest. Finally creates bindings for the source and destination agents, # connects them, and returns a list of source agent and destination agent. Simulator instproc create-connection-list {s_type source d_type dest pktClass} { set s_agent [new Agent/$s_type] set d_agent [new Agent/$d_type] $s_agent set fid_ $pktClass $d_agent set fid_ $pktClass $self attach-agent $source $s_agent $self attach-agent $dest $d_agent $self connect $s_agent $d_agent return [list $s_agent $d_agent] } # This seems to be an obsolete procedure. Simulator instproc create-tcp-connection {s_type source d_type dest pktClass} { set s_agent [new Agent/$s_type] set d_agent [new Agent/$d_type] $s_agent set fid_ $pktClass $d_agent set fid_ $pktClass $self attach-agent $source $s_agent $self attach-agent $dest $d_agent # $self connect $s_agent $d_agent return "$s_agent $d_agent" } Classifier instproc no-slot slot { #XXX should say something better for routing problem puts stderr "$self: no target for slot $slot" exit 1 } # # Other classifier methods overload the instproc-likes to track # and return the installed objects. # Classifier instproc install {slot val} { $self set slots_($slot) $val $self cmd install $slot $val } Classifier instproc installNext val { set slot [$self cmd installNext $val] $self set slots_($slot) $val set slot } Classifier instproc adjacents {} { $self array get slots_ } Classifier instproc in-slot? slot { $self instvar slots_ set ret "" if {[array size slots_] < $slot} { set ret slots_($slot) } set ret } # dump is for debugging purposes Classifier instproc dump {} { $self instvar slots_ offset_ shift_ mask_ puts "classifier $self" puts "\t$offset_ offset" puts "\t$shift_ shift" puts "\t$mask_ mask" puts "\t[array size slots_] slots" foreach i [lsort -integer [array names slots_]] { set iv $slots_($i) puts "\t\tslot $i: $iv" } } Classifier/Hash instproc dump args { eval $self next $args $self instvar default_ puts "\t$default_ default" } Classifier/Hash instproc init nbuck { # we need to make sure that port shift/mask values are there # so we set them after they get their default values $self next $nbuck $self instvar shift_ mask_ set shift_ [AddrParams set NodeShift_(1)] set mask_ [AddrParams set NodeMask_(1)] } Simulator instproc makeflowmon { cltype { clslots 29 } } { set flowmon [new QueueMonitor/ED/Flowmon] set cl [new Classifier/Hash/$cltype $clslots] $cl proc unknown-flow { src dst fid } { set fdesc [new QueueMonitor/ED/Flow] set dsamp [new Samples] $fdesc set-delay-samples $dsamp set slot [$self installNext $fdesc] $self set-hash auto $src $dst $fid $slot } $cl proc no-slot slotnum { # # note: we can wind up here when a packet passes # through either an Out or a Drop Snoop Queue for # a queue that the flow doesn't belong to anymore. # Since there is no longer hash state in the # hash classifier, we get a -1 return value for the # hash classifier's classify() function, and there # is no node at slot_[-1]. What to do about this? # Well, we are talking about flows that have already # been moved and so should rightly have their stats # zero'd anyhow, so for now just ignore this case.. # puts "classifier $self, no-slot for slotnum $slotnum" } $flowmon classifier $cl return $flowmon } # attach a flow monitor to a link # 3rd argument dictates whether early drop support is to be used Simulator instproc attach-fmon {lnk fm { edrop 0 } } { set isnoop [new SnoopQueue/In] set osnoop [new SnoopQueue/Out] set dsnoop [new SnoopQueue/Drop] $lnk attach-monitors $isnoop $osnoop $dsnoop $fm if { $edrop != 0 } { set edsnoop [new SnoopQueue/EDrop] $edsnoop set-monitor $fm [$lnk queue] early-drop-target $edsnoop $edsnoop target [$self set nullAgent_] } [$lnk queue] drop-target $dsnoop } # Imported from session.tcl. It is deleted there. ### to insert loss module to regular links in detailed Simulator Simulator instproc lossmodel {lossobj from to} { set link [$self link $from $to] $link errormodule $lossobj } # This function generates losses that can be visualized by nam. Simulator instproc link-lossmodel {lossobj from to} { set link [$self link $from $to] $link insert-linkloss $lossobj } Simulator instproc bw_parse { bspec } { if { [scan $bspec "%f%s" b unit] == 1 } { set unit b } # xxx: all units should support X"ps" --johnh switch $unit { b { return $b } bps { return $b } kb { return [expr $b*1000] } Mb { return [expr $b*1000000] } Gb { return [expr $b*1000000000] } default { puts "error: bw_parse: unknown unit `$unit'" exit 1 } } } Simulator instproc delay_parse { dspec } { if { [scan $dspec "%f%s" b unit] == 1 } { set unit s } switch $unit { s { return $b } ms { return [expr $b*0.001] } ns { return [expr $b*0.000001] } default { puts "error: bw_parse: unknown unit `$unit'" exit 1 } } } #### Polly Huang: Simulator class instproc to support abstract tcp simulations Simulator instproc rtt { src dst } { $self instvar routingTable_ delay_ set srcid [[$src set node_] id] set dstid [[$dst set node_] id] set delay 0 set tmpid $srcid while {$tmpid != $dstid} { set nextid [$routingTable_ lookup $tmpid $dstid] set tmpnode [$self get-node-by-id $tmpid] set nextnode [$self get-node-by-id $nextid] set tmplink [[$self link $tmpnode $nextnode] link] set delay [expr $delay + [expr 2 * [$tmplink set delay_]]] set delay [expr $delay + [expr 8320 / [$tmplink set bandwidth_]]] set tmpid $nextid } $src rtt $delay return $delay } Simulator instproc abstract-tcp {} { $self instvar TahoeAckfsm_ RenoAckfsm_ TahoeDelAckfsm_ RenoDelAckfsm_ dropper_ $self set TahoeAckfsm_ [new FSM/TahoeAck] $self set RenoAckfsm_ [new FSM/RenoAck] $self set TahoeDelAckfsm_ [new FSM/TahoeDelAck] $self set RenoDelAckfsm_ [new FSM/RenoDelAck] $self set nullAgent_ [new DropTargetAgent] }