sctp-newreno.cc

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2001-2003 by the Protocol Engineering Lab, U of Delaware
00003  * All rights reserved.
00004  *
00005  * Armando L. Caro Jr. <acaro@@cis,udel,edu>
00006  * Keyur Shah <shah@@cis,udel,edu>
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted provided that the following conditions
00010  * are met:
00011  *
00012  * 1. Redistributions of source code must retain the above copyright
00013  *    notice, this list of conditions and the following disclaimer.
00014  *
00015  * 2. Redistributions in binary form must reproduce the above copyright
00016  *    notice, this list of conditions and the following disclaimer in the
00017  *    documentation and/or other materials provided with the distribution.
00018  *
00019  * 3. Neither the name of the University nor of the Laboratory may be used
00020  *    to endorse or promote products derived from this software without
00021  *    specific prior written permission.
00022  *
00023  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
00024  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00025  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00026  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
00027  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00028  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00029  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00030  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00031  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00032  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00033  * SUCH DAMAGE.
00034  */
00035 
00036 /* NewReno extension introduces a recover state variable per destination
00037  * similar to avoid multiple cwnd cuts in a single window (ie, round
00038  * trip). The concept is borrowed from TCP NewReno.
00039  */
00040 
00041 #ifndef lint
00042 static const char rcsid[] =
00043 "@(#) $Header: /nfs/jade/vint/CVSROOT/ns-2/sctp/sctp-newreno.cc,v 1.3 2005/07/13 03:51:27 tomh Exp $ (UD/PEL)";
00044 #endif
00045 
00046 #include "ip.h"
00047 #include "sctp-newreno.h"
00048 #include "flags.h"
00049 #include "random.h"
00050 #include "template.h"
00051 
00052 #include "sctpDebug.h"
00053 
00054 #ifdef DMALLOC
00055 #include "dmalloc.h"
00056 #endif
00057 
00058 #define MIN(x,y)    (((x)<(y))?(x):(y))
00059 #define MAX(x,y)    (((x)>(y))?(x):(y))
00060 
00061 static class NewRenoSctpClass : public TclClass 
00062 { 
00063 public:
00064   NewRenoSctpClass() : TclClass("Agent/SCTP/Newreno") {}
00065   TclObject* create(int, const char*const*) 
00066   {
00067     return (new NewRenoSctpAgent());
00068   }
00069 } classSctpNewReno;
00070 
00071 NewRenoSctpAgent::NewRenoSctpAgent() : SctpAgent()
00072 {
00073 }
00074 
00075 void NewRenoSctpAgent::delay_bind_init_all()
00076 {
00077   SctpAgent::delay_bind_init_all();
00078 }
00079 
00080 int NewRenoSctpAgent::delay_bind_dispatch(const char *cpVarName, 
00081                       const char *cpLocalName, 
00082                       TclObject *opTracer)
00083 {
00084   return SctpAgent::delay_bind_dispatch(cpVarName, cpLocalName, opTracer);
00085 }
00086 
00087 void NewRenoSctpAgent::OptionReset()
00088 {
00089   DBG_I(OptionReset);
00090   uiRecover = 0;
00091   DBG_X(OptionReset);
00092 }
00093 
00094 /* Go thru the send buffer deleting all chunks which have a tsn <= the 
00095  * tsn parameter passed in. We assume the chunks in the rtx list are ordered by 
00096  * their tsn value. In addtion, for each chunk deleted:
00097  *   1. we add the chunk length to # newly acked bytes and partial bytes acked
00098  *   2. we update round trip time if appropriate
00099  *   3. stop the timer if the chunk's destination timer is running
00100  */
00101 void NewRenoSctpAgent::SendBufferDequeueUpTo(u_int uiTsn)
00102 {
00103   DBG_I(SendBufferDequeueUpTo);
00104 
00105   Node_S *spDeleteNode = NULL;
00106   Node_S *spCurrNode = sSendBuffer.spHead;
00107   SctpSendBufferNode_S *spCurrNodeData = NULL;
00108 
00109   /* Only the first TSN that is being dequeued can be used to reset the
00110    * error cunter on a destination. Why? Well, suppose there are some
00111    * chunks that were gap acked before the primary had errors. Then the
00112    * gap gets filled with a retransmission using an alternate path. The
00113    * filled gap will cause the cum ack to move past the gap acked TSNs,
00114    * but does not mean that the they can reset the errors on the primary.
00115    */
00116   
00117   uiAssocErrorCount = 0;
00118 
00119   spCurrNodeData = (SctpSendBufferNode_S *) spCurrNode->vpData;
00120 
00121   /* trigger trace ONLY if it was previously NOT 0 */
00122   if(spCurrNodeData->spDest->uiErrorCount != 0)
00123     {
00124       spCurrNodeData->spDest->uiErrorCount = 0; // clear error counter
00125       tiErrorCount++;                          // ... and trace it too!
00126       spCurrNodeData->spDest->eStatus = SCTP_DEST_STATUS_ACTIVE;
00127       if(spCurrNodeData->spDest == spPrimaryDest &&
00128      spNewTxDest != spPrimaryDest) 
00129     {
00130       DBG_PL(SendBufferDequeueUpTo,
00131          "primary recovered... migrating back from %p to %p"),
00132         spNewTxDest, spPrimaryDest DBG_PR;
00133       spNewTxDest = spPrimaryDest; // return to primary
00134     }
00135     }
00136 
00137   while(spCurrNode != NULL &&
00138     ((SctpSendBufferNode_S*)spCurrNode->vpData)->spChunk->uiTsn <= uiTsn)
00139     {
00140       spCurrNodeData = (SctpSendBufferNode_S *) spCurrNode->vpData;
00141 
00142       /* Only count this chunk as newly acked and towards partial bytes
00143        * acked if it hasn't been gap acked or marked as ack'd due to rtx
00144        * limit.  
00145        */
00146       if((spCurrNodeData->eGapAcked == FALSE) &&
00147      (spCurrNodeData->eAdvancedAcked == FALSE) )
00148     {
00149       // BEGIN -- NewReno changes to this function
00150       uiHighestTsnNewlyAcked = spCurrNodeData->spChunk->uiTsn;
00151       // END -- NewReno changes to this function
00152 
00153       spCurrNodeData->spDest->uiNumNewlyAckedBytes 
00154         += spCurrNodeData->spChunk->sHdr.usLength;
00155 
00156       /* only add to partial bytes acked if we are in congestion
00157        * avoidance mode and if there was cwnd amount of data
00158        * outstanding on the destination (implementor's guide) 
00159        */
00160       if(spCurrNodeData->spDest->uiCwnd >spCurrNodeData->spDest->uiSsthresh &&
00161          ( spCurrNodeData->spDest->uiOutstandingBytes 
00162            >= spCurrNodeData->spDest->uiCwnd) )
00163         {
00164           spCurrNodeData->spDest->uiPartialBytesAcked 
00165         += spCurrNodeData->spChunk->sHdr.usLength;
00166         }
00167     }
00168 
00169 
00170       /* This is to ensure that Max.Burst is applied when a SACK
00171        * acknowledges a chunk which has been fast retransmitted. If it is
00172        * ineligible for fast rtx, that can only be because it was fast
00173        * rtxed or it timed out. If it timed out, a burst shouldn't be
00174        * possible, but shouldn't hurt either. The fast rtx case is what we
00175        * are really after. This is a proposed change to RFC2960 section
00176        * 7.2.4
00177        */
00178       if(spCurrNodeData->eIneligibleForFastRtx == TRUE)
00179     eApplyMaxBurst = TRUE;
00180         
00181       /* We update the RTT estimate if the following hold true:
00182        *   1. RTO pending flag is set (6.3.1.C4 measured once per round trip)
00183        *   2. Timestamp is set for this chunk (ie, we were measuring this chunk)
00184        *   3. This chunk has not been retransmitted
00185        *   4. This chunk has not been gap acked already 
00186        *   5. This chunk has not been advanced acked (pr-sctp: exhausted rtxs)
00187        */
00188       if(spCurrNodeData->spDest->eRtoPending == TRUE &&
00189      spCurrNodeData->dTxTimestamp > 0 &&
00190      spCurrNodeData->iNumTxs == 1 &&
00191      spCurrNodeData->eGapAcked == FALSE &&
00192      spCurrNodeData->eAdvancedAcked == FALSE) 
00193     {
00194       RttUpdate(spCurrNodeData->dTxTimestamp, spCurrNodeData->spDest);
00195       spCurrNodeData->spDest->eRtoPending = FALSE;
00196     }
00197 
00198       /* if there is a timer running on the chunk's destination, then stop it
00199        */
00200       if(spCurrNodeData->spDest->eRtxTimerIsRunning == TRUE)
00201     StopT3RtxTimer(spCurrNodeData->spDest);
00202 
00203       spDeleteNode = spCurrNode;
00204       spCurrNode = spCurrNode->spNext;
00205       DeleteNode(&sSendBuffer, spDeleteNode);
00206       spDeleteNode = NULL;
00207     }
00208 
00209   DBG_X(SendBufferDequeueUpTo);
00210 }
00211 
00212 /* This function is called as soon as we are done processing a SACK which 
00213  * notifies the need of a fast rtx. Following RFC2960, we pack as many chunks
00214  * as possible into one packet (PTMU restriction). The remaining marked packets
00215  * are sent as soon as cwnd allows.
00216  */
00217 void NewRenoSctpAgent::FastRtx()
00218 {
00219   DBG_I(FastRtx);
00220 
00221   Node_S *spCurrDestNode = NULL;
00222   SctpDest_S *spCurrDestData = NULL;
00223   Node_S *spCurrBuffNode = NULL;
00224   SctpSendBufferNode_S *spCurrBuffData = NULL;
00225   SctpSendBufferNode_S *spTailData = NULL;
00226 
00227   /* be sure we clear all the eCcApplied flags before using them!
00228    */
00229   for(spCurrDestNode = sDestList.spHead;
00230       spCurrDestNode != NULL;
00231       spCurrDestNode = spCurrDestNode->spNext)
00232     {
00233       spCurrDestData = (SctpDest_S *) spCurrDestNode->vpData;
00234       spCurrDestData->eCcApplied = FALSE;
00235     }
00236   
00237   /* go thru chunks marked for rtx and cut back their ssthresh, cwnd, and
00238    * partial bytes acked. make sure we only apply congestion control once
00239    * per destination and once per window (ie, round-trip).
00240    */
00241   for(spCurrBuffNode = sSendBuffer.spHead;
00242       spCurrBuffNode != NULL;
00243       spCurrBuffNode = spCurrBuffNode->spNext)
00244     {
00245       spCurrBuffData = (SctpSendBufferNode_S *) spCurrBuffNode->vpData;
00246 
00247 
00248       // BEGIN -- NewReno changes to this function
00249 
00250       /* If the chunk has been either marked for rtx or advanced ack, we want
00251        * to apply congestion control (assuming we didn't already).
00252        *
00253        * Why do we do it for advanced ack chunks? Well they were advanced ack'd
00254        * because they were lost. The ONLY reason we are not fast rtxing them is
00255        * because the chunk has run out of retransmissions (u-sctp). So we need
00256        * to still account for the fact they were lost... so apply congestion
00257        * control!
00258        */
00259       if((spCurrBuffData->eMarkedForRtx == TRUE ||
00260       spCurrBuffData->eAdvancedAcked == TRUE) &&
00261      spCurrBuffData->spDest->eCcApplied == FALSE &&
00262      spCurrBuffData->spChunk->uiTsn > uiRecover)
00263      
00264     { 
00265       /* section 7.2.3 of rfc2960 (w/ implementor's guide v.02) 
00266        */
00267       spCurrBuffData->spDest->uiSsthresh 
00268         = MAX(spCurrBuffData->spDest->uiCwnd/2, 2*uiMaxPayloadSize);
00269       spCurrBuffData->spDest->uiCwnd = spCurrBuffData->spDest->uiSsthresh;
00270       spCurrBuffData->spDest->uiPartialBytesAcked = 0; //reset
00271       tiCwnd++; // trigger changes for trace to pick up
00272       spCurrBuffData->spDest->eCcApplied = TRUE;
00273 
00274       /* Set the recover variable to avoid multiple cwnd cuts for losses
00275        * in the same window (ie, round-trip).
00276        */
00277       spTailData = (SctpSendBufferNode_S *) sSendBuffer.spTail->vpData;
00278       uiRecover = spTailData->spChunk->uiTsn;
00279     }
00280 
00281       // END -- NewReno changes to this function
00282     }
00283 
00284   /* possible that no chunks are pending retransmission since they could be 
00285    * advanced ack'd 
00286    */
00287   if(eMarkedChunksPending == TRUE)  
00288     RtxMarkedChunks(RTX_LIMIT_ONE_PACKET);
00289 
00290   DBG_X(FastRtx);
00291 }
00292 
00293 /* returns a boolean of whether a fast retransmit is necessary
00294  */
00295 Boolean_E NewRenoSctpAgent::ProcessGapAckBlocks(u_char *ucpSackChunk,
00296                      Boolean_E eNewCumAck)
00297 {
00298   DBG_I(ProcessGapAckBlocks);
00299 
00300   Boolean_E eFastRtxNeeded = FALSE;
00301   u_int uiHighestTsnSacked = uiHighestTsnNewlyAcked; // NewReno changes
00302   u_int uiStartTsn;
00303   u_int uiEndTsn;
00304   Node_S *spCurrNode = NULL;
00305   SctpSendBufferNode_S *spCurrNodeData = NULL;
00306   Node_S *spCurrDestNode = NULL;
00307   SctpDest_S *spCurrDestNodeData = NULL;
00308   Boolean_E eFirstOutstanding = FALSE;  
00309 
00310   SctpSackChunk_S *spSackChunk = (SctpSackChunk_S *) ucpSackChunk;
00311 
00312   u_short usNumGapAcksProcessed = 0;
00313   SctpGapAckBlock_S *spCurrGapAck 
00314     = (SctpGapAckBlock_S *) (ucpSackChunk + sizeof(SctpSackChunk_S));
00315 
00316   DBG_PL(ProcessGapAckBlocks,"CumAck=%d"), spSackChunk->uiCumAck DBG_PR;
00317 
00318   if(sSendBuffer.spHead == NULL) // do we have ANYTHING in the rtx buffer?
00319     {
00320       /* This COULD mean that this sack arrived late, and a previous one
00321        * already cum ack'd everything. ...so, what do we do? nothing??
00322        */
00323     }
00324   
00325   else // we do have chunks in the rtx buffer
00326     {
00327       /* make sure we clear all the eSeenFirstOutstanding flags before
00328        * using them!  
00329        */
00330       for(spCurrDestNode = sDestList.spHead;
00331       spCurrDestNode != NULL;
00332       spCurrDestNode = spCurrDestNode->spNext)
00333     {
00334       spCurrDestNodeData = (SctpDest_S *) spCurrDestNode->vpData;
00335       spCurrDestNodeData->eSeenFirstOutstanding = FALSE;
00336     }
00337 
00338       for(spCurrNode = sSendBuffer.spHead;
00339       (spCurrNode != NULL) &&
00340         (usNumGapAcksProcessed != spSackChunk->usNumGapAckBlocks);
00341       spCurrNode = spCurrNode->spNext)
00342     {
00343       spCurrNodeData = (SctpSendBufferNode_S *) spCurrNode->vpData;
00344 
00345       DBG_PL(ProcessGapAckBlocks, "eSeenFirstOutstanding=%s"),
00346         spCurrNodeData->spDest->eSeenFirstOutstanding ? "TRUE" : "FALSE"
00347         DBG_PR;
00348          
00349       /* is this chunk the first outstanding on its destination?
00350        */
00351       if(spCurrNodeData->spDest->eSeenFirstOutstanding == FALSE &&
00352          spCurrNodeData->eGapAcked == FALSE &&
00353          spCurrNodeData->eAdvancedAcked == FALSE)
00354         {
00355           /* yes, it is the first!
00356            */
00357           eFirstOutstanding = TRUE;
00358           spCurrNodeData->spDest->eSeenFirstOutstanding = TRUE;
00359         }
00360       else
00361         {
00362           /* nope, not the first...
00363            */
00364           eFirstOutstanding = FALSE;
00365         }
00366 
00367       DBG_PL(ProcessGapAckBlocks, "eFirstOutstanding=%s"),
00368         eFirstOutstanding ? "TRUE" : "FALSE" DBG_PR;
00369 
00370       DBG_PL(ProcessGapAckBlocks, "--> rtx list chunk begin") DBG_PR;
00371 
00372       DBG_PL(ProcessGapAckBlocks, "    TSN=%d"), 
00373         spCurrNodeData->spChunk->uiTsn 
00374         DBG_PR;
00375 
00376       DBG_PL(ProcessGapAckBlocks, "    %s=%s %s=%s"),
00377         "eGapAcked", 
00378         spCurrNodeData->eGapAcked ? "TRUE" : "FALSE",
00379         "eAddedToPartialBytesAcked",
00380         spCurrNodeData->eAddedToPartialBytesAcked ? "TRUE" : "FALSE" 
00381         DBG_PR;
00382 
00383       DBG_PL(ProcessGapAckBlocks, "    NumMissingReports=%d NumTxs=%d"),
00384         spCurrNodeData->iNumMissingReports, 
00385         spCurrNodeData->iNumTxs 
00386         DBG_PR;
00387 
00388       DBG_PL(ProcessGapAckBlocks, "<-- rtx list chunk end") DBG_PR;
00389       
00390       DBG_PL(ProcessGapAckBlocks,"GapAckBlock StartOffset=%d EndOffset=%d"),
00391         spCurrGapAck->usStartOffset, spCurrGapAck->usEndOffset DBG_PR;
00392 
00393       uiStartTsn = spSackChunk->uiCumAck + spCurrGapAck->usStartOffset;
00394       uiEndTsn = spSackChunk->uiCumAck + spCurrGapAck->usEndOffset;
00395       
00396       DBG_PL(ProcessGapAckBlocks, "GapAckBlock StartTsn=%d EndTsn=%d"),
00397         uiStartTsn, uiEndTsn DBG_PR;
00398 
00399       if(spCurrNodeData->spChunk->uiTsn < uiStartTsn)
00400         {
00401           /* This chunk is NOT being acked and is missing at the receiver
00402            */
00403 
00404           /* If this chunk was GapAcked before, then either the
00405            * receiver has renegged the chunk (which our simulation
00406            * doesn't do) or this SACK is arriving out of order.
00407            */
00408           if(spCurrNodeData->eGapAcked == TRUE)
00409         {
00410           DBG_PL(ProcessGapAckBlocks, 
00411              "out of order SACK? setting TSN=%d eGapAcked=FALSE"),
00412             spCurrNodeData->spChunk->uiTsn DBG_PR;
00413           spCurrNodeData->eGapAcked = FALSE;
00414           spCurrNodeData->spDest->uiOutstandingBytes 
00415             += spCurrNodeData->spChunk->sHdr.usLength;
00416 
00417           /* section 6.3.2.R4 says that we should restart the
00418            * T3-rtx timer here if it isn't running already. In our
00419            * implementation, it isn't necessary since
00420            * ProcessSackChunk will restart the timer for any
00421            * destinations which have outstanding data and don't
00422            * have a timer running.
00423            */
00424         }
00425         }
00426       else if((uiStartTsn <= spCurrNodeData->spChunk->uiTsn) && 
00427           (spCurrNodeData->spChunk->uiTsn <= uiEndTsn) )
00428         {
00429           /* This chunk is being acked via a gap ack block
00430            */
00431           DBG_PL(ProcessGapAckBlocks, "gap ack acks this chunk: %s%s"),
00432         "eGapAcked=",
00433         spCurrNodeData->eGapAcked ? "TRUE" : "FALSE" 
00434         DBG_PR;
00435 
00436           // BEGIN -- NewReno changes to this function
00437 
00438           /* modified HTNA algorithm... we need to know the highest TSN
00439            * sacked (even if it isn't new), so that when the sender
00440            * is in Fast Recovery, the outstanding tsns beyond the 
00441            * last sack tsn do not have their missing reports incremented
00442            */
00443           if(uiHighestTsnSacked < spCurrNodeData->spChunk->uiTsn)
00444         uiHighestTsnSacked = spCurrNodeData->spChunk->uiTsn;
00445 
00446           // END -- NewReno changes to this function
00447 
00448           if(spCurrNodeData->eGapAcked == FALSE)
00449         {
00450           DBG_PL(ProcessGapAckBlocks, "setting eGapAcked=TRUE") DBG_PR;
00451           spCurrNodeData->eGapAcked = TRUE;
00452           spCurrNodeData->eMarkedForRtx = FALSE; // unmark
00453 
00454           /* HTNA algorithm... we need to know the highest TSN
00455            * newly acked
00456            */
00457           if(uiHighestTsnNewlyAcked < spCurrNodeData->spChunk->uiTsn)
00458             uiHighestTsnNewlyAcked = spCurrNodeData->spChunk->uiTsn;
00459 
00460           if(spCurrNodeData->eAdvancedAcked == FALSE)
00461             {
00462               spCurrNodeData->spDest->uiNumNewlyAckedBytes 
00463             += spCurrNodeData->spChunk->sHdr.usLength;
00464             }
00465           
00466           /* only increment partial bytes acked if we are in
00467            * congestion avoidance mode, we have a new cum ack, and
00468            * we haven't already incremented it for this TSN
00469            */
00470           if(( spCurrNodeData->spDest->uiCwnd 
00471                > spCurrNodeData->spDest->uiSsthresh) &&
00472              eNewCumAck == TRUE &&
00473              spCurrNodeData->eAddedToPartialBytesAcked == FALSE)
00474             {
00475               DBG_PL(ProcessGapAckBlocks, 
00476                  "setting eAddedToPartiallyBytesAcked=TRUE") DBG_PR;
00477               
00478               spCurrNodeData->eAddedToPartialBytesAcked = TRUE; // set
00479 
00480               spCurrNodeData->spDest->uiPartialBytesAcked 
00481             += spCurrNodeData->spChunk->sHdr.usLength;
00482             }
00483 
00484           /* We update the RTT estimate if the following hold true:
00485            *   1. RTO pending flag is set (6.3.1.C4)
00486            *   2. Timestamp is set for this chunk 
00487            *   3. This chunk has not been retransmitted
00488            *   4. This chunk has not been gap acked already 
00489            *   5. This chunk has not been advanced acked (pr-sctp)
00490            */
00491           if(spCurrNodeData->spDest->eRtoPending == TRUE &&
00492              spCurrNodeData->dTxTimestamp > 0 &&
00493              spCurrNodeData->iNumTxs == 1 &&
00494              spCurrNodeData->eAdvancedAcked == FALSE) 
00495             {
00496               RttUpdate(spCurrNodeData->dTxTimestamp, 
00497                 spCurrNodeData->spDest);
00498               spCurrNodeData->spDest->eRtoPending = FALSE;
00499             }
00500 
00501           /* section 6.3.2.R3 - Stop the timer if this is the
00502            * first outstanding for this destination (note: it may
00503            * have already been stopped if there was a new cum
00504            * ack). If there are still outstanding bytes on this
00505            * destination, we'll restart the timer later in
00506            * ProcessSackChunk() 
00507            */
00508           if(eFirstOutstanding == TRUE 
00509              && spCurrNodeData->spDest->eRtxTimerIsRunning == TRUE)
00510             StopT3RtxTimer(spCurrNodeData->spDest);
00511           
00512           uiAssocErrorCount = 0;
00513           
00514           /* trigger trace ONLY if it was previously NOT 0
00515            */
00516           if(spCurrNodeData->spDest->uiErrorCount != 0)
00517             {
00518               spCurrNodeData->spDest->uiErrorCount = 0; // clear errors
00519               tiErrorCount++;                       // ... and trace it!
00520               spCurrNodeData->spDest->eStatus = SCTP_DEST_STATUS_ACTIVE;
00521               if(spCurrNodeData->spDest == spPrimaryDest &&
00522              spNewTxDest != spPrimaryDest) 
00523             {
00524               DBG_PL(ProcessGapAckBlocks,
00525                  "primary recovered... "
00526                  "migrating back from %p to %p"),
00527                 spNewTxDest, spPrimaryDest DBG_PR;
00528               spNewTxDest = spPrimaryDest; // return to primary
00529             }
00530             }
00531         }
00532         }
00533       else if(spCurrNodeData->spChunk->uiTsn > uiEndTsn)
00534         {
00535           /* This point in the rtx buffer is already past the tsns which are
00536            * being acked by this gap ack block.  
00537            */
00538           usNumGapAcksProcessed++; 
00539 
00540           /* Did we process all the gap ack blocks?
00541            */
00542           if(usNumGapAcksProcessed != spSackChunk->usNumGapAckBlocks)
00543         {
00544           DBG_PL(ProcessGapAckBlocks, "jump to next gap ack block") 
00545             DBG_PR;
00546 
00547           spCurrGapAck 
00548             = ((SctpGapAckBlock_S *)
00549                (ucpSackChunk + sizeof(SctpSackChunk_S)
00550             + (usNumGapAcksProcessed * sizeof(SctpGapAckBlock_S))));
00551         }
00552 
00553           /* If this chunk was GapAcked before, then either the
00554            * receiver has renegged the chunk (which our simulation
00555            * doesn't do) or this SACK is arriving out of order.
00556            */
00557           if(spCurrNodeData->eGapAcked == TRUE)
00558         {
00559           DBG_PL(ProcessGapAckBlocks, 
00560              "out of order SACK? setting TSN=%d eGapAcked=FALSE"),
00561             spCurrNodeData->spChunk->uiTsn DBG_PR;
00562           spCurrNodeData->eGapAcked = FALSE;
00563           spCurrNodeData->spDest->uiOutstandingBytes 
00564             += spCurrNodeData->spChunk->sHdr.usLength;
00565           
00566           /* section 6.3.2.R4 says that we should restart the
00567            * T3-rtx timer here if it isn't running already. In our
00568            * implementation, it isn't necessary since
00569            * ProcessSackChunk will restart the timer for any
00570            * destinations which have outstanding data and don't
00571            * have a timer running.
00572            */
00573         }
00574         }
00575     }
00576 
00577       /* By this time, either we have run through the entire send buffer or we
00578        * have run out of gap ack blocks. In the case that we have run out of gap
00579        * ack blocks before we finished running through the send buffer, we need
00580        * to mark the remaining chunks in the send buffer as eGapAcked=FALSE.
00581        * This final marking needs to be done, because we only trust gap ack info
00582        * from the last SACK. Otherwise, renegging (which we don't do) or out of
00583        * order SACKs would give the sender an incorrect view of the peer's rwnd.
00584        */
00585       for(; spCurrNode != NULL; spCurrNode = spCurrNode->spNext)
00586     {
00587       /* This chunk is NOT being acked and is missing at the receiver
00588        */
00589       spCurrNodeData = (SctpSendBufferNode_S *) spCurrNode->vpData;
00590 
00591       /* If this chunk was GapAcked before, then either the
00592        * receiver has renegged the chunk (which our simulation
00593        * doesn't do) or this SACK is arriving out of order.
00594        */
00595       if(spCurrNodeData->eGapAcked == TRUE)
00596       {
00597           DBG_PL(ProcessGapAckBlocks, 
00598              "out of order SACK? setting TSN=%d eGapAcked=FALSE"),
00599               spCurrNodeData->spChunk->uiTsn DBG_PR;
00600           spCurrNodeData->eGapAcked = FALSE;
00601           spCurrNodeData->spDest->uiOutstandingBytes 
00602               += spCurrNodeData->spChunk->sHdr.usLength;
00603           
00604           /* section 6.3.2.R4 says that we should restart the T3-rtx
00605            * timer here if it isn't running already. In our
00606            * implementation, it isn't necessary since ProcessSackChunk
00607            * will restart the timer for any destinations which have
00608            * outstanding data and don't have a timer running.
00609            */
00610       }
00611     }
00612 
00613       DBG_PL(ProcessGapAckBlocks, "now incrementing missing reports...") DBG_PR;
00614       DBG_PL(ProcessGapAckBlocks, "uiHighestTsnNewlyAcked=%d"), 
00615          uiHighestTsnNewlyAcked DBG_PR;
00616 
00617       for(spCurrNode = sSendBuffer.spHead;
00618       spCurrNode != NULL; 
00619       spCurrNode = spCurrNode->spNext)
00620     {
00621       spCurrNodeData = (SctpSendBufferNode_S *) spCurrNode->vpData;
00622 
00623       DBG_PL(ProcessGapAckBlocks, "TSN=%d eGapAcked=%s"), 
00624         spCurrNodeData->spChunk->uiTsn,
00625         spCurrNodeData->eGapAcked ? "TRUE" : "FALSE"
00626         DBG_PR;
00627 
00628       if(spCurrNodeData->eGapAcked == FALSE)
00629         {
00630           // BEGIN -- NewReno changes to this function
00631 
00632           /* HTNA (Highest TSN Newly Acked) algorithm from
00633            * implementer' guide
00634            *
00635            * NewReno modifies HTNA to also increment the missing reports 
00636            * when the sender is in Fast Recovery, the cum ack changes, and
00637            * the new cum ack is less than recover.
00638            */
00639           if( (spCurrNodeData->spChunk->uiTsn < uiHighestTsnNewlyAcked) ||
00640           (eNewCumAck == TRUE && 
00641            uiHighestTsnNewlyAcked <= uiRecover &&
00642            spCurrNodeData->spChunk->uiTsn < uiHighestTsnSacked))
00643         {
00644           spCurrNodeData->iNumMissingReports++;
00645           DBG_PL(ProcessGapAckBlocks, 
00646              "incrementing missing report for TSN=%d to %d"), 
00647             spCurrNodeData->spChunk->uiTsn,
00648             spCurrNodeData->iNumMissingReports
00649             DBG_PR;
00650 
00651           if(spCurrNodeData->iNumMissingReports >= FAST_RTX_TRIGGER &&
00652              spCurrNodeData->eIneligibleForFastRtx == FALSE &&
00653              spCurrNodeData->eAdvancedAcked == FALSE)
00654             {
00655               MarkChunkForRtx(spCurrNodeData);
00656               eFastRtxNeeded = TRUE;
00657               spCurrNodeData->eIneligibleForFastRtx = TRUE;
00658               DBG_PL(ProcessGapAckBlocks, 
00659                  "setting eFastRtxNeeded = TRUE") DBG_PR;
00660             }
00661         }
00662           // END -- NewReno changes to this function
00663         }
00664     }
00665     }
00666 
00667   DBG_PL(ProcessGapAckBlocks, "eFastRtxNeeded=%s"), 
00668     eFastRtxNeeded ? "TRUE" : "FALSE" DBG_PR;
00669   DBG_X(ProcessGapAckBlocks);
00670   return eFastRtxNeeded;
00671 }
00672 
00673 void NewRenoSctpAgent::ProcessSackChunk(u_char *ucpSackChunk)
00674 {
00675   DBG_I(ProcessSackChunk);
00676 
00677   SctpSackChunk_S *spSackChunk = (SctpSackChunk_S *) ucpSackChunk;
00678 
00679   DBG_PL(ProcessSackChunk, "cum=%d arwnd=%d #gapacks=%d #duptsns=%d"),
00680     spSackChunk->uiCumAck, spSackChunk->uiArwnd, 
00681     spSackChunk->usNumGapAckBlocks, spSackChunk->usNumDupTsns 
00682     DBG_PR;
00683 
00684   Boolean_E eFastRtxNeeded = FALSE;
00685   Boolean_E eNewCumAck = FALSE;
00686   Node_S *spCurrDestNode = NULL;
00687   SctpDest_S *spCurrDestNodeData = NULL;
00688   u_int uiTotalOutstanding = 0;
00689   int i = 0;
00690 
00691   /* make sure we clear all the uiNumNewlyAckedBytes before using them!
00692    */
00693   for(spCurrDestNode = sDestList.spHead;
00694       spCurrDestNode != NULL;
00695       spCurrDestNode = spCurrDestNode->spNext)
00696     {
00697       spCurrDestNodeData = (SctpDest_S *) spCurrDestNode->vpData;
00698       spCurrDestNodeData->uiNumNewlyAckedBytes = 0;
00699       spCurrDestNodeData->eSeenFirstOutstanding = FALSE;
00700     }
00701 
00702   if(spSackChunk->uiCumAck < uiCumAckPoint) 
00703     {
00704       /* this cumAck's a previously cumAck'd tsn (ie, it's out of order!)
00705        * ...so ignore!
00706        */
00707       DBG_PL(ProcessSackChunk, "ignoring out of order sack!") DBG_PR;
00708       DBG_X(ProcessSackChunk);
00709       return;
00710     }
00711   else if(spSackChunk->uiCumAck > uiCumAckPoint)
00712     {
00713       eNewCumAck = TRUE; // incomding SACK's cum ack advances the cum ack point
00714       SendBufferDequeueUpTo(spSackChunk->uiCumAck);
00715       uiCumAckPoint = spSackChunk->uiCumAck; // Advance the cumAck pointer
00716     }
00717 
00718   if(spSackChunk->usNumGapAckBlocks != 0) // are there any gaps??
00719     {
00720       eFastRtxNeeded = ProcessGapAckBlocks(ucpSackChunk, eNewCumAck);
00721     } 
00722 
00723   for(spCurrDestNode = sDestList.spHead;
00724       spCurrDestNode != NULL;
00725       spCurrDestNode = spCurrDestNode->spNext)
00726     {
00727       spCurrDestNodeData = (SctpDest_S *) spCurrDestNode->vpData;
00728 
00729       // BEGIN -- NewReno changes to this function
00730 
00731       /* Only adjust cwnd if:
00732        *    1. sack advanced the cum ack point 
00733        *    2. this destination has newly acked bytes
00734        *    3. the cum ack is at or beyond the recovery window
00735        *
00736        * Also, we MUST adjust our congestion window BEFORE we update the
00737        * number of outstanding bytes to reflect the newly acked bytes in
00738        * received SACK.
00739        */
00740       if(eNewCumAck == TRUE &&
00741      spCurrDestNodeData->uiNumNewlyAckedBytes > 0 &&
00742      spSackChunk->uiCumAck >= uiRecover)
00743     {
00744       AdjustCwnd(spCurrDestNodeData);
00745     }
00746 
00747       // END -- NewReno changes to this function
00748 
00749       /* The number of outstanding bytes is reduced by how many bytes this sack 
00750        * acknowledges.
00751        */
00752       if(spCurrDestNodeData->uiNumNewlyAckedBytes <=
00753      spCurrDestNodeData->uiOutstandingBytes)
00754     {
00755       spCurrDestNodeData->uiOutstandingBytes 
00756         -= spCurrDestNodeData->uiNumNewlyAckedBytes;
00757     }
00758       else
00759     spCurrDestNodeData->uiOutstandingBytes = 0;
00760 
00761       DBG_PL(ProcessSackChunk,"Dest #%d (%d:%d) (%p): outstanding=%d, cwnd=%d"),
00762     ++i, spCurrDestNodeData->iNsAddr, spCurrDestNodeData->iNsPort,
00763     spCurrDestNodeData, spCurrDestNodeData->uiOutstandingBytes, 
00764     spCurrDestNodeData->uiCwnd DBG_PR;
00765 
00766       if(spCurrDestNodeData->uiOutstandingBytes == 0)
00767     {
00768       /* All outstanding data has been acked
00769        */
00770       spCurrDestNodeData->uiPartialBytesAcked = 0;  // section 7.2.2
00771 
00772       /* section 6.3.2.R2
00773        */
00774       if(spCurrDestNodeData->eRtxTimerIsRunning == TRUE)
00775         {
00776           DBG_PL(ProcessSackChunk, "Dest #%d (%p): stopping timer"), 
00777         i, spCurrDestNodeData DBG_PR;
00778           StopT3RtxTimer(spCurrDestNodeData);
00779         }
00780     }
00781 
00782       /* section 6.3.2.R3 - Restart timers for destinations that have
00783        * acknowledged their first outstanding (ie, no timer running) and
00784        * still have outstanding data in flight.  
00785        */
00786       if(spCurrDestNodeData->uiOutstandingBytes > 0 &&
00787      spCurrDestNodeData->eRtxTimerIsRunning == FALSE)
00788     {
00789       StartT3RtxTimer(spCurrDestNodeData);
00790     }
00791     }
00792 
00793   DBG_F(ProcessSackChunk, DumpSendBuffer());
00794 
00795   AdvancePeerAckPoint();
00796 
00797   if(eFastRtxNeeded == TRUE)  // section 7.2.4
00798     FastRtx();
00799 
00800   /* Let's see if after process this sack, there are still any chunks
00801    * pending... If so, rtx all allowed by cwnd.
00802    */
00803   else if( (eMarkedChunksPending = AnyMarkedChunks()) == TRUE)
00804     {
00805       /* section 6.1.C) When the time comes for the sender to
00806        * transmit, before sending new DATA chunks, the sender MUST
00807        * first transmit any outstanding DATA chunks which are marked
00808        * for retransmission (limited by the current cwnd).  
00809        */
00810       RtxMarkedChunks(RTX_LIMIT_CWND);
00811     }
00812 
00813   /* (6.2.1.D.ii) Adjust PeerRwnd based on total oustanding bytes on all
00814    * destinations. We need to this adjustment after any
00815    * retransmissions. Otherwise the sender's view of the peer rwnd will be
00816    * off, because the number outstanding increases again once a marked
00817    * chunk gets retransmitted (when marked, outstanding is decreased).
00818    */
00819   uiTotalOutstanding = TotalOutstanding();
00820   if(uiTotalOutstanding <= spSackChunk->uiArwnd)
00821     uiPeerRwnd = (spSackChunk->uiArwnd  - uiTotalOutstanding);
00822   else
00823     uiPeerRwnd = 0;
00824   
00825   DBG_PL(ProcessSackChunk, "uiPeerRwnd=%d, uiArwnd=%d"), uiPeerRwnd, 
00826     spSackChunk->uiArwnd DBG_PR;
00827   DBG_X(ProcessSackChunk);
00828 }
00829 
00830 

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