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

通讯编程

开发平台:

Visual C++

  1.   spCurrNodeData->eGapAcked = TRUE;
  2.   /****** Begin CMT Change ******/
  3.   /* CMT-PF: if the gapacked chunk was new data sent to a
  4.    * PF dest, then, mark destination to ACTIVE; else, 
  5.    * ambiguity in deciding whether gap-ack was for the
  6.    * original transmission or retransmission.
  7.    */
  8.    if ((spCurrNodeData->spDest->eStatus == 
  9. SCTP_DEST_STATUS_POSSIBLY_FAILED) &&
  10. (spCurrNodeData->iNumTxs == 1))
  11.      {
  12.        spCurrNodeData->spDest->eStatus=SCTP_DEST_STATUS_ACTIVE;
  13.        spCurrNodeData->spDest->iErrorCount = 0; //clr err ctr
  14.        tiErrorCount++;     // ... and trace it too!
  15.        DBG_PL(ProcessGapAckBlocks, 
  16.       "Dest:%p, New data gapacked. Mark from PF to Active"),
  17.  spCurrNodeData->spDest DBG_PR;
  18.      }
  19.   /* CMT CUC algo: If newly acked TSN passes
  20.    * pseudo-cumack, we have a new pseudo-cumack!
  21.    * Increment cwnd accordingly.
  22.    */
  23.   if (spCurrNodeData->spChunk->uiTsn == 
  24.       spCurrNodeData->spDest->uiExpectedPseudoCum) {
  25.     spCurrNodeData->spDest->eNewPseudoCum = TRUE;
  26.     spCurrNodeData->spDest->eFindExpectedPseudoCum = TRUE;
  27.   }
  28.   if (spCurrNodeData->spChunk->uiTsn == 
  29.       spCurrNodeData->spDest->uiExpectedRtxPseudoCum) {
  30.     spCurrNodeData->spDest->eNewPseudoCum = TRUE;
  31.     spCurrNodeData->spDest->eFindExpectedRtxPseudoCum = TRUE;
  32.   }
  33.   /* SFR algo: Mark eSawNewAck for newly acked destination and
  34.    * update highest TSN seen in current SACK (per destination) 
  35.    * if appropriate. uiHighestTsnInSackForDest is used for 
  36.    * HTNA also. We do not need uiHighestTsnNewlyAcked with 
  37.    * the SFR algo, hence commented out.
  38.    */
  39.   spCurrNodeData->spDest->eSawNewAck = TRUE;
  40.   if (spCurrNodeData->spDest->uiHighestTsnInSackForDest < 
  41.       spCurrNodeData->spChunk->uiTsn)
  42.     {
  43.       spCurrNodeData->spDest->uiHighestTsnInSackForDest =
  44. spCurrNodeData->spChunk->uiTsn;
  45.     }
  46.   
  47.   /* CMT DAC algo: Update lowest TSN seen in current SACK
  48.    * (per destination) if appropriate. The lowest TSN is
  49.    * initialized to zero, and since we process cum and gap
  50.    * TSNs in TSN order, the first value this variable is
  51.    * set to will continue to be the lowest TSN
  52.    * acked. uiLowestTsnInSackForDest is used for missing
  53.    * report increments with delayed SACKs in CMT.
  54.    */
  55.   if (spCurrNodeData->spDest->uiLowestTsnInSackForDest == 0)
  56.     {
  57.       spCurrNodeData->spDest->uiLowestTsnInSackForDest =
  58. spCurrNodeData->spChunk->uiTsn;
  59.     }
  60.   /****** End CMT Change ******/
  61.   
  62.   /* HTNA algorithm... we need to know the highest TSN
  63.    * newly acked
  64.    */
  65.   if(uiHighestTsnNewlyAcked < spCurrNodeData->spChunk->uiTsn)
  66.     uiHighestTsnNewlyAcked = spCurrNodeData->spChunk->uiTsn;
  67.   if(spCurrNodeData->eAdvancedAcked == FALSE)
  68.     {
  69.       spCurrNodeData->spDest->iNumNewlyAckedBytes 
  70. += spCurrNodeData->spChunk->sHdr.usLength;
  71.     }
  72.   
  73.   /****** Begin CMT Change ******/
  74.   /* only increment partial bytes acked if we are in
  75.    * congestion avoidance mode, we have a new cum ack, and
  76.    * we haven't already incremented it for this TSN
  77.    */
  78.   /* CMT change: If new pseudo-cumack, then adjust cwnd. 
  79.    * This check looks a little ugly, so look closely.
  80.    */
  81.   /* JRI-TODO: Remove Check for CmtCwnd
  82.    */
  83.   if((spCurrNodeData->spDest->iCwnd 
  84.       > spCurrNodeData->spDest->iSsthresh) &&
  85.      ((eUseCmtCwnd)&&(spCurrNodeData->spDest->eNewPseudoCum) ||
  86.       (!eUseCmtCwnd) && (eNewCumAck)) &&
  87.      (spCurrNodeData->eAddedToPartialBytesAcked == FALSE))
  88.   /****** End CMT Change ******/
  89.     {
  90.       DBG_PL(ProcessGapAckBlocks, 
  91.      "setting eAddedToPartiallyBytesAcked=TRUE")DBG_PR;
  92.       
  93.       spCurrNodeData->eAddedToPartialBytesAcked = TRUE; // set
  94.       spCurrNodeData->spDest->iPartialBytesAcked 
  95. += spCurrNodeData->spChunk->sHdr.usLength;
  96.     }
  97.   /****** Begin CMT Change ******/
  98.   /* We update the RTT estimate if the following hold true:
  99.    *   1. RTO pending flag is set (6.3.1.C4)
  100.    *   2. RTT is being measured for this chunk 
  101.    *   3. This chunk has not been retransmitted
  102.    *   4. This chunk has not been gap acked already 
  103.    *   5. This chunk has not been advanced acked (pr-sctp)
  104.    */
  105.   if(spCurrNodeData->spDest->eRtoPending == TRUE &&
  106.      spCurrNodeData->eMeasuringRtt == TRUE &&
  107.      spCurrNodeData->iNumTxs == 1 &&
  108.      spCurrNodeData->eAdvancedAcked == FALSE) 
  109.     /****** End CMT Change ******/
  110.     {
  111.       /* If the chunk is marked for timeout rtx, then the
  112.        * sender is an ambigious state. Were the sacks lost
  113.        * or was there a failure?  Since we don't clear the
  114.        * error counter below, we also don't update the
  115.        * RTT. This could be a problem for late arriving
  116.        * SACKs.
  117.        */
  118.       if(spCurrNodeData->eMarkedForRtx != TIMEOUT_RTX)
  119. RttUpdate(spCurrNodeData->dTxTimestamp, 
  120.   spCurrNodeData->spDest);
  121.       spCurrNodeData->spDest->eRtoPending = FALSE;
  122.     }
  123.   /* section 6.3.2.R3 - Stop the timer if this is the
  124.    * first outstanding for this destination (note: it may
  125.    * have already been stopped if there was a new cum
  126.    * ack). If there are still outstanding bytes on this
  127.    * destination, we'll restart the timer later in
  128.    * ProcessSackChunk() 
  129.    */
  130.   if(spCurrNodeData->spDest->spFirstOutstanding 
  131.      == spCurrNodeData)
  132.     {
  133.       if(spCurrNodeData->spDest->eRtxTimerIsRunning == TRUE)
  134. StopT3RtxTimer(spCurrNodeData->spDest);
  135.     }
  136.   iAssocErrorCount = 0;
  137.   
  138.   /* We don't want to clear the error counter if it's
  139.    * cleared already; otherwise, we'll unnecessarily
  140.    * trigger a trace event.
  141.    *
  142.    * Also, the error counter is cleared by SACKed data
  143.    * ONLY if the TSNs are not marked for timeout
  144.    * retransmission and has not been gap acked
  145.    * before. Without this condition, we can run into a
  146.    * problem for failure detection. When a failure occurs,
  147.    * some data may have made it through before the
  148.    * failure, but the sacks got lost. When the sender
  149.    * retransmits the first outstanding, the receiver will
  150.    * sack all the data whose sacks got lost. We don't want
  151.    * these sacks to clear the error counter, or else
  152.    * failover would take longer.
  153.    */
  154.   if((spCurrNodeData->spDest->iErrorCount != 0) &&
  155.      (spCurrNodeData->eMarkedForRtx != TIMEOUT_RTX))
  156.     {
  157.       DBG_PL(ProcessGapAckBlocks,
  158.      "clearing error counter for %p with tsn=%lu"), 
  159. spCurrNodeData->spDest, 
  160. spCurrNodeData->spChunk->uiTsn DBG_PR;
  161.       spCurrNodeData->spDest->iErrorCount = 0; // clear errors
  162.       tiErrorCount++;                      // ... and trace it!
  163.       spCurrNodeData->spDest->eStatus =SCTP_DEST_STATUS_ACTIVE;
  164.       if(spCurrNodeData->spDest == spPrimaryDest &&
  165.  spNewTxDest != spPrimaryDest) 
  166. {
  167.   DBG_PL(ProcessGapAckBlocks,
  168.  "primary recovered... "
  169.  "migrating back from %p to %p"),
  170.     spNewTxDest, spPrimaryDest DBG_PR;
  171.   spNewTxDest = spPrimaryDest; // return to primary
  172. }
  173.     }
  174.   spCurrNodeData->eMarkedForRtx = NO_RTX; // unmark
  175. }
  176.     }
  177.   else if(spCurrNodeData->spChunk->uiTsn > uiEndTsn)
  178.     {
  179.       /* This point in the rtx buffer is already past the tsns which 
  180.        * are being acked by this gap ack block.  
  181.        */
  182.       usNumGapAcksProcessed++; 
  183.       /* Did we process all the gap ack blocks?
  184.        */
  185.       if(usNumGapAcksProcessed != spSackChunk->usNumGapAckBlocks)
  186. {
  187.   DBG_PL(ProcessGapAckBlocks, "jump to next gap ack block") 
  188.     DBG_PR;
  189.   spCurrGapAck 
  190.     = ((SctpGapAckBlock_S *)
  191.        (ucpSackChunk + sizeof(SctpSackChunk_S)
  192. +(usNumGapAcksProcessed * sizeof(SctpGapAckBlock_S))));
  193. }
  194.       /* If this chunk was GapAcked before, then either the
  195.        * receiver has renegged the chunk (which our simulation
  196.        * doesn't do) or this SACK is arriving out of order.
  197.        */
  198.       if(spCurrNodeData->eGapAcked == TRUE)
  199. {
  200.   DBG_PL(ProcessGapAckBlocks, 
  201.  "out of order SACK? setting TSN=%d eGapAcked=FALSE"),
  202.     spCurrNodeData->spChunk->uiTsn DBG_PR;
  203.   spCurrNodeData->eGapAcked = FALSE;
  204.   spCurrNodeData->spDest->iOutstandingBytes 
  205.     += spCurrNodeData->spChunk->sHdr.usLength;
  206.   
  207.   /* section 6.3.2.R4 says that we should restart the
  208.    * T3-rtx timer here if it isn't running already. In our
  209.    * implementation, it isn't necessary since
  210.    * ProcessSackChunk will restart the timer for any
  211.    * destinations which have outstanding data and don't
  212.    * have a timer running.
  213.    */
  214. }
  215.     }
  216. }
  217.       /* By this time, either we have run through the entire send buffer or we
  218.        * have run out of gap ack blocks.In the case that we have run out of gap
  219.        * ack blocks before we finished running through the send buffer, we need
  220.        * to mark the remaining chunks in the send buffer as eGapAcked=FALSE.
  221.        * This final marking needs to be done,because we only trust gap ack info
  222.        * from the last SACK. Otherwise, renegging (which we don't do) or out of
  223.        * order SACKs would give the sender an incorrect view of the peer's 
  224.        * rwnd.
  225.        */
  226.       for(; spCurrNode != NULL; spCurrNode = spCurrNode->spNext)
  227. {
  228.   /* This chunk is NOT being acked and is missing at the receiver
  229.    */
  230.   spCurrNodeData = (SctpSendBufferNode_S *) spCurrNode->vpData;
  231.   /* If this chunk was GapAcked before, then either the
  232.    * receiver has renegged the chunk (which our simulation
  233.    * doesn't do) or this SACK is arriving out of order.
  234.    */
  235.   if(spCurrNodeData->eGapAcked == TRUE)
  236.     {
  237.       DBG_PL(ProcessGapAckBlocks, 
  238.      "out of order SACK? setting TSN=%d eGapAcked=FALSE"),
  239. spCurrNodeData->spChunk->uiTsn DBG_PR;
  240.       spCurrNodeData->eGapAcked = FALSE;
  241.       spCurrNodeData->spDest->iOutstandingBytes 
  242. += spCurrNodeData->spChunk->sHdr.usLength;
  243.       /* section 6.3.2.R4 says that we should restart the T3-rtx
  244.        * timer here if it isn't running already. In our
  245.        * implementation, it isn't necessary since ProcessSackChunk
  246.        * will restart the timer for any destinations which have
  247.        * outstanding data and don't have a timer running.
  248.        */
  249.     }
  250. }
  251.       DBG_PL(ProcessGapAckBlocks,"now incrementing missing reports...") DBG_PR;
  252.       DBG_PL(ProcessGapAckBlocks, "uiHighestTsnNewlyAcked=%d"), 
  253. uiHighestTsnNewlyAcked DBG_PR;
  254.       /****** Begin CMT Change ******/
  255.       /* CMT DAC algo: The SACK chunk flags are unused in RFC2960. We
  256.        * propose to use 1 of the 8 bits in the SACK flags for the CMT
  257.        * delayed ack algo.  This bit will be used to indicate the number
  258.        * of DATA packets were received between the previous and the
  259.        * current SACK. This information will be used by the DATA sender to
  260.        * increment missing reports better in CMT.
  261.        * 
  262.        ** If the SACK is a mixed SACK (multiple dests being acked) then
  263.        *  the sender conservatively increases missing reports by 1. 
  264.        ** If not a mixed SACK, and TSNs are acked across a lost TSN t
  265.        *  (t- and t+ TSNs being acked by this SACK), sender conservatively 
  266.        *  can use only 1 missing report.
  267.        ** If not a mixed SACK, and TSNs are not acked across lost TSN t, 
  268.        *  then the sender can use number of packets reported by the SACK 
  269.        *  as the number of missing reports to be used.
  270.        *
  271.        * For 2 latter cases, sender needs to know lowest TSN and highest TSN 
  272.        * acked in the current SACK for the destination for which missing 
  273.        * reports are to be incremented. If the TSN to be marked is between
  274.        * the highest and the lowest, then the SACK should be treated as 
  275.        * acking across the missing TSN. This code appears further below.
  276.        */
  277.       uiNumDestsSacked = 0;
  278.       for(spCurrDestNode = sDestList.spHead;
  279.   spCurrDestNode != NULL;
  280.   spCurrDestNode = spCurrDestNode->spNext)
  281. {
  282.   spCurrDestNodeData = (SctpDest_S *) spCurrDestNode->vpData;
  283.   if (spCurrDestNodeData->eSawNewAck)
  284.     uiNumDestsSacked++;
  285. }
  286.       DBG_PL(ProcessGapAckBlocks, "number of dests SACKed = %d"),
  287.      uiNumDestsSacked DBG_PR;
  288.       
  289.       for(spCurrNode = sSendBuffer.spHead;
  290.   spCurrNode != NULL; 
  291.   spCurrNode = spCurrNode->spNext)
  292. {
  293.   spCurrNodeData = (SctpSendBufferNode_S *) spCurrNode->vpData;
  294.   DBG_PL(ProcessGapAckBlocks, "TSN=%d eGapAcked=%s"), 
  295.     spCurrNodeData->spChunk->uiTsn,
  296.     spCurrNodeData->eGapAcked ? "TRUE" : "FALSE"
  297.     DBG_PR;
  298.   if(spCurrNodeData->eGapAcked == FALSE)
  299.     {
  300.       /* Applying SFR. The modified check satisfies SFR and
  301.        * handles part of HTNA per destination. Does not take care
  302.        * of the HTNA algo during Fast Recovery.
  303.        */
  304.       /* JRI-TODO: Look into HTNA bit during Fast Recovery,
  305.        * and how it can be implemented.
  306.        */
  307.       if (((eUseCmtReordering == FALSE) &&
  308.    (spCurrNodeData->spChunk->uiTsn < uiHighestTsnNewlyAcked)) 
  309.   ||
  310.   // ((eUseCmtReordering == FALSE) && (eNewCumAck == TRUE) && 
  311.   // (uiHighestTsnNewlyAcked <= uiRecover) &&
  312.   // (spCurrNodeData->spChunk->uiTsn < uiHighestTsnSacked)) ||
  313.   ((eUseCmtReordering == TRUE) && 
  314.    (spCurrNodeData->spDest->eSawNewAck) &&
  315.    (spCurrNodeData->spChunk->uiTsn < 
  316.     spCurrNodeData->spDest->uiHighestTsnInSackForDest)))
  317. {
  318.   /* CMT DAC algo: For 2 latter cases mentioned above,
  319.    * sender needs to know lowest TSN and highest TSN acked
  320.    * in the current SACK for the destination for which
  321.    * missing reports are to be incremented. If the TSN to
  322.    * be marked is between the highest and the lowest, then
  323.    * the SACK should be treated as acking across the
  324.    * missing TSN.
  325.    */
  326.   if (eUseCmtDelAck == TRUE) 
  327.     {
  328.       if ((uiNumDestsSacked > 1) || 
  329.   (spCurrNodeData->spDest->uiLowestTsnInSackForDest < 
  330.    spCurrNodeData->spChunk->uiTsn)) 
  331. {
  332.   spCurrNodeData->iNumMissingReports += 1;
  333. } else if (uiNumDestsSacked == 1) {
  334.   spCurrNodeData->iNumMissingReports += 
  335.     uiNumPacketsSacked;
  336. } else {/* do nothing if uiNumDestsSacked < 1 ! */}
  337.     } else {
  338.       spCurrNodeData->iNumMissingReports++;
  339.     }
  340.   /****** End CMT Change ******/
  341.   DBG_PL(ProcessGapAckBlocks, 
  342.  "incrementing missing report for TSN=%d to %d"), 
  343.     spCurrNodeData->spChunk->uiTsn,
  344.     spCurrNodeData->iNumMissingReports
  345.     DBG_PR;
  346.   if(spCurrNodeData->iNumMissingReports >= iFastRtxTrigger &&
  347.      spCurrNodeData->eIneligibleForFastRtx == FALSE &&
  348.      spCurrNodeData->eAdvancedAcked == FALSE)
  349.     {
  350.       MarkChunkForRtx(spCurrNodeData, FAST_RTX);
  351.       eFastRtxNeeded = TRUE;
  352.       spCurrNodeData->eIneligibleForFastRtx = TRUE;
  353.       DBG_PL(ProcessGapAckBlocks, 
  354.      "setting eFastRtxNeeded = TRUE") DBG_PR;
  355.     }
  356. }
  357.     }
  358. }
  359.     }
  360.   if(eFastRtxNeeded == TRUE)
  361.     tiFrCount++;
  362.   
  363.   DBG_PL(ProcessGapAckBlocks, "eFastRtxNeeded=%s"), 
  364.     eFastRtxNeeded ? "TRUE" : "FALSE" DBG_PR;
  365.   DBG_X(ProcessGapAckBlocks);
  366.   return eFastRtxNeeded;
  367. }
  368. void SctpCMTAgent::ProcessSackChunk(u_char *ucpSackChunk)
  369. {
  370.   DBG_I(ProcessSackChunk);
  371.   
  372.   SctpSackChunk_S *spSackChunk = (SctpSackChunk_S *) ucpSackChunk;
  373.   
  374.   DBG_PL(ProcessSackChunk, "cum=%d arwnd=%d #gapacks=%d #duptsns=%d"),
  375.     spSackChunk->uiCumAck, spSackChunk->uiArwnd, 
  376.     spSackChunk->usNumGapAckBlocks, spSackChunk->usNumDupTsns 
  377.     DBG_PR;
  378.   Boolean_E eFastRtxNeeded = FALSE;
  379.   Boolean_E eNewCumAck = FALSE;
  380.   Node_S *spCurrDestNode = NULL;
  381.   SctpDest_S *spCurrDestNodeData = NULL;
  382.   u_int uiTotalOutstanding = 0;
  383.   int i = 0;
  384.   /****** Begin CMT Change ******/
  385.   Node_S *spCurrBuffNode = NULL;
  386.   SctpSendBufferNode_S *spCurrBuffNodeData = NULL;
  387.   SctpDataChunkHdr_S  *spCurrChunk;
  388.   /* CMT DAC algo: The SACK chunk flags are unused in RFC2960. We propose
  389.    * to use 2 of the 8 bits in the SACK flags for the CMT delayed ack
  390.    * algo.  These bits will be used to indicate the number of DATA packets
  391.    * were received between the previous and the current SACK. This
  392.    * information will be used by the DATA sender to increment missing
  393.    * reports better in CMT.
  394.    */
  395.   uiNumPacketsSacked = spSackChunk->sHdr.ucFlags;
  396.   DBG_PL(ProcessSackChunk, "Sack Flags=%d"), spSackChunk->sHdr.ucFlags DBG_PR;
  397.   /****** End CMT Change ******/
  398.   
  399.   /* make sure we clear all the iNumNewlyAckedBytes before using them!
  400.    */
  401.   for(spCurrDestNode = sDestList.spHead;
  402.       spCurrDestNode != NULL;
  403.       spCurrDestNode = spCurrDestNode->spNext)
  404.     {
  405.       spCurrDestNodeData = (SctpDest_S *) spCurrDestNode->vpData;
  406.       spCurrDestNodeData->iNumNewlyAckedBytes = 0;
  407.       spCurrDestNodeData->spFirstOutstanding = NULL;
  408.       /****** Begin CMT Change ******/
  409.       spCurrDestNodeData->eNewPseudoCum = FALSE;
  410.       spCurrDestNodeData->eSawNewAck = FALSE;
  411.       /* CMT DAC algo: Initialize uiLowestTsn to zero, this variable
  412.        * should get set during SACK processing to some TSN.
  413.        */
  414.       spCurrDestNodeData->uiLowestTsnInSackForDest = 0;
  415.       spCurrDestNodeData->uiBurstLength = 0;
  416.       /****** End CMT Change ******/
  417.     }
  418.   if(spSackChunk->uiCumAck < uiCumAckPoint) 
  419.     {
  420.       /* this cumAck's a previously cumAck'd tsn (ie, it's out of order!)
  421.        * ...so ignore!
  422.        */
  423.       DBG_PL(ProcessSackChunk, "ignoring out of order sack!") DBG_PR;
  424.       DBG_X(ProcessSackChunk);
  425.       return;
  426.     }
  427.   else if(spSackChunk->uiCumAck > uiCumAckPoint)
  428.     {
  429.       eNewCumAck = TRUE; // incomding SACK's cum ack advances the cum ack point
  430.       SendBufferDequeueUpTo(spSackChunk->uiCumAck);
  431.       uiCumAckPoint = spSackChunk->uiCumAck; // Advance the cumAck pointer
  432.     }
  433.   /****** Begin CMT Change ******/
  434.   /* CMT change In case of reneg, or reordered SACKs where previously
  435.    * present gapack info is now absent, arwnd info can be stale and
  436.    * dangerous if used without consideration to the fact that the receiver
  437.    * has apparently reneged on data. Note that ProcessGapAckBlocks was
  438.    * previously not called when gapacks were absent in the SACK. What if
  439.    * previously present gapacks were reneged? Since we do not reneg in
  440.    * this implementation, this can happen only with reordered SACKs.  In
  441.    * any case, ProcessGapAckBlocks() has code that will handle such
  442.    * apparent reneging, i.e., calculate the correct new
  443.    * outstanding_bytes. Therefore, this function should be called even if
  444.    * there are no gapack blocks in the SACK chunk to handle renegs, or
  445.    * reordered SACKs with no gapack info.
  446.    *
  447.    * if(spSackChunk->usNumGapAckBlocks != 0) // are there any gaps??
  448.    * {
  449.    *   eFastRtxNeeded = ProcessGapAckBlocks(ucpSackChunk, eNewCumAck);
  450.    * } 
  451.    */
  452.   /* JRI-TODO: Move change to SCTP code. This problem can be caused by 
  453.    * reordered SACKs too.The SCTP code handles calculation of outstanding bytes
  454.    * when SACK is reordered, so this check should be removed there too.
  455.    */
  456.   eFastRtxNeeded = ProcessGapAckBlocks(ucpSackChunk, eNewCumAck);
  457.   /* TEMP-FIX: Calculate the correct outstanding bytes in each dest, since
  458.    * even data that is marked for rtx is still considered oustanding. The
  459.    * value of outstanding_bytes is restored later below. Need this to stop
  460.    * T3 timers correctly.  This problem needs to be fixed here, and in SCTP.
  461.    */
  462.   for(spCurrBuffNode = sSendBuffer.spHead; 
  463.       spCurrBuffNode != NULL;
  464.       spCurrBuffNode = spCurrBuffNode->spNext)
  465.     {
  466.       spCurrBuffNodeData = (SctpSendBufferNode_S *) spCurrBuffNode->vpData;
  467.       if(spCurrBuffNodeData->eMarkedForRtx != NO_RTX)
  468.         {
  469.           spCurrChunk = spCurrBuffNodeData->spChunk;
  470.           spCurrBuffNodeData->spDest->iOutstandingBytes
  471.             -= spCurrChunk->sHdr.usLength;
  472.         }
  473.     }
  474.   /****** End CMT Change ******/
  475.   
  476.   for(spCurrDestNode = sDestList.spHead;
  477.       spCurrDestNode != NULL;
  478.       spCurrDestNode = spCurrDestNode->spNext)
  479.     {
  480.       spCurrDestNodeData = (SctpDest_S *) spCurrDestNode->vpData;
  481.       /****** Begin CMT Change ******/
  482.       /* Adjust cwnd if sack advanced the pseudo-cumack point AND this
  483.        * destination has newly acked bytes. Also, we MUST adjust our
  484.        * congestion window BEFORE we update the number of outstanding
  485.        * bytes to reflect the newly acked bytes in received SACK.  
  486.        */
  487.       DBG_PL(ProcessSackChunk, "eNewPseudoCum=%d, newlyacked=%d"), 
  488. spCurrDestNodeData->eNewPseudoCum, 
  489. spCurrDestNodeData->iNumNewlyAckedBytes DBG_PR;
  490.       if (eUseCmtCwnd == TRUE) 
  491. {
  492.   if((spCurrDestNodeData->eNewPseudoCum == TRUE) && 
  493.      (spCurrDestNodeData->iNumNewlyAckedBytes > 0))
  494.     AdjustCwnd(spCurrDestNodeData);
  495.       else if (eUseCmtCwnd == FALSE) 
  496. {
  497.   if(eNewCumAck == TRUE && spCurrDestNodeData->iNumNewlyAckedBytes > 0)
  498.     AdjustCwnd(spCurrDestNodeData);
  499. }
  500.       /****** End CMT Change ******/
  501.       /* The number of outstanding bytes is reduced by how many bytes this sack
  502.        * acknowledges.
  503.        */
  504.       if(spCurrDestNodeData->iNumNewlyAckedBytes <=
  505.  spCurrDestNodeData->iOutstandingBytes)
  506. {
  507.   spCurrDestNodeData->iOutstandingBytes 
  508.     -= spCurrDestNodeData->iNumNewlyAckedBytes;
  509. }
  510.       else
  511. spCurrDestNodeData->iOutstandingBytes = 0;
  512.       DBG_PL(ProcessSackChunk,"Dest #%d (%d:%d) (%p): outstanding=%d, cwnd=%d"),
  513. ++i, spCurrDestNodeData->iNsAddr, spCurrDestNodeData->iNsPort,
  514. spCurrDestNodeData, spCurrDestNodeData->iOutstandingBytes, 
  515. spCurrDestNodeData->iCwnd DBG_PR;
  516.       if(spCurrDestNodeData->iOutstandingBytes == 0)
  517. {
  518.   /* All outstanding data has been acked
  519.    */
  520.   spCurrDestNodeData->iPartialBytesAcked = 0;  // section 7.2.2
  521.   /* section 6.3.2.R2
  522.    */
  523.   if(spCurrDestNodeData->eRtxTimerIsRunning == TRUE)
  524.     {
  525.       DBG_PL(ProcessSackChunk, "Dest #%d (%p): stopping timer"), 
  526. i, spCurrDestNodeData DBG_PR;
  527.       StopT3RtxTimer(spCurrDestNodeData);
  528.     }
  529. }
  530.       /* section 6.3.2.R3 - Restart timers for destinations that have
  531.        * acknowledged their first outstanding (ie, no timer running) and
  532.        * still have outstanding data in flight.  
  533.        */
  534.       if(spCurrDestNodeData->iOutstandingBytes > 0 &&
  535.  spCurrDestNodeData->eRtxTimerIsRunning == FALSE)
  536. {
  537.   StartT3RtxTimer(spCurrDestNodeData);
  538. }
  539.     }
  540.   /****** Begin CMT Change ******/
  541.   /* TEMP-FIX: Restore value of outstanding_bytes. See TEMP-FIX comment
  542.    * above for details.
  543.    */
  544.   for(spCurrBuffNode = sSendBuffer.spHead; 
  545.       spCurrBuffNode != NULL;
  546.       spCurrBuffNode = spCurrBuffNode->spNext)
  547.     {
  548.       spCurrBuffNodeData = (SctpSendBufferNode_S *) spCurrBuffNode->vpData;
  549.       
  550.       if(spCurrBuffNodeData->eMarkedForRtx != NO_RTX)
  551.         {
  552.           spCurrChunk = spCurrBuffNodeData->spChunk;
  553.           spCurrBuffNodeData->spDest->iOutstandingBytes
  554.             += spCurrChunk->sHdr.usLength;
  555.         }
  556.     }
  557.   /****** End CMT Change ******/
  558.   DBG_F(ProcessSackChunk, DumpSendBuffer());
  559.   AdvancePeerAckPoint();
  560.   /****** Begin CMT Change ******/
  561.   uiArwnd = spSackChunk->uiArwnd;
  562.   /****** End CMT Change ******/
  563.   if(eFastRtxNeeded == TRUE)  // section 7.2.4
  564.     FastRtx();
  565.   /* Let's see if after process this sack, there are still any chunks
  566.    * pending... If so, rtx all allowed by cwnd.
  567.    */
  568.   else if( (eMarkedChunksPending = AnyMarkedChunks()) == TRUE)
  569.     {
  570.       /* section 6.1.C) When the time comes for the sender to
  571.        * transmit, before sending new DATA chunks, the sender MUST
  572.        * first transmit any outstanding DATA chunks which are marked
  573.        * for retransmission (limited by the current cwnd).  
  574.        */
  575.       RtxMarkedChunks(RTX_LIMIT_CWND);
  576.     }
  577.   /* (6.2.1.D.ii) Adjust PeerRwnd based on total oustanding bytes on all
  578.    * destinations. We need to this adjustment after any
  579.    * retransmissions. Otherwise the sender's view of the peer rwnd will be
  580.    * off, because the number outstanding increases again once a marked
  581.    * chunk gets retransmitted (when marked, outstanding is decreased).
  582.    */
  583.   uiTotalOutstanding = TotalOutstanding();
  584.   if(uiTotalOutstanding <= spSackChunk->uiArwnd)
  585.     uiPeerRwnd = (spSackChunk->uiArwnd  - uiTotalOutstanding);
  586.   else
  587.     uiPeerRwnd = 0;
  588.   tiRwnd++; // trigger changes to be traced
  589.   DBG_PL(ProcessSackChunk, "uiPeerRwnd=%d, uiArwnd=%d"), uiPeerRwnd, 
  590.     spSackChunk->uiArwnd DBG_PR;
  591.   DBG_X(ProcessSackChunk);
  592. }
  593. int SctpCMTAgent::ProcessChunk(u_char *ucpInChunk, u_char **ucppOutData)
  594. {
  595.   DBG_I(ProcessChunk);
  596.   int iThisOutDataSize = 0;
  597.   Node_S *spCurrNode = NULL;
  598.   SctpDest_S *spCurrDest = NULL;
  599.   double dCurrTime = Scheduler::instance().clock();
  600.   double dTime;
  601.   SctpHeartbeatAckChunk_S *spHeartbeatChunk = NULL;
  602.   SctpHeartbeatAckChunk_S *spHeartbeatAckChunk = NULL;
  603.   /****** Begin CMT Change ******/
  604.   Boolean_E eFoundNonPFDest =  FALSE;
  605.   Boolean_E eCmtPFCwndChange = FALSE;
  606.   u_int uiTotalOutstanding = 0;
  607.   Boolean_E eThisDestWasInactive = FALSE; /* For triggering data tx in CMT */
  608.   /****** End CMT Change ******/
  609.   Boolean_E eThisDestWasUnconfirmed = FALSE;
  610.   Boolean_E eFoundUnconfirmedDest = FALSE;
  611.   switch(eState)
  612.     {
  613.     case SCTP_STATE_CLOSED:
  614.       switch( ((SctpChunkHdr_S *)ucpInChunk)->ucType)
  615. {
  616. case SCTP_CHUNK_INIT:
  617.   DBG_PL(ProcessChunk, "got INIT!! ...sending INIT_ACK") DBG_PR;
  618.   ProcessInitChunk(ucpInChunk);
  619.   iThisOutDataSize = GenChunk(SCTP_CHUNK_INIT_ACK, *ucppOutData);
  620.   *ucppOutData += iThisOutDataSize;
  621.   /* stay in the closed state */
  622.   break;
  623. case SCTP_CHUNK_COOKIE_ECHO:
  624.   DBG_PL(ProcessChunk, 
  625.  "got COOKIE_ECHO!! (established!) ...sending COOKIE_ACK")
  626.     DBG_PR;
  627.   ProcessCookieEchoChunk( (SctpCookieEchoChunk_S *) ucpInChunk );
  628.   iThisOutDataSize = GenChunk(SCTP_CHUNK_COOKIE_ACK, *ucppOutData);
  629.   *ucppOutData += iThisOutDataSize;
  630.   eState = SCTP_STATE_ESTABLISHED;
  631.   if(uiHeartbeatInterval != 0)
  632.     {
  633.       dTime = CalcHeartbeatTime(spPrimaryDest->dRto);
  634.       opHeartbeatGenTimer->force_cancel();
  635.       opHeartbeatGenTimer->resched(dTime);
  636.       opHeartbeatGenTimer->dStartTime = dCurrTime;
  637.       for(spCurrNode = sDestList.spHead;
  638.   spCurrNode != NULL;
  639.   spCurrNode = spCurrNode->spNext)
  640. {
  641.   spCurrDest = (SctpDest_S *) spCurrNode->vpData;
  642.   spCurrDest->dIdleSince = dCurrTime;
  643. }
  644.     }
  645.   break;
  646.   
  647.    default:
  648.   /* ALC 1/25/2002
  649.    *
  650.    * no error statement here, because there are times when this could
  651.    * occur due to abrupt disconnections via the "reset" command. how?
  652.    * well, "reset" resets all the association state. however, there may
  653.    * still be packets in transit. if and when those packets arrive,they
  654.    * will be unexpected packets since the association is closed. since
  655.    * this is a simulation, it shouldn't be a problem. however, if an 
  656.    * application needs a more graceful shutdown, we would need to 
  657.    * implement sctp's proper shutdown procedure. until the need arises,
  658.    * we won't do it. instead, what do we do? ignore the "unexpected"
  659.    * packet.
  660.    */
  661.   DBG_PL(ProcessChunk, "association closed... ignoring chunk %s"), 
  662.     "(not COOKIE_ECHO or INIT)" DBG_PR;
  663.   break;
  664. }
  665.       break;
  666.   
  667.     case SCTP_STATE_COOKIE_WAIT:
  668.       DBG_PL(ProcessChunk, "got INIT_ACK!! ...sending COOKIE_ECHO") DBG_PR;
  669.       ProcessInitAckChunk(ucpInChunk);
  670.       iThisOutDataSize = GenChunk(SCTP_CHUNK_COOKIE_ECHO, *ucppOutData);
  671.       *ucppOutData += iThisOutDataSize;
  672.       opT1CookieTimer->resched(spPrimaryDest->dRto);
  673.       eState = SCTP_STATE_COOKIE_ECHOED;
  674.       break;
  675.     case SCTP_STATE_COOKIE_ECHOED:
  676.       DBG_PL(ProcessChunk, "got COOKIE_ACK!! (established!) ...sending DATA")
  677. DBG_PR;
  678.       ProcessCookieAckChunk( (SctpCookieAckChunk_S *) ucpInChunk );
  679.       eSendNewDataChunks = TRUE;
  680.       eState = SCTP_STATE_ESTABLISHED;
  681.       /* PN: Confirming Destinations 
  682.        * RFC allows to send data to a confirmed destination.
  683.        * However, this implementation is more conservative than
  684.        * the RFC, since, data is sent to any destionation only if
  685.        * all destinations have been confirmed. 
  686.        */ 
  687.       for(spCurrNode = sDestList.spHead;
  688.   spCurrNode != NULL;
  689.   spCurrNode = spCurrNode->spNext)
  690. {
  691.   spCurrDest = (SctpDest_S *) spCurrNode->vpData;
  692.   spCurrDest->eStatus = SCTP_DEST_STATUS_UNCONFIRMED;
  693.   
  694.    /* Send Heartbeat to confirm this dest and get RTT */
  695.   SendHeartbeat(spCurrDest);
  696. }
  697.       
  698.       eSendNewDataChunks = FALSE;
  699.       
  700.       break;
  701.     case SCTP_STATE_ESTABLISHED:
  702.       switch( ((SctpChunkHdr_S *)ucpInChunk)->ucType)
  703. {
  704. case SCTP_CHUNK_DATA:
  705.   DBG_PL(ProcessChunk, "got DATA (TSN=%d)!!"), 
  706.     ((SctpDataChunkHdr_S *)ucpInChunk)->uiTsn DBG_PR;
  707.   if(eUseDelayedSacks == FALSE) // are we doing delayed sacks?
  708.     {
  709.       /* NO, so generate sack immediately!
  710.        */
  711.       eSackChunkNeeded = TRUE;
  712.     }
  713.   else  // we are doing delayed sacks, so...
  714.     {
  715.       /* rfc2960 section 6.2 - determine if a SACK will be generated
  716.        */
  717.       if(eStartOfPacket == TRUE)  
  718. {
  719.   eStartOfPacket = FALSE;  // reset
  720.   iDataPktCountSinceLastSack++;
  721.   if(iDataPktCountSinceLastSack == 1)   
  722.     {
  723.       opSackGenTimer->resched(dSackDelay);
  724.     }
  725.   else if(iDataPktCountSinceLastSack == DELAYED_SACK_TRIGGER)
  726.     {
  727.       /****** Begin CMT Change ******/
  728.       /* CMT change: Do not reset packet count yet !  Pkt
  729.        * count is put into SACK flags for CMT delayed ack
  730.        * algo. Hence reset only after putting into SACK
  731.        * flags.
  732.        *
  733.        * iDataPktCountSinceLastSack = 0; // reset
  734.        */
  735.       /****** End CMT Change ******/
  736.       opSackGenTimer->force_cancel();
  737.       eSackChunkNeeded = TRUE;
  738.     }
  739. }
  740.     }
  741.   ProcessDataChunk( (SctpDataChunkHdr_S *) ucpInChunk );
  742.   /****** Begin CMT Change ******/
  743.   /* section 6.7 - There is at least one "gap in the received DATA
  744.    * chunk sequence", so let's ensure we send a SACK immediately!
  745.    */
  746.   /* If CMT DAC algo, always delay SACKs; sender has the
  747.    * responsibility of handling missing report counts according to
  748.    * number of packets acked.
  749.    */
  750.   if((eUseCmtDelAck == FALSE) && (sRecvTsnBlockList.uiLength > 0))
  751.     {
  752.       iDataPktCountSinceLastSack = 0; // reset
  753.       opSackGenTimer->force_cancel();
  754.       eSackChunkNeeded = TRUE;
  755.     }
  756.   /****** End CMT Change ******/
  757.   /* no state change 
  758.    */   
  759.   break;
  760. case SCTP_CHUNK_SACK:
  761.   DBG_PL(ProcessChunk, "got SACK (CumAck=%d)!!"),
  762.     ((SctpSackChunk_S *)ucpInChunk)->uiCumAck DBG_PR;
  763.   ProcessSackChunk(ucpInChunk);
  764.   /* Do we need to transmit a FORWARD TSN chunk??
  765.    */
  766.   if(uiAdvancedPeerAckPoint > uiCumAckPoint)
  767.     eForwardTsnNeeded = TRUE;
  768.   eSendNewDataChunks = TRUE;
  769.   break; // no state change
  770. case SCTP_CHUNK_FORWARD_TSN:
  771.   DBG_PL(ProcessChunk, "got FORWARD TSN (tsn=%d)!!"),
  772.     ((SctpForwardTsnChunk_S *) ucpInChunk)->uiNewCum DBG_PR;
  773.   ProcessForwardTsnChunk( (SctpForwardTsnChunk_S *) ucpInChunk );
  774.   break; // no state change
  775. case SCTP_CHUNK_HB:
  776.   DBG_PL(ProcessChunk, "got HEARTBEAT!!") DBG_PR;
  777.   /* GenChunk() doesn't copy HB info
  778.    */
  779.   iThisOutDataSize = GenChunk(SCTP_CHUNK_HB_ACK, *ucppOutData);
  780.   /* ...so we copy it here!
  781.    */
  782.   spHeartbeatChunk = (SctpHeartbeatAckChunk_S *) ucpInChunk;
  783.   spHeartbeatAckChunk = (SctpHeartbeatAckChunk_S *) *ucppOutData;
  784.   spHeartbeatAckChunk->dTimestamp = spHeartbeatChunk->dTimestamp;
  785.   spHeartbeatAckChunk->spDest = spHeartbeatChunk->spDest;
  786.   *ucppOutData += iThisOutDataSize;
  787.   break; // no state change
  788. case SCTP_CHUNK_HB_ACK:
  789.   DBG_PL(ProcessChunk, "got HEARTBEAT ACK!!") DBG_PR;
  790.   /****** Begin CMT Change ******/
  791.   spHeartbeatAckChunk = (SctpHeartbeatAckChunk_S *) ucpInChunk;
  792.   /* CMT-PF: Is HB-ACK on a PF path?
  793.    */
  794.   if (spHeartbeatAckChunk->spDest->eStatus ==
  795.       SCTP_DEST_STATUS_POSSIBLY_FAILED)
  796.     {
  797.       eCmtPFCwndChange = TRUE;
  798.       DBG_PL(ProcessChunk, "HB-ACK: dest:%p, status:%s"), 
  799. spHeartbeatAckChunk->spDest, 
  800. PrintDestStatus(spHeartbeatAckChunk->spDest) DBG_PR;
  801.     }
  802.   
  803.   if (spHeartbeatAckChunk->spDest->eStatus ==
  804.       SCTP_DEST_STATUS_INACTIVE)
  805.     eThisDestWasInactive = TRUE;
  806.     /* CMT-PF: Are there any non-PF destinations? Need this info
  807.    * for HB timer processing further below.
  808.    */
  809.           for(spCurrNode = sDestList.spHead;
  810.                   spCurrNode != NULL;
  811.                   spCurrNode = spCurrNode->spNext)
  812.                 {
  813.                   spCurrDest = (SctpDest_S *) spCurrNode->vpData;
  814.   if (spCurrDest->eStatus != SCTP_DEST_STATUS_POSSIBLY_FAILED)
  815.     {
  816.       eFoundNonPFDest = TRUE;
  817.       DBG_PL(ProcessChunk, "Found non PF dest=%p"), 
  818. spCurrDest DBG_PR;
  819.       /* break; */
  820.     }
  821.                 }
  822.           /* PN: Is this destination unconfirmed?
  823.    */
  824.   if (spHeartbeatAckChunk->spDest->eStatus ==
  825.       SCTP_DEST_STATUS_UNCONFIRMED)
  826. eThisDestWasUnconfirmed = TRUE;
  827.   
  828.   ProcessHeartbeatAckChunk( (SctpHeartbeatAckChunk_S *) ucpInChunk);
  829.   /* PN: If this destination was unconfirmed and all other
  830.    * destinations have been confirmed, then allow data
  831.    * transmission on the association
  832.            */
  833.   if (eThisDestWasUnconfirmed == TRUE) {
  834.            for(spCurrNode = sDestList.spHead;
  835.                   spCurrNode != NULL;
  836.                   spCurrNode = spCurrNode->spNext)
  837.               {
  838.                   spCurrDest = (SctpDest_S *) spCurrNode->vpData;
  839.   if (spCurrDest->eStatus == SCTP_DEST_STATUS_UNCONFIRMED)
  840.     {
  841.       eFoundUnconfirmedDest = TRUE;
  842.       DBG_PL(ProcessChunk, "dest=%p UNCONFIRMED"), 
  843. spCurrDest DBG_PR;
  844.       break;
  845.     }
  846. }
  847.   
  848. if (eFoundUnconfirmedDest == TRUE)
  849. break; /* From case HB_ACK chunk */
  850. else {
  851.    /* All destinations have been confirmed.
  852.     * Start new data transfer
  853.     */
  854.    eSendNewDataChunks = TRUE;
  855.    /* Process HeartbeatTimers for the association. 
  856.     * This code was originally in the COOKIE ACK case above  
  857.     */ 
  858.           if(uiHeartbeatInterval != 0)
  859.      {
  860.        dTime = CalcHeartbeatTime(spPrimaryDest->dRto);
  861.        opHeartbeatGenTimer->force_cancel();
  862.        opHeartbeatGenTimer->resched(dTime);
  863.        opHeartbeatGenTimer->dStartTime = dCurrTime;
  864.        for(spCurrNode = sDestList.spHead;
  865.    spCurrNode != NULL;
  866.    spCurrNode = spCurrNode->spNext)
  867.  {
  868.    spCurrDest = (SctpDest_S *) spCurrNode->vpData;
  869.    spCurrDest->dIdleSince = dCurrTime;
  870.  }
  871.      } 
  872.   break; /* From case HB_ACK */
  873.   
  874. } /* End else eFoundUnconfirmedDest == TRUE */
  875.   } /* End of if (eThisDestWasUnconfirmed == TRUE) */
  876.   
  877.   /* CMT-PF: Set cwnd to uiCmtPFCwnd MTUs if HB-ack was received from a
  878.    * destination marked PF
  879.    */
  880.   if ((eCmtPFCwndChange == TRUE) || (eThisDestWasInactive == TRUE))
  881.     {
  882.       if (eCmtPFCwndChange == TRUE)
  883. {
  884.   
  885.   spHeartbeatAckChunk->spDest->iCwnd 
  886.     = uiCmtPFCwnd*uiMaxDataSize;
  887.   DBG_PL(ProcessChunk, "dest=%p, CMTPFCwnd=%ld"), 
  888.     spHeartbeatAckChunk->spDest, 
  889.     spHeartbeatAckChunk->spDest->iCwnd DBG_PR;
  890.   //tiCwnd++; // for trace
  891.       
  892.   spHeartbeatAckChunk->spDest->dPFSince = 0; //unset
  893. }
  894.       /* CMT: Since all paths were INACTIVE and one of them has
  895.        * become ACTIVE now. First send retransmissions to the
  896.        * ACTIVE destination. 
  897.        *
  898.        * CMT-PF: Since a PF destination has become ACTIVE, first 
  899.        * send retransmissions to the ACTIVE destination. 
  900.        */
  901.       TimeoutRtx(spHeartbeatAckChunk->spDest);
  902.       
  903.       /* CMT, CMT-PF: If no marked chunks, send new data 
  904.        */
  905.       if (AnyMarkedChunks() == FALSE) 
  906. {
  907.   /* All marked chunks are in flight or have been sacked.
  908.    * If they are in flight, then peerRwnd does not reflect
  909.    * these outstanding bytes. To recap: MarkChunksForRtx
  910.    * marks chunks and adds their size to
  911.    * peerRwnd. RtxMarkedChunks does not decrement peerRwnd
  912.    * when these chunks are rtxmed. So right now, we have
  913.    * incorrect view of peerRwnd.  If peerRwnd is > than
  914.    * what it should be, sending new data in the space will
  915.    * cause problems. (How?: rtx tsns were lost and before
  916.    * this was detected, new data sent fills up
  917.    * peerRwnd. In that case, recv silently discards later
  918.    * rtxms and sender keeps timing out on that rtxms.
  919.    * Deadlock !)
  920.    *
  921.    * So update peerRwnd based on the last advertised
  922.    * receiver window. 
  923.    */
  924.   uiTotalOutstanding = TotalOutstanding();
  925.   if(uiTotalOutstanding <= uiArwnd)
  926.     uiPeerRwnd = (uiArwnd  - uiTotalOutstanding);
  927.   else
  928.     uiPeerRwnd = 0;
  929.   
  930.   DBG_PL(ProcessChunk,"uiPeerRwnd=%d, uiArwnd=%d"), uiPeerRwnd,
  931.     uiArwnd DBG_PR;
  932.   
  933.   /* Set flag for SendMuch to send new data. 
  934.    */
  935.   eSendNewDataChunks = TRUE;
  936.      }
  937.      
  938.   break; // no state change
  939. case SCTP_CHUNK_INIT:
  940.   DBG_PL(ProcessChunk, "unexpected chunk type (INIT) at %f"),
  941.     dCurrTime DBG_PR;
  942.   printf("[ProcessChunk] unexpected chunk type (INIT) at %fn",
  943.     dCurrTime);
  944.   break;
  945. case SCTP_CHUNK_INIT_ACK:
  946.   DBG_PL(ProcessChunk, "unexpected chunk type (INIT_ACK) at %f"),
  947.     dCurrTime DBG_PR;
  948.   printf("[ProcessChunk] unexpected chunk type (INIT_ACK) at %fn",
  949.     dCurrTime);
  950.   break;
  951.   /* even though the association is established,COOKIE_ECHO needs to be
  952.    * handled because the peer may have not received the COOKIE_ACK.
  953.    *
  954.    * Note: we don't follow the rfc's complex process for handling this
  955.    * case, because we don't deal with tie-tags, etc in simulation. :-)
  956.    */
  957. case SCTP_CHUNK_COOKIE_ECHO:
  958.   DBG_PL(ProcessChunk,
  959.  "got COOKIE_ECHO!! (established!) ...sending COOKIE_ACK")
  960.     DBG_PR;
  961.   ProcessCookieEchoChunk( (SctpCookieEchoChunk_S *) ucpInChunk);
  962.   iThisOutDataSize = GenChunk(SCTP_CHUNK_COOKIE_ACK, *ucppOutData);
  963.   *ucppOutData += iThisOutDataSize;
  964.   break;
  965. case SCTP_CHUNK_COOKIE_ACK:
  966.   DBG_PL(ProcessChunk, "unexpected chunk type (COOKIE_ACK) at %f"),
  967.     dCurrTime DBG_PR;
  968.   printf("[ProcessChunk] unexpected chunk type (COOKIE_ACK) at %fn",
  969.  dCurrTime);
  970.   break;
  971. default:
  972.   ProcessOptionChunk(ucpInChunk);
  973.   break;
  974. }
  975.       break;
  976.     case SCTP_STATE_UNINITIALIZED:
  977.     case SCTP_STATE_SHUTDOWN_SENT:
  978.     case SCTP_STATE_SHUTDOWN_RECEIVED:
  979.     case SCTP_STATE_SHUTDOWN_ACK_SENT:
  980.     case SCTP_STATE_SHUTDOWN_PENDING:
  981.       break;
  982.     }
  983.   DBG_X(ProcessChunk);
  984.   return iThisOutDataSize;
  985. }
  986. /* This function is called as soon as we are done processing a SACK which 
  987.  * notifies the need of a fast rtx. Following RFC2960, we pack as many chunks
  988.  * as possible into one packet (PTMU restriction). The remaining marked packets
  989.  * are sent as soon as cwnd allows.
  990.  */
  991. void SctpCMTAgent::FastRtx()
  992. {
  993.   DBG_I(FastRtx);
  994.   Node_S *spCurrDestNode = NULL;
  995.   SctpDest_S *spCurrDestData = NULL;
  996.   Node_S *spCurrBuffNode = NULL;
  997.   SctpSendBufferNode_S *spCurrBuffData = NULL;
  998.   /* be sure we clear all the eCcApplied flags before using them!
  999.    */
  1000.   for(spCurrDestNode = sDestList.spHead;
  1001.       spCurrDestNode != NULL;
  1002.       spCurrDestNode = spCurrDestNode->spNext)
  1003.     {
  1004.       spCurrDestData = (SctpDest_S *) spCurrDestNode->vpData;
  1005.       spCurrDestData->eCcApplied = FALSE;
  1006.       /****** Begin CMT Change *******/
  1007.       /* JRI-TODO: Why do I need RTX_LIMIT_ZERO? Why not use LIMIT_CWND as
  1008.        * the default?
  1009.        */
  1010.       spCurrDestData->eRtxLimit = RTX_LIMIT_ZERO;
  1011.       /****** End CMT Change *******/
  1012.     }
  1013.   
  1014.   /* go thru chunks marked for rtx and cut back their ssthresh, cwnd, and
  1015.    * partial bytes acked. make sure we only apply congestion control once
  1016.    * per destination and once per window (ie, round-trip).
  1017.    */
  1018.   for(spCurrBuffNode = sSendBuffer.spHead;
  1019.       spCurrBuffNode != NULL;
  1020.       spCurrBuffNode = spCurrBuffNode->spNext)
  1021.     {
  1022.       spCurrBuffData = (SctpSendBufferNode_S *) spCurrBuffNode->vpData;
  1023.       /* If the chunk has been either marked for rtx or advanced ack, we want
  1024.        * to apply congestion control (assuming we didn't already).
  1025.        *
  1026.        * Why do we do it for advanced ack chunks? Well they were advanced ack'd
  1027.        * because they were lost. The ONLY reason we are not fast rtxing them is
  1028.        * because the chunk has run out of retransmissions (u-sctp). So we need
  1029.        * to still account for the fact they were lost... so apply congestion
  1030.        * control!
  1031.        */
  1032.       /****** Begin CMT Change *******/
  1033.       /* CMT: use per-destination uiRecover
  1034.        */
  1035.       if((spCurrBuffData->eMarkedForRtx != NO_RTX ||
  1036.   spCurrBuffData->eAdvancedAcked == TRUE) &&
  1037.  spCurrBuffData->spDest->eCcApplied == FALSE &&
  1038.  spCurrBuffData->spChunk->uiTsn > spCurrBuffData->spDest->uiRecover)
  1039. /****** End CMT Change *******/
  1040.   /* section 7.2.3 of rfc2960 (w/ implementor's guide) 
  1041.    */
  1042.   spCurrBuffData->spDest->iSsthresh 
  1043.     = MAX(spCurrBuffData->spDest->iCwnd/2, 
  1044.   iInitialCwnd * (int) uiMaxDataSize);
  1045.   spCurrBuffData->spDest->iCwnd = spCurrBuffData->spDest->iSsthresh;
  1046.   spCurrBuffData->spDest->iPartialBytesAcked = 0; //reset
  1047.   tiCwnd++; // trigger changes for trace to pick up
  1048.   spCurrBuffData->spDest->eCcApplied = TRUE;
  1049.   /* Cancel any pending RTT measurement on this
  1050.    * destination. Stephan Baucke (2004-04-27) suggested this
  1051.    * action as a fix for the following simple scenario:
  1052.    *
  1053.    * - Host A sends packets 1, 2 and 3 to host B, and choses 3 for
  1054.    *   an RTT measurement
  1055.    *
  1056.    * - Host B receives all packets correctly and sends ACK1, ACK2,
  1057.    *   and ACK3.
  1058.    *
  1059.    * - ACK2 and ACK3 are lost on the return path
  1060.    *
  1061.    * - Eventually a timeout fires for packet 2, and A retransmits 2
  1062.    *
  1063.    * - Upon receipt of 2, B sends a cumulative ACK3 (since it has
  1064.    *   received 2 & 3 before)
  1065.    *
  1066.    * - Since packet 3 has never been retransmitted, the SCTP code
  1067.    *   actually accepts the ACK for an RTT measurement, although it
  1068.    *   was sent in reply to the retransmission of 2, which results
  1069.    *   in a much too high RTT estimate. Since this case tends to
  1070.    *   happen in case of longer link interruptions, the error is
  1071.    *   often amplified by subsequent timer backoffs.
  1072.    */
  1073.   spCurrBuffData->spDest->eRtoPending = FALSE; 
  1074.   /****** Begin CMT Change *******/
  1075.   /* Set the recover variable to avoid multiple cwnd cuts for losses
  1076.    * in the same window (ie, round-trip).
  1077.    */
  1078.   spCurrBuffData->spDest->uiRecover = 
  1079.     GetHighestOutstandingTsn(spCurrBuffData->spDest);
  1080.   /****** End CMT Change *******/
  1081. }
  1082.       /****** Begin CMT Change *******/
  1083.       /* To specify which destinationto apply LIMIT_ONE_PACKET to. 
  1084.        * Earlier, every destination got one packet rtx out when 
  1085.        * rtxmarkedchunks was called with LIMIT_ONE_PACKET.
  1086.        */
  1087.       if(spCurrBuffData->eMarkedForRtx == FAST_RTX)
  1088.       {
  1089. spCurrBuffData->spDest->eRtxLimit = RTX_LIMIT_ONE_PACKET;
  1090.       }
  1091.       /****** End CMT Change *******/
  1092.     }
  1093.   /****** Begin CMT Change *******/
  1094.   eMarkedChunksPending = AnyMarkedChunks();
  1095.   /****** End CMT Change *******/
  1096.   /* possible that no chunks are pending retransmission since they could be 
  1097.    * advanced ack'd 
  1098.    */
  1099.   if(eMarkedChunksPending == TRUE)
  1100.     RtxMarkedChunks(RTX_LIMIT_ONE_PACKET);
  1101.   DBG_X(FastRtx);
  1102. }
  1103. void SctpCMTAgent::MarkChunkForRtx(SctpSendBufferNode_S *spNodeData, 
  1104.    MarkedForRtx_E eMarkedForRtx)
  1105. {
  1106.   DBG_I(MarkChunkForRtx);
  1107.   SctpDataChunkHdr_S  *spChunk = spNodeData->spChunk;
  1108.   SctpOutStream_S *spStream = &(spOutStreams[spChunk->usStreamId]);
  1109.   DBG_PL(MarkChunkForRtx, "tsn=%lu eMarkedForRtx=%s"), 
  1110.     spChunk->uiTsn,
  1111.     !eMarkedForRtx ? "NO_RTX" 
  1112.     : (eMarkedForRtx==FAST_RTX ? "FAST_RTX": "TIMEOUT_RTX") DBG_PR;
  1113.   spNodeData->eMarkedForRtx = eMarkedForRtx;
  1114.   uiPeerRwnd += spChunk->sHdr.usLength; // 6.2.1.C1 
  1115.   /* let's see if this chunk is on an unreliable stream.if so and the chunk has
  1116.    * run out of retransmissions,mark it as advanced acked and unmark it for rtx
  1117.    */
  1118.   if(spStream->eMode == SCTP_STREAM_UNRELIABLE)
  1119.     {
  1120.       /* have we run out of retransmissions??
  1121.        */
  1122.       if(spNodeData->iNumTxs > spNodeData->iUnrelRtxLimit)
  1123. {
  1124.   DBG_PL(MarkChunkForRtx, "giving up on tsn %lu..."),
  1125.     spChunk->uiTsn DBG_PR;
  1126.   spNodeData->eAdvancedAcked = TRUE;
  1127.   spNodeData->eMarkedForRtx = NO_RTX;
  1128.   spNodeData->spDest->iOutstandingBytes -= spChunk->sHdr.usLength;
  1129. }
  1130.     }
  1131.   
  1132.   if(spNodeData->eMarkedForRtx != NO_RTX)
  1133.     {
  1134.       eMarkedChunksPending = TRUE;
  1135.       /*** Begin CMT change ***/
  1136.       /* Also set eMarkedChunksPending for corresp. destination
  1137.        */
  1138.       spNodeData->spDest->eMarkedChunksPending = TRUE;
  1139.       /*** End of CMT change ***/
  1140.     }
  1141.   DBG_PL(MarkChunkForRtx, "uiPeerRwnd=%lu"), uiPeerRwnd DBG_PR;
  1142.   DBG_X(MarkChunkForRtx);
  1143. }
  1144. void SctpCMTAgent::recv(Packet *opInPkt, Handler*)
  1145. {
  1146.   /* Let's make sure that a Reset() is called, because it isn't always
  1147.    * called explicitly with the "reset" command. For example, wireless
  1148.    * nodes don't automatically "reset" their agents, but wired nodes do. 
  1149.    */
  1150.   if(eState == SCTP_STATE_UNINITIALIZED)
  1151.     Reset(); 
  1152.   DBG_I(recv);
  1153.   hdr_ip *spIpHdr = hdr_ip::access(opInPkt);
  1154.   PacketData *opInPacketData = (PacketData *) opInPkt->userdata();
  1155.   u_char *ucpInData = opInPacketData->data();
  1156.   u_char *ucpCurrInChunk = ucpInData;
  1157.   int iRemainingDataLen = opInPacketData->size();
  1158.   u_char *ucpOutData = new u_char[uiMaxPayloadSize];
  1159.   u_char *ucpCurrOutData = ucpOutData;
  1160.   /* local variable which maintains how much data has been filled in the 
  1161.    * current outgoing packet
  1162.    */
  1163.   int iOutDataSize = 0; 
  1164.   memset(ucpOutData, 0, uiMaxPayloadSize);
  1165.   memset(spSctpTrace, 0,
  1166.  (uiMaxPayloadSize / sizeof(SctpChunkHdr_S)) * sizeof(SctpTrace_S) );
  1167.   spReplyDest = GetReplyDestination(spIpHdr);
  1168.   eStartOfPacket = TRUE;
  1169.   do
  1170.     {
  1171.       DBG_PL(recv, "iRemainingDataLen=%d"), iRemainingDataLen DBG_PR;
  1172.       /* processing chunks may need to generate response chunks, so the
  1173.        * current outgoing packet *may* be filled in and our out packet's data
  1174.        * size is incremented to reflect the new data
  1175.        */
  1176.       iOutDataSize += ProcessChunk(ucpCurrInChunk, &ucpCurrOutData);
  1177.       NextChunk(&ucpCurrInChunk, &iRemainingDataLen);
  1178.     }
  1179.   while(ucpCurrInChunk != NULL);
  1180.   /* Let's see if we have any response chunks(currently only handshake related)
  1181.    * to transmit. 
  1182.    *
  1183.    * Note: We don't bundle these responses (yet!)
  1184.    */
  1185.   if(iOutDataSize > 0) 
  1186.     {
  1187.       SendPacket(ucpOutData, iOutDataSize, spReplyDest);
  1188.       DBG_PL(recv, "responded with control chunk(s)") DBG_PR;
  1189.     }
  1190.   /* Let's check to see if we need to generate and send a SACK chunk.
  1191.    *
  1192.    * Note: With uni-directional traffic, SACK and DATA chunks will not be 
  1193.    * bundled together in one packet.
  1194.    * Perhaps we will implement this in the future?  
  1195.    */
  1196.   if(eSackChunkNeeded == TRUE)
  1197.     {
  1198.       memset(ucpOutData, 0, uiMaxPayloadSize);
  1199.       iOutDataSize = BundleControlChunks(ucpOutData);
  1200.       iOutDataSize += GenChunk(SCTP_CHUNK_SACK, ucpOutData+iOutDataSize);
  1201.       SendPacket(ucpOutData, iOutDataSize, spReplyDest);
  1202.       DBG_PL(recv, "SACK sent (%d bytes)"), iOutDataSize DBG_PR;
  1203.       eSackChunkNeeded = FALSE;  // reset AFTER sent (o/w breaks dependencies)
  1204.     }
  1205.   /* Do we need to transmit a FORWARD TSN chunk??
  1206.    */
  1207.   if(eForwardTsnNeeded == TRUE)
  1208.     {
  1209.       memset(ucpOutData, 0, uiMaxPayloadSize);
  1210.       iOutDataSize = BundleControlChunks(ucpOutData);
  1211.       iOutDataSize += GenChunk(SCTP_CHUNK_FORWARD_TSN,ucpOutData+iOutDataSize);
  1212.       SendPacket(ucpOutData, iOutDataSize, spNewTxDest);
  1213.       DBG_PL(recv, "FORWARD TSN chunk sent") DBG_PR;
  1214.       eForwardTsnNeeded = FALSE; // reset AFTER sent (o/w breaks dependencies)
  1215.     }
  1216.   /***** Begin CMT Change *****/
  1217.   /* CMT change: We do not check to see if any rtxs are pending to be
  1218.    * sent.  Since rtxs can be sent to a different dest than the original,
  1219.    * the ordering rule of SCTP that rtxs need to be sent before txs is
  1220.    * blatantly ignored by CMT :)
  1221.    */
  1222.   /* Note: We aren't bundling with what was sent above, but we could. Just 
  1223.    * avoiding that for now... why? simplicity :-)
  1224.    */
  1225.   DBG_PL(recv, "eSendNewDataChunnks: %d"), eSendNewDataChunks DBG_PR;
  1226.   if(eSendNewDataChunks == TRUE)
  1227.     {
  1228.       SendMuch();       // Send new data till our cwnd is full!
  1229.       eSendNewDataChunks = FALSE; // reset AFTER sent (o/w breaks dependencies)
  1230.     }
  1231.   /***** End CMT Change *****/
  1232.   delete hdr_sctp::access(opInPkt)->SctpTrace();
  1233.   hdr_sctp::access(opInPkt)->SctpTrace() = NULL;
  1234.   Packet::free(opInPkt);
  1235.   opInPkt = NULL;
  1236.   DBG_X(recv);
  1237.   delete [] ucpOutData;
  1238. }
  1239. void SctpCMTAgent::SendMuch()
  1240. {
  1241.   DBG_I(SendMuch);
  1242.   DBG_PL(SendMuch, "eDataSource=%s"), 
  1243.     ( (eDataSource == DATA_SOURCE_APPLICATION)
  1244.       ? "DATA_SOURCE_APPLICATION" 
  1245.       : "DATA_SOURCE_INFINITE" )
  1246.     DBG_PR;
  1247.   u_char *ucpOutData = new u_char[uiMaxPayloadSize];
  1248.   int iOutDataSize = 0;
  1249.   double dCurrTime = Scheduler::instance().clock();
  1250.   /****** Begin CMT Change ******/
  1251.   Node_S *spCurrNode = NULL;
  1252.   static Node_S *spNodeLastSentTo = NULL;
  1253.   Node_S *spLoopStartNode = NULL;
  1254.   SctpDest_S *spSavedTxDest = NULL;
  1255.   SctpDest_S *spTempDest = NULL;
  1256.   /* spNewDest is a var used throughout the program to maintain vars for
  1257.    * dest to which new transmissions are sent, such as cwnd. Since we
  1258.    * choose which dest new data is sent to during this loop, we save the
  1259.    * previous value of this var, and then set it back at the end of this
  1260.    * function.
  1261.    */
  1262.   spSavedTxDest = spNewTxDest;
  1263.   /* CMT-PF: If all destinations are marked PF, select one from them for
  1264.    * data transmission 
  1265.    */
  1266.   if (eUseCmtPF == TRUE)
  1267.     {
  1268.       spTempDest = SelectFromPFDests();
  1269.       if (spTempDest != NULL)
  1270.       {
  1271.   /* spTempDest was PF and changed to Active by SelectFromPFDests()
  1272.    */
  1273.    tiCountPFToActiveNewData++; // trace will pick up
  1274. }
  1275.     }
  1276.   
  1277.   /* RR Scheduling: Start sending to the dest next to the one last sent to.
  1278.    */
  1279.   if (spNodeLastSentTo != NULL) 
  1280.     {
  1281.       spCurrNode = spNodeLastSentTo->spNext;
  1282.       if (spCurrNode == NULL) {
  1283. spCurrNode = sDestList.spHead;
  1284.       }
  1285.     } 
  1286.   else 
  1287.     spCurrNode = sDestList.spHead;
  1288.   
  1289.   spLoopStartNode = spCurrNode;
  1290.   DBG_PL(SendMuch,"RR debug: Entering loop with dest=%p"), 
  1291.     (SctpDest_S *) spLoopStartNode->vpData DBG_PR;
  1292.   /* Sending to destinations as cwnd permits. Check to see if we can send
  1293.    * on any destination address. If there is space available in any cwnd,
  1294.    * we send. Hence this bigger loop.
  1295.    */
  1296.   do {
  1297.       iOutDataSize = 0;
  1298.       spNewTxDest = (SctpDest_S *) spCurrNode->vpData;
  1299.       DBG_PL(SendMuch,"RR debug: In send loop, trying for dest=%p"), 
  1300.         spNewTxDest DBG_PR;
  1301.       /* CMT-PF: Do not send to failed or PF destinations! 
  1302.        */
  1303.       if (spNewTxDest->eStatus != SCTP_DEST_STATUS_ACTIVE) 
  1304. {
  1305.   /* RR Scheduling: get next dest
  1306.    */
  1307.   spCurrNode = spCurrNode->spNext;
  1308.   if (spCurrNode == NULL) 
  1309.     spCurrNode = sDestList.spHead;
  1310.   continue;
  1311. }
  1312.       
  1313.       /* At this point, if there are chunks marked for rtx, no new data
  1314.        * will be transmitted anyways to this dest, since there will be no
  1315.        * cwnd space left. Note that sendmuch() is called after
  1316.        * rtxmarkedchunks() is called, therefore if an rtx could be sent to
  1317.        * this dest, it has been sent already. There is no point in
  1318.        * checking whether any rtxs are pending on this dest. 
  1319.        * (The reason this check was done at this point in SCTP was because
  1320.        * SCTP did not send any new data on any path when rtxs had to be
  1321.        * sent. Therefore, even if rtxs were outstanding on the alternate,
  1322.        * no new data could be sent to the primary.  In CMT, we ignore this
  1323.        * policy.)
  1324.        */
  1325.       /****** End CMT Change ******/
  1326.       
  1327.       /* Keep sending out packets until our cwnd is full!  The proposed
  1328.        * correction to RFC2960 section 6.1.B says "The sender may exceed
  1329.        * cwnd by up to (PMTU-1) bytes on a new transmission if the cwnd is
  1330.        * not currently exceeded". Hence, as long as cwnd isn't
  1331.        * full... send another packet.  
  1332.        *
  1333.        * Also, if our data source is the application layer (instead of the 
  1334.        * infinite source used for ftp), check if there is any data buffered 
  1335.        * from the app layer. If so, then send as much as we can.
  1336.        */
  1337.       while((spNewTxDest->iOutstandingBytes < spNewTxDest->iCwnd) &&
  1338.             (eDataSource == DATA_SOURCE_INFINITE || 
  1339.      sAppLayerBuffer.uiLength != 0))
  1340.         {
  1341.   DBG_PL(SendMuch, "Dest=%p, status=%s"), spNewTxDest, 
  1342.     (spNewTxDest->eStatus == SCTP_DEST_STATUS_ACTIVE) ? "ACTIVE":"PF" 
  1343.     DBG_PR;
  1344.   DBG_PL(SendMuch, "uiAdvancedPeerAckPoint=%d uiCumAckPoint=%d"), 
  1345.     uiAdvancedPeerAckPoint, uiCumAckPoint DBG_PR;
  1346.   DBG_PL(SendMuch, "uiPeerRwnd=%d"), uiPeerRwnd DBG_PR;
  1347.   DBG_PL(SendMuch, "spNewTxDest->iCwnd=%d"), spNewTxDest->iCwnd DBG_PR;
  1348.   DBG_PL(SendMuch, "spNewTxDest->iPartialBytesAcked=%d"), 
  1349.     spNewTxDest->iPartialBytesAcked DBG_PR;
  1350.   DBG_PL(SendMuch, "spNewTxDest->iOutstandingBytes=%d"), 
  1351.     spNewTxDest->iOutstandingBytes DBG_PR;
  1352.   DBG_PL(SendMuch, "TotalOutstanding=%lu"), 
  1353.     TotalOutstanding() DBG_PR;
  1354.   DBG_PL(SendMuch, "eApplyMaxBurst=%s uiBurstLength=%d"),
  1355.     eApplyMaxBurst ? "TRUE" : "FALSE", uiBurstLength DBG_PR;
  1356.   
  1357.           if(GetNextDataChunkSize() <= uiPeerRwnd)
  1358.             {
  1359.               /* This addresses the proposed change to RFC2960 section 7.2.4,
  1360.                * regarding using of Max.Burst. We have an option which allows
  1361.                * to control if Max.Burst is applied.
  1362.                */
  1363.       /****** Begin CMT Change ******/
  1364.       /* CMT change: per destination max.burst
  1365.        */
  1366.               if(eUseMaxBurst == MAX_BURST_USAGE_ON)
  1367.                 if((eApplyMaxBurst == TRUE) && 
  1368.    ((spNewTxDest->uiBurstLength)++ >= MAX_BURST))
  1369.                   {
  1370.                     /* we've reached Max.Burst limit, so jump out of loop
  1371.                      */
  1372.     DBG_PL(SendMuch,"Burstlength=%d - max.burst reached"),
  1373.                       spNewTxDest->uiBurstLength DBG_PR;
  1374.                     break;
  1375.                   }
  1376.               DBG_PL(SendMuch,"Burstlength=%d"), 
  1377. spNewTxDest->uiBurstLength DBG_PR;
  1378.       /****** End CMT Change ******/
  1379.       
  1380.               memset(ucpOutData, 0, uiMaxPayloadSize); // reset
  1381.               iOutDataSize = BundleControlChunks(ucpOutData);
  1382.               iOutDataSize += GenMultipleDataChunks(ucpOutData+iOutDataSize,0);
  1383.               SendPacket(ucpOutData, iOutDataSize, spNewTxDest);
  1384.               DBG_PL(SendMuch, "DATA chunk(s) sent") DBG_PR;
  1385.             }
  1386.           else if(TotalOutstanding() == 0)  // section 6.1.A
  1387.             {
  1388.               /* probe for a change in peer's rwnd
  1389.                */
  1390.               memset(ucpOutData, 0, uiMaxPayloadSize);
  1391.               iOutDataSize = BundleControlChunks(ucpOutData);
  1392.               iOutDataSize += GenOneDataChunk(ucpOutData+iOutDataSize);
  1393.               SendPacket(ucpOutData, iOutDataSize, spNewTxDest);
  1394.               DBG_PL(SendMuch, "DATA chunk probe sent") DBG_PR;
  1395.             }
  1396.           else
  1397.             {
  1398.               break;
  1399.             }      
  1400.         }
  1401.       if(iOutDataSize > 0)  // did we send anything??
  1402.         {
  1403.           spNewTxDest->opCwndDegradeTimer->resched(spNewTxDest->dRto);
  1404.   if(uiHeartbeatInterval != 0)
  1405.             {
  1406.               spNewTxDest->dIdleSince = dCurrTime;
  1407.             }
  1408.   /****** Begin CMT Change ******/
  1409.           /* RR Scheduling: Maintain last dest data was sent to
  1410.            */
  1411.           spNodeLastSentTo = spCurrNode;
  1412.           DBG_PL(SendMuch,"RR debug: Sent to dest=%p, %d"), 
  1413.             spNewTxDest, iOutDataSize DBG_PR;
  1414.         }
  1415.       /* RR Scheduling: get next dest
  1416.        */
  1417.       spCurrNode = spCurrNode->spNext;
  1418.       if (spCurrNode == NULL)
  1419.         spCurrNode = sDestList.spHead;
  1420.   } while (spCurrNode != spLoopStartNode); // End of CMT destination loop
  1421.   /* Restore spNewTxDest */
  1422.   /* JRI-TODO: Check why spNewTxDest needs to be saved and restored.
  1423.    * This code can be cleaner if the value did not have to be maintained.
  1424.    */
  1425.   spNewTxDest = spSavedTxDest;
  1426.   /* this reset MUST happen at the end of the function, because the burst 
  1427.    * measurement is carried over from RtxMarkedChunks() if it was called.
  1428.    */
  1429.   /* CMT change: per destination burstlength
  1430.    */
  1431.   for(spCurrNode = sDestList.spHead;
  1432.       spCurrNode != NULL;
  1433.       spCurrNode = spCurrNode->spNext)
  1434.     {
  1435.       ((SctpDest_S*)spCurrNode->vpData)->uiBurstLength = 0; // reset
  1436.     }
  1437.   /****** End CMT Change ******/
  1438.     
  1439.   DBG_X(SendMuch);
  1440.   delete [] ucpOutData;
  1441. }
  1442. /* Handles timeouts for both DATA chunks and HEARTBEAT chunks. The actions are
  1443.  * slightly different for each. Covers rfc sections 6.3.3 and 8.3.
  1444.  */
  1445. void SctpCMTAgent::Timeout(SctpChunkType_E eChunkType, SctpDest_S *spDest)
  1446. {
  1447.   /*** Begin CMT change ***/
  1448.   SctpDest_S *spTraceDest;
  1449.   char cpOutString[500];
  1450.   /*** End CMT change ***/
  1451.   DBG_I(Timeout);
  1452.   DBG_PL(Timeout, "eChunkType=%s spDest=%p"), 
  1453.     (eChunkType == SCTP_CHUNK_DATA) ? "DATA" : "HEARTBEAT",
  1454.     spDest
  1455.     DBG_PR;
  1456.   double dCurrTime = Scheduler::instance().clock();
  1457.   DBG_PL(Timeout, "dCurrTime=%f"), dCurrTime DBG_PR;
  1458.   
  1459.   if(eChunkType == SCTP_CHUNK_DATA)
  1460.     {
  1461.       spDest->eRtxTimerIsRunning = FALSE;
  1462.       
  1463.       /* section 7.2.3 of rfc2960 (w/ implementor's guide v.02)
  1464.        */
  1465.       
  1466.       /* Why is there a conditional change to ssthresh below??
  1467.        * No information reg. the same in either  RFC or implementors guide.
  1468.        * During failure or high loss rates that result in back-to-back 
  1469.        * timeouts, ssthresh is not reduced after the first
  1470.        * timeout. Hence, CMT's RTX_SSTHRESH is at a disadvantage. 
  1471.        * NASIF FIX THIS: Confirm if this should be here. if not, remove
  1472.        * comment below. This might have to be fixed in sctp.cc's Timeout() as 
  1473.        * well.
  1474.        */
  1475.       /* Begin Change:
  1476.        *if(spDest->iCwnd > 1 * (int) uiMaxDataSize)
  1477.        * {
  1478.        *
  1479.        */
  1480.       spDest->iSsthresh 
  1481. = MAX(spDest->iCwnd/2, iInitialCwnd * (int)uiMaxDataSize);
  1482.       spDest->iCwnd = 1*uiMaxDataSize;
  1483.       
  1484.       spDest->iPartialBytesAcked = 0; // reset
  1485.       tiCwnd++; // trigger changes for trace to pick up
  1486.       
  1487.        /* } End Change */
  1488.       spDest->opCwndDegradeTimer->force_cancel();
  1489.       /* Cancel any pending RTT measurement on this destination. Stephan
  1490.        * Baucke suggested (2004-04-27) this action as a fix for the
  1491.        * following simple scenario:
  1492.        *
  1493.        * - Host A sends packets 1, 2 and 3 to host B, and choses 3 for
  1494.        *   an RTT measurement
  1495.        *
  1496.        * - Host B receives all packets correctly and sends ACK1, ACK2,
  1497.        *   and ACK3.
  1498.        *
  1499.        * - ACK2 and ACK3 are lost on the return path
  1500.        *
  1501.        * - Eventually a timeout fires for packet 2, and A retransmits 2
  1502.        *
  1503.        * - Upon receipt of 2, B sends a cumulative ACK3 (since it has
  1504.        *   received 2 & 3 before)
  1505.        *
  1506.        * - Since packet 3 has never been retransmitted, the SCTP code
  1507.        *   actually accepts the ACK for an RTT measurement, although it
  1508.        *   was sent in reply to the retransmission of 2, which results
  1509.        *   in a much too high RTT estimate. Since this case tends to
  1510.        *   happen in case of longer link interruptions, the error is
  1511.        *   often amplified by subsequent timer backoffs.
  1512.        */
  1513.       spDest->eRtoPending = FALSE; // cancel any pending RTT measurement
  1514.       /*** Begin CMT change ***/
  1515.       /* track data timeouts
  1516.        */
  1517.       spTraceDest = spDest;
  1518.       SetSource(spTraceDest); // gives us the correct source addr & port
  1519.       sprintf(cpOutString,
  1520.       "time: %-8.5f  "
  1521.       "saddr: %-2d sport: %-2d daddr: %-2d dport: %-2d "
  1522.       "DataTimeout, peerRwnd: %d rto: %-6.3f errCnt: %d n",
  1523.       dCurrTime,
  1524.       addr(), port(), spTraceDest->iNsAddr, spTraceDest->iNsPort,
  1525.       uiPeerRwnd, spTraceDest->dRto, spTraceDest->iErrorCount);
  1526.       if(channel_)
  1527.        (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
  1528.       sprintf(cpOutString, "n");
  1529.       if(channel_)
  1530.        (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
  1531.       /*** End CMT change ***/
  1532.     }
  1533.   DBG_PL(Timeout, "was spDest->dRto=%f"), spDest->dRto DBG_PR;
  1534.   spDest->dRto *= 2;    // back off the timer
  1535.   if(spDest->dRto > dMaxRto)
  1536.     spDest->dRto = dMaxRto;
  1537.   tdRto++;              // trigger changes for trace to pick up
  1538.   DBG_PL(Timeout, "now spDest->dRto=%f"), spDest->dRto DBG_PR;
  1539.   spDest->iTimeoutCount++;
  1540.   spDest->iErrorCount++; // @@@ window probe timeouts sould not be counted
  1541.   DBG_PL(Timeout, "now spDest->iErrorCount=%d"), spDest->iErrorCount DBG_PR;
  1542.   /*** Begin CMT change ***/
  1543.   if((spDest->eStatus == SCTP_DEST_STATUS_ACTIVE) || 
  1544. (spDest->eStatus == SCTP_DEST_STATUS_POSSIBLY_FAILED))
  1545.     {  
  1546.       iAssocErrorCount++;
  1547.       DBG_PL(Timeout, "now iAssocErrorCount=%d"), iAssocErrorCount DBG_PR;
  1548.       if ((spDest->iErrorCount == 1) && (eUseCmtPF == TRUE))
  1549. {
  1550.   spDest->eStatus = SCTP_DEST_STATUS_POSSIBLY_FAILED;
  1551.   spDest->dPFSince = dCurrTime;
  1552.   DBG_PL(Timeout, "Dest=%p Marked PF at time: %-8.5f"), spDest,
  1553.     spDest->dPFSince DBG_PR;
  1554.   
  1555.   if(uiHeartbeatInterval != 0)
  1556.     {
  1557.       spDest->opHeartbeatTimeoutTimer->force_cancel();
  1558.       DBG_PL(Timeout, "Dest=%p Cancelled Heartbeat Timer"),
  1559. spDest DBG_PR;
  1560.     } 
  1561. }
  1562.       /*** End CMT change ***/
  1563.       
  1564.       // Path.Max.Retrans exceeded?
  1565.       if(spDest->iErrorCount > (int) uiPathMaxRetrans) 
  1566. {
  1567.   spDest->eStatus = SCTP_DEST_STATUS_INACTIVE;
  1568.   if(spDest == spNewTxDest)
  1569.     {
  1570.       spNewTxDest = GetNextDest(spNewTxDest);
  1571.       DBG_PL(Timeout, "failing over from %p to %p"),
  1572. spDest, spNewTxDest DBG_PR;
  1573.     }
  1574. }
  1575.       if(iAssocErrorCount > (int) uiAssociationMaxRetrans)
  1576. {
  1577.   /* abruptly close the association!  (section 8.1)
  1578.    */
  1579.   DBG_PL(Timeout, "abruptly closing the association!") DBG_PR;
  1580.   Close();
  1581.   DBG_X(Timeout);
  1582.   return;
  1583. }
  1584.     }
  1585.   // trace it!
  1586.   tiTimeoutCount++;
  1587.   tiErrorCount++;       
  1588.   if(spDest->iErrorCount > (int) uiChangePrimaryThresh &&
  1589.      spDest == spPrimaryDest)
  1590.     {
  1591.       spPrimaryDest = spNewTxDest;
  1592.       DBG_PL(Timeout, "changing primary from %p to %p"),
  1593. spDest, spNewTxDest DBG_PR;
  1594.     }
  1595.   /*** Begin CMT change ***/
  1596.   if(eChunkType == SCTP_CHUNK_DATA)
  1597.     {
  1598.       TimeoutRtx(spDest);
  1599.       DBG_PL(Timeout, "Dest: %p, out: %d"), spDest, spDest->iOutstandingBytes
  1600. DBG_PR; 
  1601.       if(spDest->eStatus == SCTP_DEST_STATUS_POSSIBLY_FAILED) 
  1602.       {
  1603.    SendHeartbeat(spDest);
  1604.       } 
  1605.       if(spDest->eStatus == SCTP_DEST_STATUS_INACTIVE && 
  1606.  uiHeartbeatInterval!=0)
  1607. SendHeartbeat(spDest); // just marked inactive, so send HB immediately!
  1608.     }
  1609.   else if(eChunkType == SCTP_CHUNK_HB)
  1610.     {
  1611.       if((uiHeartbeatInterval != 0) ||
  1612.  (spDest->eStatus == SCTP_DEST_STATUS_POSSIBLY_FAILED) ||
  1613.  (spDest->eStatus == SCTP_DEST_STATUS_UNCONFIRMED))
  1614. SendHeartbeat(spDest);
  1615.     }
  1616.   /*** End CMT change ***/
  1617.   DBG_X(Timeout);  
  1618. }
  1619. /* CMT change: changed function to get highest outstanding TSN per dest
  1620.  */
  1621. u_int SctpCMTAgent::GetHighestOutstandingTsn(SctpDest_S *spOutstandingOnDest)
  1622. {
  1623.   DBG_I(GetHighestOutstandingTsn);
  1624.   u_int uiHighestOutstandingTsn = 0;
  1625.   Node_S *spCurrBuffNode = NULL;
  1626.   SctpSendBufferNode_S *spCurrBuffData = NULL;
  1627.   /* start from the tailof the send buffer and search towards the head for the 
  1628.    * first tsn oustanding... that's the highest outstanding tsn.
  1629.    */
  1630.   for(spCurrBuffNode = sSendBuffer.spTail;
  1631.       spCurrBuffNode != NULL;
  1632.       spCurrBuffNode = spCurrBuffNode->spPrev)
  1633.     {
  1634.       spCurrBuffData = (SctpSendBufferNode_S *) spCurrBuffNode->vpData;
  1635.       /****** Begin CMT Change ******/
  1636.       /* CMT change: changed function to get highest outstanding TSN per dest
  1637.        */
  1638.       if( (spCurrBuffData->spDest == spOutstandingOnDest)&&
  1639.   (spCurrBuffData->eMarkedForRtx == NO_RTX))  // is it oustanding?
  1640.         {
  1641.           uiHighestOutstandingTsn = spCurrBuffData->spChunk->uiTsn; //found it!
  1642.           break;
  1643.         }
  1644.       /****** End CMT Change ******/
  1645.     }
  1646.   DBG_PL(GetHighestOutstandingTsn, "uiHighestOutstandingTsn=%d"),
  1647.     uiHighestOutstandingTsn DBG_PR;
  1648.   DBG_X(GetHighestOutstandingTsn);
  1649.  
  1650.   return uiHighestOutstandingTsn;
  1651. }
  1652. void SctpCMTAgent::HeartbeatGenTimerExpiration(double dTimerStartTime)
  1653. {
  1654.   DBG_I(HeartbeatGenTimerExpiration);
  1655.   Node_S *spCurrNode = NULL;
  1656.   SctpDest_S *spCurrNodeData = NULL;
  1657.   Node_S *spLongestIdleNode = NULL;
  1658.   SctpDest_S *spLongestIdleNodeData = NULL;
  1659.   double dCurrTime = Scheduler::instance().clock();
  1660.   double dTime;
  1661.   DBG_PL(HeartbeatGenTimerExpiration, "Time: %-8.5f"), dCurrTime DBG_PR;
  1662.   
  1663.   DBG_PL(HeartbeatGenTimerExpiration, "finding the longest idle dest...") 
  1664.     DBG_PR;
  1665.   /* find the destination which has been idle the longest 
  1666.    */
  1667.   for(spCurrNode = spLongestIdleNode = sDestList.spHead;
  1668.       spCurrNode != NULL;
  1669.       spCurrNode = spCurrNode->spNext)
  1670.     {
  1671.       spCurrNodeData = (SctpDest_S *) spCurrNode->vpData;
  1672.       spLongestIdleNodeData = (SctpDest_S *) spLongestIdleNode->vpData;
  1673.       
  1674.       DBG_PL(HeartbeatGenTimerExpiration, "spDest=%p idle since %f"), 
  1675. spCurrNodeData, spCurrNodeData->dIdleSince DBG_PR;
  1676.   
  1677.       if(spCurrNodeData->dIdleSince < spLongestIdleNodeData->dIdleSince)
  1678. spLongestIdleNode = spCurrNode;
  1679.     }
  1680.   
  1681.   /* has it been idle long enough?
  1682.    */
  1683.   spLongestIdleNodeData = (SctpDest_S *) spLongestIdleNode->vpData;
  1684.   DBG_PL(HeartbeatGenTimerExpiration, "longest idle dest since %f"),
  1685.     spLongestIdleNodeData->dIdleSince DBG_PR;
  1686.   DBG_PL(HeartbeatGenTimerExpiration, "timer start time %f"),
  1687.     dTimerStartTime DBG_PR;
  1688.   
  1689.   /****** Begin CMT Change ******/
  1690.   /* For PF destinations, Timeout() (T3 timer expiration) function
  1691.    * sends out HBs. Snd HB here only if destination is not PF.
  1692.    */
  1693.   if(spLongestIdleNodeData->dIdleSince <= dTimerStartTime && 
  1694.      spLongestIdleNodeData->eStatus != SCTP_DEST_STATUS_POSSIBLY_FAILED )
  1695.     SendHeartbeat(spLongestIdleNodeData);
  1696.   else
  1697.     DBG_PL(HeartbeatGenTimerExpiration, 
  1698.    "longest idle dest not idle long enough!") DBG_PR;
  1699.   /****** End CMT Change ******/
  1700.   
  1701.   /* start the timer again...
  1702.    */
  1703.   dTime = CalcHeartbeatTime(spLongestIdleNodeData->dRto);
  1704.   opHeartbeatGenTimer->resched(dTime);
  1705.   opHeartbeatGenTimer->dStartTime = dCurrTime;
  1706.   DBG_X(HeartbeatGenTimerExpiration);
  1707. }
  1708. void SctpCMTAgent::SackGenTimerExpiration() // section 6.2
  1709. {
  1710.   DBG_I(SackGenTimerExpiration);
  1711.   u_char *ucpOutData = new u_char[uiMaxPayloadSize];
  1712.   int iOutDataSize = 0;
  1713.   memset(ucpOutData, 0, uiMaxPayloadSize);
  1714.   iDataPktCountSinceLastSack = 0; // reset 
  1715.   iOutDataSize = BundleControlChunks(ucpOutData);
  1716.   iOutDataSize += GenChunk(SCTP_CHUNK_SACK, ucpOutData+iOutDataSize);
  1717.   SendPacket(ucpOutData, iOutDataSize, spReplyDest); 
  1718.   DBG_PL(SackGenTimerExpiration, "SACK sent (%d bytes)"), iOutDataSize DBG_PR;
  1719.   /**** Begin CMT change ****/
  1720.   /* this variable should be reset only after GenChunk() is called.
  1721.    * GenChunk puts in the number of pkts into the SACK flags, which is
  1722.    * used by the sender to increment the missing report count (CMT DAC
  1723.    * algo). If this var is reset here, when a SACK is sent after SACK
  1724.    * timer expires, missing report count is incremented by 0 ! Moving line
  1725.    * to after SendPacket().
  1726.    */
  1727.    iDataPktCountSinceLastSack = 0; // reset 
  1728.   /**** End of CMT change ****/
  1729.   DBG_X(SackGenTimerExpiration);
  1730.   delete [] ucpOutData;
  1731. }
  1732. /****** Begin CMT Change ******/
  1733. /* New CMT function:
  1734.  * Function returns dest status as string for printing
  1735.  */
  1736. char* SctpCMTAgent::PrintDestStatus(SctpDest_S* spDest)
  1737. {
  1738.   switch(spDest->eStatus)
  1739.     {
  1740.     case SCTP_DEST_STATUS_ACTIVE:
  1741.       return "ACTIVE";
  1742.     case SCTP_DEST_STATUS_INACTIVE:
  1743.       return "INACTIVE";
  1744.     case SCTP_DEST_STATUS_POSSIBLY_FAILED:
  1745.       return "PF";
  1746.     case SCTP_DEST_STATUS_UNCONFIRMED:
  1747.       return "UNCONFIRMED";
  1748.     default:
  1749.       return "UNKNOWN";
  1750.     }
  1751. }
  1752. /* New CMT-PF method:
  1753.  *
  1754.  * If all destinations are marked PF, send data to one of
  1755.  * the destinations so that CMT-PF does not perform worse than
  1756.  * CMT.  
  1757.  *
  1758.  * Algorithm to select the PF destination:
  1759.  *   Select the dest with the least error count.
  1760.  *   If tie in errorcount values, then all destinations
  1761.  *   have "Possibly failed" to the same degree. So, select dest
  1762.  *   that was most recently active (marked PF most recently). 
  1763.  *
  1764.  * Implementation: 
  1765.  * If atleast 1 non-PF dest exists, return NULL
  1766.  * else, 
  1767.  * - select a dest based on the above algo.
  1768.  *  - set cwnd on the selected PF dest to 1 MTU
  1769.  *  - stop the HB timer if it is running. - hb goes to blackhole !
  1770.  *   hb loss cannot be detected. But, if hb-ack comes back,
  1771.  *   errorcount for the destination is cleared. Situation is
  1772.  *   similar to an ACTIVE dest recving hb. 
  1773.  *  - set eRtxTimerIsRunning to false -- when data is sent, 
  1774.  *    T3RTX timer be started by RtxMarkedChunks or GenOneDataChunk.
  1775.  * - change dest status to ACTIVE. This dest status is just for
  1776.  *   easier implementation and does not change anything in the
  1777.  *   CMT-PF details. This implementation uses the following rule:
  1778.  * If a dest is PF, only HBs are sent
  1779.  * If a dest is ACTIVE, data is sent
  1780.  * - Schedule HeartbeatGenTimer 
  1781.  *
  1782.  * Note: This implementation can be considered _aggressive_,
  1783.  * since, on the selected PF dest, a HB could've been sent and
  1784.  * before HB-ACK arrives, 1MTU of data will be sent.  
  1785.  * MAYBE, this aggressiveness can be
  1786.  * avoided with a better implementation - do not send HB on
  1787.  * a destination with the least error count? 
  1788.  */
  1789. SctpDest_S*  SctpCMTAgent::SelectFromPFDests()
  1790. {
  1791.   
  1792.   int iLeastErrorCount = (int) uiPathMaxRetrans; 
  1793.   double dMostRecentPFSince = 0; 
  1794.   Node_S *spCurrDestNode = NULL;
  1795.   SctpDest_S* spCurrDestNodeData = NULL;
  1796.   SctpDest_S* spSelectedDest = NULL;
  1797.   
  1798.   for(spCurrDestNode = sDestList.spHead;
  1799.       spCurrDestNode != NULL;
  1800.       spCurrDestNode = spCurrDestNode->spNext)
  1801.     { 
  1802.       spCurrDestNodeData = (SctpDest_S *) spCurrDestNode->vpData;
  1803.       
  1804.       if (spCurrDestNodeData->eStatus == SCTP_DEST_STATUS_POSSIBLY_FAILED)
  1805. {
  1806.   if (spCurrDestNodeData->iErrorCount < iLeastErrorCount) 
  1807.     {
  1808.       spSelectedDest = spCurrDestNodeData;
  1809.       iLeastErrorCount = spCurrDestNodeData->iErrorCount;
  1810.       dMostRecentPFSince = spCurrDestNodeData->dPFSince;
  1811.     }
  1812.   else if (spCurrDestNodeData->iErrorCount == iLeastErrorCount)
  1813.     {
  1814.       if (spCurrDestNodeData->dPFSince > dMostRecentPFSince)
  1815. {
  1816.   spSelectedDest = spCurrDestNodeData;
  1817.   iLeastErrorCount = spCurrDestNodeData->iErrorCount;
  1818.   dMostRecentPFSince = spCurrDestNodeData->dPFSince;
  1819. }
  1820.       else if (spCurrDestNodeData->dPFSince == dMostRecentPFSince)
  1821. {
  1822.   if (Random::random()&01)
  1823.     spSelectedDest = spCurrDestNodeData;
  1824. }
  1825.     } 
  1826.   
  1827.       else 
  1828. {
  1829.   /* Found a non-PF dest, return NULL 
  1830.    */
  1831.   return NULL;
  1832.   
  1833. }
  1834.       
  1835.     } 
  1836.   /* A PF destination was selected to be marked Active
  1837.    */
  1838.   if (spSelectedDest->eRtxTimerIsRunning == TRUE)
  1839.     StopT3RtxTimer(spSelectedDest);
  1840.   
  1841.   spSelectedDest->opHeartbeatTimeoutTimer->force_cancel();
  1842.   spSelectedDest->eHBTimerIsRunning = FALSE;
  1843.   
  1844.   spSelectedDest->iCwnd = 1*uiMaxDataSize;
  1845.   
  1846.   spSelectedDest->eStatus = SCTP_DEST_STATUS_ACTIVE;
  1847.   spSelectedDest->dPFSince = 0;
  1848.   
  1849.   //  DBG_PL(SelectFromPFDests, "Dest: %p changed from PF to Active"),
  1850.   // spSelectedDest  DBG_PR;
  1851.   
  1852.   return spSelectedDest;
  1853. }
  1854. /* New CMT function. Currently not used. */
  1855. void SctpCMTAgent::SetSharedCCParams(SctpDest_S *spCurrInfo)
  1856. {
  1857. // {
  1858. //   Node_S *spCurrNode = NULL;
  1859. //   SctpDest_S *spCurrDest = NULL;
  1860. //   for(spCurrNode = sDestList.spHead;
  1861. //       spCurrNode != NULL;
  1862. //       spCurrNode = spCurrNode->spNext)
  1863. //     {
  1864. //       spCurrDest = (SctpDest_S *) spCurrNode->vpData;
  1865. //       spCurrDest->iCwnd = spCurrInfo->iSharedCwnd;
  1866. //       spCurrDest->iSsthresh = spCurrInfo->iSharedSsthresh;
  1867. //     }
  1868. // }
  1869. }
  1870.  
  1871. /****** End CMT Change ******/