sctp-mfrTimestamp.cc
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:29k
源码类别:

通讯编程

开发平台:

Visual C++

  1. /*
  2.  * Copyright (c) 2006-2007 by the Protocol Engineering Lab, U of Delaware
  3.  * All rights reserved.
  4.  * 
  5.  * Protocol Engineering Lab web page : http://pel.cis.udel.edu/
  6.  *
  7.  * Paul D. Amer        <amer@@cis,udel,edu>
  8.  * Armando L. Caro Jr. <acaro@@cis,udel,edu>
  9.  *
  10.  * Redistribution and use in source and binary forms, with or without
  11.  * modification, are permitted provided that the following conditions
  12.  * are met:
  13.  *
  14.  * 1. Redistributions of source code must retain the above copyright
  15.  *    notice, this list of conditions and the following disclaimer.
  16.  *
  17.  * 2. Redistributions in binary form must reproduce the above copyright
  18.  *    notice, this list of conditions and the following disclaimer in the
  19.  *    documentation and/or other materials provided with the distribution.
  20.  *
  21.  * 3. Neither the name of the University nor of the Laboratory may be used
  22.  *    to endorse or promote products derived from this software without
  23.  *    specific prior written permission.
  24.  *
  25.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  26.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  27.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  28.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  29.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  30.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  31.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  32.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  33.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  34.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  35.  * SUCH DAMAGE.
  36.  */
  37. /* MultipleFastRtx extension implements the Caro Multiple Fast Retransmit
  38.  * Algorithm. Caro's Algorithm introduces a fastRtxRecover state variable
  39.  * per TSN in the send buffer. Any time a TSN is retransmitted, its
  40.  * fastRtxRecover is set to the highest TSN outstanding at the time of
  41.  * retransmit. That way, only missing reports triggered by TSNs beyond
  42.  * fastRtxRecover may trigger yet another fast retransmit.
  43.  */
  44. #ifndef lint
  45. static const char rcsid[] =
  46. "@(#) $Header: /cvsroot/nsnam/ns-2/sctp/sctp-mfrTimestamp.cc,v 1.2 2007/06/17 21:44:41 tom_henderson Exp $ (UD/PEL)";
  47. #endif
  48. #include "ip.h"
  49. #include "sctp-mfrTimestamp.h"
  50. #include "flags.h"
  51. #include "random.h"
  52. #include "template.h"
  53. #include "sctpDebug.h"
  54. #ifdef DMALLOC
  55. #include "dmalloc.h"
  56. #endif
  57. #define MIN(x,y) (((x)<(y))?(x):(y))
  58. #define MAX(x,y) (((x)>(y))?(x):(y))
  59. static class MfrTimestampSctpClass : public TclClass 
  60. public:
  61.   MfrTimestampSctpClass() : TclClass("Agent/SCTP/MfrTimestamp") {}
  62.   TclObject* create(int, const char*const*) 
  63.   {
  64.     return (new MfrTimestampSctpAgent());
  65.   }
  66. } classSctpMfrTimestamp;
  67. MfrTimestampSctpAgent::MfrTimestampSctpAgent() : SctpAgent()
  68. {
  69. }
  70. void MfrTimestampSctpAgent::delay_bind_init_all()
  71. {
  72.   delay_bind_init_one("mfrCount_");
  73.   TimestampSctpAgent::delay_bind_init_all();
  74. }
  75. int MfrTimestampSctpAgent::delay_bind_dispatch(const char *cpVarName, 
  76.   const char *cpLocalName, 
  77.   TclObject *opTracer)
  78. {
  79.   if(delay_bind(cpVarName, cpLocalName, "mfrCount_", &tiMfrCount, opTracer))
  80.     return TCL_OK;
  81.   return TimestampSctpAgent::delay_bind_dispatch(cpVarName, 
  82.  cpLocalName, opTracer);
  83. }
  84. void MfrTimestampSctpAgent::TraceAll()
  85. {
  86.   char cpOutString[500];
  87.   Node_S *spCurrNode = NULL;
  88.   SctpDest_S *spCurrDest = NULL;
  89.   double dCurrTime = Scheduler::instance().clock();
  90.   for(spCurrNode = sDestList.spHead;
  91.       spCurrNode != NULL;
  92.       spCurrNode = spCurrNode->spNext)
  93.     {
  94.       spCurrDest = (SctpDest_S *) spCurrNode->vpData;
  95.       SetSource(spCurrDest); // gives us the correct source addr & port
  96.       sprintf(cpOutString,
  97.       "time: %-8.5f  "
  98.       "saddr: %-2d sport: %-2d daddr: %-2d dport: %-2d "
  99.       "cwnd: %d pba: %d out: %d ssthresh: %d peerRwnd: %d "
  100.       "rto: %-6.3f srtt: %-6.3f rttvar: %-6.3f "
  101.       "assocErrors: %d pathErrors: %d dstatus: %s isPrimary: %s "
  102.       "frCount: %d mfrCount: %d timeoutCount: %d rcdCount: %dn",
  103.       dCurrTime,
  104.       addr(), port(), spCurrDest->iNsAddr, spCurrDest->iNsPort,
  105.       spCurrDest->iCwnd, spCurrDest->iPartialBytesAcked, 
  106.       spCurrDest->iOutstandingBytes, spCurrDest->iSsthresh, 
  107.       uiPeerRwnd,
  108.       spCurrDest->dRto, spCurrDest->dSrtt, 
  109.       spCurrDest->dRttVar,
  110.       iAssocErrorCount,
  111.       spCurrDest->iErrorCount,
  112.       spCurrDest->eStatus ? "ACTIVE" : "INACTIVE",
  113.       (spCurrDest == spPrimaryDest) ? "TRUE" : "FALSE",
  114.       int(tiFrCount),
  115.       // BEGIN -- MultipleFastRtx changes to this function  
  116.       int(tiMfrCount),
  117.               // END -- MultipleFastRtx changes to this function  
  118.       spCurrDest->iTimeoutCount,
  119.       spCurrDest->iRcdCount);
  120.       if(channel_)
  121. (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
  122.     }
  123.   sprintf(cpOutString, "n");
  124.   if(channel_)
  125.     (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
  126. }
  127. void MfrTimestampSctpAgent::TraceVar(const char* cpVar)
  128. {
  129.   char cpOutString[500];
  130.   Node_S *spCurrNode = NULL;
  131.   SctpDest_S *spCurrDest = NULL;
  132.   double dCurrTime = Scheduler::instance().clock();
  133.   if(!strcmp(cpVar, "cwnd_"))
  134.     for(spCurrNode = sDestList.spHead;
  135. spCurrNode != NULL;
  136. spCurrNode = spCurrNode->spNext)
  137.       {
  138. spCurrDest = (SctpDest_S *) spCurrNode->vpData;
  139. SetSource(spCurrDest); // gives us the correct source addr & port
  140. sprintf(cpOutString,
  141. "time: %-8.5f  "
  142. "saddr: %-2d sport: %-2d daddr: %-2d dport: %-2d "
  143. "cwnd: %d pba: %d out: %d ssthresh: %d peerRwnd: %dn",
  144. dCurrTime, 
  145. addr(), port(), 
  146. spCurrDest->iNsAddr, spCurrDest->iNsPort,
  147. spCurrDest->iCwnd, spCurrDest->iPartialBytesAcked, 
  148. spCurrDest->iOutstandingBytes, spCurrDest->iSsthresh, 
  149. uiPeerRwnd);
  150. if(channel_)
  151.   (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
  152.       }
  153.   else if(!strcmp(cpVar, "rto_"))
  154.     for(spCurrNode = sDestList.spHead;
  155. spCurrNode != NULL;
  156. spCurrNode = spCurrNode->spNext)
  157.       {
  158. spCurrDest = (SctpDest_S *) spCurrNode->vpData;
  159. SetSource(spCurrDest); // gives us the correct source addr & port
  160. sprintf(cpOutString,
  161. "time: %-8.5f  "
  162. "saddr: %-2d sport: %-2d daddr: %-2d dport: %-2d "
  163. "rto: %-6.3f srtt: %-6.3f rttvar: %-6.3fn",
  164. dCurrTime, 
  165. addr(), port(), 
  166. spCurrDest->iNsAddr, spCurrDest->iNsPort,
  167. spCurrDest->dRto, spCurrDest->dSrtt, 
  168. spCurrDest->dRttVar);
  169. if(channel_)
  170.   (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
  171.       }
  172.   else if(!strcmp(cpVar, "errorCount_"))
  173.     for(spCurrNode = sDestList.spHead;
  174. spCurrNode != NULL;
  175. spCurrNode = spCurrNode->spNext)
  176.       {
  177. spCurrDest = (SctpDest_S *) spCurrNode->vpData;
  178. SetSource(spCurrDest); // gives us the correct source addr & port
  179. sprintf(cpOutString, 
  180. "time: %-8.5f  "
  181. "saddr: %-2d sport: %-2d daddr: %-2d dport: %-2d "
  182. "assocErrors: %d pathErrors: %d dstatus: %s isPrimary: %sn",
  183. dCurrTime, 
  184. addr(), port(), 
  185. spCurrDest->iNsAddr, spCurrDest->iNsPort,
  186. iAssocErrorCount,
  187. spCurrDest->iErrorCount,
  188. spCurrDest->eStatus ? "ACTIVE" : "INACTIVE",
  189. (spCurrDest == spPrimaryDest) ? "TRUE" : "FALSE");
  190. if(channel_)
  191.   (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
  192.       }
  193.   else if(!strcmp(cpVar, "frCount_"))
  194.     {
  195.       sprintf(cpOutString, 
  196.       "time: %-8.5f  "
  197.       "frCount: %dn",
  198.       dCurrTime, 
  199.       int(*((TracedInt*) cpVar)) );
  200.       if(channel_)
  201. (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
  202.     }
  203.   // BEGIN -- MultipleFastRtx changes to this function  
  204.   else if(!strcmp(cpVar, "mfrCount_"))
  205.     {
  206.       sprintf(cpOutString, 
  207.       "time: %-8.5f  "
  208.       "mfrCount: %dn",
  209.       dCurrTime, 
  210.       int(*((TracedInt*) cpVar)) );
  211.       if(channel_)
  212. (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
  213.     }
  214.   // END -- MultipleFastRtx changes to this function  
  215.   else if(!strcmp(cpVar, "timeoutCount_"))
  216.     {
  217.     for(spCurrNode = sDestList.spHead;
  218. spCurrNode != NULL;
  219. spCurrNode = spCurrNode->spNext)
  220.       {
  221. spCurrDest = (SctpDest_S *) spCurrNode->vpData;
  222. SetSource(spCurrDest); // gives us the correct source addr & port
  223. sprintf(cpOutString, 
  224. "time: %-8.5f  "
  225. "saddr: %-2d sport: %-2d daddr: %-2d dport: %-2d "
  226. "timeoutCount: %dn",
  227. dCurrTime, 
  228. addr(), port(), 
  229. spCurrDest->iNsAddr, spCurrDest->iNsPort,
  230. spCurrDest->iTimeoutCount);
  231. if(channel_)
  232.   (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
  233.       }
  234.     }
  235.   else if(!strcmp(cpVar, "rcdCount_"))
  236.     {
  237.     for(spCurrNode = sDestList.spHead;
  238. spCurrNode != NULL;
  239. spCurrNode = spCurrNode->spNext)
  240.       {
  241. spCurrDest = (SctpDest_S *) spCurrNode->vpData;
  242. SetSource(spCurrDest); // gives us the correct source addr & port
  243. sprintf(cpOutString, 
  244. "time: %-8.5f  "
  245. "saddr: %-2d sport: %-2d daddr: %-2d dport: %-2d "
  246. "rcdCount: %dn",
  247. dCurrTime, 
  248. addr(), port(), 
  249. spCurrDest->iNsAddr, spCurrDest->iNsPort,
  250. spCurrDest->iRcdCount);
  251. if(channel_)
  252.   (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
  253.       }
  254.     }
  255.   else
  256.     {
  257.       sprintf(cpOutString,
  258.       "time: %-8.5f  "
  259.       "saddr: %-2d sport: %-2d daddr: %-2d dport: %-2d %s: %sn",
  260.       dCurrTime, addr(), port(), daddr(), dport(),
  261.       cpVar, "ERROR (unepected trace variable)"); 
  262.       if(channel_)
  263. (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
  264.     }
  265.   sprintf(cpOutString, "n");
  266.   if(channel_)
  267.     (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
  268. }
  269. void MfrTimestampSctpAgent::AddToSendBuffer(SctpDataChunkHdr_S *spChunk, 
  270. int iChunkSize,
  271. u_int uiReliability,
  272. SctpDest_S *spDest)
  273. {
  274.   DBG_I(AddToSendBuffer);
  275.   DBG_PL(AddToSendBuffer, "spDest=%p  iChunkSize=%d"), 
  276.     spDest, iChunkSize DBG_PR;
  277.   Node_S *spNewNode = new Node_S;
  278.   spNewNode->eType = NODE_TYPE_SEND_BUFFER;
  279.   spNewNode->vpData = new SctpSendBufferNode_S;
  280.   SctpSendBufferNode_S * spNewNodeData 
  281.     = (SctpSendBufferNode_S *) spNewNode->vpData;
  282.   /* This can NOT simply be a 'new SctpDataChunkHdr_S', because we need to
  283.    * allocate the space for the ENTIRE data chunk and not just the data
  284.    * chunk header.  
  285.    */
  286.   spNewNodeData->spChunk = (SctpDataChunkHdr_S *) new u_char[iChunkSize];
  287.   memcpy(spNewNodeData->spChunk, spChunk, iChunkSize);
  288.   spNewNodeData->eAdvancedAcked = FALSE;
  289.   spNewNodeData->eGapAcked = FALSE;
  290.   spNewNodeData->eAddedToPartialBytesAcked = FALSE;
  291.   spNewNodeData->iNumMissingReports = 0;
  292.   spNewNodeData->iUnrelRtxLimit = uiReliability;
  293.   spNewNodeData->eMarkedForRtx = NO_RTX;
  294.   spNewNodeData->eIneligibleForFastRtx = FALSE;
  295.   spNewNodeData->iNumTxs = 1;
  296.   spNewNodeData->spDest = spDest;
  297.   // BEGIN -- MultipleFastRtx changes to this function  
  298.   spNewNodeData->uiFastRtxRecover = 0;
  299.   // END -- MultipleFastRtx changes to this function  
  300.   // BEGIN -- Timestamp changes to this function
  301.   spNewNodeData->dTxTimestamp = Scheduler::instance().clock();
  302.   // END -- Timestamp changes to this function
  303.   InsertNode(&sSendBuffer, sSendBuffer.spTail, spNewNode, NULL);
  304.   DBG_X(AddToSendBuffer);
  305. }
  306. /* Go thru the send buffer deleting all chunks which have a tsn <= the 
  307.  * tsn parameter passed in. We assume the chunks in the rtx list are ordered by 
  308.  * their tsn value. In addtion, for each chunk deleted:
  309.  *   1. we add the chunk length to # newly acked bytes and partial bytes acked
  310.  *   2. we update round trip time if appropriate
  311.  *   3. stop the timer if the chunk's destination timer is running
  312.  */
  313. void MfrTimestampSctpAgent::SendBufferDequeueUpTo(u_int uiTsn)
  314. {
  315.   DBG_I(SendBufferDequeueUpTo);
  316.   Node_S *spDeleteNode = NULL;
  317.   Node_S *spCurrNode = sSendBuffer.spHead;
  318.   SctpSendBufferNode_S *spCurrNodeData = NULL;
  319.   iAssocErrorCount = 0;
  320.   while(spCurrNode != NULL &&
  321. ((SctpSendBufferNode_S*)spCurrNode->vpData)->spChunk->uiTsn <= uiTsn)
  322.     {
  323.       spCurrNodeData = (SctpSendBufferNode_S *) spCurrNode->vpData;
  324.       /* Only count this chunk as newly acked and towards partial bytes
  325.        * acked if it hasn't been gap acked or marked as ack'd due to rtx
  326.        * limit.  
  327.        */
  328.       if((spCurrNodeData->eGapAcked == FALSE) &&
  329.  (spCurrNodeData->eAdvancedAcked == FALSE) )
  330. {
  331.   uiHighestTsnNewlyAcked = spCurrNodeData->spChunk->uiTsn;
  332.   spCurrNodeData->spDest->iNumNewlyAckedBytes 
  333.     += spCurrNodeData->spChunk->sHdr.usLength;
  334.   /* only add to partial bytes acked if we are in congestion
  335.    * avoidance mode and if there was cwnd amount of data
  336.    * outstanding on the destination (implementor's guide) 
  337.    */
  338.   if(spCurrNodeData->spDest->iCwnd >spCurrNodeData->spDest->iSsthresh &&
  339.      ( spCurrNodeData->spDest->iOutstandingBytes 
  340.        >= spCurrNodeData->spDest->iCwnd) )
  341.     {
  342.       spCurrNodeData->spDest->iPartialBytesAcked 
  343. += spCurrNodeData->spChunk->sHdr.usLength;
  344.     }
  345. }
  346.       // BEGIN -- MultipleFastRtx changes to this function  
  347.       /* This is to ensure that Max.Burst is applied when a SACK
  348.        * acknowledges a chunk which has been fast retransmitted. (If the
  349.        * fast rtx recover variable is set, that can only be because it was
  350.        * fast rtxed.) This is a proposed change to RFC2960 section 7.2.4
  351.        */
  352.       if(spCurrNodeData->uiFastRtxRecover > 0)
  353. eApplyMaxBurst = TRUE;
  354.       // END -- MultipleFastRtx changes to this function  
  355.     
  356.       // BEGIN -- Timestamp changes to this function
  357.       /* We update the RTT estimate if the following hold true:
  358.        *   1. Timestamp set for this chunk matches echoed timestamp
  359.        *   2. This chunk has not been gap acked already 
  360.        *   3. This chunk has not been advanced acked (pr-sctp: exhausted rtxs)
  361.        */
  362.       if(fInTimestampEcho == (float) spCurrNodeData->dTxTimestamp &&
  363.  spCurrNodeData->eGapAcked == FALSE &&
  364.  spCurrNodeData->eAdvancedAcked == FALSE) 
  365. {
  366.   RttUpdate(spCurrNodeData->dTxTimestamp, spCurrNodeData->spDest);
  367. }
  368.       // END -- Timestamp changes to this function
  369.       /* if there is a timer running on the chunk's destination, then stop it
  370.        */
  371.       if(spCurrNodeData->spDest->eRtxTimerIsRunning == TRUE)
  372. StopT3RtxTimer(spCurrNodeData->spDest);
  373.       /* We don't want to clear the error counter if it's cleared already;
  374.        * otherwise, we'll unnecessarily trigger a trace event.
  375.        *
  376.        * Also, the error counter is cleared by SACKed data ONLY if the
  377.        * TSNs are not marked for timeout retransmission and has not been
  378.        * gap acked before. Without this condition, we can run into a
  379.        * problem for failure detection. When a failure occurs, some data
  380.        * may have made it through before the failure, but the sacks got
  381.        * lost. When the sender retransmits the first outstanding, the
  382.        * receiver will sack all the data whose sacks got lost. We don't
  383.        * want these sacks to clear the error counter, or else failover
  384.        * would take longer.
  385.        */
  386.       if(spCurrNodeData->spDest->iErrorCount != 0 &&
  387.  spCurrNodeData->eMarkedForRtx != TIMEOUT_RTX &&
  388.  spCurrNodeData->eGapAcked == FALSE)
  389. {
  390.   DBG_PL(SendBufferDequeueUpTo, 
  391.  "clearing error counter for %p with tsn=%lu"), 
  392.     spCurrNodeData->spDest, spCurrNodeData->spChunk->uiTsn DBG_PR;
  393.   spCurrNodeData->spDest->iErrorCount = 0; // clear error counter
  394.   tiErrorCount++;                          // ... and trace it too!
  395.   spCurrNodeData->spDest->eStatus = SCTP_DEST_STATUS_ACTIVE;
  396.   if(spCurrNodeData->spDest == spPrimaryDest &&
  397.      spNewTxDest != spPrimaryDest) 
  398.     {
  399.       DBG_PL(SendBufferDequeueUpTo,
  400.      "primary recovered... migrating back from %p to %p"),
  401. spNewTxDest, spPrimaryDest DBG_PR;
  402.       spNewTxDest = spPrimaryDest; // return to primary
  403.     }
  404. }
  405.       spDeleteNode = spCurrNode;
  406.       spCurrNode = spCurrNode->spNext;
  407.       DeleteNode(&sSendBuffer, spDeleteNode);
  408.       spDeleteNode = NULL;
  409.     }
  410.   DBG_X(SendBufferDequeueUpTo);
  411. }
  412. /* returns a boolean of whether a fast retransmit is necessary
  413.  */
  414. Boolean_E MfrTimestampSctpAgent::ProcessGapAckBlocks(u_char *ucpSackChunk,
  415.  Boolean_E eNewCumAck)
  416. {
  417.   DBG_I(ProcessGapAckBlocks);
  418.   Boolean_E eFastRtxNeeded = FALSE;
  419.   u_int uiHighestTsnSacked = uiHighestTsnNewlyAcked;
  420.   u_int uiStartTsn;
  421.   u_int uiEndTsn;
  422.   Node_S *spCurrNode = NULL;
  423.   SctpSendBufferNode_S *spCurrNodeData = NULL;
  424.   Node_S *spCurrDestNode = NULL;
  425.   SctpDest_S *spCurrDestNodeData = NULL;
  426.   SctpSackChunk_S *spSackChunk = (SctpSackChunk_S *) ucpSackChunk;
  427.   u_short usNumGapAcksProcessed = 0;
  428.   SctpGapAckBlock_S *spCurrGapAck 
  429.     = (SctpGapAckBlock_S *) (ucpSackChunk + sizeof(SctpSackChunk_S));
  430.   // BEGIN -- MultipleFastRtx changes to this function    
  431.   u_int uiHighestOutstandingTsn = GetHighestOutstandingTsn();
  432.   /* We want to track any time a regular FR and a MFR is invoked
  433.    */
  434.   Boolean_E eFrInvoked = FALSE;
  435.   Boolean_E eMfrInvoked = FALSE;
  436.   // END -- MultipleFastRtx changes to this function    
  437.   DBG_PL(ProcessGapAckBlocks,"CumAck=%d"), spSackChunk->uiCumAck DBG_PR;
  438.   if(sSendBuffer.spHead == NULL) // do we have ANYTHING in the rtx buffer?
  439.     {
  440.       /* This COULD mean that this sack arrived late, and a previous one
  441.        * already cum ack'd everything. ...so, what do we do? nothing??
  442.        */
  443.     }
  444.   
  445.   else // we do have chunks in the rtx buffer
  446.     {
  447.       /* make sure we clear all the spFirstOutstanding pointers before
  448.        * using them!  
  449.        */
  450.       for(spCurrDestNode = sDestList.spHead;
  451.   spCurrDestNode != NULL;
  452.   spCurrDestNode = spCurrDestNode->spNext)
  453. {
  454.   spCurrDestNodeData = (SctpDest_S *) spCurrDestNode->vpData;
  455.   spCurrDestNodeData->spFirstOutstanding = NULL;
  456. }
  457.       for(spCurrNode = sSendBuffer.spHead;
  458.   (spCurrNode != NULL) &&
  459.     (usNumGapAcksProcessed != spSackChunk->usNumGapAckBlocks);
  460.   spCurrNode = spCurrNode->spNext)
  461. {
  462.   spCurrNodeData = (SctpSendBufferNode_S *) spCurrNode->vpData;
  463.   /* is this chunk the first outstanding on its destination?
  464.    */
  465.   if(spCurrNodeData->spDest->spFirstOutstanding == NULL &&
  466.      spCurrNodeData->eGapAcked == FALSE &&
  467.      spCurrNodeData->eAdvancedAcked == FALSE)
  468.     {
  469.       /* yes, it is the first!
  470.        */
  471.       spCurrNodeData->spDest->spFirstOutstanding = spCurrNodeData;
  472.     }
  473.   DBG_PL(ProcessGapAckBlocks, "--> rtx list chunk begin") DBG_PR;
  474.   DBG_PL(ProcessGapAckBlocks, "    TSN=%d"), 
  475.     spCurrNodeData->spChunk->uiTsn 
  476.     DBG_PR;
  477.   DBG_PL(ProcessGapAckBlocks, "    %s=%s %s=%s"),
  478.     "eGapAcked", 
  479.     spCurrNodeData->eGapAcked ? "TRUE" : "FALSE",
  480.     "eAddedToPartialBytesAcked",
  481.     spCurrNodeData->eAddedToPartialBytesAcked ? "TRUE" : "FALSE" 
  482.     DBG_PR;
  483.   DBG_PL(ProcessGapAckBlocks, "    NumMissingReports=%d NumTxs=%d"),
  484.     spCurrNodeData->iNumMissingReports, 
  485.     spCurrNodeData->iNumTxs 
  486.     DBG_PR;
  487.   DBG_PL(ProcessGapAckBlocks, "<-- rtx list chunk end") DBG_PR;
  488.   
  489.   DBG_PL(ProcessGapAckBlocks,"GapAckBlock StartOffset=%d EndOffset=%d"),
  490.     spCurrGapAck->usStartOffset, spCurrGapAck->usEndOffset DBG_PR;
  491.   uiStartTsn = spSackChunk->uiCumAck + spCurrGapAck->usStartOffset;
  492.   uiEndTsn = spSackChunk->uiCumAck + spCurrGapAck->usEndOffset;
  493.   
  494.   DBG_PL(ProcessGapAckBlocks, "GapAckBlock StartTsn=%d EndTsn=%d"),
  495.     uiStartTsn, uiEndTsn DBG_PR;
  496.   if(spCurrNodeData->spChunk->uiTsn < uiStartTsn)
  497.     {
  498.       /* This chunk is NOT being acked and is missing at the receiver
  499.        */
  500.       /* If this chunk was GapAcked before, then either the
  501.        * receiver has renegged the chunk (which our simulation
  502.        * doesn't do) or this SACK is arriving out of order.
  503.        */
  504.       if(spCurrNodeData->eGapAcked == TRUE)
  505. {
  506.   DBG_PL(ProcessGapAckBlocks, 
  507.  "out of order SACK? setting TSN=%d eGapAcked=FALSE"),
  508.     spCurrNodeData->spChunk->uiTsn DBG_PR;
  509.   spCurrNodeData->eGapAcked = FALSE;
  510.   spCurrNodeData->spDest->iOutstandingBytes 
  511.     += spCurrNodeData->spChunk->sHdr.usLength;
  512.   /* section 6.3.2.R4 says that we should restart the
  513.    * T3-rtx timer here if it isn't running already. In our
  514.    * implementation, it isn't necessary since
  515.    * ProcessSackChunk will restart the timer for any
  516.    * destinations which have outstanding data and don't
  517.    * have a timer running.
  518.    */
  519. }
  520.     }
  521.   else if((uiStartTsn <= spCurrNodeData->spChunk->uiTsn) && 
  522.   (spCurrNodeData->spChunk->uiTsn <= uiEndTsn) )
  523.     {
  524.       /* This chunk is being acked via a gap ack block
  525.        */
  526.       DBG_PL(ProcessGapAckBlocks, "gap ack acks this chunk: %s%s"),
  527. "eGapAcked=",
  528. spCurrNodeData->eGapAcked ? "TRUE" : "FALSE" 
  529. DBG_PR;
  530.       /* HTNA algorithm... we need to know the highest TSN sacked
  531.        * (even if it isn't new), so that when the sender is in
  532.        * Fast Recovery, the outstanding tsns beyond the last sack
  533.        * tsn do not have their missing reports incremented
  534.        */
  535.       if(uiHighestTsnSacked < spCurrNodeData->spChunk->uiTsn)
  536. uiHighestTsnSacked = spCurrNodeData->spChunk->uiTsn;
  537.       if(spCurrNodeData->eGapAcked == FALSE)
  538. {
  539.   DBG_PL(ProcessGapAckBlocks, "setting eGapAcked=TRUE") DBG_PR;
  540.   spCurrNodeData->eGapAcked = TRUE;
  541.   /* HTNA algorithm... we need to know the highest TSN
  542.    * newly acked
  543.    */
  544.   if(uiHighestTsnNewlyAcked < spCurrNodeData->spChunk->uiTsn)
  545.     uiHighestTsnNewlyAcked = spCurrNodeData->spChunk->uiTsn;
  546.   if(spCurrNodeData->eAdvancedAcked == FALSE)
  547.     {
  548.       spCurrNodeData->spDest->iNumNewlyAckedBytes 
  549. += spCurrNodeData->spChunk->sHdr.usLength;
  550.     }
  551.   /* only increment partial bytes acked if we are in
  552.    * congestion avoidance mode, we have a new cum ack, and
  553.    * we haven't already incremented it for this sack
  554.    */
  555.   if(( spCurrNodeData->spDest->iCwnd 
  556.        > spCurrNodeData->spDest->iSsthresh) &&
  557.      eNewCumAck == TRUE &&
  558.      spCurrNodeData->eAddedToPartialBytesAcked == FALSE)
  559.     {
  560.       DBG_PL(ProcessGapAckBlocks, 
  561.      "setting eAddedToPartiallyBytesAcked=TRUE") DBG_PR;
  562.       spCurrNodeData->eAddedToPartialBytesAcked = TRUE; // set
  563.       spCurrNodeData->spDest->iPartialBytesAcked 
  564. += spCurrNodeData->spChunk->sHdr.usLength;
  565.     }
  566.   // BEGIN -- Timestamp changes to this function
  567.   /* We update the RTT estimate if the following hold true:
  568.    *   1. Timestamp set for this chunk matches echoed timestamp
  569.    *   2. This chunk has not been gap acked already 
  570.    *   3. This chunk has not been advanced acked
  571.    */
  572.   if(fInTimestampEcho == (float) spCurrNodeData->dTxTimestamp &&
  573.      spCurrNodeData->eAdvancedAcked == FALSE) 
  574.     {
  575.       RttUpdate(spCurrNodeData->dTxTimestamp, 
  576. spCurrNodeData->spDest);
  577.     }
  578.   // END -- Timestamp changes to this function
  579.   /* section 6.3.2.R3 - Stop the timer if this is the
  580.    * first outstanding for this destination (note: it may
  581.    * have already been stopped if there was a new cum
  582.    * ack). If there are still outstanding bytes on this
  583.    * destination, we'll restart the timer later in
  584.    * ProcessSackChunk() 
  585.    */
  586.   if(spCurrNodeData->spDest->spFirstOutstanding 
  587.      == spCurrNodeData)
  588.     
  589.     {
  590.       if(spCurrNodeData->spDest->eRtxTimerIsRunning == TRUE)
  591. StopT3RtxTimer(spCurrNodeData->spDest);
  592.     }
  593.   
  594.   iAssocErrorCount = 0;
  595.   
  596.   /* We don't want to clear the error counter if it's
  597.    * cleared already; otherwise, we'll unnecessarily
  598.    * trigger a trace event.
  599.    *
  600.    * Also, the error counter is cleared by SACKed data
  601.    * ONLY if the TSNs are not marked for timeout
  602.    * retransmission and has not been gap acked
  603.    * before. Without this condition, we can run into a
  604.    * problem for failure detection. When a failure occurs,
  605.    * some data may have made it through before the
  606.    * failure, but the sacks got lost. When the sender
  607.    * retransmits the first outstanding, the receiver will
  608.    * sack all the data whose sacks got lost. We don't want
  609.    * these sacks * to clear the error counter, or else
  610.    * failover would take longer.
  611.    */
  612.   if(spCurrNodeData->spDest->iErrorCount != 0  &&
  613.      spCurrNodeData->eMarkedForRtx != TIMEOUT_RTX)
  614.     {
  615.       DBG_PL(ProcessGapAckBlocks,
  616.      "clearing error counter for %p with tsn=%lu"), 
  617. spCurrNodeData->spDest, 
  618. spCurrNodeData->spChunk->uiTsn DBG_PR;
  619.       spCurrNodeData->spDest->iErrorCount = 0; // clear errors
  620.       tiErrorCount++;                       // ... and trace it!
  621.       spCurrNodeData->spDest->eStatus = SCTP_DEST_STATUS_ACTIVE;
  622.       if(spCurrNodeData->spDest == spPrimaryDest &&
  623.  spNewTxDest != spPrimaryDest) 
  624. {
  625.   DBG_PL(ProcessGapAckBlocks,
  626.  "primary recovered... "
  627.  "migrating back from %p to %p"),
  628.     spNewTxDest, spPrimaryDest DBG_PR;
  629.   spNewTxDest = spPrimaryDest; // return to primary
  630. }
  631.     }
  632.   spCurrNodeData->eMarkedForRtx = NO_RTX; // unmark
  633. }
  634.     }
  635.   else if(spCurrNodeData->spChunk->uiTsn > uiEndTsn)
  636.     {
  637.       /* This point in the rtx buffer is already past the tsns which are
  638.        * being acked by this gap ack block.  
  639.        */
  640.       usNumGapAcksProcessed++; 
  641.       /* Did we process all the gap ack blocks?
  642.        */
  643.       if(usNumGapAcksProcessed != spSackChunk->usNumGapAckBlocks)
  644. {
  645.   DBG_PL(ProcessGapAckBlocks, "jump to next gap ack block") 
  646.     DBG_PR;
  647.   spCurrGapAck 
  648.     = ((SctpGapAckBlock_S *)
  649.        (ucpSackChunk + sizeof(SctpSackChunk_S)
  650. + (usNumGapAcksProcessed * sizeof(SctpGapAckBlock_S))));
  651. }
  652.       /* If this chunk was GapAcked before, then either the
  653.        * receiver has renegged the chunk (which our simulation
  654.        * doesn't do) or this SACK is arriving out of order.
  655.        */
  656.       if(spCurrNodeData->eGapAcked == TRUE)
  657. {
  658.   DBG_PL(ProcessGapAckBlocks, 
  659.  "out of order SACK? setting TSN=%d eGapAcked=FALSE"),
  660.     spCurrNodeData->spChunk->uiTsn DBG_PR;
  661.   spCurrNodeData->eGapAcked = FALSE;
  662.   spCurrNodeData->spDest->iOutstandingBytes 
  663.     += spCurrNodeData->spChunk->sHdr.usLength;
  664.   
  665.   /* section 6.3.2.R4 says that we should restart the
  666.    * T3-rtx timer here if it isn't running already. In our
  667.    * implementation, it isn't necessary since
  668.    * ProcessSackChunk will restart the timer for any
  669.    * destinations which have outstanding data and don't
  670.    * have a timer running.
  671.    */
  672. }
  673.     }
  674. }
  675.       /* By this time, either we have run through the entire send buffer or we
  676.        * have run out of gap ack blocks. In the case that we have run out of gap
  677.        * ack blocks before we finished running through the send buffer, we need
  678.        * to mark the remaining chunks in the send buffer as eGapAcked=FALSE.
  679.        * This final marking needs to be done, because we only trust gap ack info
  680.        * from the last SACK. Otherwise, renegging (which we don't do) or out of
  681.        * order SACKs would give the sender an incorrect view of the peer's rwnd.
  682.        */
  683.       for(; spCurrNode != NULL; spCurrNode = spCurrNode->spNext)
  684. {
  685.   /* This chunk is NOT being acked and is missing at the receiver
  686.    */
  687.   spCurrNodeData = (SctpSendBufferNode_S *) spCurrNode->vpData;
  688.   /* If this chunk was GapAcked before, then either the
  689.    * receiver has renegged the chunk (which our simulation
  690.    * doesn't do) or this SACK is arriving out of order.
  691.    */
  692.   if(spCurrNodeData->eGapAcked == TRUE)
  693.     {
  694.       DBG_PL(ProcessGapAckBlocks, 
  695.      "out of order SACK? setting TSN=%d eGapAcked=FALSE"),
  696. spCurrNodeData->spChunk->uiTsn DBG_PR;
  697.       spCurrNodeData->eGapAcked = FALSE;
  698.       spCurrNodeData->spDest->iOutstandingBytes 
  699. += spCurrNodeData->spChunk->sHdr.usLength;
  700.       /* section 6.3.2.R4 says that we should restart the T3-rtx
  701.        * timer here if it isn't running already. In our
  702.        * implementation, it isn't necessary since ProcessSackChunk
  703.        * will restart the timer for any destinations which have
  704.        * outstanding data and don't have a timer running.
  705.        */
  706.     }
  707. }
  708.       DBG_PL(ProcessGapAckBlocks, "now incrementing missing reports...") DBG_PR;
  709.       DBG_PL(ProcessGapAckBlocks, "uiHighestTsnNewlyAcked=%d"), 
  710.      uiHighestTsnNewlyAcked DBG_PR;
  711.       for(spCurrNode = sSendBuffer.spHead;
  712.   spCurrNode != NULL; 
  713.   spCurrNode = spCurrNode->spNext)
  714. {
  715.   spCurrNodeData = (SctpSendBufferNode_S *) spCurrNode->vpData;
  716.   DBG_PL(ProcessGapAckBlocks, "TSN=%d eGapAcked=%s"), 
  717.     spCurrNodeData->spChunk->uiTsn,
  718.     spCurrNodeData->eGapAcked ? "TRUE" : "FALSE"
  719.     DBG_PR;
  720.   if(spCurrNodeData->eGapAcked == FALSE)
  721.     {
  722.       // BEGIN -- MultipleFastRtx changes to this function  
  723.       /* Caro's Multiple Fast Rtx Algorithm 
  724.        * (in addition to existing HTNA algorithm)
  725.        */
  726.       if(( (spCurrNodeData->spChunk->uiTsn < uiHighestTsnNewlyAcked) ||
  727.    (eNewCumAck == TRUE && 
  728.     uiHighestTsnNewlyAcked <= uiRecover &&
  729.     spCurrNodeData->spChunk->uiTsn < uiHighestTsnSacked) ) &&
  730.  uiHighestTsnNewlyAcked > spCurrNodeData->uiFastRtxRecover)
  731. {
  732.   spCurrNodeData->iNumMissingReports++;
  733.   DBG_PL(ProcessGapAckBlocks, 
  734.  "incrementing missing report for TSN=%d to %d"), 
  735.     spCurrNodeData->spChunk->uiTsn,
  736.     spCurrNodeData->iNumMissingReports
  737.     DBG_PR;
  738.   if(spCurrNodeData->iNumMissingReports == iFastRtxTrigger &&
  739.      spCurrNodeData->eAdvancedAcked == FALSE)
  740.     {
  741.       if(spCurrNodeData->uiFastRtxRecover == 0)
  742. eFrInvoked = TRUE;
  743.       else
  744. eMfrInvoked = TRUE;
  745.       spCurrNodeData->iNumMissingReports = 0;
  746.       MarkChunkForRtx(spCurrNodeData, FAST_RTX);
  747.       eFastRtxNeeded = TRUE;
  748.       spCurrNodeData->uiFastRtxRecover =uiHighestOutstandingTsn;
  749.       DBG_PL(ProcessGapAckBlocks, 
  750.      "resetting missing report for TSN=%d to 0"), 
  751. spCurrNodeData->spChunk->uiTsn
  752. DBG_PR;
  753.       DBG_PL(ProcessGapAckBlocks, 
  754.      "setting uiFastRtxRecover=%d"), 
  755. spCurrNodeData->uiFastRtxRecover
  756. DBG_PR;
  757.     }
  758. }
  759.       // END -- MultipleFastRtx changes to this function  
  760.     }
  761. }
  762.     }
  763.   // BEGIN -- MultipleFastRtx changes to this function    
  764.   if(eFrInvoked == TRUE)
  765.     tiFrCount++;
  766.   if(eMfrInvoked == TRUE)
  767.     tiMfrCount++;
  768.   // END -- MultipleFastRtx changes to this function    
  769.   DBG_PL(ProcessGapAckBlocks, "eFastRtxNeeded=%s"), 
  770.     eFastRtxNeeded ? "TRUE" : "FALSE" DBG_PR;
  771.   DBG_X(ProcessGapAckBlocks);
  772.   return eFastRtxNeeded;
  773. }