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

通讯编程

开发平台:

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. /* Timestamp extension adds a TIMESTAMP chunk into every packet with DATA
  38.  * or SACK chunk(s). The timestamps allow RTT measurements to be made per
  39.  * packet on both original transmissiosn and retransmissions. Thus, Karn's
  40.  * algorithm is no longer needed.
  41.  */
  42. #ifndef lint
  43. static const char rcsid[] =
  44. "@(#) $Header: /cvsroot/nsnam/ns-2/sctp/sctp-timestamp.cc,v 1.5 2007/06/17 21:44:44 tom_henderson Exp $ (UD/PEL)";
  45. #endif
  46. #include "ip.h"
  47. #include "sctp-timestamp.h"
  48. #include "flags.h"
  49. #include "random.h"
  50. #include "template.h"
  51. #include "sctpDebug.h"
  52. #ifdef DMALLOC
  53. #include "dmalloc.h"
  54. #endif
  55. #define MIN(x,y) (((x)<(y))?(x):(y))
  56. #define MAX(x,y) (((x)>(y))?(x):(y))
  57. static class TimestampSctpClass : public TclClass 
  58. public:
  59.   TimestampSctpClass() : TclClass("Agent/SCTP/Timestamp") {}
  60.   TclObject* create(int, const char*const*) 
  61.   {
  62.     return (new TimestampSctpAgent());
  63.   }
  64. } classSctpTimestamp;
  65. TimestampSctpAgent::TimestampSctpAgent() : SctpAgent()
  66. {
  67. }
  68. void TimestampSctpAgent::delay_bind_init_all()
  69. {
  70.   SctpAgent::delay_bind_init_all();
  71. }
  72. int TimestampSctpAgent::delay_bind_dispatch(const char *cpVarName, 
  73.     const char *cpLocalName, 
  74.     TclObject *opTracer)
  75. {
  76.   return SctpAgent::delay_bind_dispatch(cpVarName, cpLocalName, opTracer);
  77. }
  78. void TimestampSctpAgent::OptionReset()
  79. {
  80.   DBG_I(OptionReset);
  81.   eNeedTimestampEcho = FALSE;
  82.   DBG_X(OptionReset);
  83. }
  84. /* This function returns the reserved size needed for timestamp chunks.
  85.  */
  86. u_int TimestampSctpAgent::ControlChunkReservation()
  87. {
  88.   DBG_I(ControlChunkReservation);
  89.   DBG_PL(ControlChunkReservation, "returning %d"), 
  90.     sizeof(SctpTimestampChunk_S) DBG_PR;
  91.   DBG_X(ControlChunkReservation);
  92.   return sizeof(SctpTimestampChunk_S);
  93. }
  94. /* This function bundles control chunks with data chunks. We copy the timestamp
  95.  * chunk into the outgoing packet and return the size of the timestamp chunk.
  96.  */
  97. int TimestampSctpAgent::BundleControlChunks(u_char *ucpOutData)
  98. {
  99.   DBG_I(BundleControlChunks);
  100.   SctpTimestampChunk_S *spTimestampChunk = (SctpTimestampChunk_S *) ucpOutData;
  101.   spTimestampChunk->sHdr.usLength = sizeof(SctpTimestampChunk_S);
  102.   /* We need to send a timestamp in the timestamp chunk when sending data
  103.    * chunks (and not sack or foward tsn chunks)... otherwise, we may only
  104.    * need the timestamp echo to be set.
  105.    */
  106.   if((eSendNewDataChunks == TRUE || eMarkedChunksPending == TRUE) && 
  107.      eSackChunkNeeded == FALSE  && 
  108.      eForwardTsnNeeded == FALSE)
  109.     {
  110.       spTimestampChunk->sHdr.ucType = SCTP_CHUNK_TIMESTAMP;
  111.       spTimestampChunk->sHdr.ucFlags |= SCTP_TIMESTAMP_FLAG_TS;
  112.       spTimestampChunk->fTimestamp = (float) Scheduler::instance().clock();
  113.     }
  114.   if(eNeedTimestampEcho == TRUE)
  115.     {
  116.       spTimestampChunk->sHdr.ucType = SCTP_CHUNK_TIMESTAMP;
  117.       spTimestampChunk->sHdr.ucFlags |= SCTP_TIMESTAMP_FLAG_ECHO;
  118.       spTimestampChunk->fEcho = fOutTimestampEcho;
  119.       eNeedTimestampEcho = FALSE;
  120.     }
  121.   DBG_PL(BundleControlChunks, "returning %d"), 
  122.     spTimestampChunk->sHdr.usLength DBG_PR;
  123.   DBG_X(BundleControlChunks);
  124.   return spTimestampChunk->sHdr.usLength;
  125. }
  126. void TimestampSctpAgent::AddToSendBuffer(SctpDataChunkHdr_S *spChunk, 
  127. int iChunkSize,
  128. u_int uiReliability,
  129. SctpDest_S *spDest)
  130. {
  131.   DBG_I(AddToSendBuffer);
  132.   DBG_PL(AddToSendBuffer, "spDest=%p  iChunkSize=%d"), 
  133.     spDest, iChunkSize DBG_PR;
  134.   Node_S *spNewNode = new Node_S;
  135.   spNewNode->eType = NODE_TYPE_SEND_BUFFER;
  136.   spNewNode->vpData = new SctpSendBufferNode_S;
  137.   SctpSendBufferNode_S * spNewNodeData 
  138.     = (SctpSendBufferNode_S *) spNewNode->vpData;
  139.   /* This can NOT simply be a 'new SctpDataChunkHdr_S', because we need to
  140.    * allocate the space for the ENTIRE data chunk and not just the data
  141.    * chunk header.  
  142.    */
  143.   spNewNodeData->spChunk = (SctpDataChunkHdr_S *) new u_char[iChunkSize];
  144.   memcpy(spNewNodeData->spChunk, spChunk, iChunkSize);
  145.   spNewNodeData->eAdvancedAcked = FALSE;
  146.   spNewNodeData->eGapAcked = FALSE;
  147.   spNewNodeData->eAddedToPartialBytesAcked = FALSE;
  148.   spNewNodeData->iNumMissingReports = 0;
  149.   spNewNodeData->iUnrelRtxLimit = uiReliability;
  150.   spNewNodeData->eMarkedForRtx = NO_RTX;
  151.   spNewNodeData->eIneligibleForFastRtx = FALSE;
  152.   spNewNodeData->iNumTxs = 1;
  153.   spNewNodeData->spDest = spDest;
  154.   // BEGIN -- Timestamp changes to this function
  155.   spNewNodeData->dTxTimestamp = Scheduler::instance().clock();
  156.   // END -- Timestamp changes to this function
  157.   InsertNode(&sSendBuffer, sSendBuffer.spTail, spNewNode, NULL);
  158.   DBG_X(AddToSendBuffer);
  159. }
  160. /* Go thru the send buffer deleting all chunks which have a tsn <= the 
  161.  * tsn parameter passed in. We assume the chunks in the rtx list are ordered by 
  162.  * their tsn value. In addtion, for each chunk deleted:
  163.  *   1. we add the chunk length to # newly acked bytes and partial bytes acked
  164.  *   2. we update round trip time if appropriate
  165.  *   3. stop the timer if the chunk's destination timer is running
  166.  */
  167. void TimestampSctpAgent::SendBufferDequeueUpTo(u_int uiTsn)
  168. {
  169.   DBG_I(SendBufferDequeueUpTo);
  170.   Node_S *spDeleteNode = NULL;
  171.   Node_S *spCurrNode = sSendBuffer.spHead;
  172.   SctpSendBufferNode_S *spCurrNodeData = NULL;
  173.   iAssocErrorCount = 0;
  174.   while(spCurrNode != NULL &&
  175. ((SctpSendBufferNode_S*)spCurrNode->vpData)->spChunk->uiTsn <= uiTsn)
  176.     {
  177.       spCurrNodeData = (SctpSendBufferNode_S *) spCurrNode->vpData;
  178.       /* Only count this chunk as newly acked and towards partial bytes
  179.        * acked if it hasn't been gap acked or marked as ack'd due to rtx
  180.        * limit.  
  181.        */
  182.       if((spCurrNodeData->eGapAcked == FALSE) &&
  183.  (spCurrNodeData->eAdvancedAcked == FALSE) )
  184. {
  185.   uiHighestTsnNewlyAcked = spCurrNodeData->spChunk->uiTsn;
  186.   spCurrNodeData->spDest->iNumNewlyAckedBytes 
  187.     += spCurrNodeData->spChunk->sHdr.usLength;
  188.   /* only add to partial bytes acked if we are in congestion
  189.    * avoidance mode and if there was cwnd amount of data
  190.    * outstanding on the destination (implementor's guide) 
  191.    */
  192.   if(spCurrNodeData->spDest->iCwnd >spCurrNodeData->spDest->iSsthresh &&
  193.      ( spCurrNodeData->spDest->iOutstandingBytes 
  194.        >= spCurrNodeData->spDest->iCwnd) )
  195.     {
  196.       spCurrNodeData->spDest->iPartialBytesAcked 
  197. += spCurrNodeData->spChunk->sHdr.usLength;
  198.     }
  199. }
  200.       /* This is to ensure that Max.Burst is applied when a SACK
  201.        * acknowledges a chunk which has been fast retransmitted. If it is
  202.        * ineligible for fast rtx, that can only be because it was fast
  203.        * rtxed or it timed out. If it timed out, a burst shouldn't be
  204.        * possible, but shouldn't hurt either. The fast rtx case is what we
  205.        * are really after. This is a proposed change to RFC2960 section
  206.        * 7.2.4
  207.        */
  208.       if(spCurrNodeData->eIneligibleForFastRtx == TRUE)
  209. eApplyMaxBurst = TRUE;
  210.       // BEGIN -- Timestamp changes to this function
  211.       /* We update the RTT estimate if the following hold true:
  212.        *   1. Timestamp set for this chunk matches echoed timestamp
  213.        *   2. This chunk has not been gap acked already 
  214.        *   3. This chunk has not been advanced acked (pr-sctp: exhausted rtxs)
  215.        */
  216.       if(fInTimestampEcho == (float) spCurrNodeData->dTxTimestamp &&
  217.  spCurrNodeData->eGapAcked == FALSE &&
  218.  spCurrNodeData->eAdvancedAcked == FALSE) 
  219. {
  220.   RttUpdate(spCurrNodeData->dTxTimestamp, spCurrNodeData->spDest);
  221. }
  222.       // END -- Timestamp changes to this function
  223.       
  224.       /* if there is a timer running on the chunk's destination, then stop it
  225.        */
  226.       if(spCurrNodeData->spDest->eRtxTimerIsRunning == TRUE)
  227. StopT3RtxTimer(spCurrNodeData->spDest);
  228.       /* We don't want to clear the error counter if it's cleared already;
  229.        * otherwise, we'll unnecessarily trigger a trace event.
  230.        *
  231.        * Also, the error counter is cleared by SACKed data ONLY if the
  232.        * TSNs are not marked for timeout retransmission and has not been
  233.        * gap acked before. Without this condition, we can run into a
  234.        * problem for failure detection. When a failure occurs, some data
  235.        * may have made it through before the failure, but the sacks got
  236.        * lost. When the sender retransmits the first outstanding, the
  237.        * receiver will sack all the data whose sacks got lost. We don't
  238.        * want these sacks to clear the error counter, or else failover
  239.        * would take longer.
  240.        */
  241.       if(spCurrNodeData->spDest->iErrorCount != 0 &&
  242.  spCurrNodeData->eMarkedForRtx != TIMEOUT_RTX &&
  243.  spCurrNodeData->eGapAcked == FALSE)
  244. {
  245.   DBG_PL(SendBufferDequeueUpTo, 
  246.  "clearing error counter for %p with tsn=%lu"), 
  247.     spCurrNodeData->spDest, spCurrNodeData->spChunk->uiTsn DBG_PR;
  248.   spCurrNodeData->spDest->iErrorCount = 0; // clear error counter
  249.   tiErrorCount++;                          // ... and trace it too!
  250.   spCurrNodeData->spDest->eStatus = SCTP_DEST_STATUS_ACTIVE;
  251.   if(spCurrNodeData->spDest == spPrimaryDest &&
  252.      spNewTxDest != spPrimaryDest) 
  253.     {
  254.       DBG_PL(SendBufferDequeueUpTo,
  255.      "primary recovered... migrating back from %p to %p"),
  256. spNewTxDest, spPrimaryDest DBG_PR;
  257.       spNewTxDest = spPrimaryDest; // return to primary
  258.     }
  259. }
  260.       spDeleteNode = spCurrNode;
  261.       spCurrNode = spCurrNode->spNext;
  262.       DeleteNode(&sSendBuffer, spDeleteNode);
  263.       spDeleteNode = NULL;
  264.     }
  265.   DBG_X(SendBufferDequeueUpTo);
  266. }
  267. /* This function goes through the entire send buffer filling a packet with 
  268.  * chunks marked for retransmission. Once a packet is full (according to MTU)
  269.  * it is transmittted. If the eLimit is one packet, than that is all that is
  270.  * done. If the eLimit is cwnd, then packets full of marked tsns are sent until
  271.  * cwnd is full.
  272.  */
  273. void TimestampSctpAgent::RtxMarkedChunks(SctpRtxLimit_E eLimit)
  274. {
  275.   DBG_I(RtxMarkedChunks);
  276.   u_char *ucpOutData = new u_char[uiMaxPayloadSize];
  277.   u_char *ucpCurrOutData = ucpOutData;
  278.   int iBundledControlChunkSize = 0;
  279.   int iCurrSize = 0;
  280.   int iOutDataSize = 0;
  281.   Node_S *spCurrBuffNode = NULL;
  282.   SctpSendBufferNode_S *spCurrBuffNodeData = NULL;
  283.   SctpDataChunkHdr_S  *spCurrChunk;
  284.   SctpDest_S *spRtxDest = NULL;
  285.   Node_S *spCurrDestNode = NULL;
  286.   SctpDest_S *spCurrDestNodeData = NULL;
  287.   Boolean_E eControlChunkBundled = FALSE;
  288.   int iNumPacketsSent = 0;
  289.   memset(ucpOutData, 0, uiMaxPayloadSize);
  290.   uiBurstLength = 0;
  291.   /* make sure we clear all the spFirstOutstanding pointers before using them!
  292.    */
  293.   for(spCurrDestNode = sDestList.spHead;
  294.       spCurrDestNode != NULL;
  295.       spCurrDestNode = spCurrDestNode->spNext)
  296.     {
  297.       spCurrDestNodeData = (SctpDest_S *) spCurrDestNode->vpData;
  298.       spCurrDestNodeData->spFirstOutstanding = NULL;  // reset
  299.     }
  300.   /* We need to set the destination address for the retransmission(s). We assume
  301.    * that on a given call to this function, all should all be sent to the same
  302.    * address (should be a reasonable assumption). So, to determine the address,
  303.    * we find the first marked chunk and determine the destination it was last 
  304.    * sent to. 
  305.    *
  306.    * Also, we temporarily count all marked chunks as not outstanding. Why? Well,
  307.    * if we try retransmitting on the same dest as used previously, the cwnd may
  308.    * never let us retransmit because the outstanding is counting marked chunks
  309.    * too. At the end of this function, we'll count all marked chunks as 
  310.    * outstanding again. (ugh... there has to be a better way!)
  311.    */
  312.   for(spCurrBuffNode = sSendBuffer.spHead; 
  313.       spCurrBuffNode != NULL;
  314.       spCurrBuffNode = spCurrBuffNode->spNext)
  315.     {
  316.       spCurrBuffNodeData = (SctpSendBufferNode_S *) spCurrBuffNode->vpData;
  317.   
  318.       if(spCurrBuffNodeData->eMarkedForRtx != NO_RTX)
  319. {
  320.   spCurrChunk = spCurrBuffNodeData->spChunk;
  321.   if(spRtxDest == NULL)
  322.     {
  323.       /* RFC2960 says that retransmissions should go to an
  324.        * alternate destination when available, which is the
  325.        * default behavior characterized by
  326.        * eRtxToAlt=RTX_TO_ALT_ON. 
  327.        *
  328.        * We add two experimental options:
  329.        *    1. rtx all data to same destination (RTX_TO_ALT_OFF)
  330.        *    2. rtx only timeouts to alt dest (RTX_TO_ALT_TIMEOUTS_ONLY)
  331.        *
  332.        * Note: Even with these options, if the same dest is inactive,
  333.        * then alt dest is used.
  334.        */
  335.       switch(eRtxToAlt)
  336. {
  337. case RTX_TO_ALT_OFF:
  338.   if(spCurrBuffNodeData->spDest->eStatus 
  339.      == SCTP_DEST_STATUS_ACTIVE)
  340.     {
  341.       spRtxDest = spCurrBuffNodeData->spDest;
  342.     }
  343.   else
  344.     {
  345.       spRtxDest = GetNextDest(spCurrBuffNodeData->spDest);
  346.     }
  347.   break;
  348.   
  349. case RTX_TO_ALT_ON:
  350.   spRtxDest = GetNextDest(spCurrBuffNodeData->spDest);
  351.   break;
  352. case RTX_TO_ALT_TIMEOUTS_ONLY:
  353.   if(spCurrBuffNodeData->eMarkedForRtx == FAST_RTX &&
  354.      spCurrBuffNodeData->spDest->eStatus 
  355.      == SCTP_DEST_STATUS_ACTIVE)
  356.     {
  357.       spRtxDest = spCurrBuffNodeData->spDest; 
  358.     }
  359.   else
  360.     {
  361.       spRtxDest = GetNextDest(spCurrBuffNodeData->spDest);
  362.     }
  363.   break;
  364. }
  365.     }
  366.   spCurrBuffNodeData->spDest->iOutstandingBytes
  367.     -= spCurrChunk->sHdr.usLength;
  368. }
  369.     }
  370.   spCurrBuffNode = sSendBuffer.spHead;
  371.   while( (eLimit == RTX_LIMIT_ONE_PACKET && 
  372.   iNumPacketsSent < 1 && 
  373.   spCurrBuffNode != NULL) ||
  374.  (eLimit == RTX_LIMIT_CWND &&
  375.   spRtxDest->iOutstandingBytes < spRtxDest->iCwnd &&
  376.   spCurrBuffNode != NULL) )
  377.     {
  378.       DBG_PL(RtxMarkedChunks, 
  379.      "eLimit=%s pktsSent=%d out=%d cwnd=%d spCurrBuffNode=%p"),
  380. (eLimit == RTX_LIMIT_ONE_PACKET) ? "ONE_PACKET" : "CWND",
  381. iNumPacketsSent, spRtxDest->iOutstandingBytes, spRtxDest->iCwnd,
  382. spCurrBuffNode
  383. DBG_PR;
  384.       
  385.       /* section 7.2.4.3
  386.        *
  387.        * continue filling up the packet with chunks which are marked for
  388.        * rtx. exit loop when we have either run out of chunks or the
  389.        * packet is full.
  390.        *
  391.        * note: we assume at least one data chunk fits in the packet.  
  392.        */
  393.       for(eControlChunkBundled = FALSE; 
  394.   spCurrBuffNode != NULL; 
  395.   spCurrBuffNode = spCurrBuffNode->spNext)
  396. {
  397.   spCurrBuffNodeData = (SctpSendBufferNode_S *) spCurrBuffNode->vpData;
  398.   
  399.   /* is this chunk the first outstanding on its destination?
  400.    */
  401.     if(spCurrBuffNodeData->spDest->spFirstOutstanding == NULL &&
  402.      spCurrBuffNodeData->eGapAcked == FALSE &&
  403.      spCurrBuffNodeData->eAdvancedAcked == FALSE)
  404.     {
  405.       /* yes, it is the first!
  406.        */
  407.       spCurrBuffNodeData->spDest->spFirstOutstanding 
  408. = spCurrBuffNodeData;
  409.     }
  410.   /* Only retransmit the chunks which have been marked for rtx.
  411.    */
  412.   if(spCurrBuffNodeData->eMarkedForRtx != NO_RTX)
  413.     {
  414.       spCurrChunk = spCurrBuffNodeData->spChunk;
  415.       /* bundle the control chunk before any data chunks and only
  416.        * once per packet
  417.        */
  418.       if(eControlChunkBundled == FALSE)
  419. {
  420.   eControlChunkBundled = TRUE;
  421.   iBundledControlChunkSize =BundleControlChunks(ucpCurrOutData);
  422.   ucpCurrOutData += iBundledControlChunkSize;
  423.   iOutDataSize += iBundledControlChunkSize;
  424. }
  425.       /* can we fit this chunk into the packet without exceeding MTU?? 
  426.        */
  427.          if((iOutDataSize + spCurrChunk->sHdr.usLength) 
  428.  > (int) uiMaxPayloadSize)
  429.   break;  // doesn't fit in packet... jump out of the for loop
  430.       /* If this chunk was being used to measure the RTT, stop using it.
  431.        */
  432.       if(spCurrBuffNodeData->spDest->eRtoPending == TRUE &&
  433.  spCurrBuffNodeData->dTxTimestamp > 0)
  434. {
  435.   spCurrBuffNodeData->dTxTimestamp = 0;
  436.   spCurrBuffNodeData->spDest->eRtoPending = FALSE;
  437. }
  438.       /* section 7.2.4.4 (condition 2) - is this the first
  439.        * outstanding for the destination and are there still
  440.        * outstanding bytes on the destination? if so, restart
  441.        * timer.  
  442.        */
  443.       if(spCurrBuffNodeData->spDest->spFirstOutstanding 
  444.  == spCurrBuffNodeData)
  445. {
  446.   if(spCurrBuffNodeData->spDest->iOutstandingBytes > 0)
  447.     StartT3RtxTimer(spCurrBuffNodeData->spDest);
  448. }
  449.       /* JRI, ALC - Bugfix 2004/01/21 - section 6.1 - Whenever a
  450.        * transmission or retransmission is made to any address, if
  451.        * the T3-rtx timer of that address is not currently
  452.        * running, the sender MUST start that timer.  If the timer
  453.        * for that address is already running, the sender MUST
  454.        * restart the timer if the earliest (i.e., lowest TSN)
  455.        * outstanding DATA chunk sent to that address is being
  456.        * retransmitted.  Otherwise, the data sender MUST NOT
  457.        * restart the timer.  
  458.        */
  459.       if(spRtxDest->spFirstOutstanding == NULL ||
  460.  spCurrChunk->uiTsn <
  461.  spRtxDest->spFirstOutstanding->spChunk->uiTsn)
  462. {
  463.   /* This chunk is now the first outstanding on spRtxDest.
  464.    */
  465.   spRtxDest->spFirstOutstanding = spCurrBuffNodeData;
  466.   StartT3RtxTimer(spRtxDest);
  467. }
  468.       
  469.       memcpy(ucpCurrOutData, spCurrChunk, spCurrChunk->sHdr.usLength);
  470.       iCurrSize = spCurrChunk->sHdr.usLength;
  471.       /* the chunk length field does not include the padded bytes,
  472.        * so we need to account for these extra bytes.
  473.        */
  474.       if( (iCurrSize % 4) != 0 ) 
  475. iCurrSize += 4 - (iCurrSize % 4);
  476.       ucpCurrOutData += iCurrSize;
  477.       iOutDataSize += iCurrSize;
  478.       spCurrBuffNodeData->spDest = spRtxDest;
  479.       spCurrBuffNodeData->iNumTxs++;
  480.       spCurrBuffNodeData->eMarkedForRtx = NO_RTX;
  481.       
  482.       // BEGIN -- Timestamp changes to this function
  483.       spCurrBuffNodeData->dTxTimestamp = Scheduler::instance().clock();
  484.       // END -- Timestamp changes to this function
  485.       /* fill in tracing fields too
  486.        */
  487.       spSctpTrace[uiNumChunks].eType = SCTP_CHUNK_DATA;
  488.       spSctpTrace[uiNumChunks].uiTsn = spCurrChunk->uiTsn;
  489.       spSctpTrace[uiNumChunks].usStreamId = spCurrChunk->usStreamId;
  490.       spSctpTrace[uiNumChunks].usStreamSeqNum 
  491. = spCurrChunk->usStreamSeqNum;
  492.       uiNumChunks++;
  493.       /* the chunk is now outstanding on the alternate destination
  494.        */
  495.       spCurrBuffNodeData->spDest->iOutstandingBytes
  496. += spCurrChunk->sHdr.usLength;
  497.       uiPeerRwnd -= spCurrChunk->sHdr.usLength; // 6.2.1.B
  498.       DBG_PL(RtxMarkedChunks, "spDest->iOutstandingBytes=%d"), 
  499. spCurrBuffNodeData->spDest->iOutstandingBytes DBG_PR;
  500.       DBG_PL(RtxMarkedChunks, "TSN=%d"), spCurrChunk->uiTsn DBG_PR;
  501.     }
  502.   else if(spCurrBuffNodeData->eAdvancedAcked == TRUE)
  503.     {
  504.       if(spCurrBuffNodeData->spDest->spFirstOutstanding 
  505.  == spCurrBuffNodeData)
  506. {
  507.   /* This WAS considered the first outstanding chunk for
  508.    * the destination, then stop the timer if there are no
  509.    * outstanding chunks waiting behind this one in the
  510.    * send buffer.  However, if there ARE more outstanding
  511.    * chunks on this destination, we need to restart timer
  512.    * for those.
  513.    */
  514.   if(spCurrBuffNodeData->spDest->iOutstandingBytes > 0)
  515.     StartT3RtxTimer(spCurrBuffNodeData->spDest);
  516.   else
  517.     StopT3RtxTimer(spCurrBuffNodeData->spDest);
  518. }
  519.     }
  520. }
  521.       /* Transmit the packet now...
  522.        */
  523.       if(iOutDataSize > 0)
  524. {
  525.   SendPacket(ucpOutData, iOutDataSize, spRtxDest);
  526.   if(spRtxDest->eRtxTimerIsRunning == FALSE)
  527.     StartT3RtxTimer(spRtxDest);
  528.   iNumPacketsSent++;   
  529.   iOutDataSize = 0; // reset
  530.   ucpCurrOutData = ucpOutData; // reset
  531.   memset(ucpOutData, 0, uiMaxPayloadSize); // reset
  532.   spRtxDest->opCwndDegradeTimer->resched(spRtxDest->dRto);
  533.   /* This addresses the proposed change to RFC2960 section 7.2.4,
  534.    * regarding using of Max.Burst. We have an option which allows
  535.    * to control if Max.Burst is applied.
  536.    */
  537.   if(eUseMaxBurst == MAX_BURST_USAGE_ON)
  538.     if( (eApplyMaxBurst == TRUE) && (uiBurstLength++ >= MAX_BURST) )
  539.       {
  540. /* we've reached Max.Burst limit, so jump out of loop
  541.  */
  542. eApplyMaxBurst = FALSE; // reset before jumping out of loop
  543. break;
  544.       }
  545. }
  546.     } 
  547.   /* Ok, let's count all marked chunks as outstanding again. (ugh... there
  548.    * has to be a better way!)  
  549.    */
  550.   for(spCurrBuffNode = sSendBuffer.spHead; 
  551.       spCurrBuffNode != NULL;
  552.       spCurrBuffNode = spCurrBuffNode->spNext)
  553.     {
  554.       spCurrBuffNodeData = (SctpSendBufferNode_S *) spCurrBuffNode->vpData;
  555.   
  556.       if(spCurrBuffNodeData->eMarkedForRtx != NO_RTX)
  557. {
  558.   spCurrChunk = spCurrBuffNodeData->spChunk;
  559.   spCurrBuffNodeData->spDest->iOutstandingBytes
  560.     += spCurrChunk->sHdr.usLength;
  561. }
  562.     }
  563.     
  564.   /* If we made it here, either our limit was only one packet worth of
  565.    * retransmissions or we hit the end of the list and there are no more
  566.    * marked chunks. If we didn't hit the end, let's see if there are more marked
  567.    * chunks.
  568.    */
  569.   eMarkedChunksPending = AnyMarkedChunks();
  570.   delete[] ucpOutData;
  571.   DBG_X(RtxMarkedChunks);
  572. }
  573. /* returns a boolean of whether a fast retransmit is necessary
  574.  */
  575. Boolean_E TimestampSctpAgent::ProcessGapAckBlocks(u_char *ucpSackChunk,
  576.   Boolean_E eNewCumAck)
  577. {
  578.   DBG_I(ProcessGapAckBlocks);
  579.   Boolean_E eFastRtxNeeded = FALSE;
  580.   u_int uiHighestTsnSacked = uiHighestTsnNewlyAcked;
  581.   u_int uiStartTsn;
  582.   u_int uiEndTsn;
  583.   Node_S *spCurrNode = NULL;
  584.   SctpSendBufferNode_S *spCurrNodeData = NULL;
  585.   Node_S *spCurrDestNode = NULL;
  586.   SctpDest_S *spCurrDestNodeData = NULL;
  587.   SctpSackChunk_S *spSackChunk = (SctpSackChunk_S *) ucpSackChunk;
  588.   u_short usNumGapAcksProcessed = 0;
  589.   SctpGapAckBlock_S *spCurrGapAck 
  590.     = (SctpGapAckBlock_S *) (ucpSackChunk + sizeof(SctpSackChunk_S));
  591.   DBG_PL(ProcessGapAckBlocks,"CumAck=%d"), spSackChunk->uiCumAck DBG_PR;
  592.   if(sSendBuffer.spHead == NULL) // do we have ANYTHING in the rtx buffer?
  593.     {
  594.       /* This COULD mean that this sack arrived late, and a previous one
  595.        * already cum ack'd everything. ...so, what do we do? nothing??
  596.        */
  597.     }
  598.   
  599.   else // we do have chunks in the rtx buffer
  600.     {
  601.       /* make sure we clear all the spFirstOutstanding pointers before
  602.        * using them!  
  603.        */
  604.       for(spCurrDestNode = sDestList.spHead;
  605.   spCurrDestNode != NULL;
  606.   spCurrDestNode = spCurrDestNode->spNext)
  607. {
  608.   spCurrDestNodeData = (SctpDest_S *) spCurrDestNode->vpData;
  609.   spCurrDestNodeData->spFirstOutstanding = NULL;
  610. }
  611.       for(spCurrNode = sSendBuffer.spHead;
  612.   (spCurrNode != NULL) &&
  613.     (usNumGapAcksProcessed != spSackChunk->usNumGapAckBlocks);
  614.   spCurrNode = spCurrNode->spNext)
  615. {
  616.   spCurrNodeData = (SctpSendBufferNode_S *) spCurrNode->vpData;
  617.   /* is this chunk the first outstanding on its destination?
  618.    */
  619.   if(spCurrNodeData->spDest->spFirstOutstanding == NULL &&
  620.      spCurrNodeData->eGapAcked == FALSE &&
  621.      spCurrNodeData->eAdvancedAcked == FALSE)
  622.     {
  623.       /* yes, it is the first!
  624.        */
  625.       spCurrNodeData->spDest->spFirstOutstanding = spCurrNodeData;
  626.     }
  627.   DBG_PL(ProcessGapAckBlocks, "--> rtx list chunk begin") DBG_PR;
  628.   DBG_PL(ProcessGapAckBlocks, "    TSN=%d"), 
  629.     spCurrNodeData->spChunk->uiTsn 
  630.     DBG_PR;
  631.   DBG_PL(ProcessGapAckBlocks, "    %s=%s %s=%s"),
  632.     "eGapAcked", 
  633.     spCurrNodeData->eGapAcked ? "TRUE" : "FALSE",
  634.     "eAddedToPartialBytesAcked",
  635.     spCurrNodeData->eAddedToPartialBytesAcked ? "TRUE" : "FALSE" 
  636.     DBG_PR;
  637.   DBG_PL(ProcessGapAckBlocks, "    NumMissingReports=%d NumTxs=%d"),
  638.     spCurrNodeData->iNumMissingReports, 
  639.     spCurrNodeData->iNumTxs 
  640.     DBG_PR;
  641.   DBG_PL(ProcessGapAckBlocks, "<-- rtx list chunk end") DBG_PR;
  642.   
  643.   DBG_PL(ProcessGapAckBlocks,"GapAckBlock StartOffset=%d EndOffset=%d"),
  644.     spCurrGapAck->usStartOffset, spCurrGapAck->usEndOffset DBG_PR;
  645.   uiStartTsn = spSackChunk->uiCumAck + spCurrGapAck->usStartOffset;
  646.   uiEndTsn = spSackChunk->uiCumAck + spCurrGapAck->usEndOffset;
  647.   
  648.   DBG_PL(ProcessGapAckBlocks, "GapAckBlock StartTsn=%d EndTsn=%d"),
  649.     uiStartTsn, uiEndTsn DBG_PR;
  650.   if(spCurrNodeData->spChunk->uiTsn < uiStartTsn)
  651.     {
  652.       /* This chunk is NOT being acked and is missing at the receiver
  653.        */
  654.       /* If this chunk was GapAcked before, then either the
  655.        * receiver has renegged the chunk (which our simulation
  656.        * doesn't do) or this SACK is arriving out of order.
  657.        */
  658.       if(spCurrNodeData->eGapAcked == TRUE)
  659. {
  660.   DBG_PL(ProcessGapAckBlocks, 
  661.  "out of order SACK? setting TSN=%d eGapAcked=FALSE"),
  662.     spCurrNodeData->spChunk->uiTsn DBG_PR;
  663.   spCurrNodeData->eGapAcked = FALSE;
  664.   spCurrNodeData->spDest->iOutstandingBytes 
  665.     += spCurrNodeData->spChunk->sHdr.usLength;
  666.   /* section 6.3.2.R4 says that we should restart the
  667.    * T3-rtx timer here if it isn't running already. In our
  668.    * implementation, it isn't necessary since
  669.    * ProcessSackChunk will restart the timer for any
  670.    * destinations which have outstanding data and don't
  671.    * have a timer running.
  672.    */
  673. }
  674.     }
  675.   else if((uiStartTsn <= spCurrNodeData->spChunk->uiTsn) && 
  676.   (spCurrNodeData->spChunk->uiTsn <= uiEndTsn) )
  677.     {
  678.       /* This chunk is being acked via a gap ack block
  679.        */
  680.       DBG_PL(ProcessGapAckBlocks, "gap ack acks this chunk: %s%s"),
  681. "eGapAcked=",
  682. spCurrNodeData->eGapAcked ? "TRUE" : "FALSE" 
  683. DBG_PR;
  684.       /* HTNA algorithm... we need to know the highest TSN
  685.        * sacked (even if it isn't new), so that when the sender
  686.        * is in Fast Recovery, the outstanding tsns beyond the 
  687.        * last sack tsn do not have their missing reports incremented
  688.        */
  689.       if(uiHighestTsnSacked < spCurrNodeData->spChunk->uiTsn)
  690. uiHighestTsnSacked = spCurrNodeData->spChunk->uiTsn;
  691.       if(spCurrNodeData->eGapAcked == FALSE)
  692. {
  693.   DBG_PL(ProcessGapAckBlocks, "setting eGapAcked=TRUE") DBG_PR;
  694.   spCurrNodeData->eGapAcked = TRUE;
  695.   /* HTNA algorithm... we need to know the highest TSN
  696.    * newly acked
  697.    */
  698.   if(uiHighestTsnNewlyAcked < spCurrNodeData->spChunk->uiTsn)
  699.     uiHighestTsnNewlyAcked = spCurrNodeData->spChunk->uiTsn;
  700.   if(spCurrNodeData->eAdvancedAcked == FALSE)
  701.     {
  702.       spCurrNodeData->spDest->iNumNewlyAckedBytes 
  703. += spCurrNodeData->spChunk->sHdr.usLength;
  704.     }
  705.   /* only increment partial bytes acked if we are in
  706.    * congestion avoidance mode, we have a new cum ack, and
  707.    * we haven't already incremented it for this sack
  708.    */
  709.   if(( spCurrNodeData->spDest->iCwnd 
  710.        > spCurrNodeData->spDest->iSsthresh) &&
  711.      eNewCumAck == TRUE &&
  712.      spCurrNodeData->eAddedToPartialBytesAcked == FALSE)
  713.     {
  714.       DBG_PL(ProcessGapAckBlocks, 
  715.      "setting eAddedToPartiallyBytesAcked=TRUE") DBG_PR;
  716.       spCurrNodeData->eAddedToPartialBytesAcked = TRUE; // set
  717.       spCurrNodeData->spDest->iPartialBytesAcked 
  718. += spCurrNodeData->spChunk->sHdr.usLength;
  719.     }
  720.   // BEGIN -- Timestamp changes to this function
  721.   /* We update the RTT estimate if the following hold true:
  722.    *   1. Timestamp set for this chunk matches echoed timestamp
  723.    *   2. This chunk has not been gap acked already 
  724.    *   3. This chunk has not been advanced acked
  725.    */
  726.   if(fInTimestampEcho == (float) spCurrNodeData->dTxTimestamp &&
  727.      spCurrNodeData->eAdvancedAcked == FALSE) 
  728.     {
  729.       RttUpdate(spCurrNodeData->dTxTimestamp, 
  730. spCurrNodeData->spDest);
  731.     }
  732.   // END -- Timestamp changes to this function
  733.   /* section 6.3.2.R3 - Stop the timer if this is the
  734.    * first outstanding for this destination (note: it may
  735.    * have already been stopped if there was a new cum
  736.    * ack). If there are still outstanding bytes on this
  737.    * destination, we'll restart the timer later in
  738.    * ProcessSackChunk() 
  739.    */
  740.   if(spCurrNodeData->spDest->spFirstOutstanding 
  741.      == spCurrNodeData)
  742.     
  743.     {
  744.       if(spCurrNodeData->spDest->eRtxTimerIsRunning == TRUE)
  745. StopT3RtxTimer(spCurrNodeData->spDest);
  746.     }
  747.   
  748.   iAssocErrorCount = 0;
  749.   
  750.   /* We don't want to clear the error counter if it's
  751.    * cleared already; otherwise, we'll unnecessarily
  752.    * trigger a trace event.
  753.    *
  754.    * Also, the error counter is cleared by SACKed data
  755.    * ONLY if the TSNs are not marked for timeout
  756.    * retransmission and has not been gap acked
  757.    * before. Without this condition, we can run into a
  758.    * problem for failure detection. When a failure occurs,
  759.    * some data may have made it through before the
  760.    * failure, but the sacks got lost. When the sender
  761.    * retransmits the first outstanding, the receiver will
  762.    * sack all the data whose sacks got lost. We don't want
  763.    * these sacks * to clear the error counter, or else
  764.    * failover would take longer.
  765.    */
  766.   if(spCurrNodeData->spDest->iErrorCount != 0 &&
  767.      spCurrNodeData->eMarkedForRtx != TIMEOUT_RTX)
  768.     {
  769.       DBG_PL(ProcessGapAckBlocks,
  770.      "clearing error counter for %p with tsn=%lu"), 
  771. spCurrNodeData->spDest, 
  772. spCurrNodeData->spChunk->uiTsn DBG_PR;
  773.       spCurrNodeData->spDest->iErrorCount = 0; // clear errors
  774.       tiErrorCount++;                       // ... and trace it!
  775.       spCurrNodeData->spDest->eStatus = SCTP_DEST_STATUS_ACTIVE;
  776.       if(spCurrNodeData->spDest == spPrimaryDest &&
  777.  spNewTxDest != spPrimaryDest) 
  778. {
  779.   DBG_PL(ProcessGapAckBlocks,
  780.  "primary recovered... "
  781.  "migrating back from %p to %p"),
  782.     spNewTxDest, spPrimaryDest DBG_PR;
  783.   spNewTxDest = spPrimaryDest; // return to primary
  784. }
  785.     }
  786.   spCurrNodeData->eMarkedForRtx = NO_RTX; // unmark
  787. }
  788.     }
  789.   else if(spCurrNodeData->spChunk->uiTsn > uiEndTsn)
  790.     {
  791.       /* This point in the rtx buffer is already past the tsns which are
  792.        * being acked by this gap ack block.  
  793.        */
  794.       usNumGapAcksProcessed++; 
  795.       /* Did we process all the gap ack blocks?
  796.        */
  797.       if(usNumGapAcksProcessed != spSackChunk->usNumGapAckBlocks)
  798. {
  799.   DBG_PL(ProcessGapAckBlocks, "jump to next gap ack block") 
  800.     DBG_PR;
  801.   spCurrGapAck 
  802.     = ((SctpGapAckBlock_S *)
  803.        (ucpSackChunk + sizeof(SctpSackChunk_S)
  804. + (usNumGapAcksProcessed * sizeof(SctpGapAckBlock_S))));
  805. }
  806.       /* If this chunk was GapAcked before, then either the
  807.        * receiver has renegged the chunk (which our simulation
  808.        * doesn't do) or this SACK is arriving out of order.
  809.        */
  810.       if(spCurrNodeData->eGapAcked == TRUE)
  811. {
  812.   DBG_PL(ProcessGapAckBlocks, 
  813.  "out of order SACK? setting TSN=%d eGapAcked=FALSE"),
  814.     spCurrNodeData->spChunk->uiTsn DBG_PR;
  815.   spCurrNodeData->eGapAcked = FALSE;
  816.   spCurrNodeData->spDest->iOutstandingBytes 
  817.     += spCurrNodeData->spChunk->sHdr.usLength;
  818.   
  819.   /* section 6.3.2.R4 says that we should restart the
  820.    * T3-rtx timer here if it isn't running already. In our
  821.    * implementation, it isn't necessary since
  822.    * ProcessSackChunk will restart the timer for any
  823.    * destinations which have outstanding data and don't
  824.    * have a timer running.
  825.    */
  826. }
  827.     }
  828. }
  829.       /* By this time, either we have run through the entire send buffer or we
  830.        * have run out of gap ack blocks. In the case that we have run out of gap
  831.        * ack blocks before we finished running through the send buffer, we need
  832.        * to mark the remaining chunks in the send buffer as eGapAcked=FALSE.
  833.        * This final marking needs to be done, because we only trust gap ack info
  834.        * from the last SACK. Otherwise, renegging (which we don't do) or out of
  835.        * order SACKs would give the sender an incorrect view of the peer's rwnd.
  836.        */
  837.       for(; spCurrNode != NULL; spCurrNode = spCurrNode->spNext)
  838. {
  839.   /* This chunk is NOT being acked and is missing at the receiver
  840.    */
  841.   spCurrNodeData = (SctpSendBufferNode_S *) spCurrNode->vpData;
  842.   /* If this chunk was GapAcked before, then either the
  843.    * receiver has renegged the chunk (which our simulation
  844.    * doesn't do) or this SACK is arriving out of order.
  845.    */
  846.   if(spCurrNodeData->eGapAcked == TRUE)
  847.     {
  848.       DBG_PL(ProcessGapAckBlocks, 
  849.      "out of order SACK? setting TSN=%d eGapAcked=FALSE"),
  850. spCurrNodeData->spChunk->uiTsn DBG_PR;
  851.       spCurrNodeData->eGapAcked = FALSE;
  852.       spCurrNodeData->spDest->iOutstandingBytes 
  853. += spCurrNodeData->spChunk->sHdr.usLength;
  854.       /* section 6.3.2.R4 says that we should restart the T3-rtx
  855.        * timer here if it isn't running already. In our
  856.        * implementation, it isn't necessary since ProcessSackChunk
  857.        * will restart the timer for any destinations which have
  858.        * outstanding data and don't have a timer running.
  859.        */
  860.     }
  861. }
  862.       DBG_PL(ProcessGapAckBlocks, "now incrementing missing reports...") DBG_PR;
  863.       DBG_PL(ProcessGapAckBlocks, "uiHighestTsnNewlyAcked=%d"), 
  864.      uiHighestTsnNewlyAcked DBG_PR;
  865.       for(spCurrNode = sSendBuffer.spHead;
  866.   spCurrNode != NULL; 
  867.   spCurrNode = spCurrNode->spNext)
  868. {
  869.   spCurrNodeData = (SctpSendBufferNode_S *) spCurrNode->vpData;
  870.   DBG_PL(ProcessGapAckBlocks, "TSN=%d eGapAcked=%s"), 
  871.     spCurrNodeData->spChunk->uiTsn,
  872.     spCurrNodeData->eGapAcked ? "TRUE" : "FALSE"
  873.     DBG_PR;
  874.   if(spCurrNodeData->eGapAcked == FALSE)
  875.     {
  876.       /* HTNA (Highest TSN Newly Acked) algorithm from
  877.        * implementer's guide. The HTNA increments missing reports
  878.        * for TSNs not GapAcked when one of the following
  879.        * conditions hold true:
  880.        *
  881.        *    1. The TSN is less than the highest TSN newly acked.
  882.        *
  883.        *    2. The TSN is less than the highest TSN sacked so far
  884.        *    (not necessarily newly acked), the sender is in Fast
  885.        *    Recovery, the cum ack changes, and the new cum ack is less
  886.        *    than recover.
  887.        */
  888.       if( (spCurrNodeData->spChunk->uiTsn < uiHighestTsnNewlyAcked) ||
  889.   (eNewCumAck == TRUE && 
  890.    uiHighestTsnNewlyAcked <= uiRecover &&
  891.    spCurrNodeData->spChunk->uiTsn < uiHighestTsnSacked))
  892. {
  893.   spCurrNodeData->iNumMissingReports++;
  894.   DBG_PL(ProcessGapAckBlocks, 
  895.  "incrementing missing report for TSN=%d to %d"), 
  896.     spCurrNodeData->spChunk->uiTsn,
  897.     spCurrNodeData->iNumMissingReports
  898.     DBG_PR;
  899.   if(spCurrNodeData->iNumMissingReports >= iFastRtxTrigger &&
  900.      spCurrNodeData->eIneligibleForFastRtx == FALSE &&
  901.      spCurrNodeData->eAdvancedAcked == FALSE)
  902.     {
  903.       MarkChunkForRtx(spCurrNodeData, FAST_RTX);
  904.       eFastRtxNeeded = TRUE;
  905.       spCurrNodeData->eIneligibleForFastRtx = TRUE;
  906.       DBG_PL(ProcessGapAckBlocks, 
  907.      "setting eFastRtxNeeded = TRUE") DBG_PR;
  908.     }
  909. }
  910.     }
  911. }
  912.     }
  913.   if(eFastRtxNeeded == TRUE)
  914.     tiFrCount++;
  915.   DBG_PL(ProcessGapAckBlocks, "eFastRtxNeeded=%s"), 
  916.     eFastRtxNeeded ? "TRUE" : "FALSE" DBG_PR;
  917.   DBG_X(ProcessGapAckBlocks);
  918.   return eFastRtxNeeded;
  919. }
  920. /* This function is left as a hook for extensions to process chunk types not
  921.  * supported in the base sctp. Here, we can only expect the timestamp chunk.
  922.  */
  923. void TimestampSctpAgent::ProcessOptionChunk(u_char *ucpInChunk)
  924. {
  925.   DBG_I(ProcessOptionChunk);
  926.   double dCurrTime = Scheduler::instance().clock();
  927.   switch( ((SctpChunkHdr_S *)ucpInChunk)->ucType)
  928.     {
  929.     case SCTP_CHUNK_TIMESTAMP:
  930.       ProcessTimestampChunk((SctpTimestampChunk_S *) ucpInChunk);
  931.       break;
  932.       
  933.     default:
  934.       DBG_PL(ProcessOptionChunk, "unexpected chunk type (unknown: %d) at %f"),
  935. ((SctpChunkHdr_S *)ucpInChunk)->ucType, dCurrTime DBG_PR;
  936.       printf("[ProcessOptionChunk] unexpected chunk type (unknown: %d) at %fn",
  937.      ((SctpChunkHdr_S *)ucpInChunk)->ucType, dCurrTime);
  938.       break;
  939.     }
  940.   DBG_X(ProcessOptionChunk);
  941. }
  942. void TimestampSctpAgent::ProcessTimestampChunk(SctpTimestampChunk_S 
  943.        *spTimestampChunk)
  944. {
  945.   DBG_I(ProcessTimestampChunk);
  946.   /* Only echo the timestamp if there isn't already an echo ready to go. Since
  947.    * the receiver may delay sacks and ack 2 data packets with one sack, we want
  948.    * to echo the timestamp of the first data chunk that triggers the sack.
  949.    */
  950.   if(spTimestampChunk->sHdr.ucFlags & SCTP_TIMESTAMP_FLAG_TS)
  951.     {
  952.       if(eNeedTimestampEcho == FALSE)
  953. {
  954.   eNeedTimestampEcho = TRUE;
  955.   fOutTimestampEcho = spTimestampChunk->fTimestamp;
  956.   DBG_PL(ProcessTimestampChunk, "timestamp=%f ...echoing!"), 
  957.     spTimestampChunk->fTimestamp DBG_PR;
  958. }
  959.       else
  960. {
  961.   DBG_PL(ProcessTimestampChunk, 
  962.  "timestamp=%f ...ignoring (already echoing one)"), 
  963.     spTimestampChunk->fTimestamp DBG_PR;
  964. }
  965.     }
  966.   if(spTimestampChunk->sHdr.ucFlags & SCTP_TIMESTAMP_FLAG_ECHO)
  967.     {
  968.       fInTimestampEcho = spTimestampChunk->fEcho;
  969.       DBG_PL(ProcessTimestampChunk, "echo=%f"), 
  970. spTimestampChunk->fEcho DBG_PR;
  971.     }
  972.   DBG_X(ProcessTimestampChunk);
  973. }