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

通讯编程

开发平台:

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.  * Janardhan Iyengar   <iyengar@@cis,udel,edu>
  10.  * Preethi Natarajan   <nataraja@@cis,udel,edu>
  11.  * Nasif Ekiz          <nekiz@@cis,udel,edu>
  12.  *
  13.  * Redistribution and use in source and binary forms, with or without
  14.  * modification, are permitted provided that the following conditions
  15.  * are met:
  16.  *
  17.  * 1. Redistributions of source code must retain the above copyright
  18.  *    notice, this list of conditions and the following disclaimer.
  19.  *
  20.  * 2. Redistributions in binary form must reproduce the above copyright
  21.  *    notice, this list of conditions and the following disclaimer in the
  22.  *    documentation and/or other materials provided with the distribution.
  23.  *
  24.  * 3. Neither the name of the University nor of the Laboratory may be used
  25.  *    to endorse or promote products derived from this software without
  26.  *    specific prior written permission.
  27.  *
  28.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  29.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  30.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  31.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  32.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  33.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  34.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  35.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  36.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  37.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  38.  * SUCH DAMAGE.
  39.  */
  40. /* Concurrent Multipath Transfer extension: To allow SCTP to correctly use
  41.  * the multiple paths available at the transport.
  42.  */
  43. #include "ip.h"
  44. #include "sctp-cmt.h"
  45. #include "sctp.h"
  46. #include "flags.h"
  47. #include "random.h"
  48. #include "template.h"
  49. #include "sctpDebug.h"
  50. #ifdef DMALLOC
  51. #include "dmalloc.h"
  52. #endif
  53. #define MIN(x,y) (((x)<(y))?(x):(y))
  54. #define MAX(x,y) (((x)>(y))?(x):(y))
  55. static class SctpCMTClass : public TclClass 
  56. public:
  57.   SctpCMTClass() : TclClass("Agent/SCTP/CMT") {}
  58.   TclObject* create(int, const char*const*) 
  59.   {
  60.     return (new SctpCMTAgent());
  61.   }
  62. } classSctpCMT;
  63. SctpCMTAgent::SctpCMTAgent() : SctpAgent() {}
  64. void SctpCMTAgent::delay_bind_init_all()
  65. {
  66.   delay_bind_init_one("useCmtReordering_");
  67.   delay_bind_init_one("useCmtCwnd_");
  68.   delay_bind_init_one("useCmtDelAck_");
  69.   delay_bind_init_one("eCmtRtxPolicy_");
  70.   delay_bind_init_one("useCmtPF_");
  71.   delay_bind_init_one("cmtPFCwnd_");
  72.   delay_bind_init_one("countPFToActiveNewData_");
  73.   delay_bind_init_one("countPFToActiveRtxms_");
  74.   //  delay_bind_init_one("useSharedCC_");
  75.   SctpAgent::delay_bind_init_all();
  76. }
  77. int SctpCMTAgent::delay_bind_dispatch(const char *cpVarName, 
  78.   const char *cpLocalName, 
  79.   TclObject *opTracer)
  80. {
  81.   if(delay_bind(cpVarName, cpLocalName, "useCmtReordering_", 
  82. (int*)&eUseCmtReordering, opTracer))
  83.     return TCL_OK;
  84.   if(delay_bind(cpVarName, cpLocalName, "useCmtCwnd_", 
  85. (int*)&eUseCmtCwnd, opTracer))
  86.     return TCL_OK;
  87.   if(delay_bind(cpVarName, cpLocalName, "useCmtDelAck_", 
  88. (int*)&eUseCmtDelAck, opTracer))
  89.     return TCL_OK;
  90.   if(delay_bind(cpVarName, cpLocalName, "eCmtRtxPolicy_", 
  91. (int*)&eCmtRtxPolicy, opTracer))
  92.     return TCL_OK;
  93.   if(delay_bind(cpVarName, cpLocalName, "useCmtPF_", 
  94. (int*)&eUseCmtPF, opTracer))
  95.     return TCL_OK;
  96.   if(delay_bind(cpVarName, cpLocalName, "cmtPFCwnd_", 
  97. (u_int*)&uiCmtPFCwnd, opTracer))
  98.     return TCL_OK;
  99.   if(delay_bind(cpVarName, cpLocalName, "countPFToActiveNewData_", 
  100. &tiCountPFToActiveNewData, opTracer))
  101.     return TCL_OK;
  102.   if(delay_bind(cpVarName, cpLocalName, "countPFToActiveRtxms_", 
  103. &tiCountPFToActiveRtxms, opTracer))
  104.     return TCL_OK;
  105.   //   if(delay_bind(cpVarName, cpLocalName, "useSharedCC_", 
  106.   //  (int*)&eUseSharedCC, opTracer))
  107.   //     return TCL_OK;
  108.   else
  109.     return SctpAgent::delay_bind_dispatch(cpVarName, 
  110.   cpLocalName, opTracer);
  111. }
  112. void SctpCMTAgent::TraceAll()
  113. {
  114.   char cpOutString[500];
  115.   Node_S *spCurrNode = NULL;
  116.   SctpDest_S *spCurrDest = NULL;
  117.   double dCurrTime = Scheduler::instance().clock();
  118.   for(spCurrNode = sDestList.spHead;
  119.       spCurrNode != NULL;
  120.       spCurrNode = spCurrNode->spNext)
  121.     {
  122.       spCurrDest = (SctpDest_S *) spCurrNode->vpData;
  123.       SetSource(spCurrDest); // gives us the correct source addr & port
  124.       sprintf(cpOutString,
  125.       "time: %-8.5f  "
  126.       "saddr: %-2d sport: %-2d daddr: %-2d dport: %-2d "
  127.       "cwnd: %d pba: %d out: %d ssthresh: %d peerRwnd: %d "
  128.       "rto: %-6.3f srtt: %-6.3f rttvar: %-6.3f "
  129.       "assocErrors: %d pathErrors: %d dstatus: %s "
  130.       "frCount: %d timeoutCount: %d rcdCount: %d "
  131.       "countPFToActiveNewData: %d countPFToActiveRtxms: %dn",
  132.       dCurrTime,
  133.       addr(), port(), spCurrDest->iNsAddr, spCurrDest->iNsPort,
  134.       spCurrDest->iCwnd, spCurrDest->iPartialBytesAcked, 
  135.       spCurrDest->iOutstandingBytes, spCurrDest->iSsthresh, 
  136.       uiPeerRwnd,
  137.       spCurrDest->dRto, spCurrDest->dSrtt, 
  138.       spCurrDest->dRttVar,
  139.       iAssocErrorCount,
  140.       spCurrDest->iErrorCount,
  141.       PrintDestStatus(spCurrDest),
  142.       int(tiFrCount),
  143.       spCurrDest->iTimeoutCount,
  144.       spCurrDest->iRcdCount,
  145.       int(tiCountPFToActiveNewData),
  146.               int(tiCountPFToActiveRtxms));
  147.       if(channel_)
  148. (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
  149.     }
  150.   sprintf(cpOutString, "n");
  151.   if(channel_)
  152.     (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
  153. }
  154. void SctpCMTAgent::TraceVar(const char* cpVar)
  155. {
  156.   char cpOutString[500];
  157.   Node_S *spCurrNode = NULL;
  158.   SctpDest_S *spCurrDest = NULL;
  159.   double dCurrTime = Scheduler::instance().clock();
  160.   if(!strcmp(cpVar, "cwnd_"))
  161.     for(spCurrNode = sDestList.spHead;
  162. spCurrNode != NULL;
  163. spCurrNode = spCurrNode->spNext)
  164.       {
  165. spCurrDest = (SctpDest_S *) spCurrNode->vpData;
  166. SetSource(spCurrDest); // gives us the correct source addr & port
  167. sprintf(cpOutString,
  168. "time: %-8.5f  "
  169. "saddr: %-2d sport: %-2d daddr: %-2d dport: %-2d "
  170. "cwnd: %d pba: %d out: %d ssthresh: %d peerRwnd: %dn",
  171. dCurrTime, 
  172. addr(), port(), 
  173. spCurrDest->iNsAddr, spCurrDest->iNsPort,
  174. spCurrDest->iCwnd, spCurrDest->iPartialBytesAcked, 
  175. spCurrDest->iOutstandingBytes, spCurrDest->iSsthresh,
  176. uiPeerRwnd);
  177. if(channel_)
  178.   (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
  179.       }
  180.   //   else if(!strcmp(cpVar, "rwnd_"))
  181.   //     {
  182.   //       sprintf(cpOutString, "time: %-8.5f rwnd: %d peerRwnd: %dn", 
  183.   //        dCurrTime, uiMyRwnd, uiPeerRwnd);
  184.   //       if(channel_)
  185.   //        (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
  186.   //     }
  187.   
  188.   else if(!strcmp(cpVar, "rto_"))
  189.     for(spCurrNode = sDestList.spHead;
  190. spCurrNode != NULL;
  191. spCurrNode = spCurrNode->spNext)
  192.       {
  193. spCurrDest = (SctpDest_S *) spCurrNode->vpData;
  194. SetSource(spCurrDest); // gives us the correct source addr & port
  195. sprintf(cpOutString,
  196. "time: %-8.5f  "
  197. "saddr: %-2d sport: %-2d daddr: %-2d dport: %-2d "
  198. "rto: %-6.3f srtt: %-6.3f rttvar: %-6.3f timeoutCount: %dn",
  199. dCurrTime, 
  200. addr(), port(), 
  201. spCurrDest->iNsAddr, spCurrDest->iNsPort,
  202. spCurrDest->dRto, spCurrDest->dSrtt, 
  203. spCurrDest->dRttVar, spCurrDest->iTimeoutCount);
  204. if(channel_)
  205.   (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
  206.       }
  207.   else if(!strcmp(cpVar, "errorCount_"))
  208.     for(spCurrNode = sDestList.spHead;
  209. spCurrNode != NULL;
  210. spCurrNode = spCurrNode->spNext)
  211.       {
  212. spCurrDest = (SctpDest_S *) spCurrNode->vpData;
  213. SetSource(spCurrDest); // gives us the correct source addr & port
  214. sprintf(cpOutString, 
  215. "time: %-8.5f  "
  216. "saddr: %-2d sport: %-2d daddr: %-2d dport: %-2d "
  217. "assocErrors: %d pathErrors: %d dstatus: %dn",
  218. dCurrTime, 
  219. addr(), port(), 
  220. spCurrDest->iNsAddr, spCurrDest->iNsPort,
  221. iAssocErrorCount,
  222. spCurrDest->iErrorCount,
  223. spCurrDest->eStatus);
  224. if(channel_)
  225.   (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
  226.       }
  227.   else if(!strcmp(cpVar, "frCount_"))
  228.     {
  229.       /* JRI-TODO: Check code in SCTP for frCount
  230.        */
  231.       sprintf(cpOutString, 
  232.       "time: %-8.5f  "
  233.       "frCount: %dn",
  234.       dCurrTime, 
  235.       int(tiFrCount));
  236.       if(channel_)
  237. (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
  238.     }
  239.   else if(!strcmp(cpVar, "timeoutCount_"))
  240.     {
  241.     for(spCurrNode = sDestList.spHead;
  242. spCurrNode != NULL;
  243. spCurrNode = spCurrNode->spNext)
  244.       {
  245. spCurrDest = (SctpDest_S *) spCurrNode->vpData;
  246. SetSource(spCurrDest); // gives us the correct source addr & port
  247. sprintf(cpOutString, 
  248. "time: %-8.5f  "
  249. "saddr: %-2d sport: %-2d daddr: %-2d dport: %-2d "
  250. "timeoutCount: %dn",
  251. dCurrTime, 
  252. addr(), port(), 
  253. spCurrDest->iNsAddr, spCurrDest->iNsPort,
  254. spCurrDest->iTimeoutCount);
  255. if(channel_)
  256.   (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
  257.       }
  258.     }
  259.   else if(!strcmp(cpVar, "countPFToActiveNewData_"))
  260.     {
  261.       sprintf(cpOutString,
  262.       "time: %-8.5f   "
  263.       "countPFToActiveNewData: %dn",
  264.       dCurrTime, 
  265.       int(tiCountPFToActiveNewData));
  266.       if(channel_)
  267. (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
  268.     }
  269.   else if(!strcmp(cpVar, "countPFToActiveRtxms_"))
  270.     {
  271.       sprintf(cpOutString,
  272.       "time: %-8.5f   "
  273.       "countPFToActiveRtxms: %dn",
  274.       dCurrTime, 
  275.       int(tiCountPFToActiveRtxms));
  276. if(channel_)
  277.   (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
  278.     }
  279.   else
  280.     {
  281.       sprintf(cpOutString,
  282.       "time: %-8.5f  "
  283.       "saddr: %-2d sport: %-2d daddr: %-2d dport: %-2d %s: %sn",
  284.       dCurrTime, addr(), port(), daddr(), dport(),
  285.       cpVar, "ERROR (unepected trace variable)"); 
  286.       if(channel_)
  287. (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
  288.     }
  289.   sprintf(cpOutString, "n");
  290.   if(channel_)
  291.     (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
  292. }
  293. void SctpCMTAgent::trace(TracedVar *v)
  294. {
  295.   if(eTraceAll == TRUE)
  296. TraceAll();
  297.   else
  298. TraceVar(v->name());
  299. }
  300. void SctpCMTAgent::OptionReset()
  301. {
  302.   DBG_I(OptionReset);
  303.   SctpAgent::OptionReset();
  304.   DBG_X(OptionReset);
  305. }
  306. void SctpCMTAgent::Reset() 
  307. {
  308.   Node_S *spCurrNode = NULL;
  309.   SctpDest_S *spCurrDest = NULL;
  310.   /* Call parent's reset() first
  311.    */
  312.   SctpAgent::Reset();
  313.   for(spCurrNode = sDestList.spHead;
  314.       spCurrNode != NULL;
  315.       spCurrNode = spCurrNode->spNext)
  316.     {
  317.       spCurrDest = (SctpDest_S *) spCurrNode->vpData;
  318.       spCurrDest->uiHighestTsnInSackForDest = 0;
  319.       spCurrDest->uiLowestTsnInSackForDest = 0;
  320.       spCurrDest->eSawNewAck = FALSE;
  321.       spCurrDest->uiRecover = 0;
  322.       spCurrDest->eFindExpectedPseudoCum = TRUE;
  323.       spCurrDest->eFindExpectedRtxPseudoCum = TRUE;
  324.       spCurrDest->eRtxLimit = RTX_LIMIT_ZERO;
  325.     }
  326.   tiCountPFToActiveNewData = 0;
  327.   tiCountPFToActiveRtxms = 0;
  328. }
  329. /* returns the size of the chunk
  330.  */
  331. int SctpCMTAgent::GenChunk(SctpChunkType_E eType, u_char *ucpChunk)
  332. {
  333.   DBG_I(GenChunk);
  334.   double dCurrTime = Scheduler::instance().clock();
  335.   AppData_S *spAppMessage = NULL;
  336.   int iSize = 0;
  337.   DBG_PL(GenChunk, "spSctpTrace=%p"), spSctpTrace DBG_PR;
  338.   switch(eType)
  339.     {
  340.     case SCTP_CHUNK_INIT:
  341.       iSize = sizeof(SctpInitChunk_S);
  342.       ((SctpInitChunk_S *) ucpChunk)->sHdr.ucType = eType;
  343.       ((SctpInitChunk_S *) ucpChunk)->uiArwnd = uiInitialRwnd; 
  344.       ((SctpInitChunk_S *) ucpChunk)->usNumOutboundStreams = uiNumOutStreams;
  345.       ((SctpInitChunk_S *) ucpChunk)->uiInitialTsn = 0; 
  346.       ((SctpInitChunk_S *) ucpChunk)->sUnrelStream.usType 
  347. = SCTP_INIT_PARAM_UNREL;
  348.       ((SctpInitChunk_S *) ucpChunk)->sUnrelStream.usLength
  349. = sizeof(SctpUnrelStreamsParam_S);
  350.       
  351.       if(uiNumUnrelStreams > 0)
  352. {
  353.   ((SctpInitChunk_S *) ucpChunk)->sUnrelStream.usLength 
  354.     += sizeof(SctpUnrelStreamPair_S);
  355.   SctpUnrelStreamPair_S *spUnrelStreamPair 
  356.             = (SctpUnrelStreamPair_S *) (ucpChunk+iSize);
  357.           spUnrelStreamPair->usStart = 0;
  358.           spUnrelStreamPair->usEnd = uiNumUnrelStreams - 1;
  359.   iSize += sizeof(SctpUnrelStreamPair_S);
  360.   }
  361.       ((SctpInitChunk_S *) ucpChunk)->sHdr.usLength = iSize;
  362.       /* fill in tracing fields too
  363.        */
  364.       spSctpTrace[uiNumChunks].eType = eType;
  365.       spSctpTrace[uiNumChunks].uiTsn = (u_int) -1;
  366.       spSctpTrace[uiNumChunks].usStreamId = (u_short) -1;
  367.       spSctpTrace[uiNumChunks].usStreamSeqNum = (u_short) -1;
  368.       break;
  369.     case SCTP_CHUNK_INIT_ACK:
  370.       iSize = sizeof(SctpInitAckChunk_S);
  371.       ((SctpInitAckChunk_S *) ucpChunk)->sHdr.ucType = eType;
  372.       ((SctpInitAckChunk_S *) ucpChunk)->uiArwnd = uiInitialRwnd; 
  373.       ((SctpInitChunk_S *) ucpChunk)->usNumOutboundStreams = uiNumOutStreams;
  374.       ((SctpInitAckChunk_S *) ucpChunk)->uiInitialTsn = 0; 
  375.       ((SctpInitAckChunk_S *) ucpChunk)->sUnrelStream.usType 
  376. = SCTP_INIT_PARAM_UNREL;
  377.       ((SctpInitAckChunk_S *) ucpChunk)->sUnrelStream.usLength
  378. = sizeof(SctpUnrelStreamsParam_S);
  379.       if(uiNumUnrelStreams > 0)
  380. {
  381.   ((SctpInitAckChunk_S *) ucpChunk)->sUnrelStream.usLength 
  382.     += sizeof(SctpUnrelStreamPair_S);
  383.   SctpUnrelStreamPair_S *spUnrelStreamPair 
  384.             = (SctpUnrelStreamPair_S *) (ucpChunk+iSize);
  385.           spUnrelStreamPair->usStart = 0;
  386.           spUnrelStreamPair->usEnd = uiNumUnrelStreams - 1;
  387.   iSize += sizeof(SctpUnrelStreamPair_S);
  388. }
  389.       ((SctpInitAckChunk_S *) ucpChunk)->sHdr.usLength = iSize;
  390.       /* fill in tracing fields too
  391.        */
  392.       spSctpTrace[uiNumChunks].eType = eType;
  393.       spSctpTrace[uiNumChunks].uiTsn = (u_int) -1;
  394.       spSctpTrace[uiNumChunks].usStreamId = (u_short) -1;
  395.       spSctpTrace[uiNumChunks].usStreamSeqNum = (u_short) -1;
  396.       break;
  397.     case SCTP_CHUNK_COOKIE_ECHO:
  398.       iSize = sizeof(SctpCookieEchoChunk_S);
  399.       ((SctpCookieEchoChunk_S *) ucpChunk)->sHdr.ucType = eType;
  400.       ((SctpCookieEchoChunk_S *) ucpChunk)->sHdr.usLength = iSize;
  401.       /* fill in tracing fields too
  402.        */
  403.       spSctpTrace[uiNumChunks].eType = eType;
  404.       spSctpTrace[uiNumChunks].uiTsn = (u_int) -1;
  405.       spSctpTrace[uiNumChunks].usStreamId = (u_short) -1;
  406.       spSctpTrace[uiNumChunks].usStreamSeqNum = (u_short) -1;
  407.       break;
  408.       
  409.     case SCTP_CHUNK_COOKIE_ACK:
  410.       iSize = sizeof(SctpCookieAckChunk_S);
  411.       ((SctpCookieAckChunk_S *) ucpChunk)->sHdr.ucType = eType;
  412.       ((SctpCookieAckChunk_S *) ucpChunk)->sHdr.usLength = iSize;
  413.       /* fill in tracing fields too
  414.        */
  415.       spSctpTrace[uiNumChunks].eType = eType;
  416.       spSctpTrace[uiNumChunks].uiTsn = (u_int) -1;
  417.       spSctpTrace[uiNumChunks].usStreamId = (u_short) -1;
  418.       spSctpTrace[uiNumChunks].usStreamSeqNum = (u_short) -1;
  419.       break;
  420.     case SCTP_CHUNK_DATA:
  421.       /* Depending on eDataSource, we either use chunk parameters 
  422.        * from the app layer buffer, or use the defaults for infinite 
  423.        * data generation.
  424.        */
  425.       if(eDataSource == DATA_SOURCE_APPLICATION)
  426. {
  427.   /* If DATA_SOURCE_APPLICATION, we have to get chunk parameters
  428.    * from the app layer buffer 
  429.    */
  430.   DBG_PL (GenChunk, "eDataSource=DATA_SOURCE_APPLICATION") DBG_PR; 
  431.   spAppMessage = (AppData_S *) (sAppLayerBuffer.spHead->vpData);
  432.   DBG_PL (GenChunk, "App Message ---------------->") DBG_PR;
  433.   DBG_PL (GenChunk, "usNumStreams: %d"), 
  434.     spAppMessage->usNumStreams DBG_PR;
  435.   DBG_PL (GenChunk, "usNumUnreliable: %d"), 
  436.     spAppMessage->usNumUnreliable DBG_PR;
  437.   DBG_PL (GenChunk, "uiNumBytes: %d"),
  438.     spAppMessage->uiNumBytes DBG_PR;
  439.   DBG_PL (GenChunk, "usStreamId: %d"), 
  440.     spAppMessage->usStreamId DBG_PR;
  441.   DBG_PL (GenChunk, "eUnordered: %s"), 
  442.     spAppMessage->eUnordered ? "TRUE" : "FALSE" DBG_PR;
  443.   DBG_PL (GenChunk, "usReliability: %d"), 
  444.     spAppMessage->usReliability DBG_PR;
  445.   DBG_PL (GenChunk, "App Message <----------------") DBG_PR;
  446.   iSize = spAppMessage->uiNumBytes + sizeof(SctpDataChunkHdr_S);
  447.   ((SctpDataChunkHdr_S *) ucpChunk)->sHdr.ucType = SCTP_CHUNK_DATA;
  448.   ((SctpDataChunkHdr_S *) ucpChunk)->sHdr.usLength = iSize;
  449.   /* DATA chunks must be padded to a 4 byte boundary (section 3.3.1)
  450.    * ...but the adjustment should happen after usLength field is set,
  451.    * because the field should represent the size before padding.
  452.    */
  453.   if( (iSize % 4) != 0)
  454.     iSize += 4 - (iSize % 4);
  455.   ((SctpDataChunkHdr_S *) ucpChunk)->uiTsn = ++uiNextTsn;
  456.   
  457.   ((SctpDataChunkHdr_S *) ucpChunk)->usStreamId 
  458.     = spAppMessage->usStreamId; 
  459.   if(spAppMessage->eUnordered == TRUE)
  460.     {
  461.       ((SctpDataChunkHdr_S *) ucpChunk)->sHdr.ucFlags 
  462. |= SCTP_DATA_FLAG_UNORDERED;
  463.       /* no stream seq num on unordered chunks!
  464.        */
  465.     }
  466.   else
  467.     {
  468.       ((SctpDataChunkHdr_S *) ucpChunk)->sHdr.ucFlags = 0;
  469.       ((SctpDataChunkHdr_S *) ucpChunk)->usStreamSeqNum 
  470. = spOutStreams[spAppMessage->usStreamId].usNextStreamSeqNum++;
  471.     }
  472. }
  473.       else
  474. {
  475.   DBG_PL (GenChunk, "eDataSource=DATA_SOURCE_INFINITE") DBG_PR; 
  476.   iSize = uiDataChunkSize;
  477.   ((SctpDataChunkHdr_S *) ucpChunk)->sHdr.ucType = SCTP_CHUNK_DATA;
  478.   if(eUnordered == TRUE)
  479.     {
  480.       ((SctpDataChunkHdr_S *) ucpChunk)->sHdr.ucFlags 
  481. |= SCTP_DATA_FLAG_UNORDERED;
  482.     }
  483.   else
  484.     {
  485.       ((SctpDataChunkHdr_S *) ucpChunk)->sHdr.ucFlags = 0;
  486.     }
  487.   ((SctpDataChunkHdr_S *) ucpChunk)->sHdr.usLength = iSize;
  488.   /* DATA chunks must be padded to a 4 byte boundary (section 3.3.1)
  489.    * ...but the adjustment should happen after usLength field is set,
  490.    * because the field should represent the size before padding.
  491.    */
  492.   if( (uiDataChunkSize % 4) != 0)
  493.     iSize += 4 - (uiDataChunkSize % 4);
  494.   ((SctpDataChunkHdr_S *) ucpChunk)->uiTsn = ++uiNextTsn;
  495.   
  496.   ((SctpDataChunkHdr_S *) ucpChunk)->usStreamId 
  497.     = (usNextStreamId % uiNumOutStreams); 
  498.   if(eUnordered == TRUE)
  499.     {
  500.       ((SctpDataChunkHdr_S *) ucpChunk)->sHdr.ucFlags 
  501. |= SCTP_DATA_FLAG_UNORDERED;
  502.       /* no stream seq num on unordered chunks!
  503.        */
  504.     }
  505.   else
  506.     {
  507.       ((SctpDataChunkHdr_S *) ucpChunk)->sHdr.ucFlags = 0;
  508.       ((SctpDataChunkHdr_S *) ucpChunk)->usStreamSeqNum 
  509. = spOutStreams[usNextStreamId%uiNumOutStreams].usNextStreamSeqNum++;
  510.     }
  511.   
  512.   usNextStreamId++; // round-robin stream feeding
  513. }
  514.       /* fill in tracing fields too
  515.        */
  516.       spSctpTrace[uiNumChunks].eType = eType;
  517.       spSctpTrace[uiNumChunks].uiTsn 
  518. = ((SctpDataChunkHdr_S *) ucpChunk)->uiTsn;
  519.       spSctpTrace[uiNumChunks].usStreamId 
  520. = ((SctpDataChunkHdr_S *) ucpChunk)->usStreamId;
  521.       spSctpTrace[uiNumChunks].usStreamSeqNum 
  522. = ((SctpDataChunkHdr_S *) ucpChunk)->usStreamSeqNum;
  523.       DBG_PL(GenChunk, "DataTsn=%d at dCurrTime=%f"), 
  524. uiNextTsn, dCurrTime DBG_PR;
  525.       break;
  526.     case SCTP_CHUNK_SACK:
  527.       iSize = sizeof(SctpSackChunk_S);
  528.       ((SctpSackChunk_S *) ucpChunk)->sHdr.ucType = eType;
  529.       ((SctpSackChunk_S *) ucpChunk)->uiCumAck = uiCumAck;
  530.       ((SctpSackChunk_S *) ucpChunk)->uiArwnd = uiMyRwnd;
  531.       ((SctpSackChunk_S *) ucpChunk)->usNumGapAckBlocks 
  532. = sRecvTsnBlockList.uiLength;
  533.       ((SctpSackChunk_S *) ucpChunk)->usNumDupTsns = sDupTsnList.uiLength;
  534.       /****** Begin CMT change ******/
  535.       /* The SACK chunk flags are unused in RFC2960. We propose to use 1
  536.        * of the 8 bits in the SACK flags for the CMT delayed ack algo.
  537.        * These bits will be used to indicate the number of DATA packets
  538.        * were received between the previous and the current SACK. This 
  539.        * information will be used by the DATA sender to increment missing
  540.        * reports better in CMT.
  541.        */
  542.       ((SctpSackChunk_S*)ucpChunk)->sHdr.ucFlags = iDataPktCountSinceLastSack;
  543.       DBG_PL(GenChunk, "SACK CumAck=%d arwnd=%d flags=%d"), 
  544. uiCumAck, uiMyRwnd, iDataPktCountSinceLastSack DBG_PR;
  545.       
  546.       /* Pkt count is put into SACK flags for CMT delayed ack algo. 
  547.        * Reset only after putting into SACK flags.
  548.        */
  549.       iDataPktCountSinceLastSack = 0; // reset
  550.       /****** End CMT change ******/
  551.       /* Append all the Gap Ack Blocks
  552.        */
  553.       for(Node_S *spCurrFrag = sRecvTsnBlockList.spHead;
  554.   (spCurrFrag != NULL) &&
  555.     (iSize + sizeof(SctpGapAckBlock_S) < uiMaxDataSize); 
  556.   spCurrFrag = spCurrFrag->spNext, iSize += sizeof(SctpGapAckBlock_S) )
  557. {
  558.   SctpGapAckBlock_S *spGapAckBlock 
  559.     = (SctpGapAckBlock_S *) (ucpChunk+iSize);
  560.   spGapAckBlock->usStartOffset 
  561.     = ((SctpRecvTsnBlock_S *)spCurrFrag->vpData)->uiStartTsn-uiCumAck;
  562.   spGapAckBlock->usEndOffset
  563.     = ((SctpRecvTsnBlock_S *)spCurrFrag->vpData)->uiEndTsn - uiCumAck;
  564.   DBG_PL(GenChunk, "GapAckBlock StartOffset=%d EndOffset=%d"), 
  565.     spGapAckBlock->usStartOffset, spGapAckBlock->usEndOffset DBG_PR;
  566. }
  567.       /* Append all the Duplicate TSNs
  568.        */
  569.       for(Node_S *spPrevDup = NULL, *spCurrDup = sDupTsnList.spHead;
  570.   (spCurrDup != NULL) &&
  571.     (iSize + sizeof(SctpDupTsn_S) < uiMaxDataSize); 
  572.   spPrevDup = spCurrDup, spCurrDup = spCurrDup->spNext, 
  573.     DeleteNode(&sDupTsnList, spPrevDup), iSize += sizeof(SctpDupTsn_S))
  574. {
  575.   SctpDupTsn_S *spDupTsn = (SctpDupTsn_S *) (ucpChunk+iSize);
  576.   
  577.   spDupTsn->uiTsn = ((SctpDupTsn_S *) spCurrDup->vpData)->uiTsn;
  578.   DBG_PL(GenChunk, "DupTsn=%d"), spDupTsn->uiTsn DBG_PR;
  579. }
  580.       /* After all the dynamic appending, we can NOW fill in the chunk size!
  581.        */
  582.       ((SctpSackChunk_S *) ucpChunk)->sHdr.usLength = iSize;
  583.       /* fill in tracing fields too
  584.        */
  585.       spSctpTrace[uiNumChunks].eType = eType;
  586.       spSctpTrace[uiNumChunks].uiTsn = ((SctpSackChunk_S *)ucpChunk)->uiCumAck;
  587.       spSctpTrace[uiNumChunks].usStreamId = (u_short) -1;
  588.       spSctpTrace[uiNumChunks].usStreamSeqNum = (u_short) -1;
  589.       break;
  590.     case SCTP_CHUNK_FORWARD_TSN:
  591.       iSize = SCTP_CHUNK_FORWARD_TSN_LENGTH;
  592.       ((SctpForwardTsnChunk_S *) ucpChunk)->sHdr.ucType = eType;
  593.       ((SctpForwardTsnChunk_S *) ucpChunk)->sHdr.ucFlags = 0; //flags not used?
  594.       ((SctpForwardTsnChunk_S *) ucpChunk)->sHdr.usLength = iSize;
  595.       ((SctpForwardTsnChunk_S *) ucpChunk)->uiNewCum = uiAdvancedPeerAckPoint;
  596.       /* fill in tracing fields too
  597.        */
  598.       spSctpTrace[uiNumChunks].eType = eType;
  599.       spSctpTrace[uiNumChunks].uiTsn = uiAdvancedPeerAckPoint;
  600.       spSctpTrace[uiNumChunks].usStreamId = (u_short) -1;
  601.       spSctpTrace[uiNumChunks].usStreamSeqNum = (u_short) -1;
  602.       break;
  603.     case SCTP_CHUNK_HB:     
  604.     case SCTP_CHUNK_HB_ACK:
  605.       iSize = SCTP_CHUNK_HEARTBEAT_LENGTH;
  606.       ((SctpHeartbeatChunk_S *) ucpChunk)->sHdr.ucType = eType;
  607.       ((SctpHeartbeatChunk_S *) ucpChunk)->sHdr.ucFlags = 0;
  608.       ((SctpHeartbeatChunk_S *) ucpChunk)->sHdr.usLength = iSize;
  609.       ((SctpHeartbeatChunk_S *) ucpChunk)->usInfoType = 1;
  610.       ((SctpHeartbeatChunk_S *) ucpChunk)->usInfoLength 
  611. = iSize - sizeof(SctpChunkHdr_S);
  612.       ((SctpHeartbeatChunk_S *) ucpChunk)->dTimestamp = dCurrTime;
  613.       ((SctpHeartbeatChunk_S *) ucpChunk)->spDest = NULL; // caller sets dest
  614.       /* fill in tracing fields too
  615.        */
  616.       spSctpTrace[uiNumChunks].eType = eType;
  617.       spSctpTrace[uiNumChunks].uiTsn = (u_int) -1;
  618.       spSctpTrace[uiNumChunks].usStreamId = (u_short) -1;
  619.       spSctpTrace[uiNumChunks].usStreamSeqNum = (u_short) -1;
  620.       break;
  621.     case SCTP_CHUNK_SHUTDOWN:
  622.       break;
  623.     case SCTP_CHUNK_SHUTDOWN_ACK:
  624.       break;
  625.     case SCTP_CHUNK_SHUTDOWN_COMPLETE:
  626.       break;
  627.     default:
  628.       fprintf(stderr, "[GenChunk] ERROR: bad chunk type!n");
  629.       DBG_PL(GenChunk, "ERROR: bad chunk type!") DBG_PR;
  630.       DBG_PL(GenChunk, "exiting...") DBG_PR;
  631.       exit(-1);
  632.     }
  633.   uiNumChunks++;
  634.   DBG_X(GenChunk);
  635.   return iSize;
  636. }
  637. void SctpCMTAgent::AddToSendBuffer(SctpDataChunkHdr_S *spChunk, 
  638.    int iChunkSize,
  639.    u_int uiReliability,
  640.    SctpDest_S *spDest)
  641. {
  642.   DBG_I(AddToSendBuffer);
  643.   DBG_PL(AddToSendBuffer, "spDest=%p  iChunkSize=%d"), 
  644.     spDest, iChunkSize DBG_PR;
  645.   Node_S *spNewNode = new Node_S;
  646.   spNewNode->eType = NODE_TYPE_SEND_BUFFER;
  647.   spNewNode->vpData = new SctpSendBufferNode_S;
  648.   SctpSendBufferNode_S * spNewNodeData 
  649.     = (SctpSendBufferNode_S *) spNewNode->vpData;
  650.   /* This can NOT simply be a 'new SctpDataChunkHdr_S', because we need to
  651.    * allocate the space for the ENTIRE data chunk and not just the data
  652.    * chunk header.  
  653.    */
  654.   spNewNodeData->spChunk = (SctpDataChunkHdr_S *) new u_char[iChunkSize];
  655.   memcpy(spNewNodeData->spChunk, spChunk, iChunkSize);
  656.   spNewNodeData->eAdvancedAcked = FALSE;
  657.   spNewNodeData->eGapAcked = FALSE;
  658.   spNewNodeData->eAddedToPartialBytesAcked = FALSE;
  659.   spNewNodeData->iNumMissingReports = 0;
  660.   spNewNodeData->iUnrelRtxLimit = uiReliability;
  661.   spNewNodeData->eMarkedForRtx = NO_RTX;
  662.   spNewNodeData->eIneligibleForFastRtx = FALSE;
  663.   spNewNodeData->iNumTxs = 1;
  664.   spNewNodeData->spDest = spDest;
  665.   /*** Begin CMT change ***/
  666.   /* Maintain timestamp for each TSN to use for rtx decision after RTO
  667.    * (RTT heuristic). Orig SCTP uses existence of a non-zero timestamp for
  668.    * indication of whether this TSN is being used for RTT estimate or
  669.    * not. Since that cannot be used anymore with this CMT change, we
  670.    * introduce a new per-TSN variable eMeasuringRtt which is TRUE is this
  671.    * TSN is being used, and FALSE otherwise.
  672.    */
  673.   spNewNodeData->dTxTimestamp = Scheduler::instance().clock();
  674.   /* Is there already a DATA chunk in flight measuring an RTT? 
  675.    * (6.3.1.C4 RTT measured once per round trip)
  676.    */
  677.   if(spDest->eRtoPending == FALSE)  // NO?
  678.     {
  679.       spNewNodeData->eMeasuringRtt = TRUE;
  680.       spDest->eRtoPending = TRUE;   // ...well now there is :-)
  681.     }
  682.   else
  683.     spNewNodeData->eMeasuringRtt = FALSE; // don't use this TSN for RTT est
  684.   /*** End of CMT change ***/
  685.   InsertNode(&sSendBuffer, sSendBuffer.spTail, spNewNode, NULL);
  686.   DBG_X(AddToSendBuffer);
  687. }
  688. /* Go thru the send buffer deleting all chunks which have a tsn <= the 
  689.  * tsn parameter passed in. We assume the chunks in the rtx list are ordered by
  690.  * their tsn value. In addtion, for each chunk deleted:
  691.  *   1. we add the chunk length to # newly acked bytes and partial bytes acked
  692.  *   2. we update round trip time if appropriate
  693.  *   3. stop the timer if the chunk's destination timer is running
  694.  */
  695. void SctpCMTAgent::SendBufferDequeueUpTo(u_int uiTsn)
  696. {
  697.   DBG_I(SendBufferDequeueUpTo);
  698.   Node_S *spDeleteNode = NULL;
  699.   Node_S *spCurrNode = sSendBuffer.spHead;
  700.   SctpSendBufferNode_S *spCurrNodeData = NULL;
  701.   iAssocErrorCount = 0;
  702.   while(spCurrNode != NULL &&
  703. ((SctpSendBufferNode_S*)spCurrNode->vpData)->spChunk->uiTsn <= uiTsn)
  704.     {
  705.       spCurrNodeData = (SctpSendBufferNode_S *) spCurrNode->vpData;
  706.       /* Only count this chunk as newly acked and towards partial bytes
  707.        * acked if it hasn't been gap acked or marked as ack'd due to rtx
  708.        * limit.  
  709.        */
  710.       if((spCurrNodeData->eGapAcked == FALSE) &&
  711.  (spCurrNodeData->eAdvancedAcked == FALSE) )
  712. {
  713.   uiHighestTsnNewlyAcked = spCurrNodeData->spChunk->uiTsn;
  714.   spCurrNodeData->spDest->iNumNewlyAckedBytes 
  715.     += spCurrNodeData->spChunk->sHdr.usLength;
  716.   /****** Begin CMT Change ******/
  717.   /* DAC algo: 
  718.    * Update lowest TSN seen in current SACK (per destination) 
  719.    * if appropriate. The lowest TSN is initialized to zero,
  720.    * and since we process cum and gap TSNs in TSN order, 
  721.    * the first value this variable gets set to will continue to be
  722.    * the lowest TSN acked. uiLowestTsnInSackForDest is used for
  723.    * missing report increments with delayed SACKs in CMT.
  724.    */
  725.   if (spCurrNodeData->spDest->uiLowestTsnInSackForDest == 0)
  726.     {
  727.       spCurrNodeData->spDest->uiLowestTsnInSackForDest =
  728. spCurrNodeData->spChunk->uiTsn;
  729.     }
  730.   /* CUC algo: Since this tsn has NOT been gapacked 
  731.    * previously, the pseudo-cum for this dest must be <= tsn.
  732.    * :. Set eFindExpectedPseudoCum to TRUE for destination 
  733.    * being cum acked. In gap ack processing, we'll start 
  734.    * searching for new expected-pseudo-cum.
  735.    */
  736.   spCurrNodeData->spDest->eFindExpectedPseudoCum = TRUE;
  737.   spCurrNodeData->spDest->eFindExpectedRtxPseudoCum = TRUE;
  738.     
  739.   /* CUC algo: Set eNewPseudoCum to TRUE to trigger cwnd adjustment.
  740.    * The cwnd should be adjusted only if new-pseudo-cum exists;
  741.    * otherwise this TSN would have already contributed to the cwnd.
  742.    * If this TSN had been gap acked previously, then it cannot be 
  743.    * the current expected-pseudo-cum.. and hence, the TSN cannot 
  744.    * contribute as a new-pseudo-cum. Here, we know that the TSN
  745.    * has not been gap acked before.
  746.    */
  747.   spCurrNodeData->spDest->eNewPseudoCum = TRUE;
  748.   
  749.   /****** End CMT Change ******/
  750.   /* only add to partial bytes acked if we are in congestion
  751.    * avoidance mode and if there was cwnd amount of data
  752.    * outstanding on the destination (implementor's guide) 
  753.    */
  754.   if(spCurrNodeData->spDest->iCwnd >spCurrNodeData->spDest->iSsthresh 
  755.      &&
  756.      ( spCurrNodeData->spDest->iOutstandingBytes 
  757.        >= spCurrNodeData->spDest->iCwnd) )
  758.     {
  759.       spCurrNodeData->spDest->iPartialBytesAcked 
  760. += spCurrNodeData->spChunk->sHdr.usLength;
  761.     }
  762. }
  763.       /* This is to ensure that Max.Burst is applied when a SACK
  764.        * acknowledges a chunk which has been fast retransmitted. If it is
  765.        * ineligible for fast rtx, that can only be because it was fast
  766.        * rtxed or it timed out. If it timed out, a burst shouldn't be
  767.        * possible, but shouldn't hurt either. The fast rtx case is what we
  768.        * are really after. This is a proposed change to RFC2960 section
  769.        * 7.2.4
  770.        */
  771.       if(spCurrNodeData->eIneligibleForFastRtx == TRUE)
  772. eApplyMaxBurst = TRUE;
  773.       /****** Begin CMT Change ******/
  774.       /* We update the RTT estimate if the following hold true:
  775.        *   1. RTO pending flag is set (6.3.1.C4 measured once per round trip)
  776.        *   2. We were measuring RTT with this chunk
  777.        *   3. This chunk has not been retransmitted
  778.        *   4. This chunk has not been gap acked already 
  779.        *   5. This chunk has not been advanced acked (pr-sctp: exhausted rtxs)
  780.        */
  781.       if(spCurrNodeData->spDest->eRtoPending == TRUE &&
  782.  spCurrNodeData->eMeasuringRtt == TRUE &&
  783.  spCurrNodeData->iNumTxs == 1 &&
  784.  spCurrNodeData->eGapAcked == FALSE &&
  785.  spCurrNodeData->eAdvancedAcked == FALSE) 
  786. {
  787.   /* If the chunk is marked for timeout rtx, then the sender is an 
  788.    * ambigious state. Were the sacks lost or was there a failure? 
  789.    * (See below, where we talk about error count resett1ing)
  790.    * Since we don't clear the error counter below, we also don't
  791.    * update the RTT. This could be a problem for late arriving SACKs.
  792.    */
  793.   if(spCurrNodeData->eMarkedForRtx != TIMEOUT_RTX)
  794.     RttUpdate(spCurrNodeData->dTxTimestamp, spCurrNodeData->spDest);
  795.   spCurrNodeData->spDest->eRtoPending = FALSE;
  796. }
  797.       /* if there is a timer running on the chunk's destination, then stop it
  798.        */
  799.       /* CMT: Added check for expectedPseudoCum.Before this change, even if the
  800.        * timer for a dest was not running on any of the TSNs being dequeued
  801.        * (because the pseudoCum had moved way further already), the timer
  802.        * was being restarted due to the reset here. This caused timeouts
  803.        * to get delayed, and timeout recovery got longer.
  804.        */
  805.       if((spCurrNodeData->spDest->eRtxTimerIsRunning == TRUE) &&
  806.  (spCurrNodeData->spDest->uiExpectedPseudoCum <=
  807.   spCurrNodeData->spChunk->uiTsn) ||
  808.  (spCurrNodeData->spDest->uiExpectedRtxPseudoCum <=
  809.   spCurrNodeData->spChunk->uiTsn))
  810. StopT3RtxTimer(spCurrNodeData->spDest);
  811.       /****** End CMT Change ******/
  812.  
  813.       /* We don't want to clear the error counter if it's cleared already;
  814.        * otherwise, we'll unnecessarily trigger a trace event.
  815.        *
  816.        * Also, the error counter is cleared by SACKed data ONLY if the
  817.        * TSNs are not marked for timeout retransmission and has not been
  818.        * gap acked before. Without this condition, we can run into a
  819.        * problem for failure detection. When a failure occurs, some data
  820.        * may have made it through before the failure, but the sacks got
  821.        * lost. When the sender retransmits the first outstanding, the
  822.        * receiver will sack all the data whose sacks got lost. We don't
  823.        * want these sacks to clear the error counter, or else failover
  824.        * would take longer.
  825.        */
  826.       if(spCurrNodeData->spDest->iErrorCount != 0 && 
  827.  spCurrNodeData->eMarkedForRtx != TIMEOUT_RTX &&
  828.  spCurrNodeData->eGapAcked == FALSE)
  829. {
  830.   
  831.   /* If running CMT-PF, clear error counter and move destination
  832.    * from PF to Active only if this chunk has been transmitted
  833.    * only once. Else, ambiguity in deciding whether sack was for
  834.    * original transmission or retransmission.
  835.    */
  836.   if (((eUseCmtPF == TRUE) && (spCurrNodeData->iNumTxs == 1)) ||
  837.       (eUseCmtPF == FALSE))
  838.     {
  839.       DBG_PL(SendBufferDequeueUpTo, 
  840.      "clearing error counter for %p with tsn=%lu"), 
  841. spCurrNodeData->spDest, spCurrNodeData->spChunk->uiTsn DBG_PR;
  842.       
  843.       spCurrNodeData->spDest->iErrorCount = 0; // clear error counter
  844.       tiErrorCount++;                          // ... and trace it too!
  845.       spCurrNodeData->spDest->eStatus = SCTP_DEST_STATUS_ACTIVE;
  846.       if(spCurrNodeData->spDest == spPrimaryDest &&
  847.  spNewTxDest != spPrimaryDest) 
  848. {
  849.   DBG_PL(SendBufferDequeueUpTo,
  850.  "primary recovered... migrating back from %p to %p"),
  851.     spNewTxDest, spPrimaryDest DBG_PR;
  852.   spNewTxDest = spPrimaryDest; // return to primary
  853. }
  854.     }
  855. }
  856.       
  857.       spDeleteNode = spCurrNode;
  858.       spCurrNode = spCurrNode->spNext;
  859.       DeleteNode(&sSendBuffer, spDeleteNode);
  860.       spDeleteNode = NULL;
  861.     }
  862.   
  863.   DBG_X(SendBufferDequeueUpTo);
  864. }
  865. Boolean_E SctpCMTAgent::AnyMarkedChunks()
  866. {
  867.   /****** Begin CMT Change ******/
  868.   DBG_I(AnyMarkedChunks);
  869.   Node_S *spCurrBuffNode = NULL;
  870.   SctpSendBufferNode_S *spCurrBuffNodeData = NULL;
  871.   Boolean_E eMarkedChunksExist = FALSE;
  872.   Node_S *spCurrDestNode = NULL;
  873.   SctpDest_S *spCurrDestNodeData = NULL;
  874.   /* CMT change: added eMarkedChunksPending per destination.
  875.    * reset eMarkedChunksPending for all dests
  876.    */
  877.   for(spCurrDestNode = sDestList.spHead;
  878.       spCurrDestNode != NULL;
  879.       spCurrDestNode = spCurrDestNode->spNext)
  880.     {
  881.       spCurrDestNodeData = (SctpDest_S *) spCurrDestNode->vpData;
  882.       spCurrDestNodeData->eMarkedChunksPending = FALSE;
  883.     }
  884.   for(spCurrBuffNode = sSendBuffer.spHead;
  885.       spCurrBuffNode != NULL; 
  886.       spCurrBuffNode = spCurrBuffNode->spNext)
  887.     {
  888.       spCurrBuffNodeData = (SctpSendBufferNode_S *) spCurrBuffNode->vpData;
  889.       if(spCurrBuffNodeData->eMarkedForRtx != NO_RTX)
  890. {
  891.   eMarkedChunksExist = TRUE;
  892.   spCurrBuffNodeData->spDest->eMarkedChunksPending = TRUE;
  893.   DBG_PL(AnyMarkedChunks, "Dest %p: TRUE"), 
  894.     spCurrBuffNodeData->spDest DBG_PR;
  895. }
  896.     }
  897.   DBG_PL(AnyMarkedChunks, "Returning %s"), 
  898.     eMarkedChunksExist ? "TRUE" : "FALSE" DBG_PR;
  899.   DBG_X(AnyMarkedChunks);
  900.   return eMarkedChunksExist;
  901.   /****** End CMT Change ******/
  902. }
  903. /* This function goes through the entire send buffer filling a packet with 
  904.  * chunks marked for retransmission. Once a packet is full (according to MTU)
  905.  * it is transmittted. If the eLimit is one packet, than that is all that is
  906.  * done. If the eLimit is cwnd, then packets full of marked tsns are sent until
  907.  * cwnd is full.
  908.  */
  909. void SctpCMTAgent::RtxMarkedChunks(SctpRtxLimit_E eLimit)
  910. {
  911.   DBG_I(RtxMarkedChunks);
  912.   u_char *ucpOutData = new u_char[uiMaxPayloadSize];
  913.   u_char *ucpCurrOutData = ucpOutData;
  914.   int iBundledControlChunkSize = 0;
  915.   int iCurrSize = 0;
  916.   int iOutDataSize = 0;
  917.   Node_S *spCurrBuffNode = NULL;
  918.   SctpSendBufferNode_S *spCurrBuffNodeData = NULL;
  919.   SctpDataChunkHdr_S  *spCurrChunk;
  920.   SctpDest_S *spRtxDest = NULL;
  921.   Node_S *spCurrDestNode = NULL;
  922.   SctpDest_S *spCurrDestNodeData = NULL;
  923.   Boolean_E eControlChunkBundled = FALSE;
  924.   /****** Begin CMT Change ******/
  925.   /* CMT change: Var is now per dest, part of SctpDest_S
  926.    *
  927.    * int iNumPacketsSent = 0; 
  928.    */
  929.   SctpDest_S *spTraceDest = NULL;
  930.   char cpOutString[500];
  931.   u_int uiRtxTsn = 0;
  932.   double dCurrTime = Scheduler::instance().clock();
  933.   /****** End CMT Change ******/
  934.   memset(ucpOutData, 0, uiMaxPayloadSize);
  935.   /****** Begin CMT Change ******/
  936.   /* CMT change: This var is now per dest. Initialized inside loop.
  937.    * uiBurstLength = 0;
  938.    */
  939.   /****** End CMT Change ******/
  940.   /* make sure we clear all the spFirstOutstanding pointers before using them!
  941.    */
  942.   for(spCurrDestNode = sDestList.spHead;
  943.       spCurrDestNode != NULL;
  944.       spCurrDestNode = spCurrDestNode->spNext)
  945.     {
  946.       spCurrDestNodeData = (SctpDest_S *) spCurrDestNode->vpData;
  947.       spCurrDestNodeData->spFirstOutstanding = NULL;  // reset
  948.       /****** Begin CMT Change ******/
  949.       spCurrDestNodeData->iNumPacketsSent = 0; // reset
  950.       spCurrDestNodeData->uiBurstLength = 0; // reset
  951.       /****** End CMT Change ******/
  952.     }
  953.   /****** Begin CMT Change ******/
  954.   /* We need to set the destination address for the retransmission(s).We assume
  955.    * that on a given call to this function, all should all be sent to the same
  956.    * address (should be a reasonable assumption). So, to determine the address,
  957.    * we find the first marked chunk and determine the destination it was last 
  958.    * sent to. 
  959.    *
  960.    * CMT change: This assumption is not reasonable for CMT. We therefore 
  961.    * separate the rtxdest selection into a separate function.
  962.    *
  963.    * Also, we temporarily count all marked chunks as not outstanding.Why? Well,
  964.    * if we try retransmitting on the same dest as used previously, the cwnd may
  965.    * never let us retransmit because the outstanding is counting marked chunks
  966.    * too. At the end of this function, we'll count all marked chunks as 
  967.    * outstanding again. (ugh... there has to be a better way!)
  968.    */
  969.   for(spCurrBuffNode = sSendBuffer.spHead; 
  970.       spCurrBuffNode != NULL;
  971.       spCurrBuffNode = spCurrBuffNode->spNext)
  972.     {
  973.       spCurrBuffNodeData = (SctpSendBufferNode_S *) spCurrBuffNode->vpData;
  974.   
  975.       if(spCurrBuffNodeData->eMarkedForRtx != NO_RTX)
  976. {
  977.   spCurrChunk = spCurrBuffNodeData->spChunk;
  978.   spCurrBuffNodeData->spDest->iOutstandingBytes
  979.     -= spCurrChunk->sHdr.usLength;
  980.   /* CMT change: Removed code for RtxDest selection. CMT does its
  981.    * selection per TSN, and is inside the loop.
  982.    */
  983. }
  984.     }
  985.   spCurrBuffNode = sSendBuffer.spHead;
  986.   /* CMT change: While loop should go through entire rtx list. 
  987.    * checks are within loop to decide when an rtx should occur or not.
  988.    * check has to be done per tsn to check if a dest is available for
  989.    * that tsn and whether the eLimit applies to a given dest.
  990.    */
  991.   while(spCurrBuffNode != NULL)
  992.     {
  993.       
  994.       /* CMT change: Set spRtxDest to the rtx dest 
  995.        * of the first chunk in a rtx packet. Init to NULL now, and
  996.        * set to rtx dest (according to policy) when chunk to be rtx'd 
  997.        * is found.
  998.        */
  999.        spRtxDest = NULL;
  1000.       /* section 7.2.4.3
  1001.        *
  1002.        * continue filling up the packet with chunks which are marked for
  1003.        * rtx. exit loop when we have either run out of chunks or the
  1004.        * packet is full.
  1005.        *
  1006.        * note: we assume at least one chunk fits in the packet.  
  1007.        */
  1008.       for(eControlChunkBundled = FALSE; 
  1009.   spCurrBuffNode != NULL; 
  1010.   spCurrBuffNode = spCurrBuffNode->spNext)
  1011. {
  1012.   spCurrBuffNodeData = (SctpSendBufferNode_S *) spCurrBuffNode->vpData;
  1013.   
  1014.   /* is this chunk the first outstanding on its destination?
  1015.    */
  1016.   if(spCurrBuffNodeData->spDest->spFirstOutstanding == NULL &&
  1017.      spCurrBuffNodeData->eGapAcked == FALSE &&
  1018.      spCurrBuffNodeData->eAdvancedAcked == FALSE)
  1019.     {
  1020.       /* yes, it is the first!
  1021.        */
  1022.       spCurrBuffNodeData->spDest->spFirstOutstanding 
  1023. = spCurrBuffNodeData;
  1024.     }
  1025.   /* Only retransmit the chunks which have been marked for rtx.
  1026.    */
  1027.   if(spCurrBuffNodeData->eMarkedForRtx != NO_RTX)
  1028.     {
  1029.       spCurrChunk = spCurrBuffNodeData->spChunk;
  1030.       /* CMT rtx policy used here. Set spRtxDest (according
  1031.        * to policy) to the rtx dest of the first chunk in a rtx packet.
  1032.        * spRtxDest is set to NULL at the beginning of a packet; once
  1033.        * a chunk to be rtx'd is found, rtxDest for the packet is set to
  1034.        * the rtx dest for that chunk, which is decided by the rtx 
  1035.        * policy.
  1036.        */
  1037.       if(spRtxDest == NULL) 
  1038. {
  1039.   /* SelectRtxDest() decides rtx dest based on policy and
  1040.    * returns dest. If NULL is returned, this TSN cannot
  1041.    * be rtx'd at this time---this will cause the for loop
  1042.    * to terminate, and the while loop to be reentered.
  1043.    */
  1044.   spRtxDest = SelectRtxDest(spCurrBuffNodeData, eLimit);
  1045. }
  1046.       
  1047.       /* Check finally if for loop should be quit.
  1048.        * If spRtxDest is NULL, no dest was able to send rtx.
  1049.        */
  1050.       if(spRtxDest == NULL)
  1051. {
  1052.   /* Jump out of for loop;move spCurrBuffNode to next in queue.
  1053.    * At this point, no dest was able to send this rtx. 
  1054.    * There may be other TSNs in the queue waiting for rtx, 
  1055.    * so we move on to process them. "while" loop is reentered,
  1056.    * and all tsns in the queue are processed.
  1057.    */
  1058.   spCurrBuffNode = spCurrBuffNode->spNext;
  1059.   DBG_PL(RtxMarkedChunks, 
  1060.  "Unable to rtx TSN %d at this timen"), 
  1061.     spCurrChunk->uiTsn DBG_PR;
  1062.   break; 
  1063. }
  1064.       else
  1065. {
  1066.   /* CMT CUC algo: If rtxdest is not the same as orig dest, set
  1067.      * both eFindExpectedPseudoCumack and 
  1068.    * eFindExpectedRtxPseudoCumack to TRUE.
  1069.    */
  1070.   if(spCurrBuffNodeData->spDest != spRtxDest)
  1071.     {
  1072.       spCurrBuffNodeData->spDest->eFindExpectedPseudoCum 
  1073. = TRUE;
  1074.       spCurrBuffNodeData->spDest->eFindExpectedRtxPseudoCum
  1075. = TRUE;
  1076.     }
  1077. }
  1078.       DBG_PL(RtxMarkedChunks, 
  1079.      "eLimit=%s spRtxDest->eRtxLimit = %d pktsSent=%d out=%d cwnd=%d spCurrChunk->uiTsn=%d"),
  1080. (eLimit == RTX_LIMIT_ONE_PACKET) ? "ONE_PACKET" : "CWND",
  1081. spRtxDest->eRtxLimit,
  1082. spRtxDest->iNumPacketsSent, spRtxDest->iOutstandingBytes, 
  1083. spRtxDest->iCwnd, 
  1084. spCurrChunk->uiTsn DBG_PR;
  1085.       /* CMT change: Code moved from timeoutrtx
  1086.        * No fast rtxs allowed on TSNs that have timed out.
  1087.        * This is particularly important for Changeover/CMT.
  1088.        */
  1089.       spCurrBuffNodeData->eIneligibleForFastRtx = TRUE;
  1090.       
  1091.       /****** End CMT Change ******/
  1092.       /* bundle the control chunk before any data chunks and only
  1093.        * once per packet
  1094.        */
  1095.       if(eControlChunkBundled == FALSE)
  1096. {
  1097.   eControlChunkBundled = TRUE;
  1098.   iBundledControlChunkSize 
  1099.     = BundleControlChunks(ucpCurrOutData);
  1100.   ucpCurrOutData += iBundledControlChunkSize;
  1101.   iOutDataSize += iBundledControlChunkSize;
  1102. }
  1103.       /* can we fit this chunk into the packet without exceeding MTU?? 
  1104.        */
  1105.          if((iOutDataSize + spCurrChunk->sHdr.usLength) 
  1106.  > (int) uiMaxPayloadSize)
  1107. break;  // doesn't fit in packet... jump out of the for loop
  1108.       /* If this chunk was being used to measure the RTT,stop using it.
  1109.        */
  1110.       if(spCurrBuffNodeData->spDest->eRtoPending == TRUE &&
  1111.  spCurrBuffNodeData->eMeasuringRtt == TRUE)
  1112. {
  1113.   spCurrBuffNodeData->eMeasuringRtt = FALSE;
  1114.   spCurrBuffNodeData->spDest->eRtoPending = FALSE;
  1115. }
  1116.       /* section 7.2.4.4 (condition 2) - is this the first
  1117.        * outstanding for the destination and are there still
  1118.        * outstanding bytes on the destination? if so, restart
  1119.        * timer.  
  1120.        */
  1121.       if(spCurrBuffNodeData->spDest->spFirstOutstanding 
  1122.  == spCurrBuffNodeData)
  1123. {
  1124.   if(spCurrBuffNodeData->spDest->iOutstandingBytes > 0)
  1125.     StartT3RtxTimer(spCurrBuffNodeData->spDest);
  1126. }
  1127.       /* section 6.1 - Whenever a transmission or retransmission is 
  1128.        * made to any address, if the T3-rtx timer of that address 
  1129.        * is not currently running, the sender MUST start that timer.
  1130.        * If the timer for that address is already running, the sender 
  1131.        * MUST restart the timer if the earliest (i.e., lowest TSN)
  1132.        * outstanding DATA chunk sent to that address is being
  1133.        * retransmitted.  Otherwise, the data sender MUST NOT
  1134.        * restart the timer.  
  1135.        */
  1136.         if(spRtxDest->spFirstOutstanding == NULL ||
  1137.  spCurrChunk->uiTsn <
  1138.  spRtxDest->spFirstOutstanding->spChunk->uiTsn)
  1139.   {
  1140.   /* This chunk is now the first outstanding on spRtxDest.
  1141.    */
  1142.     spRtxDest->spFirstOutstanding = spCurrBuffNodeData;
  1143.     StartT3RtxTimer(spRtxDest);
  1144. }
  1145.       memcpy(ucpCurrOutData, spCurrChunk, spCurrChunk->sHdr.usLength);
  1146.       iCurrSize = spCurrChunk->sHdr.usLength;
  1147.       /* the chunk length field does not include the padded bytes,
  1148.        * so we need to account for these extra bytes.
  1149.        */
  1150.       if( (iCurrSize % 4) != 0 ) 
  1151. iCurrSize += 4 - (iCurrSize % 4);
  1152.       ucpCurrOutData += iCurrSize;
  1153.       iOutDataSize += iCurrSize;
  1154.       spCurrBuffNodeData->spDest = spRtxDest;
  1155.       spCurrBuffNodeData->iNumTxs++;
  1156.       spCurrBuffNodeData->eMarkedForRtx = NO_RTX;
  1157.       /* fill in tracing fields too
  1158.        */
  1159.       spSctpTrace[uiNumChunks].eType = SCTP_CHUNK_DATA;
  1160.       spSctpTrace[uiNumChunks].uiTsn = spCurrChunk->uiTsn;
  1161.       spSctpTrace[uiNumChunks].usStreamId = spCurrChunk->usStreamId;
  1162.       spSctpTrace[uiNumChunks].usStreamSeqNum 
  1163. = spCurrChunk->usStreamSeqNum;
  1164.       uiNumChunks++;
  1165.       /* the chunk is now outstanding on the alternate destination
  1166.        */
  1167.       spCurrBuffNodeData->spDest->iOutstandingBytes
  1168. += spCurrChunk->sHdr.usLength;
  1169.       uiPeerRwnd -= spCurrChunk->sHdr.usLength; // 6.2.1.B
  1170.       DBG_PL(RtxMarkedChunks, "spDest->iOutstandingBytes=%d"), 
  1171. spCurrBuffNodeData->spDest->iOutstandingBytes DBG_PR;
  1172.       DBG_PL(RtxMarkedChunks, "TSN=%d"), spCurrChunk->uiTsn DBG_PR;
  1173.       uiRtxTsn=spCurrChunk->uiTsn;
  1174.     }
  1175.   else if(spCurrBuffNodeData->eAdvancedAcked == TRUE)
  1176.     {
  1177.       if(spCurrBuffNodeData->spDest->spFirstOutstanding 
  1178.  == spCurrBuffNodeData)
  1179. {
  1180.   /* This WAS considered the first outstanding chunk for
  1181.    * the destination, then stop the timer if there are no
  1182.    * outstanding chunks waiting behind this one in the
  1183.    * send buffer.  However, if there ARE more outstanding
  1184.    * chunks on this destination, we need to restart timer
  1185.    * for those.
  1186.    */
  1187.   if(spCurrBuffNodeData->spDest->iOutstandingBytes > 0)
  1188.     StartT3RtxTimer(spCurrBuffNodeData->spDest);
  1189.   else
  1190.     StopT3RtxTimer(spCurrBuffNodeData->spDest);
  1191. }
  1192.     }
  1193. } // end of for loop
  1194.       /* Transmit the packet now...
  1195.        */
  1196.       if(iOutDataSize > 0)
  1197. {
  1198.   SendPacket(ucpOutData, iOutDataSize, spRtxDest);
  1199.   DBG_PL(RtxMarkedChunks, "OutDataSize = %d"), iOutDataSize DBG_PR;
  1200.   /****** Begin CMT Change ******/
  1201.   /* PN: Track rtx tsns in the trace file for debug purposes. 
  1202.    */
  1203.   spTraceDest = spRtxDest;
  1204.   SetSource(spTraceDest); // gives us the correct source addr & port
  1205.   sprintf(cpOutString,
  1206.   "time: %-8.5f  "
  1207.   "saddr: %-2d sport: %-2d daddr: %-2d dport: %-2d "
  1208.   "RTX TSN: %d ",
  1209.   dCurrTime,
  1210.   addr(), port(), spTraceDest->iNsAddr, spTraceDest->iNsPort,
  1211.   uiRtxTsn);
  1212.   if(channel_)
  1213.     (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
  1214.   sprintf(cpOutString, "nn");
  1215.   if(channel_)
  1216.     (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
  1217.   /****** End CMT Change ******/
  1218.   if(spRtxDest->eRtxTimerIsRunning == FALSE)
  1219.     StartT3RtxTimer(spRtxDest);
  1220.   /****** Begin CMT Change ******/
  1221.   (spRtxDest->iNumPacketsSent)++;   
  1222.   /****** End CMT Change ******/
  1223.   iOutDataSize = 0; // reset
  1224.   ucpCurrOutData = ucpOutData; // reset
  1225.   memset(ucpOutData, 0, uiMaxPayloadSize); // reset
  1226.   spRtxDest->opCwndDegradeTimer->resched(spRtxDest->dRto);
  1227.   /****** Begin CMT Change ******/
  1228.   /* This addresses the proposed change to RFC2960 section 7.2.4,
  1229.    * regarding using of Max.Burst. We have an option which allows
  1230.    * to control if Max.Burst is applied.
  1231.    */
  1232.   /* CMT change: Use per dest burstlength to track bursts per dest.  
  1233.    * *** BUG: The while loop should terminate after ALL
  1234.    * selectable destinations have either exhausted their cwnds, or
  1235.    * their burst limits.  As the code stands, whenever the burst
  1236.    * limit is reached on ANY dest, the while loop is quit.  Is
  1237.    * this a problem? Well... maybe not. RTX-SSTHRESH/CWND/LR
  1238.    * should have (normally) one destination to which rtxs are
  1239.    * being sent. In this case, we would not more destinations to
  1240.    * try on anyways. In RTX-SAME/ASAP, the next incoming SACK will
  1241.    * trigger a call to this function anyways.  
  1242.    * 
  1243.    * JRI-TODO: This bug should be fixed! Idea: Move check inside 
  1244.    * SelectRtxDest(). Dest not selectable if MaxBurst exceeded. 
  1245.    * That should do it.
  1246.    */
  1247.   if(eUseMaxBurst == MAX_BURST_USAGE_ON)
  1248.     if( (eApplyMaxBurst == TRUE) && 
  1249. ((spRtxDest->uiBurstLength)++ >= MAX_BURST) )
  1250.       {
  1251. /* we've reached Max.Burst limit, so jump out of loop
  1252.  */
  1253. break;
  1254.       }
  1255.   /****** End CMT Change ******/
  1256. }
  1257.     } // end of while loop
  1258.   /****** Begin CMT Change ******/
  1259.   /* 
  1260.    * With the RTT heuristic in CMT, TSNs outstanding for less than 1 SRTT
  1261.    * are not marked for rtx. Therefore, at this point in the function, if
  1262.    * no rtxs were sent out, but there are these unmarked TSNs less than 1
  1263.    * SRTT old outstanding, then the timer should be running for those TSNs.
  1264.    * 
  1265.    * Interesting case here. Rtxs are marked, but should not be
  1266.    * outstanding, and may not have been rtx'd at all. But with the RTT
  1267.    * heuristic there may be TSNs sent less than 1 SRTT ago that are
  1268.    * outstanding (i.e., not marked for rtx) and need the timer. So, what
  1269.    * should spFirstOutstanding point to? We think that the first REAL
  1270.    * outstanding chunk should be spFirstOutstanding. That is, set
  1271.    * spFirstOutstanding to the first chunk that (i) is not marked for rtx,
  1272.    * and (ii) is not yet gapacked. If a TSN does get rtx'd later to this
  1273.    * same dest, then the spFirstOutstanding will move to point to the
  1274.    * lower TSN.  
  1275.    * 
  1276.    * JRI-TODO, PN-TODO: Go through sendbuf and set spFirstOutstanding to
  1277.    * first TSN which is (i) is not marked for rtx, and (ii) is not yet
  1278.    * gapacked.
  1279.    */
  1280.     for(spCurrDestNode = sDestList.spHead;
  1281.       spCurrDestNode != NULL;
  1282.       spCurrDestNode = spCurrDestNode->spNext)
  1283.     {
  1284.       spCurrDestNodeData = (SctpDest_S *) spCurrDestNode->vpData;
  1285.       if((spCurrDestNodeData->eRtxTimerIsRunning == FALSE) && 
  1286.  (spCurrDestNodeData->iOutstandingBytes > 0)) 
  1287. {
  1288.   DBG_PL(RtxMarkedChunks, "Dest %p: starting T3 timer"), 
  1289.     spCurrDestNodeData DBG_PR;
  1290.   StartT3RtxTimer(spCurrBuffNodeData->spDest);
  1291. }
  1292.     }
  1293.     /****** End CMT Change ******/
  1294.   /* Ok, let's count all marked chunks as outstanding again. (ugh... there
  1295.    * has to be a better way!)  
  1296.    */
  1297.   for(spCurrBuffNode = sSendBuffer.spHead; 
  1298.       spCurrBuffNode != NULL;
  1299.       spCurrBuffNode = spCurrBuffNode->spNext)
  1300.     {
  1301.       spCurrBuffNodeData = (SctpSendBufferNode_S *) spCurrBuffNode->vpData;
  1302.   
  1303.       if(spCurrBuffNodeData->eMarkedForRtx != NO_RTX)
  1304. {
  1305.   spCurrChunk = spCurrBuffNodeData->spChunk;
  1306.   spCurrBuffNodeData->spDest->iOutstandingBytes
  1307.     += spCurrChunk->sHdr.usLength;
  1308. }
  1309.     }
  1310.     
  1311.   /* If we made it here, either our limit was only one packet worth of
  1312.    * retransmissions or we hit the end of the list and there are no more
  1313.    * marked chunks.If we didn't hit the end, let's see if there are more marked
  1314.    * chunks.
  1315.    */
  1316.   eMarkedChunksPending = AnyMarkedChunks();
  1317.   DBG_X(RtxMarkedChunks);
  1318.   delete [] ucpOutData;
  1319. }
  1320. /****** Begin CMT Change ******/
  1321. /* New CMT function:
  1322.  * returns the retransmission destination for the given
  1323.  * TSN and policy.  returns NULL if no dest is found that can accomodate
  1324.  * this TSN given the policy and eLimit.
  1325.  *
  1326.  * CMT-PF: CMT-PF code implemented only for 
  1327.  * RTX-SSTHRESH and RTX-CWND policies
  1328.  */
  1329. SctpDest_S* SctpCMTAgent::SelectRtxDest(SctpSendBufferNode_S 
  1330. *spCurrBuffNodeData,
  1331. SctpRtxLimit_E eLimit)
  1332. {
  1333.   Boolean_E eFoundRtxDest = FALSE;
  1334.   SctpDest_S *spRtxDest = NULL;
  1335.   Node_S *spCurrDestNode = NULL;
  1336.   SctpDest_S *spCurrDestNodeData = NULL;
  1337.   unsigned int uiHighestSsthresh = 0, uiHighestCwnd = 0;
  1338.   float fLowestLossrate = 0;
  1339.   
  1340.   DBG_I(SelectRtxDest);
  1341.   eFoundRtxDest = FALSE;
  1342.   switch (eCmtRtxPolicy)
  1343.     {
  1344.     case RTX_ASAP:
  1345.       /* NE: 04/16/2007
  1346.        * CMT rtx policy - rtxasap: Try to rtx to any destination for
  1347.        * which the sender has cwnd space available at the time of 
  1348.        * retransmission. If multiple destinations have available cwnd
  1349.        * space, one is chosen randomly.
  1350.        * Randomization of selecting a destination is not uniform in 
  1351.        * this implementation.
  1352.        * IMPORTANT NOTICE: This rtx. policy is used for experimental purposes
  1353.        * ONLY and is retained for verifying previous experiments. Please use
  1354.        * one of the suggested rtx. policies for CMT and CMT-PF experiments: 
  1355.        * RTX_SSTHRESH or RTX_CWND.
  1356.        */
  1357.       
  1358.       spCurrDestNodeData = spCurrBuffNodeData->spDest;
  1359.       DBG_PL(SelectRtxDest, "Current Dest: %p, Status: %d"), 
  1360. spCurrDestNodeData, spCurrDestNodeData->eStatus DBG_PR;
  1361.       for(spCurrDestNode = sDestList.spHead;
  1362.   spCurrDestNode != NULL;
  1363.   spCurrDestNode = spCurrDestNode->spNext)
  1364. {
  1365.   spCurrDestNodeData = (SctpDest_S *) spCurrDestNode->vpData;
  1366.   /* Modified rtx mechanisms from SCTP. Allow ONE_PACKET_LIMIT
  1367.    * for dest which has suffered a loss, so that at least one dest
  1368.    * is allowed to retransmit one lost packet immediately. The rest
  1369.    * of the dests are bound by their resp. cwnds. Subsequent calls 
  1370.    * triggering sending rtxs will be bound by the resp. cwnds.
  1371.    */
  1372.   if((eLimit == RTX_LIMIT_ONE_PACKET && 
  1373.       spCurrDestNodeData->eRtxLimit == RTX_LIMIT_ONE_PACKET &&
  1374.       spCurrDestNodeData->iNumPacketsSent < 1 ) ||
  1375.      (spCurrDestNodeData->iOutstandingBytes < 
  1376.       spCurrDestNodeData->iCwnd)) 
  1377.     {
  1378.       if (spCurrDestNodeData->eStatus == SCTP_DEST_STATUS_ACTIVE) 
  1379. {   
  1380.   if((eFoundRtxDest == TRUE) && (Random::random()&01))
  1381.     {
  1382.       /* Randomize dest if more than one found.
  1383.        * If random() returns 1, then retain prev dest.,
  1384.        * else set rtxDest to this dest.
  1385.        * Nothing to be done here.
  1386.        */
  1387.     }
  1388.   else
  1389.     {
  1390.       /* rtx will be sent to this destination.
  1391.        * Set eFoundRtxDest to TRUE, to continue with 
  1392.        * the procedure. 
  1393.        */
  1394.       eFoundRtxDest = TRUE;
  1395.       spRtxDest = spCurrDestNodeData;
  1396.       
  1397.     }
  1398. }
  1399.     }
  1400. }
  1401.       /* No Active destinations found, set eFoundRtxDest to FALSE
  1402.        * to return NULL 
  1403.        */
  1404.       if (spRtxDest == NULL) 
  1405. eFoundRtxDest = FALSE;
  1406.       else
  1407.         {
  1408.   
  1409.   DBG_PL(SelectRtxDest, "Dest: %p"), spRtxDest  DBG_PR;
  1410.   
  1411.          /* Modified rtx mechanisms from SCTP. Allow ONE_PACKET_LIMIT
  1412.            * for dest which has suffered a loss, so that at least one dest
  1413.            * is allowed to retransmit one lost packet immediately. The rest
  1414.            * of the dests are bound by their resp. cwnds. Subsequent calls 
  1415.            * triggering sending rtxs will be bound by the resp. cwnds.
  1416.            */
  1417.          if((eLimit == RTX_LIMIT_ONE_PACKET && 
  1418.       spRtxDest->eRtxLimit == RTX_LIMIT_ONE_PACKET &&
  1419.       spRtxDest->iNumPacketsSent < 1 ) ||
  1420.      (spRtxDest->iOutstandingBytes < spRtxDest->iCwnd)) 
  1421.     {
  1422.       /* rtx will be sent to rtxDest. Set eFoundRtxDest
  1423.        * to TRUE, to continue with the rtx procedure.
  1424.        */
  1425.       eFoundRtxDest = TRUE;
  1426.       DBG_PL(SelectRtxDest, "SelectedRtxDest: %p"), spRtxDest DBG_PR;
  1427.     }
  1428.         }
  1429.       
  1430.       break; 
  1431.       
  1432.     case RTX_TO_SAME:
  1433.       /* NE: 4/29/2007
  1434.        * CMT rtx policy - rtxtosame: Rtx goes to same dest only(until the 
  1435.        * destination is deemed INACTIVE due to failure).
  1436.        * IMPORTANT NOTICE: This rtx. policy is used for experimental purposes
  1437.        * ONLY and is retained for verifying previous experiments. Please use
  1438.        * one of the suggested rtx. policies for CMT and CMT-PF experiments: 
  1439.        * RTX_SSTHRESH or RTX_CWND. 
  1440.        */
  1441.       DBG_PL(SelectRtxDest, "Rtx policy: RTX-SAME") DBG_PR;
  1442.       spRtxDest = spCurrBuffNodeData->spDest;
  1443.       /* If RtxDest is failed, get next in list. 
  1444.        * Infinite loop if all dests failed! 
  1445.        */
  1446.       while (spRtxDest->eStatus == SCTP_DEST_STATUS_INACTIVE) 
  1447. {
  1448.   spRtxDest = GetNextDest(spRtxDest);
  1449. }
  1450.       
  1451.       DBG_PL(SelectRtxDest, 
  1452.      "spRtxDest %p: iCwnd=%d iOutstandingBytes=%d iNumPacketsSent=%d"),
  1453. spRtxDest, spRtxDest->iCwnd, spRtxDest->iOutstandingBytes, 
  1454. spRtxDest->iNumPacketsSent DBG_PR;
  1455.       
  1456.       /* No Active destinations found, set eFoundRtxDest to FALSE
  1457.        * to return NULL 
  1458.        */
  1459.       if (spRtxDest == NULL) 
  1460. eFoundRtxDest = FALSE;
  1461.       else
  1462.         {
  1463.   if((eLimit == RTX_LIMIT_ONE_PACKET && 
  1464.       spRtxDest->eRtxLimit == RTX_LIMIT_ONE_PACKET &&
  1465.       spRtxDest->iNumPacketsSent < 1 ) ||
  1466.      (spRtxDest->iOutstandingBytes < spRtxDest->iCwnd)) 
  1467.     {
  1468.       /* rtx will be sent to same dest as tx. Set eFoundRtxDest
  1469.        * to TRUE, to continue with the rtx procedure.
  1470.        */
  1471.       eFoundRtxDest = TRUE;
  1472.       DBG_PL(SelectRtxDest, "SelectedRtxDest: %p"), spRtxDest DBG_PR;
  1473.     }
  1474.   else 
  1475.     {
  1476.       DBG_PL(SelectRtxDest, "Unable to find rtx destination") DBG_PR;
  1477.     }
  1478. }
  1479.       break; // end of rtxtosame dest selection
  1480.     case RTX_SSTHRESH:
  1481.       /* NE: 4/29/2007
  1482.        * CMT rtx. policy - RTX_SSTHRESH: rtx. is sent to the destination for 
  1483.        * which the sender has the largest ssthresh. A tie is broken by random
  1484.        * selection. 
  1485.        */
  1486.       uiHighestSsthresh = 0;
  1487.       /* CMT-PF: If all destinations are marked PF, select one from them
  1488.        * for retransmissions
  1489.        */
  1490.       if (eUseCmtPF == TRUE)
  1491. {
  1492.   spRtxDest = SelectFromPFDests();
  1493.   if (spRtxDest != NULL)
  1494.     {
  1495.       /* spRtxDest was PF and changed to Active by SelectFromPFDests()
  1496.        */
  1497.       tiCountPFToActiveRtxms++ ; //trace will pick it up
  1498.       
  1499.       eFoundRtxDest = TRUE;
  1500.       
  1501.       break; // break for RTX_SSTHRESH case.
  1502.     }
  1503. }
  1504.       for(spCurrDestNode = sDestList.spHead;
  1505.   spCurrDestNode != NULL;
  1506.   spCurrDestNode = spCurrDestNode->spNext)
  1507. {
  1508.   spCurrDestNodeData = (SctpDest_S *) spCurrDestNode->vpData;
  1509.   DBG_PL(SelectRtxDest, "Dest: %p, Status: %d"), 
  1510.     spCurrDestNodeData, spCurrDestNodeData->eStatus DBG_PR;
  1511.   /* Do not retransmit to an INACTIVE or PF destination. 
  1512.    */
  1513.   if((spCurrDestNodeData->eStatus == SCTP_DEST_STATUS_INACTIVE) ||
  1514.      (spCurrDestNodeData->eStatus == SCTP_DEST_STATUS_POSSIBLY_FAILED))
  1515.     continue;
  1516.   DBG_PL(SelectRtxDest, "Dest: %p, Ssthresh: %d, Out: %d"), 
  1517. spCurrDestNodeData, spCurrDestNodeData->iSsthresh,
  1518. spCurrDestNodeData->iOutstandingBytes DBG_PR;
  1519.   if ((int)uiHighestSsthresh < spCurrDestNodeData->iSsthresh)
  1520.     {
  1521.       uiHighestSsthresh = spCurrDestNodeData->iSsthresh;
  1522.       spRtxDest = spCurrDestNodeData;
  1523.     }
  1524.   else if ((int)uiHighestSsthresh == spCurrDestNodeData->iSsthresh)
  1525.     {
  1526.       // break ties with random selection
  1527.       if(Random::random()&01)
  1528. spRtxDest = spCurrDestNodeData;
  1529.     }
  1530. }
  1531.       /* if no active dest found, then return null
  1532.        */
  1533.       if (spRtxDest == NULL) 
  1534. eFoundRtxDest = FALSE;
  1535.       else
  1536. {
  1537.   DBG_PL(SelectRtxDest, "Dest: %p"), spRtxDest  DBG_PR;
  1538.   /* Modified rtx mechanisms from SCTP. Allow ONE_PACKET_LIMIT
  1539.    * for dest which has suffered a loss, so that at least one dest
  1540.    * is allowed to retransmit one lost packet immediately. The rest
  1541.    * of the dests are bound by their resp. cwnds. Subsequent calls 
  1542.    * triggering sending rtxs will be bound by the resp. cwnds.
  1543.    */
  1544.   if((eLimit == RTX_LIMIT_ONE_PACKET && 
  1545.       spRtxDest->eRtxLimit == RTX_LIMIT_ONE_PACKET &&
  1546.       spRtxDest->iNumPacketsSent < 1 ) ||
  1547.      (spRtxDest->iOutstandingBytes < spRtxDest->iCwnd)) 
  1548.     {
  1549.       /* rtx will be sent to rtxDest. Set eFoundRtxDest
  1550.        * to TRUE, to continue with the rtx procedure.
  1551.        */
  1552.       eFoundRtxDest = TRUE;
  1553.       DBG_PL(SelectRtxDest, "SelectedRtxDest: %p"), spRtxDest DBG_PR;
  1554.     }
  1555. }
  1556.       break;
  1557.     case RTX_LOSSRATE:
  1558.       /* NE: 4/24/2007
  1559.        * CMT rtx. policy - RTX_LOSSRATE: rtx. is sent to the destination with
  1560.        * the lowest loss rate path. If multiple destinations have the same
  1561.        * loss rate, one is selected randomly.
  1562.        * IMPORTANT NOTICE: This rtx. policy is used for experimental purposes
  1563.        * ONLY and is retained for verifying previous experiments. Please use
  1564.        * one of the suggested rtx. policies for CMT and CMT-PF experiments: 
  1565.        * RTX_SSTHRESH or RTX_CWND.
  1566.        */
  1567.  
  1568.       fLowestLossrate = 10;
  1569.       for(spCurrDestNode = sDestList.spHead;
  1570.   spCurrDestNode != NULL;
  1571.   spCurrDestNode = spCurrDestNode->spNext)
  1572. {
  1573.   spCurrDestNodeData = (SctpDest_S *) spCurrDestNode->vpData;
  1574.   if (spCurrDestNodeData->eStatus == SCTP_DEST_STATUS_INACTIVE)
  1575.     continue;
  1576.   if (fLowestLossrate > spCurrDestNodeData->fLossrate)
  1577.     {
  1578.       fLowestLossrate = spCurrDestNodeData->fLossrate;
  1579.       spRtxDest = spCurrDestNodeData;
  1580.       
  1581.     }
  1582.   else if (fLowestLossrate == spCurrDestNodeData->fLossrate)
  1583.     {
  1584.       // break ties with random selection
  1585.       if(Random::random()&01)
  1586. spRtxDest = spCurrDestNodeData;
  1587.     }
  1588. }
  1589.       /* if no active dest found, then return null
  1590.        */
  1591.       if (spRtxDest == NULL) 
  1592. eFoundRtxDest = FALSE;
  1593.       else
  1594. {
  1595.   /* Modified rtx mechanisms from SCTP. Allow ONE_PACKET_LIMIT
  1596.    * for dest which has suffered a loss, so that at least one dest
  1597.    * is allowed to retransmit one lost packet immediately. The rest
  1598.    * of the dests are bound by their resp. cwnds. Subsequent calls 
  1599.    * triggering sending rtxs will be bound by the resp. cwnds.
  1600.    */
  1601.   if((eLimit == RTX_LIMIT_ONE_PACKET && 
  1602.       spRtxDest->eRtxLimit == RTX_LIMIT_ONE_PACKET &&
  1603.       spRtxDest->iNumPacketsSent < 1 ) ||
  1604.      (spRtxDest->iOutstandingBytes < spRtxDest->iCwnd)) 
  1605.     {
  1606.       /* rtx will be sent to rtxDest. Set eFoundRtxDest
  1607.        * to TRUE, to continue with the rtx procedure.
  1608.        */
  1609.       eFoundRtxDest = TRUE;
  1610.       DBG_PL(SelectRtxDest, "SelectedRtxDest: %p"), spRtxDest DBG_PR;
  1611.     }
  1612. }
  1613.       break;
  1614.     case RTX_CWND:
  1615.       /* NE: 4/29/2007
  1616.        * CMT rtx. policy - RTX_CWND: rtx. is sent to the destination for which
  1617.        * the sender has the largest cwnd. A tie is broken by random selection.
  1618.        */
  1619.       uiHighestCwnd = 0;
  1620.       /* CMT-PF: If all destinations are marked PF, select one from them
  1621.        * for retransmissions
  1622.        */
  1623.       if (eUseCmtPF == TRUE)
  1624. {
  1625.   spRtxDest = SelectFromPFDests();
  1626.   if (spRtxDest != NULL)
  1627.     {
  1628.       /* spRtxDest was PF and changed to Active by SelectFromPFDests()
  1629.        */
  1630.       tiCountPFToActiveRtxms++ ; //trace will pick it up
  1631.       
  1632.       eFoundRtxDest = TRUE;
  1633.       
  1634.       break; // break for RTX_CWND case.
  1635.     }
  1636.   
  1637. } /* if (eUseCmtPF == TRUE) */
  1638.       
  1639.       for(spCurrDestNode = sDestList.spHead;
  1640.   spCurrDestNode != NULL;
  1641.   spCurrDestNode = spCurrDestNode->spNext)
  1642. {
  1643.   spCurrDestNodeData = (SctpDest_S *) spCurrDestNode->vpData;
  1644.   
  1645.   DBG_PL(SelectRtxDest, "Dest: %p, Status: %d"), 
  1646.     spCurrDestNodeData, spCurrDestNodeData->eStatus DBG_PR;
  1647.   
  1648.   /* Do not retransmit to an INACTIVE or PF destination. 
  1649.    */
  1650.   if((spCurrDestNodeData->eStatus == SCTP_DEST_STATUS_INACTIVE) ||
  1651.      (spCurrDestNodeData->eStatus == SCTP_DEST_STATUS_POSSIBLY_FAILED))
  1652.     continue;
  1653.   DBG_PL(SelectRtxDest, "Dest: %p, Cwnd: %d, Out: %d"), 
  1654.     spCurrDestNodeData, spCurrDestNodeData->iCwnd,
  1655.     spCurrDestNodeData->iOutstandingBytes DBG_PR;
  1656.   
  1657.   if ((int)uiHighestCwnd < spCurrDestNodeData->iCwnd)
  1658.     {
  1659.       uiHighestCwnd = spCurrDestNodeData->iCwnd;
  1660.       spRtxDest = spCurrDestNodeData;
  1661.     }
  1662.   else if ((int)uiHighestCwnd == spCurrDestNodeData->iCwnd)
  1663.     {
  1664.       // break ties with random selection
  1665.       if(Random::random()&01)
  1666. spRtxDest = spCurrDestNodeData;
  1667.     }
  1668. }
  1669.       
  1670.       /* No Active destinations found, set eFoundRtxDest to FALSE
  1671.        * to return NULL 
  1672.        */
  1673.       if (spRtxDest == NULL) 
  1674. eFoundRtxDest = FALSE;
  1675.       else
  1676.         {
  1677.   
  1678.   DBG_PL(SelectRtxDest, "Dest: %p"), spRtxDest  DBG_PR;
  1679.   
  1680.          /* Modified rtx mechanisms from SCTP. Allow ONE_PACKET_LIMIT
  1681.            * for dest which has suffered a loss, so that at least one dest
  1682.            * is allowed to retransmit one lost packet immediately. The rest
  1683.            * of the dests are bound by their resp. cwnds. Subsequent calls 
  1684.            * triggering sending rtxs will be bound by the resp. cwnds.
  1685.            */
  1686.          if((eLimit == RTX_LIMIT_ONE_PACKET && 
  1687.       spRtxDest->eRtxLimit == RTX_LIMIT_ONE_PACKET &&
  1688.       spRtxDest->iNumPacketsSent < 1 ) ||
  1689.      (spRtxDest->iOutstandingBytes < spRtxDest->iCwnd)) 
  1690.     {
  1691.       /* rtx will be sent to rtxDest. Set eFoundRtxDest
  1692.        * to TRUE, to continue with the rtx procedure.
  1693.        */
  1694.       eFoundRtxDest = TRUE;
  1695.       DBG_PL(SelectRtxDest, "SelectedRtxDest: %p"), spRtxDest DBG_PR;
  1696.     }
  1697.         }
  1698.       break;
  1699.       
  1700.     }
  1701.   
  1702.   if(eFoundRtxDest == FALSE)
  1703.     spRtxDest = NULL;
  1704.   DBG_PL(SelectRtxDest, "Selected Dest: %p"), spRtxDest DBG_PR;
  1705.  
  1706.   DBG_X(SelectRtxDest);
  1707.   return spRtxDest;
  1708. }
  1709. /****** End CMT Change ******/
  1710. /* returns a boolean of whether a fast retransmit is necessary
  1711.  */
  1712. Boolean_E SctpCMTAgent::ProcessGapAckBlocks(u_char *ucpSackChunk,
  1713.  Boolean_E eNewCumAck)
  1714. {
  1715.   DBG_I(ProcessGapAckBlocks);
  1716.   Boolean_E eFastRtxNeeded = FALSE;
  1717.   u_int uiHighestTsnSacked = uiHighestTsnNewlyAcked;
  1718.   u_int uiStartTsn;
  1719.   u_int uiEndTsn;
  1720.   Node_S *spCurrNode = NULL;
  1721.   SctpSendBufferNode_S *spCurrNodeData = NULL;
  1722.   Node_S *spCurrDestNode = NULL;
  1723.   SctpDest_S *spCurrDestNodeData = NULL;
  1724.   SctpSackChunk_S *spSackChunk = (SctpSackChunk_S *) ucpSackChunk;
  1725.   u_short usNumGapAcksProcessed = 0;
  1726.   SctpGapAckBlock_S *spCurrGapAck 
  1727.     = (SctpGapAckBlock_S *) (ucpSackChunk + sizeof(SctpSackChunk_S));
  1728.   DBG_PL(ProcessGapAckBlocks,"CumAck=%d"), spSackChunk->uiCumAck DBG_PR;
  1729.   if(sSendBuffer.spHead == NULL) // do we have ANYTHING in the rtx buffer?
  1730.     {
  1731.       /* This COULD mean that this sack arrived late, and a previous one
  1732.        * already cum ack'd everything. ...so, what do we do? nothing??
  1733.        */
  1734.     }
  1735.   
  1736.   else // we do have chunks in the rtx buffer
  1737.     {
  1738.       /* make sure we clear all the spFirstOutstanding pointers before
  1739.        * using them!
  1740.        */
  1741.       for(spCurrDestNode = sDestList.spHead;
  1742.   spCurrDestNode != NULL;
  1743.   spCurrDestNode = spCurrDestNode->spNext)
  1744. {
  1745.   spCurrDestNodeData = (SctpDest_S *) spCurrDestNode->vpData;
  1746.   spCurrDestNodeData->spFirstOutstanding = NULL;
  1747.   
  1748.   /****** Begin CMT Change ******/
  1749.   /* Initialize uiHighestTsnInSackForDest = current cumack, since it 
  1750.    * has to be at least equal to the cumack for the destinations
  1751.    * which matter in the fast rtx process. uiHighestTsnInSackForDest 
  1752.    * is used for HTNA also.
  1753.    */
  1754.   spCurrDestNodeData->uiHighestTsnInSackForDest =spSackChunk->uiCumAck;
  1755.   /****** End CMT Change ******/
  1756. }
  1757.       for(spCurrNode = sSendBuffer.spHead;
  1758.   (spCurrNode != NULL) &&
  1759.     (usNumGapAcksProcessed != spSackChunk->usNumGapAckBlocks);
  1760.   spCurrNode = spCurrNode->spNext)
  1761. {
  1762.   spCurrNodeData = (SctpSendBufferNode_S *) spCurrNode->vpData;
  1763.  
  1764.   /****** Begin CMT Change ******/
  1765.   /* CMT CUC algo: If expected-pseudo(-rtx)-cum is to be found,
  1766.    * check if this tsn has been previously acked. If not, set
  1767.    * expected-pseudo(-rtx)-cum to this tsn. uiExpectedPseudoCum
  1768.    * tracks the PCum for new txs on a dest, and
  1769.    * uiExpectedRtxPseudoCum tracks the pcum for rtxs on the dest.
  1770.    * NOTE: This cwnd algo assumes that the sendbuf is sorted
  1771.    * according to TSN.
  1772.    */
  1773.   
  1774.   if ((spCurrNodeData->spDest->eFindExpectedPseudoCum == TRUE) &&
  1775.       (spCurrNodeData->eGapAcked == FALSE) &&
  1776.       (spCurrNodeData->iNumTxs == 1)) 
  1777.     {
  1778.       spCurrNodeData->spDest->uiExpectedPseudoCum = 
  1779. spCurrNodeData->spChunk->uiTsn;
  1780.       spCurrNodeData->spDest->eFindExpectedPseudoCum = FALSE;
  1781.     }
  1782.   if ((spCurrNodeData->spDest->eFindExpectedRtxPseudoCum == TRUE) &&
  1783.       (spCurrNodeData->eGapAcked == FALSE) &&
  1784.       (spCurrNodeData->iNumTxs > 1)) 
  1785.     {
  1786.       spCurrNodeData->spDest->uiExpectedRtxPseudoCum = 
  1787. spCurrNodeData->spChunk->uiTsn;
  1788.       spCurrNodeData->spDest->eFindExpectedRtxPseudoCum = FALSE;
  1789.     }
  1790.   /****** End CMT Change ******/
  1791.   /* is this chunk the first outstanding on its destination?
  1792.    */
  1793.   if(spCurrNodeData->spDest->spFirstOutstanding == NULL &&
  1794.      spCurrNodeData->eGapAcked == FALSE &&
  1795.      spCurrNodeData->eAdvancedAcked == FALSE)
  1796.     {
  1797.       /* yes, it is the first!
  1798.        */
  1799.       spCurrNodeData->spDest->spFirstOutstanding = spCurrNodeData;
  1800.     }
  1801.   DBG_PL(ProcessGapAckBlocks, "--> rtx list chunk begin") DBG_PR;
  1802.   DBG_PL(ProcessGapAckBlocks, "    TSN=%d"), 
  1803.     spCurrNodeData->spChunk->uiTsn 
  1804.     DBG_PR;
  1805.   DBG_PL(ProcessGapAckBlocks, "    %s=%s %s=%s"),
  1806.     "eGapAcked", 
  1807.     spCurrNodeData->eGapAcked ? "TRUE" : "FALSE",
  1808.     "eAddedToPartialBytesAcked",
  1809.     spCurrNodeData->eAddedToPartialBytesAcked ? "TRUE" : "FALSE" 
  1810.     DBG_PR;
  1811.   DBG_PL(ProcessGapAckBlocks, "    NumMissingReports=%d NumTxs=%d"),
  1812.     spCurrNodeData->iNumMissingReports, 
  1813.     spCurrNodeData->iNumTxs 
  1814.     DBG_PR;
  1815.   DBG_PL(ProcessGapAckBlocks, "<-- rtx list chunk end") DBG_PR;
  1816.   
  1817.   DBG_PL(ProcessGapAckBlocks, "GapAckBlock StartOffset=%d EndOffset=%d"),
  1818.     spCurrGapAck->usStartOffset, spCurrGapAck->usEndOffset DBG_PR;
  1819.   uiStartTsn = spSackChunk->uiCumAck + spCurrGapAck->usStartOffset;
  1820.   uiEndTsn = spSackChunk->uiCumAck + spCurrGapAck->usEndOffset;
  1821.   
  1822.   DBG_PL(ProcessGapAckBlocks, "GapAckBlock StartTsn=%d EndTsn=%d"),
  1823.     uiStartTsn, uiEndTsn DBG_PR;
  1824.   if(spCurrNodeData->spChunk->uiTsn < uiStartTsn)
  1825.     {
  1826.       /* This chunk is NOT being acked and is missing at the receiver
  1827.        */
  1828.       /* If this chunk was GapAcked before, then either the
  1829.        * receiver has renegged the chunk (which our simulation
  1830.        * doesn't do) or this SACK is arriving out of order.
  1831.        */
  1832.       if(spCurrNodeData->eGapAcked == TRUE)
  1833. {
  1834.   DBG_PL(ProcessGapAckBlocks, 
  1835.  "out of order SACK? setting TSN=%d eGapAcked=FALSE"),
  1836.     spCurrNodeData->spChunk->uiTsn DBG_PR;
  1837.   spCurrNodeData->eGapAcked = FALSE;
  1838.   spCurrNodeData->spDest->iOutstandingBytes 
  1839.     += spCurrNodeData->spChunk->sHdr.usLength;
  1840.   /* section 6.3.2.R4 says that we should restart the
  1841.    * T3-rtx timer here if it isn't running already. In our
  1842.    * implementation, it isn't necessary since
  1843.    * ProcessSackChunk will restart the timer for any
  1844.    * destinations which have outstanding data and don't
  1845.    * have a timer running.
  1846.    */
  1847. }
  1848.     }
  1849.   else if((uiStartTsn <= spCurrNodeData->spChunk->uiTsn) && 
  1850.   (spCurrNodeData->spChunk->uiTsn <= uiEndTsn) )
  1851.     {
  1852.       /* This chunk is being acked via a gap ack block
  1853.        */
  1854.       DBG_PL(ProcessGapAckBlocks, "gap ack acks this chunk: %s%s"),
  1855. "eGapAcked=",
  1856. spCurrNodeData->eGapAcked ? "TRUE" : "FALSE" 
  1857. DBG_PR;
  1858.       /* HTNA algorithm... we need to know the highest TSN
  1859.        * sacked (even if it isn't new), so that when the sender
  1860.        * is in Fast Recovery, the outstanding tsns beyond the 
  1861.        * last sack tsn do not have their missing reports incremented
  1862.        */
  1863.       if(uiHighestTsnSacked < spCurrNodeData->spChunk->uiTsn)
  1864. uiHighestTsnSacked = spCurrNodeData->spChunk->uiTsn;
  1865.       if(spCurrNodeData->eGapAcked == FALSE)
  1866. {
  1867.   DBG_PL(ProcessGapAckBlocks, "setting eGapAcked=TRUE") DBG_PR;