sctp-multipleFastRtx.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  *
00007  * Redistribution and use in source and binary forms, with or without
00008  * modification, are permitted provided that the following conditions
00009  * are met:
00010  *
00011  * 1. Redistributions of source code must retain the above copyright
00012  *    notice, this list of conditions and the following disclaimer.
00013  *
00014  * 2. Redistributions in binary form must reproduce the above copyright
00015  *    notice, this list of conditions and the following disclaimer in the
00016  *    documentation and/or other materials provided with the distribution.
00017  *
00018  * 3. Neither the name of the University nor of the Laboratory may be used
00019  *    to endorse or promote products derived from this software without
00020  *    specific prior written permission.
00021  *
00022  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
00023  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00024  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00025  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
00026  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00027  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00028  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00029  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00030  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00031  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00032  * SUCH DAMAGE.
00033  */
00034 
00035 /* MultipleFastRtx extension implements the Caro Multiple Fast Retransmit
00036  * Algorithm. Caro's Algorithm introduces a fastRtxRecover state variable
00037  * per TSN in the send buffer. Any time a TSN is retransmitted, its
00038  * fastRtxRecover is set to the highest TSN outstanding at the time of
00039  * retransmit. That way, only missing reports triggered by TSNs beyond
00040  * fastRtxRecover may trigger yet another fast retransmit.
00041  */
00042 
00043 #ifndef lint
00044 static const char rcsid[] =
00045 "@(#) $Header: /nfs/jade/vint/CVSROOT/ns-2/sctp/sctp-multipleFastRtx.cc,v 1.3 2005/10/07 05:58:29 tomh Exp $ (UD/PEL)";
00046 #endif
00047 
00048 #include "ip.h"
00049 #include "sctp-multipleFastRtx.h"
00050 #include "flags.h"
00051 #include "random.h"
00052 #include "template.h"
00053 
00054 #include "sctpDebug.h"
00055 
00056 #ifdef DMALLOC
00057 #include "dmalloc.h"
00058 #endif
00059 
00060 #define MIN(x,y)    (((x)<(y))?(x):(y))
00061 #define MAX(x,y)    (((x)>(y))?(x):(y))
00062 
00063 static class MultipleFastRtxSctpClass : public TclClass 
00064 { 
00065 public:
00066   MultipleFastRtxSctpClass() : TclClass("Agent/SCTP/MultipleFastRtx") {}
00067   TclObject* create(int, const char*const*) 
00068   {
00069     return (new MultipleFastRtxSctpAgent());
00070   }
00071 } classSctpMultipleFastRtx;
00072 
00073 MultipleFastRtxSctpAgent::MultipleFastRtxSctpAgent() : SctpAgent()
00074 {
00075 }
00076 
00077 void MultipleFastRtxSctpAgent::delay_bind_init_all()
00078 {
00079   delay_bind_init_one("mfrCount_");
00080   SctpAgent::delay_bind_init_all();
00081 }
00082 
00083 int MultipleFastRtxSctpAgent::delay_bind_dispatch(const char *cpVarName, 
00084                       const char *cpLocalName, 
00085                       TclObject *opTracer)
00086 {
00087   if(delay_bind(cpVarName, cpLocalName, "mfrCount_", &tiMfrCount, opTracer))
00088     return TCL_OK;
00089 
00090   return SctpAgent::delay_bind_dispatch(cpVarName, cpLocalName, opTracer);
00091 }
00092 
00093 void MultipleFastRtxSctpAgent::TraceAll()
00094 {
00095   char cpOutString[500];
00096   Node_S *spCurrNode = NULL;
00097   SctpDest_S *spCurrDest = NULL;
00098   double dCurrTime = Scheduler::instance().clock();
00099 
00100   for(spCurrNode = sDestList.spHead;
00101       spCurrNode != NULL;
00102       spCurrNode = spCurrNode->spNext)
00103     {
00104       spCurrDest = (SctpDest_S *) spCurrNode->vpData;
00105       SetSource(spCurrDest); // gives us the correct source addr & port
00106       sprintf(cpOutString,
00107           "time: %-8.5f  "
00108           "saddr: %-2d sport: %-2d daddr: %-2d dport: %-2d "
00109           "cwnd: %d pba: %d out: %d ssthresh: %d peerRwnd: %d "
00110           "rto: %-6.3f srtt: %-6.3f rttvar: %-6.3f "
00111           "assocErrors: %d pathErrors: %d dstatus: %s isPrimary: %s "
00112           "frCount: %d mfrCount: %d timeoutCount: %d rcdCount: %d\n",
00113           dCurrTime,
00114           addr(), port(), spCurrDest->iNsAddr, spCurrDest->iNsPort,
00115           spCurrDest->iCwnd, spCurrDest->iPartialBytesAcked, 
00116           spCurrDest->iOutstandingBytes, spCurrDest->iSsthresh, 
00117           uiPeerRwnd,
00118           spCurrDest->dRto, spCurrDest->dSrtt, 
00119           spCurrDest->dRttVar,
00120           iAssocErrorCount,
00121           spCurrDest->iErrorCount,
00122           spCurrDest->eStatus ? "ACTIVE" : "INACTIVE",
00123           (spCurrDest == spPrimaryDest) ? "TRUE" : "FALSE",
00124           int(tiFrCount),
00125           // BEGIN -- MultipleFastRtx changes to this function  
00126           int(tiMfrCount),
00127               // END -- MultipleFastRtx changes to this function  
00128           spCurrDest->iTimeoutCount,
00129           spCurrDest->iRcdCount);
00130       if(channel_)
00131     (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
00132     }
00133 
00134   sprintf(cpOutString, "\n");
00135   if(channel_)
00136     (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
00137 }
00138 
00139 void MultipleFastRtxSctpAgent::TraceVar(const char* cpVar)
00140 {
00141   char cpOutString[500];
00142   Node_S *spCurrNode = NULL;
00143   SctpDest_S *spCurrDest = NULL;
00144   double dCurrTime = Scheduler::instance().clock();
00145 
00146   if(!strcmp(cpVar, "cwnd_"))
00147     for(spCurrNode = sDestList.spHead;
00148     spCurrNode != NULL;
00149     spCurrNode = spCurrNode->spNext)
00150       {
00151     spCurrDest = (SctpDest_S *) spCurrNode->vpData;
00152     SetSource(spCurrDest); // gives us the correct source addr & port
00153     sprintf(cpOutString,
00154         "time: %-8.5f  "
00155         "saddr: %-2d sport: %-2d daddr: %-2d dport: %-2d "
00156         "cwnd: %d pba: %d out: %d ssthresh: %d peerRwnd: %d\n",
00157         dCurrTime, 
00158         addr(), port(), 
00159         spCurrDest->iNsAddr, spCurrDest->iNsPort,
00160         spCurrDest->iCwnd, spCurrDest->iPartialBytesAcked, 
00161         spCurrDest->iOutstandingBytes, spCurrDest->iSsthresh, 
00162         uiPeerRwnd);
00163     if(channel_)
00164       (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
00165       }
00166 
00167   else if(!strcmp(cpVar, "rto_"))
00168     for(spCurrNode = sDestList.spHead;
00169     spCurrNode != NULL;
00170     spCurrNode = spCurrNode->spNext)
00171       {
00172     spCurrDest = (SctpDest_S *) spCurrNode->vpData;
00173     SetSource(spCurrDest); // gives us the correct source addr & port
00174     sprintf(cpOutString,
00175         "time: %-8.5f  "
00176         "saddr: %-2d sport: %-2d daddr: %-2d dport: %-2d "
00177         "rto: %-6.3f srtt: %-6.3f rttvar: %-6.3f\n",
00178         dCurrTime, 
00179         addr(), port(), 
00180         spCurrDest->iNsAddr, spCurrDest->iNsPort,
00181         spCurrDest->dRto, spCurrDest->dSrtt, 
00182         spCurrDest->dRttVar);
00183     if(channel_)
00184       (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
00185       }
00186 
00187   else if(!strcmp(cpVar, "errorCount_"))
00188     for(spCurrNode = sDestList.spHead;
00189     spCurrNode != NULL;
00190     spCurrNode = spCurrNode->spNext)
00191       {
00192     spCurrDest = (SctpDest_S *) spCurrNode->vpData;
00193     SetSource(spCurrDest); // gives us the correct source addr & port
00194     sprintf(cpOutString, 
00195         "time: %-8.5f  "
00196         "saddr: %-2d sport: %-2d daddr: %-2d dport: %-2d "
00197         "assocErrors: %d pathErrors: %d dstatus: %s isPrimary: %s\n",
00198         dCurrTime, 
00199         addr(), port(), 
00200         spCurrDest->iNsAddr, spCurrDest->iNsPort,
00201         iAssocErrorCount,
00202         spCurrDest->iErrorCount,
00203         spCurrDest->eStatus ? "ACTIVE" : "INACTIVE",
00204         (spCurrDest == spPrimaryDest) ? "TRUE" : "FALSE");
00205     if(channel_)
00206       (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
00207       }
00208 
00209   else if(!strcmp(cpVar, "frCount_"))
00210     {
00211       sprintf(cpOutString, 
00212           "time: %-8.5f  "
00213           "frCount: %d\n",
00214           dCurrTime, 
00215           int(*((TracedInt*) cpVar)) );
00216       if(channel_)
00217     (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
00218     }
00219 
00220   // BEGIN -- MultipleFastRtx changes to this function  
00221   else if(!strcmp(cpVar, "mfrCount_"))
00222     {
00223       sprintf(cpOutString, 
00224           "time: %-8.5f  "
00225           "mfrCount: %d\n",
00226           dCurrTime, 
00227           int(*((TracedInt*) cpVar)) );
00228       if(channel_)
00229     (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
00230     }
00231   // END -- MultipleFastRtx changes to this function  
00232 
00233   else if(!strcmp(cpVar, "timeoutCount_"))
00234     {
00235     for(spCurrNode = sDestList.spHead;
00236     spCurrNode != NULL;
00237     spCurrNode = spCurrNode->spNext)
00238       {
00239     spCurrDest = (SctpDest_S *) spCurrNode->vpData;
00240     SetSource(spCurrDest); // gives us the correct source addr & port
00241     sprintf(cpOutString, 
00242         "time: %-8.5f  "
00243         "saddr: %-2d sport: %-2d daddr: %-2d dport: %-2d "
00244         "timeoutCount: %d\n",
00245         dCurrTime, 
00246         addr(), port(), 
00247         spCurrDest->iNsAddr, spCurrDest->iNsPort,
00248         spCurrDest->iTimeoutCount);
00249     if(channel_)
00250       (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
00251       }
00252     }
00253 
00254   else if(!strcmp(cpVar, "rcdCount_"))
00255     {
00256     for(spCurrNode = sDestList.spHead;
00257     spCurrNode != NULL;
00258     spCurrNode = spCurrNode->spNext)
00259       {
00260     spCurrDest = (SctpDest_S *) spCurrNode->vpData;
00261     SetSource(spCurrDest); // gives us the correct source addr & port
00262     sprintf(cpOutString, 
00263         "time: %-8.5f  "
00264         "saddr: %-2d sport: %-2d daddr: %-2d dport: %-2d "
00265         "rcdCount: %d\n",
00266         dCurrTime, 
00267         addr(), port(), 
00268         spCurrDest->iNsAddr, spCurrDest->iNsPort,
00269         spCurrDest->iRcdCount);
00270     if(channel_)
00271       (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
00272       }
00273     }
00274 
00275   else
00276     {
00277       sprintf(cpOutString,
00278           "time: %-8.5f  "
00279           "saddr: %-2d sport: %-2d daddr: %-2d dport: %-2d %s: %s\n",
00280           dCurrTime, addr(), port(), daddr(), dport(),
00281           cpVar, "ERROR (unepected trace variable)"); 
00282       if(channel_)
00283     (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
00284     }
00285 
00286   sprintf(cpOutString, "\n");
00287   if(channel_)
00288     (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
00289 }
00290 
00291 
00292 void MultipleFastRtxSctpAgent::AddToSendBuffer(SctpDataChunkHdr_S *spChunk, 
00293                 int iChunkSize,
00294                 u_int uiReliability,
00295                 SctpDest_S *spDest)
00296 {
00297   DBG_I(AddToSendBuffer);
00298   DBG_PL(AddToSendBuffer, "spDest=%p  iChunkSize=%d"), 
00299     spDest, iChunkSize DBG_PR;
00300 
00301   Node_S *spNewNode = new Node_S;
00302   spNewNode->eType = NODE_TYPE_SEND_BUFFER;
00303   spNewNode->vpData = new SctpSendBufferNode_S;
00304 
00305   SctpSendBufferNode_S * spNewNodeData 
00306     = (SctpSendBufferNode_S *) spNewNode->vpData;
00307 
00308   /* This can NOT simply be a 'new SctpDataChunkHdr_S', because we need to
00309    * allocate the space for the ENTIRE data chunk and not just the data
00310    * chunk header.  
00311    */
00312   spNewNodeData->spChunk = (SctpDataChunkHdr_S *) new u_char[iChunkSize];
00313   memcpy(spNewNodeData->spChunk, spChunk, iChunkSize);
00314 
00315   spNewNodeData->eAdvancedAcked = FALSE;
00316   spNewNodeData->eGapAcked = FALSE;
00317   spNewNodeData->eAddedToPartialBytesAcked = FALSE;
00318   spNewNodeData->iNumMissingReports = 0;
00319   spNewNodeData->iUnrelRtxLimit = uiReliability;
00320   spNewNodeData->eMarkedForRtx = NO_RTX;
00321   spNewNodeData->eIneligibleForFastRtx = FALSE;
00322   spNewNodeData->iNumTxs = 1;
00323   spNewNodeData->spDest = spDest;
00324 
00325   // BEGIN -- MultipleFastRtx changes to this function  
00326   spNewNodeData->uiFastRtxRecover = 0;
00327   // END -- MultipleFastRtx changes to this function  
00328 
00329   /* Is there already a DATA chunk in flight measuring an RTT? 
00330    * (6.3.1.C4 RTT measured once per round trip)
00331    */
00332   if(spDest->eRtoPending == FALSE)  // NO?
00333     {
00334       spNewNodeData->dTxTimestamp = Scheduler::instance().clock();
00335       spDest->eRtoPending = TRUE;   // ...well now there is :-)
00336     }
00337   else
00338     spNewNodeData->dTxTimestamp = 0; // don't use this check for RTT estimate
00339 
00340   InsertNode(&sSendBuffer, sSendBuffer.spTail, spNewNode, NULL);
00341 
00342   DBG_X(AddToSendBuffer);
00343 }
00344 
00345 /* Go thru the send buffer deleting all chunks which have a tsn <= the 
00346  * tsn parameter passed in. We assume the chunks in the rtx list are ordered by 
00347  * their tsn value. In addtion, for each chunk deleted:
00348  *   1. we add the chunk length to # newly acked bytes and partial bytes acked
00349  *   2. we update round trip time if appropriate
00350  *   3. stop the timer if the chunk's destination timer is running
00351  */
00352 void MultipleFastRtxSctpAgent::SendBufferDequeueUpTo(u_int uiTsn)
00353 {
00354   DBG_I(SendBufferDequeueUpTo);
00355 
00356   Node_S *spDeleteNode = NULL;
00357   Node_S *spCurrNode = sSendBuffer.spHead;
00358   SctpSendBufferNode_S *spCurrNodeData = NULL;
00359 
00360   iAssocErrorCount = 0;
00361 
00362   while(spCurrNode != NULL &&
00363     ((SctpSendBufferNode_S*)spCurrNode->vpData)->spChunk->uiTsn <= uiTsn)
00364     {
00365       spCurrNodeData = (SctpSendBufferNode_S *) spCurrNode->vpData;
00366 
00367       /* Only count this chunk as newly acked and towards partial bytes
00368        * acked if it hasn't been gap acked or marked as ack'd due to rtx
00369        * limit.  
00370        */
00371       if((spCurrNodeData->eGapAcked == FALSE) &&
00372      (spCurrNodeData->eAdvancedAcked == FALSE) )
00373     {
00374       uiHighestTsnNewlyAcked = spCurrNodeData->spChunk->uiTsn;
00375 
00376       spCurrNodeData->spDest->iNumNewlyAckedBytes 
00377         += spCurrNodeData->spChunk->sHdr.usLength;
00378 
00379       /* only add to partial bytes acked if we are in congestion
00380        * avoidance mode and if there was cwnd amount of data
00381        * outstanding on the destination (implementor's guide) 
00382        */
00383       if(spCurrNodeData->spDest->iCwnd >spCurrNodeData->spDest->iSsthresh &&
00384          ( spCurrNodeData->spDest->iOutstandingBytes 
00385            >= spCurrNodeData->spDest->iCwnd) )
00386         {
00387           spCurrNodeData->spDest->iPartialBytesAcked 
00388         += spCurrNodeData->spChunk->sHdr.usLength;
00389         }
00390     }
00391 
00392 
00393       // BEGIN -- MultipleFastRtx changes to this function  
00394 
00395       /* This is to ensure that Max.Burst is applied when a SACK
00396        * acknowledges a chunk which has been fast retransmitted. (If the
00397        * fast rtx recover variable is set, that can only be because it was
00398        * fast rtxed.) This is a proposed change to RFC2960 section 7.2.4
00399        */
00400       if(spCurrNodeData->uiFastRtxRecover > 0)
00401     eApplyMaxBurst = TRUE;
00402 
00403       // END -- MultipleFastRtx changes to this function  
00404 
00405         
00406       /* We update the RTT estimate if the following hold true:
00407        *   1. RTO pending flag is set (6.3.1.C4 measured once per round trip)
00408        *   2. Timestamp is set for this chunk (ie, we were measuring this chunk)
00409        *   3. This chunk has not been retransmitted
00410        *   4. This chunk has not been gap acked already 
00411        *   5. This chunk has not been advanced acked (pr-sctp: exhausted rtxs)
00412        */
00413       if(spCurrNodeData->spDest->eRtoPending == TRUE &&
00414      spCurrNodeData->dTxTimestamp > 0 &&
00415      spCurrNodeData->iNumTxs == 1 &&
00416      spCurrNodeData->eGapAcked == FALSE &&
00417      spCurrNodeData->eAdvancedAcked == FALSE) 
00418     {
00419 
00420       /* If the chunk is marked for timeout rtx, then the sender is an 
00421        * ambigious state. Were the sacks lost or was there a failure?
00422        * Since we don't clear the error counter below, we also don't
00423        * update the RTT. This could be a problem for late arriving SACKs.
00424        */
00425       if(spCurrNodeData->eMarkedForRtx != TIMEOUT_RTX)
00426         RttUpdate(spCurrNodeData->dTxTimestamp, spCurrNodeData->spDest);
00427       spCurrNodeData->spDest->eRtoPending = FALSE;
00428     }
00429 
00430       /* if there is a timer running on the chunk's destination, then stop it
00431        */
00432       if(spCurrNodeData->spDest->eRtxTimerIsRunning == TRUE)
00433     StopT3RtxTimer(spCurrNodeData->spDest);
00434 
00435       /* We don't want to clear the error counter if it's cleared already;
00436        * otherwise, we'll unnecessarily trigger a trace event.
00437        *
00438        * Also, the error counter is cleared by SACKed data ONLY if the
00439        * TSNs are not marked for timeout retransmission and has not been
00440        * gap acked before. Without this condition, we can run into a
00441        * problem for failure detection. When a failure occurs, some data
00442        * may have made it through before the failure, but the sacks got
00443        * lost. When the sender retransmits the first outstanding, the
00444        * receiver will sack all the data whose sacks got lost. We don't
00445        * want these sacks to clear the error counter, or else failover
00446        * would take longer.
00447        */
00448       if(spCurrNodeData->spDest->iErrorCount != 0 &&
00449      spCurrNodeData->eMarkedForRtx != TIMEOUT_RTX &&
00450      spCurrNodeData->eGapAcked == FALSE)
00451     {
00452       DBG_PL(SendBufferDequeueUpTo, 
00453          "clearing error counter for %p with tsn=%lu"), 
00454         spCurrNodeData->spDest, spCurrNodeData->spChunk->uiTsn DBG_PR;
00455 
00456       spCurrNodeData->spDest->iErrorCount = 0; // clear error counter
00457       tiErrorCount++;                          // ... and trace it too!
00458       spCurrNodeData->spDest->eStatus = SCTP_DEST_STATUS_ACTIVE;
00459       if(spCurrNodeData->spDest == spPrimaryDest &&
00460          spNewTxDest != spPrimaryDest) 
00461         {
00462           DBG_PL(SendBufferDequeueUpTo,
00463              "primary recovered... migrating back from %p to %p"),
00464         spNewTxDest, spPrimaryDest DBG_PR;
00465           spNewTxDest = spPrimaryDest; // return to primary
00466         }
00467     }
00468 
00469       spDeleteNode = spCurrNode;
00470       spCurrNode = spCurrNode->spNext;
00471       DeleteNode(&sSendBuffer, spDeleteNode);
00472       spDeleteNode = NULL;
00473     }
00474 
00475   DBG_X(SendBufferDequeueUpTo);
00476 }
00477 
00478 /* returns a boolean of whether a fast retransmit is necessary
00479  */
00480 Boolean_E MultipleFastRtxSctpAgent::ProcessGapAckBlocks(u_char *ucpSackChunk,
00481                      Boolean_E eNewCumAck)
00482 {
00483   DBG_I(ProcessGapAckBlocks);
00484 
00485   Boolean_E eFastRtxNeeded = FALSE;
00486   u_int uiHighestTsnSacked = uiHighestTsnNewlyAcked;
00487   u_int uiStartTsn;
00488   u_int uiEndTsn;
00489   Node_S *spCurrNode = NULL;
00490   SctpSendBufferNode_S *spCurrNodeData = NULL;
00491   Node_S *spCurrDestNode = NULL;
00492   SctpDest_S *spCurrDestNodeData = NULL;
00493 
00494   SctpSackChunk_S *spSackChunk = (SctpSackChunk_S *) ucpSackChunk;
00495 
00496   u_short usNumGapAcksProcessed = 0;
00497   SctpGapAckBlock_S *spCurrGapAck 
00498     = (SctpGapAckBlock_S *) (ucpSackChunk + sizeof(SctpSackChunk_S));
00499 
00500   // BEGIN -- MultipleFastRtx changes to this function    
00501   u_int uiHighestOutstandingTsn = GetHighestOutstandingTsn();
00502 
00503   /* We want to track any time a regular FR and a MFR is invoked
00504    */
00505   Boolean_E eFrInvoked = FALSE;
00506   Boolean_E eMfrInvoked = FALSE;
00507   // END -- MultipleFastRtx changes to this function    
00508 
00509   DBG_PL(ProcessGapAckBlocks,"CumAck=%d"), spSackChunk->uiCumAck DBG_PR;
00510 
00511   if(sSendBuffer.spHead == NULL) // do we have ANYTHING in the rtx buffer?
00512     {
00513       /* This COULD mean that this sack arrived late, and a previous one
00514        * already cum ack'd everything. ...so, what do we do? nothing??
00515        */
00516     }
00517   
00518   else // we do have chunks in the rtx buffer
00519     {
00520       /* make sure we clear all the spFirstOutstanding pointers before
00521        * using them!  
00522        */
00523       for(spCurrDestNode = sDestList.spHead;
00524       spCurrDestNode != NULL;
00525       spCurrDestNode = spCurrDestNode->spNext)
00526     {
00527       spCurrDestNodeData = (SctpDest_S *) spCurrDestNode->vpData;
00528       spCurrDestNodeData->spFirstOutstanding = NULL;
00529     }
00530 
00531       for(spCurrNode = sSendBuffer.spHead;
00532       (spCurrNode != NULL) &&
00533         (usNumGapAcksProcessed != spSackChunk->usNumGapAckBlocks);
00534       spCurrNode = spCurrNode->spNext)
00535     {
00536       spCurrNodeData = (SctpSendBufferNode_S *) spCurrNode->vpData;
00537 
00538       /* is this chunk the first outstanding on its destination?
00539        */
00540       if(spCurrNodeData->spDest->spFirstOutstanding == NULL &&
00541          spCurrNodeData->eGapAcked == FALSE &&
00542          spCurrNodeData->eAdvancedAcked == FALSE)
00543         {
00544           /* yes, it is the first!
00545            */
00546           spCurrNodeData->spDest->spFirstOutstanding = spCurrNodeData;
00547         }
00548 
00549       DBG_PL(ProcessGapAckBlocks, "--> rtx list chunk begin") DBG_PR;
00550 
00551       DBG_PL(ProcessGapAckBlocks, "    TSN=%d"), 
00552         spCurrNodeData->spChunk->uiTsn 
00553         DBG_PR;
00554 
00555       DBG_PL(ProcessGapAckBlocks, "    %s=%s %s=%s"),
00556         "eGapAcked", 
00557         spCurrNodeData->eGapAcked ? "TRUE" : "FALSE",
00558         "eAddedToPartialBytesAcked",
00559         spCurrNodeData->eAddedToPartialBytesAcked ? "TRUE" : "FALSE" 
00560         DBG_PR;
00561 
00562       DBG_PL(ProcessGapAckBlocks, "    NumMissingReports=%d NumTxs=%d"),
00563         spCurrNodeData->iNumMissingReports, 
00564         spCurrNodeData->iNumTxs 
00565         DBG_PR;
00566 
00567       DBG_PL(ProcessGapAckBlocks, "<-- rtx list chunk end") DBG_PR;
00568       
00569       DBG_PL(ProcessGapAckBlocks,"GapAckBlock StartOffset=%d EndOffset=%d"),
00570         spCurrGapAck->usStartOffset, spCurrGapAck->usEndOffset DBG_PR;
00571 
00572       uiStartTsn = spSackChunk->uiCumAck + spCurrGapAck->usStartOffset;
00573       uiEndTsn = spSackChunk->uiCumAck + spCurrGapAck->usEndOffset;
00574       
00575       DBG_PL(ProcessGapAckBlocks, "GapAckBlock StartTsn=%d EndTsn=%d"),
00576         uiStartTsn, uiEndTsn DBG_PR;
00577 
00578       if(spCurrNodeData->spChunk->uiTsn < uiStartTsn)
00579         {
00580           /* This chunk is NOT being acked and is missing at the receiver
00581            */
00582 
00583           /* If this chunk was GapAcked before, then either the
00584            * receiver has renegged the chunk (which our simulation
00585            * doesn't do) or this SACK is arriving out of order.
00586            */
00587           if(spCurrNodeData->eGapAcked == TRUE)
00588         {
00589           DBG_PL(ProcessGapAckBlocks, 
00590              "out of order SACK? setting TSN=%d eGapAcked=FALSE"),
00591             spCurrNodeData->spChunk->uiTsn DBG_PR;
00592           spCurrNodeData->eGapAcked = FALSE;
00593           spCurrNodeData->spDest->iOutstandingBytes 
00594             += spCurrNodeData->spChunk->sHdr.usLength;
00595 
00596           /* section 6.3.2.R4 says that we should restart the
00597            * T3-rtx timer here if it isn't running already. In our
00598            * implementation, it isn't necessary since
00599            * ProcessSackChunk will restart the timer for any
00600            * destinations which have outstanding data and don't
00601            * have a timer running.
00602            */
00603         }
00604         }
00605       else if((uiStartTsn <= spCurrNodeData->spChunk->uiTsn) && 
00606           (spCurrNodeData->spChunk->uiTsn <= uiEndTsn) )
00607         {
00608           /* This chunk is being acked via a gap ack block
00609            */
00610           DBG_PL(ProcessGapAckBlocks, "gap ack acks this chunk: %s%s"),
00611         "eGapAcked=",
00612         spCurrNodeData->eGapAcked ? "TRUE" : "FALSE" 
00613         DBG_PR;
00614 
00615           /* HTNA algorithm... we need to know the highest TSN sacked
00616            * (even if it isn't new), so that when the sender is in
00617            * Fast Recovery, the outstanding tsns beyond the last sack
00618            * tsn do not have their missing reports incremented
00619            */
00620           if(uiHighestTsnSacked < spCurrNodeData->spChunk->uiTsn)
00621         uiHighestTsnSacked = spCurrNodeData->spChunk->uiTsn;
00622 
00623           if(spCurrNodeData->eGapAcked == FALSE)
00624         {
00625           DBG_PL(ProcessGapAckBlocks, "setting eGapAcked=TRUE") DBG_PR;
00626           spCurrNodeData->eGapAcked = TRUE;
00627 
00628           /* HTNA algorithm... we need to know the highest TSN
00629            * newly acked
00630            */
00631           if(uiHighestTsnNewlyAcked < spCurrNodeData->spChunk->uiTsn)
00632             uiHighestTsnNewlyAcked = spCurrNodeData->spChunk->uiTsn;
00633 
00634           if(spCurrNodeData->eAdvancedAcked == FALSE)
00635             {
00636               spCurrNodeData->spDest->iNumNewlyAckedBytes 
00637             += spCurrNodeData->spChunk->sHdr.usLength;
00638             }
00639 
00640           /* only increment partial bytes acked if we are in
00641            * congestion avoidance mode, we have a new cum ack, and
00642            * we haven't already incremented it for this sack
00643            */
00644           if(( spCurrNodeData->spDest->iCwnd 
00645                > spCurrNodeData->spDest->iSsthresh) &&
00646              eNewCumAck == TRUE &&
00647              spCurrNodeData->eAddedToPartialBytesAcked == FALSE)
00648             {
00649               DBG_PL(ProcessGapAckBlocks, 
00650                  "setting eAddedToPartiallyBytesAcked=TRUE") DBG_PR;
00651 
00652               spCurrNodeData->eAddedToPartialBytesAcked = TRUE; // set
00653 
00654               spCurrNodeData->spDest->iPartialBytesAcked 
00655             += spCurrNodeData->spChunk->sHdr.usLength;
00656             }
00657           /* We update the RTT estimate if the following hold true:
00658            *   1. RTO pending flag is set (6.3.1.C4)
00659            *   2. Timestamp is set for this chunk 
00660            *   3. This chunk has not been retransmitted
00661            *   4. This chunk has not been gap acked already 
00662            *   5. This chunk has not been advanced acked (pr-sctp)
00663            */
00664           if(spCurrNodeData->spDest->eRtoPending == TRUE &&
00665              spCurrNodeData->dTxTimestamp > 0 &&
00666              spCurrNodeData->iNumTxs == 1 &&
00667              spCurrNodeData->eAdvancedAcked == FALSE) 
00668             {
00669               /* If the chunk is marked for timeout rtx, then the
00670                * sender is an ambigious state. Were the sacks lost
00671                * or was there a failure?  Since we don't clear the
00672                * error counter below, we also don't update the
00673                * RTT. This could be a problem for late arriving
00674                * SACKs.
00675                */
00676               if(spCurrNodeData->eMarkedForRtx != TIMEOUT_RTX)
00677             RttUpdate(spCurrNodeData->dTxTimestamp, 
00678                   spCurrNodeData->spDest);
00679               spCurrNodeData->spDest->eRtoPending = FALSE;
00680             }
00681 
00682           /* section 6.3.2.R3 - Stop the timer if this is the
00683            * first outstanding for this destination (note: it may
00684            * have already been stopped if there was a new cum
00685            * ack). If there are still outstanding bytes on this
00686            * destination, we'll restart the timer later in
00687            * ProcessSackChunk() 
00688            */
00689           if(spCurrNodeData->spDest->spFirstOutstanding 
00690              == spCurrNodeData)
00691             
00692             {
00693               if(spCurrNodeData->spDest->eRtxTimerIsRunning == TRUE)
00694             StopT3RtxTimer(spCurrNodeData->spDest);
00695             }
00696           
00697           iAssocErrorCount = 0;
00698           
00699           /* We don't want to clear the error counter if it's
00700            * cleared already; otherwise, we'll unnecessarily
00701            * trigger a trace event.
00702            *
00703            * Also, the error counter is cleared by SACKed data
00704            * ONLY if the TSNs are not marked for timeout
00705            * retransmission and has not been gap acked
00706            * before. Without this condition, we can run into a
00707            * problem for failure detection. When a failure occurs,
00708            * some data may have made it through before the
00709            * failure, but the sacks got lost. When the sender
00710            * retransmits the first outstanding, the receiver will
00711            * sack all the data whose sacks got lost. We don't want
00712            * these sacks * to clear the error counter, or else
00713            * failover would take longer.
00714            */
00715           if(spCurrNodeData->spDest->iErrorCount != 0  &&
00716              spCurrNodeData->eMarkedForRtx != TIMEOUT_RTX)
00717             {
00718               DBG_PL(ProcessGapAckBlocks,
00719                  "clearing error counter for %p with tsn=%lu"), 
00720             spCurrNodeData->spDest, 
00721             spCurrNodeData->spChunk->uiTsn DBG_PR;
00722 
00723               spCurrNodeData->spDest->iErrorCount = 0; // clear errors
00724               tiErrorCount++;                       // ... and trace it!
00725               spCurrNodeData->spDest->eStatus = SCTP_DEST_STATUS_ACTIVE;
00726               if(spCurrNodeData->spDest == spPrimaryDest &&
00727              spNewTxDest != spPrimaryDest) 
00728             {
00729               DBG_PL(ProcessGapAckBlocks,
00730                  "primary recovered... "
00731                  "migrating back from %p to %p"),
00732                 spNewTxDest, spPrimaryDest DBG_PR;
00733               spNewTxDest = spPrimaryDest; // return to primary
00734             }
00735             }
00736 
00737           spCurrNodeData->eMarkedForRtx = NO_RTX; // unmark
00738         }
00739         }
00740       else if(spCurrNodeData->spChunk->uiTsn > uiEndTsn)
00741         {
00742           /* This point in the rtx buffer is already past the tsns which are
00743            * being acked by this gap ack block.  
00744            */
00745           usNumGapAcksProcessed++; 
00746 
00747           /* Did we process all the gap ack blocks?
00748            */
00749           if(usNumGapAcksProcessed != spSackChunk->usNumGapAckBlocks)
00750         {
00751           DBG_PL(ProcessGapAckBlocks, "jump to next gap ack block") 
00752             DBG_PR;
00753 
00754           spCurrGapAck 
00755             = ((SctpGapAckBlock_S *)
00756                (ucpSackChunk + sizeof(SctpSackChunk_S)
00757             + (usNumGapAcksProcessed * sizeof(SctpGapAckBlock_S))));
00758         }
00759 
00760           /* If this chunk was GapAcked before, then either the
00761            * receiver has renegged the chunk (which our simulation
00762            * doesn't do) or this SACK is arriving out of order.
00763            */
00764           if(spCurrNodeData->eGapAcked == TRUE)
00765         {
00766           DBG_PL(ProcessGapAckBlocks, 
00767              "out of order SACK? setting TSN=%d eGapAcked=FALSE"),
00768             spCurrNodeData->spChunk->uiTsn DBG_PR;
00769           spCurrNodeData->eGapAcked = FALSE;
00770           spCurrNodeData->spDest->iOutstandingBytes 
00771             += spCurrNodeData->spChunk->sHdr.usLength;
00772           
00773           /* section 6.3.2.R4 says that we should restart the
00774            * T3-rtx timer here if it isn't running already. In our
00775            * implementation, it isn't necessary since
00776            * ProcessSackChunk will restart the timer for any
00777            * destinations which have outstanding data and don't
00778            * have a timer running.
00779            */
00780         }
00781         }
00782     }
00783 
00784       /* By this time, either we have run through the entire send buffer or we
00785        * have run out of gap ack blocks. In the case that we have run out of gap
00786        * ack blocks before we finished running through the send buffer, we need
00787        * to mark the remaining chunks in the send buffer as eGapAcked=FALSE.
00788        * This final marking needs to be done, because we only trust gap ack info
00789        * from the last SACK. Otherwise, renegging (which we don't do) or out of
00790        * order SACKs would give the sender an incorrect view of the peer's rwnd.
00791        */
00792       for(; spCurrNode != NULL; spCurrNode = spCurrNode->spNext)
00793     {
00794       /* This chunk is NOT being acked and is missing at the receiver
00795        */
00796       spCurrNodeData = (SctpSendBufferNode_S *) spCurrNode->vpData;
00797 
00798       /* If this chunk was GapAcked before, then either the
00799        * receiver has renegged the chunk (which our simulation
00800        * doesn't do) or this SACK is arriving out of order.
00801        */
00802       if(spCurrNodeData->eGapAcked == TRUE)
00803         {
00804           DBG_PL(ProcessGapAckBlocks, 
00805              "out of order SACK? setting TSN=%d eGapAcked=FALSE"),
00806         spCurrNodeData->spChunk->uiTsn DBG_PR;
00807           spCurrNodeData->eGapAcked = FALSE;
00808           spCurrNodeData->spDest->iOutstandingBytes 
00809         += spCurrNodeData->spChunk->sHdr.usLength;
00810 
00811           /* section 6.3.2.R4 says that we should restart the T3-rtx
00812            * timer here if it isn't running already. In our
00813            * implementation, it isn't necessary since ProcessSackChunk
00814            * will restart the timer for any destinations which have
00815            * outstanding data and don't have a timer running.
00816            */
00817         }
00818     }
00819 
00820       DBG_PL(ProcessGapAckBlocks, "now incrementing missing reports...") DBG_PR;
00821       DBG_PL(ProcessGapAckBlocks, "uiHighestTsnNewlyAcked=%d"), 
00822          uiHighestTsnNewlyAcked DBG_PR;
00823 
00824       for(spCurrNode = sSendBuffer.spHead;
00825       spCurrNode != NULL; 
00826       spCurrNode = spCurrNode->spNext)
00827     {
00828       spCurrNodeData = (SctpSendBufferNode_S *) spCurrNode->vpData;
00829 
00830       DBG_PL(ProcessGapAckBlocks, "TSN=%d eGapAcked=%s"), 
00831         spCurrNodeData->spChunk->uiTsn,
00832         spCurrNodeData->eGapAcked ? "TRUE" : "FALSE"
00833         DBG_PR;
00834 
00835       if(spCurrNodeData->eGapAcked == FALSE)
00836         {
00837           // BEGIN -- MultipleFastRtx changes to this function  
00838 
00839           /* Caro's Multiple Fast Rtx Algorithm 
00840            * (in addition to existing HTNA algorithm)
00841            */
00842           if(( (spCurrNodeData->spChunk->uiTsn < uiHighestTsnNewlyAcked) ||
00843            (eNewCumAck == TRUE && 
00844             uiHighestTsnNewlyAcked <= uiRecover &&
00845             spCurrNodeData->spChunk->uiTsn < uiHighestTsnSacked) ) &&
00846          uiHighestTsnNewlyAcked > spCurrNodeData->uiFastRtxRecover)
00847         {
00848           spCurrNodeData->iNumMissingReports++;
00849           DBG_PL(ProcessGapAckBlocks, 
00850              "incrementing missing report for TSN=%d to %d"), 
00851             spCurrNodeData->spChunk->uiTsn,
00852             spCurrNodeData->iNumMissingReports
00853             DBG_PR;
00854 
00855           if(spCurrNodeData->iNumMissingReports == iFastRtxTrigger &&
00856              spCurrNodeData->eAdvancedAcked == FALSE)
00857             {
00858               if(spCurrNodeData->uiFastRtxRecover == 0)
00859             eFrInvoked = TRUE;
00860               else
00861             eMfrInvoked = TRUE;
00862 
00863               spCurrNodeData->iNumMissingReports = 0;
00864               MarkChunkForRtx(spCurrNodeData, FAST_RTX);
00865               eFastRtxNeeded = TRUE;
00866               spCurrNodeData->uiFastRtxRecover =uiHighestOutstandingTsn;
00867             
00868               DBG_PL(ProcessGapAckBlocks, 
00869                  "resetting missing report for TSN=%d to 0"), 
00870             spCurrNodeData->spChunk->uiTsn
00871             DBG_PR;
00872               DBG_PL(ProcessGapAckBlocks, 
00873                  "setting uiFastRtxRecover=%d"), 
00874             spCurrNodeData->uiFastRtxRecover
00875             DBG_PR;
00876             }
00877         }
00878           // END -- MultipleFastRtx changes to this function  
00879         }
00880     }
00881     }
00882 
00883   // BEGIN -- MultipleFastRtx changes to this function    
00884   if(eFrInvoked == TRUE)
00885     tiFrCount++;
00886   if(eMfrInvoked == TRUE)
00887     tiMfrCount++;
00888   // END -- MultipleFastRtx changes to this function    
00889 
00890   DBG_PL(ProcessGapAckBlocks, "eFastRtxNeeded=%s"), 
00891     eFastRtxNeeded ? "TRUE" : "FALSE" DBG_PR;
00892   DBG_X(ProcessGapAckBlocks);
00893   return eFastRtxNeeded;
00894 }

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