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

通讯编程

开发平台:

Visual C++

  1.     {
  2.       DBG_PL(UpdateHighestTsn, "returning FALSE") DBG_PR;
  3.       DBG_X(UpdateHighestTsn);
  4.       return FALSE; /* no update of highest */ 
  5.     }
  6. }
  7. /* Determines whether a chunk is duplicate or not. 
  8.  */
  9. Boolean_E SctpAgent::IsDuplicateChunk(u_int uiTsn)
  10. {
  11.   DBG_I(IsDuplicateChunk);
  12.   Node_S *spCurrNode = NULL;
  13.   /* Assume highest have already been updated 
  14.    */
  15.   if(uiTsn <= uiCumAck)
  16.     {
  17.       DBG_PL(IsDuplicateChunk, "returning TRUE") DBG_PR;
  18.       DBG_X(IsDuplicateChunk);
  19.       return TRUE;
  20.     }
  21.   if( !((uiCumAck < uiTsn) && (uiTsn <= uiHighestRecvTsn)) )
  22.     {
  23.       DBG_PL(IsDuplicateChunk, "returning FALSE") DBG_PR;
  24.       DBG_X(IsDuplicateChunk);
  25.       return FALSE;
  26.     }
  27.   /* Let's see if uiTsn is in the sorted list of recv'd tsns
  28.    */
  29.   if(sRecvTsnBlockList.uiLength == 0)
  30.     {
  31.       DBG_PL(IsDuplicateChunk, "returning FALSE") DBG_PR;
  32.       DBG_X(IsDuplicateChunk);
  33.       return FALSE;           /* no frags in list! */
  34.     }
  35.   /* If we get this far, we need to check whether uiTsn is already in the list
  36.    * of received tsns. Simply do a linear search thru the sRecvTsnBlockList.
  37.    */
  38.   for(spCurrNode = sRecvTsnBlockList.spHead; 
  39.       spCurrNode != NULL; 
  40.       spCurrNode = spCurrNode->spNext)
  41.     {
  42.       if(((SctpRecvTsnBlock_S *)spCurrNode->vpData)->uiStartTsn <= uiTsn &&
  43.  uiTsn <= ((SctpRecvTsnBlock_S *)spCurrNode->vpData)->uiEndTsn )
  44. {
  45.   DBG_PL(IsDuplicateChunk, "returning TRUE") DBG_PR;
  46.   DBG_X(IsDuplicateChunk);
  47.   return TRUE;     
  48. }
  49.       /* Assuming an ordered list of tsn blocks, don't continue looking if this
  50.        * block ends with a larger tsn than the chunk we currently have.
  51.        */
  52.       if( ((SctpRecvTsnBlock_S *)spCurrNode->vpData)->uiEndTsn > uiTsn )
  53. {
  54.   DBG_PL(IsDuplicateChunk, "returning FALSE") DBG_PR;
  55.   DBG_X(IsDuplicateChunk);
  56.   return FALSE;
  57. }
  58.     }
  59.   DBG_PL(IsDuplicateChunk, "returning FALSE") DBG_PR;
  60.   DBG_X(IsDuplicateChunk);
  61.   return FALSE;
  62. }
  63. /* Inserts uiTsn into the list of duplicates tsns
  64.  */
  65. void SctpAgent::InsertDuplicateTsn(u_int uiTsn)
  66. {
  67.   DBG_I(InsertDuplicateTsn);
  68.   Node_S *spCurrNode = NULL;
  69.   Node_S *spPrevNode = NULL;
  70.   Node_S *spNewNode = NULL;
  71.   u_int uiCurrTsn;
  72.   /* linear search
  73.    */
  74.   for(spPrevNode = NULL, spCurrNode = sDupTsnList.spHead; 
  75.       spCurrNode != NULL;
  76.       spPrevNode = spCurrNode, spCurrNode = spCurrNode->spNext)
  77.     {
  78.       uiCurrTsn = ((SctpDupTsn_S *) spCurrNode->vpData)->uiTsn;
  79.       if(uiTsn <= uiCurrTsn)
  80. break;
  81.     }
  82.   /* If we reached the end of the list 
  83.    * OR we found the location in the list where it should go (assuming it 
  84.    * isn't already there)... insert it.
  85.    */
  86.   if( (spCurrNode == NULL) || (uiTsn != uiCurrTsn) )
  87.     {
  88.       spNewNode = new Node_S;
  89.       spNewNode->eType = NODE_TYPE_DUP_TSN;
  90.       spNewNode->vpData = new SctpDupTsn_S;
  91.       ((SctpDupTsn_S *) spNewNode->vpData)->uiTsn = uiTsn;
  92.       InsertNode(&sDupTsnList, spPrevNode, spNewNode, spCurrNode);
  93.     }
  94.   DBG_X(InsertDuplicateTsn);
  95. }
  96. /* This function updates uiCumAck (a receive variable) to reflect newly arrived
  97.  * data.
  98.  */
  99. void SctpAgent::UpdateCumAck()
  100. {
  101.   DBG_I(UpdateCumAck);
  102.   Node_S *spCurrNode = NULL;
  103.   if(sRecvTsnBlockList.uiLength == 0)
  104.     {
  105.       DBG_X(UpdateCumAck);
  106.       return;
  107.     }
  108.   for(spCurrNode = sRecvTsnBlockList.spHead; 
  109.       spCurrNode != NULL; 
  110.       spCurrNode = spCurrNode->spNext)
  111.     {
  112.       if( uiCumAck+1 == ((SctpRecvTsnBlock_S *)spCurrNode->vpData)->uiStartTsn)
  113. {
  114.   uiCumAck = ((SctpRecvTsnBlock_S *)spCurrNode->vpData)->uiEndTsn;
  115. }
  116.       else
  117.         {
  118.           DBG_X(UpdateCumAck);
  119.           return;
  120.         }
  121.     }
  122.   DBG_X(UpdateCumAck);
  123. }
  124. /**
  125.  * Helper function to do the correct update the received tsn blocks.
  126.  * This function will also call UpdateCumAck() when necessary.
  127.  */
  128. void SctpAgent::UpdateRecvTsnBlocks(u_int uiTsn)
  129. {
  130.   DBG_I(UpdateRecvTsnBlocks);
  131.   u_int uiLow;
  132.   u_int uiHigh;
  133.   u_int uiGapSize;
  134.   Node_S *spCurrNode = NULL;
  135.   Node_S *spPrevNode = NULL;
  136.   Node_S *spNewNode = NULL;
  137.   uiLow = uiCumAck + 1;
  138.   for(spCurrNode = sRecvTsnBlockList.spHead; 
  139.       spCurrNode != NULL; 
  140.       spPrevNode = spCurrNode, spCurrNode = spCurrNode->spNext)
  141.     {
  142.       uiHigh = ((SctpRecvTsnBlock_S *)spCurrNode->vpData)->uiStartTsn - 1;
  143.       
  144.       /* Does uiTsn fall in the gap?
  145.        */
  146.       if( uiLow <= uiTsn && uiTsn <= uiHigh )
  147. {
  148.   uiGapSize = uiHigh - uiLow + 1;
  149.   if(uiGapSize > 1) // is the gap too big for one uiTsn to fill?
  150.     {
  151.       /* Does uiTsn border the lower edge of the current tsn block?
  152.        */
  153.       if(uiTsn == uiHigh) 
  154. {
  155.   ((SctpRecvTsnBlock_S *)spCurrNode->vpData)->uiStartTsn 
  156.     = uiTsn;
  157.   UpdateCumAck();
  158.   DBG_X(UpdateRecvTsnBlocks);
  159.   return;
  160.       /* Does uiTsn border the left edge of the previous tsn block?
  161.        */
  162.       else if(uiTsn == uiLow)
  163. {
  164.   if(uiTsn == uiCumAck+1) // can we increment our uiCumAck?
  165.     {
  166.       uiCumAck++;
  167.       UpdateCumAck();
  168.       DBG_X(UpdateRecvTsnBlocks);
  169.       return;
  170.     }
  171.   else // otherwise, move previous tsn block's right edge
  172.     {
  173.       ((SctpRecvTsnBlock_S *)spPrevNode->vpData)->uiEndTsn 
  174. = uiTsn;
  175.       UpdateCumAck();
  176.       DBG_X(UpdateRecvTsnBlocks);
  177.       return;
  178.     }
  179. }
  180.       /* This uiTsn creates a new tsn block in between uiLow & uiHigh
  181.        */
  182.       else 
  183. {   
  184.   spNewNode = new Node_S;
  185.   spNewNode->eType = NODE_TYPE_RECV_TSN_BLOCK;
  186.   spNewNode->vpData = new SctpRecvTsnBlock_S;
  187.   ((SctpRecvTsnBlock_S *)spNewNode->vpData)->uiStartTsn 
  188.     = uiTsn;
  189.   ((SctpRecvTsnBlock_S *)spNewNode->vpData)->uiEndTsn = uiTsn;
  190.   InsertNode(&sRecvTsnBlockList, 
  191.      spPrevNode, spNewNode, spCurrNode);
  192.   DBG_X(UpdateRecvTsnBlocks);
  193.   return; // no UpdateCumAck() necessary
  194. }
  195.     } 
  196.   else // uiGapSize == 1
  197.     {
  198.       if(uiLow == uiCumAck+1)  // can we adjust our uiCumAck?
  199. {
  200.   /* delete tsn block
  201.    */
  202.   uiCumAck 
  203.     = ((SctpRecvTsnBlock_S *)spCurrNode->vpData)->uiEndTsn;
  204.   DeleteNode(&sRecvTsnBlockList, spCurrNode);
  205.   spCurrNode = NULL;
  206.   UpdateCumAck();
  207.   DBG_X(UpdateRecvTsnBlocks);
  208.   return;
  209.       else  // otherwise, move previous tsn block's right edge
  210. {
  211.   ((SctpRecvTsnBlock_S *)spPrevNode->vpData)->uiEndTsn
  212.     = ((SctpRecvTsnBlock_S *)spCurrNode->vpData)->uiEndTsn;
  213.   DeleteNode(&sRecvTsnBlockList, spCurrNode);
  214.   spCurrNode = NULL;
  215.   UpdateCumAck();
  216.   DBG_X(UpdateRecvTsnBlocks);
  217.   return;
  218. }
  219.     }
  220.       /* uiTsn is not in the gap between these two tsn blocks, so let's move 
  221.        * our "low pointer" to one past the end of the current tsn block and 
  222.        * continue
  223.        */
  224.       else 
  225. {         
  226.   uiLow =  ((SctpRecvTsnBlock_S *)spCurrNode->vpData)->uiEndTsn + 1;
  227. }
  228.     }
  229.   /* If we get here, then the list is either NULL or the end of the list has
  230.    * been reached 
  231.    */
  232.   if(uiTsn == uiLow) 
  233.     {
  234.       if(uiTsn == uiCumAck+1) // Can we increment the uiCumAck?
  235. {
  236.   uiCumAck = uiTsn;
  237.   UpdateCumAck();
  238.   DBG_X(UpdateRecvTsnBlocks);
  239.   return;
  240.         }
  241.       
  242.       /* Update previous tsn block by increasing it's uiEndTsn
  243.        */
  244.       if(spPrevNode != NULL)
  245. {
  246.   ((SctpRecvTsnBlock_S *) spPrevNode->vpData)->uiEndTsn++;   
  247. }
  248.       DBG_X(UpdateRecvTsnBlocks);
  249.       return; // no UpdateCumAck() necessary
  250.     } 
  251.   /* uiTsn creates a new tsn block to go at the end of the sRecvTsnBlockList
  252.    */
  253.   else 
  254.     {
  255.       spNewNode = new Node_S;
  256.       spNewNode->eType = NODE_TYPE_RECV_TSN_BLOCK;
  257.       spNewNode->vpData = new SctpRecvTsnBlock_S;
  258.       ((SctpRecvTsnBlock_S *) spNewNode->vpData)->uiStartTsn = uiTsn;
  259.       ((SctpRecvTsnBlock_S *) spNewNode->vpData)->uiEndTsn = uiTsn;
  260.       InsertNode(&sRecvTsnBlockList, spPrevNode, spNewNode, spCurrNode);
  261.       DBG_X(UpdateRecvTsnBlocks);
  262.       return; // no UpdateCumAck() necessary
  263.     }
  264. }
  265. /* This function is merely a hook for future implementation and currently does
  266.  * NOT actually pass the data to the upper layer.
  267.  */
  268. void SctpAgent::PassToUpperLayer(SctpDataChunkHdr_S *spDataChunkHdr)
  269. {
  270.   DBG_I(PassToUpperLayer);
  271.   DBG_PL(PassToUpperLayer, "tsn=%d"), spDataChunkHdr->uiTsn DBG_PR;
  272.   /* We really don't want to credit the window until the upper layer actually
  273.    * wants the data, but now we'll assume that the application readily
  274.    * consumes incoming chunks immediately.  
  275.    */
  276.   uiMyRwnd += spDataChunkHdr->sHdr.usLength; 
  277.   tiRwnd++; // trigger changes to be traced
  278.   DBG_PL(PassToUpperLayer, "uiMyRwnd=%d"), uiMyRwnd DBG_PR;
  279.   DBG_X(PassToUpperLayer);
  280. }
  281. void SctpAgent::InsertInStreamBuffer(List_S *spBufferedChunkList,
  282.      SctpDataChunkHdr_S *spChunk)
  283. {
  284.   DBG_I(InsertInStreamBuffer);
  285.   Node_S *spPrevNode;
  286.   Node_S *spCurrNode;
  287.   Node_S *spNewNode;
  288.   SctpStreamBufferNode_S *spCurrNodeData;
  289.   SctpStreamBufferNode_S *spNewNodeData;
  290.   u_short usCurrStreamSeqNum;
  291.   spPrevNode = NULL;
  292.   spCurrNode = spBufferedChunkList->spHead;
  293.   /* linear search through stream sequence #'s 
  294.    */ 
  295.   for(spPrevNode = NULL, spCurrNode = spBufferedChunkList->spHead;
  296.       spCurrNode != NULL;
  297.       spPrevNode = spCurrNode, spCurrNode = spCurrNode->spNext)
  298.     {
  299.       spCurrNodeData = (SctpStreamBufferNode_S *) spCurrNode->vpData;
  300.       usCurrStreamSeqNum = spCurrNodeData->spChunk->usStreamSeqNum;
  301.       /* break out of loop when we find the place to insert our new chunk
  302.        */
  303.       if(spChunk->usStreamSeqNum <= usCurrStreamSeqNum)
  304. break;
  305.     }
  306.   
  307.   /* If we reached the end of the list OR we found the location in the list
  308.    * where it should go (assuming it isn't already there)... insert it.  
  309.    */
  310.   if( (spCurrNode == NULL) || (usCurrStreamSeqNum != spChunk->usStreamSeqNum) )
  311.     {
  312.       spNewNode = new Node_S;
  313.       spNewNode->eType = NODE_TYPE_STREAM_BUFFER;
  314.       spNewNode->vpData = new SctpStreamBufferNode_S;
  315.       spNewNodeData = (SctpStreamBufferNode_S *) spNewNode->vpData;
  316.       /* This can NOT simply be a 'new SctpDataChunkHdr_S', because we
  317.        * need to allocate the space for the ENTIRE data chunk and not just
  318.        * the data chunk header.  
  319.        */
  320.       spNewNodeData->spChunk 
  321. = (SctpDataChunkHdr_S *) new u_char[spChunk->sHdr.usLength];
  322.       memcpy(spNewNodeData->spChunk, spChunk, spChunk->sHdr.usLength);
  323.       DBG_PL(InsertInStreamBuffer, "vpData=%p spChunk=%p"), 
  324. spNewNodeData, spNewNodeData->spChunk DBG_PR;
  325.       InsertNode(spBufferedChunkList, spPrevNode, spNewNode, spCurrNode);
  326.     }
  327.   DBG_X(InsertInStreamBuffer);
  328. }
  329. /* We have not implemented fragmentation, so this function safely assumes all
  330.  * chunks are complete. 
  331.  */
  332. void SctpAgent::PassToStream(SctpDataChunkHdr_S *spChunk)
  333. {
  334.   DBG_I(PassToStream);
  335.   DBG_PL(PassToStream, "uiMyRwnd=%d"), uiMyRwnd DBG_PR;
  336.   SctpInStream_S *spStream = &(spInStreams[spChunk->usStreamId]);
  337.   if(spChunk->sHdr.ucFlags & SCTP_DATA_FLAG_UNORDERED)
  338.     {
  339.       PassToUpperLayer(spChunk);
  340.     } 
  341.   else 
  342.     {
  343.       /* We got a numbered chunk (ordered delivery)...
  344.        *
  345.        * We insert the chunk into the corresponding buffer whether or not the 
  346.        * chunk's stream seq # is in order or not.
  347.        */
  348.       DBG_PL(PassToStream, "streamId=%d streamSeqNum=%d"),
  349. spChunk->usStreamId, spChunk->usStreamSeqNum DBG_PR;
  350.       InsertInStreamBuffer( &(spStream->sBufferedChunkList), spChunk);
  351.     }
  352.   DBG_PL(PassToStream, "uiMyRwnd=%d"), uiMyRwnd DBG_PR;
  353.   DBG_X(PassToStream);
  354. }  
  355. void SctpAgent::UpdateAllStreams()
  356. {
  357.   DBG_I(UpdateAllStreams);
  358.   DBG_PL(UpdateAllStreams, "uiMyRwnd=%d"), uiMyRwnd DBG_PR;
  359.   Node_S *spCurrNode = NULL;
  360.   Node_S *spDeleteNode = NULL;
  361.   SctpDataChunkHdr_S *spBufferedChunk = NULL;
  362.   SctpInStream_S *spStream = NULL;
  363.   int i;
  364.   for(i = 0; i < iNumInStreams; i++)
  365.     {
  366.       DBG_PL(UpdateAllStreams, "examining stream %d"), i DBG_PR;
  367.       spStream = &(spInStreams[i]);
  368.       /* Start from the lowest stream seq # buffered and sequentially pass
  369.        * up all the chunks which are "deliverable".  
  370.        */
  371.       spCurrNode = spStream->sBufferedChunkList.spHead;
  372.       while(spCurrNode != NULL)
  373. {
  374.   spBufferedChunk 
  375.     = ((SctpStreamBufferNode_S *)spCurrNode->vpData)->spChunk;
  376.   /* For unreliable streams... Any waiting tsn which is less than
  377.    * or equal to the cum ack, must be delivered now. We first
  378.    * deliver chunks without even considering the SSNs, because in
  379.    * case of unreliable streams, there may be gaps in the SSNs
  380.    * which we want to ignore.
  381.    */
  382.   if((spStream->eMode == SCTP_STREAM_UNRELIABLE) &&
  383.      (spBufferedChunk->uiTsn <= uiCumAck) )
  384.     {
  385.       spStream->usNextStreamSeqNum = spBufferedChunk->usStreamSeqNum+1;
  386.       PassToUpperLayer(spBufferedChunk);
  387.       spDeleteNode = spCurrNode;
  388.       spCurrNode = spCurrNode->spNext;
  389.       DeleteNode( &(spStream->sBufferedChunkList), spDeleteNode );
  390.       spDeleteNode = NULL;
  391.     }
  392.   
  393.   /* Let's see if we can deliver anything else based on the SSNs.
  394.    */
  395.   else if( spBufferedChunk->usStreamSeqNum == 
  396.    spStream->usNextStreamSeqNum )
  397.     {
  398.       spStream->usNextStreamSeqNum++;
  399.       PassToUpperLayer(spBufferedChunk);
  400.       spDeleteNode = spCurrNode;
  401.       spCurrNode = spCurrNode->spNext;
  402.       DeleteNode( &(spStream->sBufferedChunkList), spDeleteNode );
  403.       spDeleteNode = NULL;
  404.     }
  405.   
  406.   /* ok, we have delivered all that we can!
  407.    */
  408.   else
  409.     break;
  410. }
  411.     }
  412.   DBG_PL(UpdateAllStreams, "uiMyRwnd=%d"), uiMyRwnd DBG_PR;
  413.   DBG_X(UpdateAllStreams);
  414. void SctpAgent::ProcessInitChunk(u_char *ucpInitChunk)
  415. {
  416.   DBG_I(ProcessInitChunk);
  417.   SctpInitChunk_S *spInitChunk = (SctpInitChunk_S *) ucpInitChunk;
  418.   SctpUnrelStreamPair_S *spUnrelStreamPair 
  419.     = (SctpUnrelStreamPair_S *) (ucpInitChunk + sizeof(SctpInitChunk_S));
  420.   int i = 0;
  421.   uiPeerRwnd = spInitChunk->uiArwnd;
  422.   iNumInStreams = spInitChunk->usNumOutboundStreams;
  423.   spInStreams = new SctpInStream_S[iNumInStreams];
  424.   memset(spInStreams, 0, (iNumInStreams * sizeof(SctpInStream_S)) );
  425.   if(spInitChunk->sHdr.usLength > sizeof(SctpInitChunk_S) )
  426.   {
  427.     for(i = spUnrelStreamPair->usStart; i <= spUnrelStreamPair->usEnd; i++)
  428.       {
  429. DBG_PL(ProcessInitChunk, "setting inStream %d to UNRELIABLE"),i DBG_PR;
  430. spInStreams[i].eMode = SCTP_STREAM_UNRELIABLE;
  431.       }
  432.   }
  433.   
  434.   for(; i < iNumInStreams; i++)
  435.     {
  436.       DBG_PL(ProcessInitChunk, "setting inStream %d to RELIABLE"), i DBG_PR;
  437.       spInStreams[i].eMode = SCTP_STREAM_RELIABLE;
  438.     }
  439.   
  440.   DBG_X(ProcessInitChunk);
  441. }
  442. void SctpAgent::ProcessInitAckChunk(u_char *ucpInitAckChunk)
  443. {
  444.   DBG_I(ProcessInitAckChunk);
  445.   SctpInitAckChunk_S *spInitAckChunk = (SctpInitAckChunk_S *) ucpInitAckChunk;
  446.   SctpUnrelStreamPair_S *spUnrelStreamPair 
  447.     = (SctpUnrelStreamPair_S *)(ucpInitAckChunk + sizeof(SctpInitAckChunk_S) );
  448.   int i = 0;
  449.   opT1InitTimer->force_cancel();
  450.   uiPeerRwnd = spInitAckChunk->uiArwnd;
  451.   tiRwnd++; // trigger changes to be traced
  452.   iNumInStreams = spInitAckChunk->usNumOutboundStreams;
  453.   spInStreams = new SctpInStream_S[iNumInStreams];
  454.   memset(spInStreams, 0, (iNumInStreams * sizeof(SctpInStream_S)) );
  455.   if(spInitAckChunk->sHdr.usLength > sizeof(SctpInitAckChunk_S) )
  456.   {
  457.     for(i = spUnrelStreamPair->usStart; i <= spUnrelStreamPair->usEnd; i++)
  458.       {
  459. DBG_PL(ProcessInitAckChunk, "setting inStream %d to UNRELIABLE"), 
  460.   i DBG_PR;
  461. spInStreams[i].eMode = SCTP_STREAM_UNRELIABLE;
  462.       }
  463.   }
  464.   
  465.   for(; i < iNumInStreams; i++)
  466.     {
  467.       DBG_PL(ProcessInitAckChunk, "setting inStream %d to RELIABLE"), i DBG_PR;
  468.       spInStreams[i].eMode = SCTP_STREAM_RELIABLE;
  469.     }
  470.   
  471.   DBG_X(ProcessInitAckChunk);
  472. }
  473. void SctpAgent::ProcessCookieEchoChunk(SctpCookieEchoChunk_S *spCookieEchoChunk)
  474. {
  475.   /* dummy empty function left as a hook for cookie echo processing */
  476. }
  477. void SctpAgent::ProcessCookieAckChunk(SctpCookieAckChunk_S *spCookieAckChunk)
  478. {
  479.   opT1CookieTimer->force_cancel();
  480. }
  481. /* This function treats only one incoming data chunk at a time.
  482.  */
  483. void SctpAgent::ProcessDataChunk(SctpDataChunkHdr_S *spChunk)
  484. {
  485.   DBG_I(ProcessDataChunk);
  486.   /* Is there still room in my receiver window?? We can only process the DATA
  487.    * chunk if there is. Otherwise, we drop it!
  488.    */
  489.   if(spChunk->sHdr.usLength <= uiMyRwnd) 
  490.     {
  491.       if((UpdateHighestTsn(spChunk->uiTsn) != TRUE) &&
  492.  (IsDuplicateChunk(spChunk->uiTsn) == TRUE) )
  493. {
  494.   InsertDuplicateTsn(spChunk->uiTsn);
  495.   eSackChunkNeeded = TRUE; // section 6.7 - send sack immediately!
  496.   DBG_PL(ProcessDataChunk, "duplicate tsn=%d"), spChunk->uiTsn DBG_PR;
  497. }
  498.       else 
  499. {
  500.   /* Received a new chunk...  Reduce receiver window until application
  501.    * consumes the incoming chunk 
  502.    */
  503.   uiMyRwnd -= spChunk->sHdr.usLength;
  504.   tiRwnd++; // trigger rwnd changes to be traced
  505.   UpdateRecvTsnBlocks(spChunk->uiTsn);
  506.   PassToStream(spChunk);
  507.   UpdateAllStreams();
  508.   DBG_PL(ProcessDataChunk, "uiMyRwnd=%d"), uiMyRwnd DBG_PR;
  509. }
  510.     }
  511.   else
  512.     {
  513.       /* Do not generate a SACK if we are dropping the chunk!!
  514.        */
  515.       eSackChunkNeeded = FALSE; 
  516.       DBG_PL(ProcessDataChunk, "rwnd full... dropping tsn=%d"), 
  517. spChunk->uiTsn DBG_PR;
  518.     }
  519.   
  520.   DBG_X(ProcessDataChunk);
  521. }
  522. /* returns a boolean of whether a fast retransmit is necessary
  523.  */
  524. Boolean_E SctpAgent::ProcessGapAckBlocks(u_char *ucpSackChunk,
  525.  Boolean_E eNewCumAck)
  526. {
  527.   DBG_I(ProcessGapAckBlocks);
  528.   Boolean_E eFastRtxNeeded = FALSE;
  529.   u_int uiHighestTsnSacked = uiHighestTsnNewlyAcked;
  530.   u_int uiStartTsn;
  531.   u_int uiEndTsn;
  532.   Node_S *spCurrNode = NULL;
  533.   SctpSendBufferNode_S *spCurrNodeData = NULL;
  534.   Node_S *spCurrDestNode = NULL;
  535.   SctpDest_S *spCurrDestNodeData = NULL;
  536.   SctpSackChunk_S *spSackChunk = (SctpSackChunk_S *) ucpSackChunk;
  537.   u_short usNumGapAcksProcessed = 0;
  538.   SctpGapAckBlock_S *spCurrGapAck 
  539.     = (SctpGapAckBlock_S *) (ucpSackChunk + sizeof(SctpSackChunk_S));
  540.   DBG_PL(ProcessGapAckBlocks,"CumAck=%d"), spSackChunk->uiCumAck DBG_PR;
  541.   if(sSendBuffer.spHead == NULL) // do we have ANYTHING in the rtx buffer?
  542.     {
  543.       /* This COULD mean that this sack arrived late, and a previous one
  544.        * already cum ack'd everything. ...so, what do we do? nothing??
  545.        */
  546.     }
  547.   
  548.   else // we do have chunks in the rtx buffer
  549.     {
  550.       /* make sure we clear all the spFirstOutstanding pointers before
  551.        * using them!
  552.        */
  553.       for(spCurrDestNode = sDestList.spHead;
  554.   spCurrDestNode != NULL;
  555.   spCurrDestNode = spCurrDestNode->spNext)
  556. {
  557.   spCurrDestNodeData = (SctpDest_S *) spCurrDestNode->vpData;
  558.   spCurrDestNodeData->spFirstOutstanding = NULL;
  559. }
  560.       for(spCurrNode = sSendBuffer.spHead;
  561.   (spCurrNode != NULL) &&
  562.     (usNumGapAcksProcessed != spSackChunk->usNumGapAckBlocks);
  563.   spCurrNode = spCurrNode->spNext)
  564. {
  565.   spCurrNodeData = (SctpSendBufferNode_S *) spCurrNode->vpData;
  566.   /* is this chunk the first outstanding on its destination?
  567.    */
  568.   if(spCurrNodeData->spDest->spFirstOutstanding == NULL &&
  569.      spCurrNodeData->eGapAcked == FALSE &&
  570.      spCurrNodeData->eAdvancedAcked == FALSE)
  571.     {
  572.       /* yes, it is the first!
  573.        */
  574.       spCurrNodeData->spDest->spFirstOutstanding = spCurrNodeData;
  575.     }
  576.   DBG_PL(ProcessGapAckBlocks, "--> rtx list chunk begin") DBG_PR;
  577.   DBG_PL(ProcessGapAckBlocks, "    TSN=%d"), 
  578.     spCurrNodeData->spChunk->uiTsn 
  579.     DBG_PR;
  580.   DBG_PL(ProcessGapAckBlocks, "    %s=%s %s=%s"),
  581.     "eGapAcked", 
  582.     spCurrNodeData->eGapAcked ? "TRUE" : "FALSE",
  583.     "eAddedToPartialBytesAcked",
  584.     spCurrNodeData->eAddedToPartialBytesAcked ? "TRUE" : "FALSE" 
  585.     DBG_PR;
  586.   DBG_PL(ProcessGapAckBlocks, "    NumMissingReports=%d NumTxs=%d"),
  587.     spCurrNodeData->iNumMissingReports, 
  588.     spCurrNodeData->iNumTxs 
  589.     DBG_PR;
  590.   DBG_PL(ProcessGapAckBlocks, "<-- rtx list chunk end") DBG_PR;
  591.   
  592.   DBG_PL(ProcessGapAckBlocks,"GapAckBlock StartOffset=%d EndOffset=%d"),
  593.     spCurrGapAck->usStartOffset, spCurrGapAck->usEndOffset DBG_PR;
  594.   uiStartTsn = spSackChunk->uiCumAck + spCurrGapAck->usStartOffset;
  595.   uiEndTsn = spSackChunk->uiCumAck + spCurrGapAck->usEndOffset;
  596.   
  597.   DBG_PL(ProcessGapAckBlocks, "GapAckBlock StartTsn=%d EndTsn=%d"),
  598.     uiStartTsn, uiEndTsn DBG_PR;
  599.   if(spCurrNodeData->spChunk->uiTsn < uiStartTsn)
  600.     {
  601.       /* This chunk is NOT being acked and is missing at the receiver
  602.        */
  603.       /* If this chunk was GapAcked before, then either the
  604.        * receiver has renegged the chunk (which our simulation
  605.        * doesn't do) or this SACK is arriving out of order.
  606.        */
  607.       if(spCurrNodeData->eGapAcked == TRUE)
  608. {
  609.   DBG_PL(ProcessGapAckBlocks, 
  610.  "out of order SACK? setting TSN=%d eGapAcked=FALSE"),
  611.     spCurrNodeData->spChunk->uiTsn DBG_PR;
  612.   spCurrNodeData->eGapAcked = FALSE;
  613.   spCurrNodeData->spDest->iOutstandingBytes 
  614.     += spCurrNodeData->spChunk->sHdr.usLength;
  615.   /* section 6.3.2.R4 says that we should restart the
  616.    * T3-rtx timer here if it isn't running already. In our
  617.    * implementation, it isn't necessary since
  618.    * ProcessSackChunk will restart the timer for any
  619.    * destinations which have outstanding data and don't
  620.    * have a timer running.
  621.    */
  622. }
  623.     }
  624.   else if((uiStartTsn <= spCurrNodeData->spChunk->uiTsn) && 
  625.   (spCurrNodeData->spChunk->uiTsn <= uiEndTsn) )
  626.     {
  627.       /* This chunk is being acked via a gap ack block
  628.        */
  629.       DBG_PL(ProcessGapAckBlocks, "gap ack acks this chunk: %s%s"),
  630. "eGapAcked=",
  631. spCurrNodeData->eGapAcked ? "TRUE" : "FALSE" 
  632. DBG_PR;
  633.       /* HTNA algorithm... we need to know the highest TSN
  634.        * sacked (even if it isn't new), so that when the sender
  635.        * is in Fast Recovery, the outstanding tsns beyond the 
  636.        * last sack tsn do not have their missing reports incremented
  637.        */
  638.       if(uiHighestTsnSacked < spCurrNodeData->spChunk->uiTsn)
  639. uiHighestTsnSacked = spCurrNodeData->spChunk->uiTsn;
  640.       if(spCurrNodeData->eGapAcked == FALSE)
  641. {
  642.   DBG_PL(ProcessGapAckBlocks, "setting eGapAcked=TRUE") DBG_PR;
  643.   spCurrNodeData->eGapAcked = TRUE;
  644.   /* HTNA algorithm... we need to know the highest TSN
  645.    * newly acked
  646.    */
  647.   if(uiHighestTsnNewlyAcked < spCurrNodeData->spChunk->uiTsn)
  648.     uiHighestTsnNewlyAcked = spCurrNodeData->spChunk->uiTsn;
  649.   if(spCurrNodeData->eAdvancedAcked == FALSE)
  650.     {
  651.       spCurrNodeData->spDest->iNumNewlyAckedBytes 
  652. += spCurrNodeData->spChunk->sHdr.usLength;
  653.     }
  654.   
  655.   /* only increment partial bytes acked if we are in
  656.    * congestion avoidance mode, we have a new cum ack, and
  657.    * we haven't already incremented it for this TSN
  658.    */
  659.   if(( spCurrNodeData->spDest->iCwnd 
  660.        > spCurrNodeData->spDest->iSsthresh) &&
  661.      eNewCumAck == TRUE &&
  662.      spCurrNodeData->eAddedToPartialBytesAcked == FALSE)
  663.     {
  664.       DBG_PL(ProcessGapAckBlocks, 
  665.      "setting eAddedToPartiallyBytesAcked=TRUE")DBG_PR;
  666.       
  667.       spCurrNodeData->eAddedToPartialBytesAcked = TRUE; // set
  668.       spCurrNodeData->spDest->iPartialBytesAcked 
  669. += spCurrNodeData->spChunk->sHdr.usLength;
  670.     }
  671.   /* We update the RTT estimate if the following hold true:
  672.    *   1. RTO pending flag is set (6.3.1.C4)
  673.    *   2. Timestamp is set for this chunk 
  674.    *   3. This chunk has not been retransmitted
  675.    *   4. This chunk has not been gap acked already 
  676.    *   5. This chunk has not been advanced acked (pr-sctp)
  677.    */
  678.   if(spCurrNodeData->spDest->eRtoPending == TRUE &&
  679.      spCurrNodeData->dTxTimestamp > 0 &&
  680.      spCurrNodeData->iNumTxs == 1 &&
  681.      spCurrNodeData->eAdvancedAcked == FALSE) 
  682.     {
  683.       /* If the chunk is marked for timeout rtx, then the
  684.        * sender is an ambigious state. Were the sacks lost
  685.        * or was there a failure?  Since we don't clear the
  686.        * error counter below, we also don't update the
  687.        * RTT. This could be a problem for late arriving
  688.        * SACKs.
  689.        */
  690.       if(spCurrNodeData->eMarkedForRtx != TIMEOUT_RTX)
  691. RttUpdate(spCurrNodeData->dTxTimestamp, 
  692.   spCurrNodeData->spDest);
  693.       spCurrNodeData->spDest->eRtoPending = FALSE;
  694.     }
  695.   /* section 6.3.2.R3 - Stop the timer if this is the
  696.    * first outstanding for this destination (note: it may
  697.    * have already been stopped if there was a new cum
  698.    * ack). If there are still outstanding bytes on this
  699.    * destination, we'll restart the timer later in
  700.    * ProcessSackChunk() 
  701.    */
  702.   if(spCurrNodeData->spDest->spFirstOutstanding 
  703.      == spCurrNodeData)
  704.     
  705.     {
  706.       if(spCurrNodeData->spDest->eRtxTimerIsRunning == TRUE)
  707. StopT3RtxTimer(spCurrNodeData->spDest);
  708.     }
  709.   
  710.   iAssocErrorCount = 0;
  711.   
  712.   /* We don't want to clear the error counter if it's
  713.    * cleared already; otherwise, we'll unnecessarily
  714.    * trigger a trace event.
  715.    *
  716.    * Also, the error counter is cleared by SACKed data
  717.    * ONLY if the TSNs are not marked for timeout
  718.    * retransmission and has not been gap acked
  719.    * before. Without this condition, we can run into a
  720.    * problem for failure detection. When a failure occurs,
  721.    * some data may have made it through before the
  722.    * failure, but the sacks got lost. When the sender
  723.    * retransmits the first outstanding, the receiver will
  724.    * sack all the data whose sacks got lost. We don't want
  725.    * these sacks to clear the error counter, or else
  726.    * failover would take longer.
  727.    */
  728.   if(spCurrNodeData->spDest->iErrorCount != 0 &&
  729.      spCurrNodeData->eMarkedForRtx != TIMEOUT_RTX)
  730.     {
  731.       DBG_PL(ProcessGapAckBlocks,
  732.      "clearing error counter for %p with tsn=%lu"), 
  733. spCurrNodeData->spDest, 
  734. spCurrNodeData->spChunk->uiTsn DBG_PR;
  735.       spCurrNodeData->spDest->iErrorCount = 0; // clear errors
  736.       tiErrorCount++;                      // ... and trace it!
  737.       spCurrNodeData->spDest->eStatus=SCTP_DEST_STATUS_ACTIVE;
  738.       if(spCurrNodeData->spDest == spPrimaryDest &&
  739.  spNewTxDest != spPrimaryDest) 
  740. {
  741.   DBG_PL(ProcessGapAckBlocks,
  742.  "primary recovered... "
  743.  "migrating back from %p to %p"),
  744.     spNewTxDest, spPrimaryDest DBG_PR;
  745.   spNewTxDest = spPrimaryDest; // return to primary
  746. }
  747.     }
  748.   spCurrNodeData->eMarkedForRtx = NO_RTX; // unmark
  749. }
  750.     }
  751.   else if(spCurrNodeData->spChunk->uiTsn > uiEndTsn)
  752.     {
  753.       /* This point in the rtx buffer is already past the tsns which 
  754.        * are being acked by this gap ack block.  
  755.        */
  756.       usNumGapAcksProcessed++; 
  757.       /* Did we process all the gap ack blocks?
  758.        */
  759.       if(usNumGapAcksProcessed != spSackChunk->usNumGapAckBlocks)
  760. {
  761.   DBG_PL(ProcessGapAckBlocks, "jump to next gap ack block") 
  762.     DBG_PR;
  763.   spCurrGapAck 
  764.     = ((SctpGapAckBlock_S *)
  765.        (ucpSackChunk + sizeof(SctpSackChunk_S)
  766. +(usNumGapAcksProcessed * sizeof(SctpGapAckBlock_S))));
  767. }
  768.       /* If this chunk was GapAcked before, then either the
  769.        * receiver has renegged the chunk (which our simulation
  770.        * doesn't do) or this SACK is arriving out of order.
  771.        */
  772.       if(spCurrNodeData->eGapAcked == TRUE)
  773. {
  774.   DBG_PL(ProcessGapAckBlocks, 
  775.  "out of order SACK? setting TSN=%d eGapAcked=FALSE"),
  776.     spCurrNodeData->spChunk->uiTsn DBG_PR;
  777.   spCurrNodeData->eGapAcked = FALSE;
  778.   spCurrNodeData->spDest->iOutstandingBytes 
  779.     += spCurrNodeData->spChunk->sHdr.usLength;
  780.   
  781.   /* section 6.3.2.R4 says that we should restart the
  782.    * T3-rtx timer here if it isn't running already. In our
  783.    * implementation, it isn't necessary since
  784.    * ProcessSackChunk will restart the timer for any
  785.    * destinations which have outstanding data and don't
  786.    * have a timer running.
  787.    */
  788. }
  789.     }
  790. }
  791.       /* By this time, either we have run through the entire send buffer or we
  792.        * have run out of gap ack blocks. In the case that we have run out of 
  793.        * gap ack blocks before we finished running through the send buffer, we
  794.        * need to mark the remaining chunks in the send buffer as 
  795.        * eGapAcked=FALSE. This final marking needs to be done, because we only
  796.        * trust gap ack info from the last SACK. Otherwise, renegging (which we
  797.        * don't do) or out of order SACKs would give the sender an incorrect 
  798.        * view of the peer's rwnd.
  799.        */
  800.       for(; spCurrNode != NULL; spCurrNode = spCurrNode->spNext)
  801. {
  802.   /* This chunk is NOT being acked and is missing at the receiver
  803.    */
  804.   spCurrNodeData = (SctpSendBufferNode_S *) spCurrNode->vpData;
  805.   /* If this chunk was GapAcked before, then either the
  806.    * receiver has renegged the chunk (which our simulation
  807.    * doesn't do) or this SACK is arriving out of order.
  808.    */
  809.   if(spCurrNodeData->eGapAcked == TRUE)
  810.     {
  811.       DBG_PL(ProcessGapAckBlocks, 
  812.      "out of order SACK? setting TSN=%d eGapAcked=FALSE"),
  813. spCurrNodeData->spChunk->uiTsn DBG_PR;
  814.       spCurrNodeData->eGapAcked = FALSE;
  815.       spCurrNodeData->spDest->iOutstandingBytes 
  816. += spCurrNodeData->spChunk->sHdr.usLength;
  817.       /* section 6.3.2.R4 says that we should restart the T3-rtx
  818.        * timer here if it isn't running already. In our
  819.        * implementation, it isn't necessary since ProcessSackChunk
  820.        * will restart the timer for any destinations which have
  821.        * outstanding data and don't have a timer running.
  822.        */
  823.     }
  824. }
  825.       DBG_PL(ProcessGapAckBlocks,"now incrementing missing reports...") DBG_PR;
  826.       DBG_PL(ProcessGapAckBlocks,"uiHighestTsnNewlyAcked=%d"), 
  827.      uiHighestTsnNewlyAcked DBG_PR;
  828.       for(spCurrNode = sSendBuffer.spHead;
  829.   spCurrNode != NULL; 
  830.   spCurrNode = spCurrNode->spNext)
  831. {
  832.   spCurrNodeData = (SctpSendBufferNode_S *) spCurrNode->vpData;
  833.   DBG_PL(ProcessGapAckBlocks, "TSN=%d eGapAcked=%s"), 
  834.     spCurrNodeData->spChunk->uiTsn,
  835.     spCurrNodeData->eGapAcked ? "TRUE" : "FALSE"
  836.     DBG_PR;
  837.   if(spCurrNodeData->eGapAcked == FALSE)
  838.     {
  839.       /* HTNA (Highest TSN Newly Acked) algorithm from
  840.        * implementer's guide. The HTNA increments missing reports
  841.        * for TSNs not GapAcked when one of the following
  842.        * conditions hold true:
  843.        *
  844.        *    1. The TSN is less than the highest TSN newly acked.
  845.        *
  846.        *    2. The TSN is less than the highest TSN sacked so far
  847.        *    (not necessarily newly acked), the sender is in Fast
  848.        *    Recovery, the cum ack changes, and the new cum ack is less
  849.        *    than recover.
  850.        */
  851.       if( (spCurrNodeData->spChunk->uiTsn < uiHighestTsnNewlyAcked) ||
  852.   (eNewCumAck == TRUE && 
  853.    uiHighestTsnNewlyAcked <= uiRecover &&
  854.    spCurrNodeData->spChunk->uiTsn < uiHighestTsnSacked))
  855. {
  856.   spCurrNodeData->iNumMissingReports++;
  857.   DBG_PL(ProcessGapAckBlocks, 
  858.  "incrementing missing report for TSN=%d to %d"), 
  859.     spCurrNodeData->spChunk->uiTsn,
  860.     spCurrNodeData->iNumMissingReports
  861.     DBG_PR;
  862.   if(spCurrNodeData->iNumMissingReports >= iFastRtxTrigger &&
  863.      spCurrNodeData->eIneligibleForFastRtx == FALSE &&
  864.      spCurrNodeData->eAdvancedAcked == FALSE)
  865.     {
  866.       MarkChunkForRtx(spCurrNodeData, FAST_RTX);
  867.       eFastRtxNeeded = TRUE;
  868.       spCurrNodeData->eIneligibleForFastRtx = TRUE;
  869.       DBG_PL(ProcessGapAckBlocks, 
  870.      "setting eFastRtxNeeded = TRUE") DBG_PR;
  871.     }
  872. }
  873.     }
  874. }
  875.     }
  876.   if(eFastRtxNeeded == TRUE)
  877.     tiFrCount++;
  878.   DBG_PL(ProcessGapAckBlocks, "eFastRtxNeeded=%s"), 
  879.     eFastRtxNeeded ? "TRUE" : "FALSE" DBG_PR;
  880.   DBG_X(ProcessGapAckBlocks);
  881.   return eFastRtxNeeded;
  882. }
  883. void SctpAgent::ProcessSackChunk(u_char *ucpSackChunk)
  884. {
  885.   DBG_I(ProcessSackChunk);
  886.   SctpSackChunk_S *spSackChunk = (SctpSackChunk_S *) ucpSackChunk;
  887.   DBG_PL(ProcessSackChunk, "cum=%d arwnd=%d #gapacks=%d #duptsns=%d"),
  888.     spSackChunk->uiCumAck, spSackChunk->uiArwnd, 
  889.     spSackChunk->usNumGapAckBlocks, spSackChunk->usNumDupTsns 
  890.     DBG_PR;
  891.   Boolean_E eFastRtxNeeded = FALSE;
  892.   Boolean_E eNewCumAck = FALSE;
  893.   Node_S *spCurrDestNode = NULL;
  894.   SctpDest_S *spCurrDestNodeData = NULL;
  895.   u_int uiTotalOutstanding = 0;
  896.   int i = 0;
  897.   /* make sure we clear all the iNumNewlyAckedBytes before using them!
  898.    */
  899.   for(spCurrDestNode = sDestList.spHead;
  900.       spCurrDestNode != NULL;
  901.       spCurrDestNode = spCurrDestNode->spNext)
  902.     {
  903.       spCurrDestNodeData = (SctpDest_S *) spCurrDestNode->vpData;
  904.       spCurrDestNodeData->iNumNewlyAckedBytes = 0;
  905.       spCurrDestNodeData->spFirstOutstanding = NULL;
  906.     }
  907.   if(spSackChunk->uiCumAck < uiCumAckPoint) 
  908.     {
  909.       /* this cumAck's a previously cumAck'd tsn (ie, it's out of order!)
  910.        * ...so ignore!
  911.        */
  912.       DBG_PL(ProcessSackChunk, "ignoring out of order sack!") DBG_PR;
  913.       DBG_X(ProcessSackChunk);
  914.       return;
  915.     }
  916.   else if(spSackChunk->uiCumAck > uiCumAckPoint)
  917.     {
  918.       eNewCumAck = TRUE; // incomding SACK's cum ack advances the cum ack point
  919.       SendBufferDequeueUpTo(spSackChunk->uiCumAck);
  920.       uiCumAckPoint = spSackChunk->uiCumAck; // Advance the cumAck pointer
  921.     }
  922.   if(spSackChunk->usNumGapAckBlocks != 0) // are there any gaps??
  923.     {
  924.       eFastRtxNeeded = ProcessGapAckBlocks(ucpSackChunk, eNewCumAck);
  925.     } 
  926.   for(spCurrDestNode = sDestList.spHead;
  927.       spCurrDestNode != NULL;
  928.       spCurrDestNode = spCurrDestNode->spNext)
  929.     {
  930.       spCurrDestNodeData = (SctpDest_S *) spCurrDestNode->vpData;
  931.       /* Only adjust cwnd if:
  932.        *    1. sack advanced the cum ack point 
  933.        *    2. this destination has newly acked bytes
  934.        *    3. the cum ack is at or beyond the recovery window
  935.        *
  936.        * Also, we MUST adjust our congestion window BEFORE we update the
  937.        * number of outstanding bytes to reflect the newly acked bytes in
  938.        * received SACK.
  939.        */
  940.       if(eNewCumAck == TRUE &&
  941.  spCurrDestNodeData->iNumNewlyAckedBytes > 0 &&
  942.  spSackChunk->uiCumAck >= uiRecover)
  943. {
  944.   AdjustCwnd(spCurrDestNodeData);
  945. }
  946.       /* The number of outstanding bytes is reduced by how many bytes this 
  947.        * sack acknowledges.
  948.        */
  949.       if(spCurrDestNodeData->iNumNewlyAckedBytes <=
  950.  spCurrDestNodeData->iOutstandingBytes)
  951. {
  952.   spCurrDestNodeData->iOutstandingBytes 
  953.     -= spCurrDestNodeData->iNumNewlyAckedBytes;
  954. }
  955.       else
  956. spCurrDestNodeData->iOutstandingBytes = 0;
  957.       DBG_PL(ProcessSackChunk,"Dest #%d (%d:%d) (%p): outstanding=%d, cwnd=%d"),
  958. ++i, spCurrDestNodeData->iNsAddr, spCurrDestNodeData->iNsPort,
  959. spCurrDestNodeData, spCurrDestNodeData->iOutstandingBytes, 
  960. spCurrDestNodeData->iCwnd DBG_PR;
  961.       
  962.       if(spCurrDestNodeData->iOutstandingBytes == 0)
  963. {
  964.   /* All outstanding data has been acked
  965.    */
  966.   spCurrDestNodeData->iPartialBytesAcked = 0;  // section 7.2.2
  967.   /* section 6.3.2.R2
  968.    */
  969.   if(spCurrDestNodeData->eRtxTimerIsRunning == TRUE)
  970.     {
  971.       DBG_PL(ProcessSackChunk, "Dest #%d (%p): stopping timer"), 
  972. i, spCurrDestNodeData DBG_PR;
  973.       StopT3RtxTimer(spCurrDestNodeData);
  974.     }
  975. }
  976.       /* section 6.3.2.R3 - Restart timers for destinations that have
  977.        * acknowledged their first outstanding (ie, no timer running) and
  978.        * still have outstanding data in flight.  
  979.        */
  980.       if(spCurrDestNodeData->iOutstandingBytes > 0 &&
  981.  spCurrDestNodeData->eRtxTimerIsRunning == FALSE)
  982. {
  983.   StartT3RtxTimer(spCurrDestNodeData);
  984. }
  985.     }
  986.   DBG_F(ProcessSackChunk, DumpSendBuffer());
  987.   AdvancePeerAckPoint();
  988.   if(eFastRtxNeeded == TRUE)  // section 7.2.4
  989.     FastRtx();
  990.   /* Let's see if after process this sack, there are still any chunks
  991.    * pending... If so, rtx all allowed by cwnd.
  992.    */
  993.   else if( (eMarkedChunksPending = AnyMarkedChunks()) == TRUE)
  994.     {
  995.       /* section 6.1.C) When the time comes for the sender to
  996.        * transmit, before sending new DATA chunks, the sender MUST
  997.        * first transmit any outstanding DATA chunks which are marked
  998.        * for retransmission (limited by the current cwnd).  
  999.        */
  1000.       RtxMarkedChunks(RTX_LIMIT_CWND);
  1001.     }
  1002.   /* (6.2.1.D.ii) Adjust PeerRwnd based on total oustanding bytes on all
  1003.    * destinations. We need to this adjustment after any
  1004.    * retransmissions. Otherwise the sender's view of the peer rwnd will be
  1005.    * off, because the number outstanding increases again once a marked
  1006.    * chunk gets retransmitted (when marked, outstanding is decreased).
  1007.    */
  1008.   uiTotalOutstanding = TotalOutstanding();
  1009.   if(uiTotalOutstanding <= spSackChunk->uiArwnd)
  1010.     uiPeerRwnd = (spSackChunk->uiArwnd  - uiTotalOutstanding);
  1011.   else
  1012.     uiPeerRwnd = 0;
  1013.   
  1014.   DBG_PL(ProcessSackChunk, "uiPeerRwnd=%d, uiArwnd=%d"), uiPeerRwnd, 
  1015.     spSackChunk->uiArwnd DBG_PR;
  1016.   DBG_X(ProcessSackChunk);
  1017. }
  1018. void SctpAgent::ProcessForwardTsnChunk(SctpForwardTsnChunk_S *spForwardTsnChunk)
  1019. {
  1020.   DBG_I(ProcessForwardTsnChunk);
  1021.   
  1022.   int i;
  1023.   u_int uiNewCum = spForwardTsnChunk->uiNewCum;
  1024.   /* Although we haven't actually received a DATA chunk, we have received
  1025.    * a FORWARD CUM TSN chunk, which essentially tells the receiver to
  1026.    * pretend that it has received all the DATA chunks up to the forwarded cum.
  1027.    * Some of the tsns we are forwarding past have been received, while others
  1028.    * have not.
  1029.    */
  1030.   for(i = uiCumAck+1; i <= (int) uiNewCum; i++)
  1031.     {
  1032.       if( IsDuplicateChunk(i) == FALSE )  // make sure it hasn't been received
  1033. UpdateRecvTsnBlocks(i);
  1034.     }
  1035.   UpdateAllStreams();
  1036.   DBG_PL(ProcessForwardTsnChunk, "uiCumAck=%d"), uiCumAck DBG_PR;
  1037.   DBG_X(ProcessForwardTsnChunk);
  1038. }
  1039. void SctpAgent::ProcessHeartbeatAckChunk(SctpHeartbeatAckChunk_S 
  1040.  *spHeartbeatAckChunk)
  1041. {
  1042.   DBG_I(ProcessHeartbeatAckChunk);
  1043.   iAssocErrorCount = 0;
  1044.   /* trigger trace ONLY if it was previously NOT 0
  1045.    */
  1046.   
  1047.   /* NE - 4/11/2007
  1048.    * Change for Confirming Destination Addresses
  1049.    * condition (spHeartbeatAckChunk->spDest->iErrorCount != 0) is changed 
  1050.    * with (spHeartbeatAckChunk->spDest->iErrorCount >= 0) to enable
  1051.    * destination address confirmation when the association moves to 
  1052.    * ESTABLISHED state. iErrorCount may be 0 for any given destinations
  1053.    * during the confirmation.
  1054.    */
  1055.   if(spHeartbeatAckChunk->spDest->iErrorCount >= 0)
  1056.     {
  1057.       DBG_PL(ProcessHeartbeatAckChunk, "marking dest %p to active"),
  1058.                         spHeartbeatAckChunk->spDest DBG_PR;
  1059.       spHeartbeatAckChunk->spDest->eStatus = SCTP_DEST_STATUS_ACTIVE;
  1060.       spHeartbeatAckChunk->spDest->iErrorCount = 0; // clear the error count
  1061.       tiErrorCount++;                               // ...and trace it too!
  1062.       if(spHeartbeatAckChunk->spDest == spPrimaryDest &&
  1063.  spNewTxDest != spPrimaryDest) 
  1064. {
  1065.   DBG_PL(ProcessHeartbeatAckChunk,
  1066.  "primary recovered... migrating back from %p to %p"),
  1067.     spNewTxDest, spPrimaryDest DBG_PR;
  1068.   spNewTxDest = spPrimaryDest; // return to primary
  1069. }
  1070.     }
  1071.   RttUpdate(spHeartbeatAckChunk->dTimestamp, spHeartbeatAckChunk->spDest);
  1072.   DBG_PL(ProcessHeartbeatAckChunk, "set rto of dest=%p to %f"),
  1073.     spHeartbeatAckChunk->spDest, spHeartbeatAckChunk->spDest->dRto DBG_PR;
  1074.   
  1075.   spHeartbeatAckChunk->spDest->opHeartbeatTimeoutTimer->force_cancel();
  1076.   /* Track HB-Timer for CMT-PF 
  1077.    */
  1078.   spHeartbeatAckChunk->spDest->eHBTimerIsRunning = FALSE;
  1079.   DBG_X(ProcessHeartbeatAckChunk);
  1080. }
  1081. /* This function is left as a hook for extensions to process chunk types not
  1082.  * supported in the base scpt. In the base sctp, we simply report the error...
  1083.  * the chunk is unexpected and unknown.
  1084.  */
  1085. void SctpAgent::ProcessOptionChunk(u_char *ucpInChunk)
  1086. {
  1087.   DBG_I(ProcessOptionChunk);
  1088.   double dCurrTime = Scheduler::instance().clock();
  1089.   DBG_PL(ProcessOptionChunk, "unexpected chunk type (unknown: %d) at %f"),
  1090.     ((SctpChunkHdr_S *)ucpInChunk)->ucType, dCurrTime DBG_PR;
  1091.   printf("[ProcessOptionChunk] unexpected chunk type (unknown: %d) at %fn",
  1092.  ((SctpChunkHdr_S *)ucpInChunk)->ucType, dCurrTime);
  1093.   DBG_X(ProcessOptionChunk);
  1094. }
  1095. int SctpAgent::ProcessChunk(u_char *ucpInChunk, u_char **ucppOutData)
  1096. {
  1097.   DBG_I(ProcessChunk);
  1098.   int iThisOutDataSize = 0;
  1099.   Node_S *spCurrNode = NULL;
  1100.   SctpDest_S *spCurrDest = NULL;
  1101.   double dCurrTime = Scheduler::instance().clock();
  1102.   double dTime;
  1103.   SctpHeartbeatAckChunk_S *spHeartbeatChunk = NULL;
  1104.   SctpHeartbeatAckChunk_S *spHeartbeatAckChunk = NULL;
  1105.   /* NE: 4/11/2007 - Confirming Destinations */
  1106.   Boolean_E eThisDestWasUnconfirmed = FALSE;
  1107.   Boolean_E eFoundUnconfirmedDest = FALSE;
  1108.   /* End of Confirming Destinations */
  1109.   
  1110.   switch(eState)
  1111.     {
  1112.     case SCTP_STATE_CLOSED:
  1113.       switch( ((SctpChunkHdr_S *)ucpInChunk)->ucType)
  1114. {
  1115. case SCTP_CHUNK_INIT:
  1116.   DBG_PL(ProcessChunk, "got INIT!! ...sending INIT_ACK") DBG_PR;
  1117.   ProcessInitChunk(ucpInChunk);
  1118.   iThisOutDataSize = GenChunk(SCTP_CHUNK_INIT_ACK, *ucppOutData);
  1119.   *ucppOutData += iThisOutDataSize;
  1120.   /* stay in the closed state */
  1121.   break;
  1122. case SCTP_CHUNK_COOKIE_ECHO:
  1123.   DBG_PL(ProcessChunk, 
  1124.  "got COOKIE_ECHO!! (established!) ...sending COOKIE_ACK")
  1125.     DBG_PR;
  1126.   ProcessCookieEchoChunk( (SctpCookieEchoChunk_S *) ucpInChunk );
  1127.   iThisOutDataSize = GenChunk(SCTP_CHUNK_COOKIE_ACK, *ucppOutData);
  1128.   *ucppOutData += iThisOutDataSize;
  1129.   eState = SCTP_STATE_ESTABLISHED;
  1130.   if(uiHeartbeatInterval != 0)
  1131.     {
  1132.       dTime = CalcHeartbeatTime(spPrimaryDest->dRto);
  1133.       opHeartbeatGenTimer->force_cancel();
  1134.       opHeartbeatGenTimer->resched(dTime);
  1135.       opHeartbeatGenTimer->dStartTime = dCurrTime;
  1136.       
  1137.       for(spCurrNode = sDestList.spHead;
  1138.   spCurrNode != NULL;
  1139.   spCurrNode = spCurrNode->spNext)
  1140. {
  1141.   spCurrDest = (SctpDest_S *) spCurrNode->vpData;
  1142.   spCurrDest->dIdleSince = dCurrTime;
  1143. }
  1144.     }
  1145.   
  1146.   break;
  1147.   
  1148.    default:
  1149.   /* ALC 1/25/2002
  1150.    *
  1151.    * no error statement here, because there are times when this could
  1152.    * occur due to abrupt disconnections via the "reset" command. how?
  1153.    * well, "reset" resets all the association state. however, there may
  1154.    * still be packets in transit.if and when those packets arrive, they
  1155.    * will be unexpected packets since the association is closed. since
  1156.    * this is a simulation, it shouldn't be a problem. however, if an 
  1157.    * application needs a more graceful shutdown, we would need to 
  1158.    * implement sctp's proper shutdown procedure. until the need arises,
  1159.    * we won't do it. instead, what do we do? ignore the "unexpected"
  1160.    * packet.
  1161.    */
  1162.   DBG_PL(ProcessChunk, "association closed... ignoring chunk %s"), 
  1163.     "(not COOKIE_ECHO or INIT)" DBG_PR;
  1164.   break;
  1165. }
  1166.       break;
  1167.   
  1168.     case SCTP_STATE_COOKIE_WAIT:
  1169.       DBG_PL(ProcessChunk, "got INIT_ACK!! ...sending COOKIE_ECHO") DBG_PR;
  1170.       ProcessInitAckChunk(ucpInChunk);
  1171.       iThisOutDataSize = GenChunk(SCTP_CHUNK_COOKIE_ECHO, *ucppOutData);
  1172.       *ucppOutData += iThisOutDataSize;
  1173.       opT1CookieTimer->resched(spPrimaryDest->dRto);
  1174.       eState = SCTP_STATE_COOKIE_ECHOED;
  1175.       break;
  1176.     case SCTP_STATE_COOKIE_ECHOED:
  1177.       DBG_PL(ProcessChunk, "got COOKIE_ACK!! (established!) ...sending DATA")
  1178. DBG_PR;
  1179.       ProcessCookieAckChunk( (SctpCookieAckChunk_S *) ucpInChunk );
  1180.       eSendNewDataChunks = TRUE;
  1181.       eState = SCTP_STATE_ESTABLISHED;
  1182.       
  1183.       /* NE: 4/11/2007 - Confirming Destinations 
  1184.        * Confirming Destination Addresses for sender of INIT.
  1185.        * In our implementation destinations are confirmed just for 
  1186.        * sender of the INIT. Confirming of destinations is not done for
  1187.        * receiver of the INIT.
  1188.        *
  1189.        * RFC 4460 - section 5.4 - Path Verification
  1190.        * Rule 3 - all addresses not covered by rule 1 and 2 are 
  1191.        * considered UNCONFIRMED.
  1192.        */
  1193.       for(spCurrNode = sDestList.spHead;
  1194.   spCurrNode != NULL;
  1195.   spCurrNode = spCurrNode->spNext)
  1196.         {
  1197.   spCurrDest = (SctpDest_S *) spCurrNode->vpData;
  1198.           spCurrDest->eStatus = SCTP_DEST_STATUS_UNCONFIRMED;
  1199.   /* Send Heartbeat to confirm this dest and get RTT */
  1200.   SendHeartbeat(spCurrDest);
  1201.         }
  1202.       /* NE: 4/11/2007 - Confirming Destinations */
  1203.       /* Do not send any data until all dests have been confirmed
  1204.        * RFC allows to send data on confirmed dests though.
  1205.        * This implementation is more conservative than the RFC
  1206.        */ 
  1207.       eSendNewDataChunks = FALSE;
  1208.   
  1209.       break;
  1210.     case SCTP_STATE_ESTABLISHED:
  1211.       switch( ((SctpChunkHdr_S *)ucpInChunk)->ucType)
  1212. {
  1213. case SCTP_CHUNK_DATA:
  1214.   DBG_PL(ProcessChunk, "got DATA (TSN=%d)!!"), 
  1215.     ((SctpDataChunkHdr_S *)ucpInChunk)->uiTsn DBG_PR;
  1216.   
  1217.   if(eUseDelayedSacks == FALSE) // are we doing delayed sacks?
  1218.     {
  1219.       /* NO, so generate sack immediately!
  1220.        */
  1221.       eSackChunkNeeded = TRUE;
  1222.     }
  1223.   else  // we are doing delayed sacks, so...
  1224.     {
  1225.       /* rfc2960 section 6.2 - determine if a SACK will be generated
  1226.        */
  1227.       if(eStartOfPacket == TRUE)  
  1228. {
  1229.   eStartOfPacket = FALSE;  // reset
  1230.   iDataPktCountSinceLastSack++;
  1231.   if(iDataPktCountSinceLastSack == 1)   
  1232.     {
  1233.       opSackGenTimer->resched(dSackDelay);
  1234.     }
  1235.   else if(iDataPktCountSinceLastSack == DELAYED_SACK_TRIGGER)
  1236.     {
  1237.       iDataPktCountSinceLastSack = 0; // reset
  1238.       opSackGenTimer->force_cancel();
  1239.       eSackChunkNeeded = TRUE;
  1240.     }
  1241. }
  1242.     }
  1243.   ProcessDataChunk( (SctpDataChunkHdr_S *) ucpInChunk );
  1244.   /* section 6.7 - There is at least one "gap in the received DATA
  1245.    * chunk sequence", so let's ensure we send a SACK immediately!
  1246.    */
  1247.   if(sRecvTsnBlockList.uiLength > 0)
  1248.     {
  1249.       iDataPktCountSinceLastSack = 0; // reset
  1250.       opSackGenTimer->force_cancel();
  1251.       eSackChunkNeeded = TRUE;
  1252.     }
  1253.   /* no state change 
  1254.    */   
  1255.   break;
  1256. case SCTP_CHUNK_SACK:
  1257.   DBG_PL(ProcessChunk, "got SACK (CumAck=%d)!!"),
  1258.     ((SctpSackChunk_S *)ucpInChunk)->uiCumAck DBG_PR;
  1259.   ProcessSackChunk(ucpInChunk);
  1260.   /* Do we need to transmit a FORWARD TSN chunk??
  1261.    */
  1262.   if(uiAdvancedPeerAckPoint > uiCumAckPoint)
  1263.     eForwardTsnNeeded = TRUE;
  1264.   eSendNewDataChunks = TRUE;
  1265.   break; // no state change
  1266. case SCTP_CHUNK_FORWARD_TSN:
  1267.   DBG_PL(ProcessChunk, "got FORWARD TSN (tsn=%d)!!"),
  1268.     ((SctpForwardTsnChunk_S *) ucpInChunk)->uiNewCum DBG_PR;
  1269.   ProcessForwardTsnChunk( (SctpForwardTsnChunk_S *) ucpInChunk );
  1270.   break; // no state change
  1271. case SCTP_CHUNK_HB:
  1272.   DBG_PL(ProcessChunk, "got HEARTBEAT!!") DBG_PR;
  1273.   /* GenChunk() doesn't copy HB info
  1274.    */
  1275.   iThisOutDataSize = GenChunk(SCTP_CHUNK_HB_ACK, *ucppOutData);
  1276.   /* ...so we copy it here!
  1277.    */
  1278.   spHeartbeatChunk = (SctpHeartbeatAckChunk_S *) ucpInChunk;
  1279.   spHeartbeatAckChunk = (SctpHeartbeatAckChunk_S *) *ucppOutData;
  1280.   spHeartbeatAckChunk->dTimestamp = spHeartbeatChunk->dTimestamp;
  1281.   spHeartbeatAckChunk->spDest = spHeartbeatChunk->spDest;
  1282.   *ucppOutData += iThisOutDataSize;
  1283.   break; // no state change
  1284. case SCTP_CHUNK_HB_ACK:
  1285.   DBG_PL(ProcessChunk, "got HEARTBEAT ACK!!") DBG_PR;
  1286.   
  1287.   spHeartbeatAckChunk = (SctpHeartbeatAckChunk_S *) ucpInChunk;
  1288.   
  1289.   /* NE: 4/11/2007 - Confirming Destinations */
  1290.   if( spHeartbeatAckChunk->spDest->eStatus ==
  1291.       SCTP_DEST_STATUS_UNCONFIRMED )
  1292.     eThisDestWasUnconfirmed = TRUE;
  1293.   
  1294.   ProcessHeartbeatAckChunk( (SctpHeartbeatAckChunk_S *) ucpInChunk);
  1295.   
  1296.   /* NE: 4/11/2007 - Confirming Destinations */
  1297.   /* Check if all dests have been confirmed. 
  1298.            * If yes, allow new data transmission
  1299.            */
  1300.   if(eThisDestWasUnconfirmed == TRUE) 
  1301.             {
  1302.       for(spCurrNode = sDestList.spHead;
  1303.                   spCurrNode != NULL;
  1304.                   spCurrNode = spCurrNode->spNext)
  1305.                 {
  1306.                   spCurrDest = (SctpDest_S *) spCurrNode->vpData;
  1307.                   if(spCurrDest->eStatus == SCTP_DEST_STATUS_UNCONFIRMED)
  1308.     {
  1309.       eFoundUnconfirmedDest = TRUE;
  1310.       DBG_PL(ProcessChunk, "dest=%p UNCONFIRMED"), 
  1311. spCurrDest DBG_PR;
  1312.       break;
  1313.     }
  1314. }
  1315.   
  1316. if(eFoundUnconfirmedDest == TRUE)
  1317.   break; /* From this case */
  1318. else 
  1319.   {
  1320.     /* All dests have been confirmed */
  1321.     eSendNewDataChunks = TRUE;
  1322.     
  1323.     /* Moved code from COOKIE ACK recvd */
  1324.                    if(uiHeartbeatInterval != 0)
  1325.       {
  1326. dTime = CalcHeartbeatTime(spPrimaryDest->dRto);
  1327. opHeartbeatGenTimer->force_cancel();
  1328. opHeartbeatGenTimer->resched(dTime);
  1329. opHeartbeatGenTimer->dStartTime = dCurrTime;
  1330. for(spCurrNode = sDestList.spHead;
  1331.     spCurrNode != NULL;
  1332.     spCurrNode = spCurrNode->spNext)
  1333.   {
  1334.     spCurrDest = (SctpDest_S *) spCurrNode->vpData;
  1335.     spCurrDest->dIdleSince = dCurrTime;
  1336.   }
  1337.       }
  1338.     
  1339.     DBG_PL(ProcessChunk, "All destinations confirmed!") 
  1340.       DBG_PR;
  1341.                   }
  1342.             }
  1343.   
  1344.   break; // no state change
  1345.   
  1346. case SCTP_CHUNK_INIT:
  1347.   DBG_PL(ProcessChunk, "unexpected chunk type (INIT) at %f"),
  1348.     dCurrTime DBG_PR;
  1349.   printf("[ProcessChunk] unexpected chunk type (INIT) at %fn",
  1350.     dCurrTime);
  1351.   break;
  1352. case SCTP_CHUNK_INIT_ACK:
  1353.   DBG_PL(ProcessChunk, "unexpected chunk type (INIT_ACK) at %f"),
  1354.     dCurrTime DBG_PR;
  1355.   printf("[ProcessChunk] unexpected chunk type (INIT_ACK) at %fn",
  1356.     dCurrTime);
  1357.   break;
  1358.   /* even though the association is established,COOKIE_ECHO needs to be
  1359.    * handled because the peer may have not received the COOKIE_ACK.
  1360.    *
  1361.    * Note: we don't follow the rfc's complex process for handling this
  1362.    * case, because we don't deal with tie-tags, etc in simulation. :-)
  1363.    */
  1364. case SCTP_CHUNK_COOKIE_ECHO:
  1365.   DBG_PL(ProcessChunk,
  1366.  "got COOKIE_ECHO!! (established!) ...sending COOKIE_ACK")
  1367.     DBG_PR;
  1368.   ProcessCookieEchoChunk( (SctpCookieEchoChunk_S *) ucpInChunk);
  1369.   iThisOutDataSize = GenChunk(SCTP_CHUNK_COOKIE_ACK, *ucppOutData);
  1370.   *ucppOutData += iThisOutDataSize;
  1371.   break;
  1372. case SCTP_CHUNK_COOKIE_ACK:
  1373.   DBG_PL(ProcessChunk, "unexpected chunk type (COOKIE_ACK) at %f"),
  1374.     dCurrTime DBG_PR;
  1375.   printf("[ProcessChunk] unexpected chunk type (COOKIE_ACK) at %fn",
  1376.  dCurrTime);
  1377.   break;
  1378. default:
  1379.   ProcessOptionChunk(ucpInChunk);
  1380.   break;
  1381. }
  1382.       break;
  1383.     case SCTP_STATE_UNINITIALIZED:
  1384.     case SCTP_STATE_SHUTDOWN_SENT:
  1385.     case SCTP_STATE_SHUTDOWN_RECEIVED:
  1386.     case SCTP_STATE_SHUTDOWN_ACK_SENT:
  1387.     case SCTP_STATE_SHUTDOWN_PENDING:
  1388.       break;
  1389.     }  
  1390.   DBG_X(ProcessChunk);
  1391.   return iThisOutDataSize;
  1392. }
  1393. /* Moves the pointer to the next chunk.
  1394.  * If there is no next chunk, then sent pointer to NULL. 
  1395.  */
  1396. void SctpAgent::NextChunk(u_char **ucpChunk, int *ipRemainingDataLen)
  1397. {
  1398.   DBG_I(NextChunk);
  1399.   unsigned long ulSize = ((SctpChunkHdr_S *) *ucpChunk)->usLength;
  1400.   /* the chunk length field does not include the padded bytes, so we need to
  1401.    * account for these extra bytes.
  1402.    */
  1403.   if( (ulSize % 4) != 0 ) 
  1404.     ulSize += 4 - (ulSize % 4);
  1405.   *ipRemainingDataLen -= ulSize;
  1406.   if(*ipRemainingDataLen > 0)
  1407.     *ucpChunk += ulSize;
  1408.   else
  1409.     *ucpChunk = NULL;
  1410.   DBG_X(NextChunk);
  1411. }
  1412. /* Return the next active destination based on the destination passed in by 
  1413.  * using round robin algorithm for selection. It is possible that this function
  1414.  * will return the same destination passed in. This will happen if (1) there is
  1415.  * only one destination and/or (2) all other destinations are inactive.
  1416.  */
  1417. SctpDest_S *SctpAgent::GetNextDest(SctpDest_S *spDest)
  1418. {
  1419.   DBG_I(GetNextDest);
  1420.   DBG_PL(GetNextDest, "previously dest = %p"), spDest DBG_PR;
  1421.   Node_S *spCurrDestNode = NULL;
  1422.   Node_S *spNextDestNode = NULL;
  1423.   SctpDest_S *spNextDestNodeData = NULL;
  1424.   /* find spDest before we can pick next
  1425.    */
  1426.   for(spCurrDestNode = sDestList.spHead;
  1427.       spCurrDestNode != NULL;
  1428.       spCurrDestNode = spCurrDestNode->spNext)
  1429.     {
  1430.       if(spCurrDestNode->vpData == spDest)
  1431. break; // found it, so jump out!
  1432.     }
  1433.   assert(spCurrDestNode != NULL); // if NULL, means spDest wasn't found :-/
  1434.   /* let's get the next ACTIVE destination. stop if loops wraps around to 
  1435.    * current destination.
  1436.    */
  1437.   spNextDestNode = spCurrDestNode;
  1438.   do
  1439.     {
  1440.       spNextDestNode = spNextDestNode->spNext;
  1441.       if(spNextDestNode == NULL)
  1442. spNextDestNode = sDestList.spHead; // wrap around to head of list
  1443.       spNextDestNodeData = (SctpDest_S *) spNextDestNode->vpData;      
  1444.     } while(spNextDestNodeData->eStatus == SCTP_DEST_STATUS_INACTIVE &&
  1445.     spNextDestNode != spCurrDestNode);
  1446.   /* Are we in the dormant state?
  1447.    */
  1448.   if(spNextDestNode == spCurrDestNode &&
  1449.      spNextDestNodeData->eStatus == SCTP_DEST_STATUS_INACTIVE)
  1450.     {
  1451.       switch(eDormantAction)
  1452. {
  1453. case DORMANT_HOP:
  1454.   /*
  1455.    * Simply go to the next destination. We don't care if it is inactive
  1456.    * or if it is the same destination we are already on.
  1457.    */
  1458.   spNextDestNode = spNextDestNode->spNext;
  1459.   if(spNextDestNode == NULL)
  1460.     spNextDestNode = sDestList.spHead; // wrap around to head of list
  1461.   spNextDestNodeData = (SctpDest_S *) spNextDestNode->vpData;      
  1462.   break;
  1463. case DORMANT_PRIMARY:
  1464.   spNextDestNodeData = spPrimaryDest;
  1465.   break;
  1466. case DORMANT_LASTDEST:  
  1467.   /*
  1468.    * Nothing to do. Since we just want to stay at the last destination
  1469.    * used before dormant state, then the loop above does that for us
  1470.    * already.
  1471.    */
  1472.   break;
  1473. }
  1474.     }
  1475.   DBG_PL(GetNextDest, "next dest = %p"), spNextDestNodeData DBG_PR;
  1476.   DBG_X(GetNextDest);
  1477.   return spNextDestNodeData;
  1478. }
  1479. /* section 8.3 (with implementer's guide changes) - On an idle destination
  1480.  * address that is allowed to heartbeat, a HEARTBEAT chunk is RECOMMENDED
  1481.  * to be sent once per RTO of that destination address plus the protocol
  1482.  * parameter 'HB.interval' , with jittering of +/- 50% of the RTO value
  1483.  */
  1484. double SctpAgent::CalcHeartbeatTime(double dRto)
  1485. {
  1486.   DBG_I(CalcHeartbeatTime);
  1487.   double dRetVal = 0;
  1488.   double dCurrTime = Scheduler::instance().clock();
  1489.   dRetVal = dRto + uiHeartbeatInterval;
  1490.   dRetVal += Random::uniform(dRto);
  1491.   dRetVal -= dRto/2;
  1492.   DBG_PL(CalcHeartbeatTime, "next HEARTBEAT interval in %f secs"), dRetVal
  1493.     DBG_PR;
  1494.   DBG_PL(CalcHeartbeatTime, "next HEARTBEAT interval at %f"), 
  1495.     dRetVal+dCurrTime DBG_PR;
  1496.   DBG_X(CalcHeartbeatTime);
  1497.   return dRetVal;
  1498. }
  1499. /* Sets the source addr, port, and target based on the destination information.
  1500.  * The SctpDest_S holds an already formed packet with the destination's
  1501.  * dest addr & port in the header. With this packet, we can find the correct
  1502.  * src addr, port, and target.  */
  1503. void SctpAgent::SetSource(SctpDest_S *spDest)
  1504. {
  1505.   DBG_I(SetSource);
  1506.   /* dynamically pick the route only if 2 conditions hold true:
  1507.    *    1. we are not forcing the source address and interface. 
  1508.    *    2. there exists a "multihome core" (ie, the agent is multihomed)
  1509.    *
  1510.    * otherwise, use the values are already preset
  1511.    */
  1512.   if(eForceSource == FALSE && opCoreTarget != NULL)
  1513.     {
  1514.       Node_S *spCurrNode = sInterfaceList.spHead;
  1515.       SctpInterface_S *spCurrInterface = NULL;
  1516.       /* find the connector (link) to use from the "routing table" of the core
  1517.        */
  1518.       Connector *connector 
  1519. = (Connector *) opCoreTarget->find(spDest->opRoutingAssistPacket);
  1520.       while(spCurrNode != NULL)
  1521. {
  1522.   spCurrInterface = (SctpInterface_S *) spCurrNode->vpData;
  1523.   
  1524.   if(spCurrInterface->opLink == connector)
  1525.     {
  1526.       addr() = spCurrInterface->iNsAddr;
  1527.       port() = spCurrInterface->iNsPort;
  1528.       target_ = spCurrInterface->opTarget;
  1529.       break;
  1530.     }
  1531.   else
  1532.     spCurrNode = spCurrNode->spNext;
  1533. }
  1534.     }
  1535.   DBG_PL(SetSource, "(%d:%d)"), addr(), port() DBG_PR;
  1536.   DBG_X(SetSource);
  1537. }
  1538. void SctpAgent::SetDestination(SctpDest_S *spDest)
  1539. {
  1540.   DBG_I(SetDestination);
  1541.   daddr() = spDest->iNsAddr;
  1542.   dport() = spDest->iNsPort;
  1543.   DBG_PL(SetDestination, "(%d:%d)"), daddr(), dport() DBG_PR;
  1544.   DBG_X(SetDestination);
  1545. }
  1546. void SctpAgent::SendPacket(u_char *ucpData, int iDataSize, SctpDest_S *spDest)
  1547. {
  1548.   DBG_I(SendPacket);
  1549.   DBG_PL(SendPacket, "spDest=%p (%d:%d)"), 
  1550.     spDest, spDest->iNsAddr, spDest->iNsPort DBG_PR;
  1551.   DBG_PL(SendPacket, "iDataSize=%d  uiNumChunks=%d"), 
  1552.     iDataSize, uiNumChunks DBG_PR;
  1553.   Node_S *spNewNode = NULL;
  1554.   Packet *opPacket = NULL;
  1555.   PacketData *opPacketData = NULL;
  1556.   SetSource(spDest); // set src addr, port, target based on "routing table"
  1557.   SetDestination(spDest); // set dest addr & port 
  1558.   opPacket = allocpkt();
  1559.   opPacketData = new PacketData(iDataSize);
  1560.   memcpy(opPacketData->data(), ucpData, iDataSize);
  1561.   opPacket->setdata(opPacketData);
  1562.   hdr_cmn::access(opPacket)->size() = iDataSize + SCTP_HDR_SIZE+uiIpHeaderSize;
  1563.   hdr_sctp::access(opPacket)->NumChunks() = uiNumChunks;
  1564.   hdr_sctp::access(opPacket)->SctpTrace() = new SctpTrace_S[uiNumChunks];
  1565.   memcpy(hdr_sctp::access(opPacket)->SctpTrace(), spSctpTrace, 
  1566.  (uiNumChunks * sizeof(SctpTrace_S)) );
  1567.   uiNumChunks = 0; // reset the counter
  1568.   if(dRouteCalcDelay == 0) // simulating reactive routing overheads?
  1569.     {
  1570.       send(opPacket, 0); // no... so send immediately
  1571.     }
  1572.   else
  1573.     {
  1574.       if(spDest->eRouteCached == TRUE)  
  1575. {
  1576.   /* Since the route is "cached" already, we can reschedule the route
  1577.    * flushing and send the packet immediately.
  1578.    */
  1579.   spDest->opRouteCacheFlushTimer->resched(dRouteCacheLifetime);
  1580.   send(opPacket, 0);
  1581. }
  1582.       else
  1583. {
  1584.   /* The route isn't cached, so queue the packet and "calculate" the 
  1585.    * route.
  1586.    */
  1587.   spNewNode = new Node_S;
  1588.   spNewNode->eType = NODE_TYPE_PACKET_BUFFER;
  1589.   spNewNode->vpData = opPacket;
  1590.   InsertNode(&spDest->sBufferedPackets,spDest->sBufferedPackets.spTail,
  1591.      spNewNode, NULL);
  1592.   if(spDest->opRouteCalcDelayTimer->status() != TIMER_PENDING)
  1593.     spDest->opRouteCalcDelayTimer->sched(dRouteCalcDelay);
  1594. }
  1595.     }
  1596.   DBG_X(SendPacket);
  1597. }
  1598. SctpDest_S *SctpAgent::GetReplyDestination(hdr_ip *spIpHdr)
  1599. {
  1600.   Node_S *spCurrNode = NULL;
  1601.   SctpDest_S *spDest = NULL;
  1602.   int iAddr = spIpHdr->saddr();
  1603.   int iPort = spIpHdr->sport();
  1604.   
  1605.   for(spCurrNode = sDestList.spHead;
  1606.       spCurrNode != NULL;
  1607.       spCurrNode = spCurrNode->spNext)
  1608.     {
  1609.       spDest = (SctpDest_S *) spCurrNode->vpData;
  1610.       if(spDest->iNsAddr == iAddr && spDest->iNsPort == iPort)
  1611. return spDest;
  1612.     }
  1613.   
  1614.   return NULL;  // we should never get here!
  1615. }
  1616. void SctpAgent::recv(Packet *opInPkt, Handler*)
  1617. {
  1618.   /* Let's make sure that a Reset() is called, because it isn't always
  1619.    * called explicitly with the "reset" command. For example, wireless
  1620.    * nodes don't automatically "reset" their agents, but wired nodes do. 
  1621.    */
  1622.   if(eState == SCTP_STATE_UNINITIALIZED)
  1623.     Reset(); 
  1624.   DBG_I(recv);
  1625.   hdr_ip *spIpHdr = hdr_ip::access(opInPkt);
  1626.   PacketData *opInPacketData = (PacketData *) opInPkt->userdata();
  1627.   u_char *ucpInData = opInPacketData->data();
  1628.   u_char *ucpCurrInChunk = ucpInData;
  1629.   int iRemainingDataLen = opInPacketData->size();
  1630.   u_char *ucpOutData = new u_char[uiMaxPayloadSize];
  1631.   u_char *ucpCurrOutData = ucpOutData;
  1632.   /* local variable which maintains how much data has been filled in the 
  1633.    * current outgoing packet
  1634.    */
  1635.   int iOutDataSize = 0; 
  1636.   memset(ucpOutData, 0, uiMaxPayloadSize);
  1637.   memset(spSctpTrace, 0,
  1638.  (uiMaxPayloadSize / sizeof(SctpChunkHdr_S)) * sizeof(SctpTrace_S) );
  1639.   spReplyDest = GetReplyDestination(spIpHdr);
  1640.   eStartOfPacket = TRUE;
  1641.   do
  1642.     {
  1643.       DBG_PL(recv, "iRemainingDataLen=%d"), iRemainingDataLen DBG_PR;
  1644.       /* processing chunks may need to generate response chunks, so the
  1645.        * current outgoing packet *may* be filled in and our out packet's data
  1646.        * size is incremented to reflect the new data
  1647.        */
  1648.       iOutDataSize += ProcessChunk(ucpCurrInChunk, &ucpCurrOutData);
  1649.       NextChunk(&ucpCurrInChunk, &iRemainingDataLen);
  1650.     }
  1651.   while(ucpCurrInChunk != NULL);
  1652.   /* Let's see if we have any response chunks(currently only handshake related)
  1653.    * to transmit. 
  1654.    *
  1655.    * Note: We don't bundle these responses (yet!)
  1656.    */
  1657.   if(iOutDataSize > 0) 
  1658.     {
  1659.       SendPacket(ucpOutData, iOutDataSize, spReplyDest);
  1660.       DBG_PL(recv, "responded with control chunk(s)") DBG_PR;
  1661.     }
  1662.   /* Let's check to see if we need to generate and send a SACK chunk.
  1663.    *
  1664.    * Note: With uni-directional traffic, SACK and DATA chunks will not be 
  1665.    * bundled together in one packet.
  1666.    * Perhaps we will implement this in the future?  
  1667.    */
  1668.   if(eSackChunkNeeded == TRUE)
  1669.     {
  1670.       memset(ucpOutData, 0, uiMaxPayloadSize);
  1671.       iOutDataSize = BundleControlChunks(ucpOutData);
  1672.       iOutDataSize += GenChunk(SCTP_CHUNK_SACK, ucpOutData+iOutDataSize);
  1673.       SendPacket(ucpOutData, iOutDataSize, spReplyDest);
  1674.       DBG_PL(recv, "SACK sent (%d bytes)"), iOutDataSize DBG_PR;
  1675.       eSackChunkNeeded = FALSE;  // reset AFTER sent (o/w breaks dependencies)
  1676.     }
  1677.   /* Do we need to transmit a FORWARD TSN chunk??
  1678.    */
  1679.   if(eForwardTsnNeeded == TRUE)
  1680.     {
  1681.       memset(ucpOutData, 0, uiMaxPayloadSize);
  1682.       iOutDataSize = BundleControlChunks(ucpOutData);
  1683.       iOutDataSize += GenChunk(SCTP_CHUNK_FORWARD_TSN,ucpOutData+iOutDataSize);
  1684.       SendPacket(ucpOutData, iOutDataSize, spNewTxDest);
  1685.       DBG_PL(recv, "FORWARD TSN chunk sent") DBG_PR;
  1686.       eForwardTsnNeeded = FALSE; // reset AFTER sent (o/w breaks dependencies)
  1687.     }
  1688.   /* Do we want to send out new DATA chunks in addition to whatever we may have
  1689.    * already transmitted? If so, we can only send new DATA if no marked chunks
  1690.    * are pending retransmission.
  1691.    * 
  1692.    * Note: We aren't bundling with what was sent above, but we could. Just 
  1693.    * avoiding that for now... why? simplicity :-)
  1694.    */
  1695.   if(eSendNewDataChunks == TRUE && eMarkedChunksPending == FALSE) 
  1696.     {
  1697.       SendMuch();       // Send new data till our cwnd is full!
  1698.       eSendNewDataChunks = FALSE; // reset AFTER sent (o/w breaks dependencies)
  1699.     }
  1700.   delete hdr_sctp::access(opInPkt)->SctpTrace();
  1701.   hdr_sctp::access(opInPkt)->SctpTrace() = NULL;
  1702.   Packet::free(opInPkt);
  1703.   opInPkt = NULL;
  1704.   DBG_X(recv);
  1705.   delete [] ucpOutData;
  1706. }
  1707. u_int SctpAgent::TotalOutstanding()
  1708. {
  1709.   DBG_I(TotalOutstanding);
  1710.   Node_S *spCurrNode = NULL;
  1711.   SctpDest_S *spCurrDest = NULL;
  1712.   u_int uiTotalOutstanding = 0;
  1713.   for(spCurrNode = sDestList.spHead;
  1714.       spCurrNode != NULL;
  1715.       spCurrNode = spCurrNode->spNext)
  1716.     {
  1717.       spCurrDest = (SctpDest_S *) spCurrNode->vpData;
  1718.       uiTotalOutstanding += spCurrDest->iOutstandingBytes;
  1719.       DBG_PL(TotalOutstanding, "spCurrDest->iOutstandingBytes=%lu"),
  1720. spCurrDest->iOutstandingBytes DBG_PR;
  1721.     }
  1722.   DBG_PL(TotalOutstanding, "%lu"), uiTotalOutstanding DBG_PR;
  1723.   DBG_X(TotalOutstanding);
  1724.   return uiTotalOutstanding;
  1725. }
  1726. void SctpAgent::SendMuch()
  1727. {
  1728.   DBG_I(SendMuch);
  1729.   DBG_PL(SendMuch, "eDataSource=%s"), 
  1730.     ( (eDataSource == DATA_SOURCE_APPLICATION)
  1731.       ? "DATA_SOURCE_APPLICATION" 
  1732.       : "DATA_SOURCE_INFINITE" )
  1733.     DBG_PR;
  1734.   u_char *ucpOutData = new u_char[uiMaxPayloadSize];
  1735.   int iOutDataSize = 0;
  1736.   double dCurrTime = Scheduler::instance().clock();
  1737.   /* Keep sending out packets until our cwnd is full!  The proposed
  1738.    * correction to RFC2960 section 6.1.B says "The sender may exceed
  1739.    * cwnd by up to (PMTU-1) bytes on a new transmission if the cwnd is
  1740.    * not currently exceeded". Hence, as long as cwnd isn't
  1741.    * full... send another packet.  
  1742.    *
  1743.    * Also, if our data source is the application layer (instead of the infinite
  1744.    * source used for ftp), check if there is any data buffered from the app 
  1745.    * layer. If so, then send as much as we can.
  1746.    */
  1747.   while((spNewTxDest->iOutstandingBytes < spNewTxDest->iCwnd) &&
  1748. (eDataSource == DATA_SOURCE_INFINITE || sAppLayerBuffer.uiLength != 0))
  1749.     {
  1750.       DBG_PL(SendMuch, "uiAdvancedPeerAckPoint=%d uiCumAckPoint=%d"), 
  1751. uiAdvancedPeerAckPoint, uiCumAckPoint DBG_PR;
  1752.       DBG_PL(SendMuch, "uiPeerRwnd=%d"), uiPeerRwnd DBG_PR;
  1753.       DBG_PL(SendMuch, "spNewTxDest->iCwnd=%d"), spNewTxDest->iCwnd DBG_PR;
  1754.       DBG_PL(SendMuch, "spNewTxDest->iPartialBytesAcked=%d"), 
  1755. spNewTxDest->iPartialBytesAcked DBG_PR;
  1756.       DBG_PL(SendMuch, "spNewTxDest->iOutstandingBytes=%d"), 
  1757. spNewTxDest->iOutstandingBytes DBG_PR;
  1758.       DBG_PL(SendMuch, "TotalOutstanding=%lu"), 
  1759. TotalOutstanding() DBG_PR;
  1760.       DBG_PL(SendMuch, "eApplyMaxBurst=%s uiBurstLength=%d"),
  1761. eApplyMaxBurst ? "TRUE" : "FALSE", uiBurstLength DBG_PR;
  1762.       if(GetNextDataChunkSize() <= uiPeerRwnd)
  1763. {
  1764.   /* This addresses the proposed change to RFC2960 section 7.2.4,
  1765.    * regarding using of Max.Burst. We have an option which allows
  1766.    * to control if Max.Burst is applied.
  1767.    */
  1768.   if(eUseMaxBurst == MAX_BURST_USAGE_ON)
  1769.     if( (eApplyMaxBurst == TRUE) && (uiBurstLength++ >= MAX_BURST) )
  1770.       {
  1771. /* we've reached Max.Burst limit, so jump out of loop
  1772.  */
  1773. eApplyMaxBurst = FALSE;// reset before jumping out of loop
  1774. break;
  1775.       }
  1776.   memset(ucpOutData, 0, uiMaxPayloadSize); // reset
  1777.   iOutDataSize = BundleControlChunks(ucpOutData);
  1778.   iOutDataSize += GenMultipleDataChunks(ucpOutData+iOutDataSize, 0);
  1779.   SendPacket(ucpOutData, iOutDataSize, spNewTxDest);
  1780.   DBG_PL(SendMuch, "DATA chunk(s) sent") DBG_PR;
  1781. }
  1782.       else if(TotalOutstanding() == 0)  // section 6.1.A
  1783. {
  1784.   /* probe for a change in peer's rwnd
  1785.    */
  1786.   memset(ucpOutData, 0, uiMaxPayloadSize);
  1787.   iOutDataSize = BundleControlChunks(ucpOutData);
  1788.   iOutDataSize += GenOneDataChunk(ucpOutData+iOutDataSize);
  1789.   SendPacket(ucpOutData, iOutDataSize, spNewTxDest);
  1790.   DBG_PL(SendMuch, "DATA chunk probe sent") DBG_PR;
  1791. }
  1792.       else
  1793. {
  1794.   break;
  1795. }      
  1796.     }
  1797.   if(iOutDataSize > 0)  // did we send anything??
  1798.     {
  1799.       spNewTxDest->opCwndDegradeTimer->resched(spNewTxDest->dRto);
  1800.       if(uiHeartbeatInterval != 0)
  1801. {
  1802.   spNewTxDest->dIdleSince = dCurrTime;
  1803. }
  1804.     }
  1805.   /* this reset MUST happen at the end of the function, because the burst 
  1806.    * measurement is carried over from RtxMarkedChunks() if it was called.
  1807.    */
  1808.   uiBurstLength = 0;
  1809.   DBG_X(SendMuch);
  1810.   delete [] ucpOutData;
  1811. }
  1812. void SctpAgent::sendmsg(int iNumBytes, const char *cpFlags)
  1813. {
  1814.   /* Let's make sure that a Reset() is called, because it isn't always
  1815.    * called explicitly with the "reset" command. For example, wireless
  1816.    * nodes don't automatically "reset" their agents, but wired nodes do. 
  1817.    */
  1818.   if(eState == SCTP_STATE_UNINITIALIZED)
  1819.     Reset();
  1820.   DBG_I(sendmsg);
  1821.   u_char *ucpOutData = new u_char[uiMaxPayloadSize];
  1822.   int iOutDataSize = 0;
  1823.   AppData_S *spAppData = (AppData_S *) cpFlags;
  1824.   Node_S *spNewNode = NULL;
  1825.   int iMsgSize = 0;
  1826.   u_int uiMaxFragSize = uiMaxDataSize - sizeof(SctpDataChunkHdr_S);
  1827.   if(iNumBytes == -1) 
  1828.     eDataSource = DATA_SOURCE_INFINITE;    // Send infinite data
  1829.   else 
  1830.     eDataSource = DATA_SOURCE_APPLICATION; // Send data passed from app
  1831.       
  1832.   if(eDataSource == DATA_SOURCE_APPLICATION) 
  1833.     {
  1834.       if(spAppData != NULL)
  1835. {
  1836.   /* This is an SCTP-aware app!! Anything the app passes down
  1837.    * overrides what we bound from TCL.
  1838.    */
  1839.   DBG_PL (sendmsg, "sctp-aware app: iNumBytes=%d"), iNumBytes DBG_PR;
  1840.   spNewNode = new Node_S;
  1841.   uiNumOutStreams = spAppData->usNumStreams;
  1842.   uiNumUnrelStreams = spAppData->usNumUnreliable;
  1843.   spNewNode->eType = NODE_TYPE_APP_LAYER_BUFFER;
  1844.   spNewNode->vpData = spAppData;
  1845.   InsertNode(&sAppLayerBuffer, sAppLayerBuffer.spTail, spNewNode,NULL);
  1846. }
  1847.       else
  1848. {
  1849.   /* This is NOT an SCTP-aware app!! We rely on TCL-bound variables.
  1850.    */
  1851.   DBG_PL (sendmsg,"non-sctp-aware app: iNumBytes=%d"),iNumBytes DBG_PR;
  1852.   uiNumOutStreams = 1; // non-sctp-aware apps only use 1 stream
  1853.   uiNumUnrelStreams = (uiNumUnrelStreams > 0) ? 1 : 0;
  1854.   /* To support legacy applications and uses such as "ftp send
  1855.    * 12000", we "fragment" the message. _HOWEVER_, this is not
  1856.    * REAL SCTP fragmentation!! We do not maintain the same SSN or
  1857.    * use the B/E bits. Think of this block of code as a shim which
  1858.    * breaks up the message into useable pieces for SCTP. 
  1859.    */
  1860.   for(iMsgSize = iNumBytes; 
  1861.       iMsgSize > 0; 
  1862.       iMsgSize -= MIN(iMsgSize, (int) uiMaxFragSize) )
  1863.     {
  1864.       spNewNode = new Node_S;
  1865.       spNewNode->eType = NODE_TYPE_APP_LAYER_BUFFER;
  1866.       spAppData = new AppData_S;
  1867.       spAppData->usNumStreams = uiNumOutStreams;
  1868.       spAppData->usNumUnreliable = uiNumUnrelStreams;
  1869.       spAppData->usStreamId = 0;  
  1870.       spAppData->usReliability = uiReliability;
  1871.       spAppData->eUnordered = eUnordered;
  1872.       spAppData->uiNumBytes = MIN(iMsgSize, (int) uiMaxFragSize);
  1873.       spNewNode->vpData = spAppData;
  1874.       InsertNode(&sAppLayerBuffer, sAppLayerBuffer.spTail, 
  1875.  spNewNode, NULL);
  1876.     }
  1877. }      
  1878.       if(uiNumOutStreams > MAX_NUM_STREAMS)
  1879. {
  1880.   fprintf(stderr, "%s number of streams (%d) > max (%d)n",
  1881.   "SCTP ERROR:",
  1882.   uiNumOutStreams, MAX_NUM_STREAMS);
  1883.   DBG_PL(sendmsg, "ERROR: number of streams (%d) > max (%d)"), 
  1884.     uiNumOutStreams, MAX_NUM_STREAMS DBG_PR;
  1885.   DBG_PL(sendmsg, "exiting...") DBG_PR;
  1886.   exit(-1);
  1887. }
  1888.       else if(uiNumUnrelStreams > uiNumOutStreams)
  1889. {
  1890.   fprintf(stderr,"%s number of unreliable streams (%d) > total (%d)n",
  1891.   "SCTP ERROR:",
  1892.   uiNumUnrelStreams, uiNumOutStreams);
  1893.   DBG_PL(sendmsg, 
  1894.  "ERROR: number of unreliable streams (%d) > total (%d)"), 
  1895.     uiNumUnrelStreams, uiNumOutStreams DBG_PR;
  1896.   DBG_PL(sendmsg, "exiting...") DBG_PR;
  1897.   exit(-1);
  1898. }
  1899.       if(spAppData->uiNumBytes + sizeof(SctpDataChunkHdr_S) 
  1900.  > MAX_DATA_CHUNK_SIZE)
  1901. {
  1902.   fprintf(stderr, "SCTP ERROR: message size (%d) too bign",
  1903.   spAppData->uiNumBytes);
  1904.   fprintf(stderr, "%s data chunk size (%d) > max (%d)n",
  1905.   "SCTP ERROR:",
  1906.   (int) (spAppData->uiNumBytes + sizeof(SctpDataChunkHdr_S)), 
  1907.   MAX_DATA_CHUNK_SIZE);
  1908.   DBG_PL(sendmsg, "ERROR: message size (%d) too big"),
  1909.  spAppData->uiNumBytes DBG_PR;
  1910.   DBG_PL(sendmsg, "ERROR: data chunk size (%d) > max (%d)"), 
  1911.     spAppData->uiNumBytes + sizeof(SctpDataChunkHdr_S), 
  1912.     MAX_DATA_CHUNK_SIZE 
  1913.     DBG_PR;
  1914.   DBG_PL(sendmsg, "exiting...") DBG_PR;
  1915.   exit(-1);
  1916. }
  1917.       else if(spAppData->uiNumBytes + sizeof(SctpDataChunkHdr_S)
  1918.       > uiMaxDataSize)
  1919. {
  1920.   fprintf(stderr, "SCTP ERROR: message size (%d) too bign",
  1921.   spAppData->uiNumBytes);
  1922.   fprintf(stderr, 
  1923.   "%s data chunk size (%d) + SCTP/IP header(%d) > MTU (%d)n",
  1924.   "SCTP ERROR:",
  1925.   (int) (spAppData->uiNumBytes + sizeof(SctpDataChunkHdr_S)),
  1926.   SCTP_HDR_SIZE + uiIpHeaderSize, uiMtu);
  1927.   fprintf(stderr, "           %sn",
  1928.   "...chunk fragmentation is not yet supported!");
  1929.   DBG_PL(sendmsg, "ERROR: message size (%d) too big"),
  1930.  spAppData->uiNumBytes DBG_PR;
  1931.   DBG_PL(sendmsg, "exiting...") DBG_PR;
  1932.   exit(-1);
  1933. }
  1934.     }
  1935.   switch(eState)
  1936.     {
  1937.     case SCTP_STATE_CLOSED:
  1938.       DBG_PL(sendmsg, "sending INIT") DBG_PR;
  1939.       /* This must be done especially since some of the legacy apps use their
  1940.        * own packet type (don't ask me why). We need our packet type to be
  1941.        * sctp so that our tracing output comes out correctly for scripts, etc
  1942.        */
  1943.       set_pkttype(PT_SCTP); 
  1944.       iOutDataSize = GenChunk(SCTP_CHUNK_INIT, ucpOutData);
  1945.       opT1InitTimer->resched(spPrimaryDest->dRto);
  1946.       eState = SCTP_STATE_COOKIE_WAIT;
  1947.       SendPacket(ucpOutData, iOutDataSize, spPrimaryDest);
  1948.       break;
  1949.       
  1950.     case SCTP_STATE_ESTABLISHED:
  1951.       if(eDataSource == DATA_SOURCE_APPLICATION) 
  1952. {
  1953.   SendMuch();   
  1954. }
  1955.       else if(eDataSource == DATA_SOURCE_INFINITE)
  1956. {
  1957.   fprintf(stderr, "[sendmsg] ERROR: unexpected state... %sn",
  1958.   "sendmsg called more than once for infinite data");
  1959.   DBG_PL(sendmsg, 
  1960.  "ERROR: unexpected state... %s"), 
  1961.     "sendmsg called more than once for infinite data" DBG_PR;
  1962.   DBG_PL(sendmsg, "exiting...") DBG_PR;
  1963.   exit(-1);
  1964. }
  1965.       break;
  1966.       
  1967.     default:  
  1968.       /* If we are here, we assume the application is trying to send data
  1969.        * before the 4-way handshake has completed. ...so buffering the
  1970.        * data is ok, but DON'T send it yet!!  
  1971.        */
  1972.       break;
  1973.     }
  1974.   DBG_X(sendmsg);
  1975.   delete [] ucpOutData;
  1976. }
  1977. void SctpAgent::T1InitTimerExpiration()
  1978. {
  1979.   DBG_I(T1InitTimerExpiration);
  1980.   u_char *ucpOutData = new u_char[uiMaxPayloadSize];
  1981.   int iOutDataSize = 0;
  1982.   iInitTryCount++;
  1983.   if(iInitTryCount > (int) uiMaxInitRetransmits)
  1984.     {
  1985.       Close();
  1986.     }
  1987.   else 
  1988.     {
  1989.       spPrimaryDest->dRto *= 2;
  1990.       if(spPrimaryDest->dRto > dMaxRto)
  1991. spPrimaryDest->dRto = dMaxRto;
  1992.       tdRto++;              // trigger changes for trace to pick up
  1993.       iOutDataSize = GenChunk(SCTP_CHUNK_INIT, ucpOutData);
  1994.       SendPacket(ucpOutData, iOutDataSize, spPrimaryDest);
  1995.       opT1InitTimer->resched(spPrimaryDest->dRto);
  1996.     }
  1997.   DBG_X(T1InitTimerExpiration);
  1998.   delete [] ucpOutData;
  1999. }
  2000. void T1InitTimer::expire(Event*)
  2001. {
  2002.   opAgent->T1InitTimerExpiration();
  2003. }
  2004. void SctpAgent::T1CookieTimerExpiration()
  2005. {
  2006.   DBG_I(T1CookieTimerExpiration);
  2007.   u_char *ucpOutData = new u_char[uiMaxPayloadSize];
  2008.   int iOutDataSize = 0;
  2009.  
  2010.   iInitTryCount++;
  2011.   if(iInitTryCount > (int) uiMaxInitRetransmits)
  2012.     Close();
  2013.   else 
  2014.     {
  2015.       spPrimaryDest->dRto *= 2;
  2016.       if(spPrimaryDest->dRto > dMaxRto)
  2017. spPrimaryDest->dRto = dMaxRto;
  2018.       tdRto++;              // trigger changes for trace to pick up
  2019.       iOutDataSize = GenChunk(SCTP_CHUNK_COOKIE_ECHO, ucpOutData);
  2020.       SendPacket(ucpOutData, iOutDataSize, spPrimaryDest);
  2021.       opT1CookieTimer->resched(spPrimaryDest->dRto);
  2022.   }
  2023.   DBG_X(T1CookieTimerExpiration);
  2024.   delete [] ucpOutData;
  2025. }
  2026. void T1CookieTimer::expire(Event*)
  2027. {
  2028.   opAgent->T1CookieTimerExpiration();
  2029. }
  2030. void SctpAgent::Close()
  2031. {
  2032.   DBG_I(Close);
  2033.   Node_S *spCurrNode = NULL;
  2034.   SctpDest_S *spCurrDest = NULL;
  2035.   eState = SCTP_STATE_CLOSED; 
  2036.   
  2037.   /* stop all timers
  2038.    */
  2039.   opT1InitTimer->force_cancel();
  2040.   opT1CookieTimer->force_cancel();
  2041.   opSackGenTimer->force_cancel();
  2042.   if(uiHeartbeatInterval != 0)
  2043.     {
  2044.       opHeartbeatGenTimer->force_cancel();
  2045.     }
  2046.   for(spCurrNode = sDestList.spHead;
  2047.       spCurrNode != NULL;
  2048.       spCurrNode = spCurrNode->spNext)
  2049.     {
  2050.       spCurrDest = (SctpDest_S *) spCurrNode->vpData;
  2051.       spCurrDest->opT3RtxTimer->force_cancel();
  2052.       spCurrDest->opCwndDegradeTimer->force_cancel();
  2053.       spCurrDest->opHeartbeatTimeoutTimer->force_cancel();
  2054.     }
  2055.   ClearList(&sSendBuffer);
  2056.   ClearList(&sAppLayerBuffer);
  2057.   ClearList(&sRecvTsnBlockList);
  2058.   ClearList(&sDupTsnList);
  2059.   DBG_X(Close);
  2060. }
  2061. /* Handles timeouts for both DATA chunks and HEARTBEAT chunks. The actions are
  2062.  * slightly different for each. Covers rfc sections 6.3.3 and 8.3.
  2063.  */
  2064. void SctpAgent::Timeout(SctpChunkType_E eChunkType, SctpDest_S *spDest)
  2065. {
  2066.   DBG_I(Timeout);
  2067.   DBG_PL(Timeout, "eChunkType=%s spDest=%p"), 
  2068.     (eChunkType == SCTP_CHUNK_DATA) ? "DATA" : "HEARTBEAT",
  2069.     spDest
  2070.     DBG_PR;
  2071.   double dCurrTime = Scheduler::instance().clock();
  2072.   DBG_PL(Timeout, "dCurrTime=%f"), dCurrTime DBG_PR;
  2073.   if(eChunkType == SCTP_CHUNK_DATA)
  2074.     {
  2075.       spDest->eRtxTimerIsRunning = FALSE;
  2076.       
  2077.       /* section 7.2.3 of rfc2960 (w/ implementor's guide)
  2078.        * NE: 4/29/2007 - This conditioal is used for some reason but for 
  2079.        * now we dont know why. 
  2080.        */
  2081.       if(spDest->iCwnd > 1 * (int) uiMaxDataSize)
  2082. {
  2083.   spDest->iSsthresh 
  2084.     = MAX(spDest->iCwnd/2, iInitialCwnd * (int) uiMaxDataSize);
  2085.   spDest->iCwnd = 1*uiMaxDataSize;
  2086.   spDest->iPartialBytesAcked = 0; // reset
  2087.   tiCwnd++; // trigger changes for trace to pick up
  2088. }
  2089.       spDest->opCwndDegradeTimer->force_cancel();
  2090.       /* Cancel any pending RTT measurement on this destination. Stephan
  2091.        * Baucke suggested (2004-04-27) this action as a fix for the
  2092.        * following simple scenario:
  2093.        *
  2094.        * - Host A sends packets 1, 2 and 3 to host B, and choses 3 for
  2095.        *   an RTT measurement
  2096.        *
  2097.        * - Host B receives all packets correctly and sends ACK1, ACK2,
  2098.        *   and ACK3.
  2099.        *
  2100.        * - ACK2 and ACK3 are lost on the return path
  2101.        *
  2102.        * - Eventually a timeout fires for packet 2, and A retransmits 2
  2103.        *
  2104.        * - Upon receipt of 2, B sends a cumulative ACK3 (since it has
  2105.        *   received 2 & 3 before)
  2106.        *
  2107.        * - Since packet 3 has never been retransmitted, the SCTP code
  2108.        *   actually accepts the ACK for an RTT measurement, although it
  2109.        *   was sent in reply to the retransmission of 2, which results
  2110.        *   in a much too high RTT estimate. Since this case tends to
  2111.        *   happen in case of longer link interruptions, the error is
  2112.        *   often amplified by subsequent timer backoffs.
  2113.        */
  2114.       spDest->eRtoPending = FALSE; // cancel any pending RTT measurement
  2115.     }
  2116.   DBG_PL(Timeout, "was spDest->dRto=%f"), spDest->dRto DBG_PR;
  2117.   spDest->dRto *= 2;    // back off the timer
  2118.   if(spDest->dRto > dMaxRto)
  2119.     spDest->dRto = dMaxRto;
  2120.   tdRto++;              // trigger changes for trace to pick up
  2121.   DBG_PL(Timeout, "now spDest->dRto=%f"), spDest->dRto DBG_PR;
  2122.   spDest->iTimeoutCount++;
  2123.   spDest->iErrorCount++; // @@@ window probe timeouts should not be counted
  2124.   DBG_PL(Timeout, "now spDest->iErrorCount=%d"), spDest->iErrorCount DBG_PR;
  2125.   if(spDest->eStatus == SCTP_DEST_STATUS_ACTIVE)
  2126.     {  
  2127.       iAssocErrorCount++;
  2128.       DBG_PL(Timeout, "now iAssocErrorCount=%d"), iAssocErrorCount DBG_PR;
  2129.       // Path.Max.Retrans exceeded?
  2130.       if(spDest->iErrorCount > (int) uiPathMaxRetrans) 
  2131. {
  2132.   spDest->eStatus = SCTP_DEST_STATUS_INACTIVE;
  2133.   if(spDest == spNewTxDest)
  2134.     {
  2135.       spNewTxDest = GetNextDest(spDest);
  2136.       DBG_PL(Timeout, "failing over from %p to %p"),
  2137. spDest, spNewTxDest DBG_PR;
  2138.     }
  2139. }
  2140.       if(iAssocErrorCount > (int) uiAssociationMaxRetrans)
  2141. {
  2142.   /* abruptly close the association!  (section 8.1)
  2143.    */
  2144.   DBG_PL(Timeout, "abruptly closing the association!") DBG_PR;
  2145.   Close();
  2146.   DBG_X(Timeout);
  2147.   return;
  2148. }
  2149.     }
  2150.   // trace it!
  2151.   tiTimeoutCount++;
  2152.   tiErrorCount++;       
  2153.   if(spDest->iErrorCount > (int) uiChangePrimaryThresh &&
  2154.      spDest == spPrimaryDest)
  2155.     {
  2156.       spPrimaryDest = spNewTxDest;
  2157.       DBG_PL(Timeout, "changing primary from %p to %p"),
  2158. spDest, spNewTxDest DBG_PR;
  2159.     }
  2160.   if(eChunkType == SCTP_CHUNK_DATA)
  2161.     {
  2162.       TimeoutRtx(spDest);
  2163.       if(spDest->eStatus == SCTP_DEST_STATUS_INACTIVE && 
  2164.  uiHeartbeatInterval!=0)
  2165. SendHeartbeat(spDest);  // just marked inactive, so send HB immediately
  2166.     }
  2167.   else if(eChunkType == SCTP_CHUNK_HB)
  2168.     {
  2169.       if( (uiHeartbeatInterval != 0) ||
  2170.   (spDest->eStatus == SCTP_DEST_STATUS_UNCONFIRMED))
  2171. SendHeartbeat(spDest);
  2172.     }
  2173.   DBG_X(Timeout);  
  2174. }
  2175. void T3RtxTimer::expire(Event*)
  2176. {
  2177.   opAgent->Timeout(SCTP_CHUNK_DATA, spDest);
  2178. }
  2179. void HeartbeatTimeoutTimer::expire(Event*)
  2180. {
  2181.   /* Track HB-Timer for CMT-PF 
  2182.    */
  2183.   spDest->eHBTimerIsRunning = FALSE;
  2184.   opAgent->Timeout(SCTP_CHUNK_HB, spDest);
  2185. }
  2186. void SctpAgent::CwndDegradeTimerExpiration(SctpDest_S *spDest)
  2187. {
  2188.   DBG_I(CwndDegradeTimerExpiration);
  2189.   DBG_PL(CwndDegradeTimerExpiration, "spDest=%p"), spDest DBG_PR;
  2190.   if(spDest->iCwnd > iInitialCwnd * (int) uiMaxDataSize)
  2191.     {
  2192.       spDest->iCwnd = MAX(spDest->iCwnd/2, iInitialCwnd * (int) uiMaxDataSize);
  2193.       tiCwnd++; // trigger changes for trace to pick up
  2194.     }
  2195.   spDest->opCwndDegradeTimer->resched(spDest->dRto);
  2196.   DBG_X(CwndDegradeTimerExpiration);  
  2197. }
  2198. void CwndDegradeTimer::expire(Event*)
  2199. {
  2200.   opAgent->CwndDegradeTimerExpiration(spDest);
  2201. }
  2202. void SctpAgent::SendHeartbeat(SctpDest_S *spDest)
  2203. {
  2204.   DBG_I(SendHeartbeat);
  2205.   DBG_PL(SendHeartbeat, "spDest=%p"), spDest DBG_PR;
  2206.   SctpHeartbeatChunk_S sHeartbeatChunk;
  2207.   double dCurrTime = Scheduler::instance().clock();
  2208.   GenChunk(SCTP_CHUNK_HB, (u_char *) &sHeartbeatChunk); // doesn't fill dest
  2209.   sHeartbeatChunk.spDest = spDest;          // ...so we fill it here :-)
  2210.   SendPacket((u_char *) &sHeartbeatChunk, SCTP_CHUNK_HEARTBEAT_LENGTH, spDest);
  2211.   spDest->dIdleSince = dCurrTime;
  2212.   
  2213.   spDest->opHeartbeatTimeoutTimer->resched(spDest->dRto);
  2214.   
  2215.   /* Track HB-Timer for CMT-PF 
  2216.    */
  2217.   spDest->eHBTimerIsRunning = TRUE;
  2218.   DBG_PL(SendHeartbeat, "HEARTBEAT times out at %f"), 
  2219.     spDest->dRto+dCurrTime DBG_PR;
  2220.   DBG_X(SendHeartbeat);
  2221. }
  2222. void SctpAgent::HeartbeatGenTimerExpiration(double dTimerStartTime)
  2223. {
  2224.   DBG_I(HeartbeatGenTimerExpiration);
  2225.   Node_S *spCurrNode = NULL;
  2226.   SctpDest_S *spCurrNodeData = NULL;
  2227.   Node_S *spLongestIdleNode = NULL;
  2228.   SctpDest_S *spLongestIdleNodeData = NULL;
  2229.   double dCurrTime = Scheduler::instance().clock();
  2230.   double dTime;
  2231.   DBG_PL(HeartbeatGenTimerExpiration, "finding the longest idle dest...") 
  2232.     DBG_PR;
  2233.   /* find the destination which has been idle the longest 
  2234.    */
  2235.   for(spCurrNode = spLongestIdleNode = sDestList.spHead;
  2236.       spCurrNode != NULL;
  2237.       spCurrNode = spCurrNode->spNext)
  2238.     {
  2239.       spCurrNodeData = (SctpDest_S *) spCurrNode->vpData;
  2240.       spLongestIdleNodeData = (SctpDest_S *) spLongestIdleNode->vpData;
  2241.       
  2242.       DBG_PL(HeartbeatGenTimerExpiration, "spDest=%p idle since %f"), 
  2243. spCurrNodeData, spCurrNodeData->dIdleSince DBG_PR;
  2244.       
  2245.       if(spCurrNodeData->dIdleSince < spLongestIdleNodeData->dIdleSince)
  2246. spLongestIdleNode = spCurrNode;
  2247.     }
  2248.       
  2249.   /* has it been idle long enough?
  2250.    */
  2251.   spLongestIdleNodeData = (SctpDest_S *) spLongestIdleNode->vpData;
  2252.   DBG_PL(HeartbeatGenTimerExpiration, "longest idle dest since %f"),
  2253.     spLongestIdleNodeData->dIdleSince DBG_PR;
  2254.   DBG_PL(HeartbeatGenTimerExpiration, "timer start time %f"),
  2255.     dTimerStartTime DBG_PR;
  2256.   if(spLongestIdleNodeData->dIdleSince <= dTimerStartTime)
  2257.     SendHeartbeat(spLongestIdleNodeData);
  2258.   else
  2259.     DBG_PL(HeartbeatGenTimerExpiration, 
  2260.        "longest idle dest not idle long enough!") DBG_PR;
  2261.   
  2262.   /* start the timer again...
  2263.    */
  2264.   dTime = CalcHeartbeatTime(spLongestIdleNodeData->dRto);
  2265.   opHeartbeatGenTimer->resched(dTime);
  2266.   opHeartbeatGenTimer->dStartTime = dCurrTime;
  2267.     
  2268.   DBG_X(HeartbeatGenTimerExpiration);
  2269. }
  2270. void HeartbeatGenTimer::expire(Event*)
  2271. {
  2272.   opAgent->HeartbeatGenTimerExpiration(dStartTime);
  2273. }
  2274. void SctpAgent::SackGenTimerExpiration() // section 6.2
  2275. {
  2276.   DBG_I(SackGenTimerExpiration);
  2277.   u_char *ucpOutData = new u_char[uiMaxPayloadSize];
  2278.   int iOutDataSize = 0;
  2279.   memset(ucpOutData, 0, uiMaxPayloadSize);
  2280.   iDataPktCountSinceLastSack = 0; // reset 
  2281.   iOutDataSize = BundleControlChunks(ucpOutData);
  2282.   iOutDataSize += GenChunk(SCTP_CHUNK_SACK, ucpOutData+iOutDataSize);
  2283.   SendPacket(ucpOutData, iOutDataSize, spReplyDest); 
  2284.   DBG_PL(SackGenTimerExpiration, "SACK sent (%d bytes)"), iOutDataSize DBG_PR;
  2285.   DBG_X(SackGenTimerExpiration);
  2286.   delete [] ucpOutData;
  2287. }
  2288. void SackGenTimer::expire(Event*)
  2289. {
  2290.   opAgent->SackGenTimerExpiration();
  2291. }
  2292. void SctpAgent::RouteCacheFlushTimerExpiration(SctpDest_S *spDest)
  2293. {
  2294.   DBG_I(RouteCacheFlushTimerExpiration);
  2295.   DBG_PL(RouteCacheFlushTimerExpiration, "spDest=%p"), spDest DBG_PR;
  2296.   double dCurrTime = Scheduler::instance().clock();
  2297.   DBG_PL(RouteCacheFlushTimerExpiration, "dCurrTime=%f"), dCurrTime DBG_PR;
  2298.   spDest->eRouteCached = FALSE;
  2299.   DBG_X(RouteCacheFlushTimerExpiration);
  2300. }
  2301. void RouteCacheFlushTimer::expire(Event*)
  2302. {
  2303.   opAgent->RouteCacheFlushTimerExpiration(spDest);
  2304. }
  2305. void SctpAgent::RouteCalcDelayTimerExpiration(SctpDest_S *spDest)
  2306. {
  2307.   DBG_I(RouteCalcDelayTimerExpiration);
  2308.   DBG_PL(RouteCalcDelayTimerExpiration, "spDest=%p"), spDest DBG_PR;
  2309.   double dCurrTime = Scheduler::instance().clock();
  2310.   DBG_PL(RouteCalcDelayTimerExpiration, "dCurrTime=%f"), dCurrTime DBG_PR;
  2311.   Node_S *spCurrNode = spDest->sBufferedPackets.spHead;
  2312.   Node_S *spDeleteNode = NULL;
  2313.   spDest->iRcdCount++;
  2314.   tiRcdCount++;
  2315.   spDest->eRouteCached = TRUE;
  2316.   SetSource(spDest); // set src addr, port, target based on "routing table"
  2317.   SetDestination(spDest); // set dest addr & port 
  2318.   /* Dequeue and send all queued packets
  2319.    */
  2320.   while(spCurrNode != NULL)
  2321.     {
  2322.       send( (Packet *) spCurrNode->vpData, 0);
  2323.       spDeleteNode = spCurrNode;
  2324.       spCurrNode = spCurrNode->spNext;
  2325.       DeleteNode(&spDest->sBufferedPackets, spDeleteNode);
  2326.     }
  2327.   DBG_X(RouteCalcDelayTimerExpiration);
  2328. }
  2329. void RouteCalcDelayTimer::expire(Event*)
  2330. {
  2331.   opAgent->RouteCalcDelayTimerExpiration(spDest);
  2332. }
  2333. /****************************************************************************
  2334.  * debugging functions
  2335.  ****************************************************************************/
  2336. void SctpAgent::DumpSendBuffer()
  2337. {
  2338.   DBG_IF(DumpSendBuffer)
  2339.     {
  2340.       DBG_I(DumpSendBuffer);
  2341.       Node_S *spCurrNode = NULL;
  2342.       SctpSendBufferNode_S *spCurrNodeData = NULL;
  2343.       SctpDataChunkHdr_S  *spChunk = NULL;
  2344.       for(spCurrNode = sSendBuffer.spHead;
  2345.   spCurrNode != NULL;
  2346.   spCurrNode = spCurrNode->spNext)
  2347. {
  2348.   spCurrNodeData = (SctpSendBufferNode_S *) spCurrNode->vpData;
  2349.   spChunk = spCurrNodeData->spChunk;
  2350.   DBG_PL(DumpSendBuffer, 
  2351.  "tsn=%d spDest=%p eMarkedForRtx=%s %s %s"),
  2352.     spChunk->uiTsn, 
  2353.     spCurrNodeData->spDest,
  2354.     (!spCurrNodeData->eMarkedForRtx ? "NO_RTX" 
  2355.      : (spCurrNodeData->eMarkedForRtx==FAST_RTX ? "FAST_RTX"
  2356. : "TIMEOUT_RTX")),
  2357.     spCurrNodeData->eMarkedForRtx ? "MarkedForRtx" : "",
  2358.     spCurrNodeData->eGapAcked ? "GapAcked" : "",
  2359.     spCurrNodeData->eAdvancedAcked ? "AdvancedAcked" : ""
  2360.     DBG_PR;
  2361.   DBG_PL(DumpSendBuffer, 
  2362.  "       spCurrNodeData->spDest->iOutstandingBytes=%lu"),
  2363.     spCurrNodeData->spDest->iOutstandingBytes DBG_PR;
  2364. }
  2365.       DBG_X(DumpSendBuffer);
  2366.     }
  2367. }