sctp.cc
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:164k
- /*
- * Copyright (c) 2006-2007 by the Protocol Engineering Lab, U of Delaware
- * All rights reserved.
- *
- * Protocol Engineering Lab web page : http://pel.cis.udel.edu/
- *
- * Paul D. Amer <amer@@cis,udel,edu>
- * Armando L. Caro Jr. <acaro@@cis,udel,edu>
- * Janardhan Iyengar <iyengar@@cis,udel,edu>
- * Preethi Natarajan <nataraja@@cis,udel,edu>
- * Nasif Ekiz <nekiz@@cis,udel,edu>
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * 3. Neither the name of the University nor of the Laboratory may be used
- * to endorse or promote products derived from this software without
- * specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
- #ifndef lint
- static const char rcsid[] =
- "@(#) $Header: /cvsroot/nsnam/ns-2/sctp/sctp.cc,v 1.14 2008/03/27 05:21:18 tom_henderson Exp $ (UD/PEL)";
- #endif
- #include "ip.h"
- #include "sctp.h"
- #include "flags.h"
- #include "random.h"
- #include "template.h"
- #include "sctpDebug.h"
- #ifdef DMALLOC
- #include "dmalloc.h"
- #endif
- #define MIN(x,y) (((x)<(y))?(x):(y))
- #define MAX(x,y) (((x)>(y))?(x):(y))
- int hdr_sctp::offset_;
- static class SCTPHeaderClass : public PacketHeaderClass
- {
- public:
- SCTPHeaderClass() : PacketHeaderClass("PacketHeader/SCTP",
- sizeof(hdr_sctp))
- {
- bind_offset(&hdr_sctp::offset_);
- }
- } class_sctphdr;
- static class SctpClass : public TclClass
- {
- public:
- SctpClass() : TclClass("Agent/SCTP") {}
- TclObject* create(int, const char*const*)
- {
- return (new SctpAgent());
- }
- } classSctp;
- SctpAgent::SctpAgent() : Agent(PT_SCTP)
- {
- fhpDebugFile = NULL; // init
- opCoreTarget = NULL; // initialize to NULL in case multihoming isn't used.
- memset(&sInterfaceList, 0, sizeof(List_S) );
- memset(&sDestList, 0, sizeof(List_S) );
- memset(&sAppLayerBuffer, 0, sizeof(List_S) );
- memset(&sSendBuffer, 0, sizeof(List_S) );
- memset(&sRecvTsnBlockList, 0, sizeof(List_S) );
- memset(&sDupTsnList, 0, sizeof(List_S) );
- spOutStreams = NULL;
- opSackGenTimer = new SackGenTimer(this);
- spSctpTrace = NULL;
- opHeartbeatGenTimer = new HeartbeatGenTimer(this);
- opT1InitTimer = new T1InitTimer(this);
- opT1CookieTimer = new T1CookieTimer(this);
- eState = SCTP_STATE_UNINITIALIZED;
- }
- SctpAgent::~SctpAgent()
- {
- Node_S *spCurrNode = NULL;
- Node_S *spPrevNode = NULL;
- SctpDest_S *spDest = NULL;
- delete opSackGenTimer;
- opSackGenTimer = NULL;
- delete opHeartbeatGenTimer;
- opHeartbeatGenTimer = NULL;
- delete opT1InitTimer;
- opT1InitTimer = NULL;
- delete opT1CookieTimer;
- opT1CookieTimer = NULL;
- if(spOutStreams != NULL)
- delete spOutStreams;
- for(spCurrNode = sInterfaceList.spHead;
- spCurrNode != NULL;
- spPrevNode = spCurrNode,spCurrNode=spCurrNode->spNext, delete spPrevNode)
- {
- delete (SctpInterface_S *) spCurrNode->vpData;
- spCurrNode->vpData = NULL;
- }
- for(spCurrNode = sDestList.spHead;
- spCurrNode != NULL;
- spPrevNode = spCurrNode,spCurrNode=spCurrNode->spNext, delete spPrevNode)
- {
- spDest = (SctpDest_S *) spCurrNode->vpData;
- if(spDest->opT3RtxTimer != NULL)
- {
- delete spDest->opT3RtxTimer;
- spDest->opT3RtxTimer = NULL;
- }
- if(spDest->opCwndDegradeTimer != NULL)
- {
- delete spDest->opCwndDegradeTimer;
- spDest->opCwndDegradeTimer = NULL;
- }
- if(spDest->opHeartbeatTimeoutTimer != NULL)
- {
- delete spDest->opHeartbeatTimeoutTimer;
- spDest->opHeartbeatTimeoutTimer = NULL;
- }
- if(spDest->opRouteCacheFlushTimer != NULL)
- {
- delete spDest->opRouteCacheFlushTimer;
- spDest->opRouteCacheFlushTimer = NULL;
- }
- if(spDest->opRouteCalcDelayTimer != NULL)
- {
- delete spDest->opRouteCalcDelayTimer;
- spDest->opRouteCalcDelayTimer = NULL;
- }
- Packet::free(spDest->opRoutingAssistPacket);
- spDest->opRoutingAssistPacket = NULL;
- delete (SctpDest_S *) spCurrNode->vpData; //spDest
- spCurrNode->vpData = NULL;
- }
- if(spSctpTrace != NULL)
- {
- delete spSctpTrace;
- spSctpTrace = NULL;
- }
- }
- void SctpAgent::delay_bind_init_all()
- {
- delay_bind_init_one("debugMask_");
- delay_bind_init_one("debugFileIndex_");
- delay_bind_init_one("associationMaxRetrans_");
- delay_bind_init_one("pathMaxRetrans_");
- delay_bind_init_one("changePrimaryThresh_");
- delay_bind_init_one("maxInitRetransmits_");
- delay_bind_init_one("heartbeatInterval_");
- delay_bind_init_one("mtu_");
- delay_bind_init_one("initialRwnd_");
- delay_bind_init_one("initialSsthresh_");
- delay_bind_init_one("ipHeaderSize_");
- delay_bind_init_one("dataChunkSize_");
- delay_bind_init_one("numOutStreams_");
- delay_bind_init_one("useDelayedSacks_");
- delay_bind_init_one("sackDelay_");
- delay_bind_init_one("useMaxBurst_");
- delay_bind_init_one("initialCwnd_");
- delay_bind_init_one("initialRto_");
- delay_bind_init_one("minRto_");
- delay_bind_init_one("maxRto_");
- delay_bind_init_one("fastRtxTrigger_");
- delay_bind_init_one("numUnrelStreams_");
- delay_bind_init_one("reliability_");
- delay_bind_init_one("unordered_");
- delay_bind_init_one("rtxToAlt_");
- delay_bind_init_one("dormantAction_");
- delay_bind_init_one("routeCacheLifetime_");
- delay_bind_init_one("routeCalcDelay_");
- delay_bind_init_one("trace_all_");
- delay_bind_init_one("cwnd_");
- delay_bind_init_one("rwnd_");
- delay_bind_init_one("rto_");
- delay_bind_init_one("errorCount_");
- delay_bind_init_one("frCount_");
- delay_bind_init_one("timeoutCount_");
- delay_bind_init_one("rcdCount_");
- Agent::delay_bind_init_all();
- }
- int SctpAgent::delay_bind_dispatch(const char *cpVarName,
- const char *cpLocalName,
- TclObject *opTracer)
- {
- if(delay_bind(cpVarName, cpLocalName, "debugMask_", &uiDebugMask, opTracer))
- return TCL_OK;
-
- if(delay_bind(cpVarName, cpLocalName,
- "debugFileIndex_", &iDebugFileIndex, opTracer))
- return TCL_OK;
-
- if(delay_bind(cpVarName, cpLocalName,
- "associationMaxRetrans_", &uiAssociationMaxRetrans, opTracer))
- return TCL_OK;
- if(delay_bind(cpVarName, cpLocalName,
- "pathMaxRetrans_", &uiPathMaxRetrans, opTracer))
- return TCL_OK;
- if(delay_bind(cpVarName, cpLocalName,
- "changePrimaryThresh_", &uiChangePrimaryThresh, opTracer))
- return TCL_OK;
- if(delay_bind(cpVarName, cpLocalName,
- "maxInitRetransmits_", &uiMaxInitRetransmits, opTracer))
- return TCL_OK;
- if(delay_bind(cpVarName, cpLocalName,
- "heartbeatInterval_", &uiHeartbeatInterval, opTracer))
- return TCL_OK;
- if(delay_bind(cpVarName, cpLocalName, "mtu_", &uiMtu, opTracer))
- return TCL_OK;
-
- if(delay_bind(cpVarName, cpLocalName,
- "initialRwnd_", &uiInitialRwnd, opTracer))
- return TCL_OK;
- if(delay_bind(cpVarName, cpLocalName,
- "initialSsthresh_", &iInitialSsthresh, opTracer))
- return TCL_OK;
-
- if(delay_bind(cpVarName, cpLocalName,
- "ipHeaderSize_", &uiIpHeaderSize, opTracer))
- return TCL_OK;
- if(delay_bind(cpVarName, cpLocalName,
- "dataChunkSize_", &uiDataChunkSize, opTracer))
- return TCL_OK;
-
- if(delay_bind(cpVarName, cpLocalName,
- "numOutStreams_", &uiNumOutStreams, opTracer))
- return TCL_OK;
- if(delay_bind(cpVarName, cpLocalName,
- "useDelayedSacks_", (int *) &eUseDelayedSacks, opTracer))
- return TCL_OK;
- if(delay_bind(cpVarName, cpLocalName,
- "sackDelay_", &dSackDelay, opTracer))
- return TCL_OK;
- if(delay_bind(cpVarName, cpLocalName,
- "useMaxBurst_", (int *) &eUseMaxBurst, opTracer))
- return TCL_OK;
- if(delay_bind(cpVarName, cpLocalName,
- "initialCwnd_", &iInitialCwnd, opTracer))
- return TCL_OK;
- if(delay_bind(cpVarName, cpLocalName,
- "initialRto_", &dInitialRto, opTracer))
- return TCL_OK;
- if(delay_bind(cpVarName, cpLocalName,
- "minRto_", &dMinRto, opTracer))
- return TCL_OK;
- if(delay_bind(cpVarName, cpLocalName,
- "maxRto_", &dMaxRto, opTracer))
- return TCL_OK;
-
- if(delay_bind(cpVarName, cpLocalName,
- "fastRtxTrigger_", &iFastRtxTrigger, opTracer))
- return TCL_OK;
- if(delay_bind(cpVarName, cpLocalName,
- "numUnrelStreams_", &uiNumUnrelStreams, opTracer))
- return TCL_OK;
- if(delay_bind(cpVarName, cpLocalName,
- "reliability_", &uiReliability, opTracer))
- return TCL_OK;
- if(delay_bind(cpVarName, cpLocalName,
- "unordered_", (int *) &eUnordered, opTracer))
- return TCL_OK;
- if(delay_bind(cpVarName, cpLocalName,
- "rtxToAlt_", (int *) &eRtxToAlt, opTracer))
- return TCL_OK;
- if(delay_bind(cpVarName, cpLocalName,
- "dormantAction_", (int *) &eDormantAction, opTracer))
- return TCL_OK;
- if(delay_bind(cpVarName, cpLocalName,
- "routeCacheLifetime_", &dRouteCacheLifetime, opTracer))
- return TCL_OK;
- if(delay_bind(cpVarName, cpLocalName,
- "routeCalcDelay_", &dRouteCalcDelay, opTracer))
- return TCL_OK;
- if(delay_bind(cpVarName, cpLocalName, "cwnd_", &tiCwnd, opTracer))
- return TCL_OK;
- if(delay_bind(cpVarName, cpLocalName, "rwnd_", &tiRwnd, opTracer))
- return TCL_OK;
- if(delay_bind(cpVarName, cpLocalName, "rto_", &tdRto, opTracer))
- return TCL_OK;
- if(delay_bind(cpVarName, cpLocalName, "errorCount_", &tiErrorCount,opTracer))
- return TCL_OK;
- if(delay_bind(cpVarName, cpLocalName, "frCount_", &tiFrCount, opTracer))
- return TCL_OK;
- if(delay_bind(cpVarName, cpLocalName,
- "timeoutCount_", &tiTimeoutCount, opTracer))
- return TCL_OK;
- if(delay_bind(cpVarName, cpLocalName, "rcdCount_", &tiRcdCount, opTracer))
- return TCL_OK;
- if(delay_bind(cpVarName, cpLocalName,
- "trace_all_", (int *) &eTraceAll, opTracer))
- return TCL_OK;
- return Agent::delay_bind_dispatch(cpVarName, cpLocalName, opTracer);
- }
- void SctpAgent::TraceAll()
- {
- char cpOutString[500];
- Node_S *spCurrNode = NULL;
- SctpDest_S *spCurrDest = NULL;
- double dCurrTime = Scheduler::instance().clock();
- for(spCurrNode = sDestList.spHead;
- spCurrNode != NULL;
- spCurrNode = spCurrNode->spNext)
- {
- spCurrDest = (SctpDest_S *) spCurrNode->vpData;
- SetSource(spCurrDest); // gives us the correct source addr & port
- sprintf(cpOutString,
- "time: %-8.5f "
- "saddr: %-2d sport: %-2d daddr: %-2d dport: %-2d "
- "cwnd: %d pba: %d out: %d ssthresh: %d rwnd: %d peerRwnd: %d "
- "rto: %-6.3f srtt: %-6.3f rttvar: %-6.3f "
- "assocErrors: %d pathErrors: %d dstatus: %s isPrimary: %s "
- "frCount: %d timeoutCount: %d rcdCount: %dn",
- dCurrTime,
- addr(), port(), spCurrDest->iNsAddr, spCurrDest->iNsPort,
- spCurrDest->iCwnd, spCurrDest->iPartialBytesAcked,
- spCurrDest->iOutstandingBytes, spCurrDest->iSsthresh,
- uiMyRwnd, uiPeerRwnd,
- spCurrDest->dRto, spCurrDest->dSrtt,
- spCurrDest->dRttVar,
- iAssocErrorCount,
- spCurrDest->iErrorCount,
- spCurrDest->eStatus ? "ACTIVE" : "INACTIVE",
- (spCurrDest == spPrimaryDest) ? "TRUE" : "FALSE",
- int(tiFrCount),
- spCurrDest->iTimeoutCount,
- spCurrDest->iRcdCount);
- if(channel_)
- (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
- }
- sprintf(cpOutString, "n");
- if(channel_)
- (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
- }
- void SctpAgent::TraceVar(const char* cpVar)
- {
- char cpOutString[500];
- Node_S *spCurrNode = NULL;
- SctpDest_S *spCurrDest = NULL;
- double dCurrTime = Scheduler::instance().clock();
- if(!strcmp(cpVar, "cwnd_"))
- for(spCurrNode = sDestList.spHead;
- spCurrNode != NULL;
- spCurrNode = spCurrNode->spNext)
- {
- spCurrDest = (SctpDest_S *) spCurrNode->vpData;
- SetSource(spCurrDest); // gives us the correct source addr & port
- sprintf(cpOutString,
- "time: %-8.5f "
- "saddr: %-2d sport: %-2d daddr: %-2d dport: %-2d "
- "cwnd: %d pba: %d out: %d ssthresh: %d peerRwnd: %dn",
- dCurrTime,
- addr(), port(),
- spCurrDest->iNsAddr, spCurrDest->iNsPort,
- spCurrDest->iCwnd, spCurrDest->iPartialBytesAcked,
- spCurrDest->iOutstandingBytes, spCurrDest->iSsthresh,
- uiPeerRwnd);
- if(channel_)
- (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
- }
- else if(!strcmp(cpVar, "rwnd_"))
- {
- sprintf(cpOutString, "time: %-8.5f rwnd: %d peerRwnd: %dn",
- dCurrTime, uiMyRwnd, uiPeerRwnd);
- if(channel_)
- (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
- }
-
- else if(!strcmp(cpVar, "rto_"))
- for(spCurrNode = sDestList.spHead;
- spCurrNode != NULL;
- spCurrNode = spCurrNode->spNext)
- {
- spCurrDest = (SctpDest_S *) spCurrNode->vpData;
- SetSource(spCurrDest); // gives us the correct source addr & port
- sprintf(cpOutString,
- "time: %-8.5f "
- "saddr: %-2d sport: %-2d daddr: %-2d dport: %-2d "
- "rto: %-6.3f srtt: %-6.3f rttvar: %-6.3fn",
- dCurrTime,
- addr(), port(),
- spCurrDest->iNsAddr, spCurrDest->iNsPort,
- spCurrDest->dRto, spCurrDest->dSrtt,
- spCurrDest->dRttVar);
- if(channel_)
- (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
- }
- else if(!strcmp(cpVar, "errorCount_"))
- for(spCurrNode = sDestList.spHead;
- spCurrNode != NULL;
- spCurrNode = spCurrNode->spNext)
- {
- spCurrDest = (SctpDest_S *) spCurrNode->vpData;
- SetSource(spCurrDest); // gives us the correct source addr & port
- sprintf(cpOutString,
- "time: %-8.5f "
- "saddr: %-2d sport: %-2d daddr: %-2d dport: %-2d "
- "assocErrors: %d pathErrors: %d dstatus: %s isPrimary: %sn",
- dCurrTime,
- addr(), port(),
- spCurrDest->iNsAddr, spCurrDest->iNsPort,
- iAssocErrorCount,
- spCurrDest->iErrorCount,
- spCurrDest->eStatus ? "ACTIVE" : "INACTIVE",
- (spCurrDest == spPrimaryDest) ? "TRUE" : "FALSE");
- if(channel_)
- (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
- }
- else if(!strcmp(cpVar, "frCount_"))
- {
- sprintf(cpOutString,
- "time: %-8.5f "
- "frCount: %dn",
- dCurrTime,
- int(*((TracedInt*) cpVar)) );
- if(channel_)
- (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
- }
- else if(!strcmp(cpVar, "timeoutCount_"))
- {
- for(spCurrNode = sDestList.spHead;
- spCurrNode != NULL;
- spCurrNode = spCurrNode->spNext)
- {
- spCurrDest = (SctpDest_S *) spCurrNode->vpData;
- SetSource(spCurrDest); // gives us the correct source addr & port
- sprintf(cpOutString,
- "time: %-8.5f "
- "saddr: %-2d sport: %-2d daddr: %-2d dport: %-2d "
- "timeoutCount: %dn",
- dCurrTime,
- addr(), port(),
- spCurrDest->iNsAddr, spCurrDest->iNsPort,
- spCurrDest->iTimeoutCount);
- if(channel_)
- (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
- }
- }
- else if(!strcmp(cpVar, "rcdCount_"))
- {
- for(spCurrNode = sDestList.spHead;
- spCurrNode != NULL;
- spCurrNode = spCurrNode->spNext)
- {
- spCurrDest = (SctpDest_S *) spCurrNode->vpData;
- SetSource(spCurrDest); // gives us the correct source addr & port
- sprintf(cpOutString,
- "time: %-8.5f "
- "saddr: %-2d sport: %-2d daddr: %-2d dport: %-2d "
- "rcdCount: %dn",
- dCurrTime,
- addr(), port(),
- spCurrDest->iNsAddr, spCurrDest->iNsPort,
- spCurrDest->iRcdCount);
- if(channel_)
- (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
- }
- }
- else
- {
- sprintf(cpOutString,
- "time: %-8.5f "
- "saddr: %-2d sport: %-2d daddr: %-2d dport: %-2d %s: %sn",
- dCurrTime, addr(), port(), daddr(), dport(),
- cpVar, "ERROR (unepected trace variable)");
- if(channel_)
- (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
- }
- sprintf(cpOutString, "n");
- if(channel_)
- (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
- }
- void SctpAgent::trace(TracedVar* v)
- {
- if(eTraceAll == TRUE)
- TraceAll();
- else
- TraceVar(v->name());
- }
- /* Reset() is called to refresh the current association settings. It is used
- * for the first association. It can also be used to abruptly terminate the
- * existing association (so that a new one can begin), but this usage has NOT
- * been tested extensively.
- */
- void SctpAgent::Reset()
- {
- /* Just in case the user uses the standard ns-2 way of turning on
- * debugging, we turn on all debugging. However, if the uiDebugMask is
- * used, then the user knows about SCTP's fine-level debugging control
- * and the debug_ flag is ignored.
- */
- if(debug_ == TRUE && uiDebugMask == 0)
- uiDebugMask = 0xffffffff;
- /* No debugging output will appear until this is called. Why do we do it
- * here? This is the earliest point at which we have the necessary
- * debugging variables bound from TCL.
- */
- DBG_FOPEN(); // internal check is done to ensure we only open once!
- DBG_I(Reset);
- if(eState != SCTP_STATE_UNINITIALIZED && eState != SCTP_STATE_CLOSED)
- Close(); // abruptly close the connection
- DBG_PL(Reset, "uiDebugMask=%u"), uiDebugMask DBG_PR;
- DBG_PL(Reset, "iDebugFileIndex=%d"), iDebugFileIndex DBG_PR;
- DBG_PL(Reset, "uiAssociationMaxRetrans=%ld"), uiAssociationMaxRetrans DBG_PR;
- DBG_PL(Reset, "uiPathMaxRetrans=%ld"), uiPathMaxRetrans DBG_PR;
- DBG_PL(Reset, "uiChangePrimaryThresh=%d"), uiChangePrimaryThresh DBG_PR;
- DBG_PL(Reset, "uiHeartbeatInterval=%ld"), uiHeartbeatInterval DBG_PR;
- DBG_PL(Reset, "uiMtu=%ld"), uiMtu DBG_PR;
- DBG_PL(Reset, "uiInitialRwnd=%ld"), uiInitialRwnd DBG_PR;
- DBG_PL(Reset, "iInitialSsthresh=%ld"), iInitialSsthresh DBG_PR;
- DBG_PL(Reset, "uiIpHeaderSize=%ld"), uiIpHeaderSize DBG_PR;
- DBG_PL(Reset, "uiDataChunkSize=%ld"), uiDataChunkSize DBG_PR;
- DBG_PL(Reset, "uiNumOutStreams=%ld"), uiNumOutStreams DBG_PR;
- DBG_PL(Reset, "eUseDelayedSacks=%s"),
- eUseDelayedSacks ? "TRUE" : "FALSE" DBG_PR;
- DBG_PL(Reset, "dSackDelay=%f"), dSackDelay DBG_PR;
- DBG_PL(Reset, "eUseMaxBurst=%s"), eUseMaxBurst ? "TRUE" : "FALSE" DBG_PR;
- DBG_PL(Reset, "iInitialCwnd=%ld"), iInitialCwnd DBG_PR;
- DBG_PL(Reset, "dInitialRto=%f"), dInitialRto DBG_PR;
- DBG_PL(Reset, "dMinRto=%f"), dMinRto DBG_PR;
- DBG_PL(Reset, "dMaxRto=%f"), dMaxRto DBG_PR;
- DBG_PL(Reset, "iFastRtxTrigger=%ld"), iFastRtxTrigger DBG_PR;
- DBG_PL(Reset, "uiNumUnrelStreams=%ld"), uiNumUnrelStreams DBG_PR;
- DBG_PL(Reset, "uiReliability=%ld"), uiReliability DBG_PR;
- DBG_PL(Reset, "eUnordered=%s"), eUnordered ? "TRUE" : "FALSE" DBG_PR;
- switch(eRtxToAlt)
- {
- case RTX_TO_ALT_OFF:
- DBG_PL(Reset, "eRtxToAlt=RTX_TO_ALT_OFF") DBG_PR;
- break;
- case RTX_TO_ALT_ON:
- DBG_PL(Reset, "eRtxToAlt=RTX_TO_ALT_ON") DBG_PR;
- break;
- case RTX_TO_ALT_TIMEOUTS_ONLY:
- DBG_PL(Reset, "eRtxToAlt=RTX_TO_ALT_TIMEOUTS_ONLY") DBG_PR;
- break;
- }
- switch(eDormantAction)
- {
- case DORMANT_HOP:
- DBG_PL(Reset, "eDormantAction=DORMANT_HOP") DBG_PR;
- break;
- case DORMANT_PRIMARY:
- DBG_PL(Reset, "eDormantAction=DORMANT_PRIMARY") DBG_PR;
- break;
- case DORMANT_LASTDEST:
- DBG_PL(Reset, "eDormantAction=DORMANT_LASTDEST") DBG_PR;
- break;
- }
- DBG_PL(Reset, "eTraceAll=%s"), eTraceAll ? "TRUE" : "FALSE" DBG_PR;
- Node_S *spCurrNode = NULL;
- SctpDest_S *spCurrDest = NULL;
- int i;
- if(uiInitialRwnd > MAX_RWND_SIZE)
- {
- fprintf(stderr, "SCTP ERROR: initial rwnd (%d) > max (%d)n",
- uiInitialRwnd, MAX_RWND_SIZE);
- DBG_PL(Reset, "ERROR: initial rwnd (%d) > max (%d)"),
- uiInitialRwnd, MAX_RWND_SIZE DBG_PR;
- DBG_PL(Reset, "exiting...") DBG_PR;
- exit(-1);
- }
- if(uiNumOutStreams > MAX_NUM_STREAMS)
- {
- fprintf(stderr, "%s number of streams (%d) > max (%d)n",
- "SCTP ERROR:",
- uiNumOutStreams, MAX_NUM_STREAMS);
- DBG_PL(Reset, "ERROR: number of streams (%d) > max (%d)"),
- uiNumOutStreams, MAX_NUM_STREAMS DBG_PR;
- DBG_PL(Reset, "exiting...") DBG_PR;
- exit(-1);
- }
- else if(uiNumUnrelStreams > uiNumOutStreams)
- {
- fprintf(stderr, "%s number of unreliable streams (%d) > total (%d)n",
- "SCTP ERROR:",
- uiNumUnrelStreams, uiNumOutStreams);
- DBG_PL(Reset, "ERROR: number of unreliable streams (%d) > total (%d)"),
- uiNumUnrelStreams, uiNumOutStreams DBG_PR;
- DBG_PL(Reset, "exiting...") DBG_PR;
- exit(-1);
- }
- uiMaxPayloadSize = uiMtu - SCTP_HDR_SIZE - uiIpHeaderSize;
- uiMaxDataSize = uiMaxPayloadSize - ControlChunkReservation();
- if(uiDataChunkSize > MAX_DATA_CHUNK_SIZE)
- {
- fprintf(stderr, "%s data chunk size (%d) > max (%d)n",
- "SCTP ERROR:",
- uiDataChunkSize, MAX_DATA_CHUNK_SIZE);
- DBG_PL(Reset, "ERROR: data chunk size (%d) > max (%d)"),
- uiDataChunkSize, MAX_DATA_CHUNK_SIZE DBG_PR;
- DBG_PL(Reset, "exiting...") DBG_PR;
- exit(-1);
- }
- else if(uiDataChunkSize > uiMaxDataSize)
- {
- fprintf(stderr, "SCTP ERROR: DATA chunk size (%d) too big!n",
- uiDataChunkSize);
- fprintf(stderr, " SCTP/IP header = %dn",
- SCTP_HDR_SIZE + uiIpHeaderSize);
- fprintf(stderr, " Control chunk reservation = %dn",
- ControlChunkReservation());
- fprintf(stderr, " MTU = %dn", uiMtu);
- fprintf(stderr, "n");
- DBG_PL(Reset,
- "ERROR: data chunk size (%d) + SCTP/IP header(%d) + Reserved (%d) > MTU (%d)"),
- uiDataChunkSize, SCTP_HDR_SIZE + uiIpHeaderSize,
- ControlChunkReservation(), uiMtu DBG_PR;
- DBG_PL(Reset, "exiting...") DBG_PR;
- exit(-1);
- }
- else if(uiDataChunkSize < MIN_DATA_CHUNK_SIZE)
- {
- fprintf(stderr, "%s data chunk size (%d) < min (%d)n",
- "SCTP ERROR:",
- uiDataChunkSize, MIN_DATA_CHUNK_SIZE);
- DBG_PL(Reset, "ERROR: data chunk size (%d) < min (%d)"),
- uiDataChunkSize, MIN_DATA_CHUNK_SIZE DBG_PR;
- DBG_PL(Reset, "exiting...") DBG_PR;
- exit(-1);
- }
- /* size_ is an Agent variable which is normally the packet size, but
- * SCTP uses size_ to dictate to non-sctp aware applications the max
- * data size of an application write. If size_ isn't set by SCTP, some
- * non-sctp aware apps (such as Telnet) will call sendmsg() with 0
- * bytes.
- */
- size_ = uiMtu - SCTP_HDR_SIZE - uiIpHeaderSize - sizeof(SctpDataChunkHdr_S);
- eState = SCTP_STATE_CLOSED;
- eForceSource = FALSE;
- iAssocErrorCount = 0;
- if(uiHeartbeatInterval != 0)
- {
- opHeartbeatGenTimer->force_cancel();
- }
- opT1InitTimer->force_cancel();
- opT1CookieTimer->force_cancel();
- iInitTryCount = 0;
- uiNextTsn = 0;
- usNextStreamId = 0;
- /* if it's already allocated, let's delete and reallocate just in case user
- * has changed TCL bindable variables
- */
- if(spOutStreams != NULL)
- delete spOutStreams;
- spOutStreams = new SctpOutStream_S [uiNumOutStreams];
- memset(spOutStreams, 0, (uiNumOutStreams * sizeof(SctpOutStream_S)) );
- for(i = 0; i < (int) uiNumUnrelStreams; i++)
- {
- DBG_PL(Reset, "setting outStream %d to UNRELIABLE"), i DBG_PR;
- spOutStreams[i].eMode = SCTP_STREAM_UNRELIABLE;
- }
- for(; i < (int) uiNumOutStreams; i++)
- {
- DBG_PL(Reset, "setting outStream %d to RELIABLE"), i DBG_PR;
- spOutStreams[i].eMode = SCTP_STREAM_RELIABLE;
- }
- uiPeerRwnd = 0;
- uiCumAckPoint = 0;
- uiAdvancedPeerAckPoint = 0;
- uiHighestTsnNewlyAcked = 0;
- uiRecover = 0;
- memset(&sAppLayerBuffer, 0, sizeof(List_S) );
- memset(&sSendBuffer, 0, sizeof(List_S) );
- if(uiAssociationMaxRetrans > (sDestList.uiLength * uiPathMaxRetrans))
- {
- DBG_PL(Reset,
- "WARNING: Association.Max.Retrans > "
- "summation of all destinations' Path.Max.Retrans "
- "(rfc2960 section 8.1)") DBG_PR;
- }
- for(spCurrNode = sDestList.spHead;
- spCurrNode != NULL;
- spCurrNode = spCurrNode->spNext)
- {
- spCurrDest = (SctpDest_S *) spCurrNode->vpData;
- spCurrDest->iCwnd = iInitialCwnd * uiMaxDataSize;
- spCurrDest->iSsthresh = iInitialSsthresh;
- spCurrDest->eFirstRttMeasurement = TRUE;
- spCurrDest->dRto = dInitialRto;
- if(spCurrDest->opT3RtxTimer == NULL)
- spCurrDest->opT3RtxTimer = new T3RtxTimer(this, spCurrDest);
- else
- spCurrDest->opT3RtxTimer->force_cancel();
- spCurrDest->iOutstandingBytes = 0;
- spCurrDest->iPartialBytesAcked = 0;
- spCurrDest->iErrorCount = 0;
- spCurrDest->iTimeoutCount = 0;
- spCurrDest->eStatus = SCTP_DEST_STATUS_ACTIVE;
- if(spCurrDest->opCwndDegradeTimer == NULL)
- {
- spCurrDest->opCwndDegradeTimer =
- new CwndDegradeTimer(this, spCurrDest);
- }
- else
- {
- spCurrDest->opCwndDegradeTimer->force_cancel();
- }
-
- spCurrDest->dIdleSince = 0;
-
- if(spCurrDest->opHeartbeatTimeoutTimer == NULL)
- {
- spCurrDest->opHeartbeatTimeoutTimer =
- new HeartbeatTimeoutTimer(this, spCurrDest);
- }
- else
- {
- spCurrDest->opHeartbeatTimeoutTimer->force_cancel();
- }
-
- spCurrDest->eCcApplied = FALSE;
- spCurrDest->spFirstOutstanding = NULL;
-
- /* per destination vars for CMT
- */
- spCurrDest->uiBurstLength = 0;
- spCurrDest->eMarkedChunksPending = FALSE;
-
- spCurrDest->iRcdCount = 0;
- spCurrDest->eRouteCached = FALSE;
-
- if(spCurrDest->opRouteCacheFlushTimer == NULL)
- {
- spCurrDest->opRouteCacheFlushTimer =
- new RouteCacheFlushTimer(this, spCurrDest);
- }
- else
- {
- spCurrDest->opRouteCacheFlushTimer->force_cancel();
- }
- if(spCurrDest->opRouteCalcDelayTimer == NULL)
- {
- spCurrDest->opRouteCalcDelayTimer =
- new RouteCalcDelayTimer(this, spCurrDest);
- }
- else
- {
- spCurrDest->opRouteCalcDelayTimer->force_cancel();
- }
- memset(&spCurrDest->sBufferedPackets, 0, sizeof(List_S) );
- }
- eForwardTsnNeeded = FALSE;
- eSendNewDataChunks = FALSE;
- eMarkedChunksPending = FALSE;
- eApplyMaxBurst = FALSE; // Why is MaxBurst init'd to FALSE?? (JRI)
- eDataSource = DATA_SOURCE_APPLICATION;
- uiBurstLength = 0;
- uiMyRwnd = uiInitialRwnd;
- uiCumAck = 0;
- uiHighestRecvTsn = 0;
- memset(&sRecvTsnBlockList, 0, sizeof(List_S) );
- memset(&sDupTsnList, 0, sizeof(List_S) );
- eStartOfPacket = FALSE;
- iDataPktCountSinceLastSack = 0;
- eSackChunkNeeded = FALSE;
- opSackGenTimer->force_cancel();
- /* if it's already allocated, let's delete and reallocate just in case user
- * has changed TCL bindable variables
- */
- if(spSctpTrace != NULL)
- delete spSctpTrace;
-
- /* We don't know how many chunks will be in a packet, so we assume the
- * theoretical maximum... a packet full of only chunk headers.
- */
- spSctpTrace = new SctpTrace_S[uiMaxPayloadSize / sizeof(SctpChunkHdr_S)];
- uiNumChunks = 0;
- /* Trigger changes for trace to pick up (we want to know the initial values)
- */
- tiCwnd++;
- tiRwnd++;
- tdRto++;
- tiErrorCount++;
- tiFrCount = 0;
- tiTimeoutCount++;
- tiRcdCount++;
- OptionReset();
- DBG_PL(Reset, "spSctpTrace=%p"), spSctpTrace DBG_PR;
- DBG_X(Reset);
- }
- /* This function serves as a hook for optional extensions to reset additional
- * state variables.
- */
- void SctpAgent::OptionReset()
- {
- return;
- }
- /* This function returns a size based on how much space is needed for
- * bundled chunks. Real implementations, such as the KAME stack, reserve
- * space for ECN, etc. We are using it for experimental extensions such as
- * the Timestamp chunk (sctp-timestamp.cc). Such a function avoids having
- * to maintain nearly identical copies of the Reset() function for each
- * extension. For the base sctp, we don't reserve any space... so return 0.
- */
- u_int SctpAgent::ControlChunkReservation()
- {
- DBG_I(ControlChunkReservation);
- DBG_PL(ControlChunkReservation, "returning 0") DBG_PR;
- DBG_X(ControlChunkReservation);
- return 0;
- }
- int SctpAgent::command(int argc, const char*const* argv)
- {
- DBG_I(command);// internal check is done to avoid printing if file is unopen!
- double dCurrTime = Scheduler::instance().clock();
- DBG_PL(command, "<time:%f> argc=%d argv[1]=%s"),
- dCurrTime, argc, argv[1] DBG_PR;
- Tcl& oTcl = Tcl::instance();
- Node *opNode = NULL;
- int iNsAddr;
- int iNsPort;
- NsObject *opTarget = NULL;
- NsObject *opLink = NULL;
- int iRetVal;
- if(argc == 2)
- {
- if (strcmp(argv[1], "reset") == 0)
- {
- Reset();
- DBG_X(command);
- return (TCL_OK);
- }
- else if (strcmp(argv[1], "close") == 0)
- {
- Close();
- DBG_X(command);
- return (TCL_OK);
- }
- }
- else if(argc == 3)
- {
- if (strcmp(argv[1], "advance") == 0)
- {
- DBG_X(command);
- return (TCL_OK);
- }
- else if (strcmp(argv[1], "set-multihome-core") == 0)
- {
- opCoreTarget = (Classifier *) TclObject::lookup(argv[2]);
- if(opCoreTarget == NULL)
- {
- oTcl.resultf("no such object %s", argv[4]);
- return (TCL_ERROR);
- }
- DBG_X(command);
- return (TCL_OK);
- }
- else if (strcmp(argv[1], "set-primary-destination") == 0)
- {
- opNode = (Node *) TclObject::lookup(argv[2]);
- if(opNode == NULL)
- {
- oTcl.resultf("no such object %s", argv[2]);
- return (TCL_ERROR);
- }
- iRetVal = SetPrimary( opNode->address() );
- if(iRetVal == TCL_ERROR)
- {
- fprintf(stderr, "[SctpAgent::command] ERROR:"
- "%s is not a valid destinationn", argv[2]);
- DBG_X(command);
- return (TCL_ERROR);
- }
- DBG_X(command);
- return (TCL_OK);
- }
- else if (strcmp(argv[1], "force-source") == 0)
- {
- opNode = (Node *) TclObject::lookup(argv[2]);
- if(opNode == NULL)
- {
- oTcl.resultf("no such object %s", argv[2]);
- return (TCL_ERROR);
- }
- iRetVal = ForceSource( opNode->address() );
- if(iRetVal == TCL_ERROR)
- {
- fprintf(stderr, "[SctpAgent::command] ERROR:"
- "%s is not a valid sourcen", argv[2]);
- DBG_X(command);
- return (TCL_ERROR);
- }
- DBG_X(command);
- return (TCL_OK);
- }
- else if (strcmp(argv[1], "print") == 0)
- {
- if(eTraceAll == TRUE)
- TraceAll();
- else
- TraceVar(argv[2]);
- DBG_X(command);
- return (TCL_OK);
- }
- }
- else if(argc == 4)
- {
- if (strcmp(argv[1], "add-multihome-destination") == 0)
- {
- iNsAddr = atoi(argv[2]);
- iNsPort = atoi(argv[3]);
- AddDestination(iNsAddr, iNsPort);
- DBG_X(command);
- return (TCL_OK);
- }
- else if (strcmp(argv[1], "set-destination-lossrate") == 0)
- {
- opNode = (Node *) TclObject::lookup(argv[2]);
- if(opNode == NULL)
- {
- oTcl.resultf("no such object %s", argv[2]);
- return (TCL_ERROR);
- }
- iRetVal = SetLossrate( opNode->address(), atof(argv[3]) );
- if(iRetVal == TCL_ERROR)
- {
- fprintf(stderr, "[SctpAgent::command] ERROR:"
- "%s is not a valid destinationn", argv[2]);
- DBG_X(command);
- return (TCL_ERROR);
- }
- DBG_X(command);
- return (TCL_OK);
- }
- }
- else if(argc == 6)
- {
- if (strcmp(argv[1], "add-multihome-interface") == 0)
- {
- iNsAddr = atoi(argv[2]);
- iNsPort = atoi(argv[3]);
- opTarget = (NsObject *) TclObject::lookup(argv[4]);
- if(opTarget == NULL)
- {
- oTcl.resultf("no such object %s", argv[4]);
- return (TCL_ERROR);
- }
- opLink = (NsObject *) TclObject::lookup(argv[5]);
- if(opLink == NULL)
- {
- oTcl.resultf("no such object %s", argv[5]);
- return (TCL_ERROR);
- }
- AddInterface(iNsAddr, iNsPort, opTarget, opLink);
- DBG_X(command);
- return (TCL_OK);
- }
- }
- DBG_X(command);
- return (Agent::command(argc, argv));
- }
- /* Given params: sList, spPrevNode, spNextNode, spNewNode... insert spNewNode
- * into sList between spPrevNode and spNextNode.
- */
- void SctpAgent::InsertNode(List_S *spList, Node_S *spPrevNode,
- Node_S *spNewNode, Node_S *spNextNode)
- {
- if(spPrevNode == NULL)
- spList->spHead = spNewNode;
- else
- spPrevNode->spNext = spNewNode;
- spNewNode->spPrev = spPrevNode;
- spNewNode->spNext = spNextNode;
- if(spNextNode == NULL)
- spList->spTail = spNewNode;
- else
- spNextNode->spPrev = spNewNode;
- spList->uiLength++;
- }
- void SctpAgent::DeleteNode(List_S *spList, Node_S *spNode)
- {
- if(spNode->spPrev == NULL)
- spList->spHead = spNode->spNext;
- else
- spNode->spPrev->spNext = spNode->spNext;
-
- if(spNode->spNext == NULL)
- spList->spTail = spNode->spPrev;
- else
- spNode->spNext->spPrev = spNode->spPrev;
- /* now let's free the internal structures
- */
- switch(spNode->eType)
- {
- case NODE_TYPE_STREAM_BUFFER:
- delete[] (u_char *) ((SctpStreamBufferNode_S *) spNode->vpData)->spChunk;
- ((SctpStreamBufferNode_S *) spNode->vpData)->spChunk = NULL;
- delete (SctpStreamBufferNode_S *) spNode->vpData;
- spNode->vpData = NULL;
- break;
- case NODE_TYPE_RECV_TSN_BLOCK:
- delete (SctpRecvTsnBlock_S *) spNode->vpData;
- spNode->vpData = NULL;
- break;
- case NODE_TYPE_DUP_TSN:
- delete (SctpDupTsn_S *) spNode->vpData;
- spNode->vpData = NULL;
- break;
- case NODE_TYPE_SEND_BUFFER:
- delete[] (u_char *) ((SctpSendBufferNode_S *) spNode->vpData)->spChunk;
- ((SctpSendBufferNode_S *) spNode->vpData)->spChunk = NULL;
- delete (SctpSendBufferNode_S *) spNode->vpData;
- spNode->vpData = NULL;
- break;
- case NODE_TYPE_APP_LAYER_BUFFER:
- delete (AppData_S *) spNode->vpData;
- spNode->vpData = NULL;
- break;
- case NODE_TYPE_INTERFACE_LIST:
- delete (SctpInterface_S *) spNode->vpData;
- spNode->vpData = NULL;
- break;
- case NODE_TYPE_DESTINATION_LIST:
- delete (SctpDest_S *) spNode->vpData;
- spNode->vpData = NULL;
- break;
- case NODE_TYPE_PACKET_BUFFER:
- /* no need to free data, because SctpAgent hands over responsibility to
- * the send() function
- */
- spNode->vpData = NULL;
- break;
- }
- delete spNode;
- spList->uiLength--;
- }
- void SctpAgent::ClearList(List_S *spList)
- {
- Node_S *spCurrNode = spList->spHead;
- Node_S *spDeleteNode = NULL;
- while(spCurrNode != NULL)
- {
- spDeleteNode = spCurrNode;
- spCurrNode = spCurrNode->spNext;
- DeleteNode(spList, spDeleteNode);
- }
- }
- void SctpAgent::AddInterface(int iNsAddr, int iNsPort,
- NsObject *opTarget, NsObject *opLink)
- {
- DBG_I(AddInterface);
- Node_S *spNewNode = new Node_S;
- spNewNode->eType = NODE_TYPE_INTERFACE_LIST;
- spNewNode->vpData = new SctpInterface_S;
- SctpInterface_S *spNewInterface = (SctpInterface_S *) spNewNode->vpData;
- spNewInterface->iNsAddr = iNsAddr;
- spNewInterface->iNsPort = iNsPort;
- spNewInterface->opTarget = opTarget;
- spNewInterface->opLink = opLink;
-
- InsertNode(&sInterfaceList, sInterfaceList.spTail, spNewNode, NULL);
- DBG_X(AddInterface);
- }
- void SctpAgent::AddDestination(int iNsAddr, int iNsPort)
- {
- DBG_I(AddDestination);
- Node_S *spNewNode = new Node_S;
- spNewNode->eType = NODE_TYPE_DESTINATION_LIST;
- spNewNode->vpData = new SctpDest_S;
- SctpDest_S *spNewDest = (SctpDest_S *) spNewNode->vpData;
- memset(spNewDest, 0, sizeof(SctpDest_S));
- spNewDest->iNsAddr = iNsAddr;
- spNewDest->iNsPort = iNsPort;
- /* set the primary to the last destination added just in case the user does
- * not set a primary
- */
- spPrimaryDest = spNewDest;
- spNewTxDest = spPrimaryDest;
- /* allocate packet with the dest addr. to be used later for setting src dest
- */
- daddr() = spNewDest->iNsAddr;
- dport() = spNewDest->iNsPort;
- spNewDest->opRoutingAssistPacket = allocpkt();
- InsertNode(&sDestList, sDestList.spTail, spNewNode, NULL);
- DBG_X(AddDestination);
- }
- int SctpAgent::SetPrimary(int iNsAddr)
- {
- DBG_I(SetPrimary);
- Node_S *spCurrNode = NULL;
- SctpDest_S *spCurrDest = NULL;
- for(spCurrNode = sDestList.spHead;
- spCurrNode != NULL;
- spCurrNode = spCurrNode->spNext)
- {
- spCurrDest = (SctpDest_S *) spCurrNode->vpData;
-
- if(spCurrDest->iNsAddr == iNsAddr)
- {
- spPrimaryDest = spCurrDest;
- spNewTxDest = spPrimaryDest;
- DBG_PL(SetPrimary, "returning TCL_OK") DBG_PR;
- DBG_X(SetPrimary);
- return (TCL_OK);
- }
- }
- DBG_PL(SetPrimary, "returning TCL_ERROR") DBG_PR;
- DBG_X(SetPrimary);
- return (TCL_ERROR);
- }
- /* Helps maintain information passed down from the TCL script by an oracle
- * about path lossrates. Used in RTX-LOSSRATE rtx policy for
- * CMT. Lossrate for a path is set in the dest node's datastructure.
- */
- int SctpAgent::SetLossrate(int iNsAddr, float fLossrate)
- {
- Node_S *spCurrNode = NULL;
- SctpDest_S *spCurrDest = NULL;
- for(spCurrNode = sDestList.spHead;
- spCurrNode != NULL;
- spCurrNode = spCurrNode->spNext)
- {
- spCurrDest = (SctpDest_S *) spCurrNode->vpData;
- if(spCurrDest->iNsAddr == iNsAddr)
- {
- spCurrDest->fLossrate = fLossrate;
- return (TCL_OK);
- }
- }
- return (TCL_ERROR);
- }
- int SctpAgent::ForceSource(int iNsAddr)
- {
- DBG_I(ForceSource);
- Node_S *spCurrNode = sInterfaceList.spHead;
- SctpInterface_S *spCurrInterface = NULL;
- while(spCurrNode != NULL)
- {
- spCurrInterface = (SctpInterface_S *) spCurrNode->vpData;
- if(spCurrInterface->iNsAddr == iNsAddr)
- {
- addr() = spCurrInterface->iNsAddr;
- port() = spCurrInterface->iNsPort;
- target_ = spCurrInterface->opTarget;
- eForceSource = TRUE;
- DBG_PL(ForceSource, "returning TCL_OK") DBG_PR;
- DBG_X(ForceSource);
- return (TCL_OK);
- }
- else
- spCurrNode = spCurrNode->spNext;
- }
- DBG_PL(ForceSource, "returning TCL_ERROR") DBG_PR;
- DBG_X(ForceSource);
- return (TCL_ERROR);
- }
- /* returns the size of the chunk
- */
- int SctpAgent::GenChunk(SctpChunkType_E eType, u_char *ucpChunk)
- {
- DBG_I(GenChunk);
- double dCurrTime = Scheduler::instance().clock();
- AppData_S *spAppMessage = NULL;
- int iSize = 0;
- DBG_PL(GenChunk, "spSctpTrace=%p"), spSctpTrace DBG_PR;
- switch(eType)
- {
- case SCTP_CHUNK_INIT:
- iSize = sizeof(SctpInitChunk_S);
- ((SctpInitChunk_S *) ucpChunk)->sHdr.ucType = eType;
- ((SctpInitChunk_S *) ucpChunk)->uiArwnd = uiInitialRwnd;
- ((SctpInitChunk_S *) ucpChunk)->usNumOutboundStreams = uiNumOutStreams;
- ((SctpInitChunk_S *) ucpChunk)->uiInitialTsn = 0;
- ((SctpInitChunk_S *) ucpChunk)->sUnrelStream.usType
- = SCTP_INIT_PARAM_UNREL;
- ((SctpInitChunk_S *) ucpChunk)->sUnrelStream.usLength
- = sizeof(SctpUnrelStreamsParam_S);
-
- if(uiNumUnrelStreams > 0)
- {
- ((SctpInitChunk_S *) ucpChunk)->sUnrelStream.usLength
- += sizeof(SctpUnrelStreamPair_S);
- SctpUnrelStreamPair_S *spUnrelStreamPair
- = (SctpUnrelStreamPair_S *) (ucpChunk+iSize);
- spUnrelStreamPair->usStart = 0;
- spUnrelStreamPair->usEnd = uiNumUnrelStreams - 1;
- iSize += sizeof(SctpUnrelStreamPair_S);
- }
- ((SctpInitChunk_S *) ucpChunk)->sHdr.usLength = iSize;
- /* fill in tracing fields too
- */
- spSctpTrace[uiNumChunks].eType = eType;
- spSctpTrace[uiNumChunks].uiTsn = (u_int) -1;
- spSctpTrace[uiNumChunks].usStreamId = (u_short) -1;
- spSctpTrace[uiNumChunks].usStreamSeqNum = (u_short) -1;
- break;
- case SCTP_CHUNK_INIT_ACK:
- iSize = sizeof(SctpInitAckChunk_S);
- ((SctpInitAckChunk_S *) ucpChunk)->sHdr.ucType = eType;
- ((SctpInitAckChunk_S *) ucpChunk)->uiArwnd = uiInitialRwnd;
- ((SctpInitChunk_S *) ucpChunk)->usNumOutboundStreams = uiNumOutStreams;
- ((SctpInitAckChunk_S *) ucpChunk)->uiInitialTsn = 0;
- ((SctpInitAckChunk_S *) ucpChunk)->sUnrelStream.usType
- = SCTP_INIT_PARAM_UNREL;
- ((SctpInitAckChunk_S *) ucpChunk)->sUnrelStream.usLength
- = sizeof(SctpUnrelStreamsParam_S);
- if(uiNumUnrelStreams > 0)
- {
- ((SctpInitAckChunk_S *) ucpChunk)->sUnrelStream.usLength
- += sizeof(SctpUnrelStreamPair_S);
- SctpUnrelStreamPair_S *spUnrelStreamPair
- = (SctpUnrelStreamPair_S *) (ucpChunk+iSize);
- spUnrelStreamPair->usStart = 0;
- spUnrelStreamPair->usEnd = uiNumUnrelStreams - 1;
- iSize += sizeof(SctpUnrelStreamPair_S);
- }
- ((SctpInitAckChunk_S *) ucpChunk)->sHdr.usLength = iSize;
- /* fill in tracing fields too
- */
- spSctpTrace[uiNumChunks].eType = eType;
- spSctpTrace[uiNumChunks].uiTsn = (u_int) -1;
- spSctpTrace[uiNumChunks].usStreamId = (u_short) -1;
- spSctpTrace[uiNumChunks].usStreamSeqNum = (u_short) -1;
- break;
- case SCTP_CHUNK_COOKIE_ECHO:
- iSize = sizeof(SctpCookieEchoChunk_S);
- ((SctpCookieEchoChunk_S *) ucpChunk)->sHdr.ucType = eType;
- ((SctpCookieEchoChunk_S *) ucpChunk)->sHdr.usLength = iSize;
- /* fill in tracing fields too
- */
- spSctpTrace[uiNumChunks].eType = eType;
- spSctpTrace[uiNumChunks].uiTsn = (u_int) -1;
- spSctpTrace[uiNumChunks].usStreamId = (u_short) -1;
- spSctpTrace[uiNumChunks].usStreamSeqNum = (u_short) -1;
- break;
-
- case SCTP_CHUNK_COOKIE_ACK:
- iSize = sizeof(SctpCookieAckChunk_S);
- ((SctpCookieAckChunk_S *) ucpChunk)->sHdr.ucType = eType;
- ((SctpCookieAckChunk_S *) ucpChunk)->sHdr.usLength = iSize;
- /* fill in tracing fields too
- */
- spSctpTrace[uiNumChunks].eType = eType;
- spSctpTrace[uiNumChunks].uiTsn = (u_int) -1;
- spSctpTrace[uiNumChunks].usStreamId = (u_short) -1;
- spSctpTrace[uiNumChunks].usStreamSeqNum = (u_short) -1;
- break;
- case SCTP_CHUNK_DATA:
- /* Depending on eDataSource, we either use chunk parameters
- * from the app layer buffer, or use the defaults for infinite
- * data generation.
- */
- if(eDataSource == DATA_SOURCE_APPLICATION)
- {
- /* If DATA_SOURCE_APPLICATION, we have to get chunk parameters
- * from the app layer buffer
- */
- DBG_PL (GenChunk, "eDataSource=DATA_SOURCE_APPLICATION") DBG_PR;
- spAppMessage = (AppData_S *) (sAppLayerBuffer.spHead->vpData);
- DBG_PL (GenChunk, "App Message ---------------->") DBG_PR;
- DBG_PL (GenChunk, "usNumStreams: %d"),
- spAppMessage->usNumStreams DBG_PR;
- DBG_PL (GenChunk, "usNumUnreliable: %d"),
- spAppMessage->usNumUnreliable DBG_PR;
- DBG_PL (GenChunk, "uiNumBytes: %d"),
- spAppMessage->uiNumBytes DBG_PR;
- DBG_PL (GenChunk, "usStreamId: %d"),
- spAppMessage->usStreamId DBG_PR;
- DBG_PL (GenChunk, "eUnordered: %s"),
- spAppMessage->eUnordered ? "TRUE" : "FALSE" DBG_PR;
- DBG_PL (GenChunk, "usReliability: %d"),
- spAppMessage->usReliability DBG_PR;
- DBG_PL (GenChunk, "App Message <----------------") DBG_PR;
- iSize = spAppMessage->uiNumBytes + sizeof(SctpDataChunkHdr_S);
- ((SctpDataChunkHdr_S *) ucpChunk)->sHdr.ucType = SCTP_CHUNK_DATA;
- ((SctpDataChunkHdr_S *) ucpChunk)->sHdr.usLength = iSize;
- /* DATA chunks must be padded to a 4 byte boundary (section 3.3.1)
- * ...but the adjustment should happen after usLength field is set,
- * because the field should represent the size before padding.
- */
- if( (iSize % 4) != 0)
- iSize += 4 - (iSize % 4);
- ((SctpDataChunkHdr_S *) ucpChunk)->uiTsn = ++uiNextTsn;
-
- ((SctpDataChunkHdr_S *) ucpChunk)->usStreamId
- = spAppMessage->usStreamId;
- if(spAppMessage->eUnordered == TRUE)
- {
- ((SctpDataChunkHdr_S *) ucpChunk)->sHdr.ucFlags
- |= SCTP_DATA_FLAG_UNORDERED;
- /* no stream seq num on unordered chunks!
- */
- }
- else
- {
- ((SctpDataChunkHdr_S *) ucpChunk)->sHdr.ucFlags = 0;
- ((SctpDataChunkHdr_S *) ucpChunk)->usStreamSeqNum
- = spOutStreams[spAppMessage->usStreamId].usNextStreamSeqNum++;
- }
- }
- else
- {
- DBG_PL (GenChunk, "eDataSource=DATA_SOURCE_INFINITE") DBG_PR;
- iSize = uiDataChunkSize;
- ((SctpDataChunkHdr_S *) ucpChunk)->sHdr.ucType = SCTP_CHUNK_DATA;
- if(eUnordered == TRUE)
- {
- ((SctpDataChunkHdr_S *) ucpChunk)->sHdr.ucFlags
- |= SCTP_DATA_FLAG_UNORDERED;
- }
- else
- {
- ((SctpDataChunkHdr_S *) ucpChunk)->sHdr.ucFlags = 0;
- }
- ((SctpDataChunkHdr_S *) ucpChunk)->sHdr.usLength = iSize;
- /* DATA chunks must be padded to a 4 byte boundary (section 3.3.1)
- * ...but the adjustment should happen after usLength field is set,
- * because the field should represent the size before padding.
- */
- if( (uiDataChunkSize % 4) != 0)
- iSize += 4 - (uiDataChunkSize % 4);
- ((SctpDataChunkHdr_S *) ucpChunk)->uiTsn = ++uiNextTsn;
-
- ((SctpDataChunkHdr_S *) ucpChunk)->usStreamId
- = (usNextStreamId % uiNumOutStreams);
- if(eUnordered == TRUE)
- {
- ((SctpDataChunkHdr_S *) ucpChunk)->sHdr.ucFlags
- |= SCTP_DATA_FLAG_UNORDERED;
- /* no stream seq num on unordered chunks!
- */
- }
- else
- {
- ((SctpDataChunkHdr_S *) ucpChunk)->sHdr.ucFlags = 0;
- ((SctpDataChunkHdr_S *) ucpChunk)->usStreamSeqNum
- = spOutStreams[usNextStreamId%uiNumOutStreams].usNextStreamSeqNum++;
- }
-
- usNextStreamId++; // round-robin stream feeding
- }
- /* fill in tracing fields too
- */
- spSctpTrace[uiNumChunks].eType = eType;
- spSctpTrace[uiNumChunks].uiTsn
- = ((SctpDataChunkHdr_S *) ucpChunk)->uiTsn;
- spSctpTrace[uiNumChunks].usStreamId
- = ((SctpDataChunkHdr_S *) ucpChunk)->usStreamId;
- spSctpTrace[uiNumChunks].usStreamSeqNum
- = ((SctpDataChunkHdr_S *) ucpChunk)->usStreamSeqNum;
- DBG_PL(GenChunk, "DataTsn=%d at dCurrTime=%f"),
- uiNextTsn, dCurrTime DBG_PR;
- break;
- case SCTP_CHUNK_SACK:
- iSize = sizeof(SctpSackChunk_S);
- ((SctpSackChunk_S *) ucpChunk)->sHdr.ucType = eType;
- ((SctpSackChunk_S *) ucpChunk)->uiCumAck = uiCumAck;
- ((SctpSackChunk_S *) ucpChunk)->uiArwnd = uiMyRwnd;
- ((SctpSackChunk_S *) ucpChunk)->usNumGapAckBlocks
- = sRecvTsnBlockList.uiLength;
- ((SctpSackChunk_S *) ucpChunk)->usNumDupTsns = sDupTsnList.uiLength;
- DBG_PL(GenChunk, "SACK CumAck=%d arwnd=%d"), uiCumAck, uiMyRwnd DBG_PR;
- /* Append all the Gap Ack Blocks
- */
- for(Node_S *spCurrFrag = sRecvTsnBlockList.spHead;
- (spCurrFrag != NULL) &&
- (iSize + sizeof(SctpGapAckBlock_S) < uiMaxDataSize);
- spCurrFrag = spCurrFrag->spNext, iSize += sizeof(SctpGapAckBlock_S) )
- {
- SctpGapAckBlock_S *spGapAckBlock
- = (SctpGapAckBlock_S *) (ucpChunk+iSize);
- spGapAckBlock->usStartOffset
- = ((SctpRecvTsnBlock_S *)spCurrFrag->vpData)->uiStartTsn -uiCumAck;
- spGapAckBlock->usEndOffset
- = ((SctpRecvTsnBlock_S *)spCurrFrag->vpData)->uiEndTsn - uiCumAck;
- DBG_PL(GenChunk, "GapAckBlock StartOffset=%d EndOffset=%d"),
- spGapAckBlock->usStartOffset, spGapAckBlock->usEndOffset DBG_PR;
- }
- /* Append all the Duplicate TSNs
- */
- for(Node_S *spPrevDup = NULL, *spCurrDup = sDupTsnList.spHead;
- (spCurrDup != NULL) &&
- (iSize + sizeof(SctpDupTsn_S) < uiMaxDataSize);
- spPrevDup = spCurrDup, spCurrDup = spCurrDup->spNext,
- DeleteNode(&sDupTsnList, spPrevDup), iSize += sizeof(SctpDupTsn_S))
- {
- SctpDupTsn_S *spDupTsn = (SctpDupTsn_S *) (ucpChunk+iSize);
- spDupTsn->uiTsn = ((SctpDupTsn_S *) spCurrDup->vpData)->uiTsn;
- DBG_PL(GenChunk, "DupTsn=%d"), spDupTsn->uiTsn DBG_PR;
- }
- /* After all the dynamic appending, we can NOW fill in the chunk size!
- */
- ((SctpSackChunk_S *) ucpChunk)->sHdr.usLength = iSize;
- /* fill in tracing fields too
- */
- spSctpTrace[uiNumChunks].eType = eType;
- spSctpTrace[uiNumChunks].uiTsn = ((SctpSackChunk_S *)ucpChunk)->uiCumAck;
- spSctpTrace[uiNumChunks].usStreamId = (u_short) -1;
- spSctpTrace[uiNumChunks].usStreamSeqNum = (u_short) -1;
- break;
- case SCTP_CHUNK_FORWARD_TSN:
- iSize = SCTP_CHUNK_FORWARD_TSN_LENGTH;
- ((SctpForwardTsnChunk_S *) ucpChunk)->sHdr.ucType = eType;
- ((SctpForwardTsnChunk_S *) ucpChunk)->sHdr.ucFlags = 0;// flags not used?
- ((SctpForwardTsnChunk_S *) ucpChunk)->sHdr.usLength = iSize;
- ((SctpForwardTsnChunk_S *) ucpChunk)->uiNewCum = uiAdvancedPeerAckPoint;
- /* fill in tracing fields too
- */
- spSctpTrace[uiNumChunks].eType = eType;
- spSctpTrace[uiNumChunks].uiTsn = uiAdvancedPeerAckPoint;
- spSctpTrace[uiNumChunks].usStreamId = (u_short) -1;
- spSctpTrace[uiNumChunks].usStreamSeqNum = (u_short) -1;
- break;
- case SCTP_CHUNK_HB:
- case SCTP_CHUNK_HB_ACK:
- iSize = SCTP_CHUNK_HEARTBEAT_LENGTH;
- ((SctpHeartbeatChunk_S *) ucpChunk)->sHdr.ucType = eType;
- ((SctpHeartbeatChunk_S *) ucpChunk)->sHdr.ucFlags = 0;
- ((SctpHeartbeatChunk_S *) ucpChunk)->sHdr.usLength = iSize;
- ((SctpHeartbeatChunk_S *) ucpChunk)->usInfoType = 1;
- ((SctpHeartbeatChunk_S *) ucpChunk)->usInfoLength
- = iSize - sizeof(SctpChunkHdr_S);
- ((SctpHeartbeatChunk_S *) ucpChunk)->dTimestamp = dCurrTime;
- ((SctpHeartbeatChunk_S *) ucpChunk)->spDest = NULL; // caller sets dest
- /* fill in tracing fields too
- */
- spSctpTrace[uiNumChunks].eType = eType;
- spSctpTrace[uiNumChunks].uiTsn = (u_int) -1;
- spSctpTrace[uiNumChunks].usStreamId = (u_short) -1;
- spSctpTrace[uiNumChunks].usStreamSeqNum = (u_short) -1;
- break;
- case SCTP_CHUNK_SHUTDOWN:
- break;
- case SCTP_CHUNK_SHUTDOWN_ACK:
- break;
- case SCTP_CHUNK_SHUTDOWN_COMPLETE:
- break;
- default:
- fprintf(stderr, "[GenChunk] ERROR: bad chunk type!n");
- DBG_PL(GenChunk, "ERROR: bad chunk type!") DBG_PR;
- DBG_PL(GenChunk, "exiting...") DBG_PR;
- exit(-1);
- }
- uiNumChunks++;
- DBG_X(GenChunk);
- return iSize;
- }
- u_int SctpAgent::GetNextDataChunkSize()
- {
- DBG_I(GetNextDataChunkSize);
- AppData_S *spAppMessage = NULL;
- u_int uiChunkSize = 0;
- if(eDataSource == DATA_SOURCE_APPLICATION)
- {
- if(sAppLayerBuffer.uiLength == 0)
- {
- uiChunkSize = 0;
- }
- else
- {
- spAppMessage = (AppData_S *) (sAppLayerBuffer.spHead->vpData);
- uiChunkSize = spAppMessage->uiNumBytes + sizeof(SctpDataChunkHdr_S);
- }
- }
- else
- {
- uiChunkSize = uiDataChunkSize;
- }
- /* DATA chunks must be padded to a 4 byte boundary (section 3.3.1)
- */
- if( (uiChunkSize % 4) != 0)
- uiChunkSize += 4 - (uiChunkSize % 4);
- DBG_PL(GetNextDataChunkSize, "returning %d"), uiChunkSize DBG_PR;
- DBG_X(GetNextDataChunkSize);
- return uiChunkSize;
- }
- /* This function generates ONE data chunk.Since we could call GenChunk directly
- * with only one extra parameter, it may seem pointless to have this function.
- * However, it isn't! All variable adjustments that go with generating a new
- * data chunk should be isolated in one place to avoid bugs.
- */
- int SctpAgent::GenOneDataChunk(u_char *ucpOutData)
- {
- DBG_I(GenOneDataChunk);
- AppData_S *spAppData = NULL;
- int iChunkSize = GenChunk(SCTP_CHUNK_DATA, ucpOutData);
- if(eDataSource == DATA_SOURCE_APPLICATION)
- {
- spAppData = (AppData_S *) sAppLayerBuffer.spHead->vpData;
- AddToSendBuffer( (SctpDataChunkHdr_S *) ucpOutData, iChunkSize,
- spAppData->usReliability, spNewTxDest);
- DeleteNode(&sAppLayerBuffer, sAppLayerBuffer.spHead);
- }
- else
- {
- AddToSendBuffer( (SctpDataChunkHdr_S *) ucpOutData, iChunkSize,
- uiReliability, spNewTxDest);
- }
-
- DBG_PL(GenOneDataChunk, "dest=%p chunk length=%d"),
- spNewTxDest, ((SctpDataChunkHdr_S *) ucpOutData)->sHdr.usLength DBG_PR;
- spNewTxDest->iOutstandingBytes
- += ((SctpDataChunkHdr_S *) ucpOutData)->sHdr.usLength;
- DBG_PL(GenOneDataChunk,"After adding chunk, out=%d"),
- spNewTxDest->iOutstandingBytes DBG_PR;
- /* rfc2960 section 6.2.1.B
- */
- if(iChunkSize <= (int) uiPeerRwnd)
- uiPeerRwnd -= iChunkSize;
- else
- uiPeerRwnd = 0;
- if(spNewTxDest->eRtxTimerIsRunning == FALSE)
- StartT3RtxTimer(spNewTxDest); //section 6.3.2.R1
- DBG_X(GenOneDataChunk);
- return iChunkSize;
- }
- /* This function fills a packet with as many chunks that can fit into the PMTU
- * and the peer's rwnd.
- *
- * Since, this function is called the first time a chunk(s) is sent, all chunks
- * generated here are added to the sSendBuffer.
- *
- * returns the resulting size of the packet
- */
- int SctpAgent::GenMultipleDataChunks(u_char *ucpOutData, int iTotalOutDataSize)
- {
- DBG_I(GenMultipleDataChunks);
-
- int iChunkSize = 0;
- int iStartingTotalSize = iTotalOutDataSize;
- while((iTotalOutDataSize + GetNextDataChunkSize() <= uiMaxDataSize) &&
- (GetNextDataChunkSize() <= uiPeerRwnd) &&
- (eDataSource == DATA_SOURCE_INFINITE || sAppLayerBuffer.uiLength != 0))
- {
- iChunkSize = GenOneDataChunk(ucpOutData);
- ucpOutData += iChunkSize;
- iTotalOutDataSize += iChunkSize;
- }
-
- DBG_X(GenMultipleDataChunks);
- return iTotalOutDataSize - iStartingTotalSize;
- }
- /* This function serves as a hook for extensions (such as Timestamp) which
- * need to bundle extra control chunks. However, it can later be used to
- * actually bundle SACKs, FOWARD_TSN, DATA w/ COOKIE and
- * COOKIE_ECHO. Since we don't currently bundle any control chunks in the
- * base SCTP, this function just returns 0 (ie, no bundled chunks).
- *
- * returns the aggregate size of the control chunk(s)
- */
- int SctpAgent::BundleControlChunks(u_char *ucpOutData)
- {
- DBG_I(BundleControlChunks);
- DBG_PL(BundleControlChunks, "None... returning 0") DBG_PR;
- DBG_X(BundleControlChunks);
- return 0;
- }
- void SctpAgent::StartT3RtxTimer(SctpDest_S *spDest)
- {
- DBG_I(StartT3RtxTimer);
- double dCurrTime = Scheduler::instance().clock();
- DBG_PL(StartT3RtxTimer, "spDest=%p dCurrTime=%f expires at %f "),
- spDest, dCurrTime, dCurrTime+spDest->dRto DBG_PR;
- spDest->opT3RtxTimer->resched(spDest->dRto);
- spDest->eRtxTimerIsRunning = TRUE;
- DBG_X(StartT3RtxTimer);
- }
- void SctpAgent::StopT3RtxTimer(SctpDest_S *spDest)
- {
- DBG_I(StopT3RtxTimer);
- double dCurrTime = Scheduler::instance().clock();
- DBG_PL(StopT3RtxTimer, "spDest=%p dCurrTime=%f"), spDest, dCurrTime DBG_PR;
- spDest->opT3RtxTimer->force_cancel();
- spDest->eRtxTimerIsRunning = FALSE;
- DBG_X(StopT3RtxTimer);
- }
- void SctpAgent::AddToSendBuffer(SctpDataChunkHdr_S *spChunk,
- int iChunkSize,
- u_int uiReliability,
- SctpDest_S *spDest)
- {
- DBG_I(AddToSendBuffer);
- DBG_PL(AddToSendBuffer, "spDest=%p iChunkSize=%d"),
- spDest, iChunkSize DBG_PR;
- Node_S *spNewNode = new Node_S;
- spNewNode->eType = NODE_TYPE_SEND_BUFFER;
- spNewNode->vpData = new SctpSendBufferNode_S;
- SctpSendBufferNode_S * spNewNodeData
- = (SctpSendBufferNode_S *) spNewNode->vpData;
- /* This can NOT simply be a 'new SctpDataChunkHdr_S', because we need to
- * allocate the space for the ENTIRE data chunk and not just the data
- * chunk header.
- */
- spNewNodeData->spChunk = (SctpDataChunkHdr_S *) new u_char[iChunkSize];
- memcpy(spNewNodeData->spChunk, spChunk, iChunkSize);
- spNewNodeData->eAdvancedAcked = FALSE;
- spNewNodeData->eGapAcked = FALSE;
- spNewNodeData->eAddedToPartialBytesAcked = FALSE;
- spNewNodeData->iNumMissingReports = 0;
- spNewNodeData->iUnrelRtxLimit = uiReliability;
- spNewNodeData->eMarkedForRtx = NO_RTX;
- spNewNodeData->eIneligibleForFastRtx = FALSE;
- spNewNodeData->iNumTxs = 1;
- spNewNodeData->spDest = spDest;
- /* Is there already a DATA chunk in flight measuring an RTT?
- * (6.3.1.C4 RTT measured once per round trip)
- */
- if(spDest->eRtoPending == FALSE) // NO?
- {
- spNewNodeData->dTxTimestamp = Scheduler::instance().clock();
- spDest->eRtoPending = TRUE; // ...well now there is :-)
- }
- else
- spNewNodeData->dTxTimestamp = 0; // don't use this check for RTT estimate
- InsertNode(&sSendBuffer, sSendBuffer.spTail, spNewNode, NULL);
- DBG_X(AddToSendBuffer);
- }
- void SctpAgent::RttUpdate(double dTxTime, SctpDest_S *spDest)
- {
- DBG_I(RttUpdate);
- double dNewRtt;
- double dCurrTime = Scheduler::instance().clock();
- dNewRtt = dCurrTime - dTxTime;
-
- if(spDest->eFirstRttMeasurement == TRUE) // section 6.3.1.C2
- {
- /* Bug Fix. eFirstRttMeasurement should be set to FALSE here.
- * 06/11/2001 - PDA and JRI
- */
- spDest->eFirstRttMeasurement = FALSE;
- spDest->dSrtt = dNewRtt;
- spDest->dRttVar = dNewRtt/2;
- spDest->dRto = spDest->dSrtt + 4 * spDest->dRttVar;
- }
- else //section 6.3.1.C3
- {
- spDest->dRttVar
- = ( (1 - RTO_BETA) * spDest->dRttVar
- + RTO_BETA * abs(spDest->dSrtt - dNewRtt) );
- spDest->dSrtt
- = (1 - RTO_ALPHA) * spDest->dSrtt + RTO_ALPHA * dNewRtt;
-
- spDest->dRto = spDest->dSrtt + 4 * spDest->dRttVar;
- }
- if(spDest->dRto < dMinRto) // section 6.3.1.C6
- spDest->dRto = dMinRto;
- else if(spDest->dRto > dMaxRto) // section 6.3.1.C7
- spDest->dRto = dMaxRto;
- tdRto++; // trigger changes for trace to pick up
- DBG_PL(RttUpdate, "spDest->dRto=%f"), spDest->dRto DBG_PR;
- DBG_X(RttUpdate);
- }
- /* Go thru the send buffer deleting all chunks which have a tsn <= the
- * tsn parameter passed in. We assume the chunks in the rtx list are ordered by
- * their tsn value. In addtion, for each chunk deleted:
- * 1. we add the chunk length to # newly acked bytes and partial bytes acked
- * 2. we update round trip time if appropriate
- * 3. stop the timer if the chunk's destination timer is running
- */
- void SctpAgent::SendBufferDequeueUpTo(u_int uiTsn)
- {
- DBG_I(SendBufferDequeueUpTo);
- Node_S *spDeleteNode = NULL;
- Node_S *spCurrNode = sSendBuffer.spHead;
- SctpSendBufferNode_S *spCurrNodeData = NULL;
- iAssocErrorCount = 0;
- while(spCurrNode != NULL &&
- ((SctpSendBufferNode_S*)spCurrNode->vpData)->spChunk->uiTsn <= uiTsn)
- {
- spCurrNodeData = (SctpSendBufferNode_S *) spCurrNode->vpData;
- /* Only count this chunk as newly acked and towards partial bytes
- * acked if it hasn't been gap acked or marked as ack'd due to rtx
- * limit.
- */
- if((spCurrNodeData->eGapAcked == FALSE) &&
- (spCurrNodeData->eAdvancedAcked == FALSE) )
- {
- uiHighestTsnNewlyAcked = spCurrNodeData->spChunk->uiTsn;
- spCurrNodeData->spDest->iNumNewlyAckedBytes
- += spCurrNodeData->spChunk->sHdr.usLength;
- /* only add to partial bytes acked if we are in congestion
- * avoidance mode and if there was cwnd amount of data
- * outstanding on the destination (implementor's guide)
- */
- if(spCurrNodeData->spDest->iCwnd >spCurrNodeData->spDest->iSsthresh
- &&
- ( spCurrNodeData->spDest->iOutstandingBytes
- >= spCurrNodeData->spDest->iCwnd) )
- {
- spCurrNodeData->spDest->iPartialBytesAcked
- += spCurrNodeData->spChunk->sHdr.usLength;
- }
- }
- /* This is to ensure that Max.Burst is applied when a SACK
- * acknowledges a chunk which has been fast retransmitted. If it is
- * ineligible for fast rtx, that can only be because it was fast
- * rtxed or it timed out. If it timed out, a burst shouldn't be
- * possible, but shouldn't hurt either. The fast rtx case is what we
- * are really after. This is a proposed change to RFC2960 section
- * 7.2.4
- */
- if(spCurrNodeData->eIneligibleForFastRtx == TRUE)
- eApplyMaxBurst = TRUE;
-
- /* We update the RTT estimate if the following hold true:
- * 1. RTO pending flag is set (6.3.1.C4 measured once per round trip)
- * 2. Timestamp is set for this chunk(ie, we were measuring this chunk)
- * 3. This chunk has not been retransmitted
- * 4. This chunk has not been gap acked already
- * 5. This chunk has not been advanced acked (pr-sctp: exhausted rtxs)
- */
- if(spCurrNodeData->spDest->eRtoPending == TRUE &&
- spCurrNodeData->dTxTimestamp > 0 &&
- spCurrNodeData->iNumTxs == 1 &&
- spCurrNodeData->eGapAcked == FALSE &&
- spCurrNodeData->eAdvancedAcked == FALSE)
- {
- /* If the chunk is marked for timeout rtx, then the sender is an
- * ambigious state. Were the sacks lost or was there a failure?
- * (See below, where we talk about error count resetting)
- * Since we don't clear the error counter below, we also don't
- * update the RTT. This could be a problem for late arriving SACKs.
- */
- if(spCurrNodeData->eMarkedForRtx != TIMEOUT_RTX)
- RttUpdate(spCurrNodeData->dTxTimestamp, spCurrNodeData->spDest);
- spCurrNodeData->spDest->eRtoPending = FALSE;
- }
- /* if there is a timer running on the chunk's destination, then stop it
- */
- if(spCurrNodeData->spDest->eRtxTimerIsRunning == TRUE)
- StopT3RtxTimer(spCurrNodeData->spDest);
- /* We don't want to clear the error counter if it's cleared already;
- * otherwise, we'll unnecessarily trigger a trace event.
- *
- * Also, the error counter is cleared by SACKed data ONLY if the
- * TSNs are not marked for timeout retransmission and has not been
- * gap acked before. Without this condition, we can run into a
- * problem for failure detection. When a failure occurs, some data
- * may have made it through before the failure, but the sacks got
- * lost. When the sender retransmits the first outstanding, the
- * receiver will sack all the data whose sacks got lost. We don't
- * want these sacks to clear the error counter, or else failover
- * would take longer.
- */
- if(spCurrNodeData->spDest->iErrorCount != 0 &&
- spCurrNodeData->eMarkedForRtx != TIMEOUT_RTX &&
- spCurrNodeData->eGapAcked == FALSE)
- {
- DBG_PL(SendBufferDequeueUpTo,
- "clearing error counter for %p with tsn=%lu"),
- spCurrNodeData->spDest, spCurrNodeData->spChunk->uiTsn DBG_PR;
- spCurrNodeData->spDest->iErrorCount = 0; // clear error counter
- tiErrorCount++; // ... and trace it too!
- spCurrNodeData->spDest->eStatus = SCTP_DEST_STATUS_ACTIVE;
- if(spCurrNodeData->spDest == spPrimaryDest &&
- spNewTxDest != spPrimaryDest)
- {
- DBG_PL(SendBufferDequeueUpTo,
- "primary recovered... migrating back from %p to %p"),
- spNewTxDest, spPrimaryDest DBG_PR;
- spNewTxDest = spPrimaryDest; // return to primary
- }
- }
- spDeleteNode = spCurrNode;
- spCurrNode = spCurrNode->spNext;
- DeleteNode(&sSendBuffer, spDeleteNode);
- spDeleteNode = NULL;
- }
- DBG_X(SendBufferDequeueUpTo);
- }
- /* This function uses the iOutstandingBytes variable and assumes that it has
- * NOT been updated yet to reflect the newly received SACK. Hence, it is the
- * previously outstanding DATA bytes.
- */
- void SctpAgent::AdjustCwnd(SctpDest_S *spDest)
- {
- DBG_I(AdjustCwnd);
- if(spDest->iCwnd <= spDest->iSsthresh) //in slow-start mode?
- {
- DBG_PL(AdjustCwnd, "slow start mode") DBG_PR;
- DBG_PL(AdjustCwnd, "iSsthresh=%d"), spDest->iSsthresh DBG_PR;
- /* section 7.2.1 (w/ implementor's guide)
- */
- if(spDest->iOutstandingBytes >= spDest->iCwnd)
- {
- spDest->iCwnd += MIN(spDest->iNumNewlyAckedBytes,(int)uiMaxDataSize);
- tiCwnd++; // trigger changes for trace to pick up
- }
- }
- else // congestion avoidance mode
- {
- DBG_PL(AdjustCwnd,"congestion avoidance mode") DBG_PR;
- DBG_PL(AdjustCwnd,"iPartialBytesAcked=%d iCwnd=%d iOutStandingBytes=%d"),
- spDest->iPartialBytesAcked,
- spDest->iCwnd,
- spDest->iOutstandingBytes
- DBG_PR;
- /* section 7.2.2
- */
- if(spDest->iPartialBytesAcked >= spDest->iCwnd &&
- spDest->iOutstandingBytes >= spDest->iCwnd )
- {
- DBG_PL(AdjustCwnd, "adjusting cwnd") DBG_PR;
- if(spDest->iCwnd <= spDest->iPartialBytesAcked)
- spDest->iPartialBytesAcked -= spDest->iCwnd;
- else
- spDest->iPartialBytesAcked = 0;
- spDest->iCwnd += uiMaxDataSize;
- tiCwnd++; // trigger changes for trace to pick up
- }
- }
- DBG_PL(AdjustCwnd, "pba=%d cwnd=%d out=%d PeerRwnd=%d"),
- spDest->iPartialBytesAcked,
- spDest->iCwnd,
- spDest->iOutstandingBytes,
- uiPeerRwnd
- DBG_PR;
- DBG_X(AdjustCwnd);
- }
- void SctpAgent::AdvancePeerAckPoint()
- {
- DBG_I(AdvancePeerAckPoint);
- Node_S *spCurrNode = NULL;
- SctpSendBufferNode_S *spCurrNodeData = NULL;
- if(uiAdvancedPeerAckPoint < uiCumAckPoint)
- uiAdvancedPeerAckPoint = uiCumAckPoint;
- for(spCurrNode = sSendBuffer.spHead;
- spCurrNode != NULL;
- spCurrNode = spCurrNode->spNext)
- {
- spCurrNodeData = (SctpSendBufferNode_S *) spCurrNode->vpData;
-
- /* If we haven't marked the chunk as ack'd either via an u-stream's
- * rtx exhaustion or a gap ack, then jump out of the loop. ...we've
- * advanced as far as we can!
- */
- if(spCurrNodeData->eAdvancedAcked == FALSE &&
- spCurrNodeData->eGapAcked == FALSE)
- break;
- uiAdvancedPeerAckPoint = spCurrNodeData->spChunk->uiTsn;
- }
- DBG_PL(AdvancePeerAckPoint, "uiAdvancedPeerAckPoint=%d"),
- uiAdvancedPeerAckPoint DBG_PR;
- DBG_X(AdvancePeerAckPoint);
- }
- u_int SctpAgent::GetHighestOutstandingTsn()
- {
- DBG_I(GetHighestOutstandingTsn);
- u_int uiHighestOutstandingTsn = 0;
- Node_S *spCurrBuffNode = NULL;
- SctpSendBufferNode_S *spCurrBuffData = NULL;
- /* start from the tailof the send buffer and search towards the head for the
- * first tsn oustanding... that's the highest outstanding tsn.
- */
- for(spCurrBuffNode = sSendBuffer.spTail;
- spCurrBuffNode != NULL;
- spCurrBuffNode = spCurrBuffNode->spPrev)
- {
- spCurrBuffData = (SctpSendBufferNode_S *) spCurrBuffNode->vpData;
-
- if(spCurrBuffData->eMarkedForRtx == NO_RTX) // is it oustanding?
- {
- uiHighestOutstandingTsn = spCurrBuffData->spChunk->uiTsn;// found it!
- break;
- }
- }
- DBG_PL(GetHighestOutstandingTsn, "uiHighestOutstandingTsn=%d"),
- uiHighestOutstandingTsn DBG_PR;
- DBG_X(GetHighestOutstandingTsn);
- return uiHighestOutstandingTsn;
- }
- /* This function is called as soon as we are done processing a SACK which
- * notifies the need of a fast rtx. Following RFC2960, we pack as many chunks
- * as possible into one packet (PTMU restriction). The remaining marked packets
- * are sent as soon as cwnd allows.
- */
- void SctpAgent::FastRtx()
- {
- DBG_I(FastRtx);
-
- Node_S *spCurrDestNode = NULL;
- SctpDest_S *spCurrDestData = NULL;
- Node_S *spCurrBuffNode = NULL;
- SctpSendBufferNode_S *spCurrBuffData = NULL;
- /* be sure we clear all the eCcApplied flags before using them!
- */
- for(spCurrDestNode = sDestList.spHead;
- spCurrDestNode != NULL;
- spCurrDestNode = spCurrDestNode->spNext)
- {
- spCurrDestData = (SctpDest_S *) spCurrDestNode->vpData;
- spCurrDestData->eCcApplied = FALSE;
- }
-
- /* go thru chunks marked for rtx and cut back their ssthresh, cwnd, and
- * partial bytes acked. make sure we only apply congestion control once
- * per destination and once per window (ie, round-trip).
- */
- for(spCurrBuffNode = sSendBuffer.spHead;
- spCurrBuffNode != NULL;
- spCurrBuffNode = spCurrBuffNode->spNext)
- {
- spCurrBuffData = (SctpSendBufferNode_S *) spCurrBuffNode->vpData;
- /* If the chunk has been either marked for rtx or advanced ack, we want
- * to apply congestion control (assuming we didn't already).
- *
- * Why do we do it for advanced ack chunks? Well they were advanced ack'd
- * because they were lost. The ONLY reason we are not fast rtxing them is
- * because the chunk has run out of retransmissions (u-sctp). So we need
- * to still account for the fact they were lost... so apply congestion
- * control!
- */
- if( (spCurrBuffData->eMarkedForRtx != NO_RTX ||
- spCurrBuffData->eAdvancedAcked == TRUE) &&
- spCurrBuffData->spDest->eCcApplied == FALSE &&
- spCurrBuffData->spChunk->uiTsn > uiRecover)
-
- {
- /* section 7.2.3 of rfc2960 (w/ implementor's guide)
- */
- spCurrBuffData->spDest->iSsthresh
- = MAX(spCurrBuffData->spDest->iCwnd/2,
- iInitialCwnd * (int) uiMaxDataSize);
- spCurrBuffData->spDest->iCwnd = spCurrBuffData->spDest->iSsthresh;
- spCurrBuffData->spDest->iPartialBytesAcked = 0; //reset
- tiCwnd++; // trigger changes for trace to pick up
- spCurrBuffData->spDest->eCcApplied = TRUE;
- /* Cancel any pending RTT measurement on this
- * destination. Stephan Baucke (2004-04-27) suggested this
- * action as a fix for the following simple scenario:
- *
- * - Host A sends packets 1, 2 and 3 to host B, and choses 3 for
- * an RTT measurement
- *
- * - Host B receives all packets correctly and sends ACK1, ACK2,
- * and ACK3.
- *
- * - ACK2 and ACK3 are lost on the return path
- *
- * - Eventually a timeout fires for packet 2, and A retransmits 2
- *
- * - Upon receipt of 2, B sends a cumulative ACK3 (since it has
- * received 2 & 3 before)
- *
- * - Since packet 3 has never been retransmitted, the SCTP code
- * actually accepts the ACK for an RTT measurement, although it
- * was sent in reply to the retransmission of 2, which results
- * in a much too high RTT estimate. Since this case tends to
- * happen in case of longer link interruptions, the error is
- * often amplified by subsequent timer backoffs.
- */
- spCurrBuffData->spDest->eRtoPending = FALSE;
- /* Set the recover variable to avoid multiple cwnd cuts for losses
- * in the same window (ie, round-trip).
- */
- uiRecover = GetHighestOutstandingTsn();
- }
- }
- /* possible that no chunks are pending retransmission since they could be
- * advanced ack'd
- */
- if(eMarkedChunksPending == TRUE)
- RtxMarkedChunks(RTX_LIMIT_ONE_PACKET);
- DBG_X(FastRtx);
- }
- void SctpAgent::TimeoutRtx(SctpDest_S *spDest)
- {
- DBG_I(TimeoutRtx);
- Node_S *spCurrNode = NULL;
- SctpSendBufferNode_S *spCurrNodeData = NULL;
-
- DBG_PL(TimeoutRtx, "spDest=%p"), spDest DBG_PR;
- for(spCurrNode = sSendBuffer.spHead;
- spCurrNode != NULL;
- spCurrNode = spCurrNode->spNext)
- {
- spCurrNodeData = (SctpSendBufferNode_S *) spCurrNode->vpData;
- /* Mark chunks that were sent to the destination which had a timeout and
- * have NOT been gap ack'd or advanced.
- */
- if(spCurrNodeData->spDest == spDest &&
- spCurrNodeData->eGapAcked == FALSE &&
- spCurrNodeData->eAdvancedAcked == FALSE)
- {
- MarkChunkForRtx(spCurrNodeData, TIMEOUT_RTX);
- spCurrNodeData->iNumMissingReports = 0;
- }
- }
- if(eMarkedChunksPending == TRUE)
- RtxMarkedChunks(RTX_LIMIT_ONE_PACKET);
- DBG_X(TimeoutRtx);
- }
- void SctpAgent::MarkChunkForRtx(SctpSendBufferNode_S *spNodeData,
- MarkedForRtx_E eMarkedForRtx)
- {
- DBG_I(MarkChunkForRtx);
- SctpDataChunkHdr_S *spChunk = spNodeData->spChunk;
- SctpOutStream_S *spStream = &(spOutStreams[spChunk->usStreamId]);
- DBG_PL(MarkChunkForRtx, "tsn=%lu eMarkedForRtx=%s"),
- spChunk->uiTsn,
- !eMarkedForRtx ? "NO_RTX"
- : (eMarkedForRtx==FAST_RTX ? "FAST_RTX": "TIMEOUT_RTX") DBG_PR;
- spNodeData->eMarkedForRtx = eMarkedForRtx;
- uiPeerRwnd += spChunk->sHdr.usLength; // 6.2.1.C1
- /* let's see if this chunk is on an unreliable stream. if so and the chunk
- * has run out of retransmissions, mark it as advanced acked and unmark it
- * for rtx
- */
- if(spStream->eMode == SCTP_STREAM_UNRELIABLE)
- {
- /* have we run out of retransmissions??
- */
- if(spNodeData->iNumTxs > spNodeData->iUnrelRtxLimit)
- {
- DBG_PL(MarkChunkForRtx, "giving up on tsn %lu..."),
- spChunk->uiTsn DBG_PR;
- spNodeData->eAdvancedAcked = TRUE;
- spNodeData->eMarkedForRtx = NO_RTX;
- spNodeData->spDest->iOutstandingBytes -= spChunk->sHdr.usLength;
- }
- }
-
- if(spNodeData->eMarkedForRtx != NO_RTX)
- eMarkedChunksPending = TRUE;
- DBG_PL(MarkChunkForRtx, "uiPeerRwnd=%lu"), uiPeerRwnd DBG_PR;
- DBG_X(MarkChunkForRtx);
- }
- Boolean_E SctpAgent::AnyMarkedChunks()
- {
- DBG_I(AnyMarkedChunks);
- Node_S *spCurrBuffNode = NULL;
- SctpSendBufferNode_S *spCurrBuffNodeData = NULL;
- for(spCurrBuffNode = sSendBuffer.spHead;
- spCurrBuffNode != NULL;
- spCurrBuffNode = spCurrBuffNode->spNext)
- {
- spCurrBuffNodeData = (SctpSendBufferNode_S *) spCurrBuffNode->vpData;
- if(spCurrBuffNodeData->eMarkedForRtx != NO_RTX)
- {
- DBG_PL(AnyMarkedChunks, "TRUE") DBG_PR;
- DBG_X(AnyMarkedChunks);
- return TRUE;
- }
- }
- DBG_PL(AnyMarkedChunks, "FALSE") DBG_PR;
- DBG_X(AnyMarkedChunks);
- return FALSE;
- }
- /* This function goes through the entire send buffer filling a packet with
- * chunks marked for retransmission. Once a packet is full (according to MTU)
- * it is transmittted. If the eLimit is one packet, than that is all that is
- * done. If the eLimit is cwnd, then packets full of marked tsns are sent until
- * cwnd is full.
- */
- void SctpAgent::RtxMarkedChunks(SctpRtxLimit_E eLimit)
- {
- DBG_I(RtxMarkedChunks);
- u_char *ucpOutData = new u_char[uiMaxPayloadSize];
- u_char *ucpCurrOutData = ucpOutData;
- int iBundledControlChunkSize = 0;
- int iCurrSize = 0;
- int iOutDataSize = 0;
- Node_S *spCurrBuffNode = NULL;
- SctpSendBufferNode_S *spCurrBuffNodeData = NULL;
- SctpDataChunkHdr_S *spCurrChunk;
- SctpDest_S *spRtxDest = NULL;
- Node_S *spCurrDestNode = NULL;
- SctpDest_S *spCurrDestNodeData = NULL;
- Boolean_E eControlChunkBundled = FALSE;
- int iNumPacketsSent = 0;
- memset(ucpOutData, 0, uiMaxPayloadSize);
- uiBurstLength = 0;
- /* make sure we clear all the spFirstOutstanding pointers before using them!
- */
- for(spCurrDestNode = sDestList.spHead;
- spCurrDestNode != NULL;
- spCurrDestNode = spCurrDestNode->spNext)
- {
- spCurrDestNodeData = (SctpDest_S *) spCurrDestNode->vpData;
- spCurrDestNodeData->spFirstOutstanding = NULL; // reset
- }
- /* We need to set the destination address for the retransmission(s).We assume
- * that on a given call to this function, all should all be sent to the same
- * address (should be a reasonable assumption). So, to determine the address,
- * we find the first marked chunk and determine the destination it was last
- * sent to.
- *
- * Also, we temporarily count all marked chunks as not outstanding.Why? Well,
- * if we try retransmitting on the same dest as used previously, the cwnd may
- * never let us retransmit because the outstanding is counting marked chunks
- * too. At the end of this function, we'll count all marked chunks as
- * outstanding again. (ugh... there has to be a better way!)
- */
- for(spCurrBuffNode = sSendBuffer.spHead;
- spCurrBuffNode != NULL;
- spCurrBuffNode = spCurrBuffNode->spNext)
- {
- spCurrBuffNodeData = (SctpSendBufferNode_S *) spCurrBuffNode->vpData;
-
- if(spCurrBuffNodeData->eMarkedForRtx != NO_RTX)
- {
- spCurrChunk = spCurrBuffNodeData->spChunk;
- if(spRtxDest == NULL)
- {
- /* RFC2960 says that retransmissions should go to an
- * alternate destination when available, which is the
- * default behavior characterized by
- * eRtxToAlt=RTX_TO_ALT_ON.
- *
- * We add two experimental options:
- * 1. rtx all data to same destination (RTX_TO_ALT_OFF)
- * 2. rtx only timeouts to alt dest (RTX_TO_ALT_TIMEOUTS_ONLY)
- *
- * Note: Even with these options, if the same dest is inactive,
- * then alt dest is used.
- */
- switch(eRtxToAlt)
- {
- case RTX_TO_ALT_OFF:
- if(spCurrBuffNodeData->spDest->eStatus
- == SCTP_DEST_STATUS_ACTIVE)
- {
- spRtxDest = spCurrBuffNodeData->spDest;
- }
- else
- {
- spRtxDest = GetNextDest(spCurrBuffNodeData->spDest);
- }
- break;
-
- case RTX_TO_ALT_ON:
- spRtxDest = GetNextDest(spCurrBuffNodeData->spDest);
- break;
- case RTX_TO_ALT_TIMEOUTS_ONLY:
- if(spCurrBuffNodeData->eMarkedForRtx == FAST_RTX &&
- spCurrBuffNodeData->spDest->eStatus
- == SCTP_DEST_STATUS_ACTIVE)
- {
- spRtxDest = spCurrBuffNodeData->spDest;
- }
- else
- {
- spRtxDest = GetNextDest(spCurrBuffNodeData->spDest);
- }
- break;
- }
- }
- spCurrBuffNodeData->spDest->iOutstandingBytes
- -= spCurrChunk->sHdr.usLength;
- }
- }
- spCurrBuffNode = sSendBuffer.spHead;
- while( (eLimit == RTX_LIMIT_ONE_PACKET &&
- iNumPacketsSent < 1 &&
- spCurrBuffNode != NULL) ||
- (eLimit == RTX_LIMIT_CWND &&
- spRtxDest->iOutstandingBytes < spRtxDest->iCwnd &&
- spCurrBuffNode != NULL) )
- {
- DBG_PL(RtxMarkedChunks,
- "eLimit=%s pktsSent=%d out=%d cwnd=%d spCurrBuffNode=%p"),
- (eLimit == RTX_LIMIT_ONE_PACKET) ? "ONE_PACKET" : "CWND",
- iNumPacketsSent, spRtxDest->iOutstandingBytes, spRtxDest->iCwnd,
- spCurrBuffNode
- DBG_PR;
-
- /* section 7.2.4.3
- *
- * continue filling up the packet with chunks which are marked for
- * rtx. exit loop when we have either run out of chunks or the
- * packet is full.
- *
- * note: we assume at least one data chunk fits in the packet.
- */
- for(eControlChunkBundled = FALSE;
- spCurrBuffNode != NULL;
- spCurrBuffNode = spCurrBuffNode->spNext)
- {
- spCurrBuffNodeData = (SctpSendBufferNode_S *) spCurrBuffNode->vpData;
-
- /* is this chunk the first outstanding on its destination?
- */
- if(spCurrBuffNodeData->spDest->spFirstOutstanding == NULL &&
- spCurrBuffNodeData->eGapAcked == FALSE &&
- spCurrBuffNodeData->eAdvancedAcked == FALSE)
- {
- /* yes, it is the first!
- */
- spCurrBuffNodeData->spDest->spFirstOutstanding
- = spCurrBuffNodeData;
- }
- /* Only retransmit the chunks which have been marked for rtx.
- */
- if(spCurrBuffNodeData->eMarkedForRtx != NO_RTX)
- {
- spCurrChunk = spCurrBuffNodeData->spChunk;
- /* bundle the control chunk before any data chunks and only
- * once per packet
- */
- if(eControlChunkBundled == FALSE)
- {
- eControlChunkBundled = TRUE;
- iBundledControlChunkSize=BundleControlChunks(ucpCurrOutData);
- ucpCurrOutData += iBundledControlChunkSize;
- iOutDataSize += iBundledControlChunkSize;
- }
- /* can we fit this chunk into the packet without exceeding MTU??
- */
- if((iOutDataSize + spCurrChunk->sHdr.usLength)
- > (int) uiMaxPayloadSize)
- break; // doesn't fit in packet... jump out of the for loop
- /* If this chunk was being used to measure the RTT,stop using it.
- */
- if(spCurrBuffNodeData->spDest->eRtoPending == TRUE &&
- spCurrBuffNodeData->dTxTimestamp > 0)
- {
- spCurrBuffNodeData->dTxTimestamp = 0;
- spCurrBuffNodeData->spDest->eRtoPending = FALSE;
- }
- /* section 7.2.4.4 (condition 2) - is this the first
- * outstanding for the destination and are there still
- * outstanding bytes on the destination? if so, restart
- * timer.
- */
- if(spCurrBuffNodeData->spDest->spFirstOutstanding
- == spCurrBuffNodeData)
- {
- if(spCurrBuffNodeData->spDest->iOutstandingBytes > 0)
- StartT3RtxTimer(spCurrBuffNodeData->spDest);
- }
-
- /* section 6.1 - Whenever a transmission or retransmission is
- * made to any address, if the T3-rtx timer of that address
- * is not currently running, the sender MUST start that timer.
- * If the timer for that address is already running, the sender
- * MUST restart the timer if the earliest (i.e., lowest TSN)
- * outstanding DATA chunk sent to that address is being
- * retransmitted. Otherwise, the data sender MUST NOT
- * restart the timer.
- */
- if(spRtxDest->spFirstOutstanding == NULL ||
- spCurrChunk->uiTsn <
- spRtxDest->spFirstOutstanding->spChunk->uiTsn)
- {
- /* This chunk is now the first outstanding on spRtxDest.
- */
- spRtxDest->spFirstOutstanding = spCurrBuffNodeData;
- StartT3RtxTimer(spRtxDest);
- }
-
- memcpy(ucpCurrOutData, spCurrChunk, spCurrChunk->sHdr.usLength);
- iCurrSize = spCurrChunk->sHdr.usLength;
-
- /* the chunk length field does not include the padded bytes,
- * so we need to account for these extra bytes.
- */
- if( (iCurrSize % 4) != 0 )
- iCurrSize += 4 - (iCurrSize % 4);
- ucpCurrOutData += iCurrSize;
- iOutDataSize += iCurrSize;
- spCurrBuffNodeData->spDest = spRtxDest;
- spCurrBuffNodeData->iNumTxs++;
- spCurrBuffNodeData->eMarkedForRtx = NO_RTX;
- /* fill in tracing fields too
- */
- spSctpTrace[uiNumChunks].eType = SCTP_CHUNK_DATA;
- spSctpTrace[uiNumChunks].uiTsn = spCurrChunk->uiTsn;
- spSctpTrace[uiNumChunks].usStreamId = spCurrChunk->usStreamId;
- spSctpTrace[uiNumChunks].usStreamSeqNum
- = spCurrChunk->usStreamSeqNum;
- uiNumChunks++;
- /* the chunk is now outstanding on the alternate destination
- */
- spCurrBuffNodeData->spDest->iOutstandingBytes
- += spCurrChunk->sHdr.usLength;
- uiPeerRwnd -= spCurrChunk->sHdr.usLength; // 6.2.1.B
- DBG_PL(RtxMarkedChunks, "spDest->iOutstandingBytes=%d"),
- spCurrBuffNodeData->spDest->iOutstandingBytes DBG_PR;
- DBG_PL(RtxMarkedChunks, "TSN=%d"), spCurrChunk->uiTsn DBG_PR;
- }
- else if(spCurrBuffNodeData->eAdvancedAcked == TRUE)
- {
- if(spCurrBuffNodeData->spDest->spFirstOutstanding
- == spCurrBuffNodeData)
- {
- /* This WAS considered the first outstanding chunk for
- * the destination, then stop the timer if there are no
- * outstanding chunks waiting behind this one in the
- * send buffer. However, if there ARE more outstanding
- * chunks on this destination, we need to restart timer
- * for those.
- */
- if(spCurrBuffNodeData->spDest->iOutstandingBytes > 0)
- StartT3RtxTimer(spCurrBuffNodeData->spDest);
- else
- StopT3RtxTimer(spCurrBuffNodeData->spDest);
- }
- }
- }
- /* Transmit the packet now...
- */
- if(iOutDataSize > 0)
- {
- SendPacket(ucpOutData, iOutDataSize, spRtxDest);
- if(spRtxDest->eRtxTimerIsRunning == FALSE)
- StartT3RtxTimer(spRtxDest);
- iNumPacketsSent++;
- iOutDataSize = 0; // reset
- ucpCurrOutData = ucpOutData; // reset
- memset(ucpOutData, 0, uiMaxPayloadSize); // reset
- spRtxDest->opCwndDegradeTimer->resched(spRtxDest->dRto);
- /* This addresses the proposed change to RFC2960 section 7.2.4,
- * regarding using of Max.Burst. We have an option which allows
- * to control if Max.Burst is applied.
- */
- if(eUseMaxBurst == MAX_BURST_USAGE_ON)
- if( (eApplyMaxBurst == TRUE) && (uiBurstLength++ >= MAX_BURST) )
- {
- /* we've reached Max.Burst limit, so jump out of loop
- */
- eApplyMaxBurst = FALSE; // reset before jumping out of loop
- break;
- }
- }
- }
- /* Ok, let's count all marked chunks as outstanding again. (ugh... there
- * has to be a better way!)
- */
- for(spCurrBuffNode = sSendBuffer.spHead;
- spCurrBuffNode != NULL;
- spCurrBuffNode = spCurrBuffNode->spNext)
- {
- spCurrBuffNodeData = (SctpSendBufferNode_S *) spCurrBuffNode->vpData;
-
- if(spCurrBuffNodeData->eMarkedForRtx != NO_RTX)
- {
- spCurrChunk = spCurrBuffNodeData->spChunk;
- spCurrBuffNodeData->spDest->iOutstandingBytes
- += spCurrChunk->sHdr.usLength;
- }
- }
-
- /* If we made it here, either our limit was only one packet worth of
- * retransmissions or we hit the end of the list and there are no more
- * marked chunks. If we didn't hit the end, let's see if there are more
- * marked chunks.
- */
- eMarkedChunksPending = AnyMarkedChunks();
- DBG_X(RtxMarkedChunks);
- delete [] ucpOutData;
- }
- /* Updates uiHighestRecvTsn
- */
- Boolean_E SctpAgent::UpdateHighestTsn(u_int uiTsn)
- {
- DBG_I(UpdateHighestTsn);
- if(uiTsn > uiHighestRecvTsn)
- {
- uiHighestRecvTsn = uiTsn;
- DBG_PL(UpdateHighestTsn, "returning TRUE") DBG_PR;
- DBG_X(UpdateHighestTsn);
- return TRUE;
- }
- else