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

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