sctp.cc
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:164k
- {
- DBG_PL(UpdateHighestTsn, "returning FALSE") DBG_PR;
- DBG_X(UpdateHighestTsn);
- return FALSE; /* no update of highest */
- }
- }
- /* Determines whether a chunk is duplicate or not.
- */
- Boolean_E SctpAgent::IsDuplicateChunk(u_int uiTsn)
- {
- DBG_I(IsDuplicateChunk);
- Node_S *spCurrNode = NULL;
- /* Assume highest have already been updated
- */
- if(uiTsn <= uiCumAck)
- {
- DBG_PL(IsDuplicateChunk, "returning TRUE") DBG_PR;
- DBG_X(IsDuplicateChunk);
- return TRUE;
- }
- if( !((uiCumAck < uiTsn) && (uiTsn <= uiHighestRecvTsn)) )
- {
- DBG_PL(IsDuplicateChunk, "returning FALSE") DBG_PR;
- DBG_X(IsDuplicateChunk);
- return FALSE;
- }
- /* Let's see if uiTsn is in the sorted list of recv'd tsns
- */
- if(sRecvTsnBlockList.uiLength == 0)
- {
- DBG_PL(IsDuplicateChunk, "returning FALSE") DBG_PR;
- DBG_X(IsDuplicateChunk);
- return FALSE; /* no frags in list! */
- }
- /* If we get this far, we need to check whether uiTsn is already in the list
- * of received tsns. Simply do a linear search thru the sRecvTsnBlockList.
- */
- for(spCurrNode = sRecvTsnBlockList.spHead;
- spCurrNode != NULL;
- spCurrNode = spCurrNode->spNext)
- {
- if(((SctpRecvTsnBlock_S *)spCurrNode->vpData)->uiStartTsn <= uiTsn &&
- uiTsn <= ((SctpRecvTsnBlock_S *)spCurrNode->vpData)->uiEndTsn )
- {
- DBG_PL(IsDuplicateChunk, "returning TRUE") DBG_PR;
- DBG_X(IsDuplicateChunk);
- return TRUE;
- }
- /* Assuming an ordered list of tsn blocks, don't continue looking if this
- * block ends with a larger tsn than the chunk we currently have.
- */
- if( ((SctpRecvTsnBlock_S *)spCurrNode->vpData)->uiEndTsn > uiTsn )
- {
- DBG_PL(IsDuplicateChunk, "returning FALSE") DBG_PR;
- DBG_X(IsDuplicateChunk);
- return FALSE;
- }
- }
- DBG_PL(IsDuplicateChunk, "returning FALSE") DBG_PR;
- DBG_X(IsDuplicateChunk);
- return FALSE;
- }
- /* Inserts uiTsn into the list of duplicates tsns
- */
- void SctpAgent::InsertDuplicateTsn(u_int uiTsn)
- {
- DBG_I(InsertDuplicateTsn);
- Node_S *spCurrNode = NULL;
- Node_S *spPrevNode = NULL;
- Node_S *spNewNode = NULL;
- u_int uiCurrTsn;
- /* linear search
- */
- for(spPrevNode = NULL, spCurrNode = sDupTsnList.spHead;
- spCurrNode != NULL;
- spPrevNode = spCurrNode, spCurrNode = spCurrNode->spNext)
- {
- uiCurrTsn = ((SctpDupTsn_S *) spCurrNode->vpData)->uiTsn;
- if(uiTsn <= uiCurrTsn)
- break;
- }
- /* If we reached the end of the list
- * OR we found the location in the list where it should go (assuming it
- * isn't already there)... insert it.
- */
- if( (spCurrNode == NULL) || (uiTsn != uiCurrTsn) )
- {
- spNewNode = new Node_S;
- spNewNode->eType = NODE_TYPE_DUP_TSN;
- spNewNode->vpData = new SctpDupTsn_S;
- ((SctpDupTsn_S *) spNewNode->vpData)->uiTsn = uiTsn;
- InsertNode(&sDupTsnList, spPrevNode, spNewNode, spCurrNode);
- }
- DBG_X(InsertDuplicateTsn);
- }
- /* This function updates uiCumAck (a receive variable) to reflect newly arrived
- * data.
- */
- void SctpAgent::UpdateCumAck()
- {
- DBG_I(UpdateCumAck);
- Node_S *spCurrNode = NULL;
- if(sRecvTsnBlockList.uiLength == 0)
- {
- DBG_X(UpdateCumAck);
- return;
- }
- for(spCurrNode = sRecvTsnBlockList.spHead;
- spCurrNode != NULL;
- spCurrNode = spCurrNode->spNext)
- {
- if( uiCumAck+1 == ((SctpRecvTsnBlock_S *)spCurrNode->vpData)->uiStartTsn)
- {
- uiCumAck = ((SctpRecvTsnBlock_S *)spCurrNode->vpData)->uiEndTsn;
- }
- else
- {
- DBG_X(UpdateCumAck);
- return;
- }
- }
- DBG_X(UpdateCumAck);
- }
- /**
- * Helper function to do the correct update the received tsn blocks.
- * This function will also call UpdateCumAck() when necessary.
- */
- void SctpAgent::UpdateRecvTsnBlocks(u_int uiTsn)
- {
- DBG_I(UpdateRecvTsnBlocks);
- u_int uiLow;
- u_int uiHigh;
- u_int uiGapSize;
- Node_S *spCurrNode = NULL;
- Node_S *spPrevNode = NULL;
- Node_S *spNewNode = NULL;
- uiLow = uiCumAck + 1;
- for(spCurrNode = sRecvTsnBlockList.spHead;
- spCurrNode != NULL;
- spPrevNode = spCurrNode, spCurrNode = spCurrNode->spNext)
- {
- uiHigh = ((SctpRecvTsnBlock_S *)spCurrNode->vpData)->uiStartTsn - 1;
-
- /* Does uiTsn fall in the gap?
- */
- if( uiLow <= uiTsn && uiTsn <= uiHigh )
- {
- uiGapSize = uiHigh - uiLow + 1;
- if(uiGapSize > 1) // is the gap too big for one uiTsn to fill?
- {
- /* Does uiTsn border the lower edge of the current tsn block?
- */
- if(uiTsn == uiHigh)
- {
- ((SctpRecvTsnBlock_S *)spCurrNode->vpData)->uiStartTsn
- = uiTsn;
- UpdateCumAck();
- DBG_X(UpdateRecvTsnBlocks);
- return;
- }
- /* Does uiTsn border the left edge of the previous tsn block?
- */
- else if(uiTsn == uiLow)
- {
- if(uiTsn == uiCumAck+1) // can we increment our uiCumAck?
- {
- uiCumAck++;
- UpdateCumAck();
- DBG_X(UpdateRecvTsnBlocks);
- return;
- }
- else // otherwise, move previous tsn block's right edge
- {
- ((SctpRecvTsnBlock_S *)spPrevNode->vpData)->uiEndTsn
- = uiTsn;
- UpdateCumAck();
- DBG_X(UpdateRecvTsnBlocks);
- return;
- }
- }
- /* This uiTsn creates a new tsn block in between uiLow & uiHigh
- */
- else
- {
- spNewNode = new Node_S;
- spNewNode->eType = NODE_TYPE_RECV_TSN_BLOCK;
- spNewNode->vpData = new SctpRecvTsnBlock_S;
- ((SctpRecvTsnBlock_S *)spNewNode->vpData)->uiStartTsn
- = uiTsn;
- ((SctpRecvTsnBlock_S *)spNewNode->vpData)->uiEndTsn = uiTsn;
- InsertNode(&sRecvTsnBlockList,
- spPrevNode, spNewNode, spCurrNode);
- DBG_X(UpdateRecvTsnBlocks);
- return; // no UpdateCumAck() necessary
- }
- }
- else // uiGapSize == 1
- {
- if(uiLow == uiCumAck+1) // can we adjust our uiCumAck?
- {
- /* delete tsn block
- */
- uiCumAck
- = ((SctpRecvTsnBlock_S *)spCurrNode->vpData)->uiEndTsn;
- DeleteNode(&sRecvTsnBlockList, spCurrNode);
- spCurrNode = NULL;
- UpdateCumAck();
- DBG_X(UpdateRecvTsnBlocks);
- return;
- }
- else // otherwise, move previous tsn block's right edge
- {
- ((SctpRecvTsnBlock_S *)spPrevNode->vpData)->uiEndTsn
- = ((SctpRecvTsnBlock_S *)spCurrNode->vpData)->uiEndTsn;
- DeleteNode(&sRecvTsnBlockList, spCurrNode);
- spCurrNode = NULL;
- UpdateCumAck();
- DBG_X(UpdateRecvTsnBlocks);
- return;
- }
- }
- }
- /* uiTsn is not in the gap between these two tsn blocks, so let's move
- * our "low pointer" to one past the end of the current tsn block and
- * continue
- */
- else
- {
- uiLow = ((SctpRecvTsnBlock_S *)spCurrNode->vpData)->uiEndTsn + 1;
- }
- }
- /* If we get here, then the list is either NULL or the end of the list has
- * been reached
- */
- if(uiTsn == uiLow)
- {
- if(uiTsn == uiCumAck+1) // Can we increment the uiCumAck?
- {
- uiCumAck = uiTsn;
- UpdateCumAck();
- DBG_X(UpdateRecvTsnBlocks);
- return;
- }
-
- /* Update previous tsn block by increasing it's uiEndTsn
- */
- if(spPrevNode != NULL)
- {
- ((SctpRecvTsnBlock_S *) spPrevNode->vpData)->uiEndTsn++;
- }
- DBG_X(UpdateRecvTsnBlocks);
- return; // no UpdateCumAck() necessary
- }
- /* uiTsn creates a new tsn block to go at the end of the sRecvTsnBlockList
- */
- else
- {
- spNewNode = new Node_S;
- spNewNode->eType = NODE_TYPE_RECV_TSN_BLOCK;
- spNewNode->vpData = new SctpRecvTsnBlock_S;
- ((SctpRecvTsnBlock_S *) spNewNode->vpData)->uiStartTsn = uiTsn;
- ((SctpRecvTsnBlock_S *) spNewNode->vpData)->uiEndTsn = uiTsn;
- InsertNode(&sRecvTsnBlockList, spPrevNode, spNewNode, spCurrNode);
- DBG_X(UpdateRecvTsnBlocks);
- return; // no UpdateCumAck() necessary
- }
- }
- /* This function is merely a hook for future implementation and currently does
- * NOT actually pass the data to the upper layer.
- */
- void SctpAgent::PassToUpperLayer(SctpDataChunkHdr_S *spDataChunkHdr)
- {
- DBG_I(PassToUpperLayer);
- DBG_PL(PassToUpperLayer, "tsn=%d"), spDataChunkHdr->uiTsn DBG_PR;
- /* We really don't want to credit the window until the upper layer actually
- * wants the data, but now we'll assume that the application readily
- * consumes incoming chunks immediately.
- */
- uiMyRwnd += spDataChunkHdr->sHdr.usLength;
- tiRwnd++; // trigger changes to be traced
- DBG_PL(PassToUpperLayer, "uiMyRwnd=%d"), uiMyRwnd DBG_PR;
- DBG_X(PassToUpperLayer);
- }
- void SctpAgent::InsertInStreamBuffer(List_S *spBufferedChunkList,
- SctpDataChunkHdr_S *spChunk)
- {
- DBG_I(InsertInStreamBuffer);
- Node_S *spPrevNode;
- Node_S *spCurrNode;
- Node_S *spNewNode;
- SctpStreamBufferNode_S *spCurrNodeData;
- SctpStreamBufferNode_S *spNewNodeData;
- u_short usCurrStreamSeqNum;
- spPrevNode = NULL;
- spCurrNode = spBufferedChunkList->spHead;
- /* linear search through stream sequence #'s
- */
- for(spPrevNode = NULL, spCurrNode = spBufferedChunkList->spHead;
- spCurrNode != NULL;
- spPrevNode = spCurrNode, spCurrNode = spCurrNode->spNext)
- {
- spCurrNodeData = (SctpStreamBufferNode_S *) spCurrNode->vpData;
- usCurrStreamSeqNum = spCurrNodeData->spChunk->usStreamSeqNum;
- /* break out of loop when we find the place to insert our new chunk
- */
- if(spChunk->usStreamSeqNum <= usCurrStreamSeqNum)
- break;
- }
-
- /* If we reached the end of the list OR we found the location in the list
- * where it should go (assuming it isn't already there)... insert it.
- */
- if( (spCurrNode == NULL) || (usCurrStreamSeqNum != spChunk->usStreamSeqNum) )
- {
- spNewNode = new Node_S;
- spNewNode->eType = NODE_TYPE_STREAM_BUFFER;
- spNewNode->vpData = new SctpStreamBufferNode_S;
- spNewNodeData = (SctpStreamBufferNode_S *) spNewNode->vpData;
-
- /* This can NOT simply be a 'new SctpDataChunkHdr_S', because we
- * need to allocate the space for the ENTIRE data chunk and not just
- * the data chunk header.
- */
- spNewNodeData->spChunk
- = (SctpDataChunkHdr_S *) new u_char[spChunk->sHdr.usLength];
- memcpy(spNewNodeData->spChunk, spChunk, spChunk->sHdr.usLength);
- DBG_PL(InsertInStreamBuffer, "vpData=%p spChunk=%p"),
- spNewNodeData, spNewNodeData->spChunk DBG_PR;
- InsertNode(spBufferedChunkList, spPrevNode, spNewNode, spCurrNode);
- }
- DBG_X(InsertInStreamBuffer);
- }
- /* We have not implemented fragmentation, so this function safely assumes all
- * chunks are complete.
- */
- void SctpAgent::PassToStream(SctpDataChunkHdr_S *spChunk)
- {
- DBG_I(PassToStream);
- DBG_PL(PassToStream, "uiMyRwnd=%d"), uiMyRwnd DBG_PR;
- SctpInStream_S *spStream = &(spInStreams[spChunk->usStreamId]);
- if(spChunk->sHdr.ucFlags & SCTP_DATA_FLAG_UNORDERED)
- {
- PassToUpperLayer(spChunk);
- }
- else
- {
- /* We got a numbered chunk (ordered delivery)...
- *
- * We insert the chunk into the corresponding buffer whether or not the
- * chunk's stream seq # is in order or not.
- */
- DBG_PL(PassToStream, "streamId=%d streamSeqNum=%d"),
- spChunk->usStreamId, spChunk->usStreamSeqNum DBG_PR;
- InsertInStreamBuffer( &(spStream->sBufferedChunkList), spChunk);
- }
- DBG_PL(PassToStream, "uiMyRwnd=%d"), uiMyRwnd DBG_PR;
- DBG_X(PassToStream);
- }
- void SctpAgent::UpdateAllStreams()
- {
- DBG_I(UpdateAllStreams);
- DBG_PL(UpdateAllStreams, "uiMyRwnd=%d"), uiMyRwnd DBG_PR;
- Node_S *spCurrNode = NULL;
- Node_S *spDeleteNode = NULL;
- SctpDataChunkHdr_S *spBufferedChunk = NULL;
- SctpInStream_S *spStream = NULL;
- int i;
- for(i = 0; i < iNumInStreams; i++)
- {
- DBG_PL(UpdateAllStreams, "examining stream %d"), i DBG_PR;
- spStream = &(spInStreams[i]);
- /* Start from the lowest stream seq # buffered and sequentially pass
- * up all the chunks which are "deliverable".
- */
- spCurrNode = spStream->sBufferedChunkList.spHead;
- while(spCurrNode != NULL)
- {
- spBufferedChunk
- = ((SctpStreamBufferNode_S *)spCurrNode->vpData)->spChunk;
- /* For unreliable streams... Any waiting tsn which is less than
- * or equal to the cum ack, must be delivered now. We first
- * deliver chunks without even considering the SSNs, because in
- * case of unreliable streams, there may be gaps in the SSNs
- * which we want to ignore.
- */
- if((spStream->eMode == SCTP_STREAM_UNRELIABLE) &&
- (spBufferedChunk->uiTsn <= uiCumAck) )
- {
- spStream->usNextStreamSeqNum = spBufferedChunk->usStreamSeqNum+1;
- PassToUpperLayer(spBufferedChunk);
- spDeleteNode = spCurrNode;
- spCurrNode = spCurrNode->spNext;
- DeleteNode( &(spStream->sBufferedChunkList), spDeleteNode );
- spDeleteNode = NULL;
- }
-
- /* Let's see if we can deliver anything else based on the SSNs.
- */
- else if( spBufferedChunk->usStreamSeqNum ==
- spStream->usNextStreamSeqNum )
- {
- spStream->usNextStreamSeqNum++;
- PassToUpperLayer(spBufferedChunk);
- spDeleteNode = spCurrNode;
- spCurrNode = spCurrNode->spNext;
- DeleteNode( &(spStream->sBufferedChunkList), spDeleteNode );
- spDeleteNode = NULL;
- }
-
- /* ok, we have delivered all that we can!
- */
- else
- break;
- }
- }
- DBG_PL(UpdateAllStreams, "uiMyRwnd=%d"), uiMyRwnd DBG_PR;
- DBG_X(UpdateAllStreams);
- }
- void SctpAgent::ProcessInitChunk(u_char *ucpInitChunk)
- {
- DBG_I(ProcessInitChunk);
- SctpInitChunk_S *spInitChunk = (SctpInitChunk_S *) ucpInitChunk;
- SctpUnrelStreamPair_S *spUnrelStreamPair
- = (SctpUnrelStreamPair_S *) (ucpInitChunk + sizeof(SctpInitChunk_S));
- int i = 0;
- uiPeerRwnd = spInitChunk->uiArwnd;
- iNumInStreams = spInitChunk->usNumOutboundStreams;
- spInStreams = new SctpInStream_S[iNumInStreams];
- memset(spInStreams, 0, (iNumInStreams * sizeof(SctpInStream_S)) );
- if(spInitChunk->sHdr.usLength > sizeof(SctpInitChunk_S) )
- {
- for(i = spUnrelStreamPair->usStart; i <= spUnrelStreamPair->usEnd; i++)
- {
- DBG_PL(ProcessInitChunk, "setting inStream %d to UNRELIABLE"),i DBG_PR;
- spInStreams[i].eMode = SCTP_STREAM_UNRELIABLE;
- }
- }
-
- for(; i < iNumInStreams; i++)
- {
- DBG_PL(ProcessInitChunk, "setting inStream %d to RELIABLE"), i DBG_PR;
- spInStreams[i].eMode = SCTP_STREAM_RELIABLE;
- }
-
- DBG_X(ProcessInitChunk);
- }
- void SctpAgent::ProcessInitAckChunk(u_char *ucpInitAckChunk)
- {
- DBG_I(ProcessInitAckChunk);
- SctpInitAckChunk_S *spInitAckChunk = (SctpInitAckChunk_S *) ucpInitAckChunk;
- SctpUnrelStreamPair_S *spUnrelStreamPair
- = (SctpUnrelStreamPair_S *)(ucpInitAckChunk + sizeof(SctpInitAckChunk_S) );
- int i = 0;
- opT1InitTimer->force_cancel();
- uiPeerRwnd = spInitAckChunk->uiArwnd;
- tiRwnd++; // trigger changes to be traced
- iNumInStreams = spInitAckChunk->usNumOutboundStreams;
- spInStreams = new SctpInStream_S[iNumInStreams];
- memset(spInStreams, 0, (iNumInStreams * sizeof(SctpInStream_S)) );
- if(spInitAckChunk->sHdr.usLength > sizeof(SctpInitAckChunk_S) )
- {
- for(i = spUnrelStreamPair->usStart; i <= spUnrelStreamPair->usEnd; i++)
- {
- DBG_PL(ProcessInitAckChunk, "setting inStream %d to UNRELIABLE"),
- i DBG_PR;
- spInStreams[i].eMode = SCTP_STREAM_UNRELIABLE;
- }
- }
-
- for(; i < iNumInStreams; i++)
- {
- DBG_PL(ProcessInitAckChunk, "setting inStream %d to RELIABLE"), i DBG_PR;
- spInStreams[i].eMode = SCTP_STREAM_RELIABLE;
- }
-
- DBG_X(ProcessInitAckChunk);
- }
- void SctpAgent::ProcessCookieEchoChunk(SctpCookieEchoChunk_S *spCookieEchoChunk)
- {
- /* dummy empty function left as a hook for cookie echo processing */
- }
- void SctpAgent::ProcessCookieAckChunk(SctpCookieAckChunk_S *spCookieAckChunk)
- {
- opT1CookieTimer->force_cancel();
- }
- /* This function treats only one incoming data chunk at a time.
- */
- void SctpAgent::ProcessDataChunk(SctpDataChunkHdr_S *spChunk)
- {
- DBG_I(ProcessDataChunk);
- /* Is there still room in my receiver window?? We can only process the DATA
- * chunk if there is. Otherwise, we drop it!
- */
- if(spChunk->sHdr.usLength <= uiMyRwnd)
- {
- if((UpdateHighestTsn(spChunk->uiTsn) != TRUE) &&
- (IsDuplicateChunk(spChunk->uiTsn) == TRUE) )
- {
- InsertDuplicateTsn(spChunk->uiTsn);
- eSackChunkNeeded = TRUE; // section 6.7 - send sack immediately!
- DBG_PL(ProcessDataChunk, "duplicate tsn=%d"), spChunk->uiTsn DBG_PR;
- }
- else
- {
- /* Received a new chunk... Reduce receiver window until application
- * consumes the incoming chunk
- */
- uiMyRwnd -= spChunk->sHdr.usLength;
- tiRwnd++; // trigger rwnd changes to be traced
- UpdateRecvTsnBlocks(spChunk->uiTsn);
- PassToStream(spChunk);
- UpdateAllStreams();
- DBG_PL(ProcessDataChunk, "uiMyRwnd=%d"), uiMyRwnd DBG_PR;
- }
- }
- else
- {
- /* Do not generate a SACK if we are dropping the chunk!!
- */
- eSackChunkNeeded = FALSE;
- DBG_PL(ProcessDataChunk, "rwnd full... dropping tsn=%d"),
- spChunk->uiTsn DBG_PR;
- }
-
- DBG_X(ProcessDataChunk);
- }
- /* returns a boolean of whether a fast retransmit is necessary
- */
- Boolean_E SctpAgent::ProcessGapAckBlocks(u_char *ucpSackChunk,
- Boolean_E eNewCumAck)
- {
- DBG_I(ProcessGapAckBlocks);
- Boolean_E eFastRtxNeeded = FALSE;
- u_int uiHighestTsnSacked = uiHighestTsnNewlyAcked;
- u_int uiStartTsn;
- u_int uiEndTsn;
- Node_S *spCurrNode = NULL;
- SctpSendBufferNode_S *spCurrNodeData = NULL;
- Node_S *spCurrDestNode = NULL;
- SctpDest_S *spCurrDestNodeData = NULL;
- SctpSackChunk_S *spSackChunk = (SctpSackChunk_S *) ucpSackChunk;
- u_short usNumGapAcksProcessed = 0;
- SctpGapAckBlock_S *spCurrGapAck
- = (SctpGapAckBlock_S *) (ucpSackChunk + sizeof(SctpSackChunk_S));
- DBG_PL(ProcessGapAckBlocks,"CumAck=%d"), spSackChunk->uiCumAck DBG_PR;
- if(sSendBuffer.spHead == NULL) // do we have ANYTHING in the rtx buffer?
- {
- /* This COULD mean that this sack arrived late, and a previous one
- * already cum ack'd everything. ...so, what do we do? nothing??
- */
- }
-
- else // we do have chunks in the rtx buffer
- {
- /* make sure we clear all the spFirstOutstanding pointers before
- * using them!
- */
- for(spCurrDestNode = sDestList.spHead;
- spCurrDestNode != NULL;
- spCurrDestNode = spCurrDestNode->spNext)
- {
- spCurrDestNodeData = (SctpDest_S *) spCurrDestNode->vpData;
- spCurrDestNodeData->spFirstOutstanding = NULL;
- }
- for(spCurrNode = sSendBuffer.spHead;
- (spCurrNode != NULL) &&
- (usNumGapAcksProcessed != spSackChunk->usNumGapAckBlocks);
- spCurrNode = spCurrNode->spNext)
- {
- spCurrNodeData = (SctpSendBufferNode_S *) spCurrNode->vpData;
- /* is this chunk the first outstanding on its destination?
- */
- if(spCurrNodeData->spDest->spFirstOutstanding == NULL &&
- spCurrNodeData->eGapAcked == FALSE &&
- spCurrNodeData->eAdvancedAcked == FALSE)
- {
- /* yes, it is the first!
- */
- spCurrNodeData->spDest->spFirstOutstanding = spCurrNodeData;
- }
- DBG_PL(ProcessGapAckBlocks, "--> rtx list chunk begin") DBG_PR;
- DBG_PL(ProcessGapAckBlocks, " TSN=%d"),
- spCurrNodeData->spChunk->uiTsn
- DBG_PR;
- DBG_PL(ProcessGapAckBlocks, " %s=%s %s=%s"),
- "eGapAcked",
- spCurrNodeData->eGapAcked ? "TRUE" : "FALSE",
- "eAddedToPartialBytesAcked",
- spCurrNodeData->eAddedToPartialBytesAcked ? "TRUE" : "FALSE"
- DBG_PR;
- DBG_PL(ProcessGapAckBlocks, " NumMissingReports=%d NumTxs=%d"),
- spCurrNodeData->iNumMissingReports,
- spCurrNodeData->iNumTxs
- DBG_PR;
- DBG_PL(ProcessGapAckBlocks, "<-- rtx list chunk end") DBG_PR;
-
- DBG_PL(ProcessGapAckBlocks,"GapAckBlock StartOffset=%d EndOffset=%d"),
- spCurrGapAck->usStartOffset, spCurrGapAck->usEndOffset DBG_PR;
- uiStartTsn = spSackChunk->uiCumAck + spCurrGapAck->usStartOffset;
- uiEndTsn = spSackChunk->uiCumAck + spCurrGapAck->usEndOffset;
-
- DBG_PL(ProcessGapAckBlocks, "GapAckBlock StartTsn=%d EndTsn=%d"),
- uiStartTsn, uiEndTsn DBG_PR;
- if(spCurrNodeData->spChunk->uiTsn < uiStartTsn)
- {
- /* This chunk is NOT being acked and is missing at the receiver
- */
- /* If this chunk was GapAcked before, then either the
- * receiver has renegged the chunk (which our simulation
- * doesn't do) or this SACK is arriving out of order.
- */
- if(spCurrNodeData->eGapAcked == TRUE)
- {
- DBG_PL(ProcessGapAckBlocks,
- "out of order SACK? setting TSN=%d eGapAcked=FALSE"),
- spCurrNodeData->spChunk->uiTsn DBG_PR;
- spCurrNodeData->eGapAcked = FALSE;
- spCurrNodeData->spDest->iOutstandingBytes
- += spCurrNodeData->spChunk->sHdr.usLength;
- /* section 6.3.2.R4 says that we should restart the
- * T3-rtx timer here if it isn't running already. In our
- * implementation, it isn't necessary since
- * ProcessSackChunk will restart the timer for any
- * destinations which have outstanding data and don't
- * have a timer running.
- */
- }
- }
- else if((uiStartTsn <= spCurrNodeData->spChunk->uiTsn) &&
- (spCurrNodeData->spChunk->uiTsn <= uiEndTsn) )
- {
- /* This chunk is being acked via a gap ack block
- */
- DBG_PL(ProcessGapAckBlocks, "gap ack acks this chunk: %s%s"),
- "eGapAcked=",
- spCurrNodeData->eGapAcked ? "TRUE" : "FALSE"
- DBG_PR;
- /* HTNA algorithm... we need to know the highest TSN
- * sacked (even if it isn't new), so that when the sender
- * is in Fast Recovery, the outstanding tsns beyond the
- * last sack tsn do not have their missing reports incremented
- */
- if(uiHighestTsnSacked < spCurrNodeData->spChunk->uiTsn)
- uiHighestTsnSacked = spCurrNodeData->spChunk->uiTsn;
- if(spCurrNodeData->eGapAcked == FALSE)
- {
- DBG_PL(ProcessGapAckBlocks, "setting eGapAcked=TRUE") DBG_PR;
- spCurrNodeData->eGapAcked = TRUE;
- /* HTNA algorithm... we need to know the highest TSN
- * newly acked
- */
- if(uiHighestTsnNewlyAcked < spCurrNodeData->spChunk->uiTsn)
- uiHighestTsnNewlyAcked = spCurrNodeData->spChunk->uiTsn;
- if(spCurrNodeData->eAdvancedAcked == FALSE)
- {
- spCurrNodeData->spDest->iNumNewlyAckedBytes
- += spCurrNodeData->spChunk->sHdr.usLength;
- }
-
- /* only increment partial bytes acked if we are in
- * congestion avoidance mode, we have a new cum ack, and
- * we haven't already incremented it for this TSN
- */
- if(( spCurrNodeData->spDest->iCwnd
- > spCurrNodeData->spDest->iSsthresh) &&
- eNewCumAck == TRUE &&
- spCurrNodeData->eAddedToPartialBytesAcked == FALSE)
- {
- DBG_PL(ProcessGapAckBlocks,
- "setting eAddedToPartiallyBytesAcked=TRUE")DBG_PR;
-
- spCurrNodeData->eAddedToPartialBytesAcked = TRUE; // set
- spCurrNodeData->spDest->iPartialBytesAcked
- += spCurrNodeData->spChunk->sHdr.usLength;
- }
- /* We update the RTT estimate if the following hold true:
- * 1. RTO pending flag is set (6.3.1.C4)
- * 2. Timestamp is set for this chunk
- * 3. This chunk has not been retransmitted
- * 4. This chunk has not been gap acked already
- * 5. This chunk has not been advanced acked (pr-sctp)
- */
- if(spCurrNodeData->spDest->eRtoPending == TRUE &&
- spCurrNodeData->dTxTimestamp > 0 &&
- spCurrNodeData->iNumTxs == 1 &&
- spCurrNodeData->eAdvancedAcked == FALSE)
- {
- /* If the chunk is marked for timeout rtx, then the
- * sender is an ambigious state. Were the sacks lost
- * or was there a failure? Since we don't clear the
- * error counter below, we also don't update the
- * RTT. This could be a problem for late arriving
- * SACKs.
- */
- if(spCurrNodeData->eMarkedForRtx != TIMEOUT_RTX)
- RttUpdate(spCurrNodeData->dTxTimestamp,
- spCurrNodeData->spDest);
- spCurrNodeData->spDest->eRtoPending = FALSE;
- }
- /* section 6.3.2.R3 - Stop the timer if this is the
- * first outstanding for this destination (note: it may
- * have already been stopped if there was a new cum
- * ack). If there are still outstanding bytes on this
- * destination, we'll restart the timer later in
- * ProcessSackChunk()
- */
- if(spCurrNodeData->spDest->spFirstOutstanding
- == spCurrNodeData)
-
- {
- if(spCurrNodeData->spDest->eRtxTimerIsRunning == TRUE)
- StopT3RtxTimer(spCurrNodeData->spDest);
- }
-
- iAssocErrorCount = 0;
-
- /* We don't want to clear the error counter if it's
- * cleared already; otherwise, we'll unnecessarily
- * trigger a trace event.
- *
- * Also, the error counter is cleared by SACKed data
- * ONLY if the TSNs are not marked for timeout
- * retransmission and has not been gap acked
- * before. Without this condition, we can run into a
- * problem for failure detection. When a failure occurs,
- * some data may have made it through before the
- * failure, but the sacks got lost. When the sender
- * retransmits the first outstanding, the receiver will
- * sack all the data whose sacks got lost. We don't want
- * these sacks to clear the error counter, or else
- * failover would take longer.
- */
- if(spCurrNodeData->spDest->iErrorCount != 0 &&
- spCurrNodeData->eMarkedForRtx != TIMEOUT_RTX)
- {
- DBG_PL(ProcessGapAckBlocks,
- "clearing error counter for %p with tsn=%lu"),
- spCurrNodeData->spDest,
- spCurrNodeData->spChunk->uiTsn DBG_PR;
- spCurrNodeData->spDest->iErrorCount = 0; // clear errors
- tiErrorCount++; // ... and trace it!
- spCurrNodeData->spDest->eStatus=SCTP_DEST_STATUS_ACTIVE;
- if(spCurrNodeData->spDest == spPrimaryDest &&
- spNewTxDest != spPrimaryDest)
- {
- DBG_PL(ProcessGapAckBlocks,
- "primary recovered... "
- "migrating back from %p to %p"),
- spNewTxDest, spPrimaryDest DBG_PR;
- spNewTxDest = spPrimaryDest; // return to primary
- }
- }
- spCurrNodeData->eMarkedForRtx = NO_RTX; // unmark
- }
- }
- else if(spCurrNodeData->spChunk->uiTsn > uiEndTsn)
- {
- /* This point in the rtx buffer is already past the tsns which
- * are being acked by this gap ack block.
- */
- usNumGapAcksProcessed++;
- /* Did we process all the gap ack blocks?
- */
- if(usNumGapAcksProcessed != spSackChunk->usNumGapAckBlocks)
- {
- DBG_PL(ProcessGapAckBlocks, "jump to next gap ack block")
- DBG_PR;
- spCurrGapAck
- = ((SctpGapAckBlock_S *)
- (ucpSackChunk + sizeof(SctpSackChunk_S)
- +(usNumGapAcksProcessed * sizeof(SctpGapAckBlock_S))));
- }
- /* If this chunk was GapAcked before, then either the
- * receiver has renegged the chunk (which our simulation
- * doesn't do) or this SACK is arriving out of order.
- */
- if(spCurrNodeData->eGapAcked == TRUE)
- {
- DBG_PL(ProcessGapAckBlocks,
- "out of order SACK? setting TSN=%d eGapAcked=FALSE"),
- spCurrNodeData->spChunk->uiTsn DBG_PR;
- spCurrNodeData->eGapAcked = FALSE;
- spCurrNodeData->spDest->iOutstandingBytes
- += spCurrNodeData->spChunk->sHdr.usLength;
-
- /* section 6.3.2.R4 says that we should restart the
- * T3-rtx timer here if it isn't running already. In our
- * implementation, it isn't necessary since
- * ProcessSackChunk will restart the timer for any
- * destinations which have outstanding data and don't
- * have a timer running.
- */
- }
- }
- }
- /* By this time, either we have run through the entire send buffer or we
- * have run out of gap ack blocks. In the case that we have run out of
- * gap ack blocks before we finished running through the send buffer, we
- * need to mark the remaining chunks in the send buffer as
- * eGapAcked=FALSE. This final marking needs to be done, because we only
- * trust gap ack info from the last SACK. Otherwise, renegging (which we
- * don't do) or out of order SACKs would give the sender an incorrect
- * view of the peer's rwnd.
- */
- for(; spCurrNode != NULL; spCurrNode = spCurrNode->spNext)
- {
- /* This chunk is NOT being acked and is missing at the receiver
- */
- spCurrNodeData = (SctpSendBufferNode_S *) spCurrNode->vpData;
- /* If this chunk was GapAcked before, then either the
- * receiver has renegged the chunk (which our simulation
- * doesn't do) or this SACK is arriving out of order.
- */
- if(spCurrNodeData->eGapAcked == TRUE)
- {
- DBG_PL(ProcessGapAckBlocks,
- "out of order SACK? setting TSN=%d eGapAcked=FALSE"),
- spCurrNodeData->spChunk->uiTsn DBG_PR;
- spCurrNodeData->eGapAcked = FALSE;
- spCurrNodeData->spDest->iOutstandingBytes
- += spCurrNodeData->spChunk->sHdr.usLength;
- /* section 6.3.2.R4 says that we should restart the T3-rtx
- * timer here if it isn't running already. In our
- * implementation, it isn't necessary since ProcessSackChunk
- * will restart the timer for any destinations which have
- * outstanding data and don't have a timer running.
- */
- }
- }
- DBG_PL(ProcessGapAckBlocks,"now incrementing missing reports...") DBG_PR;
- DBG_PL(ProcessGapAckBlocks,"uiHighestTsnNewlyAcked=%d"),
- uiHighestTsnNewlyAcked DBG_PR;
- for(spCurrNode = sSendBuffer.spHead;
- spCurrNode != NULL;
- spCurrNode = spCurrNode->spNext)
- {
- spCurrNodeData = (SctpSendBufferNode_S *) spCurrNode->vpData;
- DBG_PL(ProcessGapAckBlocks, "TSN=%d eGapAcked=%s"),
- spCurrNodeData->spChunk->uiTsn,
- spCurrNodeData->eGapAcked ? "TRUE" : "FALSE"
- DBG_PR;
- if(spCurrNodeData->eGapAcked == FALSE)
- {
- /* HTNA (Highest TSN Newly Acked) algorithm from
- * implementer's guide. The HTNA increments missing reports
- * for TSNs not GapAcked when one of the following
- * conditions hold true:
- *
- * 1. The TSN is less than the highest TSN newly acked.
- *
- * 2. The TSN is less than the highest TSN sacked so far
- * (not necessarily newly acked), the sender is in Fast
- * Recovery, the cum ack changes, and the new cum ack is less
- * than recover.
- */
- if( (spCurrNodeData->spChunk->uiTsn < uiHighestTsnNewlyAcked) ||
- (eNewCumAck == TRUE &&
- uiHighestTsnNewlyAcked <= uiRecover &&
- spCurrNodeData->spChunk->uiTsn < uiHighestTsnSacked))
- {
- spCurrNodeData->iNumMissingReports++;
- DBG_PL(ProcessGapAckBlocks,
- "incrementing missing report for TSN=%d to %d"),
- spCurrNodeData->spChunk->uiTsn,
- spCurrNodeData->iNumMissingReports
- DBG_PR;
- if(spCurrNodeData->iNumMissingReports >= iFastRtxTrigger &&
- spCurrNodeData->eIneligibleForFastRtx == FALSE &&
- spCurrNodeData->eAdvancedAcked == FALSE)
- {
- MarkChunkForRtx(spCurrNodeData, FAST_RTX);
- eFastRtxNeeded = TRUE;
- spCurrNodeData->eIneligibleForFastRtx = TRUE;
- DBG_PL(ProcessGapAckBlocks,
- "setting eFastRtxNeeded = TRUE") DBG_PR;
- }
- }
- }
- }
- }
- if(eFastRtxNeeded == TRUE)
- tiFrCount++;
- DBG_PL(ProcessGapAckBlocks, "eFastRtxNeeded=%s"),
- eFastRtxNeeded ? "TRUE" : "FALSE" DBG_PR;
- DBG_X(ProcessGapAckBlocks);
- return eFastRtxNeeded;
- }
- void SctpAgent::ProcessSackChunk(u_char *ucpSackChunk)
- {
- DBG_I(ProcessSackChunk);
- SctpSackChunk_S *spSackChunk = (SctpSackChunk_S *) ucpSackChunk;
- DBG_PL(ProcessSackChunk, "cum=%d arwnd=%d #gapacks=%d #duptsns=%d"),
- spSackChunk->uiCumAck, spSackChunk->uiArwnd,
- spSackChunk->usNumGapAckBlocks, spSackChunk->usNumDupTsns
- DBG_PR;
- Boolean_E eFastRtxNeeded = FALSE;
- Boolean_E eNewCumAck = FALSE;
- Node_S *spCurrDestNode = NULL;
- SctpDest_S *spCurrDestNodeData = NULL;
- u_int uiTotalOutstanding = 0;
- int i = 0;
- /* make sure we clear all the iNumNewlyAckedBytes before using them!
- */
- for(spCurrDestNode = sDestList.spHead;
- spCurrDestNode != NULL;
- spCurrDestNode = spCurrDestNode->spNext)
- {
- spCurrDestNodeData = (SctpDest_S *) spCurrDestNode->vpData;
- spCurrDestNodeData->iNumNewlyAckedBytes = 0;
- spCurrDestNodeData->spFirstOutstanding = NULL;
- }
- if(spSackChunk->uiCumAck < uiCumAckPoint)
- {
- /* this cumAck's a previously cumAck'd tsn (ie, it's out of order!)
- * ...so ignore!
- */
- DBG_PL(ProcessSackChunk, "ignoring out of order sack!") DBG_PR;
- DBG_X(ProcessSackChunk);
- return;
- }
- else if(spSackChunk->uiCumAck > uiCumAckPoint)
- {
- eNewCumAck = TRUE; // incomding SACK's cum ack advances the cum ack point
- SendBufferDequeueUpTo(spSackChunk->uiCumAck);
- uiCumAckPoint = spSackChunk->uiCumAck; // Advance the cumAck pointer
- }
- if(spSackChunk->usNumGapAckBlocks != 0) // are there any gaps??
- {
- eFastRtxNeeded = ProcessGapAckBlocks(ucpSackChunk, eNewCumAck);
- }
- for(spCurrDestNode = sDestList.spHead;
- spCurrDestNode != NULL;
- spCurrDestNode = spCurrDestNode->spNext)
- {
- spCurrDestNodeData = (SctpDest_S *) spCurrDestNode->vpData;
- /* Only adjust cwnd if:
- * 1. sack advanced the cum ack point
- * 2. this destination has newly acked bytes
- * 3. the cum ack is at or beyond the recovery window
- *
- * Also, we MUST adjust our congestion window BEFORE we update the
- * number of outstanding bytes to reflect the newly acked bytes in
- * received SACK.
- */
- if(eNewCumAck == TRUE &&
- spCurrDestNodeData->iNumNewlyAckedBytes > 0 &&
- spSackChunk->uiCumAck >= uiRecover)
- {
- AdjustCwnd(spCurrDestNodeData);
- }
- /* The number of outstanding bytes is reduced by how many bytes this
- * sack acknowledges.
- */
- if(spCurrDestNodeData->iNumNewlyAckedBytes <=
- spCurrDestNodeData->iOutstandingBytes)
- {
- spCurrDestNodeData->iOutstandingBytes
- -= spCurrDestNodeData->iNumNewlyAckedBytes;
- }
- else
- spCurrDestNodeData->iOutstandingBytes = 0;
- DBG_PL(ProcessSackChunk,"Dest #%d (%d:%d) (%p): outstanding=%d, cwnd=%d"),
- ++i, spCurrDestNodeData->iNsAddr, spCurrDestNodeData->iNsPort,
- spCurrDestNodeData, spCurrDestNodeData->iOutstandingBytes,
- spCurrDestNodeData->iCwnd DBG_PR;
-
- if(spCurrDestNodeData->iOutstandingBytes == 0)
- {
- /* All outstanding data has been acked
- */
- spCurrDestNodeData->iPartialBytesAcked = 0; // section 7.2.2
- /* section 6.3.2.R2
- */
- if(spCurrDestNodeData->eRtxTimerIsRunning == TRUE)
- {
- DBG_PL(ProcessSackChunk, "Dest #%d (%p): stopping timer"),
- i, spCurrDestNodeData DBG_PR;
- StopT3RtxTimer(spCurrDestNodeData);
- }
- }
- /* section 6.3.2.R3 - Restart timers for destinations that have
- * acknowledged their first outstanding (ie, no timer running) and
- * still have outstanding data in flight.
- */
- if(spCurrDestNodeData->iOutstandingBytes > 0 &&
- spCurrDestNodeData->eRtxTimerIsRunning == FALSE)
- {
- StartT3RtxTimer(spCurrDestNodeData);
- }
- }
- DBG_F(ProcessSackChunk, DumpSendBuffer());
- AdvancePeerAckPoint();
- if(eFastRtxNeeded == TRUE) // section 7.2.4
- FastRtx();
- /* Let's see if after process this sack, there are still any chunks
- * pending... If so, rtx all allowed by cwnd.
- */
- else if( (eMarkedChunksPending = AnyMarkedChunks()) == TRUE)
- {
- /* section 6.1.C) When the time comes for the sender to
- * transmit, before sending new DATA chunks, the sender MUST
- * first transmit any outstanding DATA chunks which are marked
- * for retransmission (limited by the current cwnd).
- */
- RtxMarkedChunks(RTX_LIMIT_CWND);
- }
- /* (6.2.1.D.ii) Adjust PeerRwnd based on total oustanding bytes on all
- * destinations. We need to this adjustment after any
- * retransmissions. Otherwise the sender's view of the peer rwnd will be
- * off, because the number outstanding increases again once a marked
- * chunk gets retransmitted (when marked, outstanding is decreased).
- */
- uiTotalOutstanding = TotalOutstanding();
- if(uiTotalOutstanding <= spSackChunk->uiArwnd)
- uiPeerRwnd = (spSackChunk->uiArwnd - uiTotalOutstanding);
- else
- uiPeerRwnd = 0;
-
- DBG_PL(ProcessSackChunk, "uiPeerRwnd=%d, uiArwnd=%d"), uiPeerRwnd,
- spSackChunk->uiArwnd DBG_PR;
- DBG_X(ProcessSackChunk);
- }
- void SctpAgent::ProcessForwardTsnChunk(SctpForwardTsnChunk_S *spForwardTsnChunk)
- {
- DBG_I(ProcessForwardTsnChunk);
-
- int i;
- u_int uiNewCum = spForwardTsnChunk->uiNewCum;
- /* Although we haven't actually received a DATA chunk, we have received
- * a FORWARD CUM TSN chunk, which essentially tells the receiver to
- * pretend that it has received all the DATA chunks up to the forwarded cum.
- * Some of the tsns we are forwarding past have been received, while others
- * have not.
- */
- for(i = uiCumAck+1; i <= (int) uiNewCum; i++)
- {
- if( IsDuplicateChunk(i) == FALSE ) // make sure it hasn't been received
- UpdateRecvTsnBlocks(i);
- }
- UpdateAllStreams();
- DBG_PL(ProcessForwardTsnChunk, "uiCumAck=%d"), uiCumAck DBG_PR;
- DBG_X(ProcessForwardTsnChunk);
- }
- void SctpAgent::ProcessHeartbeatAckChunk(SctpHeartbeatAckChunk_S
- *spHeartbeatAckChunk)
- {
- DBG_I(ProcessHeartbeatAckChunk);
- iAssocErrorCount = 0;
- /* trigger trace ONLY if it was previously NOT 0
- */
-
- /* NE - 4/11/2007
- * Change for Confirming Destination Addresses
- * condition (spHeartbeatAckChunk->spDest->iErrorCount != 0) is changed
- * with (spHeartbeatAckChunk->spDest->iErrorCount >= 0) to enable
- * destination address confirmation when the association moves to
- * ESTABLISHED state. iErrorCount may be 0 for any given destinations
- * during the confirmation.
- */
- if(spHeartbeatAckChunk->spDest->iErrorCount >= 0)
- {
- DBG_PL(ProcessHeartbeatAckChunk, "marking dest %p to active"),
- spHeartbeatAckChunk->spDest DBG_PR;
- spHeartbeatAckChunk->spDest->eStatus = SCTP_DEST_STATUS_ACTIVE;
- spHeartbeatAckChunk->spDest->iErrorCount = 0; // clear the error count
- tiErrorCount++; // ...and trace it too!
- if(spHeartbeatAckChunk->spDest == spPrimaryDest &&
- spNewTxDest != spPrimaryDest)
- {
- DBG_PL(ProcessHeartbeatAckChunk,
- "primary recovered... migrating back from %p to %p"),
- spNewTxDest, spPrimaryDest DBG_PR;
- spNewTxDest = spPrimaryDest; // return to primary
- }
- }
- RttUpdate(spHeartbeatAckChunk->dTimestamp, spHeartbeatAckChunk->spDest);
- DBG_PL(ProcessHeartbeatAckChunk, "set rto of dest=%p to %f"),
- spHeartbeatAckChunk->spDest, spHeartbeatAckChunk->spDest->dRto DBG_PR;
-
- spHeartbeatAckChunk->spDest->opHeartbeatTimeoutTimer->force_cancel();
- /* Track HB-Timer for CMT-PF
- */
- spHeartbeatAckChunk->spDest->eHBTimerIsRunning = FALSE;
- DBG_X(ProcessHeartbeatAckChunk);
- }
- /* This function is left as a hook for extensions to process chunk types not
- * supported in the base scpt. In the base sctp, we simply report the error...
- * the chunk is unexpected and unknown.
- */
- void SctpAgent::ProcessOptionChunk(u_char *ucpInChunk)
- {
- DBG_I(ProcessOptionChunk);
- double dCurrTime = Scheduler::instance().clock();
- DBG_PL(ProcessOptionChunk, "unexpected chunk type (unknown: %d) at %f"),
- ((SctpChunkHdr_S *)ucpInChunk)->ucType, dCurrTime DBG_PR;
- printf("[ProcessOptionChunk] unexpected chunk type (unknown: %d) at %fn",
- ((SctpChunkHdr_S *)ucpInChunk)->ucType, dCurrTime);
- DBG_X(ProcessOptionChunk);
- }
- int SctpAgent::ProcessChunk(u_char *ucpInChunk, u_char **ucppOutData)
- {
- DBG_I(ProcessChunk);
- int iThisOutDataSize = 0;
- Node_S *spCurrNode = NULL;
- SctpDest_S *spCurrDest = NULL;
- double dCurrTime = Scheduler::instance().clock();
- double dTime;
- SctpHeartbeatAckChunk_S *spHeartbeatChunk = NULL;
- SctpHeartbeatAckChunk_S *spHeartbeatAckChunk = NULL;
- /* NE: 4/11/2007 - Confirming Destinations */
- Boolean_E eThisDestWasUnconfirmed = FALSE;
- Boolean_E eFoundUnconfirmedDest = FALSE;
- /* End of Confirming Destinations */
-
- switch(eState)
- {
- case SCTP_STATE_CLOSED:
- switch( ((SctpChunkHdr_S *)ucpInChunk)->ucType)
- {
- case SCTP_CHUNK_INIT:
- DBG_PL(ProcessChunk, "got INIT!! ...sending INIT_ACK") DBG_PR;
- ProcessInitChunk(ucpInChunk);
- iThisOutDataSize = GenChunk(SCTP_CHUNK_INIT_ACK, *ucppOutData);
- *ucppOutData += iThisOutDataSize;
- /* stay in the closed state */
- break;
- case SCTP_CHUNK_COOKIE_ECHO:
- DBG_PL(ProcessChunk,
- "got COOKIE_ECHO!! (established!) ...sending COOKIE_ACK")
- DBG_PR;
- ProcessCookieEchoChunk( (SctpCookieEchoChunk_S *) ucpInChunk );
- iThisOutDataSize = GenChunk(SCTP_CHUNK_COOKIE_ACK, *ucppOutData);
- *ucppOutData += iThisOutDataSize;
- eState = SCTP_STATE_ESTABLISHED;
- if(uiHeartbeatInterval != 0)
- {
- dTime = CalcHeartbeatTime(spPrimaryDest->dRto);
- opHeartbeatGenTimer->force_cancel();
- opHeartbeatGenTimer->resched(dTime);
- opHeartbeatGenTimer->dStartTime = dCurrTime;
-
- for(spCurrNode = sDestList.spHead;
- spCurrNode != NULL;
- spCurrNode = spCurrNode->spNext)
- {
- spCurrDest = (SctpDest_S *) spCurrNode->vpData;
- spCurrDest->dIdleSince = dCurrTime;
- }
- }
-
- break;
-
- default:
- /* ALC 1/25/2002
- *
- * no error statement here, because there are times when this could
- * occur due to abrupt disconnections via the "reset" command. how?
- * well, "reset" resets all the association state. however, there may
- * still be packets in transit.if and when those packets arrive, they
- * will be unexpected packets since the association is closed. since
- * this is a simulation, it shouldn't be a problem. however, if an
- * application needs a more graceful shutdown, we would need to
- * implement sctp's proper shutdown procedure. until the need arises,
- * we won't do it. instead, what do we do? ignore the "unexpected"
- * packet.
- */
- DBG_PL(ProcessChunk, "association closed... ignoring chunk %s"),
- "(not COOKIE_ECHO or INIT)" DBG_PR;
- break;
- }
- break;
-
- case SCTP_STATE_COOKIE_WAIT:
- DBG_PL(ProcessChunk, "got INIT_ACK!! ...sending COOKIE_ECHO") DBG_PR;
- ProcessInitAckChunk(ucpInChunk);
- iThisOutDataSize = GenChunk(SCTP_CHUNK_COOKIE_ECHO, *ucppOutData);
- *ucppOutData += iThisOutDataSize;
- opT1CookieTimer->resched(spPrimaryDest->dRto);
- eState = SCTP_STATE_COOKIE_ECHOED;
- break;
- case SCTP_STATE_COOKIE_ECHOED:
- DBG_PL(ProcessChunk, "got COOKIE_ACK!! (established!) ...sending DATA")
- DBG_PR;
- ProcessCookieAckChunk( (SctpCookieAckChunk_S *) ucpInChunk );
- eSendNewDataChunks = TRUE;
- eState = SCTP_STATE_ESTABLISHED;
-
- /* NE: 4/11/2007 - Confirming Destinations
- * Confirming Destination Addresses for sender of INIT.
- * In our implementation destinations are confirmed just for
- * sender of the INIT. Confirming of destinations is not done for
- * receiver of the INIT.
- *
- * RFC 4460 - section 5.4 - Path Verification
- * Rule 3 - all addresses not covered by rule 1 and 2 are
- * considered UNCONFIRMED.
- */
- for(spCurrNode = sDestList.spHead;
- spCurrNode != NULL;
- spCurrNode = spCurrNode->spNext)
- {
- spCurrDest = (SctpDest_S *) spCurrNode->vpData;
- spCurrDest->eStatus = SCTP_DEST_STATUS_UNCONFIRMED;
- /* Send Heartbeat to confirm this dest and get RTT */
- SendHeartbeat(spCurrDest);
- }
- /* NE: 4/11/2007 - Confirming Destinations */
- /* Do not send any data until all dests have been confirmed
- * RFC allows to send data on confirmed dests though.
- * This implementation is more conservative than the RFC
- */
- eSendNewDataChunks = FALSE;
-
- break;
- case SCTP_STATE_ESTABLISHED:
- switch( ((SctpChunkHdr_S *)ucpInChunk)->ucType)
- {
- case SCTP_CHUNK_DATA:
- DBG_PL(ProcessChunk, "got DATA (TSN=%d)!!"),
- ((SctpDataChunkHdr_S *)ucpInChunk)->uiTsn DBG_PR;
-
- if(eUseDelayedSacks == FALSE) // are we doing delayed sacks?
- {
- /* NO, so generate sack immediately!
- */
- eSackChunkNeeded = TRUE;
- }
- else // we are doing delayed sacks, so...
- {
- /* rfc2960 section 6.2 - determine if a SACK will be generated
- */
- if(eStartOfPacket == TRUE)
- {
- eStartOfPacket = FALSE; // reset
- iDataPktCountSinceLastSack++;
- if(iDataPktCountSinceLastSack == 1)
- {
- opSackGenTimer->resched(dSackDelay);
- }
- else if(iDataPktCountSinceLastSack == DELAYED_SACK_TRIGGER)
- {
- iDataPktCountSinceLastSack = 0; // reset
- opSackGenTimer->force_cancel();
- eSackChunkNeeded = TRUE;
- }
- }
- }
- ProcessDataChunk( (SctpDataChunkHdr_S *) ucpInChunk );
- /* section 6.7 - There is at least one "gap in the received DATA
- * chunk sequence", so let's ensure we send a SACK immediately!
- */
- if(sRecvTsnBlockList.uiLength > 0)
- {
- iDataPktCountSinceLastSack = 0; // reset
- opSackGenTimer->force_cancel();
- eSackChunkNeeded = TRUE;
- }
- /* no state change
- */
- break;
- case SCTP_CHUNK_SACK:
- DBG_PL(ProcessChunk, "got SACK (CumAck=%d)!!"),
- ((SctpSackChunk_S *)ucpInChunk)->uiCumAck DBG_PR;
- ProcessSackChunk(ucpInChunk);
- /* Do we need to transmit a FORWARD TSN chunk??
- */
- if(uiAdvancedPeerAckPoint > uiCumAckPoint)
- eForwardTsnNeeded = TRUE;
- eSendNewDataChunks = TRUE;
- break; // no state change
- case SCTP_CHUNK_FORWARD_TSN:
- DBG_PL(ProcessChunk, "got FORWARD TSN (tsn=%d)!!"),
- ((SctpForwardTsnChunk_S *) ucpInChunk)->uiNewCum DBG_PR;
- ProcessForwardTsnChunk( (SctpForwardTsnChunk_S *) ucpInChunk );
- break; // no state change
- case SCTP_CHUNK_HB:
- DBG_PL(ProcessChunk, "got HEARTBEAT!!") DBG_PR;
- /* GenChunk() doesn't copy HB info
- */
- iThisOutDataSize = GenChunk(SCTP_CHUNK_HB_ACK, *ucppOutData);
- /* ...so we copy it here!
- */
- spHeartbeatChunk = (SctpHeartbeatAckChunk_S *) ucpInChunk;
- spHeartbeatAckChunk = (SctpHeartbeatAckChunk_S *) *ucppOutData;
- spHeartbeatAckChunk->dTimestamp = spHeartbeatChunk->dTimestamp;
- spHeartbeatAckChunk->spDest = spHeartbeatChunk->spDest;
- *ucppOutData += iThisOutDataSize;
- break; // no state change
- case SCTP_CHUNK_HB_ACK:
- DBG_PL(ProcessChunk, "got HEARTBEAT ACK!!") DBG_PR;
-
- spHeartbeatAckChunk = (SctpHeartbeatAckChunk_S *) ucpInChunk;
-
- /* NE: 4/11/2007 - Confirming Destinations */
- if( spHeartbeatAckChunk->spDest->eStatus ==
- SCTP_DEST_STATUS_UNCONFIRMED )
- eThisDestWasUnconfirmed = TRUE;
-
- ProcessHeartbeatAckChunk( (SctpHeartbeatAckChunk_S *) ucpInChunk);
-
- /* NE: 4/11/2007 - Confirming Destinations */
- /* Check if all dests have been confirmed.
- * If yes, allow new data transmission
- */
- if(eThisDestWasUnconfirmed == TRUE)
- {
- for(spCurrNode = sDestList.spHead;
- spCurrNode != NULL;
- spCurrNode = spCurrNode->spNext)
- {
- spCurrDest = (SctpDest_S *) spCurrNode->vpData;
- if(spCurrDest->eStatus == SCTP_DEST_STATUS_UNCONFIRMED)
- {
- eFoundUnconfirmedDest = TRUE;
- DBG_PL(ProcessChunk, "dest=%p UNCONFIRMED"),
- spCurrDest DBG_PR;
- break;
- }
- }
-
- if(eFoundUnconfirmedDest == TRUE)
- break; /* From this case */
- else
- {
- /* All dests have been confirmed */
- eSendNewDataChunks = TRUE;
-
- /* Moved code from COOKIE ACK recvd */
- if(uiHeartbeatInterval != 0)
- {
- dTime = CalcHeartbeatTime(spPrimaryDest->dRto);
- opHeartbeatGenTimer->force_cancel();
- opHeartbeatGenTimer->resched(dTime);
- opHeartbeatGenTimer->dStartTime = dCurrTime;
-
- for(spCurrNode = sDestList.spHead;
- spCurrNode != NULL;
- spCurrNode = spCurrNode->spNext)
- {
- spCurrDest = (SctpDest_S *) spCurrNode->vpData;
- spCurrDest->dIdleSince = dCurrTime;
- }
- }
-
- DBG_PL(ProcessChunk, "All destinations confirmed!")
- DBG_PR;
- }
- }
-
- break; // no state change
-
- case SCTP_CHUNK_INIT:
- DBG_PL(ProcessChunk, "unexpected chunk type (INIT) at %f"),
- dCurrTime DBG_PR;
- printf("[ProcessChunk] unexpected chunk type (INIT) at %fn",
- dCurrTime);
- break;
- case SCTP_CHUNK_INIT_ACK:
- DBG_PL(ProcessChunk, "unexpected chunk type (INIT_ACK) at %f"),
- dCurrTime DBG_PR;
- printf("[ProcessChunk] unexpected chunk type (INIT_ACK) at %fn",
- dCurrTime);
- break;
- /* even though the association is established,COOKIE_ECHO needs to be
- * handled because the peer may have not received the COOKIE_ACK.
- *
- * Note: we don't follow the rfc's complex process for handling this
- * case, because we don't deal with tie-tags, etc in simulation. :-)
- */
- case SCTP_CHUNK_COOKIE_ECHO:
- DBG_PL(ProcessChunk,
- "got COOKIE_ECHO!! (established!) ...sending COOKIE_ACK")
- DBG_PR;
- ProcessCookieEchoChunk( (SctpCookieEchoChunk_S *) ucpInChunk);
- iThisOutDataSize = GenChunk(SCTP_CHUNK_COOKIE_ACK, *ucppOutData);
- *ucppOutData += iThisOutDataSize;
- break;
- case SCTP_CHUNK_COOKIE_ACK:
- DBG_PL(ProcessChunk, "unexpected chunk type (COOKIE_ACK) at %f"),
- dCurrTime DBG_PR;
- printf("[ProcessChunk] unexpected chunk type (COOKIE_ACK) at %fn",
- dCurrTime);
- break;
- default:
- ProcessOptionChunk(ucpInChunk);
- break;
- }
- break;
- case SCTP_STATE_UNINITIALIZED:
- case SCTP_STATE_SHUTDOWN_SENT:
- case SCTP_STATE_SHUTDOWN_RECEIVED:
- case SCTP_STATE_SHUTDOWN_ACK_SENT:
- case SCTP_STATE_SHUTDOWN_PENDING:
- break;
- }
- DBG_X(ProcessChunk);
- return iThisOutDataSize;
- }
- /* Moves the pointer to the next chunk.
- * If there is no next chunk, then sent pointer to NULL.
- */
- void SctpAgent::NextChunk(u_char **ucpChunk, int *ipRemainingDataLen)
- {
- DBG_I(NextChunk);
- unsigned long ulSize = ((SctpChunkHdr_S *) *ucpChunk)->usLength;
- /* the chunk length field does not include the padded bytes, so we need to
- * account for these extra bytes.
- */
- if( (ulSize % 4) != 0 )
- ulSize += 4 - (ulSize % 4);
- *ipRemainingDataLen -= ulSize;
- if(*ipRemainingDataLen > 0)
- *ucpChunk += ulSize;
- else
- *ucpChunk = NULL;
- DBG_X(NextChunk);
- }
- /* Return the next active destination based on the destination passed in by
- * using round robin algorithm for selection. It is possible that this function
- * will return the same destination passed in. This will happen if (1) there is
- * only one destination and/or (2) all other destinations are inactive.
- */
- SctpDest_S *SctpAgent::GetNextDest(SctpDest_S *spDest)
- {
- DBG_I(GetNextDest);
- DBG_PL(GetNextDest, "previously dest = %p"), spDest DBG_PR;
- Node_S *spCurrDestNode = NULL;
- Node_S *spNextDestNode = NULL;
- SctpDest_S *spNextDestNodeData = NULL;
- /* find spDest before we can pick next
- */
- for(spCurrDestNode = sDestList.spHead;
- spCurrDestNode != NULL;
- spCurrDestNode = spCurrDestNode->spNext)
- {
- if(spCurrDestNode->vpData == spDest)
- break; // found it, so jump out!
- }
- assert(spCurrDestNode != NULL); // if NULL, means spDest wasn't found :-/
- /* let's get the next ACTIVE destination. stop if loops wraps around to
- * current destination.
- */
- spNextDestNode = spCurrDestNode;
- do
- {
- spNextDestNode = spNextDestNode->spNext;
- if(spNextDestNode == NULL)
- spNextDestNode = sDestList.spHead; // wrap around to head of list
- spNextDestNodeData = (SctpDest_S *) spNextDestNode->vpData;
- } while(spNextDestNodeData->eStatus == SCTP_DEST_STATUS_INACTIVE &&
- spNextDestNode != spCurrDestNode);
- /* Are we in the dormant state?
- */
- if(spNextDestNode == spCurrDestNode &&
- spNextDestNodeData->eStatus == SCTP_DEST_STATUS_INACTIVE)
- {
- switch(eDormantAction)
- {
- case DORMANT_HOP:
- /*
- * Simply go to the next destination. We don't care if it is inactive
- * or if it is the same destination we are already on.
- */
- spNextDestNode = spNextDestNode->spNext;
- if(spNextDestNode == NULL)
- spNextDestNode = sDestList.spHead; // wrap around to head of list
- spNextDestNodeData = (SctpDest_S *) spNextDestNode->vpData;
- break;
- case DORMANT_PRIMARY:
- spNextDestNodeData = spPrimaryDest;
- break;
- case DORMANT_LASTDEST:
- /*
- * Nothing to do. Since we just want to stay at the last destination
- * used before dormant state, then the loop above does that for us
- * already.
- */
- break;
- }
- }
- DBG_PL(GetNextDest, "next dest = %p"), spNextDestNodeData DBG_PR;
- DBG_X(GetNextDest);
- return spNextDestNodeData;
- }
- /* section 8.3 (with implementer's guide changes) - On an idle destination
- * address that is allowed to heartbeat, a HEARTBEAT chunk is RECOMMENDED
- * to be sent once per RTO of that destination address plus the protocol
- * parameter 'HB.interval' , with jittering of +/- 50% of the RTO value
- */
- double SctpAgent::CalcHeartbeatTime(double dRto)
- {
- DBG_I(CalcHeartbeatTime);
- double dRetVal = 0;
- double dCurrTime = Scheduler::instance().clock();
- dRetVal = dRto + uiHeartbeatInterval;
- dRetVal += Random::uniform(dRto);
- dRetVal -= dRto/2;
- DBG_PL(CalcHeartbeatTime, "next HEARTBEAT interval in %f secs"), dRetVal
- DBG_PR;
- DBG_PL(CalcHeartbeatTime, "next HEARTBEAT interval at %f"),
- dRetVal+dCurrTime DBG_PR;
- DBG_X(CalcHeartbeatTime);
- return dRetVal;
- }
- /* Sets the source addr, port, and target based on the destination information.
- * The SctpDest_S holds an already formed packet with the destination's
- * dest addr & port in the header. With this packet, we can find the correct
- * src addr, port, and target. */
- void SctpAgent::SetSource(SctpDest_S *spDest)
- {
- DBG_I(SetSource);
- /* dynamically pick the route only if 2 conditions hold true:
- * 1. we are not forcing the source address and interface.
- * 2. there exists a "multihome core" (ie, the agent is multihomed)
- *
- * otherwise, use the values are already preset
- */
- if(eForceSource == FALSE && opCoreTarget != NULL)
- {
- Node_S *spCurrNode = sInterfaceList.spHead;
- SctpInterface_S *spCurrInterface = NULL;
- /* find the connector (link) to use from the "routing table" of the core
- */
- Connector *connector
- = (Connector *) opCoreTarget->find(spDest->opRoutingAssistPacket);
- while(spCurrNode != NULL)
- {
- spCurrInterface = (SctpInterface_S *) spCurrNode->vpData;
-
- if(spCurrInterface->opLink == connector)
- {
- addr() = spCurrInterface->iNsAddr;
- port() = spCurrInterface->iNsPort;
- target_ = spCurrInterface->opTarget;
- break;
- }
- else
- spCurrNode = spCurrNode->spNext;
- }
- }
- DBG_PL(SetSource, "(%d:%d)"), addr(), port() DBG_PR;
- DBG_X(SetSource);
- }
- void SctpAgent::SetDestination(SctpDest_S *spDest)
- {
- DBG_I(SetDestination);
- daddr() = spDest->iNsAddr;
- dport() = spDest->iNsPort;
- DBG_PL(SetDestination, "(%d:%d)"), daddr(), dport() DBG_PR;
- DBG_X(SetDestination);
- }
- void SctpAgent::SendPacket(u_char *ucpData, int iDataSize, SctpDest_S *spDest)
- {
- DBG_I(SendPacket);
- DBG_PL(SendPacket, "spDest=%p (%d:%d)"),
- spDest, spDest->iNsAddr, spDest->iNsPort DBG_PR;
- DBG_PL(SendPacket, "iDataSize=%d uiNumChunks=%d"),
- iDataSize, uiNumChunks DBG_PR;
- Node_S *spNewNode = NULL;
- Packet *opPacket = NULL;
- PacketData *opPacketData = NULL;
- SetSource(spDest); // set src addr, port, target based on "routing table"
- SetDestination(spDest); // set dest addr & port
- opPacket = allocpkt();
- opPacketData = new PacketData(iDataSize);
- memcpy(opPacketData->data(), ucpData, iDataSize);
- opPacket->setdata(opPacketData);
- hdr_cmn::access(opPacket)->size() = iDataSize + SCTP_HDR_SIZE+uiIpHeaderSize;
- hdr_sctp::access(opPacket)->NumChunks() = uiNumChunks;
- hdr_sctp::access(opPacket)->SctpTrace() = new SctpTrace_S[uiNumChunks];
- memcpy(hdr_sctp::access(opPacket)->SctpTrace(), spSctpTrace,
- (uiNumChunks * sizeof(SctpTrace_S)) );
- uiNumChunks = 0; // reset the counter
- if(dRouteCalcDelay == 0) // simulating reactive routing overheads?
- {
- send(opPacket, 0); // no... so send immediately
- }
- else
- {
- if(spDest->eRouteCached == TRUE)
- {
- /* Since the route is "cached" already, we can reschedule the route
- * flushing and send the packet immediately.
- */
- spDest->opRouteCacheFlushTimer->resched(dRouteCacheLifetime);
- send(opPacket, 0);
- }
- else
- {
- /* The route isn't cached, so queue the packet and "calculate" the
- * route.
- */
- spNewNode = new Node_S;
- spNewNode->eType = NODE_TYPE_PACKET_BUFFER;
- spNewNode->vpData = opPacket;
- InsertNode(&spDest->sBufferedPackets,spDest->sBufferedPackets.spTail,
- spNewNode, NULL);
- if(spDest->opRouteCalcDelayTimer->status() != TIMER_PENDING)
- spDest->opRouteCalcDelayTimer->sched(dRouteCalcDelay);
- }
- }
- DBG_X(SendPacket);
- }
- SctpDest_S *SctpAgent::GetReplyDestination(hdr_ip *spIpHdr)
- {
- Node_S *spCurrNode = NULL;
- SctpDest_S *spDest = NULL;
- int iAddr = spIpHdr->saddr();
- int iPort = spIpHdr->sport();
-
- for(spCurrNode = sDestList.spHead;
- spCurrNode != NULL;
- spCurrNode = spCurrNode->spNext)
- {
- spDest = (SctpDest_S *) spCurrNode->vpData;
- if(spDest->iNsAddr == iAddr && spDest->iNsPort == iPort)
- return spDest;
- }
-
- return NULL; // we should never get here!
- }
- void SctpAgent::recv(Packet *opInPkt, Handler*)
- {
- /* Let's make sure that a Reset() is called, because it isn't always
- * called explicitly with the "reset" command. For example, wireless
- * nodes don't automatically "reset" their agents, but wired nodes do.
- */
- if(eState == SCTP_STATE_UNINITIALIZED)
- Reset();
- DBG_I(recv);
- hdr_ip *spIpHdr = hdr_ip::access(opInPkt);
- PacketData *opInPacketData = (PacketData *) opInPkt->userdata();
- u_char *ucpInData = opInPacketData->data();
- u_char *ucpCurrInChunk = ucpInData;
- int iRemainingDataLen = opInPacketData->size();
- u_char *ucpOutData = new u_char[uiMaxPayloadSize];
- u_char *ucpCurrOutData = ucpOutData;
- /* local variable which maintains how much data has been filled in the
- * current outgoing packet
- */
- int iOutDataSize = 0;
- memset(ucpOutData, 0, uiMaxPayloadSize);
- memset(spSctpTrace, 0,
- (uiMaxPayloadSize / sizeof(SctpChunkHdr_S)) * sizeof(SctpTrace_S) );
- spReplyDest = GetReplyDestination(spIpHdr);
- eStartOfPacket = TRUE;
- do
- {
- DBG_PL(recv, "iRemainingDataLen=%d"), iRemainingDataLen DBG_PR;
- /* processing chunks may need to generate response chunks, so the
- * current outgoing packet *may* be filled in and our out packet's data
- * size is incremented to reflect the new data
- */
- iOutDataSize += ProcessChunk(ucpCurrInChunk, &ucpCurrOutData);
- NextChunk(&ucpCurrInChunk, &iRemainingDataLen);
- }
- while(ucpCurrInChunk != NULL);
- /* Let's see if we have any response chunks(currently only handshake related)
- * to transmit.
- *
- * Note: We don't bundle these responses (yet!)
- */
- if(iOutDataSize > 0)
- {
- SendPacket(ucpOutData, iOutDataSize, spReplyDest);
- DBG_PL(recv, "responded with control chunk(s)") DBG_PR;
- }
- /* Let's check to see if we need to generate and send a SACK chunk.
- *
- * Note: With uni-directional traffic, SACK and DATA chunks will not be
- * bundled together in one packet.
- * Perhaps we will implement this in the future?
- */
- if(eSackChunkNeeded == TRUE)
- {
- memset(ucpOutData, 0, uiMaxPayloadSize);
- iOutDataSize = BundleControlChunks(ucpOutData);
- iOutDataSize += GenChunk(SCTP_CHUNK_SACK, ucpOutData+iOutDataSize);
- SendPacket(ucpOutData, iOutDataSize, spReplyDest);
- DBG_PL(recv, "SACK sent (%d bytes)"), iOutDataSize DBG_PR;
- eSackChunkNeeded = FALSE; // reset AFTER sent (o/w breaks dependencies)
- }
- /* Do we need to transmit a FORWARD TSN chunk??
- */
- if(eForwardTsnNeeded == TRUE)
- {
- memset(ucpOutData, 0, uiMaxPayloadSize);
- iOutDataSize = BundleControlChunks(ucpOutData);
- iOutDataSize += GenChunk(SCTP_CHUNK_FORWARD_TSN,ucpOutData+iOutDataSize);
- SendPacket(ucpOutData, iOutDataSize, spNewTxDest);
- DBG_PL(recv, "FORWARD TSN chunk sent") DBG_PR;
- eForwardTsnNeeded = FALSE; // reset AFTER sent (o/w breaks dependencies)
- }
- /* Do we want to send out new DATA chunks in addition to whatever we may have
- * already transmitted? If so, we can only send new DATA if no marked chunks
- * are pending retransmission.
- *
- * Note: We aren't bundling with what was sent above, but we could. Just
- * avoiding that for now... why? simplicity :-)
- */
- if(eSendNewDataChunks == TRUE && eMarkedChunksPending == FALSE)
- {
- SendMuch(); // Send new data till our cwnd is full!
- eSendNewDataChunks = FALSE; // reset AFTER sent (o/w breaks dependencies)
- }
- delete hdr_sctp::access(opInPkt)->SctpTrace();
- hdr_sctp::access(opInPkt)->SctpTrace() = NULL;
- Packet::free(opInPkt);
- opInPkt = NULL;
- DBG_X(recv);
- delete [] ucpOutData;
- }
- u_int SctpAgent::TotalOutstanding()
- {
- DBG_I(TotalOutstanding);
- Node_S *spCurrNode = NULL;
- SctpDest_S *spCurrDest = NULL;
- u_int uiTotalOutstanding = 0;
- for(spCurrNode = sDestList.spHead;
- spCurrNode != NULL;
- spCurrNode = spCurrNode->spNext)
- {
- spCurrDest = (SctpDest_S *) spCurrNode->vpData;
- uiTotalOutstanding += spCurrDest->iOutstandingBytes;
- DBG_PL(TotalOutstanding, "spCurrDest->iOutstandingBytes=%lu"),
- spCurrDest->iOutstandingBytes DBG_PR;
- }
- DBG_PL(TotalOutstanding, "%lu"), uiTotalOutstanding DBG_PR;
- DBG_X(TotalOutstanding);
- return uiTotalOutstanding;
- }
- void SctpAgent::SendMuch()
- {
- DBG_I(SendMuch);
- DBG_PL(SendMuch, "eDataSource=%s"),
- ( (eDataSource == DATA_SOURCE_APPLICATION)
- ? "DATA_SOURCE_APPLICATION"
- : "DATA_SOURCE_INFINITE" )
- DBG_PR;
- u_char *ucpOutData = new u_char[uiMaxPayloadSize];
- int iOutDataSize = 0;
- double dCurrTime = Scheduler::instance().clock();
- /* Keep sending out packets until our cwnd is full! The proposed
- * correction to RFC2960 section 6.1.B says "The sender may exceed
- * cwnd by up to (PMTU-1) bytes on a new transmission if the cwnd is
- * not currently exceeded". Hence, as long as cwnd isn't
- * full... send another packet.
- *
- * Also, if our data source is the application layer (instead of the infinite
- * source used for ftp), check if there is any data buffered from the app
- * layer. If so, then send as much as we can.
- */
- while((spNewTxDest->iOutstandingBytes < spNewTxDest->iCwnd) &&
- (eDataSource == DATA_SOURCE_INFINITE || sAppLayerBuffer.uiLength != 0))
- {
- DBG_PL(SendMuch, "uiAdvancedPeerAckPoint=%d uiCumAckPoint=%d"),
- uiAdvancedPeerAckPoint, uiCumAckPoint DBG_PR;
- DBG_PL(SendMuch, "uiPeerRwnd=%d"), uiPeerRwnd DBG_PR;
- DBG_PL(SendMuch, "spNewTxDest->iCwnd=%d"), spNewTxDest->iCwnd DBG_PR;
- DBG_PL(SendMuch, "spNewTxDest->iPartialBytesAcked=%d"),
- spNewTxDest->iPartialBytesAcked DBG_PR;
- DBG_PL(SendMuch, "spNewTxDest->iOutstandingBytes=%d"),
- spNewTxDest->iOutstandingBytes DBG_PR;
- DBG_PL(SendMuch, "TotalOutstanding=%lu"),
- TotalOutstanding() DBG_PR;
- DBG_PL(SendMuch, "eApplyMaxBurst=%s uiBurstLength=%d"),
- eApplyMaxBurst ? "TRUE" : "FALSE", uiBurstLength DBG_PR;
- if(GetNextDataChunkSize() <= uiPeerRwnd)
- {
- /* This addresses the proposed change to RFC2960 section 7.2.4,
- * regarding using of Max.Burst. We have an option which allows
- * to control if Max.Burst is applied.
- */
- if(eUseMaxBurst == MAX_BURST_USAGE_ON)
- if( (eApplyMaxBurst == TRUE) && (uiBurstLength++ >= MAX_BURST) )
- {
- /* we've reached Max.Burst limit, so jump out of loop
- */
- eApplyMaxBurst = FALSE;// reset before jumping out of loop
- break;
- }
- memset(ucpOutData, 0, uiMaxPayloadSize); // reset
- iOutDataSize = BundleControlChunks(ucpOutData);
- iOutDataSize += GenMultipleDataChunks(ucpOutData+iOutDataSize, 0);
- SendPacket(ucpOutData, iOutDataSize, spNewTxDest);
- DBG_PL(SendMuch, "DATA chunk(s) sent") DBG_PR;
- }
- else if(TotalOutstanding() == 0) // section 6.1.A
- {
- /* probe for a change in peer's rwnd
- */
- memset(ucpOutData, 0, uiMaxPayloadSize);
- iOutDataSize = BundleControlChunks(ucpOutData);
- iOutDataSize += GenOneDataChunk(ucpOutData+iOutDataSize);
- SendPacket(ucpOutData, iOutDataSize, spNewTxDest);
- DBG_PL(SendMuch, "DATA chunk probe sent") DBG_PR;
- }
- else
- {
- break;
- }
- }
- if(iOutDataSize > 0) // did we send anything??
- {
- spNewTxDest->opCwndDegradeTimer->resched(spNewTxDest->dRto);
- if(uiHeartbeatInterval != 0)
- {
- spNewTxDest->dIdleSince = dCurrTime;
- }
- }
- /* this reset MUST happen at the end of the function, because the burst
- * measurement is carried over from RtxMarkedChunks() if it was called.
- */
- uiBurstLength = 0;
- DBG_X(SendMuch);
- delete [] ucpOutData;
- }
- void SctpAgent::sendmsg(int iNumBytes, const char *cpFlags)
- {
- /* Let's make sure that a Reset() is called, because it isn't always
- * called explicitly with the "reset" command. For example, wireless
- * nodes don't automatically "reset" their agents, but wired nodes do.
- */
- if(eState == SCTP_STATE_UNINITIALIZED)
- Reset();
- DBG_I(sendmsg);
- u_char *ucpOutData = new u_char[uiMaxPayloadSize];
- int iOutDataSize = 0;
- AppData_S *spAppData = (AppData_S *) cpFlags;
- Node_S *spNewNode = NULL;
- int iMsgSize = 0;
- u_int uiMaxFragSize = uiMaxDataSize - sizeof(SctpDataChunkHdr_S);
- if(iNumBytes == -1)
- eDataSource = DATA_SOURCE_INFINITE; // Send infinite data
- else
- eDataSource = DATA_SOURCE_APPLICATION; // Send data passed from app
-
- if(eDataSource == DATA_SOURCE_APPLICATION)
- {
- if(spAppData != NULL)
- {
- /* This is an SCTP-aware app!! Anything the app passes down
- * overrides what we bound from TCL.
- */
- DBG_PL (sendmsg, "sctp-aware app: iNumBytes=%d"), iNumBytes DBG_PR;
- spNewNode = new Node_S;
- uiNumOutStreams = spAppData->usNumStreams;
- uiNumUnrelStreams = spAppData->usNumUnreliable;
- spNewNode->eType = NODE_TYPE_APP_LAYER_BUFFER;
- spNewNode->vpData = spAppData;
- InsertNode(&sAppLayerBuffer, sAppLayerBuffer.spTail, spNewNode,NULL);
- }
- else
- {
- /* This is NOT an SCTP-aware app!! We rely on TCL-bound variables.
- */
- DBG_PL (sendmsg,"non-sctp-aware app: iNumBytes=%d"),iNumBytes DBG_PR;
- uiNumOutStreams = 1; // non-sctp-aware apps only use 1 stream
- uiNumUnrelStreams = (uiNumUnrelStreams > 0) ? 1 : 0;
- /* To support legacy applications and uses such as "ftp send
- * 12000", we "fragment" the message. _HOWEVER_, this is not
- * REAL SCTP fragmentation!! We do not maintain the same SSN or
- * use the B/E bits. Think of this block of code as a shim which
- * breaks up the message into useable pieces for SCTP.
- */
- for(iMsgSize = iNumBytes;
- iMsgSize > 0;
- iMsgSize -= MIN(iMsgSize, (int) uiMaxFragSize) )
- {
- spNewNode = new Node_S;
- spNewNode->eType = NODE_TYPE_APP_LAYER_BUFFER;
- spAppData = new AppData_S;
- spAppData->usNumStreams = uiNumOutStreams;
- spAppData->usNumUnreliable = uiNumUnrelStreams;
- spAppData->usStreamId = 0;
- spAppData->usReliability = uiReliability;
- spAppData->eUnordered = eUnordered;
- spAppData->uiNumBytes = MIN(iMsgSize, (int) uiMaxFragSize);
- spNewNode->vpData = spAppData;
- InsertNode(&sAppLayerBuffer, sAppLayerBuffer.spTail,
- spNewNode, NULL);
- }
- }
- if(uiNumOutStreams > MAX_NUM_STREAMS)
- {
- fprintf(stderr, "%s number of streams (%d) > max (%d)n",
- "SCTP ERROR:",
- uiNumOutStreams, MAX_NUM_STREAMS);
- DBG_PL(sendmsg, "ERROR: number of streams (%d) > max (%d)"),
- uiNumOutStreams, MAX_NUM_STREAMS DBG_PR;
- DBG_PL(sendmsg, "exiting...") DBG_PR;
- exit(-1);
- }
- else if(uiNumUnrelStreams > uiNumOutStreams)
- {
- fprintf(stderr,"%s number of unreliable streams (%d) > total (%d)n",
- "SCTP ERROR:",
- uiNumUnrelStreams, uiNumOutStreams);
- DBG_PL(sendmsg,
- "ERROR: number of unreliable streams (%d) > total (%d)"),
- uiNumUnrelStreams, uiNumOutStreams DBG_PR;
- DBG_PL(sendmsg, "exiting...") DBG_PR;
- exit(-1);
- }
- if(spAppData->uiNumBytes + sizeof(SctpDataChunkHdr_S)
- > MAX_DATA_CHUNK_SIZE)
- {
- fprintf(stderr, "SCTP ERROR: message size (%d) too bign",
- spAppData->uiNumBytes);
- fprintf(stderr, "%s data chunk size (%d) > max (%d)n",
- "SCTP ERROR:",
- (int) (spAppData->uiNumBytes + sizeof(SctpDataChunkHdr_S)),
- MAX_DATA_CHUNK_SIZE);
- DBG_PL(sendmsg, "ERROR: message size (%d) too big"),
- spAppData->uiNumBytes DBG_PR;
- DBG_PL(sendmsg, "ERROR: data chunk size (%d) > max (%d)"),
- spAppData->uiNumBytes + sizeof(SctpDataChunkHdr_S),
- MAX_DATA_CHUNK_SIZE
- DBG_PR;
- DBG_PL(sendmsg, "exiting...") DBG_PR;
- exit(-1);
- }
- else if(spAppData->uiNumBytes + sizeof(SctpDataChunkHdr_S)
- > uiMaxDataSize)
- {
- fprintf(stderr, "SCTP ERROR: message size (%d) too bign",
- spAppData->uiNumBytes);
- fprintf(stderr,
- "%s data chunk size (%d) + SCTP/IP header(%d) > MTU (%d)n",
- "SCTP ERROR:",
- (int) (spAppData->uiNumBytes + sizeof(SctpDataChunkHdr_S)),
- SCTP_HDR_SIZE + uiIpHeaderSize, uiMtu);
- fprintf(stderr, " %sn",
- "...chunk fragmentation is not yet supported!");
- DBG_PL(sendmsg, "ERROR: message size (%d) too big"),
- spAppData->uiNumBytes DBG_PR;
- DBG_PL(sendmsg, "exiting...") DBG_PR;
- exit(-1);
- }
- }
- switch(eState)
- {
- case SCTP_STATE_CLOSED:
- DBG_PL(sendmsg, "sending INIT") DBG_PR;
- /* This must be done especially since some of the legacy apps use their
- * own packet type (don't ask me why). We need our packet type to be
- * sctp so that our tracing output comes out correctly for scripts, etc
- */
- set_pkttype(PT_SCTP);
- iOutDataSize = GenChunk(SCTP_CHUNK_INIT, ucpOutData);
- opT1InitTimer->resched(spPrimaryDest->dRto);
- eState = SCTP_STATE_COOKIE_WAIT;
- SendPacket(ucpOutData, iOutDataSize, spPrimaryDest);
- break;
-
- case SCTP_STATE_ESTABLISHED:
- if(eDataSource == DATA_SOURCE_APPLICATION)
- {
- SendMuch();
- }
- else if(eDataSource == DATA_SOURCE_INFINITE)
- {
- fprintf(stderr, "[sendmsg] ERROR: unexpected state... %sn",
- "sendmsg called more than once for infinite data");
- DBG_PL(sendmsg,
- "ERROR: unexpected state... %s"),
- "sendmsg called more than once for infinite data" DBG_PR;
- DBG_PL(sendmsg, "exiting...") DBG_PR;
- exit(-1);
- }
- break;
-
- default:
- /* If we are here, we assume the application is trying to send data
- * before the 4-way handshake has completed. ...so buffering the
- * data is ok, but DON'T send it yet!!
- */
- break;
- }
- DBG_X(sendmsg);
- delete [] ucpOutData;
- }
- void SctpAgent::T1InitTimerExpiration()
- {
- DBG_I(T1InitTimerExpiration);
- u_char *ucpOutData = new u_char[uiMaxPayloadSize];
- int iOutDataSize = 0;
- iInitTryCount++;
- if(iInitTryCount > (int) uiMaxInitRetransmits)
- {
- Close();
- }
- else
- {
- spPrimaryDest->dRto *= 2;
- if(spPrimaryDest->dRto > dMaxRto)
- spPrimaryDest->dRto = dMaxRto;
- tdRto++; // trigger changes for trace to pick up
- iOutDataSize = GenChunk(SCTP_CHUNK_INIT, ucpOutData);
- SendPacket(ucpOutData, iOutDataSize, spPrimaryDest);
- opT1InitTimer->resched(spPrimaryDest->dRto);
- }
- DBG_X(T1InitTimerExpiration);
- delete [] ucpOutData;
- }
- void T1InitTimer::expire(Event*)
- {
- opAgent->T1InitTimerExpiration();
- }
- void SctpAgent::T1CookieTimerExpiration()
- {
- DBG_I(T1CookieTimerExpiration);
- u_char *ucpOutData = new u_char[uiMaxPayloadSize];
- int iOutDataSize = 0;
-
- iInitTryCount++;
- if(iInitTryCount > (int) uiMaxInitRetransmits)
- Close();
- else
- {
- spPrimaryDest->dRto *= 2;
- if(spPrimaryDest->dRto > dMaxRto)
- spPrimaryDest->dRto = dMaxRto;
- tdRto++; // trigger changes for trace to pick up
- iOutDataSize = GenChunk(SCTP_CHUNK_COOKIE_ECHO, ucpOutData);
- SendPacket(ucpOutData, iOutDataSize, spPrimaryDest);
- opT1CookieTimer->resched(spPrimaryDest->dRto);
- }
- DBG_X(T1CookieTimerExpiration);
- delete [] ucpOutData;
- }
- void T1CookieTimer::expire(Event*)
- {
- opAgent->T1CookieTimerExpiration();
- }
- void SctpAgent::Close()
- {
- DBG_I(Close);
- Node_S *spCurrNode = NULL;
- SctpDest_S *spCurrDest = NULL;
- eState = SCTP_STATE_CLOSED;
-
- /* stop all timers
- */
- opT1InitTimer->force_cancel();
- opT1CookieTimer->force_cancel();
- opSackGenTimer->force_cancel();
- if(uiHeartbeatInterval != 0)
- {
- opHeartbeatGenTimer->force_cancel();
- }
- for(spCurrNode = sDestList.spHead;
- spCurrNode != NULL;
- spCurrNode = spCurrNode->spNext)
- {
- spCurrDest = (SctpDest_S *) spCurrNode->vpData;
- spCurrDest->opT3RtxTimer->force_cancel();
- spCurrDest->opCwndDegradeTimer->force_cancel();
- spCurrDest->opHeartbeatTimeoutTimer->force_cancel();
- }
- ClearList(&sSendBuffer);
- ClearList(&sAppLayerBuffer);
- ClearList(&sRecvTsnBlockList);
- ClearList(&sDupTsnList);
- DBG_X(Close);
- }
- /* Handles timeouts for both DATA chunks and HEARTBEAT chunks. The actions are
- * slightly different for each. Covers rfc sections 6.3.3 and 8.3.
- */
- void SctpAgent::Timeout(SctpChunkType_E eChunkType, SctpDest_S *spDest)
- {
- DBG_I(Timeout);
- DBG_PL(Timeout, "eChunkType=%s spDest=%p"),
- (eChunkType == SCTP_CHUNK_DATA) ? "DATA" : "HEARTBEAT",
- spDest
- DBG_PR;
- double dCurrTime = Scheduler::instance().clock();
- DBG_PL(Timeout, "dCurrTime=%f"), dCurrTime DBG_PR;
- if(eChunkType == SCTP_CHUNK_DATA)
- {
- spDest->eRtxTimerIsRunning = FALSE;
-
- /* section 7.2.3 of rfc2960 (w/ implementor's guide)
- * NE: 4/29/2007 - This conditioal is used for some reason but for
- * now we dont know why.
- */
- if(spDest->iCwnd > 1 * (int) uiMaxDataSize)
- {
- spDest->iSsthresh
- = MAX(spDest->iCwnd/2, iInitialCwnd * (int) uiMaxDataSize);
- spDest->iCwnd = 1*uiMaxDataSize;
- spDest->iPartialBytesAcked = 0; // reset
- tiCwnd++; // trigger changes for trace to pick up
- }
- spDest->opCwndDegradeTimer->force_cancel();
- /* Cancel any pending RTT measurement on this destination. Stephan
- * Baucke suggested (2004-04-27) this action as a fix for the
- * following simple scenario:
- *
- * - Host A sends packets 1, 2 and 3 to host B, and choses 3 for
- * an RTT measurement
- *
- * - Host B receives all packets correctly and sends ACK1, ACK2,
- * and ACK3.
- *
- * - ACK2 and ACK3 are lost on the return path
- *
- * - Eventually a timeout fires for packet 2, and A retransmits 2
- *
- * - Upon receipt of 2, B sends a cumulative ACK3 (since it has
- * received 2 & 3 before)
- *
- * - Since packet 3 has never been retransmitted, the SCTP code
- * actually accepts the ACK for an RTT measurement, although it
- * was sent in reply to the retransmission of 2, which results
- * in a much too high RTT estimate. Since this case tends to
- * happen in case of longer link interruptions, the error is
- * often amplified by subsequent timer backoffs.
- */
- spDest->eRtoPending = FALSE; // cancel any pending RTT measurement
- }
- DBG_PL(Timeout, "was spDest->dRto=%f"), spDest->dRto DBG_PR;
- spDest->dRto *= 2; // back off the timer
- if(spDest->dRto > dMaxRto)
- spDest->dRto = dMaxRto;
- tdRto++; // trigger changes for trace to pick up
- DBG_PL(Timeout, "now spDest->dRto=%f"), spDest->dRto DBG_PR;
- spDest->iTimeoutCount++;
- spDest->iErrorCount++; // @@@ window probe timeouts should not be counted
- DBG_PL(Timeout, "now spDest->iErrorCount=%d"), spDest->iErrorCount DBG_PR;
- if(spDest->eStatus == SCTP_DEST_STATUS_ACTIVE)
- {
- iAssocErrorCount++;
- DBG_PL(Timeout, "now iAssocErrorCount=%d"), iAssocErrorCount DBG_PR;
- // Path.Max.Retrans exceeded?
- if(spDest->iErrorCount > (int) uiPathMaxRetrans)
- {
- spDest->eStatus = SCTP_DEST_STATUS_INACTIVE;
- if(spDest == spNewTxDest)
- {
- spNewTxDest = GetNextDest(spDest);
- DBG_PL(Timeout, "failing over from %p to %p"),
- spDest, spNewTxDest DBG_PR;
- }
- }
- if(iAssocErrorCount > (int) uiAssociationMaxRetrans)
- {
- /* abruptly close the association! (section 8.1)
- */
- DBG_PL(Timeout, "abruptly closing the association!") DBG_PR;
- Close();
- DBG_X(Timeout);
- return;
- }
- }
- // trace it!
- tiTimeoutCount++;
- tiErrorCount++;
- if(spDest->iErrorCount > (int) uiChangePrimaryThresh &&
- spDest == spPrimaryDest)
- {
- spPrimaryDest = spNewTxDest;
- DBG_PL(Timeout, "changing primary from %p to %p"),
- spDest, spNewTxDest DBG_PR;
- }
- if(eChunkType == SCTP_CHUNK_DATA)
- {
- TimeoutRtx(spDest);
- if(spDest->eStatus == SCTP_DEST_STATUS_INACTIVE &&
- uiHeartbeatInterval!=0)
- SendHeartbeat(spDest); // just marked inactive, so send HB immediately
- }
- else if(eChunkType == SCTP_CHUNK_HB)
- {
- if( (uiHeartbeatInterval != 0) ||
- (spDest->eStatus == SCTP_DEST_STATUS_UNCONFIRMED))
-
- SendHeartbeat(spDest);
- }
- DBG_X(Timeout);
- }
- void T3RtxTimer::expire(Event*)
- {
- opAgent->Timeout(SCTP_CHUNK_DATA, spDest);
- }
- void HeartbeatTimeoutTimer::expire(Event*)
- {
- /* Track HB-Timer for CMT-PF
- */
- spDest->eHBTimerIsRunning = FALSE;
- opAgent->Timeout(SCTP_CHUNK_HB, spDest);
- }
- void SctpAgent::CwndDegradeTimerExpiration(SctpDest_S *spDest)
- {
- DBG_I(CwndDegradeTimerExpiration);
- DBG_PL(CwndDegradeTimerExpiration, "spDest=%p"), spDest DBG_PR;
- if(spDest->iCwnd > iInitialCwnd * (int) uiMaxDataSize)
- {
- spDest->iCwnd = MAX(spDest->iCwnd/2, iInitialCwnd * (int) uiMaxDataSize);
- tiCwnd++; // trigger changes for trace to pick up
- }
- spDest->opCwndDegradeTimer->resched(spDest->dRto);
- DBG_X(CwndDegradeTimerExpiration);
- }
- void CwndDegradeTimer::expire(Event*)
- {
- opAgent->CwndDegradeTimerExpiration(spDest);
- }
- void SctpAgent::SendHeartbeat(SctpDest_S *spDest)
- {
- DBG_I(SendHeartbeat);
- DBG_PL(SendHeartbeat, "spDest=%p"), spDest DBG_PR;
- SctpHeartbeatChunk_S sHeartbeatChunk;
- double dCurrTime = Scheduler::instance().clock();
- GenChunk(SCTP_CHUNK_HB, (u_char *) &sHeartbeatChunk); // doesn't fill dest
- sHeartbeatChunk.spDest = spDest; // ...so we fill it here :-)
- SendPacket((u_char *) &sHeartbeatChunk, SCTP_CHUNK_HEARTBEAT_LENGTH, spDest);
- spDest->dIdleSince = dCurrTime;
-
- spDest->opHeartbeatTimeoutTimer->resched(spDest->dRto);
-
- /* Track HB-Timer for CMT-PF
- */
- spDest->eHBTimerIsRunning = TRUE;
- DBG_PL(SendHeartbeat, "HEARTBEAT times out at %f"),
- spDest->dRto+dCurrTime DBG_PR;
- DBG_X(SendHeartbeat);
- }
- void SctpAgent::HeartbeatGenTimerExpiration(double dTimerStartTime)
- {
- DBG_I(HeartbeatGenTimerExpiration);
- Node_S *spCurrNode = NULL;
- SctpDest_S *spCurrNodeData = NULL;
- Node_S *spLongestIdleNode = NULL;
- SctpDest_S *spLongestIdleNodeData = NULL;
- double dCurrTime = Scheduler::instance().clock();
- double dTime;
- DBG_PL(HeartbeatGenTimerExpiration, "finding the longest idle dest...")
- DBG_PR;
- /* find the destination which has been idle the longest
- */
- for(spCurrNode = spLongestIdleNode = sDestList.spHead;
- spCurrNode != NULL;
- spCurrNode = spCurrNode->spNext)
- {
- spCurrNodeData = (SctpDest_S *) spCurrNode->vpData;
- spLongestIdleNodeData = (SctpDest_S *) spLongestIdleNode->vpData;
-
- DBG_PL(HeartbeatGenTimerExpiration, "spDest=%p idle since %f"),
- spCurrNodeData, spCurrNodeData->dIdleSince DBG_PR;
-
- if(spCurrNodeData->dIdleSince < spLongestIdleNodeData->dIdleSince)
- spLongestIdleNode = spCurrNode;
- }
-
- /* has it been idle long enough?
- */
- spLongestIdleNodeData = (SctpDest_S *) spLongestIdleNode->vpData;
- DBG_PL(HeartbeatGenTimerExpiration, "longest idle dest since %f"),
- spLongestIdleNodeData->dIdleSince DBG_PR;
- DBG_PL(HeartbeatGenTimerExpiration, "timer start time %f"),
- dTimerStartTime DBG_PR;
- if(spLongestIdleNodeData->dIdleSince <= dTimerStartTime)
- SendHeartbeat(spLongestIdleNodeData);
- else
- DBG_PL(HeartbeatGenTimerExpiration,
- "longest idle dest not idle long enough!") DBG_PR;
-
- /* start the timer again...
- */
- dTime = CalcHeartbeatTime(spLongestIdleNodeData->dRto);
- opHeartbeatGenTimer->resched(dTime);
- opHeartbeatGenTimer->dStartTime = dCurrTime;
-
- DBG_X(HeartbeatGenTimerExpiration);
- }
- void HeartbeatGenTimer::expire(Event*)
- {
- opAgent->HeartbeatGenTimerExpiration(dStartTime);
- }
- void SctpAgent::SackGenTimerExpiration() // section 6.2
- {
- DBG_I(SackGenTimerExpiration);
- u_char *ucpOutData = new u_char[uiMaxPayloadSize];
- int iOutDataSize = 0;
- memset(ucpOutData, 0, uiMaxPayloadSize);
- iDataPktCountSinceLastSack = 0; // reset
- iOutDataSize = BundleControlChunks(ucpOutData);
- iOutDataSize += GenChunk(SCTP_CHUNK_SACK, ucpOutData+iOutDataSize);
- SendPacket(ucpOutData, iOutDataSize, spReplyDest);
- DBG_PL(SackGenTimerExpiration, "SACK sent (%d bytes)"), iOutDataSize DBG_PR;
- DBG_X(SackGenTimerExpiration);
- delete [] ucpOutData;
- }
- void SackGenTimer::expire(Event*)
- {
- opAgent->SackGenTimerExpiration();
- }
- void SctpAgent::RouteCacheFlushTimerExpiration(SctpDest_S *spDest)
- {
- DBG_I(RouteCacheFlushTimerExpiration);
- DBG_PL(RouteCacheFlushTimerExpiration, "spDest=%p"), spDest DBG_PR;
- double dCurrTime = Scheduler::instance().clock();
- DBG_PL(RouteCacheFlushTimerExpiration, "dCurrTime=%f"), dCurrTime DBG_PR;
- spDest->eRouteCached = FALSE;
- DBG_X(RouteCacheFlushTimerExpiration);
- }
- void RouteCacheFlushTimer::expire(Event*)
- {
- opAgent->RouteCacheFlushTimerExpiration(spDest);
- }
- void SctpAgent::RouteCalcDelayTimerExpiration(SctpDest_S *spDest)
- {
- DBG_I(RouteCalcDelayTimerExpiration);
- DBG_PL(RouteCalcDelayTimerExpiration, "spDest=%p"), spDest DBG_PR;
- double dCurrTime = Scheduler::instance().clock();
- DBG_PL(RouteCalcDelayTimerExpiration, "dCurrTime=%f"), dCurrTime DBG_PR;
- Node_S *spCurrNode = spDest->sBufferedPackets.spHead;
- Node_S *spDeleteNode = NULL;
- spDest->iRcdCount++;
- tiRcdCount++;
- spDest->eRouteCached = TRUE;
- SetSource(spDest); // set src addr, port, target based on "routing table"
- SetDestination(spDest); // set dest addr & port
- /* Dequeue and send all queued packets
- */
- while(spCurrNode != NULL)
- {
- send( (Packet *) spCurrNode->vpData, 0);
- spDeleteNode = spCurrNode;
- spCurrNode = spCurrNode->spNext;
- DeleteNode(&spDest->sBufferedPackets, spDeleteNode);
- }
- DBG_X(RouteCalcDelayTimerExpiration);
- }
- void RouteCalcDelayTimer::expire(Event*)
- {
- opAgent->RouteCalcDelayTimerExpiration(spDest);
- }
- /****************************************************************************
- * debugging functions
- ****************************************************************************/
- void SctpAgent::DumpSendBuffer()
- {
- DBG_IF(DumpSendBuffer)
- {
- DBG_I(DumpSendBuffer);
- Node_S *spCurrNode = NULL;
- SctpSendBufferNode_S *spCurrNodeData = NULL;
- SctpDataChunkHdr_S *spChunk = NULL;
- for(spCurrNode = sSendBuffer.spHead;
- spCurrNode != NULL;
- spCurrNode = spCurrNode->spNext)
- {
- spCurrNodeData = (SctpSendBufferNode_S *) spCurrNode->vpData;
- spChunk = spCurrNodeData->spChunk;
- DBG_PL(DumpSendBuffer,
- "tsn=%d spDest=%p eMarkedForRtx=%s %s %s"),
- spChunk->uiTsn,
- spCurrNodeData->spDest,
- (!spCurrNodeData->eMarkedForRtx ? "NO_RTX"
- : (spCurrNodeData->eMarkedForRtx==FAST_RTX ? "FAST_RTX"
- : "TIMEOUT_RTX")),
- spCurrNodeData->eMarkedForRtx ? "MarkedForRtx" : "",
- spCurrNodeData->eGapAcked ? "GapAcked" : "",
- spCurrNodeData->eAdvancedAcked ? "AdvancedAcked" : ""
- DBG_PR;
- DBG_PL(DumpSendBuffer,
- " spCurrNodeData->spDest->iOutstandingBytes=%lu"),
- spCurrNodeData->spDest->iOutstandingBytes DBG_PR;
- }
- DBG_X(DumpSendBuffer);
- }
- }