sctp-cmt.cc
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:140k
- spCurrNodeData->eGapAcked = TRUE;
- /****** Begin CMT Change ******/
- /* CMT-PF: if the gapacked chunk was new data sent to a
- * PF dest, then, mark destination to ACTIVE; else,
- * ambiguity in deciding whether gap-ack was for the
- * original transmission or retransmission.
- */
- if ((spCurrNodeData->spDest->eStatus ==
- SCTP_DEST_STATUS_POSSIBLY_FAILED) &&
- (spCurrNodeData->iNumTxs == 1))
- {
- spCurrNodeData->spDest->eStatus=SCTP_DEST_STATUS_ACTIVE;
- spCurrNodeData->spDest->iErrorCount = 0; //clr err ctr
- tiErrorCount++; // ... and trace it too!
- DBG_PL(ProcessGapAckBlocks,
- "Dest:%p, New data gapacked. Mark from PF to Active"),
- spCurrNodeData->spDest DBG_PR;
- }
- /* CMT CUC algo: If newly acked TSN passes
- * pseudo-cumack, we have a new pseudo-cumack!
- * Increment cwnd accordingly.
- */
- if (spCurrNodeData->spChunk->uiTsn ==
- spCurrNodeData->spDest->uiExpectedPseudoCum) {
- spCurrNodeData->spDest->eNewPseudoCum = TRUE;
- spCurrNodeData->spDest->eFindExpectedPseudoCum = TRUE;
- }
- if (spCurrNodeData->spChunk->uiTsn ==
- spCurrNodeData->spDest->uiExpectedRtxPseudoCum) {
- spCurrNodeData->spDest->eNewPseudoCum = TRUE;
- spCurrNodeData->spDest->eFindExpectedRtxPseudoCum = TRUE;
- }
- /* SFR algo: Mark eSawNewAck for newly acked destination and
- * update highest TSN seen in current SACK (per destination)
- * if appropriate. uiHighestTsnInSackForDest is used for
- * HTNA also. We do not need uiHighestTsnNewlyAcked with
- * the SFR algo, hence commented out.
- */
- spCurrNodeData->spDest->eSawNewAck = TRUE;
- if (spCurrNodeData->spDest->uiHighestTsnInSackForDest <
- spCurrNodeData->spChunk->uiTsn)
- {
- spCurrNodeData->spDest->uiHighestTsnInSackForDest =
- spCurrNodeData->spChunk->uiTsn;
- }
-
- /* CMT DAC algo: Update lowest TSN seen in current SACK
- * (per destination) if appropriate. The lowest TSN is
- * initialized to zero, and since we process cum and gap
- * TSNs in TSN order, the first value this variable is
- * set to will continue to be the lowest TSN
- * acked. uiLowestTsnInSackForDest is used for missing
- * report increments with delayed SACKs in CMT.
- */
- if (spCurrNodeData->spDest->uiLowestTsnInSackForDest == 0)
- {
- spCurrNodeData->spDest->uiLowestTsnInSackForDest =
- spCurrNodeData->spChunk->uiTsn;
- }
- /****** End CMT Change ******/
-
- /* 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;
- }
-
- /****** Begin CMT Change ******/
- /* 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
- */
- /* CMT change: If new pseudo-cumack, then adjust cwnd.
- * This check looks a little ugly, so look closely.
- */
- /* JRI-TODO: Remove Check for CmtCwnd
- */
- if((spCurrNodeData->spDest->iCwnd
- > spCurrNodeData->spDest->iSsthresh) &&
- ((eUseCmtCwnd)&&(spCurrNodeData->spDest->eNewPseudoCum) ||
- (!eUseCmtCwnd) && (eNewCumAck)) &&
- (spCurrNodeData->eAddedToPartialBytesAcked == FALSE))
- /****** End CMT Change ******/
- {
- DBG_PL(ProcessGapAckBlocks,
- "setting eAddedToPartiallyBytesAcked=TRUE")DBG_PR;
-
- spCurrNodeData->eAddedToPartialBytesAcked = TRUE; // set
- spCurrNodeData->spDest->iPartialBytesAcked
- += spCurrNodeData->spChunk->sHdr.usLength;
- }
- /****** Begin CMT Change ******/
- /* We update the RTT estimate if the following hold true:
- * 1. RTO pending flag is set (6.3.1.C4)
- * 2. RTT is being measured 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->eMeasuringRtt == TRUE &&
- spCurrNodeData->iNumTxs == 1 &&
- spCurrNodeData->eAdvancedAcked == FALSE)
- /****** End CMT Change ******/
- {
- /* 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;
- /****** Begin CMT Change ******/
- /* CMT DAC algo: The SACK chunk flags are unused in RFC2960. We
- * propose to use 1 of the 8 bits in the SACK flags for the CMT
- * delayed ack algo. This bit will be used to indicate the number
- * of DATA packets were received between the previous and the
- * current SACK. This information will be used by the DATA sender to
- * increment missing reports better in CMT.
- *
- ** If the SACK is a mixed SACK (multiple dests being acked) then
- * the sender conservatively increases missing reports by 1.
- ** If not a mixed SACK, and TSNs are acked across a lost TSN t
- * (t- and t+ TSNs being acked by this SACK), sender conservatively
- * can use only 1 missing report.
- ** If not a mixed SACK, and TSNs are not acked across lost TSN t,
- * then the sender can use number of packets reported by the SACK
- * as the number of missing reports to be used.
- *
- * For 2 latter cases, sender needs to know lowest TSN and highest TSN
- * acked in the current SACK for the destination for which missing
- * reports are to be incremented. If the TSN to be marked is between
- * the highest and the lowest, then the SACK should be treated as
- * acking across the missing TSN. This code appears further below.
- */
- uiNumDestsSacked = 0;
- for(spCurrDestNode = sDestList.spHead;
- spCurrDestNode != NULL;
- spCurrDestNode = spCurrDestNode->spNext)
- {
- spCurrDestNodeData = (SctpDest_S *) spCurrDestNode->vpData;
- if (spCurrDestNodeData->eSawNewAck)
- uiNumDestsSacked++;
- }
- DBG_PL(ProcessGapAckBlocks, "number of dests SACKed = %d"),
- uiNumDestsSacked 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)
- {
- /* Applying SFR. The modified check satisfies SFR and
- * handles part of HTNA per destination. Does not take care
- * of the HTNA algo during Fast Recovery.
- */
- /* JRI-TODO: Look into HTNA bit during Fast Recovery,
- * and how it can be implemented.
- */
- if (((eUseCmtReordering == FALSE) &&
- (spCurrNodeData->spChunk->uiTsn < uiHighestTsnNewlyAcked))
- ||
- // ((eUseCmtReordering == FALSE) && (eNewCumAck == TRUE) &&
- // (uiHighestTsnNewlyAcked <= uiRecover) &&
- // (spCurrNodeData->spChunk->uiTsn < uiHighestTsnSacked)) ||
- ((eUseCmtReordering == TRUE) &&
- (spCurrNodeData->spDest->eSawNewAck) &&
- (spCurrNodeData->spChunk->uiTsn <
- spCurrNodeData->spDest->uiHighestTsnInSackForDest)))
- {
- /* CMT DAC algo: For 2 latter cases mentioned above,
- * sender needs to know lowest TSN and highest TSN acked
- * in the current SACK for the destination for which
- * missing reports are to be incremented. If the TSN to
- * be marked is between the highest and the lowest, then
- * the SACK should be treated as acking across the
- * missing TSN.
- */
- if (eUseCmtDelAck == TRUE)
- {
- if ((uiNumDestsSacked > 1) ||
- (spCurrNodeData->spDest->uiLowestTsnInSackForDest <
- spCurrNodeData->spChunk->uiTsn))
- {
- spCurrNodeData->iNumMissingReports += 1;
- } else if (uiNumDestsSacked == 1) {
- spCurrNodeData->iNumMissingReports +=
- uiNumPacketsSacked;
- } else {/* do nothing if uiNumDestsSacked < 1 ! */}
- } else {
- spCurrNodeData->iNumMissingReports++;
- }
- /****** End CMT Change ******/
- 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 SctpCMTAgent::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;
- /****** Begin CMT Change ******/
- Node_S *spCurrBuffNode = NULL;
- SctpSendBufferNode_S *spCurrBuffNodeData = NULL;
- SctpDataChunkHdr_S *spCurrChunk;
- /* CMT DAC algo: The SACK chunk flags are unused in RFC2960. We propose
- * to use 2 of the 8 bits in the SACK flags for the CMT delayed ack
- * algo. These bits will be used to indicate the number of DATA packets
- * were received between the previous and the current SACK. This
- * information will be used by the DATA sender to increment missing
- * reports better in CMT.
- */
- uiNumPacketsSacked = spSackChunk->sHdr.ucFlags;
- DBG_PL(ProcessSackChunk, "Sack Flags=%d"), spSackChunk->sHdr.ucFlags DBG_PR;
- /****** End CMT Change ******/
-
- /* 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;
- /****** Begin CMT Change ******/
- spCurrDestNodeData->eNewPseudoCum = FALSE;
- spCurrDestNodeData->eSawNewAck = FALSE;
- /* CMT DAC algo: Initialize uiLowestTsn to zero, this variable
- * should get set during SACK processing to some TSN.
- */
- spCurrDestNodeData->uiLowestTsnInSackForDest = 0;
- spCurrDestNodeData->uiBurstLength = 0;
- /****** End CMT Change ******/
- }
- 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
- }
- /****** Begin CMT Change ******/
- /* CMT change In case of reneg, or reordered SACKs where previously
- * present gapack info is now absent, arwnd info can be stale and
- * dangerous if used without consideration to the fact that the receiver
- * has apparently reneged on data. Note that ProcessGapAckBlocks was
- * previously not called when gapacks were absent in the SACK. What if
- * previously present gapacks were reneged? Since we do not reneg in
- * this implementation, this can happen only with reordered SACKs. In
- * any case, ProcessGapAckBlocks() has code that will handle such
- * apparent reneging, i.e., calculate the correct new
- * outstanding_bytes. Therefore, this function should be called even if
- * there are no gapack blocks in the SACK chunk to handle renegs, or
- * reordered SACKs with no gapack info.
- *
- * if(spSackChunk->usNumGapAckBlocks != 0) // are there any gaps??
- * {
- * eFastRtxNeeded = ProcessGapAckBlocks(ucpSackChunk, eNewCumAck);
- * }
- */
- /* JRI-TODO: Move change to SCTP code. This problem can be caused by
- * reordered SACKs too.The SCTP code handles calculation of outstanding bytes
- * when SACK is reordered, so this check should be removed there too.
- */
- eFastRtxNeeded = ProcessGapAckBlocks(ucpSackChunk, eNewCumAck);
- /* TEMP-FIX: Calculate the correct outstanding bytes in each dest, since
- * even data that is marked for rtx is still considered oustanding. The
- * value of outstanding_bytes is restored later below. Need this to stop
- * T3 timers correctly. This problem needs to be fixed here, and in SCTP.
- */
- for(spCurrBuffNode = sSendBuffer.spHead;
- spCurrBuffNode != NULL;
- spCurrBuffNode = spCurrBuffNode->spNext)
- {
- spCurrBuffNodeData = (SctpSendBufferNode_S *) spCurrBuffNode->vpData;
- if(spCurrBuffNodeData->eMarkedForRtx != NO_RTX)
- {
- spCurrChunk = spCurrBuffNodeData->spChunk;
- spCurrBuffNodeData->spDest->iOutstandingBytes
- -= spCurrChunk->sHdr.usLength;
- }
- }
- /****** End CMT Change ******/
-
- for(spCurrDestNode = sDestList.spHead;
- spCurrDestNode != NULL;
- spCurrDestNode = spCurrDestNode->spNext)
- {
- spCurrDestNodeData = (SctpDest_S *) spCurrDestNode->vpData;
- /****** Begin CMT Change ******/
- /* Adjust cwnd if sack advanced the pseudo-cumack point AND this
- * destination has newly acked bytes. Also, we MUST adjust our
- * congestion window BEFORE we update the number of outstanding
- * bytes to reflect the newly acked bytes in received SACK.
- */
- DBG_PL(ProcessSackChunk, "eNewPseudoCum=%d, newlyacked=%d"),
- spCurrDestNodeData->eNewPseudoCum,
- spCurrDestNodeData->iNumNewlyAckedBytes DBG_PR;
- if (eUseCmtCwnd == TRUE)
- {
- if((spCurrDestNodeData->eNewPseudoCum == TRUE) &&
- (spCurrDestNodeData->iNumNewlyAckedBytes > 0))
- AdjustCwnd(spCurrDestNodeData);
- }
- else if (eUseCmtCwnd == FALSE)
- {
- if(eNewCumAck == TRUE && spCurrDestNodeData->iNumNewlyAckedBytes > 0)
- AdjustCwnd(spCurrDestNodeData);
- }
- /****** End CMT Change ******/
- /* 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);
- }
- }
- /****** Begin CMT Change ******/
- /* TEMP-FIX: Restore value of outstanding_bytes. See TEMP-FIX comment
- * above for details.
- */
- for(spCurrBuffNode = sSendBuffer.spHead;
- spCurrBuffNode != NULL;
- spCurrBuffNode = spCurrBuffNode->spNext)
- {
- spCurrBuffNodeData = (SctpSendBufferNode_S *) spCurrBuffNode->vpData;
-
- if(spCurrBuffNodeData->eMarkedForRtx != NO_RTX)
- {
- spCurrChunk = spCurrBuffNodeData->spChunk;
- spCurrBuffNodeData->spDest->iOutstandingBytes
- += spCurrChunk->sHdr.usLength;
- }
- }
- /****** End CMT Change ******/
- DBG_F(ProcessSackChunk, DumpSendBuffer());
- AdvancePeerAckPoint();
- /****** Begin CMT Change ******/
- uiArwnd = spSackChunk->uiArwnd;
- /****** End CMT Change ******/
- 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;
- tiRwnd++; // trigger changes to be traced
- DBG_PL(ProcessSackChunk, "uiPeerRwnd=%d, uiArwnd=%d"), uiPeerRwnd,
- spSackChunk->uiArwnd DBG_PR;
- DBG_X(ProcessSackChunk);
- }
- int SctpCMTAgent::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;
- /****** Begin CMT Change ******/
- Boolean_E eFoundNonPFDest = FALSE;
- Boolean_E eCmtPFCwndChange = FALSE;
- u_int uiTotalOutstanding = 0;
- Boolean_E eThisDestWasInactive = FALSE; /* For triggering data tx in CMT */
- /****** End CMT Change ******/
- Boolean_E eThisDestWasUnconfirmed = FALSE;
- Boolean_E eFoundUnconfirmedDest = FALSE;
- 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;
- /* PN: Confirming Destinations
- * RFC allows to send data to a confirmed destination.
- * However, this implementation is more conservative than
- * the RFC, since, data is sent to any destionation only if
- * all destinations have been confirmed.
- */
- 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);
- }
-
- 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)
- {
- /****** Begin CMT Change ******/
- /* CMT change: Do not reset packet count yet ! Pkt
- * count is put into SACK flags for CMT delayed ack
- * algo. Hence reset only after putting into SACK
- * flags.
- *
- * iDataPktCountSinceLastSack = 0; // reset
- */
- /****** End CMT Change ******/
- opSackGenTimer->force_cancel();
- eSackChunkNeeded = TRUE;
- }
- }
- }
- ProcessDataChunk( (SctpDataChunkHdr_S *) ucpInChunk );
- /****** Begin CMT Change ******/
- /* 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 CMT DAC algo, always delay SACKs; sender has the
- * responsibility of handling missing report counts according to
- * number of packets acked.
- */
- if((eUseCmtDelAck == FALSE) && (sRecvTsnBlockList.uiLength > 0))
- {
- iDataPktCountSinceLastSack = 0; // reset
- opSackGenTimer->force_cancel();
- eSackChunkNeeded = TRUE;
- }
- /****** End CMT Change ******/
- /* 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;
- /****** Begin CMT Change ******/
- spHeartbeatAckChunk = (SctpHeartbeatAckChunk_S *) ucpInChunk;
- /* CMT-PF: Is HB-ACK on a PF path?
- */
- if (spHeartbeatAckChunk->spDest->eStatus ==
- SCTP_DEST_STATUS_POSSIBLY_FAILED)
- {
- eCmtPFCwndChange = TRUE;
- DBG_PL(ProcessChunk, "HB-ACK: dest:%p, status:%s"),
- spHeartbeatAckChunk->spDest,
- PrintDestStatus(spHeartbeatAckChunk->spDest) DBG_PR;
- }
-
- if (spHeartbeatAckChunk->spDest->eStatus ==
- SCTP_DEST_STATUS_INACTIVE)
- eThisDestWasInactive = TRUE;
- /* CMT-PF: Are there any non-PF destinations? Need this info
- * for HB timer processing further below.
- */
- for(spCurrNode = sDestList.spHead;
- spCurrNode != NULL;
- spCurrNode = spCurrNode->spNext)
- {
- spCurrDest = (SctpDest_S *) spCurrNode->vpData;
- if (spCurrDest->eStatus != SCTP_DEST_STATUS_POSSIBLY_FAILED)
- {
- eFoundNonPFDest = TRUE;
- DBG_PL(ProcessChunk, "Found non PF dest=%p"),
- spCurrDest DBG_PR;
- /* break; */
- }
- }
- /* PN: Is this destination unconfirmed?
- */
- if (spHeartbeatAckChunk->spDest->eStatus ==
- SCTP_DEST_STATUS_UNCONFIRMED)
- eThisDestWasUnconfirmed = TRUE;
-
- ProcessHeartbeatAckChunk( (SctpHeartbeatAckChunk_S *) ucpInChunk);
- /* PN: If this destination was unconfirmed and all other
- * destinations have been confirmed, then allow data
- * transmission on the association
- */
- 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 case HB_ACK chunk */
- else {
-
- /* All destinations have been confirmed.
- * Start new data transfer
- */
- eSendNewDataChunks = TRUE;
- /* Process HeartbeatTimers for the association.
- * This code was originally in the COOKIE ACK case above
- */
- 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; /* From case HB_ACK */
-
- } /* End else eFoundUnconfirmedDest == TRUE */
-
- } /* End of if (eThisDestWasUnconfirmed == TRUE) */
-
- /* CMT-PF: Set cwnd to uiCmtPFCwnd MTUs if HB-ack was received from a
- * destination marked PF
- */
- if ((eCmtPFCwndChange == TRUE) || (eThisDestWasInactive == TRUE))
- {
- if (eCmtPFCwndChange == TRUE)
- {
-
- spHeartbeatAckChunk->spDest->iCwnd
- = uiCmtPFCwnd*uiMaxDataSize;
- DBG_PL(ProcessChunk, "dest=%p, CMTPFCwnd=%ld"),
- spHeartbeatAckChunk->spDest,
- spHeartbeatAckChunk->spDest->iCwnd DBG_PR;
- //tiCwnd++; // for trace
-
- spHeartbeatAckChunk->spDest->dPFSince = 0; //unset
- }
- /* CMT: Since all paths were INACTIVE and one of them has
- * become ACTIVE now. First send retransmissions to the
- * ACTIVE destination.
- *
- * CMT-PF: Since a PF destination has become ACTIVE, first
- * send retransmissions to the ACTIVE destination.
- */
- TimeoutRtx(spHeartbeatAckChunk->spDest);
-
- /* CMT, CMT-PF: If no marked chunks, send new data
- */
- if (AnyMarkedChunks() == FALSE)
- {
- /* All marked chunks are in flight or have been sacked.
- * If they are in flight, then peerRwnd does not reflect
- * these outstanding bytes. To recap: MarkChunksForRtx
- * marks chunks and adds their size to
- * peerRwnd. RtxMarkedChunks does not decrement peerRwnd
- * when these chunks are rtxmed. So right now, we have
- * incorrect view of peerRwnd. If peerRwnd is > than
- * what it should be, sending new data in the space will
- * cause problems. (How?: rtx tsns were lost and before
- * this was detected, new data sent fills up
- * peerRwnd. In that case, recv silently discards later
- * rtxms and sender keeps timing out on that rtxms.
- * Deadlock !)
- *
- * So update peerRwnd based on the last advertised
- * receiver window.
- */
- uiTotalOutstanding = TotalOutstanding();
- if(uiTotalOutstanding <= uiArwnd)
- uiPeerRwnd = (uiArwnd - uiTotalOutstanding);
- else
- uiPeerRwnd = 0;
-
- DBG_PL(ProcessChunk,"uiPeerRwnd=%d, uiArwnd=%d"), uiPeerRwnd,
- uiArwnd DBG_PR;
-
- /* Set flag for SendMuch to send new data.
- */
- eSendNewDataChunks = TRUE;
- }
- }
-
- 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;
- }
- /* This function is called as soon as we are done processing a SACK which
- * notifies the need of a fast rtx. Following RFC2960, we pack as many chunks
- * as possible into one packet (PTMU restriction). The remaining marked packets
- * are sent as soon as cwnd allows.
- */
- void SctpCMTAgent::FastRtx()
- {
- DBG_I(FastRtx);
- Node_S *spCurrDestNode = NULL;
- SctpDest_S *spCurrDestData = NULL;
- Node_S *spCurrBuffNode = NULL;
- SctpSendBufferNode_S *spCurrBuffData = NULL;
- /* be sure we clear all the eCcApplied flags before using them!
- */
- for(spCurrDestNode = sDestList.spHead;
- spCurrDestNode != NULL;
- spCurrDestNode = spCurrDestNode->spNext)
- {
- spCurrDestData = (SctpDest_S *) spCurrDestNode->vpData;
- spCurrDestData->eCcApplied = FALSE;
- /****** Begin CMT Change *******/
- /* JRI-TODO: Why do I need RTX_LIMIT_ZERO? Why not use LIMIT_CWND as
- * the default?
- */
- spCurrDestData->eRtxLimit = RTX_LIMIT_ZERO;
- /****** End CMT Change *******/
- }
-
- /* go thru chunks marked for rtx and cut back their ssthresh, cwnd, and
- * partial bytes acked. make sure we only apply congestion control once
- * per destination and once per window (ie, round-trip).
- */
- for(spCurrBuffNode = sSendBuffer.spHead;
- spCurrBuffNode != NULL;
- spCurrBuffNode = spCurrBuffNode->spNext)
- {
- spCurrBuffData = (SctpSendBufferNode_S *) spCurrBuffNode->vpData;
- /* If the chunk has been either marked for rtx or advanced ack, we want
- * to apply congestion control (assuming we didn't already).
- *
- * Why do we do it for advanced ack chunks? Well they were advanced ack'd
- * because they were lost. The ONLY reason we are not fast rtxing them is
- * because the chunk has run out of retransmissions (u-sctp). So we need
- * to still account for the fact they were lost... so apply congestion
- * control!
- */
- /****** Begin CMT Change *******/
- /* CMT: use per-destination uiRecover
- */
- if((spCurrBuffData->eMarkedForRtx != NO_RTX ||
- spCurrBuffData->eAdvancedAcked == TRUE) &&
- spCurrBuffData->spDest->eCcApplied == FALSE &&
- spCurrBuffData->spChunk->uiTsn > spCurrBuffData->spDest->uiRecover)
- /****** End CMT Change *******/
- {
- /* section 7.2.3 of rfc2960 (w/ implementor's guide)
- */
- spCurrBuffData->spDest->iSsthresh
- = MAX(spCurrBuffData->spDest->iCwnd/2,
- iInitialCwnd * (int) uiMaxDataSize);
- spCurrBuffData->spDest->iCwnd = spCurrBuffData->spDest->iSsthresh;
- spCurrBuffData->spDest->iPartialBytesAcked = 0; //reset
- tiCwnd++; // trigger changes for trace to pick up
- spCurrBuffData->spDest->eCcApplied = TRUE;
- /* Cancel any pending RTT measurement on this
- * destination. Stephan Baucke (2004-04-27) suggested 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.
- */
- spCurrBuffData->spDest->eRtoPending = FALSE;
- /****** Begin CMT Change *******/
- /* Set the recover variable to avoid multiple cwnd cuts for losses
- * in the same window (ie, round-trip).
- */
- spCurrBuffData->spDest->uiRecover =
- GetHighestOutstandingTsn(spCurrBuffData->spDest);
- /****** End CMT Change *******/
- }
- /****** Begin CMT Change *******/
- /* To specify which destinationto apply LIMIT_ONE_PACKET to.
- * Earlier, every destination got one packet rtx out when
- * rtxmarkedchunks was called with LIMIT_ONE_PACKET.
- */
- if(spCurrBuffData->eMarkedForRtx == FAST_RTX)
- {
- spCurrBuffData->spDest->eRtxLimit = RTX_LIMIT_ONE_PACKET;
- }
- /****** End CMT Change *******/
- }
- /****** Begin CMT Change *******/
- eMarkedChunksPending = AnyMarkedChunks();
- /****** End CMT Change *******/
- /* possible that no chunks are pending retransmission since they could be
- * advanced ack'd
- */
- if(eMarkedChunksPending == TRUE)
- RtxMarkedChunks(RTX_LIMIT_ONE_PACKET);
- DBG_X(FastRtx);
- }
- void SctpCMTAgent::MarkChunkForRtx(SctpSendBufferNode_S *spNodeData,
- MarkedForRtx_E eMarkedForRtx)
- {
- DBG_I(MarkChunkForRtx);
- SctpDataChunkHdr_S *spChunk = spNodeData->spChunk;
- SctpOutStream_S *spStream = &(spOutStreams[spChunk->usStreamId]);
- DBG_PL(MarkChunkForRtx, "tsn=%lu eMarkedForRtx=%s"),
- spChunk->uiTsn,
- !eMarkedForRtx ? "NO_RTX"
- : (eMarkedForRtx==FAST_RTX ? "FAST_RTX": "TIMEOUT_RTX") DBG_PR;
- spNodeData->eMarkedForRtx = eMarkedForRtx;
- uiPeerRwnd += spChunk->sHdr.usLength; // 6.2.1.C1
- /* let's see if this chunk is on an unreliable stream.if so and the chunk has
- * run out of retransmissions,mark it as advanced acked and unmark it for rtx
- */
- if(spStream->eMode == SCTP_STREAM_UNRELIABLE)
- {
- /* have we run out of retransmissions??
- */
- if(spNodeData->iNumTxs > spNodeData->iUnrelRtxLimit)
- {
- DBG_PL(MarkChunkForRtx, "giving up on tsn %lu..."),
- spChunk->uiTsn DBG_PR;
- spNodeData->eAdvancedAcked = TRUE;
- spNodeData->eMarkedForRtx = NO_RTX;
- spNodeData->spDest->iOutstandingBytes -= spChunk->sHdr.usLength;
- }
- }
-
- if(spNodeData->eMarkedForRtx != NO_RTX)
- {
- eMarkedChunksPending = TRUE;
- /*** Begin CMT change ***/
- /* Also set eMarkedChunksPending for corresp. destination
- */
- spNodeData->spDest->eMarkedChunksPending = TRUE;
- /*** End of CMT change ***/
- }
- DBG_PL(MarkChunkForRtx, "uiPeerRwnd=%lu"), uiPeerRwnd DBG_PR;
- DBG_X(MarkChunkForRtx);
- }
- void SctpCMTAgent::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)
- }
- /***** Begin CMT Change *****/
- /* CMT change: We do not check to see if any rtxs are pending to be
- * sent. Since rtxs can be sent to a different dest than the original,
- * the ordering rule of SCTP that rtxs need to be sent before txs is
- * blatantly ignored by CMT :)
- */
- /* Note: We aren't bundling with what was sent above, but we could. Just
- * avoiding that for now... why? simplicity :-)
- */
- DBG_PL(recv, "eSendNewDataChunnks: %d"), eSendNewDataChunks DBG_PR;
- if(eSendNewDataChunks == TRUE)
- {
- SendMuch(); // Send new data till our cwnd is full!
- eSendNewDataChunks = FALSE; // reset AFTER sent (o/w breaks dependencies)
- }
- /***** End CMT Change *****/
- delete hdr_sctp::access(opInPkt)->SctpTrace();
- hdr_sctp::access(opInPkt)->SctpTrace() = NULL;
- Packet::free(opInPkt);
- opInPkt = NULL;
- DBG_X(recv);
- delete [] ucpOutData;
- }
- void SctpCMTAgent::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();
- /****** Begin CMT Change ******/
- Node_S *spCurrNode = NULL;
- static Node_S *spNodeLastSentTo = NULL;
- Node_S *spLoopStartNode = NULL;
- SctpDest_S *spSavedTxDest = NULL;
- SctpDest_S *spTempDest = NULL;
- /* spNewDest is a var used throughout the program to maintain vars for
- * dest to which new transmissions are sent, such as cwnd. Since we
- * choose which dest new data is sent to during this loop, we save the
- * previous value of this var, and then set it back at the end of this
- * function.
- */
- spSavedTxDest = spNewTxDest;
- /* CMT-PF: If all destinations are marked PF, select one from them for
- * data transmission
- */
- if (eUseCmtPF == TRUE)
- {
- spTempDest = SelectFromPFDests();
- if (spTempDest != NULL)
- {
- /* spTempDest was PF and changed to Active by SelectFromPFDests()
- */
- tiCountPFToActiveNewData++; // trace will pick up
- }
- }
-
- /* RR Scheduling: Start sending to the dest next to the one last sent to.
- */
- if (spNodeLastSentTo != NULL)
- {
- spCurrNode = spNodeLastSentTo->spNext;
- if (spCurrNode == NULL) {
- spCurrNode = sDestList.spHead;
- }
- }
- else
- spCurrNode = sDestList.spHead;
-
- spLoopStartNode = spCurrNode;
- DBG_PL(SendMuch,"RR debug: Entering loop with dest=%p"),
- (SctpDest_S *) spLoopStartNode->vpData DBG_PR;
- /* Sending to destinations as cwnd permits. Check to see if we can send
- * on any destination address. If there is space available in any cwnd,
- * we send. Hence this bigger loop.
- */
- do {
- iOutDataSize = 0;
- spNewTxDest = (SctpDest_S *) spCurrNode->vpData;
- DBG_PL(SendMuch,"RR debug: In send loop, trying for dest=%p"),
- spNewTxDest DBG_PR;
- /* CMT-PF: Do not send to failed or PF destinations!
- */
- if (spNewTxDest->eStatus != SCTP_DEST_STATUS_ACTIVE)
- {
- /* RR Scheduling: get next dest
- */
- spCurrNode = spCurrNode->spNext;
- if (spCurrNode == NULL)
- spCurrNode = sDestList.spHead;
- continue;
- }
-
- /* At this point, if there are chunks marked for rtx, no new data
- * will be transmitted anyways to this dest, since there will be no
- * cwnd space left. Note that sendmuch() is called after
- * rtxmarkedchunks() is called, therefore if an rtx could be sent to
- * this dest, it has been sent already. There is no point in
- * checking whether any rtxs are pending on this dest.
- * (The reason this check was done at this point in SCTP was because
- * SCTP did not send any new data on any path when rtxs had to be
- * sent. Therefore, even if rtxs were outstanding on the alternate,
- * no new data could be sent to the primary. In CMT, we ignore this
- * policy.)
- */
- /****** End CMT Change ******/
-
- /* 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, "Dest=%p, status=%s"), spNewTxDest,
- (spNewTxDest->eStatus == SCTP_DEST_STATUS_ACTIVE) ? "ACTIVE":"PF"
- DBG_PR;
- 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.
- */
- /****** Begin CMT Change ******/
- /* CMT change: per destination max.burst
- */
- if(eUseMaxBurst == MAX_BURST_USAGE_ON)
- if((eApplyMaxBurst == TRUE) &&
- ((spNewTxDest->uiBurstLength)++ >= MAX_BURST))
- {
- /* we've reached Max.Burst limit, so jump out of loop
- */
- DBG_PL(SendMuch,"Burstlength=%d - max.burst reached"),
- spNewTxDest->uiBurstLength DBG_PR;
- break;
- }
- DBG_PL(SendMuch,"Burstlength=%d"),
- spNewTxDest->uiBurstLength DBG_PR;
- /****** End CMT Change ******/
-
- 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;
- }
- /****** Begin CMT Change ******/
- /* RR Scheduling: Maintain last dest data was sent to
- */
- spNodeLastSentTo = spCurrNode;
- DBG_PL(SendMuch,"RR debug: Sent to dest=%p, %d"),
- spNewTxDest, iOutDataSize DBG_PR;
- }
- /* RR Scheduling: get next dest
- */
- spCurrNode = spCurrNode->spNext;
- if (spCurrNode == NULL)
- spCurrNode = sDestList.spHead;
- } while (spCurrNode != spLoopStartNode); // End of CMT destination loop
- /* Restore spNewTxDest */
- /* JRI-TODO: Check why spNewTxDest needs to be saved and restored.
- * This code can be cleaner if the value did not have to be maintained.
- */
- spNewTxDest = spSavedTxDest;
- /* this reset MUST happen at the end of the function, because the burst
- * measurement is carried over from RtxMarkedChunks() if it was called.
- */
- /* CMT change: per destination burstlength
- */
- for(spCurrNode = sDestList.spHead;
- spCurrNode != NULL;
- spCurrNode = spCurrNode->spNext)
- {
- ((SctpDest_S*)spCurrNode->vpData)->uiBurstLength = 0; // reset
- }
- /****** End CMT Change ******/
-
- DBG_X(SendMuch);
- delete [] ucpOutData;
- }
- /* 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 SctpCMTAgent::Timeout(SctpChunkType_E eChunkType, SctpDest_S *spDest)
- {
- /*** Begin CMT change ***/
- SctpDest_S *spTraceDest;
- char cpOutString[500];
- /*** End CMT change ***/
- 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 v.02)
- */
-
- /* Why is there a conditional change to ssthresh below??
- * No information reg. the same in either RFC or implementors guide.
- * During failure or high loss rates that result in back-to-back
- * timeouts, ssthresh is not reduced after the first
- * timeout. Hence, CMT's RTX_SSTHRESH is at a disadvantage.
- * NASIF FIX THIS: Confirm if this should be here. if not, remove
- * comment below. This might have to be fixed in sctp.cc's Timeout() as
- * well.
- */
- /* Begin Change:
- *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
-
- /* } End Change */
- 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
- /*** Begin CMT change ***/
- /* track data timeouts
- */
- spTraceDest = spDest;
- SetSource(spTraceDest); // gives us the correct source addr & port
- sprintf(cpOutString,
- "time: %-8.5f "
- "saddr: %-2d sport: %-2d daddr: %-2d dport: %-2d "
- "DataTimeout, peerRwnd: %d rto: %-6.3f errCnt: %d n",
- dCurrTime,
- addr(), port(), spTraceDest->iNsAddr, spTraceDest->iNsPort,
- uiPeerRwnd, spTraceDest->dRto, spTraceDest->iErrorCount);
- if(channel_)
- (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
- sprintf(cpOutString, "n");
- if(channel_)
- (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
- /*** End CMT change ***/
- }
- 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 sould not be counted
- DBG_PL(Timeout, "now spDest->iErrorCount=%d"), spDest->iErrorCount DBG_PR;
- /*** Begin CMT change ***/
- if((spDest->eStatus == SCTP_DEST_STATUS_ACTIVE) ||
- (spDest->eStatus == SCTP_DEST_STATUS_POSSIBLY_FAILED))
- {
- iAssocErrorCount++;
- DBG_PL(Timeout, "now iAssocErrorCount=%d"), iAssocErrorCount DBG_PR;
- if ((spDest->iErrorCount == 1) && (eUseCmtPF == TRUE))
- {
- spDest->eStatus = SCTP_DEST_STATUS_POSSIBLY_FAILED;
- spDest->dPFSince = dCurrTime;
- DBG_PL(Timeout, "Dest=%p Marked PF at time: %-8.5f"), spDest,
- spDest->dPFSince DBG_PR;
-
- if(uiHeartbeatInterval != 0)
- {
- spDest->opHeartbeatTimeoutTimer->force_cancel();
- DBG_PL(Timeout, "Dest=%p Cancelled Heartbeat Timer"),
- spDest DBG_PR;
- }
- }
- /*** End CMT change ***/
-
- // Path.Max.Retrans exceeded?
- if(spDest->iErrorCount > (int) uiPathMaxRetrans)
- {
- spDest->eStatus = SCTP_DEST_STATUS_INACTIVE;
- if(spDest == spNewTxDest)
- {
- spNewTxDest = GetNextDest(spNewTxDest);
- 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;
- }
- /*** Begin CMT change ***/
- if(eChunkType == SCTP_CHUNK_DATA)
- {
- TimeoutRtx(spDest);
- DBG_PL(Timeout, "Dest: %p, out: %d"), spDest, spDest->iOutstandingBytes
- DBG_PR;
- if(spDest->eStatus == SCTP_DEST_STATUS_POSSIBLY_FAILED)
- {
- SendHeartbeat(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_POSSIBLY_FAILED) ||
- (spDest->eStatus == SCTP_DEST_STATUS_UNCONFIRMED))
- SendHeartbeat(spDest);
- }
- /*** End CMT change ***/
- DBG_X(Timeout);
- }
- /* CMT change: changed function to get highest outstanding TSN per dest
- */
- u_int SctpCMTAgent::GetHighestOutstandingTsn(SctpDest_S *spOutstandingOnDest)
- {
- DBG_I(GetHighestOutstandingTsn);
- u_int uiHighestOutstandingTsn = 0;
- Node_S *spCurrBuffNode = NULL;
- SctpSendBufferNode_S *spCurrBuffData = NULL;
- /* start from the tailof the send buffer and search towards the head for the
- * first tsn oustanding... that's the highest outstanding tsn.
- */
- for(spCurrBuffNode = sSendBuffer.spTail;
- spCurrBuffNode != NULL;
- spCurrBuffNode = spCurrBuffNode->spPrev)
- {
- spCurrBuffData = (SctpSendBufferNode_S *) spCurrBuffNode->vpData;
- /****** Begin CMT Change ******/
- /* CMT change: changed function to get highest outstanding TSN per dest
- */
- if( (spCurrBuffData->spDest == spOutstandingOnDest)&&
- (spCurrBuffData->eMarkedForRtx == NO_RTX)) // is it oustanding?
- {
- uiHighestOutstandingTsn = spCurrBuffData->spChunk->uiTsn; //found it!
- break;
- }
- /****** End CMT Change ******/
- }
- DBG_PL(GetHighestOutstandingTsn, "uiHighestOutstandingTsn=%d"),
- uiHighestOutstandingTsn DBG_PR;
- DBG_X(GetHighestOutstandingTsn);
-
- return uiHighestOutstandingTsn;
- }
- void SctpCMTAgent::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, "Time: %-8.5f"), dCurrTime DBG_PR;
-
- 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;
-
- /****** Begin CMT Change ******/
- /* For PF destinations, Timeout() (T3 timer expiration) function
- * sends out HBs. Snd HB here only if destination is not PF.
- */
- if(spLongestIdleNodeData->dIdleSince <= dTimerStartTime &&
- spLongestIdleNodeData->eStatus != SCTP_DEST_STATUS_POSSIBLY_FAILED )
- SendHeartbeat(spLongestIdleNodeData);
- else
- DBG_PL(HeartbeatGenTimerExpiration,
- "longest idle dest not idle long enough!") DBG_PR;
- /****** End CMT Change ******/
-
- /* start the timer again...
- */
- dTime = CalcHeartbeatTime(spLongestIdleNodeData->dRto);
- opHeartbeatGenTimer->resched(dTime);
- opHeartbeatGenTimer->dStartTime = dCurrTime;
- DBG_X(HeartbeatGenTimerExpiration);
- }
- void SctpCMTAgent::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;
- /**** Begin CMT change ****/
- /* this variable should be reset only after GenChunk() is called.
- * GenChunk puts in the number of pkts into the SACK flags, which is
- * used by the sender to increment the missing report count (CMT DAC
- * algo). If this var is reset here, when a SACK is sent after SACK
- * timer expires, missing report count is incremented by 0 ! Moving line
- * to after SendPacket().
- */
- iDataPktCountSinceLastSack = 0; // reset
- /**** End of CMT change ****/
- DBG_X(SackGenTimerExpiration);
- delete [] ucpOutData;
- }
- /****** Begin CMT Change ******/
- /* New CMT function:
- * Function returns dest status as string for printing
- */
- char* SctpCMTAgent::PrintDestStatus(SctpDest_S* spDest)
- {
- switch(spDest->eStatus)
- {
- case SCTP_DEST_STATUS_ACTIVE:
- return "ACTIVE";
- case SCTP_DEST_STATUS_INACTIVE:
- return "INACTIVE";
- case SCTP_DEST_STATUS_POSSIBLY_FAILED:
- return "PF";
- case SCTP_DEST_STATUS_UNCONFIRMED:
- return "UNCONFIRMED";
- default:
- return "UNKNOWN";
- }
- }
- /* New CMT-PF method:
- *
- * If all destinations are marked PF, send data to one of
- * the destinations so that CMT-PF does not perform worse than
- * CMT.
- *
- * Algorithm to select the PF destination:
- * Select the dest with the least error count.
- * If tie in errorcount values, then all destinations
- * have "Possibly failed" to the same degree. So, select dest
- * that was most recently active (marked PF most recently).
- *
- * Implementation:
- * If atleast 1 non-PF dest exists, return NULL
- * else,
- * - select a dest based on the above algo.
- * - set cwnd on the selected PF dest to 1 MTU
- * - stop the HB timer if it is running. - hb goes to blackhole !
- * hb loss cannot be detected. But, if hb-ack comes back,
- * errorcount for the destination is cleared. Situation is
- * similar to an ACTIVE dest recving hb.
- * - set eRtxTimerIsRunning to false -- when data is sent,
- * T3RTX timer be started by RtxMarkedChunks or GenOneDataChunk.
- * - change dest status to ACTIVE. This dest status is just for
- * easier implementation and does not change anything in the
- * CMT-PF details. This implementation uses the following rule:
- * If a dest is PF, only HBs are sent
- * If a dest is ACTIVE, data is sent
- * - Schedule HeartbeatGenTimer
- *
- * Note: This implementation can be considered _aggressive_,
- * since, on the selected PF dest, a HB could've been sent and
- * before HB-ACK arrives, 1MTU of data will be sent.
- * MAYBE, this aggressiveness can be
- * avoided with a better implementation - do not send HB on
- * a destination with the least error count?
- */
- SctpDest_S* SctpCMTAgent::SelectFromPFDests()
- {
-
- int iLeastErrorCount = (int) uiPathMaxRetrans;
- double dMostRecentPFSince = 0;
- Node_S *spCurrDestNode = NULL;
- SctpDest_S* spCurrDestNodeData = NULL;
- SctpDest_S* spSelectedDest = NULL;
-
- for(spCurrDestNode = sDestList.spHead;
- spCurrDestNode != NULL;
- spCurrDestNode = spCurrDestNode->spNext)
- {
- spCurrDestNodeData = (SctpDest_S *) spCurrDestNode->vpData;
-
- if (spCurrDestNodeData->eStatus == SCTP_DEST_STATUS_POSSIBLY_FAILED)
- {
- if (spCurrDestNodeData->iErrorCount < iLeastErrorCount)
- {
- spSelectedDest = spCurrDestNodeData;
- iLeastErrorCount = spCurrDestNodeData->iErrorCount;
- dMostRecentPFSince = spCurrDestNodeData->dPFSince;
- }
- else if (spCurrDestNodeData->iErrorCount == iLeastErrorCount)
- {
- if (spCurrDestNodeData->dPFSince > dMostRecentPFSince)
- {
- spSelectedDest = spCurrDestNodeData;
- iLeastErrorCount = spCurrDestNodeData->iErrorCount;
- dMostRecentPFSince = spCurrDestNodeData->dPFSince;
- }
- else if (spCurrDestNodeData->dPFSince == dMostRecentPFSince)
- {
- if (Random::random()&01)
- spSelectedDest = spCurrDestNodeData;
- }
- }
-
- }
- else
- {
- /* Found a non-PF dest, return NULL
- */
- return NULL;
-
- }
-
- }
- /* A PF destination was selected to be marked Active
- */
- if (spSelectedDest->eRtxTimerIsRunning == TRUE)
- StopT3RtxTimer(spSelectedDest);
-
- spSelectedDest->opHeartbeatTimeoutTimer->force_cancel();
- spSelectedDest->eHBTimerIsRunning = FALSE;
-
- spSelectedDest->iCwnd = 1*uiMaxDataSize;
-
- spSelectedDest->eStatus = SCTP_DEST_STATUS_ACTIVE;
- spSelectedDest->dPFSince = 0;
-
- // DBG_PL(SelectFromPFDests, "Dest: %p changed from PF to Active"),
- // spSelectedDest DBG_PR;
-
- return spSelectedDest;
- }
- /* New CMT function. Currently not used. */
- void SctpCMTAgent::SetSharedCCParams(SctpDest_S *spCurrInfo)
- {
- // {
- // Node_S *spCurrNode = NULL;
- // SctpDest_S *spCurrDest = NULL;
- // for(spCurrNode = sDestList.spHead;
- // spCurrNode != NULL;
- // spCurrNode = spCurrNode->spNext)
- // {
- // spCurrDest = (SctpDest_S *) spCurrNode->vpData;
- // spCurrDest->iCwnd = spCurrInfo->iSharedCwnd;
- // spCurrDest->iSsthresh = spCurrInfo->iSharedSsthresh;
- // }
- // }
- }
-
- /****** End CMT Change ******/