sctp.cc

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2001-2004 by the Protocol Engineering Lab, U of Delaware
00003  * All rights reserved.
00004  *
00005  * Armando L. Caro Jr. <acaro@@cis,udel,edu>
00006  * Janardhan Iyengar   <iyengar@@cis,udel,edu>
00007  * Keyur Shah          <shah@@cis,udel,edu>
00008  *
00009  * Redistribution and use in source and binary forms, with or without
00010  * modification, are permitted provided that the following conditions
00011  * are met:
00012  *
00013  * 1. Redistributions of source code must retain the above copyright
00014  *    notice, this list of conditions and the following disclaimer.
00015  *
00016  * 2. Redistributions in binary form must reproduce the above copyright
00017  *    notice, this list of conditions and the following disclaimer in the
00018  *    documentation and/or other materials provided with the distribution.
00019  *
00020  * 3. Neither the name of the University nor of the Laboratory may be used
00021  *    to endorse or promote products derived from this software without
00022  *    specific prior written permission.
00023  *
00024  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
00025  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00026  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00027  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
00028  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00029  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00030  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00031  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00032  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00033  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00034  * SUCH DAMAGE.
00035  */
00036 
00037 #ifndef lint
00038 static const char rcsid[] =
00039 "@(#) $Header: /nfs/jade/vint/CVSROOT/ns-2/sctp/sctp.cc,v 1.8 2005/10/07 05:58:29 tomh Exp $ (UD/PEL)";
00040 #endif
00041 
00042 #include "ip.h"
00043 #include "sctp.h"
00044 #include "flags.h"
00045 #include "random.h"
00046 #include "template.h"
00047 
00048 #include "sctpDebug.h"
00049 
00050 #ifdef DMALLOC
00051 #include "dmalloc.h"
00052 #endif
00053 
00054 #define MIN(x,y)    (((x)<(y))?(x):(y))
00055 #define MAX(x,y)    (((x)>(y))?(x):(y))
00056 
00057 int hdr_sctp::offset_;
00058 
00059 static class SCTPHeaderClass : public PacketHeaderClass 
00060 {
00061 public:
00062   SCTPHeaderClass() : PacketHeaderClass("PacketHeader/SCTP",
00063                     sizeof(hdr_sctp)) 
00064   {
00065     bind_offset(&hdr_sctp::offset_);
00066   }
00067 } class_sctphdr;
00068 
00069 static class SctpClass : public TclClass 
00070 { 
00071 public:
00072   SctpClass() : TclClass("Agent/SCTP") {}
00073   TclObject* create(int, const char*const*) 
00074   {
00075     return (new SctpAgent());
00076   }
00077 } classSctp;
00078 
00079 SctpAgent::SctpAgent() : Agent(PT_SCTP)
00080 {
00081   fhpDebugFile = NULL;  // init
00082   opCoreTarget = NULL;  // initialize to NULL in case multihoming isn't used.
00083   memset(&sInterfaceList, 0, sizeof(List_S) );
00084   memset(&sDestList, 0, sizeof(List_S) );
00085   memset(&sAppLayerBuffer, 0, sizeof(List_S) );
00086   memset(&sSendBuffer, 0, sizeof(List_S) );
00087   memset(&sRecvTsnBlockList, 0, sizeof(List_S) );
00088   memset(&sDupTsnList, 0, sizeof(List_S) );
00089   spOutStreams = NULL;
00090   opSackGenTimer = new SackGenTimer(this);
00091   spSctpTrace = NULL;
00092   opHeartbeatGenTimer = new HeartbeatGenTimer(this, NULL);
00093   opHeartbeatTimeoutTimer = new HeartbeatTimeoutTimer(this, NULL);
00094   opT1InitTimer = new T1InitTimer(this);
00095   opT1CookieTimer = new T1CookieTimer(this);
00096   eState = SCTP_STATE_UNINITIALIZED;
00097 }
00098 
00099 SctpAgent::~SctpAgent()
00100 {
00101   Node_S *spCurrNode = NULL;
00102   Node_S *spPrevNode = NULL;
00103   SctpDest_S *spDest = NULL;
00104 
00105   delete opSackGenTimer;
00106   opSackGenTimer = NULL;
00107   delete opHeartbeatGenTimer;
00108   opHeartbeatGenTimer = NULL;
00109   delete opHeartbeatTimeoutTimer;
00110   opHeartbeatTimeoutTimer = NULL;
00111   delete opT1InitTimer;
00112   opT1InitTimer = NULL;
00113   delete opT1CookieTimer;
00114   opT1CookieTimer = NULL;
00115 
00116   if(spOutStreams != NULL)
00117     delete spOutStreams;
00118 
00119   for(spCurrNode = sInterfaceList.spHead;
00120       spCurrNode != NULL;
00121       spPrevNode = spCurrNode, spCurrNode=spCurrNode->spNext, delete spPrevNode)
00122     {
00123       delete (SctpInterface_S *) spCurrNode->vpData;
00124       spCurrNode->vpData = NULL;
00125     }
00126 
00127   for(spCurrNode = sDestList.spHead;
00128       spCurrNode != NULL;
00129       spPrevNode = spCurrNode, spCurrNode=spCurrNode->spNext, delete spPrevNode)
00130     {
00131       spDest = (SctpDest_S *) spCurrNode->vpData;
00132       if(spDest->opT3RtxTimer != NULL)
00133     {
00134       delete spDest->opT3RtxTimer;
00135       spDest->opT3RtxTimer = NULL;
00136     }
00137       if(spDest->opCwndDegradeTimer != NULL)
00138     {
00139       delete spDest->opCwndDegradeTimer;
00140       spDest->opCwndDegradeTimer = NULL;
00141     }
00142       if(spDest->opHeartbeatGenTimer != NULL)
00143     {
00144       delete spDest->opHeartbeatGenTimer;
00145       spDest->opHeartbeatGenTimer = NULL;
00146     }
00147       if(spDest->opHeartbeatTimeoutTimer != NULL)
00148     {
00149       delete spDest->opHeartbeatTimeoutTimer;
00150       spDest->opHeartbeatTimeoutTimer = NULL;
00151     }
00152       if(spDest->opRouteCacheFlushTimer != NULL)
00153     {
00154       delete spDest->opRouteCacheFlushTimer;
00155       spDest->opRouteCacheFlushTimer = NULL;
00156     }
00157       if(spDest->opRouteCalcDelayTimer != NULL)
00158     {
00159       delete spDest->opRouteCalcDelayTimer;
00160       spDest->opRouteCalcDelayTimer = NULL;
00161     }
00162       Packet::free(spDest->opRoutingAssistPacket);
00163       spDest->opRoutingAssistPacket = NULL;
00164       delete (SctpDest_S *) spCurrNode->vpData;  //spDest
00165       spCurrNode->vpData = NULL;
00166     }
00167 
00168   if(spSctpTrace != NULL)
00169     {
00170       delete spSctpTrace;
00171       spSctpTrace = NULL;
00172     }
00173 }
00174 
00175 void SctpAgent::delay_bind_init_all()
00176 {
00177   delay_bind_init_one("debugMask_");
00178   delay_bind_init_one("debugFileIndex_");
00179   delay_bind_init_one("associationMaxRetrans_");
00180   delay_bind_init_one("pathMaxRetrans_");
00181   delay_bind_init_one("changePrimaryThresh_");
00182   delay_bind_init_one("maxInitRetransmits_");
00183   delay_bind_init_one("oneHeartbeatTimer_");
00184   delay_bind_init_one("heartbeatInterval_");
00185   delay_bind_init_one("mtu_");
00186   delay_bind_init_one("initialRwnd_");
00187   delay_bind_init_one("initialSsthresh_");
00188   delay_bind_init_one("ipHeaderSize_");
00189   delay_bind_init_one("dataChunkSize_");
00190   delay_bind_init_one("numOutStreams_");
00191   delay_bind_init_one("useDelayedSacks_");
00192   delay_bind_init_one("sackDelay_");
00193   delay_bind_init_one("useMaxBurst_");
00194   delay_bind_init_one("initialCwnd_");
00195   delay_bind_init_one("initialRto_");
00196   delay_bind_init_one("minRto_");
00197   delay_bind_init_one("maxRto_");
00198   delay_bind_init_one("fastRtxTrigger_");
00199   delay_bind_init_one("numUnrelStreams_");
00200   delay_bind_init_one("reliability_");
00201   delay_bind_init_one("unordered_");
00202   delay_bind_init_one("rtxToAlt_");
00203   delay_bind_init_one("dormantAction_");
00204   delay_bind_init_one("routeCacheLifetime_");
00205   delay_bind_init_one("routeCalcDelay_");
00206 
00207   delay_bind_init_one("trace_all_");
00208   delay_bind_init_one("cwnd_");
00209   delay_bind_init_one("rto_");
00210   delay_bind_init_one("errorCount_");
00211   delay_bind_init_one("frCount_");
00212   delay_bind_init_one("timeoutCount_");
00213   delay_bind_init_one("rcdCount_");
00214 
00215   Agent::delay_bind_init_all();
00216 }
00217 
00218 int SctpAgent::delay_bind_dispatch(const char *cpVarName, 
00219                    const char *cpLocalName, 
00220                    TclObject *opTracer)
00221 {
00222   if(delay_bind(cpVarName, cpLocalName, "debugMask_", &uiDebugMask, opTracer)) 
00223     return TCL_OK;
00224   
00225   if(delay_bind(cpVarName, cpLocalName, 
00226         "debugFileIndex_", &iDebugFileIndex, opTracer)) 
00227     return TCL_OK;
00228   
00229   if(delay_bind(cpVarName, cpLocalName, 
00230         "associationMaxRetrans_", &uiAssociationMaxRetrans, opTracer)) 
00231     return TCL_OK;
00232 
00233   if(delay_bind(cpVarName, cpLocalName, 
00234         "pathMaxRetrans_", &uiPathMaxRetrans, opTracer)) 
00235     return TCL_OK;
00236 
00237   if(delay_bind(cpVarName, cpLocalName, 
00238         "changePrimaryThresh_", &uiChangePrimaryThresh, opTracer)) 
00239     return TCL_OK;
00240 
00241   if(delay_bind(cpVarName, cpLocalName, 
00242         "maxInitRetransmits_", &uiMaxInitRetransmits, opTracer)) 
00243     return TCL_OK;
00244 
00245   if(delay_bind(cpVarName, cpLocalName, 
00246         "oneHeartbeatTimer_", (int *) &eOneHeartbeatTimer, opTracer)) 
00247     return TCL_OK;
00248 
00249   if(delay_bind(cpVarName, cpLocalName, 
00250         "heartbeatInterval_", &uiHeartbeatInterval, opTracer)) 
00251     return TCL_OK;
00252 
00253   if(delay_bind(cpVarName, cpLocalName, "mtu_", &uiMtu, opTracer)) 
00254     return TCL_OK;
00255   
00256   if(delay_bind(cpVarName, cpLocalName, 
00257         "initialRwnd_", &uiInitialRwnd, opTracer)) 
00258     return TCL_OK;
00259 
00260   if(delay_bind(cpVarName, cpLocalName, 
00261         "initialSsthresh_", &iInitialSsthresh, opTracer)) 
00262     return TCL_OK;
00263   
00264   if(delay_bind(cpVarName, cpLocalName, 
00265         "ipHeaderSize_", &uiIpHeaderSize, opTracer)) 
00266     return TCL_OK;
00267 
00268   if(delay_bind(cpVarName, cpLocalName, 
00269         "dataChunkSize_", &uiDataChunkSize, opTracer)) 
00270     return TCL_OK;
00271   
00272   if(delay_bind(cpVarName, cpLocalName, 
00273         "numOutStreams_", &uiNumOutStreams, opTracer)) 
00274     return TCL_OK;
00275 
00276   if(delay_bind(cpVarName, cpLocalName, 
00277         "useDelayedSacks_", (int *) &eUseDelayedSacks, opTracer)) 
00278     return TCL_OK;
00279 
00280   if(delay_bind(cpVarName, cpLocalName, 
00281         "sackDelay_",  &dSackDelay, opTracer)) 
00282     return TCL_OK;
00283 
00284   if(delay_bind(cpVarName, cpLocalName, 
00285         "useMaxBurst_", (int *) &eUseMaxBurst, opTracer)) 
00286     return TCL_OK;
00287 
00288   if(delay_bind(cpVarName, cpLocalName, 
00289         "initialCwnd_", &iInitialCwnd, opTracer)) 
00290     return TCL_OK;
00291 
00292   if(delay_bind(cpVarName, cpLocalName, 
00293         "initialRto_", &dInitialRto, opTracer)) 
00294     return TCL_OK;
00295 
00296   if(delay_bind(cpVarName, cpLocalName, 
00297         "minRto_", &dMinRto, opTracer)) 
00298     return TCL_OK;
00299 
00300   if(delay_bind(cpVarName, cpLocalName, 
00301         "maxRto_", &dMaxRto, opTracer)) 
00302     return TCL_OK;
00303     
00304   if(delay_bind(cpVarName, cpLocalName, 
00305         "fastRtxTrigger_", &iFastRtxTrigger, opTracer)) 
00306     return TCL_OK;
00307 
00308   if(delay_bind(cpVarName, cpLocalName, 
00309         "numUnrelStreams_", &uiNumUnrelStreams, opTracer)) 
00310     return TCL_OK;
00311 
00312   if(delay_bind(cpVarName, cpLocalName, 
00313         "reliability_", &uiReliability, opTracer)) 
00314     return TCL_OK;
00315 
00316   if(delay_bind(cpVarName, cpLocalName, 
00317         "unordered_", (int *) &eUnordered, opTracer)) 
00318     return TCL_OK;
00319 
00320   if(delay_bind(cpVarName, cpLocalName, 
00321         "rtxToAlt_", (int *) &eRtxToAlt, opTracer)) 
00322     return TCL_OK;
00323 
00324   if(delay_bind(cpVarName, cpLocalName, 
00325         "dormantAction_", (int *) &eDormantAction, opTracer)) 
00326     return TCL_OK;
00327 
00328   if(delay_bind(cpVarName, cpLocalName, 
00329         "routeCacheLifetime_", &dRouteCacheLifetime, opTracer)) 
00330     return TCL_OK;
00331 
00332   if(delay_bind(cpVarName, cpLocalName, 
00333         "routeCalcDelay_", &dRouteCalcDelay, opTracer)) 
00334     return TCL_OK;
00335 
00336   if(delay_bind(cpVarName, cpLocalName, "cwnd_", &tiCwnd, opTracer)) 
00337     return TCL_OK;
00338 
00339   if(delay_bind(cpVarName, cpLocalName, "rto_", &tdRto, opTracer)) 
00340     return TCL_OK;
00341 
00342   if(delay_bind(cpVarName, cpLocalName, "errorCount_", &tiErrorCount, opTracer))
00343     return TCL_OK;
00344 
00345   if(delay_bind(cpVarName, cpLocalName, "frCount_", &tiFrCount, opTracer))
00346     return TCL_OK;
00347 
00348   if(delay_bind(cpVarName, cpLocalName, 
00349         "timeoutCount_", &tiTimeoutCount, opTracer))
00350     return TCL_OK;
00351 
00352   if(delay_bind(cpVarName, cpLocalName, "rcdCount_", &tiRcdCount, opTracer))
00353     return TCL_OK;
00354 
00355   if(delay_bind(cpVarName, cpLocalName, 
00356         "trace_all_", (int *) &eTraceAll, opTracer)) 
00357     return TCL_OK;
00358 
00359   return Agent::delay_bind_dispatch(cpVarName, cpLocalName, opTracer);
00360 }
00361 
00362 void SctpAgent::TraceAll()
00363 {
00364   char cpOutString[500];
00365   Node_S *spCurrNode = NULL;
00366   SctpDest_S *spCurrDest = NULL;
00367   double dCurrTime = Scheduler::instance().clock();
00368 
00369   for(spCurrNode = sDestList.spHead;
00370       spCurrNode != NULL;
00371       spCurrNode = spCurrNode->spNext)
00372     {
00373       spCurrDest = (SctpDest_S *) spCurrNode->vpData;
00374       SetSource(spCurrDest); // gives us the correct source addr & port
00375       sprintf(cpOutString,
00376           "time: %-8.5f  "
00377           "saddr: %-2d sport: %-2d daddr: %-2d dport: %-2d "
00378           "cwnd: %d pba: %d out: %d ssthresh: %d peerRwnd: %d "
00379           "rto: %-6.3f srtt: %-6.3f rttvar: %-6.3f "
00380           "assocErrors: %d pathErrors: %d dstatus: %s isPrimary: %s "
00381           "frCount: %d timeoutCount: %d rcdCount: %d\n",
00382           dCurrTime,
00383           addr(), port(), spCurrDest->iNsAddr, spCurrDest->iNsPort,
00384           spCurrDest->iCwnd, spCurrDest->iPartialBytesAcked, 
00385           spCurrDest->iOutstandingBytes, spCurrDest->iSsthresh, 
00386           uiPeerRwnd,
00387           spCurrDest->dRto, spCurrDest->dSrtt, 
00388           spCurrDest->dRttVar,
00389           iAssocErrorCount,
00390           spCurrDest->iErrorCount,
00391           spCurrDest->eStatus ? "ACTIVE" : "INACTIVE",
00392           (spCurrDest == spPrimaryDest) ? "TRUE" : "FALSE",
00393           int(tiFrCount),
00394           spCurrDest->iTimeoutCount,
00395           spCurrDest->iRcdCount);
00396       if(channel_)
00397     (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
00398     }
00399 
00400   sprintf(cpOutString, "\n");
00401   if(channel_)
00402     (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
00403 }
00404 
00405 void SctpAgent::TraceVar(const char* cpVar)
00406 {
00407   char cpOutString[500];
00408   Node_S *spCurrNode = NULL;
00409   SctpDest_S *spCurrDest = NULL;
00410   double dCurrTime = Scheduler::instance().clock();
00411 
00412   if(!strcmp(cpVar, "cwnd_"))
00413     for(spCurrNode = sDestList.spHead;
00414     spCurrNode != NULL;
00415     spCurrNode = spCurrNode->spNext)
00416       {
00417     spCurrDest = (SctpDest_S *) spCurrNode->vpData;
00418     SetSource(spCurrDest); // gives us the correct source addr & port
00419     sprintf(cpOutString,
00420         "time: %-8.5f  "
00421         "saddr: %-2d sport: %-2d daddr: %-2d dport: %-2d "
00422         "cwnd: %d pba: %d out: %d ssthresh: %d peerRwnd: %d\n",
00423         dCurrTime, 
00424         addr(), port(), 
00425         spCurrDest->iNsAddr, spCurrDest->iNsPort,
00426         spCurrDest->iCwnd, spCurrDest->iPartialBytesAcked, 
00427         spCurrDest->iOutstandingBytes, spCurrDest->iSsthresh, 
00428         uiPeerRwnd);
00429     if(channel_)
00430       (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
00431       }
00432 
00433   else if(!strcmp(cpVar, "rto_"))
00434     for(spCurrNode = sDestList.spHead;
00435     spCurrNode != NULL;
00436     spCurrNode = spCurrNode->spNext)
00437       {
00438     spCurrDest = (SctpDest_S *) spCurrNode->vpData;
00439     SetSource(spCurrDest); // gives us the correct source addr & port
00440     sprintf(cpOutString,
00441         "time: %-8.5f  "
00442         "saddr: %-2d sport: %-2d daddr: %-2d dport: %-2d "
00443         "rto: %-6.3f srtt: %-6.3f rttvar: %-6.3f\n",
00444         dCurrTime, 
00445         addr(), port(), 
00446         spCurrDest->iNsAddr, spCurrDest->iNsPort,
00447         spCurrDest->dRto, spCurrDest->dSrtt, 
00448         spCurrDest->dRttVar);
00449     if(channel_)
00450       (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
00451       }
00452 
00453   else if(!strcmp(cpVar, "errorCount_"))
00454     for(spCurrNode = sDestList.spHead;
00455     spCurrNode != NULL;
00456     spCurrNode = spCurrNode->spNext)
00457       {
00458     spCurrDest = (SctpDest_S *) spCurrNode->vpData;
00459     SetSource(spCurrDest); // gives us the correct source addr & port
00460     sprintf(cpOutString, 
00461         "time: %-8.5f  "
00462         "saddr: %-2d sport: %-2d daddr: %-2d dport: %-2d "
00463         "assocErrors: %d pathErrors: %d dstatus: %s isPrimary: %s\n",
00464         dCurrTime, 
00465         addr(), port(), 
00466         spCurrDest->iNsAddr, spCurrDest->iNsPort,
00467         iAssocErrorCount,
00468         spCurrDest->iErrorCount,
00469         spCurrDest->eStatus ? "ACTIVE" : "INACTIVE",
00470         (spCurrDest == spPrimaryDest) ? "TRUE" : "FALSE");
00471     if(channel_)
00472       (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
00473       }
00474 
00475   else if(!strcmp(cpVar, "frCount_"))
00476     {
00477       sprintf(cpOutString, 
00478           "time: %-8.5f  "
00479           "frCount: %d\n",
00480           dCurrTime, 
00481           int(*((TracedInt*) cpVar)) );
00482       if(channel_)
00483     (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
00484     }
00485 
00486   else if(!strcmp(cpVar, "timeoutCount_"))
00487     {
00488     for(spCurrNode = sDestList.spHead;
00489     spCurrNode != NULL;
00490     spCurrNode = spCurrNode->spNext)
00491       {
00492     spCurrDest = (SctpDest_S *) spCurrNode->vpData;
00493     SetSource(spCurrDest); // gives us the correct source addr & port
00494     sprintf(cpOutString, 
00495         "time: %-8.5f  "
00496         "saddr: %-2d sport: %-2d daddr: %-2d dport: %-2d "
00497         "timeoutCount: %d\n",
00498         dCurrTime, 
00499         addr(), port(), 
00500         spCurrDest->iNsAddr, spCurrDest->iNsPort,
00501         spCurrDest->iTimeoutCount);
00502     if(channel_)
00503       (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
00504       }
00505     }
00506 
00507   else if(!strcmp(cpVar, "rcdCount_"))
00508     {
00509     for(spCurrNode = sDestList.spHead;
00510     spCurrNode != NULL;
00511     spCurrNode = spCurrNode->spNext)
00512       {
00513     spCurrDest = (SctpDest_S *) spCurrNode->vpData;
00514     SetSource(spCurrDest); // gives us the correct source addr & port
00515     sprintf(cpOutString, 
00516         "time: %-8.5f  "
00517         "saddr: %-2d sport: %-2d daddr: %-2d dport: %-2d "
00518         "rcdCount: %d\n",
00519         dCurrTime, 
00520         addr(), port(), 
00521         spCurrDest->iNsAddr, spCurrDest->iNsPort,
00522         spCurrDest->iRcdCount);
00523     if(channel_)
00524       (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
00525       }
00526     }
00527 
00528   else
00529     {
00530       sprintf(cpOutString,
00531           "time: %-8.5f  "
00532           "saddr: %-2d sport: %-2d daddr: %-2d dport: %-2d %s: %s\n",
00533           dCurrTime, addr(), port(), daddr(), dport(),
00534           cpVar, "ERROR (unepected trace variable)"); 
00535       if(channel_)
00536     (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
00537     }
00538 
00539   sprintf(cpOutString, "\n");
00540   if(channel_)
00541     (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
00542 }
00543 
00544 
00545 void SctpAgent::trace(TracedVar* v) 
00546 {
00547   if(eTraceAll == TRUE)
00548     TraceAll();
00549   else 
00550     TraceVar(v->name());
00551 }
00552 
00553 /* Reset() is called to refresh the current association settings. It is used
00554  * for the first association. It can also be used to abruptly terminate the
00555  * existing association (so that a new one can begin), but this usage has NOT
00556  * been tested extensively.
00557  */
00558 void SctpAgent::Reset()
00559 {
00560   /* Just in case the user uses the standard ns-2 way of turning on
00561    * debugging, we turn on all debugging. However, if the uiDebugMask is
00562    * used, then the user knows about SCTP's fine-level debugging control
00563    * and the debug_ flag is ignored.
00564    */
00565   if(debug_ == TRUE && uiDebugMask == 0)
00566     uiDebugMask = 0xffffffff;
00567 
00568   /* No debugging output will appear until this is called. Why do we do it
00569    * here?  This is the earliest point at which we have the necessary
00570    * debugging variables bound from TCL.  
00571    */
00572   DBG_FOPEN(); // internal check is done to ensure we only open once!
00573   DBG_I(Reset);
00574 
00575   if(eState != SCTP_STATE_UNINITIALIZED && eState != SCTP_STATE_CLOSED)
00576     Close();  // abruptly close the connection
00577 
00578   DBG_PL(Reset, "uiDebugMask=%u"), uiDebugMask DBG_PR;
00579   DBG_PL(Reset, "iDebugFileIndex=%d"), iDebugFileIndex DBG_PR;
00580   DBG_PL(Reset, "uiAssociationMaxRetrans=%ld"), uiAssociationMaxRetrans DBG_PR;
00581   DBG_PL(Reset, "uiPathMaxRetrans=%ld"), uiPathMaxRetrans DBG_PR;
00582   DBG_PL(Reset, "uiChangePrimaryThresh=%d"), uiChangePrimaryThresh DBG_PR;
00583   DBG_PL(Reset, "eOneHeartbeatTimer=%s"), 
00584     eOneHeartbeatTimer ?  "TRUE" : "FALSE" DBG_PR;
00585   DBG_PL(Reset, "uiHeartbeatInterval=%ld"), uiHeartbeatInterval DBG_PR;
00586   DBG_PL(Reset, "uiMtu=%ld"), uiMtu DBG_PR;
00587   DBG_PL(Reset, "uiInitialRwnd=%ld"), uiInitialRwnd DBG_PR;
00588   DBG_PL(Reset, "iInitialSsthresh=%ld"), iInitialSsthresh DBG_PR;
00589   DBG_PL(Reset, "uiIpHeaderSize=%ld"), uiIpHeaderSize DBG_PR;
00590   DBG_PL(Reset, "uiDataChunkSize=%ld"), uiDataChunkSize DBG_PR;
00591   DBG_PL(Reset, "uiNumOutStreams=%ld"), uiNumOutStreams DBG_PR;
00592   DBG_PL(Reset, "eUseDelayedSacks=%s"), 
00593     eUseDelayedSacks ? "TRUE" : "FALSE" DBG_PR;
00594   DBG_PL(Reset, "dSackDelay=%f"), dSackDelay DBG_PR;
00595   DBG_PL(Reset, "eUseMaxBurst=%s"), eUseMaxBurst ? "TRUE" : "FALSE" DBG_PR;
00596   DBG_PL(Reset, "iInitialCwnd=%ld"), iInitialCwnd DBG_PR;
00597   DBG_PL(Reset, "dInitialRto=%f"), dInitialRto DBG_PR;
00598   DBG_PL(Reset, "dMinRto=%f"), dMinRto DBG_PR;
00599   DBG_PL(Reset, "dMaxRto=%f"), dMaxRto DBG_PR;
00600   DBG_PL(Reset, "iFastRtxTrigger=%ld"), iFastRtxTrigger DBG_PR;
00601   DBG_PL(Reset, "uiNumUnrelStreams=%ld"), uiNumUnrelStreams DBG_PR;
00602   DBG_PL(Reset, "uiReliability=%ld"), uiReliability DBG_PR;
00603   DBG_PL(Reset, "eUnordered=%s"), eUnordered ? "TRUE" : "FALSE" DBG_PR;
00604 
00605   switch(eRtxToAlt)
00606     {
00607     case RTX_TO_ALT_OFF:
00608       DBG_PL(Reset, "eRtxToAlt=RTX_TO_ALT_OFF") DBG_PR;
00609       break;
00610 
00611     case RTX_TO_ALT_ON:
00612       DBG_PL(Reset, "eRtxToAlt=RTX_TO_ALT_ON") DBG_PR;
00613       break;
00614 
00615     case RTX_TO_ALT_TIMEOUTS_ONLY:
00616       DBG_PL(Reset, "eRtxToAlt=RTX_TO_ALT_TIMEOUTS_ONLY") DBG_PR;
00617       break;
00618     }
00619 
00620   switch(eDormantAction)
00621     {
00622     case DORMANT_HOP:
00623       DBG_PL(Reset, "eDormantAction=DORMANT_HOP") DBG_PR;
00624       break;
00625 
00626     case DORMANT_PRIMARY:
00627       DBG_PL(Reset, "eDormantAction=DORMANT_PRIMARY") DBG_PR;
00628       break;
00629 
00630     case DORMANT_LASTDEST:
00631       DBG_PL(Reset, "eDormantAction=DORMANT_LASTDEST") DBG_PR;
00632       break;
00633     }
00634 
00635   DBG_PL(Reset, "eTraceAll=%s"), eTraceAll ? "TRUE" : "FALSE" DBG_PR;
00636 
00637   Node_S *spCurrNode = NULL;
00638   SctpDest_S *spCurrDest = NULL;
00639   int i;
00640 
00641   if(uiInitialRwnd > MAX_RWND_SIZE)
00642     {
00643       fprintf(stderr, "SCTP ERROR: initial rwnd (%d) > max (%d)\n",
00644           uiInitialRwnd, MAX_RWND_SIZE);
00645       DBG_PL(Reset, "ERROR: initial rwnd (%d) > max (%d)"), 
00646     uiInitialRwnd, MAX_RWND_SIZE DBG_PR;
00647       DBG_PL(Reset, "exiting...") DBG_PR;
00648       exit(-1);
00649     }
00650 
00651   if(uiNumOutStreams > MAX_NUM_STREAMS)
00652     {
00653       fprintf(stderr, "%s number of streams (%d) > max (%d)\n",
00654           "SCTP ERROR:",
00655           uiNumOutStreams, MAX_NUM_STREAMS);
00656       DBG_PL(Reset, "ERROR: number of streams (%d) > max (%d)"), 
00657     uiNumOutStreams, MAX_NUM_STREAMS DBG_PR;
00658       DBG_PL(Reset, "exiting...") DBG_PR;
00659       exit(-1);
00660     }
00661   else if(uiNumUnrelStreams > uiNumOutStreams)
00662     {
00663       fprintf(stderr, "%s number of unreliable streams (%d) > total (%d)\n",
00664           "SCTP ERROR:",
00665           uiNumUnrelStreams, uiNumOutStreams);
00666       DBG_PL(Reset, "ERROR: number of unreliable streams (%d) > total (%d)"), 
00667     uiNumUnrelStreams, uiNumOutStreams DBG_PR;
00668       DBG_PL(Reset, "exiting...") DBG_PR;
00669       exit(-1);
00670     }
00671 
00672   uiMaxPayloadSize = uiMtu - SCTP_HDR_SIZE - uiIpHeaderSize;
00673   uiMaxDataSize = uiMaxPayloadSize - ControlChunkReservation();
00674 
00675   if(uiDataChunkSize > MAX_DATA_CHUNK_SIZE)
00676     {
00677       fprintf(stderr, "%s data chunk size (%d) > max (%d)\n",
00678           "SCTP ERROR:",
00679           uiDataChunkSize, MAX_DATA_CHUNK_SIZE);
00680       DBG_PL(Reset, "ERROR: data chunk size (%d) > max (%d)"), 
00681     uiDataChunkSize, MAX_DATA_CHUNK_SIZE DBG_PR;
00682       DBG_PL(Reset, "exiting...") DBG_PR;
00683       exit(-1);
00684     }
00685   else if(uiDataChunkSize > uiMaxDataSize)
00686     {
00687       fprintf(stderr, "SCTP ERROR: DATA chunk size (%d) too big!\n",
00688           uiDataChunkSize);
00689       fprintf(stderr, "            SCTP/IP header = %d\n",
00690           SCTP_HDR_SIZE + uiIpHeaderSize);
00691       fprintf(stderr, "            Control chunk reservation = %d\n",
00692           ControlChunkReservation());
00693       fprintf(stderr, "            MTU = %d\n", uiMtu);
00694       fprintf(stderr, "\n");
00695 
00696       DBG_PL(Reset, 
00697          "ERROR: data chunk size (%d) + SCTP/IP header(%d) + Reserved (%d) > MTU (%d)"), 
00698     uiDataChunkSize, SCTP_HDR_SIZE + uiIpHeaderSize, 
00699     ControlChunkReservation(), uiMtu DBG_PR;
00700       DBG_PL(Reset, "exiting...") DBG_PR;
00701       exit(-1);
00702     }
00703   else if(uiDataChunkSize < MIN_DATA_CHUNK_SIZE)
00704     {
00705       fprintf(stderr, "%s data chunk size (%d) < min (%d)\n",
00706           "SCTP ERROR:",
00707           uiDataChunkSize, MIN_DATA_CHUNK_SIZE);
00708       DBG_PL(Reset, "ERROR: data chunk size (%d) < min (%d)"), 
00709     uiDataChunkSize, MIN_DATA_CHUNK_SIZE DBG_PR;
00710       DBG_PL(Reset, "exiting...") DBG_PR;
00711       exit(-1);
00712     }
00713 
00714   /* size_ is an Agent variable which is normally the packet size, but
00715    * SCTP uses size_ to dictate to non-sctp aware applications the max
00716    * data size of an application write. If size_ isn't set by SCTP, some
00717    * non-sctp aware apps (such as Telnet) will call sendmsg() with 0
00718    * bytes.
00719    */
00720   size_ = uiMtu - SCTP_HDR_SIZE - uiIpHeaderSize - sizeof(SctpDataChunkHdr_S);
00721 
00722   eState = SCTP_STATE_CLOSED;
00723   eForceSource = FALSE;
00724   iAssocErrorCount = 0;
00725 
00726   if(eOneHeartbeatTimer == TRUE && uiHeartbeatInterval != 0)
00727     {
00728       opHeartbeatGenTimer->force_cancel();
00729       opHeartbeatTimeoutTimer->force_cancel();
00730     }
00731 
00732   opT1InitTimer->force_cancel();
00733   opT1CookieTimer->force_cancel();
00734   iInitTryCount = 0;
00735   uiNextTsn = 0;
00736   usNextStreamId = 0;
00737 
00738   /* if it's already allocated, let's delete and reallocate just in case user
00739    * has changed TCL bindable variables
00740    */
00741   if(spOutStreams != NULL)
00742     delete spOutStreams;
00743   spOutStreams = new SctpOutStream_S [uiNumOutStreams];
00744   memset(spOutStreams, 0, (uiNumOutStreams * sizeof(SctpOutStream_S)) );
00745 
00746   for(i = 0; i < (int) uiNumUnrelStreams; i++)
00747     {
00748       DBG_PL(Reset, "setting outStream %d to UNRELIABLE"), i DBG_PR;
00749       spOutStreams[i].eMode = SCTP_STREAM_UNRELIABLE;
00750     }
00751   for(; i < (int) uiNumOutStreams; i++)
00752     {
00753       DBG_PL(Reset, "setting outStream %d to RELIABLE"), i DBG_PR;
00754       spOutStreams[i].eMode = SCTP_STREAM_RELIABLE;
00755     }
00756 
00757   uiPeerRwnd = 0;
00758   uiCumAckPoint = 0;
00759   uiAdvancedPeerAckPoint = 0;
00760   uiHighestTsnNewlyAcked = 0;
00761   uiRecover = 0;
00762   memset(&sAppLayerBuffer, 0, sizeof(List_S) );
00763   memset(&sSendBuffer, 0, sizeof(List_S) );
00764 
00765   if(uiAssociationMaxRetrans > (sDestList.uiLength * uiPathMaxRetrans))
00766     {
00767       DBG_PL(Reset, 
00768          "WARNING: Association.Max.Retrans > "
00769          "summation of all destinations' Path.Max.Retrans "
00770          "(rfc2960 section 8.1)") DBG_PR;
00771     }
00772 
00773   for(spCurrNode = sDestList.spHead;
00774       spCurrNode != NULL;
00775       spCurrNode = spCurrNode->spNext)
00776     {
00777       spCurrDest = (SctpDest_S *) spCurrNode->vpData;
00778 
00779       spCurrDest->iCwnd = iInitialCwnd * uiMaxDataSize; 
00780       spCurrDest->iSsthresh = iInitialSsthresh;
00781       spCurrDest->eFirstRttMeasurement = TRUE;
00782       spCurrDest->dRto = dInitialRto;
00783 
00784       if(spCurrDest->opT3RtxTimer == NULL)
00785     spCurrDest->opT3RtxTimer = new T3RtxTimer(this, spCurrDest);
00786       else 
00787     spCurrDest->opT3RtxTimer->force_cancel();
00788 
00789       spCurrDest->iOutstandingBytes = 0;
00790       spCurrDest->iPartialBytesAcked = 0;
00791 
00792       spCurrDest->iErrorCount = 0;
00793       spCurrDest->iTimeoutCount = 0;
00794       spCurrDest->eStatus = SCTP_DEST_STATUS_ACTIVE;
00795 
00796       if(spCurrDest->opCwndDegradeTimer == NULL)
00797     {
00798       spCurrDest->opCwndDegradeTimer =
00799         new CwndDegradeTimer(this, spCurrDest);
00800     }
00801       else
00802     {
00803       spCurrDest->opCwndDegradeTimer->force_cancel();
00804     }
00805 
00806       if(eOneHeartbeatTimer == TRUE && uiHeartbeatInterval != 0)
00807     {
00808       spCurrDest->dIdleSince = 0;
00809     }
00810       else if(uiHeartbeatInterval != 0)
00811     {
00812       if(spCurrDest->opHeartbeatGenTimer == NULL)
00813         {
00814           spCurrDest->opHeartbeatGenTimer =
00815         new HeartbeatGenTimer(this, spCurrDest);
00816         }
00817       else
00818         {
00819           spCurrDest->opHeartbeatGenTimer->force_cancel();
00820         }
00821 
00822       if(spCurrDest->opHeartbeatTimeoutTimer == NULL)
00823         {
00824           spCurrDest->opHeartbeatTimeoutTimer =
00825         new HeartbeatTimeoutTimer(this, spCurrDest);
00826         }
00827       else
00828         {
00829           spCurrDest->opHeartbeatTimeoutTimer->force_cancel();
00830         }
00831     }
00832 
00833       spCurrDest->eCcApplied = FALSE;
00834       spCurrDest->spFirstOutstanding = NULL;
00835 
00836       spCurrDest->iRcdCount = 0;      
00837       spCurrDest->eRouteCached = FALSE;
00838 
00839       if(spCurrDest->opRouteCacheFlushTimer == NULL)
00840     {
00841       spCurrDest->opRouteCacheFlushTimer =
00842         new RouteCacheFlushTimer(this, spCurrDest);
00843     }
00844       else
00845     {
00846       spCurrDest->opRouteCacheFlushTimer->force_cancel();
00847     }
00848 
00849       if(spCurrDest->opRouteCalcDelayTimer == NULL)
00850     {
00851       spCurrDest->opRouteCalcDelayTimer =
00852         new RouteCalcDelayTimer(this, spCurrDest);
00853     }
00854       else
00855     {
00856       spCurrDest->opRouteCalcDelayTimer->force_cancel();
00857     }
00858 
00859       memset(&spCurrDest->sBufferedPackets, 0, sizeof(List_S) );
00860     }
00861 
00862   eForwardTsnNeeded = FALSE;
00863   eSendNewDataChunks = FALSE;
00864   eMarkedChunksPending = FALSE;
00865   eApplyMaxBurst = FALSE;
00866   eDataSource = DATA_SOURCE_APPLICATION;
00867   uiBurstLength = 0;
00868 
00869   uiMyRwnd = uiInitialRwnd;
00870   uiCumAck = 0; 
00871   uiHighestRecvTsn = 0;
00872   memset(&sRecvTsnBlockList, 0, sizeof(List_S) );
00873   memset(&sDupTsnList, 0, sizeof(List_S) );
00874   eStartOfPacket = FALSE;
00875   iDataPktCountSinceLastSack = 0;
00876   eSackChunkNeeded = FALSE;
00877 
00878   opSackGenTimer->force_cancel();
00879 
00880   /* if it's already allocated, let's delete and reallocate just in case user
00881    * has changed TCL bindable variables
00882    */
00883   if(spSctpTrace != NULL)
00884     delete spSctpTrace;
00885   
00886   /* We don't know how many chunks will be in a packet, so we assume the 
00887    * theoretical maximum... a packet full of only chunk headers.
00888    */
00889   spSctpTrace = new SctpTrace_S[uiMaxPayloadSize / sizeof(SctpChunkHdr_S)];
00890 
00891   uiNumChunks = 0;
00892 
00893   /* Trigger changes for trace to pick up (we want to know the initial values)
00894    */
00895   tiCwnd++;
00896   tdRto++;
00897   tiErrorCount++;
00898   tiFrCount = 0;
00899   tiTimeoutCount++;
00900   tiRcdCount++;
00901 
00902   OptionReset();
00903 
00904   DBG_PL(Reset, "spSctpTrace=%p"), spSctpTrace DBG_PR;
00905   DBG_X(Reset);
00906 }
00907 
00908 /* This function serves as a hook for optional extensions to reset additional
00909  * state variables.
00910  */
00911 void SctpAgent::OptionReset()
00912 {
00913   return;
00914 }
00915 
00916 /* This function returns a size based on how much space is needed for
00917  * bundled chunks. Real implementations, such as the KAME stack, reserve
00918  * space for ECN, etc. We are using it for experimental extensions such as
00919  * the Timestamp chunk (sctp-timestamp.cc). Such a function avoids having
00920  * to maintain nearly identical copies of the Reset() function for each
00921  * extension. For the base sctp, we don't reserve any space... so return 0.
00922  */
00923 u_int SctpAgent::ControlChunkReservation()
00924 {
00925   DBG_I(ControlChunkReservation);
00926   DBG_PL(ControlChunkReservation, "returning 0") DBG_PR;
00927   DBG_X(ControlChunkReservation);
00928   return 0;
00929 }
00930 
00931 int SctpAgent::command(int argc, const char*const* argv)
00932 {
00933   DBG_I(command); // internal check is done to avoid printing if file is unopen!
00934 
00935   double dCurrTime = Scheduler::instance().clock();
00936   DBG_PL(command, "<time:%f> argc=%d argv[1]=%s"),
00937     dCurrTime, argc, argv[1] DBG_PR;
00938 
00939   Tcl& oTcl = Tcl::instance();
00940   Node *opNode = NULL;
00941   int iNsAddr;
00942   int iNsPort;
00943   NsObject *opTarget = NULL;
00944   NsObject *opLink = NULL;
00945   int iRetVal;
00946 
00947   if(argc == 2)   
00948     {
00949       if (strcmp(argv[1], "reset") == 0) 
00950     {
00951       Reset();
00952       DBG_X(command);
00953       return (TCL_OK);
00954     }
00955       else if (strcmp(argv[1], "close") == 0) 
00956     {
00957       Close();
00958       DBG_X(command);
00959       return (TCL_OK);
00960     }
00961     }
00962   else if(argc == 3) 
00963     {
00964       if (strcmp(argv[1], "advance") == 0) 
00965     {
00966       DBG_X(command);
00967       return (TCL_OK);
00968     }
00969       else if (strcmp(argv[1], "set-multihome-core") == 0) 
00970     { 
00971       opCoreTarget = (Classifier *) TclObject::lookup(argv[2]);
00972       if(opCoreTarget == NULL) 
00973         {
00974           oTcl.resultf("no such object %s", argv[4]);
00975           return (TCL_ERROR);
00976         }
00977       DBG_X(command);
00978       return (TCL_OK);
00979     }
00980       else if (strcmp(argv[1], "set-primary-destination") == 0)
00981     {
00982       opNode = (Node *) TclObject::lookup(argv[2]);
00983       if(opNode == NULL) 
00984         {
00985           oTcl.resultf("no such object %s", argv[2]);
00986           return (TCL_ERROR);
00987         }
00988       iRetVal = SetPrimary( opNode->address() );
00989 
00990       if(iRetVal == TCL_ERROR)
00991         {
00992           fprintf(stderr, "[SctpAgent::command] ERROR:"
00993               "%s is not a valid destination\n", argv[2]);
00994           DBG_X(command);
00995           return (TCL_ERROR);
00996         }
00997       DBG_X(command);
00998       return (TCL_OK);
00999     }
01000       else if (strcmp(argv[1], "force-source") == 0)
01001     {
01002       opNode = (Node *) TclObject::lookup(argv[2]);
01003       if(opNode == NULL) 
01004         {
01005           oTcl.resultf("no such object %s", argv[2]);
01006           return (TCL_ERROR);
01007         }
01008       iRetVal = ForceSource( opNode->address() );
01009 
01010       if(iRetVal == TCL_ERROR)
01011         {
01012           fprintf(stderr, "[SctpAgent::command] ERROR:"
01013               "%s is not a valid source\n", argv[2]);
01014           DBG_X(command);
01015           return (TCL_ERROR);
01016         }
01017       DBG_X(command);
01018       return (TCL_OK);
01019     }
01020       else if (strcmp(argv[1], "print") == 0) 
01021     {
01022       if(eTraceAll == TRUE)
01023         TraceAll();
01024       else 
01025         TraceVar(argv[2]);
01026       DBG_X(command);
01027       return (TCL_OK);
01028     }
01029     }
01030   else if(argc == 4)
01031     {
01032       if (strcmp(argv[1], "add-multihome-destination") == 0) 
01033     { 
01034       iNsAddr = atoi(argv[2]);
01035       iNsPort = atoi(argv[3]);
01036       AddDestination(iNsAddr, iNsPort);
01037       DBG_X(command);
01038       return (TCL_OK);
01039     }
01040     }
01041   else if(argc == 6)
01042     {
01043       if (strcmp(argv[1], "add-multihome-interface") == 0) 
01044     { 
01045       iNsAddr = atoi(argv[2]);
01046       iNsPort = atoi(argv[3]);
01047       opTarget = (NsObject *) TclObject::lookup(argv[4]);
01048       if(opTarget == NULL) 
01049         {
01050           oTcl.resultf("no such object %s", argv[4]);
01051           return (TCL_ERROR);
01052         }
01053       opLink = (NsObject *) TclObject::lookup(argv[5]);
01054       if(opLink == NULL) 
01055         {
01056           oTcl.resultf("no such object %s", argv[5]);
01057           return (TCL_ERROR);
01058         }
01059       AddInterface(iNsAddr, iNsPort, opTarget, opLink);
01060       DBG_X(command);
01061       return (TCL_OK);
01062     }
01063     }
01064 
01065   DBG_X(command);
01066   return (Agent::command(argc, argv));
01067 }
01068 
01069 /* Given params: sList, spPrevNode, spNextNode, spNewNode... insert spNewNode
01070  * into sList between spPrevNode and spNextNode.
01071  */
01072 void SctpAgent::InsertNode(List_S *spList, Node_S *spPrevNode, 
01073               Node_S *spNewNode,  Node_S *spNextNode)
01074 {
01075   if(spPrevNode == NULL)
01076     spList->spHead = spNewNode;
01077   else
01078     spPrevNode->spNext = spNewNode;
01079 
01080   spNewNode->spPrev = spPrevNode;
01081   spNewNode->spNext = spNextNode;
01082 
01083   if(spNextNode == NULL)
01084     spList->spTail = spNewNode;
01085   else
01086     spNextNode->spPrev = spNewNode;
01087 
01088   spList->uiLength++;
01089 }
01090 
01091 void SctpAgent::DeleteNode(List_S *spList, Node_S *spNode)
01092 {
01093   if(spNode->spPrev == NULL)
01094     spList->spHead = spNode->spNext;
01095   else
01096     spNode->spPrev->spNext = spNode->spNext;
01097   
01098   if(spNode->spNext == NULL)
01099     spList->spTail = spNode->spPrev;
01100   else
01101     spNode->spNext->spPrev = spNode->spPrev;
01102 
01103   /* now let's free the internal structures
01104    */
01105   switch(spNode->eType)
01106     {
01107     case NODE_TYPE_STREAM_BUFFER:
01108       delete[] (u_char *) ((SctpStreamBufferNode_S *) spNode->vpData)->spChunk;
01109       ((SctpStreamBufferNode_S *) spNode->vpData)->spChunk = NULL;
01110       delete (SctpStreamBufferNode_S *) spNode->vpData;
01111       spNode->vpData = NULL;
01112       break;
01113 
01114     case NODE_TYPE_RECV_TSN_BLOCK:
01115       delete (SctpRecvTsnBlock_S *) spNode->vpData;
01116       spNode->vpData = NULL;
01117       break;
01118 
01119     case NODE_TYPE_DUP_TSN:
01120       delete (SctpDupTsn_S *) spNode->vpData;
01121       spNode->vpData = NULL;
01122       break;
01123 
01124     case NODE_TYPE_SEND_BUFFER:
01125       delete[] (u_char *) ((SctpSendBufferNode_S *) spNode->vpData)->spChunk;
01126       ((SctpSendBufferNode_S *) spNode->vpData)->spChunk = NULL;
01127       delete (SctpSendBufferNode_S *) spNode->vpData;
01128       spNode->vpData = NULL;
01129       break;
01130 
01131     case NODE_TYPE_APP_LAYER_BUFFER:
01132       delete (AppData_S *) spNode->vpData;
01133       spNode->vpData = NULL;
01134       break;
01135 
01136     case NODE_TYPE_INTERFACE_LIST:
01137       delete (SctpInterface_S *) spNode->vpData;
01138       spNode->vpData = NULL;
01139       break;
01140 
01141     case NODE_TYPE_DESTINATION_LIST:
01142       delete (SctpDest_S *) spNode->vpData;
01143       spNode->vpData = NULL;
01144       break;
01145 
01146     case NODE_TYPE_PACKET_BUFFER:
01147       /* no need to free data, because SctpAgent hands over responsibility to
01148        * the send() function
01149        */
01150       spNode->vpData = NULL;
01151       break;
01152     }
01153 
01154   delete spNode;
01155   spList->uiLength--;
01156 }
01157 
01158 void SctpAgent::ClearList(List_S *spList)
01159 {
01160   Node_S *spCurrNode = spList->spHead;
01161   Node_S *spDeleteNode = NULL;
01162 
01163   while(spCurrNode != NULL)
01164     {
01165       spDeleteNode = spCurrNode;
01166       spCurrNode = spCurrNode->spNext;
01167       DeleteNode(spList, spDeleteNode);
01168     }
01169 }
01170 
01171 void SctpAgent::AddInterface(int iNsAddr, int iNsPort, 
01172                  NsObject *opTarget, NsObject *opLink)
01173 {
01174   DBG_I(AddInterface);
01175 
01176   Node_S *spNewNode = new Node_S;
01177   spNewNode->eType = NODE_TYPE_INTERFACE_LIST;
01178   spNewNode->vpData = new SctpInterface_S;
01179 
01180   SctpInterface_S *spNewInterface = (SctpInterface_S *) spNewNode->vpData;
01181   spNewInterface->iNsAddr = iNsAddr;
01182   spNewInterface->iNsPort = iNsPort;
01183   spNewInterface->opTarget = opTarget;
01184   spNewInterface->opLink = opLink;
01185   
01186   InsertNode(&sInterfaceList, sInterfaceList.spTail, spNewNode, NULL);
01187 
01188   DBG_X(AddInterface);
01189 }
01190 
01191 void SctpAgent::AddDestination(int iNsAddr, int iNsPort)
01192 {
01193   DBG_I(AddDestination);
01194 
01195   Node_S *spNewNode = new Node_S;
01196   spNewNode->eType = NODE_TYPE_DESTINATION_LIST;
01197   spNewNode->vpData = new SctpDest_S;
01198 
01199   SctpDest_S *spNewDest = (SctpDest_S *) spNewNode->vpData;
01200   memset(spNewDest, 0, sizeof(SctpDest_S));
01201   spNewDest->iNsAddr = iNsAddr;
01202   spNewDest->iNsPort = iNsPort;
01203 
01204   /* set the primary to the last destination added just in case the user does
01205    * not set a primary
01206    */
01207   spPrimaryDest = spNewDest;
01208   spNewTxDest = spPrimaryDest;
01209 
01210   /* allocate packet with the dest addr... to be used later for setting src dest
01211    */
01212   daddr() = spNewDest->iNsAddr;
01213   dport() = spNewDest->iNsPort;
01214   spNewDest->opRoutingAssistPacket = allocpkt();
01215 
01216   InsertNode(&sDestList, sDestList.spTail, spNewNode, NULL);
01217 
01218   DBG_X(AddDestination);
01219 }
01220 
01221 int SctpAgent::SetPrimary(int iNsAddr)
01222 {
01223   DBG_I(SetPrimary);
01224 
01225   Node_S *spCurrNode = NULL;
01226   SctpDest_S *spCurrDest = NULL;
01227 
01228   for(spCurrNode = sDestList.spHead;
01229       spCurrNode != NULL;
01230       spCurrNode = spCurrNode->spNext)
01231     {
01232       spCurrDest = (SctpDest_S *) spCurrNode->vpData;
01233       
01234       if(spCurrDest->iNsAddr == iNsAddr)
01235     {
01236       spPrimaryDest = spCurrDest;
01237       spNewTxDest = spPrimaryDest;
01238       DBG_PL(SetPrimary, "returning TCL_OK") DBG_PR;
01239       DBG_X(SetPrimary);
01240       return (TCL_OK);
01241     }
01242     }
01243 
01244   DBG_PL(SetPrimary, "returning TCL_ERROR") DBG_PR;
01245   DBG_X(SetPrimary);
01246   return (TCL_ERROR);
01247 }
01248 
01249 int SctpAgent::ForceSource(int iNsAddr)
01250 {
01251   DBG_I(ForceSource);
01252 
01253   Node_S *spCurrNode = sInterfaceList.spHead;
01254   SctpInterface_S *spCurrInterface = NULL;
01255 
01256   while(spCurrNode != NULL)
01257     {
01258       spCurrInterface = (SctpInterface_S *) spCurrNode->vpData;
01259 
01260       if(spCurrInterface->iNsAddr == iNsAddr)
01261     {
01262       addr() = spCurrInterface->iNsAddr;
01263       port() = spCurrInterface->iNsPort;
01264       target_ = spCurrInterface->opTarget;
01265       eForceSource = TRUE;
01266       DBG_PL(ForceSource, "returning TCL_OK") DBG_PR;
01267       DBG_X(ForceSource);
01268       return (TCL_OK);
01269     }
01270       else
01271     spCurrNode = spCurrNode->spNext;
01272     }
01273 
01274   DBG_PL(ForceSource, "returning TCL_ERROR") DBG_PR;
01275   DBG_X(ForceSource);
01276   return (TCL_ERROR);
01277 }
01278 
01279 /* returns the size of the chunk
01280  */
01281 int SctpAgent::GenChunk(SctpChunkType_E eType, u_char *ucpChunk)
01282 {
01283   DBG_I(GenChunk);
01284 
01285   double dCurrTime = Scheduler::instance().clock();
01286   AppData_S *spAppMessage = NULL;
01287   int iSize = 0;
01288 
01289   DBG_PL(GenChunk, "spSctpTrace=%p"), spSctpTrace DBG_PR;
01290 
01291   switch(eType)
01292     {
01293     case SCTP_CHUNK_INIT:
01294       iSize = sizeof(SctpInitChunk_S);
01295       ((SctpInitChunk_S *) ucpChunk)->sHdr.ucType = eType;
01296       ((SctpInitChunk_S *) ucpChunk)->uiArwnd = uiInitialRwnd; 
01297       ((SctpInitChunk_S *) ucpChunk)->usNumOutboundStreams = uiNumOutStreams;
01298       ((SctpInitChunk_S *) ucpChunk)->uiInitialTsn = 0; 
01299 
01300       ((SctpInitChunk_S *) ucpChunk)->sUnrelStream.usType 
01301     = SCTP_INIT_PARAM_UNREL;
01302 
01303       ((SctpInitChunk_S *) ucpChunk)->sUnrelStream.usLength
01304     = sizeof(SctpUnrelStreamsParam_S);
01305       
01306       if(uiNumUnrelStreams > 0)
01307     {
01308       ((SctpInitChunk_S *) ucpChunk)->sUnrelStream.usLength 
01309         += sizeof(SctpUnrelStreamPair_S);
01310 
01311       SctpUnrelStreamPair_S *spUnrelStreamPair 
01312             = (SctpUnrelStreamPair_S *) (ucpChunk+iSize);
01313           spUnrelStreamPair->usStart = 0;
01314           spUnrelStreamPair->usEnd = uiNumUnrelStreams - 1;
01315 
01316       iSize += sizeof(SctpUnrelStreamPair_S);
01317     }
01318 
01319       ((SctpInitChunk_S *) ucpChunk)->sHdr.usLength = iSize;
01320 
01321       /* fill in tracing fields too
01322        */
01323       spSctpTrace[uiNumChunks].eType = eType;
01324       spSctpTrace[uiNumChunks].uiTsn = (u_int) -1;
01325       spSctpTrace[uiNumChunks].usStreamId = (u_short) -1;
01326       spSctpTrace[uiNumChunks].usStreamSeqNum = (u_short) -1;
01327       break;
01328 
01329     case SCTP_CHUNK_INIT_ACK:
01330       iSize = sizeof(SctpInitAckChunk_S);
01331       ((SctpInitAckChunk_S *) ucpChunk)->sHdr.ucType = eType;
01332       ((SctpInitAckChunk_S *) ucpChunk)->uiArwnd = uiInitialRwnd; 
01333       ((SctpInitChunk_S *) ucpChunk)->usNumOutboundStreams = uiNumOutStreams;
01334       ((SctpInitAckChunk_S *) ucpChunk)->uiInitialTsn = 0; 
01335 
01336       ((SctpInitAckChunk_S *) ucpChunk)->sUnrelStream.usType 
01337     = SCTP_INIT_PARAM_UNREL;
01338 
01339       ((SctpInitAckChunk_S *) ucpChunk)->sUnrelStream.usLength
01340     = sizeof(SctpUnrelStreamsParam_S);
01341 
01342       if(uiNumUnrelStreams > 0)
01343     {
01344       ((SctpInitAckChunk_S *) ucpChunk)->sUnrelStream.usLength 
01345         += sizeof(SctpUnrelStreamPair_S);
01346 
01347       SctpUnrelStreamPair_S *spUnrelStreamPair 
01348             = (SctpUnrelStreamPair_S *) (ucpChunk+iSize);
01349           spUnrelStreamPair->usStart = 0;
01350           spUnrelStreamPair->usEnd = uiNumUnrelStreams - 1;
01351 
01352       iSize += sizeof(SctpUnrelStreamPair_S);
01353     }
01354 
01355       ((SctpInitAckChunk_S *) ucpChunk)->sHdr.usLength = iSize;
01356 
01357       /* fill in tracing fields too
01358        */
01359       spSctpTrace[uiNumChunks].eType = eType;
01360       spSctpTrace[uiNumChunks].uiTsn = (u_int) -1;
01361       spSctpTrace[uiNumChunks].usStreamId = (u_short) -1;
01362       spSctpTrace[uiNumChunks].usStreamSeqNum = (u_short) -1;
01363       break;
01364 
01365     case SCTP_CHUNK_COOKIE_ECHO:
01366       iSize = sizeof(SctpCookieEchoChunk_S);
01367       ((SctpCookieEchoChunk_S *) ucpChunk)->sHdr.ucType = eType;
01368       ((SctpCookieEchoChunk_S *) ucpChunk)->sHdr.usLength = iSize;
01369 
01370       /* fill in tracing fields too
01371        */
01372       spSctpTrace[uiNumChunks].eType = eType;
01373       spSctpTrace[uiNumChunks].uiTsn = (u_int) -1;
01374       spSctpTrace[uiNumChunks].usStreamId = (u_short) -1;
01375       spSctpTrace[uiNumChunks].usStreamSeqNum = (u_short) -1;
01376       break;
01377       
01378     case SCTP_CHUNK_COOKIE_ACK:
01379       iSize = sizeof(SctpCookieAckChunk_S);
01380       ((SctpCookieAckChunk_S *) ucpChunk)->sHdr.ucType = eType;
01381       ((SctpCookieAckChunk_S *) ucpChunk)->sHdr.usLength = iSize;
01382 
01383       /* fill in tracing fields too
01384        */
01385       spSctpTrace[uiNumChunks].eType = eType;
01386       spSctpTrace[uiNumChunks].uiTsn = (u_int) -1;
01387       spSctpTrace[uiNumChunks].usStreamId = (u_short) -1;
01388       spSctpTrace[uiNumChunks].usStreamSeqNum = (u_short) -1;
01389       break;
01390 
01391     case SCTP_CHUNK_DATA:
01392       /* Depending on eDataSource, we either use chunk parameters 
01393        * from the app layer buffer, or use the defaults for infinite 
01394        * data generation.
01395        */
01396       if(eDataSource == DATA_SOURCE_APPLICATION)
01397     {
01398       /* If DATA_SOURCE_APPLICATION, we have to get chunk parameters
01399        * from the app layer buffer 
01400        */
01401       DBG_PL (GenChunk, "eDataSource=DATA_SOURCE_APPLICATION") DBG_PR; 
01402       spAppMessage = (AppData_S *) (sAppLayerBuffer.spHead->vpData);
01403 
01404       DBG_PL (GenChunk, "App Message ---------------->") DBG_PR;
01405       DBG_PL (GenChunk, "usNumStreams: %d"), 
01406         spAppMessage->usNumStreams DBG_PR;
01407       DBG_PL (GenChunk, "usNumUnreliable: %d"), 
01408         spAppMessage->usNumUnreliable DBG_PR;
01409       DBG_PL (GenChunk, "uiNumBytes: %d"),
01410         spAppMessage->uiNumBytes DBG_PR;
01411       DBG_PL (GenChunk, "usStreamId: %d"), 
01412         spAppMessage->usStreamId DBG_PR;
01413       DBG_PL (GenChunk, "eUnordered: %s"), 
01414         spAppMessage->eUnordered ? "TRUE" : "FALSE" DBG_PR;
01415       DBG_PL (GenChunk, "usReliability: %d"), 
01416         spAppMessage->usReliability DBG_PR;
01417       DBG_PL (GenChunk, "App Message <----------------") DBG_PR;
01418 
01419       iSize = spAppMessage->uiNumBytes + sizeof(SctpDataChunkHdr_S);
01420       ((SctpDataChunkHdr_S *) ucpChunk)->sHdr.ucType = SCTP_CHUNK_DATA;
01421       ((SctpDataChunkHdr_S *) ucpChunk)->sHdr.usLength = iSize;
01422 
01423       /* DATA chunks must be padded to a 4 byte boundary (section 3.3.1)
01424        * ...but the adjustment should happen after usLength field is set,
01425        * because the field should represent the size before padding.
01426        */
01427       if( (iSize % 4) != 0)
01428         iSize += 4 - (iSize % 4);
01429 
01430       ((SctpDataChunkHdr_S *) ucpChunk)->uiTsn = ++uiNextTsn;
01431       
01432       ((SctpDataChunkHdr_S *) ucpChunk)->usStreamId 
01433         = spAppMessage->usStreamId; 
01434 
01435       if(spAppMessage->eUnordered == TRUE)
01436         {
01437           ((SctpDataChunkHdr_S *) ucpChunk)->sHdr.ucFlags 
01438         |= SCTP_DATA_FLAG_UNORDERED;
01439 
01440           /* no stream seq num on unordered chunks!
01441            */
01442         }
01443       else
01444         {
01445           ((SctpDataChunkHdr_S *) ucpChunk)->sHdr.ucFlags = 0;
01446           ((SctpDataChunkHdr_S *) ucpChunk)->usStreamSeqNum 
01447         = spOutStreams[spAppMessage->usStreamId].usNextStreamSeqNum++;
01448         }
01449     }
01450       else
01451     {
01452       DBG_PL (GenChunk, "eDataSource=DATA_SOURCE_INFINITE") DBG_PR; 
01453 
01454       iSize = uiDataChunkSize;
01455       ((SctpDataChunkHdr_S *) ucpChunk)->sHdr.ucType = SCTP_CHUNK_DATA;
01456 
01457       if(eUnordered == TRUE)
01458         {
01459           ((SctpDataChunkHdr_S *) ucpChunk)->sHdr.ucFlags 
01460         |= SCTP_DATA_FLAG_UNORDERED;
01461         }
01462       else
01463         {
01464           ((SctpDataChunkHdr_S *) ucpChunk)->sHdr.ucFlags = 0;
01465         }
01466 
01467       ((SctpDataChunkHdr_S *) ucpChunk)->sHdr.usLength = iSize;
01468 
01469       /* DATA chunks must be padded to a 4 byte boundary (section 3.3.1)
01470        * ...but the adjustment should happen after usLength field is set,
01471        * because the field should represent the size before padding.
01472        */
01473       if( (uiDataChunkSize % 4) != 0)
01474         iSize += 4 - (uiDataChunkSize % 4);
01475 
01476       ((SctpDataChunkHdr_S *) ucpChunk)->uiTsn = ++uiNextTsn;
01477       
01478       ((SctpDataChunkHdr_S *) ucpChunk)->usStreamId 
01479         = (usNextStreamId % uiNumOutStreams); 
01480 
01481       if(eUnordered == TRUE)
01482         {
01483           ((SctpDataChunkHdr_S *) ucpChunk)->sHdr.ucFlags 
01484         |= SCTP_DATA_FLAG_UNORDERED;
01485 
01486           /* no stream seq num on unordered chunks!
01487            */
01488         }
01489       else
01490         {
01491           ((SctpDataChunkHdr_S *) ucpChunk)->sHdr.ucFlags = 0;
01492           ((SctpDataChunkHdr_S *) ucpChunk)->usStreamSeqNum 
01493         = spOutStreams[usNextStreamId%uiNumOutStreams].usNextStreamSeqNum++;
01494         }
01495       
01496       usNextStreamId++; // round-robin stream feeding
01497     }
01498 
01499       /* fill in tracing fields too
01500        */
01501       spSctpTrace[uiNumChunks].eType = eType;
01502       spSctpTrace[uiNumChunks].uiTsn = ((SctpDataChunkHdr_S *) ucpChunk)->uiTsn;
01503       spSctpTrace[uiNumChunks].usStreamId 
01504     = ((SctpDataChunkHdr_S *) ucpChunk)->usStreamId;
01505       spSctpTrace[uiNumChunks].usStreamSeqNum 
01506     = ((SctpDataChunkHdr_S *) ucpChunk)->usStreamSeqNum;
01507 
01508       DBG_PL(GenChunk, "DataTsn=%d at dCurrTime=%f"), 
01509     uiNextTsn, dCurrTime DBG_PR;
01510       break;
01511 
01512     case SCTP_CHUNK_SACK:
01513       iSize = sizeof(SctpSackChunk_S);
01514       ((SctpSackChunk_S *) ucpChunk)->sHdr.ucType = eType;
01515       ((SctpSackChunk_S *) ucpChunk)->uiCumAck = uiCumAck;
01516       ((SctpSackChunk_S *) ucpChunk)->uiArwnd = uiMyRwnd;
01517       ((SctpSackChunk_S *) ucpChunk)->usNumGapAckBlocks 
01518     = sRecvTsnBlockList.uiLength;
01519       ((SctpSackChunk_S *) ucpChunk)->usNumDupTsns = sDupTsnList.uiLength;
01520 
01521       DBG_PL(GenChunk, "SACK CumAck=%d arwnd=%d"), uiCumAck, uiMyRwnd DBG_PR;
01522 
01523       /* Append all the Gap Ack Blocks
01524        */
01525       for(Node_S *spCurrFrag = sRecvTsnBlockList.spHead;
01526       (spCurrFrag != NULL) &&
01527         (iSize + sizeof(SctpGapAckBlock_S) < uiMaxDataSize); 
01528       spCurrFrag = spCurrFrag->spNext, iSize += sizeof(SctpGapAckBlock_S) )
01529     {
01530       SctpGapAckBlock_S *spGapAckBlock 
01531         = (SctpGapAckBlock_S *) (ucpChunk+iSize);
01532 
01533       spGapAckBlock->usStartOffset 
01534         = ((SctpRecvTsnBlock_S *)spCurrFrag->vpData)->uiStartTsn - uiCumAck;
01535 
01536       spGapAckBlock->usEndOffset
01537         = ((SctpRecvTsnBlock_S *)spCurrFrag->vpData)->uiEndTsn - uiCumAck;
01538 
01539       DBG_PL(GenChunk, "GapAckBlock StartOffset=%d EndOffset=%d"), 
01540         spGapAckBlock->usStartOffset, spGapAckBlock->usEndOffset DBG_PR;
01541     }
01542 
01543       /* Append all the Duplicate TSNs
01544        */
01545       for(Node_S *spPrevDup = NULL, *spCurrDup = sDupTsnList.spHead;
01546       (spCurrDup != NULL) &&
01547         (iSize + sizeof(SctpDupTsn_S) < uiMaxDataSize); 
01548       spPrevDup = spCurrDup, spCurrDup = spCurrDup->spNext, 
01549         DeleteNode(&sDupTsnList, spPrevDup), iSize += sizeof(SctpDupTsn_S) )
01550     {
01551       SctpDupTsn_S *spDupTsn = (SctpDupTsn_S *) (ucpChunk+iSize);
01552 
01553       spDupTsn->uiTsn = ((SctpDupTsn_S *) spCurrDup->vpData)->uiTsn;
01554 
01555       DBG_PL(GenChunk, "DupTsn=%d"), spDupTsn->uiTsn DBG_PR;
01556     }
01557 
01558       /* After all the dynamic appending, we can NOW fill in the chunk size!
01559        */
01560       ((SctpSackChunk_S *) ucpChunk)->sHdr.usLength = iSize;
01561 
01562       /* fill in tracing fields too
01563        */
01564       spSctpTrace[uiNumChunks].eType = eType;
01565       spSctpTrace[uiNumChunks].uiTsn = ((SctpSackChunk_S *) ucpChunk)->uiCumAck;
01566       spSctpTrace[uiNumChunks].usStreamId = (u_short) -1;
01567       spSctpTrace[uiNumChunks].usStreamSeqNum = (u_short) -1;
01568       break;
01569 
01570     case SCTP_CHUNK_FORWARD_TSN:
01571       iSize = SCTP_CHUNK_FORWARD_TSN_LENGTH;
01572       ((SctpForwardTsnChunk_S *) ucpChunk)->sHdr.ucType = eType;
01573       ((SctpForwardTsnChunk_S *) ucpChunk)->sHdr.ucFlags = 0; // flags not used?
01574       ((SctpForwardTsnChunk_S *) ucpChunk)->sHdr.usLength = iSize;
01575       ((SctpForwardTsnChunk_S *) ucpChunk)->uiNewCum = uiAdvancedPeerAckPoint;
01576 
01577       /* fill in tracing fields too
01578        */
01579       spSctpTrace[uiNumChunks].eType = eType;
01580       spSctpTrace[uiNumChunks].uiTsn = uiAdvancedPeerAckPoint;
01581       spSctpTrace[uiNumChunks].usStreamId = (u_short) -1;
01582       spSctpTrace[uiNumChunks].usStreamSeqNum = (u_short) -1;
01583       break;
01584 
01585     case SCTP_CHUNK_HB:     
01586     case SCTP_CHUNK_HB_ACK:
01587       iSize = SCTP_CHUNK_HEARTBEAT_LENGTH;
01588       ((SctpHeartbeatChunk_S *) ucpChunk)->sHdr.ucType = eType;
01589       ((SctpHeartbeatChunk_S *) ucpChunk)->sHdr.ucFlags = 0;
01590       ((SctpHeartbeatChunk_S *) ucpChunk)->sHdr.usLength = iSize;
01591       ((SctpHeartbeatChunk_S *) ucpChunk)->usInfoType = 1;
01592       ((SctpHeartbeatChunk_S *) ucpChunk)->usInfoLength 
01593     = iSize - sizeof(SctpChunkHdr_S);
01594       ((SctpHeartbeatChunk_S *) ucpChunk)->dTimestamp = dCurrTime;
01595       ((SctpHeartbeatChunk_S *) ucpChunk)->spDest = NULL; // caller sets dest
01596 
01597       /* fill in tracing fields too
01598        */
01599       spSctpTrace[uiNumChunks].eType = eType;
01600       spSctpTrace[uiNumChunks].uiTsn = (u_int) -1;
01601       spSctpTrace[uiNumChunks].usStreamId = (u_short) -1;
01602       spSctpTrace[uiNumChunks].usStreamSeqNum = (u_short) -1;
01603       break;
01604 
01605     case SCTP_CHUNK_SHUTDOWN:
01606       break;
01607 
01608     case SCTP_CHUNK_SHUTDOWN_ACK:
01609       break;
01610 
01611     case SCTP_CHUNK_SHUTDOWN_COMPLETE:
01612       break;
01613 
01614     default:
01615       fprintf(stderr, "[GenChunk] ERROR: bad chunk type!\n");
01616       DBG_PL(GenChunk, "ERROR: bad chunk type!") DBG_PR;
01617       DBG_PL(GenChunk, "exiting...") DBG_PR;
01618       exit(-1);
01619     }
01620 
01621   uiNumChunks++;
01622 
01623   DBG_X(GenChunk);
01624   return iSize;
01625 }
01626 
01627 u_int SctpAgent::GetNextDataChunkSize()
01628 {
01629   DBG_I(GetNextDataChunkSize);
01630 
01631   AppData_S *spAppMessage = NULL;
01632   u_int uiChunkSize = 0;
01633 
01634   if(eDataSource == DATA_SOURCE_APPLICATION)
01635     {
01636       if(sAppLayerBuffer.uiLength == 0)
01637     {
01638       uiChunkSize = 0;
01639     }
01640       else
01641     {
01642       spAppMessage = (AppData_S *) (sAppLayerBuffer.spHead->vpData);
01643       uiChunkSize = spAppMessage->uiNumBytes + sizeof(SctpDataChunkHdr_S);
01644     }
01645     }
01646   else
01647     {
01648       uiChunkSize = uiDataChunkSize;
01649     }
01650 
01651   /* DATA chunks must be padded to a 4 byte boundary (section 3.3.1)
01652    */
01653   if( (uiChunkSize % 4) != 0)
01654     uiChunkSize += 4 - (uiChunkSize % 4);
01655 
01656   DBG_PL(GetNextDataChunkSize, "returning %d"), uiChunkSize DBG_PR;
01657   DBG_X(GetNextDataChunkSize);
01658   return uiChunkSize;
01659 }
01660 
01661 /* This function generates ONE data chunk. Since we could call GenChunk directly
01662  * with only one extra parameter, it may seem pointless to have this function.
01663  * However, it isn't! All variable adjustments that go with generating a new
01664  * data chunk should be isolated in one place to avoid bugs.
01665  */
01666 int SctpAgent::GenOneDataChunk(u_char *ucpOutData)
01667 {
01668   DBG_I(GenOneDataChunk);
01669 
01670   AppData_S *spAppData = NULL;
01671   int iChunkSize = GenChunk(SCTP_CHUNK_DATA, ucpOutData);
01672 
01673   if(eDataSource == DATA_SOURCE_APPLICATION)
01674     {
01675       spAppData = (AppData_S *) sAppLayerBuffer.spHead->vpData;
01676       AddToSendBuffer( (SctpDataChunkHdr_S *) ucpOutData, iChunkSize, 
01677                spAppData->usReliability, spNewTxDest);
01678       DeleteNode(&sAppLayerBuffer, sAppLayerBuffer.spHead);
01679     }
01680   else
01681     {
01682       AddToSendBuffer( (SctpDataChunkHdr_S *) ucpOutData, iChunkSize, 
01683                uiReliability, spNewTxDest);
01684     }
01685   
01686   DBG_PL(GenOneDataChunk, "dest=%p"), spNewTxDest DBG_PR;
01687   spNewTxDest->iOutstandingBytes 
01688     += ((SctpDataChunkHdr_S *) ucpOutData)->sHdr.usLength;
01689 
01690   /* rfc2960 section 6.2.1.B
01691    */
01692   if(iChunkSize <= (int) uiPeerRwnd)
01693     uiPeerRwnd -= iChunkSize; 
01694   else
01695     uiPeerRwnd = 0;
01696 
01697   if(spNewTxDest->eRtxTimerIsRunning == FALSE)
01698     StartT3RtxTimer(spNewTxDest); //section 6.3.2.R1
01699 
01700   DBG_X(GenOneDataChunk);
01701   return iChunkSize;
01702 }
01703 
01704 /* This function fills a packet with as many chunks that can fit into the PMTU 
01705  * and the peer's rwnd.
01706  *
01707  * Since, this function is called the first time a chunk(s) is sent, all chunks
01708  * generated here are added to the sSendBuffer.
01709  *
01710  * returns the resulting size of the packet
01711  */
01712 int SctpAgent::GenMultipleDataChunks(u_char *ucpOutData, int iTotalOutDataSize)
01713 {
01714   DBG_I(GenMultipleDataChunks);
01715   
01716   int iChunkSize = 0;
01717   int iStartingTotalSize = iTotalOutDataSize;
01718 
01719   while((iTotalOutDataSize + GetNextDataChunkSize()  <= uiMaxDataSize) &&
01720     (GetNextDataChunkSize()                      <= uiPeerRwnd) &&
01721     (eDataSource == DATA_SOURCE_INFINITE || sAppLayerBuffer.uiLength != 0))
01722     {
01723       iChunkSize = GenOneDataChunk(ucpOutData);
01724       ucpOutData += iChunkSize;
01725       iTotalOutDataSize += iChunkSize;
01726     }
01727   
01728   DBG_X(GenMultipleDataChunks);
01729   return iTotalOutDataSize - iStartingTotalSize;
01730 }
01731 
01732 /* This function serves as a hook for extensions (such as Timestamp) which
01733  * need to bundle extra control chunks. However, it can later be used to
01734  * actually bundle SACKs, FOWARD_TSN, DATA w/ COOKIE and
01735  * COOKIE_ECHO. Since we don't currently bundle any control chunks in the
01736  * base SCTP, this function just returns 0 (ie, no bundled chunks).
01737  *
01738  * returns the aggregate size of the control chunk(s)
01739  */
01740 int SctpAgent::BundleControlChunks(u_char *ucpOutData)
01741 {
01742   DBG_I(BundleControlChunks);
01743   DBG_PL(BundleControlChunks, "None... returning 0") DBG_PR;
01744   DBG_X(BundleControlChunks);
01745   return 0;
01746 }
01747 
01748 void SctpAgent::StartT3RtxTimer(SctpDest_S *spDest)
01749 {
01750   DBG_I(StartT3RtxTimer);
01751   double dCurrTime = Scheduler::instance().clock();
01752   DBG_PL(StartT3RtxTimer, "spDest=%p dCurrTime=%f expires at %f "), 
01753     spDest, dCurrTime, dCurrTime+spDest->dRto DBG_PR;
01754   spDest->opT3RtxTimer->resched(spDest->dRto);
01755   spDest->eRtxTimerIsRunning = TRUE;
01756   DBG_X(StartT3RtxTimer);
01757 }
01758 
01759 void SctpAgent::StopT3RtxTimer(SctpDest_S *spDest)
01760 {
01761   DBG_I(StopT3RtxTimer);
01762   double dCurrTime = Scheduler::instance().clock();
01763   DBG_PL(StopT3RtxTimer, "spDest=%p dCurrTime=%f"), spDest, dCurrTime DBG_PR;
01764   spDest->opT3RtxTimer->force_cancel();
01765   spDest->eRtxTimerIsRunning = FALSE;
01766   DBG_X(StopT3RtxTimer);
01767 }
01768 
01769 void SctpAgent::AddToSendBuffer(SctpDataChunkHdr_S *spChunk, 
01770                 int iChunkSize,
01771                 u_int uiReliability,
01772                 SctpDest_S *spDest)
01773 {
01774   DBG_I(AddToSendBuffer);
01775   DBG_PL(AddToSendBuffer, "spDest=%p  iChunkSize=%d"), 
01776     spDest, iChunkSize DBG_PR;
01777 
01778   Node_S *spNewNode = new Node_S;
01779   spNewNode->eType = NODE_TYPE_SEND_BUFFER;
01780   spNewNode->vpData = new SctpSendBufferNode_S;
01781 
01782   SctpSendBufferNode_S * spNewNodeData 
01783     = (SctpSendBufferNode_S *) spNewNode->vpData;
01784 
01785   /* This can NOT simply be a 'new SctpDataChunkHdr_S', because we need to
01786    * allocate the space for the ENTIRE data chunk and not just the data
01787    * chunk header.  
01788    */
01789   spNewNodeData->spChunk = (SctpDataChunkHdr_S *) new u_char[iChunkSize];
01790   memcpy(spNewNodeData->spChunk, spChunk, iChunkSize);
01791 
01792   spNewNodeData->eAdvancedAcked = FALSE;
01793   spNewNodeData->eGapAcked = FALSE;
01794   spNewNodeData->eAddedToPartialBytesAcked = FALSE;
01795   spNewNodeData->iNumMissingReports = 0;
01796   spNewNodeData->iUnrelRtxLimit = uiReliability;
01797   spNewNodeData->eMarkedForRtx = NO_RTX;
01798   spNewNodeData->eIneligibleForFastRtx = FALSE;
01799   spNewNodeData->iNumTxs = 1;
01800   spNewNodeData->spDest = spDest;
01801 
01802   /* Is there already a DATA chunk in flight measuring an RTT? 
01803    * (6.3.1.C4 RTT measured once per round trip)
01804    */
01805   if(spDest->eRtoPending == FALSE)  // NO?
01806     {
01807       spNewNodeData->dTxTimestamp = Scheduler::instance().clock();
01808       spDest->eRtoPending = TRUE;   // ...well now there is :-)
01809     }
01810   else
01811     spNewNodeData->dTxTimestamp = 0; // don't use this check for RTT estimate
01812 
01813   InsertNode(&sSendBuffer, sSendBuffer.spTail, spNewNode, NULL);
01814 
01815   DBG_X(AddToSendBuffer);
01816 }
01817 
01818 void SctpAgent::RttUpdate(double dTxTime, SctpDest_S *spDest)
01819 {
01820   DBG_I(RttUpdate);
01821 
01822   double dNewRtt;
01823   double dCurrTime = Scheduler::instance().clock();
01824 
01825   dNewRtt = dCurrTime - dTxTime;
01826       
01827   if(spDest->eFirstRttMeasurement == TRUE) // section 6.3.1.C2
01828     {
01829       /* Bug Fix. eFirstRttMeasurement should be set to FALSE here.
01830        * 06/11/2001 - PDA and JRI
01831        */
01832       spDest->eFirstRttMeasurement = FALSE; 
01833       spDest->dSrtt = dNewRtt;
01834       spDest->dRttVar = dNewRtt/2;
01835       spDest->dRto = spDest->dSrtt + 4 * spDest->dRttVar;
01836     }
01837   else //section 6.3.1.C3
01838     {
01839       spDest->dRttVar 
01840     = ( (1 - RTO_BETA) * spDest->dRttVar 
01841         + RTO_BETA * abs(spDest->dSrtt - dNewRtt) );
01842 
01843       spDest->dSrtt 
01844     = (1 - RTO_ALPHA) * spDest->dSrtt + RTO_ALPHA * dNewRtt;
01845         
01846       spDest->dRto = spDest->dSrtt + 4 * spDest->dRttVar;
01847     }
01848 
01849   if(spDest->dRto < dMinRto)  // section 6.3.1.C6
01850     spDest->dRto = dMinRto;
01851   else if(spDest->dRto > dMaxRto)  // section 6.3.1.C7
01852     spDest->dRto = dMaxRto;
01853   tdRto++; // trigger changes for trace to pick up
01854 
01855   DBG_PL(RttUpdate, "spDest->dRto=%f"), spDest->dRto DBG_PR;
01856   DBG_X(RttUpdate);
01857 }
01858 
01859 /* Go thru the send buffer deleting all chunks which have a tsn <= the 
01860  * tsn parameter passed in. We assume the chunks in the rtx list are ordered by 
01861  * their tsn value. In addtion, for each chunk deleted:
01862  *   1. we add the chunk length to # newly acked bytes and partial bytes acked
01863  *   2. we update round trip time if appropriate
01864  *   3. stop the timer if the chunk's destination timer is running
01865  */
01866 void SctpAgent::SendBufferDequeueUpTo(u_int uiTsn)
01867 {
01868   DBG_I(SendBufferDequeueUpTo);
01869 
01870   Node_S *spDeleteNode = NULL;
01871   Node_S *spCurrNode = sSendBuffer.spHead;
01872   SctpSendBufferNode_S *spCurrNodeData = NULL;
01873 
01874   iAssocErrorCount = 0;
01875 
01876   while(spCurrNode != NULL &&
01877     ((SctpSendBufferNode_S*)spCurrNode->vpData)->spChunk->uiTsn <= uiTsn)
01878     {
01879       spCurrNodeData = (SctpSendBufferNode_S *) spCurrNode->vpData;
01880 
01881       /* Only count this chunk as newly acked and towards partial bytes
01882        * acked if it hasn't been gap acked or marked as ack'd due to rtx
01883        * limit.  
01884        */
01885       if((spCurrNodeData->eGapAcked == FALSE) &&
01886      (spCurrNodeData->eAdvancedAcked == FALSE) )
01887     {
01888       uiHighestTsnNewlyAcked = spCurrNodeData->spChunk->uiTsn;
01889 
01890       spCurrNodeData->spDest->iNumNewlyAckedBytes 
01891         += spCurrNodeData->spChunk->sHdr.usLength;
01892 
01893       /* only add to partial bytes acked if we are in congestion
01894        * avoidance mode and if there was cwnd amount of data
01895        * outstanding on the destination (implementor's guide) 
01896        */
01897       if(spCurrNodeData->spDest->iCwnd >spCurrNodeData->spDest->iSsthresh &&
01898          ( spCurrNodeData->spDest->iOutstandingBytes 
01899            >= spCurrNodeData->spDest->iCwnd) )
01900         {
01901           spCurrNodeData->spDest->iPartialBytesAcked 
01902         += spCurrNodeData->spChunk->sHdr.usLength;
01903         }
01904     }
01905 
01906 
01907       /* This is to ensure that Max.Burst is applied when a SACK
01908        * acknowledges a chunk which has been fast retransmitted. If it is
01909        * ineligible for fast rtx, that can only be because it was fast
01910        * rtxed or it timed out. If it timed out, a burst shouldn't be
01911        * possible, but shouldn't hurt either. The fast rtx case is what we
01912        * are really after. This is a proposed change to RFC2960 section
01913        * 7.2.4
01914        */
01915       if(spCurrNodeData->eIneligibleForFastRtx == TRUE)
01916     eApplyMaxBurst = TRUE;
01917         
01918       /* We update the RTT estimate if the following hold true:
01919        *   1. RTO pending flag is set (6.3.1.C4 measured once per round trip)
01920        *   2. Timestamp is set for this chunk (ie, we were measuring this chunk)
01921        *   3. This chunk has not been retransmitted
01922        *   4. This chunk has not been gap acked already 
01923        *   5. This chunk has not been advanced acked (pr-sctp: exhausted rtxs)
01924        */
01925       if(spCurrNodeData->spDest->eRtoPending == TRUE &&
01926      spCurrNodeData->dTxTimestamp > 0 &&
01927      spCurrNodeData->iNumTxs == 1 &&
01928      spCurrNodeData->eGapAcked == FALSE &&
01929      spCurrNodeData->eAdvancedAcked == FALSE) 
01930     {
01931       /* If the chunk is marked for timeout rtx, then the sender is an 
01932        * ambigious state. Were the sacks lost or was there a failure?
01933        * Since we don't clear the error counter below, we also don't
01934        * update the RTT. This could be a problem for late arriving SACKs.
01935        */
01936       if(spCurrNodeData->eMarkedForRtx != TIMEOUT_RTX)
01937         RttUpdate(spCurrNodeData->dTxTimestamp, spCurrNodeData->spDest);
01938       spCurrNodeData->spDest->eRtoPending = FALSE;
01939     }
01940 
01941       /* if there is a timer running on the chunk's destination, then stop it
01942        */
01943       if(spCurrNodeData->spDest->eRtxTimerIsRunning == TRUE)
01944     StopT3RtxTimer(spCurrNodeData->spDest);
01945 
01946       /* We don't want to clear the error counter if it's cleared already;
01947        * otherwise, we'll unnecessarily trigger a trace event.
01948        *
01949        * Also, the error counter is cleared by SACKed data ONLY if the
01950        * TSNs are not marked for timeout retransmission and has not been
01951        * gap acked before. Without this condition, we can run into a
01952        * problem for failure detection. When a failure occurs, some data
01953        * may have made it through before the failure, but the sacks got
01954        * lost. When the sender retransmits the first outstanding, the
01955        * receiver will sack all the data whose sacks got lost. We don't
01956        * want these sacks to clear the error counter, or else failover
01957        * would take longer.
01958        */
01959       if(spCurrNodeData->spDest->iErrorCount != 0 &&
01960      spCurrNodeData->eMarkedForRtx != TIMEOUT_RTX &&
01961      spCurrNodeData->eGapAcked == FALSE)
01962     {
01963       DBG_PL(SendBufferDequeueUpTo, 
01964          "clearing error counter for %p with tsn=%lu"), 
01965         spCurrNodeData->spDest, spCurrNodeData->spChunk->uiTsn DBG_PR;
01966 
01967       spCurrNodeData->spDest->iErrorCount = 0; // clear error counter
01968       tiErrorCount++;                          // ... and trace it too!
01969       spCurrNodeData->spDest->eStatus = SCTP_DEST_STATUS_ACTIVE;
01970       if(spCurrNodeData->spDest == spPrimaryDest &&
01971          spNewTxDest != spPrimaryDest) 
01972         {
01973           DBG_PL(SendBufferDequeueUpTo,
01974              "primary recovered... migrating back from %p to %p"),
01975         spNewTxDest, spPrimaryDest DBG_PR;
01976           spNewTxDest = spPrimaryDest; // return to primary
01977         }
01978     }
01979 
01980       spDeleteNode = spCurrNode;
01981       spCurrNode = spCurrNode->spNext;
01982       DeleteNode(&sSendBuffer, spDeleteNode);
01983       spDeleteNode = NULL;
01984     }
01985 
01986   DBG_X(SendBufferDequeueUpTo);
01987 }
01988 
01989 /* This function uses the iOutstandingBytes variable and assumes that it has NOT
01990  * been updated yet to reflect the newly received SACK. Hence, it is the 
01991  * previously outstanding DATA bytes.
01992  */
01993 void SctpAgent::AdjustCwnd(SctpDest_S *spDest)
01994 {
01995   DBG_I(AdjustCwnd);
01996 
01997   if(spDest->iCwnd <= spDest->iSsthresh) //in slow-start mode?
01998     {
01999       DBG_PL(AdjustCwnd, "slow start mode") DBG_PR;
02000       DBG_PL(AdjustCwnd, "iSsthresh=%d"), spDest->iSsthresh DBG_PR;
02001 
02002       /* section 7.2.1 (w/ implementor's guide)
02003        */
02004       if(spDest->iOutstandingBytes >= spDest->iCwnd)
02005     {
02006       spDest->iCwnd += MIN(spDest->iNumNewlyAckedBytes, (int)uiMaxDataSize);
02007       tiCwnd++; // trigger changes for trace to pick up
02008     }
02009     }
02010   else // congestion avoidance mode
02011     {
02012       DBG_PL(AdjustCwnd,"congestion avoidance mode") DBG_PR;
02013       DBG_PL(AdjustCwnd,"iPartialBytesAcked=%d iCwnd=%d iOutStandingBytes=%d"),
02014     spDest->iPartialBytesAcked, 
02015     spDest->iCwnd, 
02016     spDest->iOutstandingBytes
02017     DBG_PR;
02018 
02019       /* section 7.2.2
02020        */
02021       if(spDest->iPartialBytesAcked >= spDest->iCwnd &&
02022      spDest->iOutstandingBytes >= spDest->iCwnd )
02023     {
02024       DBG_PL(AdjustCwnd, "adjusting cwnd") DBG_PR;
02025       if(spDest->iCwnd <= spDest->iPartialBytesAcked)
02026           spDest->iPartialBytesAcked -= spDest->iCwnd;
02027       else
02028         spDest->iPartialBytesAcked = 0;
02029       spDest->iCwnd += uiMaxDataSize;
02030       tiCwnd++; // trigger changes for trace to pick up
02031     }
02032     }   
02033 
02034   DBG_PL(AdjustCwnd, "pba=%d cwnd=%d out=%d PeerRwnd=%d"),
02035     spDest->iPartialBytesAcked, 
02036     spDest->iCwnd, 
02037     spDest->iOutstandingBytes,
02038     uiPeerRwnd
02039     DBG_PR;
02040   DBG_X(AdjustCwnd);
02041 }
02042 
02043 void SctpAgent::AdvancePeerAckPoint()
02044 {
02045   DBG_I(AdvancePeerAckPoint);
02046 
02047   Node_S *spCurrNode = NULL;
02048   SctpSendBufferNode_S *spCurrNodeData = NULL;
02049 
02050   if(uiAdvancedPeerAckPoint < uiCumAckPoint)
02051     uiAdvancedPeerAckPoint = uiCumAckPoint;
02052 
02053   for(spCurrNode = sSendBuffer.spHead;
02054       spCurrNode != NULL;
02055       spCurrNode = spCurrNode->spNext)
02056     {
02057       spCurrNodeData = (SctpSendBufferNode_S *) spCurrNode->vpData;
02058       
02059       /* If we haven't marked the chunk as ack'd either via an u-stream's
02060        * rtx exhaustion or a gap ack, then jump out of the loop. ...we've
02061        * advanced as far as we can!
02062        */
02063       if(spCurrNodeData->eAdvancedAcked == FALSE &&
02064      spCurrNodeData->eGapAcked == FALSE)
02065     break;
02066 
02067       uiAdvancedPeerAckPoint = spCurrNodeData->spChunk->uiTsn;
02068     }
02069 
02070   DBG_PL(AdvancePeerAckPoint, "uiAdvancedPeerAckPoint=%d"), 
02071     uiAdvancedPeerAckPoint DBG_PR;
02072   DBG_X(AdvancePeerAckPoint);
02073 }
02074 
02075 u_int SctpAgent::GetHighestOutstandingTsn()
02076 {
02077   DBG_I(GetHighestOutstandingTsn);
02078 
02079   u_int uiHighestOutstandingTsn = 0;
02080   Node_S *spCurrBuffNode = NULL;
02081   SctpSendBufferNode_S *spCurrBuffData = NULL;
02082 
02083   /* start from the tailof the send buffer and search towards the head for the
02084    * first tsn oustanding... that's the highest outstanding tsn.
02085    */
02086   for(spCurrBuffNode = sSendBuffer.spTail;
02087       spCurrBuffNode != NULL;
02088       spCurrBuffNode = spCurrBuffNode->spPrev)
02089     {
02090       spCurrBuffData = (SctpSendBufferNode_S *) spCurrBuffNode->vpData;
02091       
02092       if(spCurrBuffData->eMarkedForRtx == NO_RTX)  // is it oustanding?
02093     {
02094       uiHighestOutstandingTsn = spCurrBuffData->spChunk->uiTsn; // found it!
02095       break;
02096     }
02097     }
02098 
02099   DBG_PL(GetHighestOutstandingTsn, "uiHighestOutstandingTsn=%d"), 
02100     uiHighestOutstandingTsn DBG_PR;
02101   DBG_X(GetHighestOutstandingTsn);
02102 
02103   return uiHighestOutstandingTsn;
02104 }
02105 
02106 /* This function is called as soon as we are done processing a SACK which 
02107  * notifies the need of a fast rtx. Following RFC2960, we pack as many chunks
02108  * as possible into one packet (PTMU restriction). The remaining marked packets
02109  * are sent as soon as cwnd allows.
02110  */
02111 void SctpAgent::FastRtx()
02112 {
02113   DBG_I(FastRtx);
02114   
02115   Node_S *spCurrDestNode = NULL;
02116   SctpDest_S *spCurrDestData = NULL;
02117   Node_S *spCurrBuffNode = NULL;
02118   SctpSendBufferNode_S *spCurrBuffData = NULL;
02119 
02120   /* be sure we clear all the eCcApplied flags before using them!
02121    */
02122   for(spCurrDestNode = sDestList.spHead;
02123       spCurrDestNode != NULL;
02124       spCurrDestNode = spCurrDestNode->spNext)
02125     {
02126       spCurrDestData = (SctpDest_S *) spCurrDestNode->vpData;
02127       spCurrDestData->eCcApplied = FALSE;
02128     }
02129   
02130   /* go thru chunks marked for rtx and cut back their ssthresh, cwnd, and
02131    * partial bytes acked. make sure we only apply congestion control once
02132    * per destination and once per window (ie, round-trip).
02133    */
02134   for(spCurrBuffNode = sSendBuffer.spHead;
02135       spCurrBuffNode != NULL;
02136       spCurrBuffNode = spCurrBuffNode->spNext)
02137     {
02138       spCurrBuffData = (SctpSendBufferNode_S *) spCurrBuffNode->vpData;
02139 
02140       /* If the chunk has been either marked for rtx or advanced ack, we want
02141        * to apply congestion control (assuming we didn't already).
02142        *
02143        * Why do we do it for advanced ack chunks? Well they were advanced ack'd
02144        * because they were lost. The ONLY reason we are not fast rtxing them is
02145        * because the chunk has run out of retransmissions (u-sctp). So we need
02146        * to still account for the fact they were lost... so apply congestion
02147        * control!
02148        */
02149       if( (spCurrBuffData->eMarkedForRtx != NO_RTX ||
02150       spCurrBuffData->eAdvancedAcked == TRUE) &&
02151      spCurrBuffData->spDest->eCcApplied == FALSE &&
02152      spCurrBuffData->spChunk->uiTsn > uiRecover)
02153      
02154     { 
02155       /* section 7.2.3 of rfc2960 (w/ implementor's guide) 
02156        */
02157       spCurrBuffData->spDest->iSsthresh 
02158         = MAX(spCurrBuffData->spDest->iCwnd/2, 
02159           iInitialCwnd * (int) uiMaxDataSize);
02160       spCurrBuffData->spDest->iCwnd = spCurrBuffData->spDest->iSsthresh;
02161       spCurrBuffData->spDest->iPartialBytesAcked = 0; //reset
02162       tiCwnd++; // trigger changes for trace to pick up
02163       spCurrBuffData->spDest->eCcApplied = TRUE;
02164 
02165       /* Cancel any pending RTT measurement on this
02166        * destination. Stephan Baucke (2004-04-27) suggested this
02167        * action as a fix for the following simple scenario:
02168        *
02169        * - Host A sends packets 1, 2 and 3 to host B, and choses 3 for
02170        *   an RTT measurement
02171        *
02172        * - Host B receives all packets correctly and sends ACK1, ACK2,
02173        *   and ACK3.
02174        *
02175        * - ACK2 and ACK3 are lost on the return path
02176        *
02177        * - Eventually a timeout fires for packet 2, and A retransmits 2
02178        *
02179        * - Upon receipt of 2, B sends a cumulative ACK3 (since it has
02180        *   received 2 & 3 before)
02181        *
02182        * - Since packet 3 has never been retransmitted, the SCTP code
02183        *   actually accepts the ACK for an RTT measurement, although it
02184        *   was sent in reply to the retransmission of 2, which results
02185        *   in a much too high RTT estimate. Since this case tends to
02186        *   happen in case of longer link interruptions, the error is
02187        *   often amplified by subsequent timer backoffs.
02188        */
02189       spCurrBuffData->spDest->eRtoPending = FALSE; 
02190 
02191       /* Set the recover variable to avoid multiple cwnd cuts for losses
02192        * in the same window (ie, round-trip).
02193        */
02194       uiRecover = GetHighestOutstandingTsn();
02195     }
02196     }
02197 
02198   /* possible that no chunks are pending retransmission since they could be 
02199    * advanced ack'd 
02200    */
02201   if(eMarkedChunksPending == TRUE)  
02202     RtxMarkedChunks(RTX_LIMIT_ONE_PACKET);
02203 
02204   DBG_X(FastRtx);
02205 }
02206 
02207 void SctpAgent::TimeoutRtx(SctpDest_S *spDest)
02208 {
02209   DBG_I(TimeoutRtx);
02210 
02211   Node_S *spCurrNode = NULL;
02212   SctpSendBufferNode_S *spCurrNodeData = NULL;
02213   
02214   DBG_PL(TimeoutRtx, "spDest=%p"), spDest DBG_PR;
02215 
02216   for(spCurrNode = sSendBuffer.spHead;
02217       spCurrNode != NULL;
02218       spCurrNode = spCurrNode->spNext)
02219     {
02220       spCurrNodeData = (SctpSendBufferNode_S *) spCurrNode->vpData;
02221 
02222       /* Mark chunks that were sent to the destination which had a timeout and
02223        * have NOT been gap ack'd or advanced.
02224        */
02225       if(spCurrNodeData->spDest == spDest &&
02226      spCurrNodeData->eGapAcked == FALSE &&
02227      spCurrNodeData->eAdvancedAcked == FALSE)
02228     {
02229       MarkChunkForRtx(spCurrNodeData, TIMEOUT_RTX);
02230       spCurrNodeData->iNumMissingReports = 0;
02231     }
02232     }
02233 
02234   if(eMarkedChunksPending == TRUE)  
02235     RtxMarkedChunks(RTX_LIMIT_ONE_PACKET);
02236 
02237   DBG_X(TimeoutRtx);
02238 }
02239 
02240 void SctpAgent::MarkChunkForRtx(SctpSendBufferNode_S *spNodeData,
02241                 MarkedForRtx_E eMarkedForRtx)
02242 {
02243   DBG_I(MarkChunkForRtx);
02244 
02245   SctpDataChunkHdr_S  *spChunk = spNodeData->spChunk;
02246   SctpOutStream_S *spStream = &(spOutStreams[spChunk->usStreamId]);
02247 
02248   DBG_PL(MarkChunkForRtx, "tsn=%lu eMarkedForRtx=%s"), 
02249     spChunk->uiTsn,
02250     !eMarkedForRtx ? "NO_RTX" 
02251     : (eMarkedForRtx==FAST_RTX ? "FAST_RTX": "TIMEOUT_RTX") DBG_PR;
02252 
02253   spNodeData->eMarkedForRtx = eMarkedForRtx;
02254   uiPeerRwnd += spChunk->sHdr.usLength; // 6.2.1.C1 
02255 
02256   /* let's see if this chunk is on an unreliable stream. if so and the chunk has
02257    * run out of retransmissions, mark it as advanced acked and unmark it for rtx
02258    */
02259   if(spStream->eMode == SCTP_STREAM_UNRELIABLE)
02260     {
02261       /* have we run out of retransmissions??
02262        */
02263       if(spNodeData->iNumTxs > spNodeData->iUnrelRtxLimit)
02264     {
02265       DBG_PL(MarkChunkForRtx, "giving up on tsn %lu..."),
02266         spChunk->uiTsn DBG_PR;
02267 
02268       spNodeData->eAdvancedAcked = TRUE;
02269       spNodeData->eMarkedForRtx = NO_RTX;
02270       spNodeData->spDest->iOutstandingBytes -= spChunk->sHdr.usLength;
02271     }
02272     }
02273   
02274   if(spNodeData->eMarkedForRtx != NO_RTX)
02275     eMarkedChunksPending = TRUE;
02276 
02277   DBG_PL(MarkChunkForRtx, "uiPeerRwnd=%lu"), uiPeerRwnd DBG_PR;
02278   DBG_X(MarkChunkForRtx);
02279 }
02280 
02281 Boolean_E SctpAgent::AnyMarkedChunks()
02282 {
02283   DBG_I(AnyMarkedChunks);
02284 
02285   Node_S *spCurrBuffNode = NULL;
02286   SctpSendBufferNode_S *spCurrBuffNodeData = NULL;
02287 
02288   for(spCurrBuffNode = sSendBuffer.spHead;
02289       spCurrBuffNode != NULL; 
02290       spCurrBuffNode = spCurrBuffNode->spNext)
02291     {
02292       spCurrBuffNodeData = (SctpSendBufferNode_S *) spCurrBuffNode->vpData;
02293       if(spCurrBuffNodeData->eMarkedForRtx != NO_RTX)
02294     {
02295       DBG_PL(AnyMarkedChunks, "TRUE") DBG_PR;
02296       DBG_X(AnyMarkedChunks);
02297       return TRUE;
02298     }
02299     }
02300 
02301   DBG_PL(AnyMarkedChunks, "FALSE") DBG_PR;
02302   DBG_X(AnyMarkedChunks);
02303   return FALSE;
02304 }
02305 
02306 /* This function goes through the entire send buffer filling a packet with 
02307  * chunks marked for retransmission. Once a packet is full (according to MTU)
02308  * it is transmittted. If the eLimit is one packet, than that is all that is
02309  * done. If the eLimit is cwnd, then packets full of marked tsns are sent until
02310  * cwnd is full.
02311  */
02312 void SctpAgent::RtxMarkedChunks(SctpRtxLimit_E eLimit)
02313 {
02314   DBG_I(RtxMarkedChunks);
02315 
02316   u_char ucpOutData[uiMaxPayloadSize]; 
02317   u_char *ucpCurrOutData = ucpOutData;
02318   int iBundledControlChunkSize = 0;
02319   int iCurrSize = 0;
02320   int iOutDataSize = 0;
02321   Node_S *spCurrBuffNode = NULL;
02322   SctpSendBufferNode_S *spCurrBuffNodeData = NULL;
02323   SctpDataChunkHdr_S  *spCurrChunk;
02324   SctpDest_S *spRtxDest = NULL;
02325   Node_S *spCurrDestNode = NULL;
02326   SctpDest_S *spCurrDestNodeData = NULL;
02327   Boolean_E eControlChunkBundled = FALSE;
02328   int iNumPacketsSent = 0;
02329 
02330   memset(ucpOutData, 0, uiMaxPayloadSize);
02331 
02332   uiBurstLength = 0;
02333 
02334   /* make sure we clear all the spFirstOutstanding pointers before using them!
02335    */
02336   for(spCurrDestNode = sDestList.spHead;
02337       spCurrDestNode != NULL;
02338       spCurrDestNode = spCurrDestNode->spNext)
02339     {
02340       spCurrDestNodeData = (SctpDest_S *) spCurrDestNode->vpData;
02341       spCurrDestNodeData->spFirstOutstanding = NULL;  // reset
02342     }
02343 
02344   /* We need to set the destination address for the retransmission(s). We assume
02345    * that on a given call to this function, all should all be sent to the same
02346    * address (should be a reasonable assumption). So, to determine the address,
02347    * we find the first marked chunk and determine the destination it was last 
02348    * sent to. 
02349    *
02350    * Also, we temporarily count all marked chunks as not outstanding. Why? Well,
02351    * if we try retransmitting on the same dest as used previously, the cwnd may
02352    * never let us retransmit because the outstanding is counting marked chunks
02353    * too. At the end of this function, we'll count all marked chunks as 
02354    * outstanding again. (ugh... there has to be a better way!)
02355    */
02356   for(spCurrBuffNode = sSendBuffer.spHead; 
02357       spCurrBuffNode != NULL;
02358       spCurrBuffNode = spCurrBuffNode->spNext)
02359     {
02360       spCurrBuffNodeData = (SctpSendBufferNode_S *) spCurrBuffNode->vpData;
02361       
02362       if(spCurrBuffNodeData->eMarkedForRtx != NO_RTX)
02363     {
02364       spCurrChunk = spCurrBuffNodeData->spChunk;
02365 
02366       if(spRtxDest == NULL)
02367         {
02368           /* RFC2960 says that retransmissions should go to an
02369            * alternate destination when available, which is the
02370            * default behavior characterized by
02371            * eRtxToAlt=RTX_TO_ALT_ON. 
02372            *
02373            * We add two experimental options:
02374            *    1. rtx all data to same destination (RTX_TO_ALT_OFF)
02375            *    2. rtx only timeouts to alt dest (RTX_TO_ALT_TIMEOUTS_ONLY)
02376            *
02377            * Note: Even with these options, if the same dest is inactive,
02378            * then alt dest is used.
02379            */
02380           switch(eRtxToAlt)
02381         {
02382         case RTX_TO_ALT_OFF:
02383           if(spCurrBuffNodeData->spDest->eStatus 
02384              == SCTP_DEST_STATUS_ACTIVE)
02385             {
02386               spRtxDest = spCurrBuffNodeData->spDest;
02387             }
02388           else
02389             {
02390               spRtxDest = GetNextDest(spCurrBuffNodeData->spDest);
02391             }
02392           break;
02393           
02394         case RTX_TO_ALT_ON:
02395           spRtxDest = GetNextDest(spCurrBuffNodeData->spDest);
02396           break;
02397 
02398         case RTX_TO_ALT_TIMEOUTS_ONLY:
02399           if(spCurrBuffNodeData->eMarkedForRtx == FAST_RTX &&
02400              spCurrBuffNodeData->spDest->eStatus 
02401              == SCTP_DEST_STATUS_ACTIVE)
02402             {
02403               spRtxDest = spCurrBuffNodeData->spDest; 
02404             }
02405           else
02406             {
02407               spRtxDest = GetNextDest(spCurrBuffNodeData->spDest);
02408             }
02409           break;
02410         }
02411         }
02412 
02413       spCurrBuffNodeData->spDest->iOutstandingBytes
02414         -= spCurrChunk->sHdr.usLength;
02415     }
02416     }
02417 
02418   spCurrBuffNode = sSendBuffer.spHead;
02419 
02420   while( (eLimit == RTX_LIMIT_ONE_PACKET && 
02421       iNumPacketsSent < 1 && 
02422       spCurrBuffNode != NULL) ||
02423      (eLimit == RTX_LIMIT_CWND &&
02424       spRtxDest->iOutstandingBytes < spRtxDest->iCwnd &&
02425       spCurrBuffNode != NULL) )
02426     {
02427       DBG_PL(RtxMarkedChunks, 
02428          "eLimit=%s pktsSent=%d out=%d cwnd=%d spCurrBuffNode=%p"),
02429     (eLimit == RTX_LIMIT_ONE_PACKET) ? "ONE_PACKET" : "CWND",
02430     iNumPacketsSent, spRtxDest->iOutstandingBytes, spRtxDest->iCwnd,
02431     spCurrBuffNode
02432     DBG_PR;
02433       
02434       /* section 7.2.4.3
02435        *
02436        * continue filling up the packet with chunks which are marked for
02437        * rtx. exit loop when we have either run out of chunks or the
02438        * packet is full.
02439        *
02440        * note: we assume at least one data chunk fits in the packet.  
02441        */
02442       for(eControlChunkBundled = FALSE; 
02443       spCurrBuffNode != NULL; 
02444       spCurrBuffNode = spCurrBuffNode->spNext)
02445     {
02446       spCurrBuffNodeData = (SctpSendBufferNode_S *) spCurrBuffNode->vpData;
02447       
02448       /* is this chunk the first outstanding on its destination?
02449        */
02450         if(spCurrBuffNodeData->spDest->spFirstOutstanding == NULL &&
02451          spCurrBuffNodeData->eGapAcked == FALSE &&
02452          spCurrBuffNodeData->eAdvancedAcked == FALSE)
02453         {
02454           /* yes, it is the first!
02455            */
02456           spCurrBuffNodeData->spDest->spFirstOutstanding 
02457         = spCurrBuffNodeData;
02458         }
02459 
02460       /* Only retransmit the chunks which have been marked for rtx.
02461        */
02462       if(spCurrBuffNodeData->eMarkedForRtx != NO_RTX)
02463         {
02464           spCurrChunk = spCurrBuffNodeData->spChunk;
02465 
02466           /* bundle the control chunk before any data chunks and only
02467            * once per packet
02468            */
02469           if(eControlChunkBundled == FALSE)
02470         {
02471           eControlChunkBundled = TRUE;
02472           iBundledControlChunkSize =BundleControlChunks(ucpCurrOutData);
02473           ucpCurrOutData += iBundledControlChunkSize;
02474           iOutDataSize += iBundledControlChunkSize;
02475         }
02476 
02477           /* can we fit this chunk into the packet without exceeding MTU?? 
02478            */
02479           if((iOutDataSize + spCurrChunk->sHdr.usLength) 
02480          > (int) uiMaxPayloadSize)
02481         break;  // doesn't fit in packet... jump out of the for loop
02482 
02483           /* If this chunk was being used to measure the RTT, stop using it.
02484            */
02485           if(spCurrBuffNodeData->spDest->eRtoPending == TRUE &&
02486          spCurrBuffNodeData->dTxTimestamp > 0)
02487         {
02488           spCurrBuffNodeData->dTxTimestamp = 0;
02489           spCurrBuffNodeData->spDest->eRtoPending = FALSE;
02490         }
02491 
02492           /* section 7.2.4.4 (condition 2) - is this the first
02493            * outstanding for the destination and are there still
02494            * outstanding bytes on the destination? if so, restart
02495            * timer.  
02496            */
02497           if(spCurrBuffNodeData->spDest->spFirstOutstanding 
02498          == spCurrBuffNodeData)
02499         {
02500           if(spCurrBuffNodeData->spDest->iOutstandingBytes > 0)
02501             StartT3RtxTimer(spCurrBuffNodeData->spDest);
02502         }
02503           
02504           /* JRI, ALC - Bugfix 2004/01/21 - section 6.1 - Whenever a
02505            * transmission or retransmission is made to any address, if
02506            * the T3-rtx timer of that address is not currently
02507            * running, the sender MUST start that timer.  If the timer
02508            * for that address is already running, the sender MUST
02509            * restart the timer if the earliest (i.e., lowest TSN)
02510            * outstanding DATA chunk sent to that address is being
02511            * retransmitted.  Otherwise, the data sender MUST NOT
02512            * restart the timer.  
02513            */
02514           if(spRtxDest->spFirstOutstanding == NULL ||
02515          spCurrChunk->uiTsn <
02516          spRtxDest->spFirstOutstanding->spChunk->uiTsn)
02517         {
02518           /* This chunk is now the first outstanding on spRtxDest.
02519            */
02520           spRtxDest->spFirstOutstanding = spCurrBuffNodeData;
02521           StartT3RtxTimer(spRtxDest);
02522         }
02523           
02524           memcpy(ucpCurrOutData, spCurrChunk, spCurrChunk->sHdr.usLength);
02525           iCurrSize = spCurrChunk->sHdr.usLength;
02526           
02527           /* the chunk length field does not include the padded bytes,
02528            * so we need to account for these extra bytes.
02529            */
02530           if( (iCurrSize % 4) != 0 ) 
02531         iCurrSize += 4 - (iCurrSize % 4);
02532 
02533           ucpCurrOutData += iCurrSize;
02534           iOutDataSize += iCurrSize;
02535           spCurrBuffNodeData->spDest = spRtxDest;
02536           spCurrBuffNodeData->iNumTxs++;
02537           spCurrBuffNodeData->eMarkedForRtx = NO_RTX;
02538 
02539           /* fill in tracing fields too
02540            */
02541           spSctpTrace[uiNumChunks].eType = SCTP_CHUNK_DATA;
02542           spSctpTrace[uiNumChunks].uiTsn = spCurrChunk->uiTsn;
02543           spSctpTrace[uiNumChunks].usStreamId = spCurrChunk->usStreamId;
02544           spSctpTrace[uiNumChunks].usStreamSeqNum 
02545         = spCurrChunk->usStreamSeqNum;
02546           uiNumChunks++;
02547 
02548           /* the chunk is now outstanding on the alternate destination
02549            */
02550           spCurrBuffNodeData->spDest->iOutstandingBytes
02551         += spCurrChunk->sHdr.usLength;
02552           uiPeerRwnd -= spCurrChunk->sHdr.usLength; // 6.2.1.B
02553           DBG_PL(RtxMarkedChunks, "spDest->iOutstandingBytes=%d"), 
02554         spCurrBuffNodeData->spDest->iOutstandingBytes DBG_PR;
02555 
02556           DBG_PL(RtxMarkedChunks, "TSN=%d"), spCurrChunk->uiTsn DBG_PR;
02557         }
02558       else if(spCurrBuffNodeData->eAdvancedAcked == TRUE)
02559         {
02560           if(spCurrBuffNodeData->spDest->spFirstOutstanding 
02561          == spCurrBuffNodeData)
02562         {
02563           /* This WAS considered the first outstanding chunk for
02564            * the destination, then stop the timer if there are no
02565            * outstanding chunks waiting behind this one in the
02566            * send buffer.  However, if there ARE more outstanding
02567            * chunks on this destination, we need to restart timer
02568            * for those.
02569            */
02570           if(spCurrBuffNodeData->spDest->iOutstandingBytes > 0)
02571             StartT3RtxTimer(spCurrBuffNodeData->spDest);
02572           else
02573             StopT3RtxTimer(spCurrBuffNodeData->spDest);
02574         }
02575         }
02576     }
02577 
02578       /* Transmit the packet now...
02579        */
02580       if(iOutDataSize > 0)
02581     {
02582       SendPacket(ucpOutData, iOutDataSize, spRtxDest);
02583       if(spRtxDest->eRtxTimerIsRunning == FALSE)
02584         StartT3RtxTimer(spRtxDest);
02585       iNumPacketsSent++;      
02586       iOutDataSize = 0; // reset
02587       ucpCurrOutData = ucpOutData; // reset
02588       memset(ucpOutData, 0, uiMaxPayloadSize); // reset
02589 
02590       spRtxDest->opCwndDegradeTimer->resched(spRtxDest->dRto);
02591 
02592       /* This addresses the proposed change to RFC2960 section 7.2.4,
02593        * regarding using of Max.Burst. We have an option which allows
02594        * to control if Max.Burst is applied.
02595        */
02596       if(eUseMaxBurst == MAX_BURST_USAGE_ON)
02597         if( (eApplyMaxBurst == TRUE) && (uiBurstLength++ >= MAX_BURST) )
02598           {
02599         /* we've reached Max.Burst limit, so jump out of loop
02600          */
02601         eApplyMaxBurst = FALSE; // reset before jumping out of loop
02602         break;
02603           }
02604     }
02605     }
02606 
02607   /* Ok, let's count all marked chunks as outstanding again. (ugh... there
02608    * has to be a better way!)  
02609    */
02610   for(spCurrBuffNode = sSendBuffer.spHead; 
02611       spCurrBuffNode != NULL;
02612       spCurrBuffNode = spCurrBuffNode->spNext)
02613     {
02614       spCurrBuffNodeData = (SctpSendBufferNode_S *) spCurrBuffNode->vpData;
02615       
02616       if(spCurrBuffNodeData->eMarkedForRtx != NO_RTX)
02617     {
02618       spCurrChunk = spCurrBuffNodeData->spChunk;
02619       spCurrBuffNodeData->spDest->iOutstandingBytes
02620         += spCurrChunk->sHdr.usLength;
02621     }
02622     }
02623     
02624   /* If we made it here, either our limit was only one packet worth of
02625    * retransmissions or we hit the end of the list and there are no more
02626    * marked chunks. If we didn't hit the end, let's see if there are more marked
02627    * chunks.
02628    */
02629   eMarkedChunksPending = AnyMarkedChunks();
02630 
02631   DBG_X(RtxMarkedChunks);
02632 }
02633 
02634 /* Updates uiHighestRecvTsn
02635  */
02636 Boolean_E SctpAgent::UpdateHighestTsn(u_int uiTsn)
02637 {
02638   DBG_I(UpdateHighestTsn);
02639 
02640   if(uiTsn > uiHighestRecvTsn)
02641     {
02642       uiHighestRecvTsn = uiTsn;
02643       DBG_PL(UpdateHighestTsn, "returning TRUE") DBG_PR;
02644       DBG_X(UpdateHighestTsn);
02645       return TRUE;
02646     } 
02647   else
02648     {
02649       DBG_PL(UpdateHighestTsn, "returning FALSE") DBG_PR;
02650       DBG_X(UpdateHighestTsn);
02651       return FALSE; /* no update of highest */ 
02652     }
02653 }
02654 
02655 /* Determines whether a chunk is duplicate or not. 
02656  */
02657 Boolean_E SctpAgent::IsDuplicateChunk(u_int uiTsn)
02658 {
02659   DBG_I(IsDuplicateChunk);
02660 
02661   Node_S *spCurrNode = NULL;
02662 
02663   /* Assume highest have already been updated 
02664    */
02665   if(uiTsn <= uiCumAck)
02666     {
02667       DBG_PL(IsDuplicateChunk, "returning TRUE") DBG_PR;
02668       DBG_X(IsDuplicateChunk);
02669       return TRUE;
02670     }
02671   if( !((uiCumAck < uiTsn) && (uiTsn <= uiHighestRecvTsn)) )
02672     {
02673       DBG_PL(IsDuplicateChunk, "returning FALSE") DBG_PR;
02674       DBG_X(IsDuplicateChunk);
02675       return FALSE;
02676     }
02677 
02678   /* Let's see if uiTsn is in the sorted list of recv'd tsns
02679    */
02680   if(sRecvTsnBlockList.uiLength == 0)
02681     {
02682       DBG_PL(IsDuplicateChunk, "returning FALSE") DBG_PR;
02683       DBG_X(IsDuplicateChunk);
02684       return FALSE;           /* no frags in list! */
02685     }
02686 
02687   /* If we get this far, we need to check whether uiTsn is already in the list
02688    * of received tsns. Simply do a linear search thru the sRecvTsnBlockList.
02689    */
02690   for(spCurrNode = sRecvTsnBlockList.spHead; 
02691       spCurrNode != NULL; 
02692       spCurrNode = spCurrNode->spNext)
02693     {
02694       if(((SctpRecvTsnBlock_S *)spCurrNode->vpData)->uiStartTsn <= uiTsn &&
02695      uiTsn <= ((SctpRecvTsnBlock_S *)spCurrNode->vpData)->uiEndTsn )
02696     {
02697       DBG_PL(IsDuplicateChunk, "returning TRUE") DBG_PR;
02698       DBG_X(IsDuplicateChunk);
02699       return TRUE;     
02700     }
02701 
02702       /* Assuming an ordered list of tsn blocks, don't continue looking if this 
02703        * block ends with a larger tsn than the chunk we currently have.
02704        */
02705       if( ((SctpRecvTsnBlock_S *)spCurrNode->vpData)->uiEndTsn > uiTsn )
02706     {
02707       DBG_PL(IsDuplicateChunk, "returning FALSE") DBG_PR;
02708       DBG_X(IsDuplicateChunk);
02709       return FALSE;
02710     }
02711     }
02712 
02713   DBG_PL(IsDuplicateChunk, "returning FALSE") DBG_PR;
02714   DBG_X(IsDuplicateChunk);
02715   return FALSE;
02716 }
02717 
02718 
02719 /* Inserts uiTsn into the list of duplicates tsns
02720  */
02721 void SctpAgent::InsertDuplicateTsn(u_int uiTsn)
02722 {
02723   DBG_I(InsertDuplicateTsn);
02724   Node_S *spCurrNode = NULL;
02725   Node_S *spPrevNode = NULL;
02726   Node_S *spNewNode = NULL;
02727   u_int uiCurrTsn;
02728 
02729   /* linear search
02730    */
02731   for(spPrevNode = NULL, spCurrNode = sDupTsnList.spHead; 
02732       spCurrNode != NULL;
02733       spPrevNode = spCurrNode, spCurrNode = spCurrNode->spNext)
02734     {
02735       uiCurrTsn = ((SctpDupTsn_S *) spCurrNode->vpData)->uiTsn;
02736       if(uiTsn <= uiCurrTsn)
02737     break;
02738     }
02739 
02740   /* If we reached the end of the list 
02741    * OR we found the location in the list where it should go (assuming it 
02742    * isn't already there)... insert it.
02743    */
02744   if( (spCurrNode == NULL) || (uiTsn != uiCurrTsn) )
02745     {
02746       spNewNode = new Node_S;
02747       spNewNode->eType = NODE_TYPE_DUP_TSN;
02748       spNewNode->vpData = new SctpDupTsn_S;
02749       ((SctpDupTsn_S *) spNewNode->vpData)->uiTsn = uiTsn;
02750       InsertNode(&sDupTsnList, spPrevNode, spNewNode, spCurrNode);
02751     }
02752 
02753   DBG_X(InsertDuplicateTsn);
02754 }
02755 
02756 /* This function updates uiCumAck (a receive variable) to reflect newly arrived
02757  * data.
02758  */
02759 void SctpAgent::UpdateCumAck()
02760 {
02761   DBG_I(UpdateCumAck);
02762   Node_S *spCurrNode = NULL;
02763 
02764   if(sRecvTsnBlockList.uiLength == 0)
02765     {
02766       DBG_X(UpdateCumAck);
02767       return;
02768     }
02769 
02770   for(spCurrNode = sRecvTsnBlockList.spHead; 
02771       spCurrNode != NULL; 
02772       spCurrNode = spCurrNode->spNext)
02773     {
02774       if( uiCumAck+1 == ((SctpRecvTsnBlock_S *)spCurrNode->vpData)->uiStartTsn )
02775     {
02776       uiCumAck = ((SctpRecvTsnBlock_S *)spCurrNode->vpData)->uiEndTsn;
02777     }
02778       else
02779         {
02780           DBG_X(UpdateCumAck);
02781           return;
02782         }
02783     }
02784 
02785   DBG_X(UpdateCumAck);
02786 }
02787 
02792 void SctpAgent::UpdateRecvTsnBlocks(u_int uiTsn)
02793 {
02794   DBG_I(UpdateRecvTsnBlocks);
02795 
02796   u_int uiLow;
02797   u_int uiHigh;
02798   u_int uiGapSize;
02799 
02800   Node_S *spCurrNode = NULL;
02801   Node_S *spPrevNode = NULL;
02802   Node_S *spNewNode = NULL;
02803 
02804   uiLow = uiCumAck + 1;
02805 
02806   for(spCurrNode = sRecvTsnBlockList.spHead; 
02807       spCurrNode != NULL; 
02808       spPrevNode = spCurrNode, spCurrNode = spCurrNode->spNext)
02809     {
02810       uiHigh = ((SctpRecvTsnBlock_S *)spCurrNode->vpData)->uiStartTsn - 1;
02811       
02812       /* Does uiTsn fall in the gap?
02813        */
02814       if( uiLow <= uiTsn && uiTsn <= uiHigh )
02815     {
02816       uiGapSize = uiHigh - uiLow + 1;
02817 
02818       if(uiGapSize > 1) // is the gap too big for one uiTsn to fill?
02819         {
02820           /* Does uiTsn border the lower edge of the current tsn block?
02821            */
02822           if(uiTsn == uiHigh) 
02823         {
02824           ((SctpRecvTsnBlock_S *)spCurrNode->vpData)->uiStartTsn 
02825             = uiTsn;
02826 
02827           UpdateCumAck();
02828           DBG_X(UpdateRecvTsnBlocks);
02829           return;
02830         } 
02831 
02832           /* Does uiTsn border the left edge of the previous tsn block?
02833            */
02834           else if(uiTsn == uiLow)
02835         {
02836           if(uiTsn == uiCumAck+1) // can we increment our uiCumAck?
02837             {
02838               uiCumAck++;
02839               UpdateCumAck();
02840               DBG_X(UpdateRecvTsnBlocks);
02841               return;
02842             }
02843           else // otherwise, move previous tsn block's right edge
02844             {
02845               ((SctpRecvTsnBlock_S *)spPrevNode->vpData)->uiEndTsn 
02846             = uiTsn;
02847 
02848               UpdateCumAck();
02849               DBG_X(UpdateRecvTsnBlocks);
02850               return;
02851             }
02852         }
02853 
02854           /* This uiTsn creates a new tsn block in between uiLow & uiHigh
02855            */
02856           else 
02857         {   
02858           spNewNode = new Node_S;
02859           spNewNode->eType = NODE_TYPE_RECV_TSN_BLOCK;
02860           spNewNode->vpData = new SctpRecvTsnBlock_S;
02861           ((SctpRecvTsnBlock_S *)spNewNode->vpData)->uiStartTsn = uiTsn;
02862           ((SctpRecvTsnBlock_S *)spNewNode->vpData)->uiEndTsn = uiTsn;
02863 
02864           InsertNode(&sRecvTsnBlockList, 
02865                  spPrevNode, spNewNode, spCurrNode);
02866 
02867           DBG_X(UpdateRecvTsnBlocks);
02868           return; // no UpdateCumAck() necessary
02869         }
02870         } 
02871 
02872       else // uiGapSize == 1
02873         {
02874           if(uiLow == uiCumAck+1)  // can we adjust our uiCumAck?
02875         {
02876           /* delete tsn block
02877            */
02878           uiCumAck 
02879             = ((SctpRecvTsnBlock_S *)spCurrNode->vpData)->uiEndTsn;
02880 
02881           DeleteNode(&sRecvTsnBlockList, spCurrNode);
02882           spCurrNode = NULL;
02883           UpdateCumAck();
02884           DBG_X(UpdateRecvTsnBlocks);
02885           return;
02886         } 
02887           else  // otherwise, move previous tsn block's right edge
02888         {
02889           ((SctpRecvTsnBlock_S *)spPrevNode->vpData)->uiEndTsn
02890             = ((SctpRecvTsnBlock_S *)spCurrNode->vpData)->uiEndTsn;
02891 
02892           DeleteNode(&sRecvTsnBlockList, spCurrNode);
02893           spCurrNode = NULL;
02894           UpdateCumAck();
02895           DBG_X(UpdateRecvTsnBlocks);
02896           return;
02897         }
02898         }
02899     } 
02900 
02901       /* uiTsn is not in the gap between these two tsn blocks, so let's move our
02902        * "low pointer" to one past the end of the current tsn block and continue
02903        */
02904       else 
02905     {         
02906       uiLow =  ((SctpRecvTsnBlock_S *)spCurrNode->vpData)->uiEndTsn + 1;
02907     }
02908     }
02909 
02910   /* If we get here, then the list is either NULL or the end of the list has
02911    * been reached 
02912    */
02913   if(uiTsn == uiLow) 
02914     {
02915       if(uiTsn == uiCumAck+1) // Can we increment the uiCumAck?
02916     {
02917       uiCumAck = uiTsn;
02918       UpdateCumAck();
02919       DBG_X(UpdateRecvTsnBlocks);
02920       return;
02921         }
02922       
02923       /* Update previous tsn block by increasing it's uiEndTsn
02924        */
02925       if(spPrevNode != NULL)
02926     {
02927       ((SctpRecvTsnBlock_S *) spPrevNode->vpData)->uiEndTsn++;    
02928     }
02929       DBG_X(UpdateRecvTsnBlocks);
02930       return; // no UpdateCumAck() necessary
02931     } 
02932 
02933   /* uiTsn creates a new tsn block to go at the end of the sRecvTsnBlockList
02934    */
02935   else 
02936     {
02937       spNewNode = new Node_S;
02938       spNewNode->eType = NODE_TYPE_RECV_TSN_BLOCK;
02939       spNewNode->vpData = new SctpRecvTsnBlock_S;
02940       ((SctpRecvTsnBlock_S *) spNewNode->vpData)->uiStartTsn = uiTsn;
02941       ((SctpRecvTsnBlock_S *) spNewNode->vpData)->uiEndTsn = uiTsn;
02942       InsertNode(&sRecvTsnBlockList, spPrevNode, spNewNode, spCurrNode);
02943       DBG_X(UpdateRecvTsnBlocks);
02944       return; // no UpdateCumAck() necessary
02945     }
02946 }
02947 
02948 /* This function is merely a hook for future implementation and currently does
02949  * NOT actually pass the data to the upper layer.
02950  */
02951 void SctpAgent::PassToUpperLayer(SctpDataChunkHdr_S *spDataChunkHdr)
02952 {
02953   DBG_I(PassToUpperLayer);
02954   DBG_PL(PassToUpperLayer, "tsn=%d"), spDataChunkHdr->uiTsn DBG_PR;
02955 
02956   /* We really don't want to credit the window until the upper layer actually
02957    * wants the data, but now we'll assume that the application readily
02958    * consumes incoming chunks immediately.  
02959    */
02960   uiMyRwnd += spDataChunkHdr->sHdr.usLength; 
02961 
02962   DBG_PL(PassToUpperLayer, "uiMyRwnd=%d"), uiMyRwnd DBG_PR;
02963   DBG_X(PassToUpperLayer);
02964 }
02965 
02966 void SctpAgent::InsertInStreamBuffer(List_S *spBufferedChunkList,
02967                      SctpDataChunkHdr_S *spChunk)
02968 {
02969   DBG_I(InsertInStreamBuffer);
02970 
02971   Node_S *spPrevNode;
02972   Node_S *spCurrNode;
02973   Node_S *spNewNode;
02974   SctpStreamBufferNode_S *spCurrNodeData;
02975   SctpStreamBufferNode_S *spNewNodeData;
02976   u_short usCurrStreamSeqNum;
02977 
02978   spPrevNode = NULL;
02979   spCurrNode = spBufferedChunkList->spHead;
02980 
02981   /* linear search through stream sequence #'s 
02982    */ 
02983   for(spPrevNode = NULL, spCurrNode = spBufferedChunkList->spHead;
02984       spCurrNode != NULL;
02985       spPrevNode = spCurrNode, spCurrNode = spCurrNode->spNext)
02986     {
02987       spCurrNodeData = (SctpStreamBufferNode_S *) spCurrNode->vpData;
02988       usCurrStreamSeqNum = spCurrNodeData->spChunk->usStreamSeqNum;
02989 
02990       /* break out of loop when we find the place to insert our new chunk
02991        */
02992       if(spChunk->usStreamSeqNum <= usCurrStreamSeqNum)
02993     break;
02994     }
02995   
02996   /* If we reached the end of the list OR we found the location in the list
02997    * where it should go (assuming it isn't already there)... insert it.  
02998    */
02999   if( (spCurrNode == NULL) || (usCurrStreamSeqNum != spChunk->usStreamSeqNum) )
03000     {
03001       spNewNode = new Node_S;
03002       spNewNode->eType = NODE_TYPE_STREAM_BUFFER;
03003       spNewNode->vpData = new SctpStreamBufferNode_S;
03004       spNewNodeData = (SctpStreamBufferNode_S *) spNewNode->vpData;
03005     
03006       /* This can NOT simply be a 'new SctpDataChunkHdr_S', because we
03007        * need to allocate the space for the ENTIRE data chunk and not just
03008        * the data chunk header.  
03009        */
03010       spNewNodeData->spChunk 
03011     = (SctpDataChunkHdr_S *) new u_char[spChunk->sHdr.usLength];
03012       memcpy(spNewNodeData->spChunk, spChunk, spChunk->sHdr.usLength);
03013 
03014       DBG_PL(InsertInStreamBuffer, "vpData=%p spChunk=%p"), 
03015     spNewNodeData, spNewNodeData->spChunk DBG_PR;
03016 
03017       InsertNode(spBufferedChunkList, spPrevNode, spNewNode, spCurrNode);
03018     }
03019 
03020   DBG_X(InsertInStreamBuffer);
03021 }
03022 
03023 /* We have not implemented fragmentation, so this function safely assumes all
03024  * chunks are complete. 
03025  */
03026 void SctpAgent::PassToStream(SctpDataChunkHdr_S *spChunk)
03027 {
03028   DBG_I(PassToStream);
03029   DBG_PL(PassToStream, "uiMyRwnd=%d"), uiMyRwnd DBG_PR;
03030 
03031   SctpInStream_S *spStream = &(spInStreams[spChunk->usStreamId]);
03032 
03033   if(spChunk->sHdr.ucFlags & SCTP_DATA_FLAG_UNORDERED)
03034     {
03035       PassToUpperLayer(spChunk);
03036     } 
03037   else 
03038     {
03039       /* We got a numbered chunk (ordered delivery)...
03040        *
03041        * We insert the chunk into the corresponding buffer whether or not the 
03042        * chunk's stream seq # is in order or not.
03043        */
03044       DBG_PL(PassToStream, "streamId=%d streamSeqNum=%d"),
03045     spChunk->usStreamId, spChunk->usStreamSeqNum DBG_PR;
03046 
03047       InsertInStreamBuffer( &(spStream->sBufferedChunkList), spChunk);
03048     }
03049 
03050   DBG_PL(PassToStream, "uiMyRwnd=%d"), uiMyRwnd DBG_PR;
03051   DBG_X(PassToStream);
03052 }  
03053 
03054 void SctpAgent::UpdateAllStreams()
03055 {
03056   DBG_I(UpdateAllStreams);
03057   DBG_PL(UpdateAllStreams, "uiMyRwnd=%d"), uiMyRwnd DBG_PR;
03058 
03059   Node_S *spCurrNode = NULL;
03060   Node_S *spDeleteNode = NULL;
03061   SctpDataChunkHdr_S *spBufferedChunk = NULL;
03062   int i;
03063 
03064   for(i = 0; i < iNumInStreams; i++)
03065     {
03066       DBG_PL(UpdateAllStreams, "examining stream %d"), i DBG_PR;
03067       SctpInStream_S *spStream = &(spInStreams[i]);
03068 
03069       /* Start from the lowest stream seq # buffered and sequentially pass
03070        * up all the chunks which are "deliverable".  
03071        */
03072       spCurrNode = spStream->sBufferedChunkList.spHead;
03073       while(spCurrNode != NULL)
03074     {
03075       spBufferedChunk 
03076         = ((SctpStreamBufferNode_S *)spCurrNode->vpData)->spChunk;
03077 
03078       /* For unreliable streams... Any waiting tsn which is less than
03079        * or equal to the cum ack, must be delivered now. We first
03080        * deliver chunks without even considering the SSNs, because in
03081        * case of unreliable streams, there may be gaps in the SSNs
03082        * which we want to ignore.
03083        */
03084       if((spStream->eMode == SCTP_STREAM_UNRELIABLE) &&
03085          (spBufferedChunk->uiTsn <= uiCumAck) )
03086         {
03087           spStream->usNextStreamSeqNum = spBufferedChunk->usStreamSeqNum+1;
03088           PassToUpperLayer(spBufferedChunk);
03089           spDeleteNode = spCurrNode;
03090           spCurrNode = spCurrNode->spNext;
03091           DeleteNode( &(spStream->sBufferedChunkList), spDeleteNode );
03092           spDeleteNode = NULL;
03093         }
03094       
03095       /* Let's see if we can deliver anything else based on the SSNs.
03096        */
03097       else if(spBufferedChunk->usStreamSeqNum==spStream->usNextStreamSeqNum)
03098         {
03099           spStream->usNextStreamSeqNum++;
03100           PassToUpperLayer(spBufferedChunk);
03101           spDeleteNode = spCurrNode;
03102           spCurrNode = spCurrNode->spNext;
03103           DeleteNode( &(spStream->sBufferedChunkList), spDeleteNode );
03104           spDeleteNode = NULL;
03105         }
03106       
03107       /* ok, we have delivered all that we can!
03108        */
03109       else
03110         break;
03111     }
03112     }
03113 
03114   DBG_PL(UpdateAllStreams, "uiMyRwnd=%d"), uiMyRwnd DBG_PR;
03115   DBG_X(UpdateAllStreams);
03116 } 
03117 
03118 void SctpAgent::ProcessInitChunk(u_char *ucpInitChunk)
03119 {
03120   DBG_I(ProcessInitChunk);
03121 
03122   SctpInitChunk_S *spInitChunk = (SctpInitChunk_S *) ucpInitChunk;
03123   SctpUnrelStreamPair_S *spUnrelStreamPair 
03124     = (SctpUnrelStreamPair_S *) (ucpInitChunk + sizeof(SctpInitChunk_S));
03125   int i = 0;
03126 
03127   uiPeerRwnd = spInitChunk->uiArwnd;
03128   iNumInStreams = spInitChunk->usNumOutboundStreams;
03129   spInStreams = new SctpInStream_S[iNumInStreams];
03130   memset(spInStreams, 0, (iNumInStreams * sizeof(SctpInStream_S)) );
03131 
03132   if(spInitChunk->sHdr.usLength > sizeof(SctpInitChunk_S) )
03133   {
03134     for(i = spUnrelStreamPair->usStart; i <= spUnrelStreamPair->usEnd; i++)
03135       {
03136     DBG_PL(ProcessInitChunk, "setting inStream %d to UNRELIABLE"), i DBG_PR;
03137     spInStreams[i].eMode = SCTP_STREAM_UNRELIABLE;
03138       }
03139   }
03140   
03141   for(; i < iNumInStreams; i++)
03142     {
03143       DBG_PL(ProcessInitChunk, "setting inStream %d to RELIABLE"), i DBG_PR;
03144       spInStreams[i].eMode = SCTP_STREAM_RELIABLE;
03145     }
03146   
03147   DBG_X(ProcessInitChunk);
03148 }
03149 
03150 void SctpAgent::ProcessInitAckChunk(u_char *ucpInitAckChunk)
03151 {
03152   DBG_I(ProcessInitAckChunk);
03153 
03154   SctpInitAckChunk_S *spInitAckChunk = (SctpInitAckChunk_S *) ucpInitAckChunk;
03155   SctpUnrelStreamPair_S *spUnrelStreamPair 
03156     = (SctpUnrelStreamPair_S *) (ucpInitAckChunk + sizeof(SctpInitAckChunk_S) );
03157   int i = 0;
03158 
03159   opT1InitTimer->force_cancel();
03160   uiPeerRwnd = spInitAckChunk->uiArwnd;
03161   iNumInStreams = spInitAckChunk->usNumOutboundStreams;
03162   spInStreams = new SctpInStream_S[iNumInStreams];
03163   memset(spInStreams, 0, (iNumInStreams * sizeof(SctpInStream_S)) );
03164 
03165   if(spInitAckChunk->sHdr.usLength > sizeof(SctpInitAckChunk_S) )
03166   {
03167     for(i = spUnrelStreamPair->usStart; i <= spUnrelStreamPair->usEnd; i++)
03168       {
03169     DBG_PL(ProcessInitAckChunk, "setting inStream %d to UNRELIABLE"), 
03170       i DBG_PR;
03171     spInStreams[i].eMode = SCTP_STREAM_UNRELIABLE;
03172       }
03173   }
03174   
03175   for(; i < iNumInStreams; i++)
03176     {
03177       DBG_PL(ProcessInitAckChunk, "setting inStream %d to RELIABLE"), i DBG_PR;
03178       spInStreams[i].eMode = SCTP_STREAM_RELIABLE;
03179     }
03180   
03181   DBG_X(ProcessInitAckChunk);
03182 }
03183 
03184 void SctpAgent::ProcessCookieEchoChunk(SctpCookieEchoChunk_S *spCookieEchoChunk)
03185 {
03186   /* dummy empty function left as a hook for cookie echo processing */
03187 }
03188 
03189 void SctpAgent::ProcessCookieAckChunk(SctpCookieAckChunk_S *spCookieAckChunk)
03190 {
03191   opT1CookieTimer->force_cancel();
03192 }
03193 
03194 /* This function treats only one incoming data chunk at a time.
03195  */
03196 void SctpAgent::ProcessDataChunk(SctpDataChunkHdr_S *spChunk)
03197 {
03198   DBG_I(ProcessDataChunk);
03199 
03200   /* Is there still room in my receiver window?? We can only process the DATA
03201    * chunk if there is. Otherwise, we drop it!
03202    */
03203   if(spChunk->sHdr.usLength <= uiMyRwnd) 
03204     {
03205       if((UpdateHighestTsn(spChunk->uiTsn) != TRUE) &&
03206      (IsDuplicateChunk(spChunk->uiTsn) == TRUE) )
03207     {
03208       InsertDuplicateTsn(spChunk->uiTsn);
03209       eSackChunkNeeded = TRUE; // section 6.7 - send sack immediately!
03210       DBG_PL(ProcessDataChunk, "duplicate tsn=%d"), spChunk->uiTsn DBG_PR;
03211     }
03212       else 
03213     {
03214       /* Received a new chunk...  Reduce receiver window until application
03215        * consumes the incoming chunk 
03216        */
03217       uiMyRwnd -= spChunk->sHdr.usLength;
03218       UpdateRecvTsnBlocks(spChunk->uiTsn);
03219       PassToStream(spChunk);
03220       UpdateAllStreams();
03221       DBG_PL(ProcessDataChunk, "uiMyRwnd=%d"), uiMyRwnd DBG_PR;
03222     }
03223     }
03224   else
03225     {
03226       /* Do not generate a SACK if we are dropping the chunk!!
03227        */
03228       eSackChunkNeeded = FALSE; 
03229       DBG_PL(ProcessDataChunk, "rwnd full... dropping tsn=%d"), 
03230     spChunk->uiTsn DBG_PR;
03231     }
03232   
03233   DBG_X(ProcessDataChunk);
03234 }
03235 
03236 /* returns a boolean of whether a fast retransmit is necessary
03237  */
03238 Boolean_E SctpAgent::ProcessGapAckBlocks(u_char *ucpSackChunk,
03239                      Boolean_E eNewCumAck)
03240 {
03241   DBG_I(ProcessGapAckBlocks);
03242 
03243   Boolean_E eFastRtxNeeded = FALSE;
03244   u_int uiHighestTsnSacked = uiHighestTsnNewlyAcked;
03245   u_int uiStartTsn;
03246   u_int uiEndTsn;
03247   Node_S *spCurrNode = NULL;
03248   SctpSendBufferNode_S *spCurrNodeData = NULL;
03249   Node_S *spCurrDestNode = NULL;
03250   SctpDest_S *spCurrDestNodeData = NULL;
03251 
03252   SctpSackChunk_S *spSackChunk = (SctpSackChunk_S *) ucpSackChunk;
03253 
03254   u_short usNumGapAcksProcessed = 0;
03255   SctpGapAckBlock_S *spCurrGapAck 
03256     = (SctpGapAckBlock_S *) (ucpSackChunk + sizeof(SctpSackChunk_S));
03257 
03258   DBG_PL(ProcessGapAckBlocks,"CumAck=%d"), spSackChunk->uiCumAck DBG_PR;
03259 
03260   if(sSendBuffer.spHead == NULL) // do we have ANYTHING in the rtx buffer?
03261     {
03262       /* This COULD mean that this sack arrived late, and a previous one
03263        * already cum ack'd everything. ...so, what do we do? nothing??
03264        */
03265     }
03266   
03267   else // we do have chunks in the rtx buffer
03268     {
03269       /* make sure we clear all the spFirstOutstanding pointers before
03270        * using them!
03271        */
03272       for(spCurrDestNode = sDestList.spHead;
03273       spCurrDestNode != NULL;
03274       spCurrDestNode = spCurrDestNode->spNext)
03275     {
03276       spCurrDestNodeData = (SctpDest_S *) spCurrDestNode->vpData;
03277       spCurrDestNodeData->spFirstOutstanding = NULL;
03278     }
03279 
03280       for(spCurrNode = sSendBuffer.spHead;
03281       (spCurrNode != NULL) &&
03282         (usNumGapAcksProcessed != spSackChunk->usNumGapAckBlocks);
03283       spCurrNode = spCurrNode->spNext)
03284     {
03285       spCurrNodeData = (SctpSendBufferNode_S *) spCurrNode->vpData;
03286 
03287       /* is this chunk the first outstanding on its destination?
03288        */
03289       if(spCurrNodeData->spDest->spFirstOutstanding == NULL &&
03290          spCurrNodeData->eGapAcked == FALSE &&
03291          spCurrNodeData->eAdvancedAcked == FALSE)
03292         {
03293           /* yes, it is the first!
03294            */
03295           spCurrNodeData->spDest->spFirstOutstanding = spCurrNodeData;
03296         }
03297 
03298       DBG_PL(ProcessGapAckBlocks, "--> rtx list chunk begin") DBG_PR;
03299 
03300       DBG_PL(ProcessGapAckBlocks, "    TSN=%d"), 
03301         spCurrNodeData->spChunk->uiTsn 
03302         DBG_PR;
03303 
03304       DBG_PL(ProcessGapAckBlocks, "    %s=%s %s=%s"),
03305         "eGapAcked", 
03306         spCurrNodeData->eGapAcked ? "TRUE" : "FALSE",
03307         "eAddedToPartialBytesAcked",
03308         spCurrNodeData->eAddedToPartialBytesAcked ? "TRUE" : "FALSE" 
03309         DBG_PR;
03310 
03311       DBG_PL(ProcessGapAckBlocks, "    NumMissingReports=%d NumTxs=%d"),
03312         spCurrNodeData->iNumMissingReports, 
03313         spCurrNodeData->iNumTxs 
03314         DBG_PR;
03315 
03316       DBG_PL(ProcessGapAckBlocks, "<-- rtx list chunk end") DBG_PR;
03317       
03318       DBG_PL(ProcessGapAckBlocks,"GapAckBlock StartOffset=%d EndOffset=%d"),
03319         spCurrGapAck->usStartOffset, spCurrGapAck->usEndOffset DBG_PR;
03320 
03321       uiStartTsn = spSackChunk->uiCumAck + spCurrGapAck->usStartOffset;
03322       uiEndTsn = spSackChunk->uiCumAck + spCurrGapAck->usEndOffset;
03323       
03324       DBG_PL(ProcessGapAckBlocks, "GapAckBlock StartTsn=%d EndTsn=%d"),
03325         uiStartTsn, uiEndTsn DBG_PR;
03326 
03327       if(spCurrNodeData->spChunk->uiTsn < uiStartTsn)
03328         {
03329           /* This chunk is NOT being acked and is missing at the receiver
03330            */
03331 
03332           /* If this chunk was GapAcked before, then either the
03333            * receiver has renegged the chunk (which our simulation
03334            * doesn't do) or this SACK is arriving out of order.
03335            */
03336           if(spCurrNodeData->eGapAcked == TRUE)
03337         {
03338           DBG_PL(ProcessGapAckBlocks, 
03339              "out of order SACK? setting TSN=%d eGapAcked=FALSE"),
03340             spCurrNodeData->spChunk->uiTsn DBG_PR;
03341           spCurrNodeData->eGapAcked = FALSE;
03342           spCurrNodeData->spDest->iOutstandingBytes 
03343             += spCurrNodeData->spChunk->sHdr.usLength;
03344 
03345           /* section 6.3.2.R4 says that we should restart the
03346            * T3-rtx timer here if it isn't running already. In our
03347            * implementation, it isn't necessary since
03348            * ProcessSackChunk will restart the timer for any
03349            * destinations which have outstanding data and don't
03350            * have a timer running.
03351            */
03352         }
03353         }
03354       else if((uiStartTsn <= spCurrNodeData->spChunk->uiTsn) && 
03355           (spCurrNodeData->spChunk->uiTsn <= uiEndTsn) )
03356         {
03357           /* This chunk is being acked via a gap ack block
03358            */
03359           DBG_PL(ProcessGapAckBlocks, "gap ack acks this chunk: %s%s"),
03360         "eGapAcked=",
03361         spCurrNodeData->eGapAcked ? "TRUE" : "FALSE" 
03362         DBG_PR;
03363 
03364           /* HTNA algorithm... we need to know the highest TSN
03365            * sacked (even if it isn't new), so that when the sender
03366            * is in Fast Recovery, the outstanding tsns beyond the 
03367            * last sack tsn do not have their missing reports incremented
03368            */
03369           if(uiHighestTsnSacked < spCurrNodeData->spChunk->uiTsn)
03370         uiHighestTsnSacked = spCurrNodeData->spChunk->uiTsn;
03371 
03372           if(spCurrNodeData->eGapAcked == FALSE)
03373         {
03374           DBG_PL(ProcessGapAckBlocks, "setting eGapAcked=TRUE") DBG_PR;
03375           spCurrNodeData->eGapAcked = TRUE;
03376 
03377           /* HTNA algorithm... we need to know the highest TSN
03378            * newly acked
03379            */
03380           if(uiHighestTsnNewlyAcked < spCurrNodeData->spChunk->uiTsn)
03381             uiHighestTsnNewlyAcked = spCurrNodeData->spChunk->uiTsn;
03382 
03383           if(spCurrNodeData->eAdvancedAcked == FALSE)
03384             {
03385               spCurrNodeData->spDest->iNumNewlyAckedBytes 
03386             += spCurrNodeData->spChunk->sHdr.usLength;
03387             }
03388           
03389           /* only increment partial bytes acked if we are in
03390            * congestion avoidance mode, we have a new cum ack, and
03391            * we haven't already incremented it for this TSN
03392            */
03393           if(( spCurrNodeData->spDest->iCwnd 
03394                > spCurrNodeData->spDest->iSsthresh) &&
03395              eNewCumAck == TRUE &&
03396              spCurrNodeData->eAddedToPartialBytesAcked == FALSE)
03397             {
03398               DBG_PL(ProcessGapAckBlocks, 
03399                  "setting eAddedToPartiallyBytesAcked=TRUE") DBG_PR;
03400               
03401               spCurrNodeData->eAddedToPartialBytesAcked = TRUE; // set
03402 
03403               spCurrNodeData->spDest->iPartialBytesAcked 
03404             += spCurrNodeData->spChunk->sHdr.usLength;
03405             }
03406 
03407           /* We update the RTT estimate if the following hold true:
03408            *   1. RTO pending flag is set (6.3.1.C4)
03409            *   2. Timestamp is set for this chunk 
03410            *   3. This chunk has not been retransmitted
03411            *   4. This chunk has not been gap acked already 
03412            *   5. This chunk has not been advanced acked (pr-sctp)
03413            */
03414           if(spCurrNodeData->spDest->eRtoPending == TRUE &&
03415              spCurrNodeData->dTxTimestamp > 0 &&
03416              spCurrNodeData->iNumTxs == 1 &&
03417              spCurrNodeData->eAdvancedAcked == FALSE) 
03418             {
03419               /* If the chunk is marked for timeout rtx, then the
03420                * sender is an ambigious state. Were the sacks lost
03421                * or was there a failure?  Since we don't clear the
03422                * error counter below, we also don't update the
03423                * RTT. This could be a problem for late arriving
03424                * SACKs.
03425                */
03426               if(spCurrNodeData->eMarkedForRtx != TIMEOUT_RTX)
03427             RttUpdate(spCurrNodeData->dTxTimestamp, 
03428                   spCurrNodeData->spDest);
03429               spCurrNodeData->spDest->eRtoPending = FALSE;
03430             }
03431 
03432           /* section 6.3.2.R3 - Stop the timer if this is the
03433            * first outstanding for this destination (note: it may
03434            * have already been stopped if there was a new cum
03435            * ack). If there are still outstanding bytes on this
03436            * destination, we'll restart the timer later in
03437            * ProcessSackChunk() 
03438            */
03439           if(spCurrNodeData->spDest->spFirstOutstanding 
03440              == spCurrNodeData)
03441             
03442             {
03443               if(spCurrNodeData->spDest->eRtxTimerIsRunning == TRUE)
03444             StopT3RtxTimer(spCurrNodeData->spDest);
03445             }
03446           
03447           iAssocErrorCount = 0;
03448           
03449           /* We don't want to clear the error counter if it's
03450            * cleared already; otherwise, we'll unnecessarily
03451            * trigger a trace event.
03452            *
03453            * Also, the error counter is cleared by SACKed data
03454            * ONLY if the TSNs are not marked for timeout
03455            * retransmission and has not been gap acked
03456            * before. Without this condition, we can run into a
03457            * problem for failure detection. When a failure occurs,
03458            * some data may have made it through before the
03459            * failure, but the sacks got lost. When the sender
03460            * retransmits the first outstanding, the receiver will
03461            * sack all the data whose sacks got lost. We don't want
03462            * these sacks * to clear the error counter, or else
03463            * failover would take longer.
03464            */
03465           if(spCurrNodeData->spDest->iErrorCount != 0 &&
03466              spCurrNodeData->eMarkedForRtx != TIMEOUT_RTX)
03467             {
03468               DBG_PL(ProcessGapAckBlocks,
03469                  "clearing error counter for %p with tsn=%lu"), 
03470             spCurrNodeData->spDest, 
03471             spCurrNodeData->spChunk->uiTsn DBG_PR;
03472 
03473               spCurrNodeData->spDest->iErrorCount = 0; // clear errors
03474               tiErrorCount++;                       // ... and trace it!
03475               spCurrNodeData->spDest->eStatus = SCTP_DEST_STATUS_ACTIVE;
03476               if(spCurrNodeData->spDest == spPrimaryDest &&
03477              spNewTxDest != spPrimaryDest) 
03478             {
03479               DBG_PL(ProcessGapAckBlocks,
03480                  "primary recovered... "
03481                  "migrating back from %p to %p"),
03482                 spNewTxDest, spPrimaryDest DBG_PR;
03483               spNewTxDest = spPrimaryDest; // return to primary
03484             }
03485             }
03486 
03487           spCurrNodeData->eMarkedForRtx = NO_RTX; // unmark
03488         }
03489         }
03490       else if(spCurrNodeData->spChunk->uiTsn > uiEndTsn)
03491         {
03492           /* This point in the rtx buffer is already past the tsns which are
03493            * being acked by this gap ack block.  
03494            */
03495           usNumGapAcksProcessed++; 
03496 
03497           /* Did we process all the gap ack blocks?
03498            */
03499           if(usNumGapAcksProcessed != spSackChunk->usNumGapAckBlocks)
03500         {
03501           DBG_PL(ProcessGapAckBlocks, "jump to next gap ack block") 
03502             DBG_PR;
03503 
03504           spCurrGapAck 
03505             = ((SctpGapAckBlock_S *)
03506                (ucpSackChunk + sizeof(SctpSackChunk_S)
03507             + (usNumGapAcksProcessed * sizeof(SctpGapAckBlock_S))));
03508         }
03509 
03510           /* If this chunk was GapAcked before, then either the
03511            * receiver has renegged the chunk (which our simulation
03512            * doesn't do) or this SACK is arriving out of order.
03513            */
03514           if(spCurrNodeData->eGapAcked == TRUE)
03515         {
03516           DBG_PL(ProcessGapAckBlocks, 
03517              "out of order SACK? setting TSN=%d eGapAcked=FALSE"),
03518             spCurrNodeData->spChunk->uiTsn DBG_PR;
03519           spCurrNodeData->eGapAcked = FALSE;
03520           spCurrNodeData->spDest->iOutstandingBytes 
03521             += spCurrNodeData->spChunk->sHdr.usLength;
03522           
03523           /* section 6.3.2.R4 says that we should restart the
03524            * T3-rtx timer here if it isn't running already. In our
03525            * implementation, it isn't necessary since
03526            * ProcessSackChunk will restart the timer for any
03527            * destinations which have outstanding data and don't
03528            * have a timer running.
03529            */
03530         }
03531         }
03532     }
03533 
03534       /* By this time, either we have run through the entire send buffer or we
03535        * have run out of gap ack blocks. In the case that we have run out of gap
03536        * ack blocks before we finished running through the send buffer, we need
03537        * to mark the remaining chunks in the send buffer as eGapAcked=FALSE.
03538        * This final marking needs to be done, because we only trust gap ack info
03539        * from the last SACK. Otherwise, renegging (which we don't do) or out of
03540        * order SACKs would give the sender an incorrect view of the peer's rwnd.
03541        */
03542       for(; spCurrNode != NULL; spCurrNode = spCurrNode->spNext)
03543     {
03544       /* This chunk is NOT being acked and is missing at the receiver
03545        */
03546       spCurrNodeData = (SctpSendBufferNode_S *) spCurrNode->vpData;
03547 
03548       /* If this chunk was GapAcked before, then either the
03549        * receiver has renegged the chunk (which our simulation
03550        * doesn't do) or this SACK is arriving out of order.
03551        */
03552       if(spCurrNodeData->eGapAcked == TRUE)
03553         {
03554           DBG_PL(ProcessGapAckBlocks, 
03555              "out of order SACK? setting TSN=%d eGapAcked=FALSE"),
03556         spCurrNodeData->spChunk->uiTsn DBG_PR;
03557           spCurrNodeData->eGapAcked = FALSE;
03558           spCurrNodeData->spDest->iOutstandingBytes 
03559         += spCurrNodeData->spChunk->sHdr.usLength;
03560 
03561           /* section 6.3.2.R4 says that we should restart the T3-rtx
03562            * timer here if it isn't running already. In our
03563            * implementation, it isn't necessary since ProcessSackChunk
03564            * will restart the timer for any destinations which have
03565            * outstanding data and don't have a timer running.
03566            */
03567         }
03568     }
03569 
03570       DBG_PL(ProcessGapAckBlocks, "now incrementing missing reports...") DBG_PR;
03571       DBG_PL(ProcessGapAckBlocks, "uiHighestTsnNewlyAcked=%d"), 
03572          uiHighestTsnNewlyAcked DBG_PR;
03573 
03574       for(spCurrNode = sSendBuffer.spHead;
03575       spCurrNode != NULL; 
03576       spCurrNode = spCurrNode->spNext)
03577     {
03578       spCurrNodeData = (SctpSendBufferNode_S *) spCurrNode->vpData;
03579 
03580       DBG_PL(ProcessGapAckBlocks, "TSN=%d eGapAcked=%s"), 
03581         spCurrNodeData->spChunk->uiTsn,
03582         spCurrNodeData->eGapAcked ? "TRUE" : "FALSE"
03583         DBG_PR;
03584 
03585       if(spCurrNodeData->eGapAcked == FALSE)
03586         {
03587           /* HTNA (Highest TSN Newly Acked) algorithm from
03588            * implementer's guide. The HTNA increments missing reports
03589            * for TSNs not GapAcked when one of the following
03590            * conditions hold true:
03591            *
03592            *    1. The TSN is less than the highest TSN newly acked.
03593            *
03594            *    2. The TSN is less than the highest TSN sacked so far
03595            *    (not necessarily newly acked), the sender is in Fast
03596            *    Recovery, the cum ack changes, and the new cum ack is less
03597            *    than recover.
03598            */
03599           if( (spCurrNodeData->spChunk->uiTsn < uiHighestTsnNewlyAcked) ||
03600           (eNewCumAck == TRUE && 
03601            uiHighestTsnNewlyAcked <= uiRecover &&
03602            spCurrNodeData->spChunk->uiTsn < uiHighestTsnSacked))
03603         {
03604           spCurrNodeData->iNumMissingReports++;
03605           DBG_PL(ProcessGapAckBlocks, 
03606              "incrementing missing report for TSN=%d to %d"), 
03607             spCurrNodeData->spChunk->uiTsn,
03608             spCurrNodeData->iNumMissingReports
03609             DBG_PR;
03610 
03611           if(spCurrNodeData->iNumMissingReports >= iFastRtxTrigger &&
03612              spCurrNodeData->eIneligibleForFastRtx == FALSE &&
03613              spCurrNodeData->eAdvancedAcked == FALSE)
03614             {
03615               MarkChunkForRtx(spCurrNodeData, FAST_RTX);
03616               eFastRtxNeeded = TRUE;
03617               spCurrNodeData->eIneligibleForFastRtx = TRUE;
03618               DBG_PL(ProcessGapAckBlocks, 
03619                  "setting eFastRtxNeeded = TRUE") DBG_PR;
03620             }
03621         }
03622         }
03623     }
03624     }
03625 
03626   if(eFastRtxNeeded == TRUE)
03627     tiFrCount++;
03628 
03629   DBG_PL(ProcessGapAckBlocks, "eFastRtxNeeded=%s"), 
03630     eFastRtxNeeded ? "TRUE" : "FALSE" DBG_PR;
03631   DBG_X(ProcessGapAckBlocks);
03632   return eFastRtxNeeded;
03633 }
03634 
03635 void SctpAgent::ProcessSackChunk(u_char *ucpSackChunk)
03636 {
03637   DBG_I(ProcessSackChunk);
03638 
03639   SctpSackChunk_S *spSackChunk = (SctpSackChunk_S *) ucpSackChunk;
03640 
03641   DBG_PL(ProcessSackChunk, "cum=%d arwnd=%d #gapacks=%d #duptsns=%d"),
03642     spSackChunk->uiCumAck, spSackChunk->uiArwnd, 
03643     spSackChunk->usNumGapAckBlocks, spSackChunk->usNumDupTsns 
03644     DBG_PR;
03645 
03646   Boolean_E eFastRtxNeeded = FALSE;
03647   Boolean_E eNewCumAck = FALSE;
03648   Node_S *spCurrDestNode = NULL;
03649   SctpDest_S *spCurrDestNodeData = NULL;
03650   u_int uiTotalOutstanding = 0;
03651   int i = 0;
03652 
03653   /* make sure we clear all the iNumNewlyAckedBytes before using them!
03654    */
03655   for(spCurrDestNode = sDestList.spHead;
03656       spCurrDestNode != NULL;
03657       spCurrDestNode = spCurrDestNode->spNext)
03658     {
03659       spCurrDestNodeData = (SctpDest_S *) spCurrDestNode->vpData;
03660       spCurrDestNodeData->iNumNewlyAckedBytes = 0;
03661       spCurrDestNodeData->spFirstOutstanding = NULL;
03662     }
03663 
03664   if(spSackChunk->uiCumAck < uiCumAckPoint) 
03665     {
03666       /* this cumAck's a previously cumAck'd tsn (ie, it's out of order!)
03667        * ...so ignore!
03668        */
03669       DBG_PL(ProcessSackChunk, "ignoring out of order sack!") DBG_PR;
03670       DBG_X(ProcessSackChunk);
03671       return;
03672     }
03673   else if(spSackChunk->uiCumAck > uiCumAckPoint)
03674     {
03675       eNewCumAck = TRUE; // incomding SACK's cum ack advances the cum ack point
03676       SendBufferDequeueUpTo(spSackChunk->uiCumAck);
03677       uiCumAckPoint = spSackChunk->uiCumAck; // Advance the cumAck pointer
03678     }
03679 
03680   if(spSackChunk->usNumGapAckBlocks != 0) // are there any gaps??
03681     {
03682       eFastRtxNeeded = ProcessGapAckBlocks(ucpSackChunk, eNewCumAck);
03683     } 
03684 
03685   for(spCurrDestNode = sDestList.spHead;
03686       spCurrDestNode != NULL;
03687       spCurrDestNode = spCurrDestNode->spNext)
03688     {
03689       spCurrDestNodeData = (SctpDest_S *) spCurrDestNode->vpData;
03690 
03691       /* Only adjust cwnd if:
03692        *    1. sack advanced the cum ack point 
03693        *    2. this destination has newly acked bytes
03694        *    3. the cum ack is at or beyond the recovery window
03695        *
03696        * Also, we MUST adjust our congestion window BEFORE we update the
03697        * number of outstanding bytes to reflect the newly acked bytes in
03698        * received SACK.
03699        */
03700       if(eNewCumAck == TRUE &&
03701      spCurrDestNodeData->iNumNewlyAckedBytes > 0 &&
03702      spSackChunk->uiCumAck >= uiRecover)
03703     {
03704       AdjustCwnd(spCurrDestNodeData);
03705     }
03706 
03707       /* The number of outstanding bytes is reduced by how many bytes this sack 
03708        * acknowledges.
03709        */
03710       if(spCurrDestNodeData->iNumNewlyAckedBytes <=
03711      spCurrDestNodeData->iOutstandingBytes)
03712     {
03713       spCurrDestNodeData->iOutstandingBytes 
03714         -= spCurrDestNodeData->iNumNewlyAckedBytes;
03715     }
03716       else
03717     spCurrDestNodeData->iOutstandingBytes = 0;
03718 
03719       DBG_PL(ProcessSackChunk,"Dest #%d (%d:%d) (%p): outstanding=%d, cwnd=%d"),
03720     ++i, spCurrDestNodeData->iNsAddr, spCurrDestNodeData->iNsPort,
03721     spCurrDestNodeData, spCurrDestNodeData->iOutstandingBytes, 
03722     spCurrDestNodeData->iCwnd DBG_PR;
03723 
03724       if(spCurrDestNodeData->iOutstandingBytes == 0)
03725     {
03726       /* All outstanding data has been acked
03727        */
03728       spCurrDestNodeData->iPartialBytesAcked = 0;  // section 7.2.2
03729 
03730       /* section 6.3.2.R2
03731        */
03732       if(spCurrDestNodeData->eRtxTimerIsRunning == TRUE)
03733         {
03734           DBG_PL(ProcessSackChunk, "Dest #%d (%p): stopping timer"), 
03735         i, spCurrDestNodeData DBG_PR;
03736           StopT3RtxTimer(spCurrDestNodeData);
03737         }
03738     }
03739 
03740       /* section 6.3.2.R3 - Restart timers for destinations that have
03741        * acknowledged their first outstanding (ie, no timer running) and
03742        * still have outstanding data in flight.  
03743        */
03744       if(spCurrDestNodeData->iOutstandingBytes > 0 &&
03745      spCurrDestNodeData->eRtxTimerIsRunning == FALSE)
03746     {
03747       StartT3RtxTimer(spCurrDestNodeData);
03748     }
03749     }
03750 
03751   DBG_F(ProcessSackChunk, DumpSendBuffer());
03752 
03753   AdvancePeerAckPoint();
03754 
03755   if(eFastRtxNeeded == TRUE)  // section 7.2.4
03756     FastRtx();
03757 
03758   /* Let's see if after process this sack, there are still any chunks
03759    * pending... If so, rtx all allowed by cwnd.
03760    */
03761   else if( (eMarkedChunksPending = AnyMarkedChunks()) == TRUE)
03762     {
03763       /* section 6.1.C) When the time comes for the sender to
03764        * transmit, before sending new DATA chunks, the sender MUST
03765        * first transmit any outstanding DATA chunks which are marked
03766        * for retransmission (limited by the current cwnd).  
03767        */
03768       RtxMarkedChunks(RTX_LIMIT_CWND);
03769     }
03770 
03771   /* (6.2.1.D.ii) Adjust PeerRwnd based on total oustanding bytes on all
03772    * destinations. We need to this adjustment after any
03773    * retransmissions. Otherwise the sender's view of the peer rwnd will be
03774    * off, because the number outstanding increases again once a marked
03775    * chunk gets retransmitted (when marked, outstanding is decreased).
03776    */
03777   uiTotalOutstanding = TotalOutstanding();
03778   if(uiTotalOutstanding <= spSackChunk->uiArwnd)
03779     uiPeerRwnd = (spSackChunk->uiArwnd  - uiTotalOutstanding);
03780   else
03781     uiPeerRwnd = 0;
03782   
03783   DBG_PL(ProcessSackChunk, "uiPeerRwnd=%d, uiArwnd=%d"), uiPeerRwnd, 
03784     spSackChunk->uiArwnd DBG_PR;
03785   DBG_X(ProcessSackChunk);
03786 }
03787 
03788 void SctpAgent::ProcessForwardTsnChunk(SctpForwardTsnChunk_S *spForwardTsnChunk)
03789 {
03790   DBG_I(ProcessForwardTsnChunk);
03791   
03792   int i;
03793   u_int uiNewCum = spForwardTsnChunk->uiNewCum;
03794 
03795   /* Although we haven't actually received a DATA chunk, we have received
03796    * a FORWARD CUM TSN chunk, which essentially tells the receiver to
03797    * pretend that it has received all the DATA chunks up to the forwarded cum.
03798    * Some of the tsns we are forwarding past have been received, while others
03799    * have not.
03800    */
03801   for(i = uiCumAck+1; i <= (int) uiNewCum; i++)
03802     {
03803       if( IsDuplicateChunk(i) == FALSE )  // make sure it hasn't been received
03804     UpdateRecvTsnBlocks(i);
03805     }
03806 
03807   UpdateAllStreams();
03808 
03809   DBG_PL(ProcessForwardTsnChunk, "uiCumAck=%d"), uiCumAck DBG_PR;
03810   DBG_X(ProcessForwardTsnChunk);
03811 }
03812 
03813 void SctpAgent::ProcessHeartbeatAckChunk(SctpHeartbeatAckChunk_S 
03814                      *spHeartbeatAckChunk)
03815 {
03816   DBG_I(ProcessHeartbeatAckChunk);
03817 
03818   double dTime = 0;
03819 
03820   iAssocErrorCount = 0;
03821 
03822   /* trigger trace ONLY if it was previously NOT 0
03823    */
03824   if(spHeartbeatAckChunk->spDest->iErrorCount != 0)
03825     {
03826       spHeartbeatAckChunk->spDest->iErrorCount = 0; // clear the error count
03827       tiErrorCount++;                               // ...and trace it too!
03828       spHeartbeatAckChunk->spDest->eStatus = SCTP_DEST_STATUS_ACTIVE;
03829       if(spHeartbeatAckChunk->spDest == spPrimaryDest &&
03830      spNewTxDest != spPrimaryDest) 
03831     {
03832       DBG_PL(ProcessHeartbeatAckChunk,
03833          "primary recovered... migrating back from %p to %p"),
03834         spNewTxDest, spPrimaryDest DBG_PR;
03835       spNewTxDest = spPrimaryDest; // return to primary
03836     }
03837     }
03838 
03839   RttUpdate(spHeartbeatAckChunk->dTimestamp, spHeartbeatAckChunk->spDest);
03840 
03841   DBG_PL(ProcessHeartbeatAckChunk, "set rto of dest=%p to %f"),
03842     spHeartbeatAckChunk->spDest, spHeartbeatAckChunk->spDest->dRto DBG_PR;
03843 
03844   if(eOneHeartbeatTimer == TRUE && uiHeartbeatInterval != 0)
03845     {
03846       opHeartbeatTimeoutTimer->force_cancel();
03847     }
03848   else if(uiHeartbeatInterval != 0)
03849     {
03850       spHeartbeatAckChunk->spDest->opHeartbeatTimeoutTimer->force_cancel();
03851       DBG_PL(ProcessHeartbeatAckChunk, 
03852          "about to calculate heartbeat time for dest=%p"), 
03853     spHeartbeatAckChunk->spDest DBG_PR;
03854       dTime = CalcHeartbeatTime(spHeartbeatAckChunk->spDest->dRto);
03855       spHeartbeatAckChunk->spDest->opHeartbeatGenTimer->resched(dTime);
03856     }
03857 
03858   DBG_X(ProcessHeartbeatAckChunk);
03859 }
03860 
03861 /* This function is left as a hook for extensions to process chunk types not
03862  * supported in the base scpt. In the base sctp, we simply report the error...
03863  * the chunk is unexpected and unknown.
03864  */
03865 void SctpAgent::ProcessOptionChunk(u_char *ucpInChunk)
03866 {
03867   DBG_I(ProcessOptionChunk);
03868   double dCurrTime = Scheduler::instance().clock();
03869 
03870   DBG_PL(ProcessOptionChunk, "unexpected chunk type (unknown: %d) at %f"),
03871     ((SctpChunkHdr_S *)ucpInChunk)->ucType, dCurrTime DBG_PR;
03872   printf("[ProcessOptionChunk] unexpected chunk type (unknown: %d) at %f\n",
03873      ((SctpChunkHdr_S *)ucpInChunk)->ucType, dCurrTime);
03874 
03875   DBG_X(ProcessOptionChunk);
03876 }
03877 
03878 int SctpAgent::ProcessChunk(u_char *ucpInChunk, u_char **ucppOutData)
03879 {
03880   DBG_I(ProcessChunk);
03881   int iThisOutDataSize = 0;
03882   Node_S *spCurrNode = NULL;
03883   SctpDest_S *spCurrDest = NULL;
03884   double dCurrTime = Scheduler::instance().clock();
03885   double dTime;
03886   SctpHeartbeatAckChunk_S *spHeartbeatChunk = NULL;
03887   SctpHeartbeatAckChunk_S *spHeartbeatAckChunk = NULL;
03888 
03889   switch(eState)
03890     {
03891     case SCTP_STATE_CLOSED:
03892       switch( ((SctpChunkHdr_S *)ucpInChunk)->ucType)
03893     {
03894     case SCTP_CHUNK_INIT:
03895       DBG_PL(ProcessChunk, "got INIT!! ...sending INIT_ACK") DBG_PR;
03896       ProcessInitChunk(ucpInChunk);
03897       iThisOutDataSize = GenChunk(SCTP_CHUNK_INIT_ACK, *ucppOutData);
03898       *ucppOutData += iThisOutDataSize;
03899       /* stay in the closed state */
03900       break;
03901 
03902     case SCTP_CHUNK_COOKIE_ECHO:
03903       DBG_PL(ProcessChunk, 
03904          "got COOKIE_ECHO!! (established!) ...sending COOKIE_ACK")
03905         DBG_PR;
03906       ProcessCookieEchoChunk( (SctpCookieEchoChunk_S *) ucpInChunk );
03907       iThisOutDataSize = GenChunk(SCTP_CHUNK_COOKIE_ACK, *ucppOutData);
03908       *ucppOutData += iThisOutDataSize;
03909       eState = SCTP_STATE_ESTABLISHED;
03910       if(eOneHeartbeatTimer == TRUE && uiHeartbeatInterval != 0)
03911         {
03912           dTime = CalcHeartbeatTime(spPrimaryDest->dRto);
03913           opHeartbeatGenTimer->force_cancel();
03914           opHeartbeatGenTimer->resched(dTime);
03915           opHeartbeatGenTimer->dStartTime = dCurrTime;
03916 
03917           for(spCurrNode = sDestList.spHead;
03918           spCurrNode != NULL;
03919           spCurrNode = spCurrNode->spNext)
03920         {
03921           spCurrDest = (SctpDest_S *) spCurrNode->vpData;
03922           spCurrDest->dIdleSince = dCurrTime;
03923         }
03924         }
03925       else if(uiHeartbeatInterval != 0)
03926         {
03927           for(spCurrNode = sDestList.spHead;
03928           spCurrNode != NULL;
03929           spCurrNode = spCurrNode->spNext)
03930         {
03931           spCurrDest = (SctpDest_S *) spCurrNode->vpData;
03932           DBG_PL(Reset, "about to calculate HB time for dest=%p"), 
03933             spCurrDest DBG_PR;
03934           dTime = CalcHeartbeatTime(spCurrDest->dRto);
03935           spCurrDest->opHeartbeatGenTimer->resched(dTime);
03936         }
03937         }
03938       break;
03939       
03940     default:
03941       /* ALC 1/25/2002
03942        *
03943        * no error statement here, because there are times when this could
03944        * occur due to abrupt disconnections via the "reset" command. how?
03945        * well, "reset" resets all the association state. however, there may
03946        * still be packets in transit. if and when those packets arrive, they
03947        * will be unexpected packets since the association is closed. since
03948        * this is a simulation, it shouldn't be a problem. however, if an 
03949        * application needs a more graceful shutdown, we would need to 
03950        * implement sctp's proper shutdown procedure. until the need arises,
03951        * we won't do it. instead, what do we do? ignore the "unexpected"
03952        * packet.
03953        */
03954       DBG_PL(ProcessChunk, "association closed... ignoring chunk %s"), 
03955         "(not COOKIE_ECHO or INIT)" DBG_PR;
03956       break;
03957     }
03958       break;
03959       
03960     case SCTP_STATE_COOKIE_WAIT:
03961       DBG_PL(ProcessChunk, "got INIT_ACK!! ...sending COOKIE_ECHO") DBG_PR;
03962       ProcessInitAckChunk(ucpInChunk);
03963       iThisOutDataSize = GenChunk(SCTP_CHUNK_COOKIE_ECHO, *ucppOutData);
03964       *ucppOutData += iThisOutDataSize;
03965       opT1CookieTimer->resched(spPrimaryDest->dRto);
03966       eState = SCTP_STATE_COOKIE_ECHOED;
03967       break;
03968 
03969     case SCTP_STATE_COOKIE_ECHOED:
03970       DBG_PL(ProcessChunk, "got COOKIE_ACK!! (established!) ...sending DATA")
03971     DBG_PR;
03972       ProcessCookieAckChunk( (SctpCookieAckChunk_S *) ucpInChunk );
03973       eSendNewDataChunks = TRUE;
03974       eState = SCTP_STATE_ESTABLISHED;
03975       if(eOneHeartbeatTimer == TRUE && uiHeartbeatInterval != 0)
03976     {
03977       dTime = CalcHeartbeatTime(spPrimaryDest->dRto);
03978       opHeartbeatGenTimer->force_cancel();
03979       opHeartbeatGenTimer->resched(dTime);
03980       opHeartbeatGenTimer->dStartTime = dCurrTime;
03981 
03982       for(spCurrNode = sDestList.spHead;
03983           spCurrNode != NULL;
03984           spCurrNode = spCurrNode->spNext)
03985         {
03986           spCurrDest = (SctpDest_S *) spCurrNode->vpData;
03987           spCurrDest->dIdleSince = dCurrTime;
03988         }
03989     }
03990       else if(uiHeartbeatInterval != 0)
03991     {
03992       for(spCurrNode = sDestList.spHead;
03993           spCurrNode != NULL;
03994           spCurrNode = spCurrNode->spNext)
03995         {
03996           spCurrDest = (SctpDest_S *) spCurrNode->vpData;
03997           DBG_PL(Reset, "about to calculate HB time for dest=%p"), 
03998         spCurrDest DBG_PR;
03999           dTime = CalcHeartbeatTime(spCurrDest->dRto);
04000           spCurrDest->opHeartbeatGenTimer->resched(dTime);
04001         }
04002     }
04003       break;
04004 
04005     case SCTP_STATE_ESTABLISHED:
04006       switch( ((SctpChunkHdr_S *)ucpInChunk)->ucType)
04007     {
04008     case SCTP_CHUNK_DATA:
04009       DBG_PL(ProcessChunk, "got DATA (TSN=%d)!!"), 
04010         ((SctpDataChunkHdr_S *)ucpInChunk)->uiTsn DBG_PR;
04011 
04012       if(eUseDelayedSacks == FALSE) // are we doing delayed sacks?
04013         {
04014           /* NO, so generate sack immediately!
04015            */
04016           eSackChunkNeeded = TRUE;
04017         }
04018       else  // we are doing delayed sacks, so...
04019         {
04020           /* rfc2960 section 6.2 - determine if a SACK will be generated
04021            */
04022           if(eStartOfPacket == TRUE)  
04023         {
04024           eStartOfPacket = FALSE;  // reset
04025           iDataPktCountSinceLastSack++;
04026 
04027           if(iDataPktCountSinceLastSack == 1)   
04028             {
04029               opSackGenTimer->resched(dSackDelay);
04030             }
04031           else if(iDataPktCountSinceLastSack == DELAYED_SACK_TRIGGER)
04032             {
04033               iDataPktCountSinceLastSack = 0; // reset
04034               opSackGenTimer->force_cancel();
04035               eSackChunkNeeded = TRUE;
04036             }
04037         }
04038         }
04039 
04040       ProcessDataChunk( (SctpDataChunkHdr_S *) ucpInChunk );
04041 
04042       /* section 6.7 - There is at least one "gap in the received DATA
04043        * chunk sequence", so let's ensure we send a SACK immediately!
04044        */
04045       if(sRecvTsnBlockList.uiLength > 0)
04046         {
04047           iDataPktCountSinceLastSack = 0; // reset
04048           opSackGenTimer->force_cancel();
04049           eSackChunkNeeded = TRUE;
04050         }
04051 
04052       /* no state change 
04053        */     
04054       break;
04055 
04056     case SCTP_CHUNK_SACK:
04057       DBG_PL(ProcessChunk, "got SACK (CumAck=%d)!!"),
04058         ((SctpSackChunk_S *)ucpInChunk)->uiCumAck DBG_PR;
04059 
04060       ProcessSackChunk(ucpInChunk);
04061 
04062       /* Do we need to transmit a FORWARD TSN chunk??
04063        */
04064       if(uiAdvancedPeerAckPoint > uiCumAckPoint)
04065         eForwardTsnNeeded = TRUE;
04066 
04067       eSendNewDataChunks = TRUE;
04068       break; // no state change
04069 
04070     case SCTP_CHUNK_FORWARD_TSN:
04071       DBG_PL(ProcessChunk, "got FORWARD TSN (tsn=%d)!!"),
04072         ((SctpForwardTsnChunk_S *) ucpInChunk)->uiNewCum DBG_PR;
04073 
04074       ProcessForwardTsnChunk( (SctpForwardTsnChunk_S *) ucpInChunk );
04075       break; // no state change
04076 
04077     case SCTP_CHUNK_HB:
04078       DBG_PL(ProcessChunk, "got HEARTBEAT!!") DBG_PR;
04079 
04080       /* GenChunk() doesn't copy HB info
04081        */
04082       iThisOutDataSize = GenChunk(SCTP_CHUNK_HB_ACK, *ucppOutData);
04083 
04084       /* ...so we copy it here!
04085        */
04086       spHeartbeatChunk = (SctpHeartbeatAckChunk_S *) ucpInChunk;
04087       spHeartbeatAckChunk = (SctpHeartbeatAckChunk_S *) *ucppOutData;
04088       spHeartbeatAckChunk->dTimestamp = spHeartbeatChunk->dTimestamp;
04089       spHeartbeatAckChunk->spDest = spHeartbeatChunk->spDest;
04090       *ucppOutData += iThisOutDataSize;
04091       break; // no state change
04092 
04093     case SCTP_CHUNK_HB_ACK:
04094       DBG_PL(ProcessChunk, "got HEARTBEAT ACK!!") DBG_PR;
04095       ProcessHeartbeatAckChunk( (SctpHeartbeatAckChunk_S *) ucpInChunk);
04096       break; // no state change
04097 
04098     case SCTP_CHUNK_INIT:
04099       DBG_PL(ProcessChunk, "unexpected chunk type (INIT) at %f"),
04100         dCurrTime DBG_PR;
04101       printf("[ProcessChunk] unexpected chunk type (INIT) at %f\n",
04102         dCurrTime);
04103       break;
04104 
04105     case SCTP_CHUNK_INIT_ACK:
04106       DBG_PL(ProcessChunk, "unexpected chunk type (INIT_ACK) at %f"),
04107         dCurrTime DBG_PR;
04108       printf("[ProcessChunk] unexpected chunk type (INIT_ACK) at %f\n",
04109         dCurrTime);
04110       break;
04111 
04112       /* even though the association is established, COOKIE_ECHO needs to be
04113        * handled because the peer may have not received the COOKIE_ACK.
04114        *
04115        * Note: we don't follow the rfc's complex process for handling this
04116        * case, because we don't deal with tie-tags, etc in simulation. :-)
04117        */
04118     case SCTP_CHUNK_COOKIE_ECHO:
04119       DBG_PL(ProcessChunk,
04120          "got COOKIE_ECHO!! (established!) ...sending COOKIE_ACK")
04121         DBG_PR;
04122       ProcessCookieEchoChunk( (SctpCookieEchoChunk_S *) ucpInChunk);
04123       iThisOutDataSize = GenChunk(SCTP_CHUNK_COOKIE_ACK, *ucppOutData);
04124       *ucppOutData += iThisOutDataSize;
04125       break;
04126 
04127     case SCTP_CHUNK_COOKIE_ACK:
04128       DBG_PL(ProcessChunk, "unexpected chunk type (COOKIE_ACK) at %f"),
04129         dCurrTime DBG_PR;
04130       printf("[ProcessChunk] unexpected chunk type (COOKIE_ACK) at %f\n",
04131          dCurrTime);
04132       break;
04133 
04134     default:
04135       ProcessOptionChunk(ucpInChunk);
04136       break;
04137     }
04138       break;
04139 
04140     case SCTP_STATE_UNINITIALIZED:
04141     case SCTP_STATE_SHUTDOWN_SENT:
04142     case SCTP_STATE_SHUTDOWN_RECEIVED:
04143     case SCTP_STATE_SHUTDOWN_ACK_SENT:
04144     case SCTP_STATE_SHUTDOWN_PENDING:
04145       break;
04146     }  
04147 
04148   DBG_X(ProcessChunk);
04149   return iThisOutDataSize;
04150 }
04151 
04152 /* Moves the pointer to the next chunk.
04153  * If there is no next chunk, then sent pointer to NULL. 
04154  */
04155 void SctpAgent::NextChunk(u_char **ucpChunk, int *ipRemainingDataLen)
04156 {
04157   DBG_I(NextChunk);
04158   unsigned long ulSize = ((SctpChunkHdr_S *) *ucpChunk)->usLength;
04159 
04160   /* the chunk length field does not include the padded bytes, so we need to
04161    * account for these extra bytes.
04162    */
04163   if( (ulSize % 4) != 0 ) 
04164     ulSize += 4 - (ulSize % 4);
04165 
04166   *ipRemainingDataLen -= ulSize;
04167 
04168   if(*ipRemainingDataLen > 0)
04169     *ucpChunk += ulSize;
04170   else
04171     *ucpChunk = NULL;
04172 
04173   DBG_X(NextChunk);
04174 }
04175 
04176 /* Return the next active destination based on the destination passed in by 
04177  * using round robin algorithm for selection. It is possible that this function
04178  * will return the same destination passed in. This will happen if (1) there is
04179  * only one destination and/or (2) all other destinations are inactive.
04180  */
04181 SctpDest_S *SctpAgent::GetNextDest(SctpDest_S *spDest)
04182 {
04183   DBG_I(GetNextDest);
04184   DBG_PL(GetNextDest, "previously dest = %p"), spDest DBG_PR;
04185 
04186   Node_S *spCurrDestNode = NULL;
04187   Node_S *spNextDestNode = NULL;
04188   SctpDest_S *spNextDestNodeData = NULL;
04189 
04190   /* find spDest before we can pick next
04191    */
04192   for(spCurrDestNode = sDestList.spHead;
04193       spCurrDestNode != NULL;
04194       spCurrDestNode = spCurrDestNode->spNext)
04195     {
04196       if(spCurrDestNode->vpData == spDest)
04197     break; // found it, so jump out!
04198     }
04199 
04200   assert(spCurrDestNode != NULL); // if NULL, means spDest wasn't found :-/
04201 
04202   /* let's get the next ACTIVE destination. stop if loops wraps around to 
04203    * current destination.
04204    */
04205   spNextDestNode = spCurrDestNode;
04206   do
04207     {
04208       spNextDestNode = spNextDestNode->spNext;
04209       if(spNextDestNode == NULL)
04210     spNextDestNode = sDestList.spHead; // wrap around to head of list
04211       spNextDestNodeData = (SctpDest_S *) spNextDestNode->vpData;      
04212     } while(spNextDestNodeData->eStatus == SCTP_DEST_STATUS_INACTIVE &&
04213         spNextDestNode != spCurrDestNode);
04214 
04215   /* Are we in the dormant state?
04216    */
04217   if(spNextDestNode == spCurrDestNode &&
04218      spNextDestNodeData->eStatus == SCTP_DEST_STATUS_INACTIVE)
04219     {
04220       switch(eDormantAction)
04221     {
04222     case DORMANT_HOP:
04223       /*
04224        * Simply go to the next destination. We don't care if it is inactive
04225        * or if it is the same destination we are already on.
04226        */
04227       spNextDestNode = spNextDestNode->spNext;
04228       if(spNextDestNode == NULL)
04229         spNextDestNode = sDestList.spHead; // wrap around to head of list
04230       spNextDestNodeData = (SctpDest_S *) spNextDestNode->vpData;      
04231       break;
04232 
04233     case DORMANT_PRIMARY:
04234       spNextDestNodeData = spPrimaryDest;
04235       break;
04236 
04237     case DORMANT_LASTDEST:  
04238       /*
04239        * Nothing to do. Since we just want to stay at the last destination
04240        * used before dormant state, then the loop above does that for us
04241        * already.
04242        */
04243       break;
04244     }
04245     }
04246 
04247   DBG_PL(GetNextDest, "next dest = %p"), spNextDestNodeData DBG_PR;
04248   DBG_X(GetNextDest);
04249   return spNextDestNodeData;
04250 }
04251 
04252 /* section 8.3 (with implementer's guide changes) - On an idle destination
04253  * address that is allowed to heartbeat, a HEARTBEAT chunk is RECOMMENDED
04254  * to be sent once per RTO of that destination address plus the protocol
04255  * parameter 'HB.interval' , with jittering of +/- 50% of the RTO value
04256  */
04257 double SctpAgent::CalcHeartbeatTime(double dRto)
04258 {
04259   DBG_I(CalcHeartbeatTime);
04260 
04261   double dRetVal = 0;
04262   double dCurrTime = Scheduler::instance().clock();
04263 
04264   dRetVal = dRto + uiHeartbeatInterval;
04265   dRetVal += Random::uniform(dRto);
04266   dRetVal -= dRto/2;
04267 
04268   DBG_PL(CalcHeartbeatTime, "next HEARTBEAT interval in %f secs"), dRetVal
04269     DBG_PR;
04270   DBG_PL(CalcHeartbeatTime, "next HEARTBEAT interval at %f"), 
04271     dRetVal+dCurrTime DBG_PR;
04272 
04273   DBG_X(CalcHeartbeatTime);
04274   return dRetVal;
04275 }
04276 
04277 /* Sets the source addr, port, and target based on the destination information.
04278  * The SctpDest_S holds an already formed packet with the destination's
04279  * dest addr & port in the header. With this packet, we can find the correct
04280  * src addr, port, and target.  */
04281 void SctpAgent::SetSource(SctpDest_S *spDest)
04282 {
04283   DBG_I(SetSource);
04284 
04285   /* dynamically pick the route only if 2 conditions hold true:
04286    *    1. we are not forcing the source address and interface. 
04287    *    2. there exists a "multihome core" (ie, the agent is multihomed)
04288    *
04289    * otherwise, use the values are already preset
04290    */
04291   if(eForceSource == FALSE && opCoreTarget != NULL)
04292     {
04293       Node_S *spCurrNode = sInterfaceList.spHead;
04294       SctpInterface_S *spCurrInterface = NULL;
04295 
04296       /* find the connector (link) to use from the "routing table" of the core
04297        */
04298       Connector *connector 
04299     = (Connector *) opCoreTarget->find(spDest->opRoutingAssistPacket);
04300 
04301       while(spCurrNode != NULL)
04302     {
04303       spCurrInterface = (SctpInterface_S *) spCurrNode->vpData;
04304       
04305       if(spCurrInterface->opLink == connector)
04306         {
04307           addr() = spCurrInterface->iNsAddr;
04308           port() = spCurrInterface->iNsPort;
04309           target_ = spCurrInterface->opTarget;
04310           break;
04311         }
04312       else
04313         spCurrNode = spCurrNode->spNext;
04314     }
04315     }
04316 
04317   DBG_PL(SetSource, "(%d:%d)"), addr(), port() DBG_PR;
04318   DBG_X(SetSource);
04319 }
04320 
04321 void SctpAgent::SetDestination(SctpDest_S *spDest)
04322 {
04323   DBG_I(SetDestination);
04324 
04325   daddr() = spDest->iNsAddr;
04326   dport() = spDest->iNsPort;
04327 
04328   DBG_PL(SetDestination, "(%d:%d)"), daddr(), dport() DBG_PR;
04329   DBG_X(SetDestination);
04330 }
04331 
04332 void SctpAgent::SendPacket(u_char *ucpData, int iDataSize, SctpDest_S *spDest)
04333 {
04334   DBG_I(SendPacket);
04335   DBG_PL(SendPacket, "spDest=%p (%d:%d)"), 
04336     spDest, spDest->iNsAddr, spDest->iNsPort DBG_PR;
04337   DBG_PL(SendPacket, "iDataSize=%d  uiNumChunks=%d"), 
04338     iDataSize, uiNumChunks DBG_PR;
04339 
04340   Node_S *spNewNode = NULL;
04341   Packet *opPacket = NULL;
04342   PacketData *opPacketData = NULL;
04343 
04344   SetSource(spDest); // set src addr, port, target based on "routing table"
04345   SetDestination(spDest); // set dest addr & port 
04346 
04347   opPacket = allocpkt();
04348   opPacketData = new PacketData(iDataSize);
04349   memcpy(opPacketData->data(), ucpData, iDataSize);
04350   opPacket->setdata(opPacketData);
04351   hdr_cmn::access(opPacket)->size() = iDataSize + SCTP_HDR_SIZE+uiIpHeaderSize;
04352 
04353   hdr_sctp::access(opPacket)->NumChunks() = uiNumChunks;
04354   hdr_sctp::access(opPacket)->SctpTrace() = new SctpTrace_S[uiNumChunks];
04355   memcpy(hdr_sctp::access(opPacket)->SctpTrace(), spSctpTrace, 
04356      (uiNumChunks * sizeof(SctpTrace_S)) );
04357 
04358   uiNumChunks = 0; // reset the counter
04359 
04360   if(dRouteCalcDelay == 0) // simulating reactive routing overheads?
04361     {
04362       send(opPacket, 0); // no... so send immediately
04363     }
04364   else
04365     {
04366       if(spDest->eRouteCached == TRUE)  
04367     {
04368       /* Since the route is "cached" already, we can reschedule the route
04369        * flushing and send the packet immediately.
04370        */
04371       spDest->opRouteCacheFlushTimer->resched(dRouteCacheLifetime);
04372       send(opPacket, 0);
04373     }
04374       else
04375     {
04376       /* The route isn't cached, so queue the packet and "calculate" the 
04377        * route.
04378        */
04379       spNewNode = new Node_S;
04380       spNewNode->eType = NODE_TYPE_PACKET_BUFFER;
04381       spNewNode->vpData = opPacket;
04382       InsertNode(&spDest->sBufferedPackets, spDest->sBufferedPackets.spTail,
04383              spNewNode, NULL);
04384 
04385       if(spDest->opRouteCalcDelayTimer->status() != TIMER_PENDING)
04386         spDest->opRouteCalcDelayTimer->sched(dRouteCalcDelay);
04387     }
04388     }
04389 
04390   DBG_X(SendPacket);
04391 }
04392 
04393 SctpDest_S *SctpAgent::GetReplyDestination(hdr_ip *spIpHdr)
04394 {
04395   Node_S *spCurrNode = NULL;
04396   SctpDest_S *spDest = NULL;
04397   int iAddr = spIpHdr->saddr();
04398   int iPort = spIpHdr->sport();
04399   
04400   for(spCurrNode = sDestList.spHead;
04401       spCurrNode != NULL;
04402       spCurrNode = spCurrNode->spNext)
04403     {
04404       spDest = (SctpDest_S *) spCurrNode->vpData;
04405 
04406       if(spDest->iNsAddr == iAddr && spDest->iNsPort == iPort)
04407     return spDest;
04408     }
04409   
04410   return NULL;  // we should never get here!
04411 }
04412 
04413 void SctpAgent::recv(Packet *opInPkt, Handler*)
04414 {
04415   /* Let's make sure that a Reset() is called, because it isn't always
04416    * called explicitly with the "reset" command. For example, wireless
04417    * nodes don't automatically "reset" their agents, but wired nodes do. 
04418    */
04419   if(eState == SCTP_STATE_UNINITIALIZED)
04420     Reset(); 
04421 
04422   DBG_I(recv);
04423 
04424   hdr_ip *spIpHdr = hdr_ip::access(opInPkt);
04425   PacketData *opInPacketData = (PacketData *) opInPkt->userdata();
04426   u_char *ucpInData = opInPacketData->data();
04427   u_char *ucpCurrInChunk = ucpInData;
04428   int iRemainingDataLen = opInPacketData->size();
04429 
04430   u_char ucpOutData[uiMaxPayloadSize]; 
04431   u_char *ucpCurrOutData = ucpOutData;
04432 
04433   /* local variable which maintains how much data has been filled in the current
04434    * outgoing packet
04435    */
04436   int iOutDataSize = 0; 
04437 
04438   memset(ucpOutData, 0, uiMaxPayloadSize);
04439   memset(spSctpTrace, 0,
04440      (uiMaxPayloadSize / sizeof(SctpChunkHdr_S)) * sizeof(SctpTrace_S) );
04441 
04442   spReplyDest = GetReplyDestination(spIpHdr);
04443 
04444   eStartOfPacket = TRUE;
04445 
04446   do
04447     {
04448       DBG_PL(recv, "iRemainingDataLen=%d"), iRemainingDataLen DBG_PR;
04449 
04450       /* processing chunks may need to generate response chunks, so the
04451        * current outgoing packet *may* be filled in and our out packet's data
04452        * size is incremented to reflect the new data
04453        */
04454       iOutDataSize += ProcessChunk(ucpCurrInChunk, &ucpCurrOutData);
04455       NextChunk(&ucpCurrInChunk, &iRemainingDataLen);
04456     }
04457   while(ucpCurrInChunk != NULL);
04458 
04459   /* Let's see if we have any response chunks (currently only handshake related)
04460    * to transmit. 
04461    *
04462    * Note: We don't bundle these responses (yet!)
04463    */
04464   if(iOutDataSize > 0) 
04465     {
04466       SendPacket(ucpOutData, iOutDataSize, spReplyDest);
04467       DBG_PL(recv, "responded with control chunk(s)") DBG_PR;
04468     }
04469 
04470   /* Let's check to see if we need to generate and send a SACK chunk.
04471    *
04472    * Note: With uni-directional traffic, SACK and DATA chunks will not be 
04473    * bundled together in one packet.
04474    * Perhaps we will implement this in the future?  
04475    */
04476   if(eSackChunkNeeded == TRUE)
04477     {
04478       memset(ucpOutData, 0, uiMaxPayloadSize);
04479       iOutDataSize = BundleControlChunks(ucpOutData);
04480       iOutDataSize += GenChunk(SCTP_CHUNK_SACK, ucpOutData+iOutDataSize);
04481       SendPacket(ucpOutData, iOutDataSize, spReplyDest);
04482       DBG_PL(recv, "SACK sent (%d bytes)"), iOutDataSize DBG_PR;
04483       eSackChunkNeeded = FALSE;  // reset AFTER sent (o/w breaks dependencies)
04484     }
04485 
04486   /* Do we need to transmit a FORWARD TSN chunk??
04487    */
04488   if(eForwardTsnNeeded == TRUE)
04489     {
04490       memset(ucpOutData, 0, uiMaxPayloadSize);
04491       iOutDataSize = BundleControlChunks(ucpOutData);
04492       iOutDataSize += GenChunk(SCTP_CHUNK_FORWARD_TSN, ucpOutData+iOutDataSize);
04493       SendPacket(ucpOutData, iOutDataSize, spNewTxDest);
04494       DBG_PL(recv, "FORWARD TSN chunk sent") DBG_PR;
04495       eForwardTsnNeeded = FALSE; // reset AFTER sent (o/w breaks dependencies)
04496     }
04497 
04498   /* Do we want to send out new DATA chunks in addition to whatever we may have
04499    * already transmitted? If so, we can only send new DATA if no marked chunks
04500    * are pending retransmission.
04501    * 
04502    * Note: We aren't bundling with what was sent above, but we could. Just 
04503    * avoiding that for now... why? simplicity :-)
04504    */
04505   if(eSendNewDataChunks == TRUE && eMarkedChunksPending == FALSE) 
04506     {
04507       SendMuch();       // Send new data till our cwnd is full!
04508       eSendNewDataChunks = FALSE; // reset AFTER sent (o/w breaks dependencies)
04509     }
04510 
04511   delete hdr_sctp::access(opInPkt)->SctpTrace();
04512   hdr_sctp::access(opInPkt)->SctpTrace() = NULL;
04513   Packet::free(opInPkt);
04514   opInPkt = NULL;
04515   DBG_X(recv);
04516 }
04517 
04518 u_int SctpAgent::TotalOutstanding()
04519 {
04520   DBG_I(TotalOutstanding);
04521 
04522   Node_S *spCurrNode = NULL;
04523   SctpDest_S *spCurrDest = NULL;
04524   u_int uiTotalOutstanding = 0;
04525 
04526   for(spCurrNode = sDestList.spHead;
04527       spCurrNode != NULL;
04528       spCurrNode = spCurrNode->spNext)
04529     {
04530       spCurrDest = (SctpDest_S *) spCurrNode->vpData;
04531       uiTotalOutstanding += spCurrDest->iOutstandingBytes;
04532       DBG_PL(TotalOutstanding, "spCurrDest->iOutstandingBytes=%lu"),
04533     spCurrDest->iOutstandingBytes DBG_PR;
04534     }
04535 
04536   DBG_PL(TotalOutstanding, "%lu"), uiTotalOutstanding DBG_PR;
04537   DBG_X(TotalOutstanding);
04538   return uiTotalOutstanding;
04539 }
04540 
04541 void SctpAgent::SendMuch()
04542 {
04543   DBG_I(SendMuch);
04544   DBG_PL(SendMuch, "eDataSource=%s"), 
04545     ( (eDataSource == DATA_SOURCE_APPLICATION)
04546       ? "DATA_SOURCE_APPLICATION" 
04547       : "DATA_SOURCE_INFINITE" )
04548     DBG_PR;
04549 
04550   u_char ucpOutData[uiMaxPayloadSize]; 
04551   int iOutDataSize = 0;
04552   double dTime = 0;
04553   double dCurrTime = Scheduler::instance().clock();
04554 
04555   /* Keep sending out packets until our cwnd is full!  The proposed
04556    * correction to RFC2960 section 6.1.B says "The sender may exceed
04557    * cwnd by up to (PMTU-1) bytes on a new transmission if the cwnd is
04558    * not currently exceeded". Hence, as long as cwnd isn't
04559    * full... send another packet.  
04560    *
04561    * Also, if our data source is the application layer (instead of the infinite
04562    * source used for ftp), check if there is any data buffered from the app 
04563    * layer. If so, then send as much as we can.
04564    */
04565   while((spNewTxDest->iOutstandingBytes < spNewTxDest->iCwnd) &&
04566     (eDataSource == DATA_SOURCE_INFINITE || sAppLayerBuffer.uiLength != 0))
04567     {
04568       DBG_PL(SendMuch, "uiAdvancedPeerAckPoint=%d uiCumAckPoint=%d"), 
04569     uiAdvancedPeerAckPoint, uiCumAckPoint DBG_PR;
04570       DBG_PL(SendMuch, "uiPeerRwnd=%d"), uiPeerRwnd DBG_PR;
04571       DBG_PL(SendMuch, "spNewTxDest->iCwnd=%d"), spNewTxDest->iCwnd DBG_PR;
04572       DBG_PL(SendMuch, "spNewTxDest->iPartialBytesAcked=%d"), 
04573     spNewTxDest->iPartialBytesAcked DBG_PR;
04574       DBG_PL(SendMuch, "spNewTxDest->iOutstandingBytes=%d"), 
04575     spNewTxDest->iOutstandingBytes DBG_PR;
04576       DBG_PL(SendMuch, "TotalOutstanding=%lu"), 
04577     TotalOutstanding() DBG_PR;
04578       DBG_PL(SendMuch, "eApplyMaxBurst=%s uiBurstLength=%d"),
04579     eApplyMaxBurst ? "TRUE" : "FALSE", uiBurstLength DBG_PR;
04580 
04581       if(GetNextDataChunkSize() <= uiPeerRwnd)
04582     {
04583       /* This addresses the proposed change to RFC2960 section 7.2.4,
04584        * regarding using of Max.Burst. We have an option which allows
04585        * to control if Max.Burst is applied.
04586        */
04587       if(eUseMaxBurst == MAX_BURST_USAGE_ON)
04588         if( (eApplyMaxBurst == TRUE) && (uiBurstLength++ >= MAX_BURST) )
04589           {
04590         /* we've reached Max.Burst limit, so jump out of loop
04591          */
04592         eApplyMaxBurst = FALSE;// reset before jumping out of loop
04593         break;
04594           }
04595 
04596       memset(ucpOutData, 0, uiMaxPayloadSize); // reset
04597       iOutDataSize = BundleControlChunks(ucpOutData);
04598       iOutDataSize += GenMultipleDataChunks(ucpOutData+iOutDataSize, 0);
04599       SendPacket(ucpOutData, iOutDataSize, spNewTxDest);
04600       DBG_PL(SendMuch, "DATA chunk(s) sent") DBG_PR;
04601     }
04602       else if(TotalOutstanding() == 0)  // section 6.1.A
04603     {
04604       /* probe for a change in peer's rwnd
04605        */
04606       memset(ucpOutData, 0, uiMaxPayloadSize);
04607       iOutDataSize = BundleControlChunks(ucpOutData);
04608       iOutDataSize += GenOneDataChunk(ucpOutData+iOutDataSize);
04609       SendPacket(ucpOutData, iOutDataSize, spNewTxDest);
04610       DBG_PL(SendMuch, "DATA chunk probe sent") DBG_PR;
04611     }
04612       else
04613     {
04614       break;
04615     }      
04616     }
04617 
04618   if(iOutDataSize > 0)  // did we send anything??
04619     {
04620       spNewTxDest->opCwndDegradeTimer->resched(spNewTxDest->dRto);
04621       if(eOneHeartbeatTimer == TRUE && uiHeartbeatInterval != 0)
04622     {
04623       spNewTxDest->dIdleSince = dCurrTime;
04624     }
04625       else if(uiHeartbeatInterval != 0)
04626     {
04627       DBG_PL(SendMuch, "about to calculate heartbeat time for dest=%p"), 
04628         spNewTxDest DBG_PR;
04629       dTime = CalcHeartbeatTime(spNewTxDest->dRto);
04630       spNewTxDest->opHeartbeatGenTimer->resched(dTime);
04631     }
04632     }
04633 
04634   /* this reset MUST happen at the end of the function, because the burst 
04635    * measurement is carried over from RtxMarkedChunks() if it was called.
04636    */
04637   uiBurstLength = 0;
04638 
04639   DBG_X(SendMuch);
04640 }
04641 
04642 void SctpAgent::sendmsg(int iNumBytes, const char *cpFlags)
04643 {
04644   /* Let's make sure that a Reset() is called, because it isn't always
04645    * called explicitly with the "reset" command. For example, wireless
04646    * nodes don't automatically "reset" their agents, but wired nodes do. 
04647    */
04648   if(eState == SCTP_STATE_UNINITIALIZED)
04649     Reset();
04650 
04651   DBG_I(sendmsg);
04652 
04653   u_char ucpOutData[uiMaxPayloadSize];
04654   int iOutDataSize = 0;
04655   AppData_S *spAppData = (AppData_S *) cpFlags;
04656   Node_S *spNewNode = NULL;
04657   int iMsgSize = 0;
04658   u_int uiMaxFragSize = uiMaxDataSize - sizeof(SctpDataChunkHdr_S);
04659 
04660   if(iNumBytes == -1) 
04661     eDataSource = DATA_SOURCE_INFINITE;    // Send infinite data
04662   else 
04663     eDataSource = DATA_SOURCE_APPLICATION; // Send data passed from app
04664       
04665   if(eDataSource == DATA_SOURCE_APPLICATION) 
04666     {
04667       if(spAppData != NULL)
04668     {
04669       /* This is an SCTP-aware app!! Anything the app passes down
04670        * overrides what we bound from TCL.
04671        */
04672       DBG_PL (sendmsg, "sctp-aware app: iNumBytes=%d"), iNumBytes DBG_PR;
04673       spNewNode = new Node_S;
04674       uiNumOutStreams = spAppData->usNumStreams;
04675       uiNumUnrelStreams = spAppData->usNumUnreliable;
04676       spNewNode->eType = NODE_TYPE_APP_LAYER_BUFFER;
04677       spNewNode->vpData = spAppData;
04678       InsertNode(&sAppLayerBuffer, sAppLayerBuffer.spTail, spNewNode, NULL);
04679     }
04680       else
04681     {
04682       /* This is NOT an SCTP-aware app!! We rely on TCL-bound variables.
04683        */
04684       DBG_PL (sendmsg, "non-sctp-aware app: iNumBytes=%d"),iNumBytes DBG_PR;
04685       uiNumOutStreams = 1; // non-sctp-aware apps only use 1 stream
04686       uiNumUnrelStreams = (uiNumUnrelStreams > 0) ? 1 : 0;
04687 
04688       /* To support legacy applications and uses such as "ftp send
04689        * 12000", we "fragment" the message. _HOWEVER_, this is not
04690        * REAL SCTP fragmentation!! We do not maintain the same SSN or
04691        * use the B/E bits. Think of this block of code as a shim which
04692        * breaks up the message into useable pieces for SCTP. 
04693        */
04694       for(iMsgSize = iNumBytes; 
04695           iMsgSize > 0; 
04696           iMsgSize -= MIN(iMsgSize, (int) uiMaxFragSize) )
04697         {
04698           spNewNode = new Node_S;
04699           spNewNode->eType = NODE_TYPE_APP_LAYER_BUFFER;
04700           spAppData = new AppData_S;
04701           spAppData->usNumStreams = uiNumOutStreams;
04702           spAppData->usNumUnreliable = uiNumUnrelStreams;
04703           spAppData->usStreamId = 0;  
04704           spAppData->usReliability = uiReliability;
04705           spAppData->eUnordered = eUnordered;
04706           spAppData->uiNumBytes = MIN(iMsgSize, (int) uiMaxFragSize);
04707           spNewNode->vpData = spAppData;
04708           InsertNode(&sAppLayerBuffer, sAppLayerBuffer.spTail, 
04709              spNewNode, NULL);
04710         }
04711     }      
04712 
04713       if(uiNumOutStreams > MAX_NUM_STREAMS)
04714     {
04715       fprintf(stderr, "%s number of streams (%d) > max (%d)\n",
04716           "SCTP ERROR:",
04717           uiNumOutStreams, MAX_NUM_STREAMS);
04718       DBG_PL(sendmsg, "ERROR: number of streams (%d) > max (%d)"), 
04719         uiNumOutStreams, MAX_NUM_STREAMS DBG_PR;
04720       DBG_PL(sendmsg, "exiting...") DBG_PR;
04721       exit(-1);
04722     }
04723       else if(uiNumUnrelStreams > uiNumOutStreams)
04724     {
04725       fprintf(stderr, "%s number of unreliable streams (%d) > total (%d)\n",
04726           "SCTP ERROR:",
04727           uiNumUnrelStreams, uiNumOutStreams);
04728       DBG_PL(sendmsg, 
04729          "ERROR: number of unreliable streams (%d) > total (%d)"), 
04730         uiNumUnrelStreams, uiNumOutStreams DBG_PR;
04731       DBG_PL(sendmsg, "exiting...") DBG_PR;
04732       exit(-1);
04733     }
04734 
04735       if(spAppData->uiNumBytes + sizeof(SctpDataChunkHdr_S) 
04736      > MAX_DATA_CHUNK_SIZE)
04737     {
04738       fprintf(stderr, "SCTP ERROR: message size (%d) too big\n",
04739           spAppData->uiNumBytes);
04740       fprintf(stderr, "%s data chunk size (%d) > max (%d)\n",
04741           "SCTP ERROR:",
04742           spAppData->uiNumBytes + sizeof(SctpDataChunkHdr_S), 
04743           MAX_DATA_CHUNK_SIZE);
04744       DBG_PL(sendmsg, "ERROR: message size (%d) too big"),
04745          spAppData->uiNumBytes DBG_PR;
04746       DBG_PL(sendmsg, "ERROR: data chunk size (%d) > max (%d)"), 
04747         spAppData->uiNumBytes + sizeof(SctpDataChunkHdr_S), 
04748         MAX_DATA_CHUNK_SIZE 
04749         DBG_PR;
04750       DBG_PL(sendmsg, "exiting...") DBG_PR;
04751       exit(-1);
04752     }
04753       else if(spAppData->uiNumBytes + sizeof(SctpDataChunkHdr_S)
04754           > uiMaxDataSize)
04755     {
04756       fprintf(stderr, "SCTP ERROR: message size (%d) too big\n",
04757           spAppData->uiNumBytes);
04758       fprintf(stderr, 
04759           "%s data chunk size (%d) + SCTP/IP header(%d) > MTU (%d)\n",
04760           "SCTP ERROR:",
04761           spAppData->uiNumBytes + sizeof(SctpDataChunkHdr_S),
04762           SCTP_HDR_SIZE + uiIpHeaderSize, uiMtu);
04763       fprintf(stderr, "           %s\n",
04764           "...chunk fragmentation is not yet supported!");
04765       DBG_PL(sendmsg, "ERROR: message size (%d) too big"),
04766          spAppData->uiNumBytes DBG_PR;
04767       DBG_PL(sendmsg, "exiting...") DBG_PR;
04768       exit(-1);
04769     }
04770     }
04771 
04772   switch(eState)
04773     {
04774     case SCTP_STATE_CLOSED:
04775       DBG_PL(sendmsg, "sending INIT") DBG_PR;
04776 
04777       /* This must be done especially since some of the legacy apps use their
04778        * own packet type (don't ask me why). We need our packet type to be
04779        * sctp so that our tracing output comes out correctly for scripts, etc
04780        */
04781       set_pkttype(PT_SCTP); 
04782       iOutDataSize = GenChunk(SCTP_CHUNK_INIT, ucpOutData);
04783       opT1InitTimer->resched(spPrimaryDest->dRto);
04784       eState = SCTP_STATE_COOKIE_WAIT;
04785       SendPacket(ucpOutData, iOutDataSize, spPrimaryDest);
04786       break;
04787       
04788     case SCTP_STATE_ESTABLISHED:
04789       if(eDataSource == DATA_SOURCE_APPLICATION) 
04790     {
04791       SendMuch();     
04792     }
04793       else if(eDataSource == DATA_SOURCE_INFINITE)
04794     {
04795       fprintf(stderr, "[sendmsg] ERROR: unexpected state... %s\n",
04796           "sendmsg called more than once for infinite data");
04797       DBG_PL(sendmsg, 
04798          "ERROR: unexpected state... %s"), 
04799         "sendmsg called more than once for infinite data" DBG_PR;
04800       DBG_PL(sendmsg, "exiting...") DBG_PR;
04801       exit(-1);
04802     }
04803       break;
04804       
04805     default:  
04806       /* If we are here, we assume the application is trying to send data
04807        * before the 4-way handshake has completed. ...so buffering the
04808        * data is ok, but DON'T send it yet!!  
04809        */
04810       break;
04811     }
04812 
04813   DBG_X(sendmsg);
04814 }
04815 
04816 void SctpAgent::T1InitTimerExpiration()
04817 {
04818   DBG_I(T1InitTimerExpiration);
04819 
04820   u_char ucpOutData[uiMaxPayloadSize];
04821   int iOutDataSize = 0;
04822 
04823   iInitTryCount++;
04824   if(iInitTryCount > (int) uiMaxInitRetransmits)
04825     {
04826       Close();
04827     }
04828   else 
04829     {
04830       spPrimaryDest->dRto *= 2;
04831       if(spPrimaryDest->dRto > dMaxRto)
04832     spPrimaryDest->dRto = dMaxRto;
04833       tdRto++;              // trigger changes for trace to pick up
04834 
04835       iOutDataSize = GenChunk(SCTP_CHUNK_INIT, ucpOutData);
04836       SendPacket(ucpOutData, iOutDataSize, spPrimaryDest);
04837       opT1InitTimer->resched(spPrimaryDest->dRto);
04838     }
04839 
04840   DBG_X(T1InitTimerExpiration);
04841 }
04842 
04843 void T1InitTimer::expire(Event*)
04844 {
04845   opAgent->T1InitTimerExpiration();
04846 }
04847 
04848 void SctpAgent::T1CookieTimerExpiration()
04849 {
04850   DBG_I(T1CookieTimerExpiration);
04851 
04852   u_char ucpOutData[uiMaxPayloadSize];
04853   int iOutDataSize = 0;
04854  
04855   iInitTryCount++;
04856   if(iInitTryCount > (int) uiMaxInitRetransmits)
04857     Close();
04858   else 
04859     {
04860       spPrimaryDest->dRto *= 2;
04861       if(spPrimaryDest->dRto > dMaxRto)
04862     spPrimaryDest->dRto = dMaxRto;
04863       tdRto++;              // trigger changes for trace to pick up
04864 
04865       iOutDataSize = GenChunk(SCTP_CHUNK_COOKIE_ECHO, ucpOutData);
04866       SendPacket(ucpOutData, iOutDataSize, spPrimaryDest);
04867       opT1CookieTimer->resched(spPrimaryDest->dRto);
04868   }
04869 
04870   DBG_X(T1CookieTimerExpiration);
04871 }
04872 
04873 void T1CookieTimer::expire(Event*)
04874 {
04875   opAgent->T1CookieTimerExpiration();
04876 }
04877 
04878 void SctpAgent::Close()
04879 {
04880   DBG_I(Close);
04881 
04882   Node_S *spCurrNode = NULL;
04883   SctpDest_S *spCurrDest = NULL;
04884 
04885   eState = SCTP_STATE_CLOSED; 
04886   
04887   /* stop all timers
04888    */
04889   opT1InitTimer->force_cancel();
04890   opT1CookieTimer->force_cancel();
04891   opSackGenTimer->force_cancel();
04892   if(eOneHeartbeatTimer == TRUE && uiHeartbeatInterval != 0)
04893     {
04894       opHeartbeatGenTimer->force_cancel();
04895       opHeartbeatTimeoutTimer->force_cancel();      
04896     }
04897 
04898   for(spCurrNode = sDestList.spHead;
04899       spCurrNode != NULL;
04900       spCurrNode = spCurrNode->spNext)
04901     {
04902       spCurrDest = (SctpDest_S *) spCurrNode->vpData;
04903       spCurrDest->opT3RtxTimer->force_cancel();
04904       spCurrDest->opCwndDegradeTimer->force_cancel();
04905       if(eOneHeartbeatTimer == FALSE && uiHeartbeatInterval != 0)
04906     {
04907       spCurrDest->opHeartbeatGenTimer->force_cancel();
04908       spCurrDest->opHeartbeatTimeoutTimer->force_cancel();      
04909     }
04910     }
04911 
04912   ClearList(&sSendBuffer);
04913   ClearList(&sAppLayerBuffer);
04914   ClearList(&sRecvTsnBlockList);
04915   ClearList(&sDupTsnList);
04916 
04917   DBG_X(Close);
04918 }
04919 
04920 /* Handles timeouts for both DATA chunks and HEARTBEAT chunks. The actions are
04921  * slightly different for each. Covers rfc sections 6.3.3 and 8.3.
04922  */
04923 void SctpAgent::Timeout(SctpChunkType_E eChunkType, SctpDest_S *spDest)
04924 {
04925   DBG_I(Timeout);
04926   DBG_PL(Timeout, "eChunkType=%s spDest=%p"), 
04927     (eChunkType == SCTP_CHUNK_DATA) ? "DATA" : "HEARTBEAT",
04928     spDest
04929     DBG_PR;
04930 
04931   double dCurrTime = Scheduler::instance().clock();
04932 
04933   DBG_PL(Timeout, "dCurrTime=%f"), dCurrTime DBG_PR;
04934 
04935   if(eChunkType == SCTP_CHUNK_DATA)
04936     {
04937       spDest->eRtxTimerIsRunning = FALSE;
04938       
04939       /* section 7.2.3 of rfc2960 (w/ implementor's guide)
04940        */
04941       if(spDest->iCwnd > 1 * (int) uiMaxDataSize)
04942     {
04943       spDest->iSsthresh 
04944         = MAX(spDest->iCwnd/2, iInitialCwnd * (int) uiMaxDataSize);
04945       spDest->iCwnd = 1*uiMaxDataSize;
04946       spDest->iPartialBytesAcked = 0; // reset
04947       tiCwnd++; // trigger changes for trace to pick up
04948     }
04949 
04950       spDest->opCwndDegradeTimer->force_cancel();
04951 
04952       /* Cancel any pending RTT measurement on this destination. Stephan
04953        * Baucke suggested (2004-04-27) this action as a fix for the
04954        * following simple scenario:
04955        *
04956        * - Host A sends packets 1, 2 and 3 to host B, and choses 3 for
04957        *   an RTT measurement
04958        *
04959        * - Host B receives all packets correctly and sends ACK1, ACK2,
04960        *   and ACK3.
04961        *
04962        * - ACK2 and ACK3 are lost on the return path
04963        *
04964        * - Eventually a timeout fires for packet 2, and A retransmits 2
04965        *
04966        * - Upon receipt of 2, B sends a cumulative ACK3 (since it has
04967        *   received 2 & 3 before)
04968        *
04969        * - Since packet 3 has never been retransmitted, the SCTP code
04970        *   actually accepts the ACK for an RTT measurement, although it
04971        *   was sent in reply to the retransmission of 2, which results
04972        *   in a much too high RTT estimate. Since this case tends to
04973        *   happen in case of longer link interruptions, the error is
04974        *   often amplified by subsequent timer backoffs.
04975        */
04976       spDest->eRtoPending = FALSE; // cancel any pending RTT measurement
04977     }
04978 
04979   DBG_PL(Timeout, "was spDest->dRto=%f"), spDest->dRto DBG_PR;
04980   spDest->dRto *= 2;    // back off the timer
04981   if(spDest->dRto > dMaxRto)
04982     spDest->dRto = dMaxRto;
04983   tdRto++;              // trigger changes for trace to pick up
04984   DBG_PL(Timeout, "now spDest->dRto=%f"), spDest->dRto DBG_PR;
04985 
04986   spDest->iTimeoutCount++;
04987   spDest->iErrorCount++; // @@@ window probe timeouts should not be counted
04988   DBG_PL(Timeout, "now spDest->iErrorCount=%d"), spDest->iErrorCount DBG_PR;
04989 
04990   if(spDest->eStatus == SCTP_DEST_STATUS_ACTIVE)
04991     {  
04992       iAssocErrorCount++;
04993       DBG_PL(Timeout, "now iAssocErrorCount=%d"), iAssocErrorCount DBG_PR;
04994 
04995       // Path.Max.Retrans exceeded?
04996       if(spDest->iErrorCount > (int) uiPathMaxRetrans) 
04997     {
04998       spDest->eStatus = SCTP_DEST_STATUS_INACTIVE;
04999       if(spDest == spNewTxDest)
05000         {
05001           spNewTxDest = GetNextDest(spDest);
05002           DBG_PL(Timeout, "failing over from %p to %p"),
05003         spDest, spNewTxDest DBG_PR;
05004         }
05005     }
05006       if(iAssocErrorCount > (int) uiAssociationMaxRetrans)
05007     {
05008       /* abruptly close the association!  (section 8.1)
05009        */
05010       DBG_PL(Timeout, "abruptly closing the association!") DBG_PR;
05011       Close();
05012       DBG_X(Timeout);
05013       return;
05014     }
05015     }
05016 
05017   // trace it!
05018   tiTimeoutCount++;
05019   tiErrorCount++;       
05020 
05021   if(spDest->iErrorCount > (int) uiChangePrimaryThresh &&
05022      spDest == spPrimaryDest)
05023     {
05024       spPrimaryDest = spNewTxDest;
05025       DBG_PL(Timeout, "changing primary from %p to %p"),
05026     spDest, spNewTxDest DBG_PR;
05027     }
05028 
05029   if(eChunkType == SCTP_CHUNK_DATA)
05030     {
05031       TimeoutRtx(spDest);
05032       if(spDest->eStatus == SCTP_DEST_STATUS_INACTIVE && uiHeartbeatInterval!=0)
05033     SendHeartbeat(spDest);  // just marked inactive, so send HB immediately!
05034     }
05035   else if(eChunkType == SCTP_CHUNK_HB)
05036     {
05037       if(uiHeartbeatInterval != 0)
05038     SendHeartbeat(spDest);
05039     }
05040 
05041   DBG_X(Timeout);  
05042 }
05043 
05044 void T3RtxTimer::expire(Event*)
05045 {
05046   opAgent->Timeout(SCTP_CHUNK_DATA, spDest);
05047 }
05048 
05049 void HeartbeatTimeoutTimer::expire(Event*)
05050 {
05051   opAgent->Timeout(SCTP_CHUNK_HB, spDest);
05052 }
05053 
05054 void SctpAgent::CwndDegradeTimerExpiration(SctpDest_S *spDest)
05055 {
05056   DBG_I(CwndDegradeTimerExpiration);
05057   DBG_PL(CwndDegradeTimerExpiration, "spDest=%p"), spDest DBG_PR;
05058   if(spDest->iCwnd > iInitialCwnd * (int) uiMaxDataSize)
05059     {
05060       spDest->iCwnd = MAX(spDest->iCwnd/2, iInitialCwnd * (int) uiMaxDataSize);
05061       tiCwnd++; // trigger changes for trace to pick up
05062     }
05063   spDest->opCwndDegradeTimer->resched(spDest->dRto);
05064   DBG_X(CwndDegradeTimerExpiration);  
05065 }
05066 
05067 void CwndDegradeTimer::expire(Event*)
05068 {
05069   opAgent->CwndDegradeTimerExpiration(spDest);
05070 }
05071 
05072 void SctpAgent::SendHeartbeat(SctpDest_S *spDest)
05073 {
05074   DBG_I(SendHeartbeat);
05075   DBG_PL(SendHeartbeat, "spDest=%p"), spDest DBG_PR;
05076 
05077   SctpHeartbeatChunk_S sHeartbeatChunk;
05078   double dCurrTime = Scheduler::instance().clock();
05079 
05080   GenChunk(SCTP_CHUNK_HB, (u_char *) &sHeartbeatChunk); // doesn't fill dest
05081   sHeartbeatChunk.spDest = spDest;          // ...so we fill it here :-)
05082   SendPacket((u_char *) &sHeartbeatChunk, SCTP_CHUNK_HEARTBEAT_LENGTH, spDest);
05083   if(eOneHeartbeatTimer == TRUE)
05084     {
05085       spDest->dIdleSince = dCurrTime;
05086       opHeartbeatTimeoutTimer->resched(spDest->dRto);
05087       opHeartbeatTimeoutTimer->spDest = spDest;
05088     }
05089   else
05090     {
05091       spDest->opHeartbeatTimeoutTimer->resched(spDest->dRto);
05092     }
05093   DBG_PL(SendHeartbeat, "HEARTBEAT times out at %f"), 
05094     spDest->dRto+dCurrTime DBG_PR;
05095 
05096   DBG_X(SendHeartbeat);
05097 }
05098 
05099 void SctpAgent::HeartbeatGenTimerExpiration(double dTimerStartTime, 
05100                         SctpDest_S *spDest)
05101 {
05102   DBG_I(HeartbeatGenTimerExpiration);
05103 
05104   Node_S *spCurrNode = NULL;
05105   SctpDest_S *spCurrNodeData = NULL;
05106   Node_S *spLongestIdleNode = NULL;
05107   SctpDest_S *spLongestIdleNodeData = NULL;
05108   double dCurrTime = Scheduler::instance().clock();
05109   double dTime;
05110 
05111   if(eOneHeartbeatTimer == FALSE)
05112     SendHeartbeat(spDest);
05113   else
05114     {
05115       DBG_PL(HeartbeatGenTimerExpiration, "finding the longest idle dest...") 
05116     DBG_PR;
05117 
05118       /* find the destination which has been idle the longest 
05119        */
05120       for(spCurrNode = spLongestIdleNode = sDestList.spHead;
05121       spCurrNode != NULL;
05122       spCurrNode = spCurrNode->spNext)
05123     {
05124       spCurrNodeData = (SctpDest_S *) spCurrNode->vpData;
05125       spLongestIdleNodeData = (SctpDest_S *) spLongestIdleNode->vpData;
05126 
05127       DBG_PL(HeartbeatGenTimerExpiration, "spDest=%p idle since %f"), 
05128         spCurrNodeData, spCurrNodeData->dIdleSince DBG_PR;
05129       
05130       if(spCurrNodeData->dIdleSince < spLongestIdleNodeData->dIdleSince)
05131         spLongestIdleNode = spCurrNode;
05132     }
05133       
05134       /* has it been idle long enough?
05135        */
05136       spLongestIdleNodeData = (SctpDest_S *) spLongestIdleNode->vpData;
05137       DBG_PL(HeartbeatGenTimerExpiration, "longest idle dest since %f"),
05138     spLongestIdleNodeData->dIdleSince DBG_PR;
05139       DBG_PL(HeartbeatGenTimerExpiration, "timer start time %f"),
05140     dTimerStartTime DBG_PR;
05141       if(spLongestIdleNodeData->dIdleSince <= dTimerStartTime)
05142     SendHeartbeat(spLongestIdleNodeData);
05143       else
05144     DBG_PL(HeartbeatGenTimerExpiration, 
05145            "longest idle dest not idle long enough!") DBG_PR;
05146 
05147       /* start the timer again...
05148        */
05149       dTime = CalcHeartbeatTime(spLongestIdleNodeData->dRto);
05150       opHeartbeatGenTimer->resched(dTime);
05151       opHeartbeatGenTimer->dStartTime = dCurrTime;
05152     }
05153 
05154   DBG_X(HeartbeatGenTimerExpiration);
05155 }
05156 
05157 void HeartbeatGenTimer::expire(Event*)
05158 {
05159   opAgent->HeartbeatGenTimerExpiration(dStartTime, spDest);
05160 }
05161 
05162 void SctpAgent::SackGenTimerExpiration() // section 6.2
05163 {
05164   DBG_I(SackGenTimerExpiration);
05165 
05166   u_char ucpOutData[uiMaxPayloadSize];
05167   int iOutDataSize = 0;
05168   memset(ucpOutData, 0, uiMaxPayloadSize);
05169 
05170   iDataPktCountSinceLastSack = 0; // reset 
05171 
05172   iOutDataSize = BundleControlChunks(ucpOutData);
05173   iOutDataSize += GenChunk(SCTP_CHUNK_SACK, ucpOutData+iOutDataSize);
05174   SendPacket(ucpOutData, iOutDataSize, spReplyDest); 
05175   DBG_PL(SackGenTimerExpiration, "SACK sent (%d bytes)"), iOutDataSize DBG_PR;
05176 
05177   DBG_X(SackGenTimerExpiration);
05178 }
05179 
05180 void SackGenTimer::expire(Event*)
05181 {
05182   opAgent->SackGenTimerExpiration();
05183 }
05184 
05185 void SctpAgent::RouteCacheFlushTimerExpiration(SctpDest_S *spDest)
05186 {
05187   DBG_I(RouteCacheFlushTimerExpiration);
05188   DBG_PL(RouteCacheFlushTimerExpiration, "spDest=%p"), spDest DBG_PR;
05189   double dCurrTime = Scheduler::instance().clock();
05190   DBG_PL(RouteCacheFlushTimerExpiration, "dCurrTime=%f"), dCurrTime DBG_PR;
05191 
05192   spDest->eRouteCached = FALSE;
05193 
05194   DBG_X(RouteCacheFlushTimerExpiration);
05195 }
05196 
05197 void RouteCacheFlushTimer::expire(Event*)
05198 {
05199   opAgent->RouteCacheFlushTimerExpiration(spDest);
05200 }
05201 
05202 void SctpAgent::RouteCalcDelayTimerExpiration(SctpDest_S *spDest)
05203 {
05204   DBG_I(RouteCalcDelayTimerExpiration);
05205   DBG_PL(RouteCalcDelayTimerExpiration, "spDest=%p"), spDest DBG_PR;
05206   double dCurrTime = Scheduler::instance().clock();
05207   DBG_PL(RouteCalcDelayTimerExpiration, "dCurrTime=%f"), dCurrTime DBG_PR;
05208 
05209   Node_S *spCurrNode = spDest->sBufferedPackets.spHead;
05210   Node_S *spDeleteNode = NULL;
05211 
05212   spDest->iRcdCount++;
05213   tiRcdCount++;
05214   spDest->eRouteCached = TRUE;
05215   SetSource(spDest); // set src addr, port, target based on "routing table"
05216   SetDestination(spDest); // set dest addr & port 
05217 
05218   /* Dequeue and send all queued packets
05219    */
05220   while(spCurrNode != NULL)
05221     {
05222       send( (Packet *) spCurrNode->vpData, 0);
05223       spDeleteNode = spCurrNode;
05224       spCurrNode = spCurrNode->spNext;
05225       DeleteNode(&spDest->sBufferedPackets, spDeleteNode);
05226     }
05227 
05228   DBG_X(RouteCalcDelayTimerExpiration);
05229 }
05230 
05231 void RouteCalcDelayTimer::expire(Event*)
05232 {
05233   opAgent->RouteCalcDelayTimerExpiration(spDest);
05234 }
05235 
05236 /****************************************************************************
05237  * debugging functions
05238  ****************************************************************************/
05239 
05240 void SctpAgent::DumpSendBuffer()
05241 {
05242   DBG_IF(DumpSendBuffer)
05243     {
05244       DBG_I(DumpSendBuffer);
05245 
05246       Node_S *spCurrNode = NULL;
05247       SctpSendBufferNode_S *spCurrNodeData = NULL;
05248       SctpDataChunkHdr_S  *spChunk = NULL;
05249 
05250       for(spCurrNode = sSendBuffer.spHead;
05251       spCurrNode != NULL;
05252       spCurrNode = spCurrNode->spNext)
05253     {
05254       spCurrNodeData = (SctpSendBufferNode_S *) spCurrNode->vpData;
05255       spChunk = spCurrNodeData->spChunk;
05256       DBG_PL(DumpSendBuffer, 
05257          "tsn=%d spDest=%p eMarkedForRtx=%s %s %s"),
05258         spChunk->uiTsn, 
05259         spCurrNodeData->spDest,
05260         (!spCurrNodeData->eMarkedForRtx ? "NO_RTX" 
05261          : (spCurrNodeData->eMarkedForRtx==FAST_RTX ? "FAST_RTX"
05262         : "TIMEOUT_RTX")),
05263         spCurrNodeData->eMarkedForRtx ? "MarkedForRtx" : "",
05264         spCurrNodeData->eGapAcked ? "GapAcked" : "",
05265         spCurrNodeData->eAdvancedAcked ? "AdvancedAcked" : ""
05266         DBG_PR;
05267       DBG_PL(DumpSendBuffer, 
05268          "       spCurrNodeData->spDest->iOutstandingBytes=%lu"),
05269         spCurrNodeData->spDest->iOutstandingBytes DBG_PR;
05270     }
05271 
05272       DBG_X(DumpSendBuffer);
05273     }
05274 }

Generated on Tue Mar 6 16:47:51 2007 for ns2 Network Simulator 2.29 by  doxygen 1.4.6