sctp-cmt.cc
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:140k
- /*
- * Copyright (c) 2006-2007 by the Protocol Engineering Lab, U of Delaware
- * All rights reserved.
- *
- * Protocol Engineering Lab web page : http://pel.cis.udel.edu/
- *
- * Paul D. Amer <amer@@cis,udel,edu>
- * Armando L. Caro Jr. <acaro@@cis,udel,edu>
- * Janardhan Iyengar <iyengar@@cis,udel,edu>
- * Preethi Natarajan <nataraja@@cis,udel,edu>
- * Nasif Ekiz <nekiz@@cis,udel,edu>
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * 3. Neither the name of the University nor of the Laboratory may be used
- * to endorse or promote products derived from this software without
- * specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
- /* Concurrent Multipath Transfer extension: To allow SCTP to correctly use
- * the multiple paths available at the transport.
- */
- #include "ip.h"
- #include "sctp-cmt.h"
- #include "sctp.h"
- #include "flags.h"
- #include "random.h"
- #include "template.h"
- #include "sctpDebug.h"
- #ifdef DMALLOC
- #include "dmalloc.h"
- #endif
- #define MIN(x,y) (((x)<(y))?(x):(y))
- #define MAX(x,y) (((x)>(y))?(x):(y))
- static class SctpCMTClass : public TclClass
- {
- public:
- SctpCMTClass() : TclClass("Agent/SCTP/CMT") {}
- TclObject* create(int, const char*const*)
- {
- return (new SctpCMTAgent());
- }
- } classSctpCMT;
- SctpCMTAgent::SctpCMTAgent() : SctpAgent() {}
- void SctpCMTAgent::delay_bind_init_all()
- {
- delay_bind_init_one("useCmtReordering_");
- delay_bind_init_one("useCmtCwnd_");
- delay_bind_init_one("useCmtDelAck_");
- delay_bind_init_one("eCmtRtxPolicy_");
- delay_bind_init_one("useCmtPF_");
- delay_bind_init_one("cmtPFCwnd_");
- delay_bind_init_one("countPFToActiveNewData_");
- delay_bind_init_one("countPFToActiveRtxms_");
- // delay_bind_init_one("useSharedCC_");
- SctpAgent::delay_bind_init_all();
- }
- int SctpCMTAgent::delay_bind_dispatch(const char *cpVarName,
- const char *cpLocalName,
- TclObject *opTracer)
- {
- if(delay_bind(cpVarName, cpLocalName, "useCmtReordering_",
- (int*)&eUseCmtReordering, opTracer))
- return TCL_OK;
- if(delay_bind(cpVarName, cpLocalName, "useCmtCwnd_",
- (int*)&eUseCmtCwnd, opTracer))
- return TCL_OK;
- if(delay_bind(cpVarName, cpLocalName, "useCmtDelAck_",
- (int*)&eUseCmtDelAck, opTracer))
- return TCL_OK;
- if(delay_bind(cpVarName, cpLocalName, "eCmtRtxPolicy_",
- (int*)&eCmtRtxPolicy, opTracer))
- return TCL_OK;
- if(delay_bind(cpVarName, cpLocalName, "useCmtPF_",
- (int*)&eUseCmtPF, opTracer))
- return TCL_OK;
- if(delay_bind(cpVarName, cpLocalName, "cmtPFCwnd_",
- (u_int*)&uiCmtPFCwnd, opTracer))
- return TCL_OK;
- if(delay_bind(cpVarName, cpLocalName, "countPFToActiveNewData_",
- &tiCountPFToActiveNewData, opTracer))
- return TCL_OK;
- if(delay_bind(cpVarName, cpLocalName, "countPFToActiveRtxms_",
- &tiCountPFToActiveRtxms, opTracer))
- return TCL_OK;
- // if(delay_bind(cpVarName, cpLocalName, "useSharedCC_",
- // (int*)&eUseSharedCC, opTracer))
- // return TCL_OK;
- else
- return SctpAgent::delay_bind_dispatch(cpVarName,
- cpLocalName, opTracer);
- }
- void SctpCMTAgent::TraceAll()
- {
- char cpOutString[500];
- Node_S *spCurrNode = NULL;
- SctpDest_S *spCurrDest = NULL;
- double dCurrTime = Scheduler::instance().clock();
- for(spCurrNode = sDestList.spHead;
- spCurrNode != NULL;
- spCurrNode = spCurrNode->spNext)
- {
- spCurrDest = (SctpDest_S *) spCurrNode->vpData;
- SetSource(spCurrDest); // gives us the correct source addr & port
- sprintf(cpOutString,
- "time: %-8.5f "
- "saddr: %-2d sport: %-2d daddr: %-2d dport: %-2d "
- "cwnd: %d pba: %d out: %d ssthresh: %d peerRwnd: %d "
- "rto: %-6.3f srtt: %-6.3f rttvar: %-6.3f "
- "assocErrors: %d pathErrors: %d dstatus: %s "
- "frCount: %d timeoutCount: %d rcdCount: %d "
- "countPFToActiveNewData: %d countPFToActiveRtxms: %dn",
- dCurrTime,
- addr(), port(), spCurrDest->iNsAddr, spCurrDest->iNsPort,
- spCurrDest->iCwnd, spCurrDest->iPartialBytesAcked,
- spCurrDest->iOutstandingBytes, spCurrDest->iSsthresh,
- uiPeerRwnd,
- spCurrDest->dRto, spCurrDest->dSrtt,
- spCurrDest->dRttVar,
- iAssocErrorCount,
- spCurrDest->iErrorCount,
- PrintDestStatus(spCurrDest),
- int(tiFrCount),
- spCurrDest->iTimeoutCount,
- spCurrDest->iRcdCount,
- int(tiCountPFToActiveNewData),
- int(tiCountPFToActiveRtxms));
- if(channel_)
- (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
- }
- sprintf(cpOutString, "n");
- if(channel_)
- (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
- }
- void SctpCMTAgent::TraceVar(const char* cpVar)
- {
- char cpOutString[500];
- Node_S *spCurrNode = NULL;
- SctpDest_S *spCurrDest = NULL;
- double dCurrTime = Scheduler::instance().clock();
- if(!strcmp(cpVar, "cwnd_"))
- for(spCurrNode = sDestList.spHead;
- spCurrNode != NULL;
- spCurrNode = spCurrNode->spNext)
- {
- spCurrDest = (SctpDest_S *) spCurrNode->vpData;
- SetSource(spCurrDest); // gives us the correct source addr & port
- sprintf(cpOutString,
- "time: %-8.5f "
- "saddr: %-2d sport: %-2d daddr: %-2d dport: %-2d "
- "cwnd: %d pba: %d out: %d ssthresh: %d peerRwnd: %dn",
- dCurrTime,
- addr(), port(),
- spCurrDest->iNsAddr, spCurrDest->iNsPort,
- spCurrDest->iCwnd, spCurrDest->iPartialBytesAcked,
- spCurrDest->iOutstandingBytes, spCurrDest->iSsthresh,
- uiPeerRwnd);
- if(channel_)
- (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
- }
- // else if(!strcmp(cpVar, "rwnd_"))
- // {
- // sprintf(cpOutString, "time: %-8.5f rwnd: %d peerRwnd: %dn",
- // dCurrTime, uiMyRwnd, uiPeerRwnd);
- // if(channel_)
- // (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
- // }
-
- else if(!strcmp(cpVar, "rto_"))
- for(spCurrNode = sDestList.spHead;
- spCurrNode != NULL;
- spCurrNode = spCurrNode->spNext)
- {
- spCurrDest = (SctpDest_S *) spCurrNode->vpData;
- SetSource(spCurrDest); // gives us the correct source addr & port
- sprintf(cpOutString,
- "time: %-8.5f "
- "saddr: %-2d sport: %-2d daddr: %-2d dport: %-2d "
- "rto: %-6.3f srtt: %-6.3f rttvar: %-6.3f timeoutCount: %dn",
- dCurrTime,
- addr(), port(),
- spCurrDest->iNsAddr, spCurrDest->iNsPort,
- spCurrDest->dRto, spCurrDest->dSrtt,
- spCurrDest->dRttVar, spCurrDest->iTimeoutCount);
- if(channel_)
- (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
- }
- else if(!strcmp(cpVar, "errorCount_"))
- for(spCurrNode = sDestList.spHead;
- spCurrNode != NULL;
- spCurrNode = spCurrNode->spNext)
- {
- spCurrDest = (SctpDest_S *) spCurrNode->vpData;
- SetSource(spCurrDest); // gives us the correct source addr & port
- sprintf(cpOutString,
- "time: %-8.5f "
- "saddr: %-2d sport: %-2d daddr: %-2d dport: %-2d "
- "assocErrors: %d pathErrors: %d dstatus: %dn",
- dCurrTime,
- addr(), port(),
- spCurrDest->iNsAddr, spCurrDest->iNsPort,
- iAssocErrorCount,
- spCurrDest->iErrorCount,
- spCurrDest->eStatus);
- if(channel_)
- (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
- }
- else if(!strcmp(cpVar, "frCount_"))
- {
- /* JRI-TODO: Check code in SCTP for frCount
- */
- sprintf(cpOutString,
- "time: %-8.5f "
- "frCount: %dn",
- dCurrTime,
- int(tiFrCount));
- if(channel_)
- (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
- }
- else if(!strcmp(cpVar, "timeoutCount_"))
- {
- for(spCurrNode = sDestList.spHead;
- spCurrNode != NULL;
- spCurrNode = spCurrNode->spNext)
- {
- spCurrDest = (SctpDest_S *) spCurrNode->vpData;
- SetSource(spCurrDest); // gives us the correct source addr & port
- sprintf(cpOutString,
- "time: %-8.5f "
- "saddr: %-2d sport: %-2d daddr: %-2d dport: %-2d "
- "timeoutCount: %dn",
- dCurrTime,
- addr(), port(),
- spCurrDest->iNsAddr, spCurrDest->iNsPort,
- spCurrDest->iTimeoutCount);
- if(channel_)
- (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
- }
- }
- else if(!strcmp(cpVar, "countPFToActiveNewData_"))
- {
- sprintf(cpOutString,
- "time: %-8.5f "
- "countPFToActiveNewData: %dn",
- dCurrTime,
- int(tiCountPFToActiveNewData));
- if(channel_)
- (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
- }
- else if(!strcmp(cpVar, "countPFToActiveRtxms_"))
- {
- sprintf(cpOutString,
- "time: %-8.5f "
- "countPFToActiveRtxms: %dn",
- dCurrTime,
- int(tiCountPFToActiveRtxms));
- if(channel_)
- (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
- }
- else
- {
- sprintf(cpOutString,
- "time: %-8.5f "
- "saddr: %-2d sport: %-2d daddr: %-2d dport: %-2d %s: %sn",
- dCurrTime, addr(), port(), daddr(), dport(),
- cpVar, "ERROR (unepected trace variable)");
- if(channel_)
- (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
- }
- sprintf(cpOutString, "n");
- if(channel_)
- (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
- }
- void SctpCMTAgent::trace(TracedVar *v)
- {
- if(eTraceAll == TRUE)
- TraceAll();
- else
- TraceVar(v->name());
- }
- void SctpCMTAgent::OptionReset()
- {
- DBG_I(OptionReset);
- SctpAgent::OptionReset();
- DBG_X(OptionReset);
- }
- void SctpCMTAgent::Reset()
- {
- Node_S *spCurrNode = NULL;
- SctpDest_S *spCurrDest = NULL;
- /* Call parent's reset() first
- */
- SctpAgent::Reset();
- for(spCurrNode = sDestList.spHead;
- spCurrNode != NULL;
- spCurrNode = spCurrNode->spNext)
- {
- spCurrDest = (SctpDest_S *) spCurrNode->vpData;
- spCurrDest->uiHighestTsnInSackForDest = 0;
- spCurrDest->uiLowestTsnInSackForDest = 0;
- spCurrDest->eSawNewAck = FALSE;
- spCurrDest->uiRecover = 0;
- spCurrDest->eFindExpectedPseudoCum = TRUE;
- spCurrDest->eFindExpectedRtxPseudoCum = TRUE;
- spCurrDest->eRtxLimit = RTX_LIMIT_ZERO;
- }
- tiCountPFToActiveNewData = 0;
- tiCountPFToActiveRtxms = 0;
- }
- /* returns the size of the chunk
- */
- int SctpCMTAgent::GenChunk(SctpChunkType_E eType, u_char *ucpChunk)
- {
- DBG_I(GenChunk);
- double dCurrTime = Scheduler::instance().clock();
- AppData_S *spAppMessage = NULL;
- int iSize = 0;
- DBG_PL(GenChunk, "spSctpTrace=%p"), spSctpTrace DBG_PR;
- switch(eType)
- {
- case SCTP_CHUNK_INIT:
- iSize = sizeof(SctpInitChunk_S);
- ((SctpInitChunk_S *) ucpChunk)->sHdr.ucType = eType;
- ((SctpInitChunk_S *) ucpChunk)->uiArwnd = uiInitialRwnd;
- ((SctpInitChunk_S *) ucpChunk)->usNumOutboundStreams = uiNumOutStreams;
- ((SctpInitChunk_S *) ucpChunk)->uiInitialTsn = 0;
- ((SctpInitChunk_S *) ucpChunk)->sUnrelStream.usType
- = SCTP_INIT_PARAM_UNREL;
- ((SctpInitChunk_S *) ucpChunk)->sUnrelStream.usLength
- = sizeof(SctpUnrelStreamsParam_S);
-
- if(uiNumUnrelStreams > 0)
- {
- ((SctpInitChunk_S *) ucpChunk)->sUnrelStream.usLength
- += sizeof(SctpUnrelStreamPair_S);
- SctpUnrelStreamPair_S *spUnrelStreamPair
- = (SctpUnrelStreamPair_S *) (ucpChunk+iSize);
- spUnrelStreamPair->usStart = 0;
- spUnrelStreamPair->usEnd = uiNumUnrelStreams - 1;
- iSize += sizeof(SctpUnrelStreamPair_S);
- }
- ((SctpInitChunk_S *) ucpChunk)->sHdr.usLength = iSize;
- /* fill in tracing fields too
- */
- spSctpTrace[uiNumChunks].eType = eType;
- spSctpTrace[uiNumChunks].uiTsn = (u_int) -1;
- spSctpTrace[uiNumChunks].usStreamId = (u_short) -1;
- spSctpTrace[uiNumChunks].usStreamSeqNum = (u_short) -1;
- break;
- case SCTP_CHUNK_INIT_ACK:
- iSize = sizeof(SctpInitAckChunk_S);
- ((SctpInitAckChunk_S *) ucpChunk)->sHdr.ucType = eType;
- ((SctpInitAckChunk_S *) ucpChunk)->uiArwnd = uiInitialRwnd;
- ((SctpInitChunk_S *) ucpChunk)->usNumOutboundStreams = uiNumOutStreams;
- ((SctpInitAckChunk_S *) ucpChunk)->uiInitialTsn = 0;
- ((SctpInitAckChunk_S *) ucpChunk)->sUnrelStream.usType
- = SCTP_INIT_PARAM_UNREL;
- ((SctpInitAckChunk_S *) ucpChunk)->sUnrelStream.usLength
- = sizeof(SctpUnrelStreamsParam_S);
- if(uiNumUnrelStreams > 0)
- {
- ((SctpInitAckChunk_S *) ucpChunk)->sUnrelStream.usLength
- += sizeof(SctpUnrelStreamPair_S);
- SctpUnrelStreamPair_S *spUnrelStreamPair
- = (SctpUnrelStreamPair_S *) (ucpChunk+iSize);
- spUnrelStreamPair->usStart = 0;
- spUnrelStreamPair->usEnd = uiNumUnrelStreams - 1;
- iSize += sizeof(SctpUnrelStreamPair_S);
- }
- ((SctpInitAckChunk_S *) ucpChunk)->sHdr.usLength = iSize;
- /* fill in tracing fields too
- */
- spSctpTrace[uiNumChunks].eType = eType;
- spSctpTrace[uiNumChunks].uiTsn = (u_int) -1;
- spSctpTrace[uiNumChunks].usStreamId = (u_short) -1;
- spSctpTrace[uiNumChunks].usStreamSeqNum = (u_short) -1;
- break;
- case SCTP_CHUNK_COOKIE_ECHO:
- iSize = sizeof(SctpCookieEchoChunk_S);
- ((SctpCookieEchoChunk_S *) ucpChunk)->sHdr.ucType = eType;
- ((SctpCookieEchoChunk_S *) ucpChunk)->sHdr.usLength = iSize;
- /* fill in tracing fields too
- */
- spSctpTrace[uiNumChunks].eType = eType;
- spSctpTrace[uiNumChunks].uiTsn = (u_int) -1;
- spSctpTrace[uiNumChunks].usStreamId = (u_short) -1;
- spSctpTrace[uiNumChunks].usStreamSeqNum = (u_short) -1;
- break;
-
- case SCTP_CHUNK_COOKIE_ACK:
- iSize = sizeof(SctpCookieAckChunk_S);
- ((SctpCookieAckChunk_S *) ucpChunk)->sHdr.ucType = eType;
- ((SctpCookieAckChunk_S *) ucpChunk)->sHdr.usLength = iSize;
- /* fill in tracing fields too
- */
- spSctpTrace[uiNumChunks].eType = eType;
- spSctpTrace[uiNumChunks].uiTsn = (u_int) -1;
- spSctpTrace[uiNumChunks].usStreamId = (u_short) -1;
- spSctpTrace[uiNumChunks].usStreamSeqNum = (u_short) -1;
- break;
- case SCTP_CHUNK_DATA:
- /* Depending on eDataSource, we either use chunk parameters
- * from the app layer buffer, or use the defaults for infinite
- * data generation.
- */
- if(eDataSource == DATA_SOURCE_APPLICATION)
- {
- /* If DATA_SOURCE_APPLICATION, we have to get chunk parameters
- * from the app layer buffer
- */
- DBG_PL (GenChunk, "eDataSource=DATA_SOURCE_APPLICATION") DBG_PR;
- spAppMessage = (AppData_S *) (sAppLayerBuffer.spHead->vpData);
- DBG_PL (GenChunk, "App Message ---------------->") DBG_PR;
- DBG_PL (GenChunk, "usNumStreams: %d"),
- spAppMessage->usNumStreams DBG_PR;
- DBG_PL (GenChunk, "usNumUnreliable: %d"),
- spAppMessage->usNumUnreliable DBG_PR;
- DBG_PL (GenChunk, "uiNumBytes: %d"),
- spAppMessage->uiNumBytes DBG_PR;
- DBG_PL (GenChunk, "usStreamId: %d"),
- spAppMessage->usStreamId DBG_PR;
- DBG_PL (GenChunk, "eUnordered: %s"),
- spAppMessage->eUnordered ? "TRUE" : "FALSE" DBG_PR;
- DBG_PL (GenChunk, "usReliability: %d"),
- spAppMessage->usReliability DBG_PR;
- DBG_PL (GenChunk, "App Message <----------------") DBG_PR;
- iSize = spAppMessage->uiNumBytes + sizeof(SctpDataChunkHdr_S);
- ((SctpDataChunkHdr_S *) ucpChunk)->sHdr.ucType = SCTP_CHUNK_DATA;
- ((SctpDataChunkHdr_S *) ucpChunk)->sHdr.usLength = iSize;
- /* DATA chunks must be padded to a 4 byte boundary (section 3.3.1)
- * ...but the adjustment should happen after usLength field is set,
- * because the field should represent the size before padding.
- */
- if( (iSize % 4) != 0)
- iSize += 4 - (iSize % 4);
- ((SctpDataChunkHdr_S *) ucpChunk)->uiTsn = ++uiNextTsn;
-
- ((SctpDataChunkHdr_S *) ucpChunk)->usStreamId
- = spAppMessage->usStreamId;
- if(spAppMessage->eUnordered == TRUE)
- {
- ((SctpDataChunkHdr_S *) ucpChunk)->sHdr.ucFlags
- |= SCTP_DATA_FLAG_UNORDERED;
- /* no stream seq num on unordered chunks!
- */
- }
- else
- {
- ((SctpDataChunkHdr_S *) ucpChunk)->sHdr.ucFlags = 0;
- ((SctpDataChunkHdr_S *) ucpChunk)->usStreamSeqNum
- = spOutStreams[spAppMessage->usStreamId].usNextStreamSeqNum++;
- }
- }
- else
- {
- DBG_PL (GenChunk, "eDataSource=DATA_SOURCE_INFINITE") DBG_PR;
- iSize = uiDataChunkSize;
- ((SctpDataChunkHdr_S *) ucpChunk)->sHdr.ucType = SCTP_CHUNK_DATA;
- if(eUnordered == TRUE)
- {
- ((SctpDataChunkHdr_S *) ucpChunk)->sHdr.ucFlags
- |= SCTP_DATA_FLAG_UNORDERED;
- }
- else
- {
- ((SctpDataChunkHdr_S *) ucpChunk)->sHdr.ucFlags = 0;
- }
- ((SctpDataChunkHdr_S *) ucpChunk)->sHdr.usLength = iSize;
- /* DATA chunks must be padded to a 4 byte boundary (section 3.3.1)
- * ...but the adjustment should happen after usLength field is set,
- * because the field should represent the size before padding.
- */
- if( (uiDataChunkSize % 4) != 0)
- iSize += 4 - (uiDataChunkSize % 4);
- ((SctpDataChunkHdr_S *) ucpChunk)->uiTsn = ++uiNextTsn;
-
- ((SctpDataChunkHdr_S *) ucpChunk)->usStreamId
- = (usNextStreamId % uiNumOutStreams);
- if(eUnordered == TRUE)
- {
- ((SctpDataChunkHdr_S *) ucpChunk)->sHdr.ucFlags
- |= SCTP_DATA_FLAG_UNORDERED;
- /* no stream seq num on unordered chunks!
- */
- }
- else
- {
- ((SctpDataChunkHdr_S *) ucpChunk)->sHdr.ucFlags = 0;
- ((SctpDataChunkHdr_S *) ucpChunk)->usStreamSeqNum
- = spOutStreams[usNextStreamId%uiNumOutStreams].usNextStreamSeqNum++;
- }
-
- usNextStreamId++; // round-robin stream feeding
- }
- /* fill in tracing fields too
- */
- spSctpTrace[uiNumChunks].eType = eType;
- spSctpTrace[uiNumChunks].uiTsn
- = ((SctpDataChunkHdr_S *) ucpChunk)->uiTsn;
- spSctpTrace[uiNumChunks].usStreamId
- = ((SctpDataChunkHdr_S *) ucpChunk)->usStreamId;
- spSctpTrace[uiNumChunks].usStreamSeqNum
- = ((SctpDataChunkHdr_S *) ucpChunk)->usStreamSeqNum;
- DBG_PL(GenChunk, "DataTsn=%d at dCurrTime=%f"),
- uiNextTsn, dCurrTime DBG_PR;
- break;
- case SCTP_CHUNK_SACK:
- iSize = sizeof(SctpSackChunk_S);
- ((SctpSackChunk_S *) ucpChunk)->sHdr.ucType = eType;
- ((SctpSackChunk_S *) ucpChunk)->uiCumAck = uiCumAck;
- ((SctpSackChunk_S *) ucpChunk)->uiArwnd = uiMyRwnd;
- ((SctpSackChunk_S *) ucpChunk)->usNumGapAckBlocks
- = sRecvTsnBlockList.uiLength;
- ((SctpSackChunk_S *) ucpChunk)->usNumDupTsns = sDupTsnList.uiLength;
- /****** Begin CMT change ******/
- /* 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.
- * 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.
- */
- ((SctpSackChunk_S*)ucpChunk)->sHdr.ucFlags = iDataPktCountSinceLastSack;
- DBG_PL(GenChunk, "SACK CumAck=%d arwnd=%d flags=%d"),
- uiCumAck, uiMyRwnd, iDataPktCountSinceLastSack DBG_PR;
-
- /* Pkt count is put into SACK flags for CMT delayed ack algo.
- * Reset only after putting into SACK flags.
- */
- iDataPktCountSinceLastSack = 0; // reset
- /****** End CMT change ******/
- /* Append all the Gap Ack Blocks
- */
- for(Node_S *spCurrFrag = sRecvTsnBlockList.spHead;
- (spCurrFrag != NULL) &&
- (iSize + sizeof(SctpGapAckBlock_S) < uiMaxDataSize);
- spCurrFrag = spCurrFrag->spNext, iSize += sizeof(SctpGapAckBlock_S) )
- {
- SctpGapAckBlock_S *spGapAckBlock
- = (SctpGapAckBlock_S *) (ucpChunk+iSize);
- spGapAckBlock->usStartOffset
- = ((SctpRecvTsnBlock_S *)spCurrFrag->vpData)->uiStartTsn-uiCumAck;
- spGapAckBlock->usEndOffset
- = ((SctpRecvTsnBlock_S *)spCurrFrag->vpData)->uiEndTsn - uiCumAck;
- DBG_PL(GenChunk, "GapAckBlock StartOffset=%d EndOffset=%d"),
- spGapAckBlock->usStartOffset, spGapAckBlock->usEndOffset DBG_PR;
- }
- /* Append all the Duplicate TSNs
- */
- for(Node_S *spPrevDup = NULL, *spCurrDup = sDupTsnList.spHead;
- (spCurrDup != NULL) &&
- (iSize + sizeof(SctpDupTsn_S) < uiMaxDataSize);
- spPrevDup = spCurrDup, spCurrDup = spCurrDup->spNext,
- DeleteNode(&sDupTsnList, spPrevDup), iSize += sizeof(SctpDupTsn_S))
- {
- SctpDupTsn_S *spDupTsn = (SctpDupTsn_S *) (ucpChunk+iSize);
-
- spDupTsn->uiTsn = ((SctpDupTsn_S *) spCurrDup->vpData)->uiTsn;
- DBG_PL(GenChunk, "DupTsn=%d"), spDupTsn->uiTsn DBG_PR;
- }
- /* After all the dynamic appending, we can NOW fill in the chunk size!
- */
- ((SctpSackChunk_S *) ucpChunk)->sHdr.usLength = iSize;
- /* fill in tracing fields too
- */
- spSctpTrace[uiNumChunks].eType = eType;
- spSctpTrace[uiNumChunks].uiTsn = ((SctpSackChunk_S *)ucpChunk)->uiCumAck;
- spSctpTrace[uiNumChunks].usStreamId = (u_short) -1;
- spSctpTrace[uiNumChunks].usStreamSeqNum = (u_short) -1;
- break;
- case SCTP_CHUNK_FORWARD_TSN:
- iSize = SCTP_CHUNK_FORWARD_TSN_LENGTH;
- ((SctpForwardTsnChunk_S *) ucpChunk)->sHdr.ucType = eType;
- ((SctpForwardTsnChunk_S *) ucpChunk)->sHdr.ucFlags = 0; //flags not used?
- ((SctpForwardTsnChunk_S *) ucpChunk)->sHdr.usLength = iSize;
- ((SctpForwardTsnChunk_S *) ucpChunk)->uiNewCum = uiAdvancedPeerAckPoint;
- /* fill in tracing fields too
- */
- spSctpTrace[uiNumChunks].eType = eType;
- spSctpTrace[uiNumChunks].uiTsn = uiAdvancedPeerAckPoint;
- spSctpTrace[uiNumChunks].usStreamId = (u_short) -1;
- spSctpTrace[uiNumChunks].usStreamSeqNum = (u_short) -1;
- break;
- case SCTP_CHUNK_HB:
- case SCTP_CHUNK_HB_ACK:
- iSize = SCTP_CHUNK_HEARTBEAT_LENGTH;
- ((SctpHeartbeatChunk_S *) ucpChunk)->sHdr.ucType = eType;
- ((SctpHeartbeatChunk_S *) ucpChunk)->sHdr.ucFlags = 0;
- ((SctpHeartbeatChunk_S *) ucpChunk)->sHdr.usLength = iSize;
- ((SctpHeartbeatChunk_S *) ucpChunk)->usInfoType = 1;
- ((SctpHeartbeatChunk_S *) ucpChunk)->usInfoLength
- = iSize - sizeof(SctpChunkHdr_S);
- ((SctpHeartbeatChunk_S *) ucpChunk)->dTimestamp = dCurrTime;
- ((SctpHeartbeatChunk_S *) ucpChunk)->spDest = NULL; // caller sets dest
- /* fill in tracing fields too
- */
- spSctpTrace[uiNumChunks].eType = eType;
- spSctpTrace[uiNumChunks].uiTsn = (u_int) -1;
- spSctpTrace[uiNumChunks].usStreamId = (u_short) -1;
- spSctpTrace[uiNumChunks].usStreamSeqNum = (u_short) -1;
- break;
- case SCTP_CHUNK_SHUTDOWN:
- break;
- case SCTP_CHUNK_SHUTDOWN_ACK:
- break;
- case SCTP_CHUNK_SHUTDOWN_COMPLETE:
- break;
- default:
- fprintf(stderr, "[GenChunk] ERROR: bad chunk type!n");
- DBG_PL(GenChunk, "ERROR: bad chunk type!") DBG_PR;
- DBG_PL(GenChunk, "exiting...") DBG_PR;
- exit(-1);
- }
- uiNumChunks++;
- DBG_X(GenChunk);
- return iSize;
- }
- void SctpCMTAgent::AddToSendBuffer(SctpDataChunkHdr_S *spChunk,
- int iChunkSize,
- u_int uiReliability,
- SctpDest_S *spDest)
- {
- DBG_I(AddToSendBuffer);
- DBG_PL(AddToSendBuffer, "spDest=%p iChunkSize=%d"),
- spDest, iChunkSize DBG_PR;
- Node_S *spNewNode = new Node_S;
- spNewNode->eType = NODE_TYPE_SEND_BUFFER;
- spNewNode->vpData = new SctpSendBufferNode_S;
- SctpSendBufferNode_S * spNewNodeData
- = (SctpSendBufferNode_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[iChunkSize];
- memcpy(spNewNodeData->spChunk, spChunk, iChunkSize);
- spNewNodeData->eAdvancedAcked = FALSE;
- spNewNodeData->eGapAcked = FALSE;
- spNewNodeData->eAddedToPartialBytesAcked = FALSE;
- spNewNodeData->iNumMissingReports = 0;
- spNewNodeData->iUnrelRtxLimit = uiReliability;
- spNewNodeData->eMarkedForRtx = NO_RTX;
- spNewNodeData->eIneligibleForFastRtx = FALSE;
- spNewNodeData->iNumTxs = 1;
- spNewNodeData->spDest = spDest;
- /*** Begin CMT change ***/
- /* Maintain timestamp for each TSN to use for rtx decision after RTO
- * (RTT heuristic). Orig SCTP uses existence of a non-zero timestamp for
- * indication of whether this TSN is being used for RTT estimate or
- * not. Since that cannot be used anymore with this CMT change, we
- * introduce a new per-TSN variable eMeasuringRtt which is TRUE is this
- * TSN is being used, and FALSE otherwise.
- */
- spNewNodeData->dTxTimestamp = Scheduler::instance().clock();
- /* Is there already a DATA chunk in flight measuring an RTT?
- * (6.3.1.C4 RTT measured once per round trip)
- */
- if(spDest->eRtoPending == FALSE) // NO?
- {
- spNewNodeData->eMeasuringRtt = TRUE;
- spDest->eRtoPending = TRUE; // ...well now there is :-)
- }
- else
- spNewNodeData->eMeasuringRtt = FALSE; // don't use this TSN for RTT est
- /*** End of CMT change ***/
- InsertNode(&sSendBuffer, sSendBuffer.spTail, spNewNode, NULL);
- DBG_X(AddToSendBuffer);
- }
- /* Go thru the send buffer deleting all chunks which have a tsn <= the
- * tsn parameter passed in. We assume the chunks in the rtx list are ordered by
- * their tsn value. In addtion, for each chunk deleted:
- * 1. we add the chunk length to # newly acked bytes and partial bytes acked
- * 2. we update round trip time if appropriate
- * 3. stop the timer if the chunk's destination timer is running
- */
- void SctpCMTAgent::SendBufferDequeueUpTo(u_int uiTsn)
- {
- DBG_I(SendBufferDequeueUpTo);
- Node_S *spDeleteNode = NULL;
- Node_S *spCurrNode = sSendBuffer.spHead;
- SctpSendBufferNode_S *spCurrNodeData = NULL;
- iAssocErrorCount = 0;
- while(spCurrNode != NULL &&
- ((SctpSendBufferNode_S*)spCurrNode->vpData)->spChunk->uiTsn <= uiTsn)
- {
- spCurrNodeData = (SctpSendBufferNode_S *) spCurrNode->vpData;
- /* Only count this chunk as newly acked and towards partial bytes
- * acked if it hasn't been gap acked or marked as ack'd due to rtx
- * limit.
- */
- if((spCurrNodeData->eGapAcked == FALSE) &&
- (spCurrNodeData->eAdvancedAcked == FALSE) )
- {
- uiHighestTsnNewlyAcked = spCurrNodeData->spChunk->uiTsn;
- spCurrNodeData->spDest->iNumNewlyAckedBytes
- += spCurrNodeData->spChunk->sHdr.usLength;
- /****** Begin CMT Change ******/
- /* 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 gets 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;
- }
- /* CUC algo: Since this tsn has NOT been gapacked
- * previously, the pseudo-cum for this dest must be <= tsn.
- * :. Set eFindExpectedPseudoCum to TRUE for destination
- * being cum acked. In gap ack processing, we'll start
- * searching for new expected-pseudo-cum.
- */
- spCurrNodeData->spDest->eFindExpectedPseudoCum = TRUE;
- spCurrNodeData->spDest->eFindExpectedRtxPseudoCum = TRUE;
-
- /* CUC algo: Set eNewPseudoCum to TRUE to trigger cwnd adjustment.
- * The cwnd should be adjusted only if new-pseudo-cum exists;
- * otherwise this TSN would have already contributed to the cwnd.
- * If this TSN had been gap acked previously, then it cannot be
- * the current expected-pseudo-cum.. and hence, the TSN cannot
- * contribute as a new-pseudo-cum. Here, we know that the TSN
- * has not been gap acked before.
- */
- spCurrNodeData->spDest->eNewPseudoCum = TRUE;
-
- /****** End CMT Change ******/
- /* only add to partial bytes acked if we are in congestion
- * avoidance mode and if there was cwnd amount of data
- * outstanding on the destination (implementor's guide)
- */
- if(spCurrNodeData->spDest->iCwnd >spCurrNodeData->spDest->iSsthresh
- &&
- ( spCurrNodeData->spDest->iOutstandingBytes
- >= spCurrNodeData->spDest->iCwnd) )
- {
- spCurrNodeData->spDest->iPartialBytesAcked
- += spCurrNodeData->spChunk->sHdr.usLength;
- }
- }
- /* This is to ensure that Max.Burst is applied when a SACK
- * acknowledges a chunk which has been fast retransmitted. If it is
- * ineligible for fast rtx, that can only be because it was fast
- * rtxed or it timed out. If it timed out, a burst shouldn't be
- * possible, but shouldn't hurt either. The fast rtx case is what we
- * are really after. This is a proposed change to RFC2960 section
- * 7.2.4
- */
- if(spCurrNodeData->eIneligibleForFastRtx == TRUE)
- eApplyMaxBurst = TRUE;
- /****** Begin CMT Change ******/
- /* We update the RTT estimate if the following hold true:
- * 1. RTO pending flag is set (6.3.1.C4 measured once per round trip)
- * 2. We were measuring RTT with 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: exhausted rtxs)
- */
- if(spCurrNodeData->spDest->eRtoPending == TRUE &&
- spCurrNodeData->eMeasuringRtt == TRUE &&
- spCurrNodeData->iNumTxs == 1 &&
- spCurrNodeData->eGapAcked == FALSE &&
- 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?
- * (See below, where we talk about error count resett1ing)
- * 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;
- }
- /* if there is a timer running on the chunk's destination, then stop it
- */
- /* CMT: Added check for expectedPseudoCum.Before this change, even if the
- * timer for a dest was not running on any of the TSNs being dequeued
- * (because the pseudoCum had moved way further already), the timer
- * was being restarted due to the reset here. This caused timeouts
- * to get delayed, and timeout recovery got longer.
- */
- if((spCurrNodeData->spDest->eRtxTimerIsRunning == TRUE) &&
- (spCurrNodeData->spDest->uiExpectedPseudoCum <=
- spCurrNodeData->spChunk->uiTsn) ||
- (spCurrNodeData->spDest->uiExpectedRtxPseudoCum <=
- spCurrNodeData->spChunk->uiTsn))
- StopT3RtxTimer(spCurrNodeData->spDest);
- /****** End CMT Change ******/
-
- /* 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 &&
- spCurrNodeData->eGapAcked == FALSE)
- {
-
- /* If running CMT-PF, clear error counter and move destination
- * from PF to Active only if this chunk has been transmitted
- * only once. Else, ambiguity in deciding whether sack was for
- * original transmission or retransmission.
- */
- if (((eUseCmtPF == TRUE) && (spCurrNodeData->iNumTxs == 1)) ||
- (eUseCmtPF == FALSE))
- {
- DBG_PL(SendBufferDequeueUpTo,
- "clearing error counter for %p with tsn=%lu"),
- spCurrNodeData->spDest, spCurrNodeData->spChunk->uiTsn DBG_PR;
-
- spCurrNodeData->spDest->iErrorCount = 0; // clear error counter
- tiErrorCount++; // ... and trace it too!
- spCurrNodeData->spDest->eStatus = SCTP_DEST_STATUS_ACTIVE;
- if(spCurrNodeData->spDest == spPrimaryDest &&
- spNewTxDest != spPrimaryDest)
- {
- DBG_PL(SendBufferDequeueUpTo,
- "primary recovered... migrating back from %p to %p"),
- spNewTxDest, spPrimaryDest DBG_PR;
- spNewTxDest = spPrimaryDest; // return to primary
- }
- }
- }
-
- spDeleteNode = spCurrNode;
- spCurrNode = spCurrNode->spNext;
- DeleteNode(&sSendBuffer, spDeleteNode);
- spDeleteNode = NULL;
- }
-
- DBG_X(SendBufferDequeueUpTo);
- }
- Boolean_E SctpCMTAgent::AnyMarkedChunks()
- {
- /****** Begin CMT Change ******/
- DBG_I(AnyMarkedChunks);
- Node_S *spCurrBuffNode = NULL;
- SctpSendBufferNode_S *spCurrBuffNodeData = NULL;
- Boolean_E eMarkedChunksExist = FALSE;
- Node_S *spCurrDestNode = NULL;
- SctpDest_S *spCurrDestNodeData = NULL;
- /* CMT change: added eMarkedChunksPending per destination.
- * reset eMarkedChunksPending for all dests
- */
- for(spCurrDestNode = sDestList.spHead;
- spCurrDestNode != NULL;
- spCurrDestNode = spCurrDestNode->spNext)
- {
- spCurrDestNodeData = (SctpDest_S *) spCurrDestNode->vpData;
- spCurrDestNodeData->eMarkedChunksPending = FALSE;
- }
- for(spCurrBuffNode = sSendBuffer.spHead;
- spCurrBuffNode != NULL;
- spCurrBuffNode = spCurrBuffNode->spNext)
- {
- spCurrBuffNodeData = (SctpSendBufferNode_S *) spCurrBuffNode->vpData;
- if(spCurrBuffNodeData->eMarkedForRtx != NO_RTX)
- {
- eMarkedChunksExist = TRUE;
- spCurrBuffNodeData->spDest->eMarkedChunksPending = TRUE;
- DBG_PL(AnyMarkedChunks, "Dest %p: TRUE"),
- spCurrBuffNodeData->spDest DBG_PR;
- }
- }
- DBG_PL(AnyMarkedChunks, "Returning %s"),
- eMarkedChunksExist ? "TRUE" : "FALSE" DBG_PR;
- DBG_X(AnyMarkedChunks);
- return eMarkedChunksExist;
- /****** End CMT Change ******/
- }
- /* This function goes through the entire send buffer filling a packet with
- * chunks marked for retransmission. Once a packet is full (according to MTU)
- * it is transmittted. If the eLimit is one packet, than that is all that is
- * done. If the eLimit is cwnd, then packets full of marked tsns are sent until
- * cwnd is full.
- */
- void SctpCMTAgent::RtxMarkedChunks(SctpRtxLimit_E eLimit)
- {
- DBG_I(RtxMarkedChunks);
- u_char *ucpOutData = new u_char[uiMaxPayloadSize];
- u_char *ucpCurrOutData = ucpOutData;
- int iBundledControlChunkSize = 0;
- int iCurrSize = 0;
- int iOutDataSize = 0;
- Node_S *spCurrBuffNode = NULL;
- SctpSendBufferNode_S *spCurrBuffNodeData = NULL;
- SctpDataChunkHdr_S *spCurrChunk;
- SctpDest_S *spRtxDest = NULL;
- Node_S *spCurrDestNode = NULL;
- SctpDest_S *spCurrDestNodeData = NULL;
- Boolean_E eControlChunkBundled = FALSE;
- /****** Begin CMT Change ******/
- /* CMT change: Var is now per dest, part of SctpDest_S
- *
- * int iNumPacketsSent = 0;
- */
- SctpDest_S *spTraceDest = NULL;
- char cpOutString[500];
- u_int uiRtxTsn = 0;
- double dCurrTime = Scheduler::instance().clock();
- /****** End CMT Change ******/
- memset(ucpOutData, 0, uiMaxPayloadSize);
- /****** Begin CMT Change ******/
- /* CMT change: This var is now per dest. Initialized inside loop.
- * uiBurstLength = 0;
- */
- /****** End CMT Change ******/
- /* 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; // reset
- /****** Begin CMT Change ******/
- spCurrDestNodeData->iNumPacketsSent = 0; // reset
- spCurrDestNodeData->uiBurstLength = 0; // reset
- /****** End CMT Change ******/
- }
- /****** Begin CMT Change ******/
- /* We need to set the destination address for the retransmission(s).We assume
- * that on a given call to this function, all should all be sent to the same
- * address (should be a reasonable assumption). So, to determine the address,
- * we find the first marked chunk and determine the destination it was last
- * sent to.
- *
- * CMT change: This assumption is not reasonable for CMT. We therefore
- * separate the rtxdest selection into a separate function.
- *
- * Also, we temporarily count all marked chunks as not outstanding.Why? Well,
- * if we try retransmitting on the same dest as used previously, the cwnd may
- * never let us retransmit because the outstanding is counting marked chunks
- * too. At the end of this function, we'll count all marked chunks as
- * outstanding again. (ugh... there has to be a better way!)
- */
- 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;
- /* CMT change: Removed code for RtxDest selection. CMT does its
- * selection per TSN, and is inside the loop.
- */
- }
- }
- spCurrBuffNode = sSendBuffer.spHead;
- /* CMT change: While loop should go through entire rtx list.
- * checks are within loop to decide when an rtx should occur or not.
- * check has to be done per tsn to check if a dest is available for
- * that tsn and whether the eLimit applies to a given dest.
- */
- while(spCurrBuffNode != NULL)
- {
-
- /* CMT change: Set spRtxDest to the rtx dest
- * of the first chunk in a rtx packet. Init to NULL now, and
- * set to rtx dest (according to policy) when chunk to be rtx'd
- * is found.
- */
- spRtxDest = NULL;
- /* section 7.2.4.3
- *
- * continue filling up the packet with chunks which are marked for
- * rtx. exit loop when we have either run out of chunks or the
- * packet is full.
- *
- * note: we assume at least one chunk fits in the packet.
- */
- for(eControlChunkBundled = FALSE;
- spCurrBuffNode != NULL;
- spCurrBuffNode = spCurrBuffNode->spNext)
- {
- spCurrBuffNodeData = (SctpSendBufferNode_S *) spCurrBuffNode->vpData;
-
- /* is this chunk the first outstanding on its destination?
- */
- if(spCurrBuffNodeData->spDest->spFirstOutstanding == NULL &&
- spCurrBuffNodeData->eGapAcked == FALSE &&
- spCurrBuffNodeData->eAdvancedAcked == FALSE)
- {
- /* yes, it is the first!
- */
- spCurrBuffNodeData->spDest->spFirstOutstanding
- = spCurrBuffNodeData;
- }
- /* Only retransmit the chunks which have been marked for rtx.
- */
- if(spCurrBuffNodeData->eMarkedForRtx != NO_RTX)
- {
- spCurrChunk = spCurrBuffNodeData->spChunk;
- /* CMT rtx policy used here. Set spRtxDest (according
- * to policy) to the rtx dest of the first chunk in a rtx packet.
- * spRtxDest is set to NULL at the beginning of a packet; once
- * a chunk to be rtx'd is found, rtxDest for the packet is set to
- * the rtx dest for that chunk, which is decided by the rtx
- * policy.
- */
- if(spRtxDest == NULL)
- {
- /* SelectRtxDest() decides rtx dest based on policy and
- * returns dest. If NULL is returned, this TSN cannot
- * be rtx'd at this time---this will cause the for loop
- * to terminate, and the while loop to be reentered.
- */
- spRtxDest = SelectRtxDest(spCurrBuffNodeData, eLimit);
- }
-
- /* Check finally if for loop should be quit.
- * If spRtxDest is NULL, no dest was able to send rtx.
- */
- if(spRtxDest == NULL)
- {
- /* Jump out of for loop;move spCurrBuffNode to next in queue.
- * At this point, no dest was able to send this rtx.
- * There may be other TSNs in the queue waiting for rtx,
- * so we move on to process them. "while" loop is reentered,
- * and all tsns in the queue are processed.
- */
- spCurrBuffNode = spCurrBuffNode->spNext;
- DBG_PL(RtxMarkedChunks,
- "Unable to rtx TSN %d at this timen"),
- spCurrChunk->uiTsn DBG_PR;
- break;
- }
- else
- {
- /* CMT CUC algo: If rtxdest is not the same as orig dest, set
- * both eFindExpectedPseudoCumack and
- * eFindExpectedRtxPseudoCumack to TRUE.
- */
- if(spCurrBuffNodeData->spDest != spRtxDest)
- {
- spCurrBuffNodeData->spDest->eFindExpectedPseudoCum
- = TRUE;
- spCurrBuffNodeData->spDest->eFindExpectedRtxPseudoCum
- = TRUE;
- }
- }
- DBG_PL(RtxMarkedChunks,
- "eLimit=%s spRtxDest->eRtxLimit = %d pktsSent=%d out=%d cwnd=%d spCurrChunk->uiTsn=%d"),
- (eLimit == RTX_LIMIT_ONE_PACKET) ? "ONE_PACKET" : "CWND",
- spRtxDest->eRtxLimit,
- spRtxDest->iNumPacketsSent, spRtxDest->iOutstandingBytes,
- spRtxDest->iCwnd,
- spCurrChunk->uiTsn DBG_PR;
- /* CMT change: Code moved from timeoutrtx
- * No fast rtxs allowed on TSNs that have timed out.
- * This is particularly important for Changeover/CMT.
- */
- spCurrBuffNodeData->eIneligibleForFastRtx = TRUE;
-
- /****** End CMT Change ******/
- /* bundle the control chunk before any data chunks and only
- * once per packet
- */
- if(eControlChunkBundled == FALSE)
- {
- eControlChunkBundled = TRUE;
- iBundledControlChunkSize
- = BundleControlChunks(ucpCurrOutData);
- ucpCurrOutData += iBundledControlChunkSize;
- iOutDataSize += iBundledControlChunkSize;
- }
- /* can we fit this chunk into the packet without exceeding MTU??
- */
- if((iOutDataSize + spCurrChunk->sHdr.usLength)
- > (int) uiMaxPayloadSize)
- break; // doesn't fit in packet... jump out of the for loop
- /* If this chunk was being used to measure the RTT,stop using it.
- */
- if(spCurrBuffNodeData->spDest->eRtoPending == TRUE &&
- spCurrBuffNodeData->eMeasuringRtt == TRUE)
- {
- spCurrBuffNodeData->eMeasuringRtt = FALSE;
- spCurrBuffNodeData->spDest->eRtoPending = FALSE;
- }
- /* section 7.2.4.4 (condition 2) - is this the first
- * outstanding for the destination and are there still
- * outstanding bytes on the destination? if so, restart
- * timer.
- */
- if(spCurrBuffNodeData->spDest->spFirstOutstanding
- == spCurrBuffNodeData)
- {
- if(spCurrBuffNodeData->spDest->iOutstandingBytes > 0)
- StartT3RtxTimer(spCurrBuffNodeData->spDest);
- }
- /* section 6.1 - Whenever a transmission or retransmission is
- * made to any address, if the T3-rtx timer of that address
- * is not currently running, the sender MUST start that timer.
- * If the timer for that address is already running, the sender
- * MUST restart the timer if the earliest (i.e., lowest TSN)
- * outstanding DATA chunk sent to that address is being
- * retransmitted. Otherwise, the data sender MUST NOT
- * restart the timer.
- */
- if(spRtxDest->spFirstOutstanding == NULL ||
- spCurrChunk->uiTsn <
- spRtxDest->spFirstOutstanding->spChunk->uiTsn)
- {
- /* This chunk is now the first outstanding on spRtxDest.
- */
- spRtxDest->spFirstOutstanding = spCurrBuffNodeData;
- StartT3RtxTimer(spRtxDest);
- }
- memcpy(ucpCurrOutData, spCurrChunk, spCurrChunk->sHdr.usLength);
- iCurrSize = spCurrChunk->sHdr.usLength;
- /* the chunk length field does not include the padded bytes,
- * so we need to account for these extra bytes.
- */
- if( (iCurrSize % 4) != 0 )
- iCurrSize += 4 - (iCurrSize % 4);
- ucpCurrOutData += iCurrSize;
- iOutDataSize += iCurrSize;
- spCurrBuffNodeData->spDest = spRtxDest;
- spCurrBuffNodeData->iNumTxs++;
- spCurrBuffNodeData->eMarkedForRtx = NO_RTX;
- /* fill in tracing fields too
- */
- spSctpTrace[uiNumChunks].eType = SCTP_CHUNK_DATA;
- spSctpTrace[uiNumChunks].uiTsn = spCurrChunk->uiTsn;
- spSctpTrace[uiNumChunks].usStreamId = spCurrChunk->usStreamId;
- spSctpTrace[uiNumChunks].usStreamSeqNum
- = spCurrChunk->usStreamSeqNum;
- uiNumChunks++;
- /* the chunk is now outstanding on the alternate destination
- */
- spCurrBuffNodeData->spDest->iOutstandingBytes
- += spCurrChunk->sHdr.usLength;
- uiPeerRwnd -= spCurrChunk->sHdr.usLength; // 6.2.1.B
- DBG_PL(RtxMarkedChunks, "spDest->iOutstandingBytes=%d"),
- spCurrBuffNodeData->spDest->iOutstandingBytes DBG_PR;
- DBG_PL(RtxMarkedChunks, "TSN=%d"), spCurrChunk->uiTsn DBG_PR;
- uiRtxTsn=spCurrChunk->uiTsn;
- }
- else if(spCurrBuffNodeData->eAdvancedAcked == TRUE)
- {
- if(spCurrBuffNodeData->spDest->spFirstOutstanding
- == spCurrBuffNodeData)
- {
- /* This WAS considered the first outstanding chunk for
- * the destination, then stop the timer if there are no
- * outstanding chunks waiting behind this one in the
- * send buffer. However, if there ARE more outstanding
- * chunks on this destination, we need to restart timer
- * for those.
- */
- if(spCurrBuffNodeData->spDest->iOutstandingBytes > 0)
- StartT3RtxTimer(spCurrBuffNodeData->spDest);
- else
- StopT3RtxTimer(spCurrBuffNodeData->spDest);
- }
- }
- } // end of for loop
- /* Transmit the packet now...
- */
- if(iOutDataSize > 0)
- {
- SendPacket(ucpOutData, iOutDataSize, spRtxDest);
- DBG_PL(RtxMarkedChunks, "OutDataSize = %d"), iOutDataSize DBG_PR;
- /****** Begin CMT Change ******/
- /* PN: Track rtx tsns in the trace file for debug purposes.
- */
- spTraceDest = spRtxDest;
- SetSource(spTraceDest); // gives us the correct source addr & port
- sprintf(cpOutString,
- "time: %-8.5f "
- "saddr: %-2d sport: %-2d daddr: %-2d dport: %-2d "
- "RTX TSN: %d ",
- dCurrTime,
- addr(), port(), spTraceDest->iNsAddr, spTraceDest->iNsPort,
- uiRtxTsn);
- if(channel_)
- (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
- sprintf(cpOutString, "nn");
- if(channel_)
- (void)Tcl_Write(channel_, cpOutString, strlen(cpOutString));
- /****** End CMT Change ******/
- if(spRtxDest->eRtxTimerIsRunning == FALSE)
- StartT3RtxTimer(spRtxDest);
- /****** Begin CMT Change ******/
- (spRtxDest->iNumPacketsSent)++;
- /****** End CMT Change ******/
- iOutDataSize = 0; // reset
- ucpCurrOutData = ucpOutData; // reset
- memset(ucpOutData, 0, uiMaxPayloadSize); // reset
- spRtxDest->opCwndDegradeTimer->resched(spRtxDest->dRto);
- /****** Begin CMT Change ******/
- /* 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.
- */
- /* CMT change: Use per dest burstlength to track bursts per dest.
- * *** BUG: The while loop should terminate after ALL
- * selectable destinations have either exhausted their cwnds, or
- * their burst limits. As the code stands, whenever the burst
- * limit is reached on ANY dest, the while loop is quit. Is
- * this a problem? Well... maybe not. RTX-SSTHRESH/CWND/LR
- * should have (normally) one destination to which rtxs are
- * being sent. In this case, we would not more destinations to
- * try on anyways. In RTX-SAME/ASAP, the next incoming SACK will
- * trigger a call to this function anyways.
- *
- * JRI-TODO: This bug should be fixed! Idea: Move check inside
- * SelectRtxDest(). Dest not selectable if MaxBurst exceeded.
- * That should do it.
- */
- if(eUseMaxBurst == MAX_BURST_USAGE_ON)
- if( (eApplyMaxBurst == TRUE) &&
- ((spRtxDest->uiBurstLength)++ >= MAX_BURST) )
- {
- /* we've reached Max.Burst limit, so jump out of loop
- */
- break;
- }
- /****** End CMT Change ******/
- }
- } // end of while loop
- /****** Begin CMT Change ******/
- /*
- * With the RTT heuristic in CMT, TSNs outstanding for less than 1 SRTT
- * are not marked for rtx. Therefore, at this point in the function, if
- * no rtxs were sent out, but there are these unmarked TSNs less than 1
- * SRTT old outstanding, then the timer should be running for those TSNs.
- *
- * Interesting case here. Rtxs are marked, but should not be
- * outstanding, and may not have been rtx'd at all. But with the RTT
- * heuristic there may be TSNs sent less than 1 SRTT ago that are
- * outstanding (i.e., not marked for rtx) and need the timer. So, what
- * should spFirstOutstanding point to? We think that the first REAL
- * outstanding chunk should be spFirstOutstanding. That is, set
- * spFirstOutstanding to the first chunk that (i) is not marked for rtx,
- * and (ii) is not yet gapacked. If a TSN does get rtx'd later to this
- * same dest, then the spFirstOutstanding will move to point to the
- * lower TSN.
- *
- * JRI-TODO, PN-TODO: Go through sendbuf and set spFirstOutstanding to
- * first TSN which is (i) is not marked for rtx, and (ii) is not yet
- * gapacked.
- */
- for(spCurrDestNode = sDestList.spHead;
- spCurrDestNode != NULL;
- spCurrDestNode = spCurrDestNode->spNext)
- {
- spCurrDestNodeData = (SctpDest_S *) spCurrDestNode->vpData;
- if((spCurrDestNodeData->eRtxTimerIsRunning == FALSE) &&
- (spCurrDestNodeData->iOutstandingBytes > 0))
- {
- DBG_PL(RtxMarkedChunks, "Dest %p: starting T3 timer"),
- spCurrDestNodeData DBG_PR;
- StartT3RtxTimer(spCurrBuffNodeData->spDest);
- }
- }
- /****** End CMT Change ******/
- /* Ok, let's count all marked chunks as outstanding again. (ugh... there
- * has to be a better way!)
- */
- 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;
- }
- }
-
- /* If we made it here, either our limit was only one packet worth of
- * retransmissions or we hit the end of the list and there are no more
- * marked chunks.If we didn't hit the end, let's see if there are more marked
- * chunks.
- */
- eMarkedChunksPending = AnyMarkedChunks();
- DBG_X(RtxMarkedChunks);
- delete [] ucpOutData;
- }
- /****** Begin CMT Change ******/
- /* New CMT function:
- * returns the retransmission destination for the given
- * TSN and policy. returns NULL if no dest is found that can accomodate
- * this TSN given the policy and eLimit.
- *
- * CMT-PF: CMT-PF code implemented only for
- * RTX-SSTHRESH and RTX-CWND policies
- */
- SctpDest_S* SctpCMTAgent::SelectRtxDest(SctpSendBufferNode_S
- *spCurrBuffNodeData,
- SctpRtxLimit_E eLimit)
- {
- Boolean_E eFoundRtxDest = FALSE;
- SctpDest_S *spRtxDest = NULL;
- Node_S *spCurrDestNode = NULL;
- SctpDest_S *spCurrDestNodeData = NULL;
- unsigned int uiHighestSsthresh = 0, uiHighestCwnd = 0;
- float fLowestLossrate = 0;
-
- DBG_I(SelectRtxDest);
- eFoundRtxDest = FALSE;
- switch (eCmtRtxPolicy)
- {
- case RTX_ASAP:
- /* NE: 04/16/2007
- * CMT rtx policy - rtxasap: Try to rtx to any destination for
- * which the sender has cwnd space available at the time of
- * retransmission. If multiple destinations have available cwnd
- * space, one is chosen randomly.
- * Randomization of selecting a destination is not uniform in
- * this implementation.
- * IMPORTANT NOTICE: This rtx. policy is used for experimental purposes
- * ONLY and is retained for verifying previous experiments. Please use
- * one of the suggested rtx. policies for CMT and CMT-PF experiments:
- * RTX_SSTHRESH or RTX_CWND.
- */
-
- spCurrDestNodeData = spCurrBuffNodeData->spDest;
- DBG_PL(SelectRtxDest, "Current Dest: %p, Status: %d"),
- spCurrDestNodeData, spCurrDestNodeData->eStatus DBG_PR;
- for(spCurrDestNode = sDestList.spHead;
- spCurrDestNode != NULL;
- spCurrDestNode = spCurrDestNode->spNext)
- {
- spCurrDestNodeData = (SctpDest_S *) spCurrDestNode->vpData;
- /* Modified rtx mechanisms from SCTP. Allow ONE_PACKET_LIMIT
- * for dest which has suffered a loss, so that at least one dest
- * is allowed to retransmit one lost packet immediately. The rest
- * of the dests are bound by their resp. cwnds. Subsequent calls
- * triggering sending rtxs will be bound by the resp. cwnds.
- */
- if((eLimit == RTX_LIMIT_ONE_PACKET &&
- spCurrDestNodeData->eRtxLimit == RTX_LIMIT_ONE_PACKET &&
- spCurrDestNodeData->iNumPacketsSent < 1 ) ||
- (spCurrDestNodeData->iOutstandingBytes <
- spCurrDestNodeData->iCwnd))
- {
- if (spCurrDestNodeData->eStatus == SCTP_DEST_STATUS_ACTIVE)
- {
- if((eFoundRtxDest == TRUE) && (Random::random()&01))
- {
- /* Randomize dest if more than one found.
- * If random() returns 1, then retain prev dest.,
- * else set rtxDest to this dest.
- * Nothing to be done here.
- */
- }
- else
- {
- /* rtx will be sent to this destination.
- * Set eFoundRtxDest to TRUE, to continue with
- * the procedure.
- */
- eFoundRtxDest = TRUE;
- spRtxDest = spCurrDestNodeData;
-
- }
- }
- }
- }
- /* No Active destinations found, set eFoundRtxDest to FALSE
- * to return NULL
- */
- if (spRtxDest == NULL)
- eFoundRtxDest = FALSE;
- else
- {
-
- DBG_PL(SelectRtxDest, "Dest: %p"), spRtxDest DBG_PR;
-
- /* Modified rtx mechanisms from SCTP. Allow ONE_PACKET_LIMIT
- * for dest which has suffered a loss, so that at least one dest
- * is allowed to retransmit one lost packet immediately. The rest
- * of the dests are bound by their resp. cwnds. Subsequent calls
- * triggering sending rtxs will be bound by the resp. cwnds.
- */
- if((eLimit == RTX_LIMIT_ONE_PACKET &&
- spRtxDest->eRtxLimit == RTX_LIMIT_ONE_PACKET &&
- spRtxDest->iNumPacketsSent < 1 ) ||
- (spRtxDest->iOutstandingBytes < spRtxDest->iCwnd))
- {
- /* rtx will be sent to rtxDest. Set eFoundRtxDest
- * to TRUE, to continue with the rtx procedure.
- */
- eFoundRtxDest = TRUE;
- DBG_PL(SelectRtxDest, "SelectedRtxDest: %p"), spRtxDest DBG_PR;
- }
- }
-
- break;
-
- case RTX_TO_SAME:
- /* NE: 4/29/2007
- * CMT rtx policy - rtxtosame: Rtx goes to same dest only(until the
- * destination is deemed INACTIVE due to failure).
- * IMPORTANT NOTICE: This rtx. policy is used for experimental purposes
- * ONLY and is retained for verifying previous experiments. Please use
- * one of the suggested rtx. policies for CMT and CMT-PF experiments:
- * RTX_SSTHRESH or RTX_CWND.
- */
- DBG_PL(SelectRtxDest, "Rtx policy: RTX-SAME") DBG_PR;
- spRtxDest = spCurrBuffNodeData->spDest;
- /* If RtxDest is failed, get next in list.
- * Infinite loop if all dests failed!
- */
- while (spRtxDest->eStatus == SCTP_DEST_STATUS_INACTIVE)
- {
- spRtxDest = GetNextDest(spRtxDest);
- }
-
- DBG_PL(SelectRtxDest,
- "spRtxDest %p: iCwnd=%d iOutstandingBytes=%d iNumPacketsSent=%d"),
- spRtxDest, spRtxDest->iCwnd, spRtxDest->iOutstandingBytes,
- spRtxDest->iNumPacketsSent DBG_PR;
-
- /* No Active destinations found, set eFoundRtxDest to FALSE
- * to return NULL
- */
- if (spRtxDest == NULL)
- eFoundRtxDest = FALSE;
- else
- {
- if((eLimit == RTX_LIMIT_ONE_PACKET &&
- spRtxDest->eRtxLimit == RTX_LIMIT_ONE_PACKET &&
- spRtxDest->iNumPacketsSent < 1 ) ||
- (spRtxDest->iOutstandingBytes < spRtxDest->iCwnd))
- {
- /* rtx will be sent to same dest as tx. Set eFoundRtxDest
- * to TRUE, to continue with the rtx procedure.
- */
- eFoundRtxDest = TRUE;
- DBG_PL(SelectRtxDest, "SelectedRtxDest: %p"), spRtxDest DBG_PR;
- }
- else
- {
- DBG_PL(SelectRtxDest, "Unable to find rtx destination") DBG_PR;
- }
- }
- break; // end of rtxtosame dest selection
- case RTX_SSTHRESH:
- /* NE: 4/29/2007
- * CMT rtx. policy - RTX_SSTHRESH: rtx. is sent to the destination for
- * which the sender has the largest ssthresh. A tie is broken by random
- * selection.
- */
- uiHighestSsthresh = 0;
- /* CMT-PF: If all destinations are marked PF, select one from them
- * for retransmissions
- */
- if (eUseCmtPF == TRUE)
- {
- spRtxDest = SelectFromPFDests();
- if (spRtxDest != NULL)
- {
- /* spRtxDest was PF and changed to Active by SelectFromPFDests()
- */
- tiCountPFToActiveRtxms++ ; //trace will pick it up
-
- eFoundRtxDest = TRUE;
-
- break; // break for RTX_SSTHRESH case.
- }
- }
- for(spCurrDestNode = sDestList.spHead;
- spCurrDestNode != NULL;
- spCurrDestNode = spCurrDestNode->spNext)
- {
- spCurrDestNodeData = (SctpDest_S *) spCurrDestNode->vpData;
- DBG_PL(SelectRtxDest, "Dest: %p, Status: %d"),
- spCurrDestNodeData, spCurrDestNodeData->eStatus DBG_PR;
- /* Do not retransmit to an INACTIVE or PF destination.
- */
- if((spCurrDestNodeData->eStatus == SCTP_DEST_STATUS_INACTIVE) ||
- (spCurrDestNodeData->eStatus == SCTP_DEST_STATUS_POSSIBLY_FAILED))
- continue;
- DBG_PL(SelectRtxDest, "Dest: %p, Ssthresh: %d, Out: %d"),
- spCurrDestNodeData, spCurrDestNodeData->iSsthresh,
- spCurrDestNodeData->iOutstandingBytes DBG_PR;
- if ((int)uiHighestSsthresh < spCurrDestNodeData->iSsthresh)
- {
- uiHighestSsthresh = spCurrDestNodeData->iSsthresh;
- spRtxDest = spCurrDestNodeData;
- }
- else if ((int)uiHighestSsthresh == spCurrDestNodeData->iSsthresh)
- {
- // break ties with random selection
- if(Random::random()&01)
- spRtxDest = spCurrDestNodeData;
- }
- }
- /* if no active dest found, then return null
- */
- if (spRtxDest == NULL)
- eFoundRtxDest = FALSE;
- else
- {
- DBG_PL(SelectRtxDest, "Dest: %p"), spRtxDest DBG_PR;
- /* Modified rtx mechanisms from SCTP. Allow ONE_PACKET_LIMIT
- * for dest which has suffered a loss, so that at least one dest
- * is allowed to retransmit one lost packet immediately. The rest
- * of the dests are bound by their resp. cwnds. Subsequent calls
- * triggering sending rtxs will be bound by the resp. cwnds.
- */
- if((eLimit == RTX_LIMIT_ONE_PACKET &&
- spRtxDest->eRtxLimit == RTX_LIMIT_ONE_PACKET &&
- spRtxDest->iNumPacketsSent < 1 ) ||
- (spRtxDest->iOutstandingBytes < spRtxDest->iCwnd))
- {
- /* rtx will be sent to rtxDest. Set eFoundRtxDest
- * to TRUE, to continue with the rtx procedure.
- */
- eFoundRtxDest = TRUE;
- DBG_PL(SelectRtxDest, "SelectedRtxDest: %p"), spRtxDest DBG_PR;
- }
- }
- break;
- case RTX_LOSSRATE:
- /* NE: 4/24/2007
- * CMT rtx. policy - RTX_LOSSRATE: rtx. is sent to the destination with
- * the lowest loss rate path. If multiple destinations have the same
- * loss rate, one is selected randomly.
- * IMPORTANT NOTICE: This rtx. policy is used for experimental purposes
- * ONLY and is retained for verifying previous experiments. Please use
- * one of the suggested rtx. policies for CMT and CMT-PF experiments:
- * RTX_SSTHRESH or RTX_CWND.
- */
-
- fLowestLossrate = 10;
- for(spCurrDestNode = sDestList.spHead;
- spCurrDestNode != NULL;
- spCurrDestNode = spCurrDestNode->spNext)
- {
- spCurrDestNodeData = (SctpDest_S *) spCurrDestNode->vpData;
- if (spCurrDestNodeData->eStatus == SCTP_DEST_STATUS_INACTIVE)
- continue;
- if (fLowestLossrate > spCurrDestNodeData->fLossrate)
- {
- fLowestLossrate = spCurrDestNodeData->fLossrate;
- spRtxDest = spCurrDestNodeData;
-
- }
- else if (fLowestLossrate == spCurrDestNodeData->fLossrate)
- {
- // break ties with random selection
- if(Random::random()&01)
- spRtxDest = spCurrDestNodeData;
- }
- }
- /* if no active dest found, then return null
- */
- if (spRtxDest == NULL)
- eFoundRtxDest = FALSE;
- else
- {
- /* Modified rtx mechanisms from SCTP. Allow ONE_PACKET_LIMIT
- * for dest which has suffered a loss, so that at least one dest
- * is allowed to retransmit one lost packet immediately. The rest
- * of the dests are bound by their resp. cwnds. Subsequent calls
- * triggering sending rtxs will be bound by the resp. cwnds.
- */
- if((eLimit == RTX_LIMIT_ONE_PACKET &&
- spRtxDest->eRtxLimit == RTX_LIMIT_ONE_PACKET &&
- spRtxDest->iNumPacketsSent < 1 ) ||
- (spRtxDest->iOutstandingBytes < spRtxDest->iCwnd))
- {
- /* rtx will be sent to rtxDest. Set eFoundRtxDest
- * to TRUE, to continue with the rtx procedure.
- */
- eFoundRtxDest = TRUE;
- DBG_PL(SelectRtxDest, "SelectedRtxDest: %p"), spRtxDest DBG_PR;
- }
- }
- break;
- case RTX_CWND:
- /* NE: 4/29/2007
- * CMT rtx. policy - RTX_CWND: rtx. is sent to the destination for which
- * the sender has the largest cwnd. A tie is broken by random selection.
- */
- uiHighestCwnd = 0;
- /* CMT-PF: If all destinations are marked PF, select one from them
- * for retransmissions
- */
- if (eUseCmtPF == TRUE)
- {
- spRtxDest = SelectFromPFDests();
- if (spRtxDest != NULL)
- {
- /* spRtxDest was PF and changed to Active by SelectFromPFDests()
- */
- tiCountPFToActiveRtxms++ ; //trace will pick it up
-
- eFoundRtxDest = TRUE;
-
- break; // break for RTX_CWND case.
- }
-
- } /* if (eUseCmtPF == TRUE) */
-
- for(spCurrDestNode = sDestList.spHead;
- spCurrDestNode != NULL;
- spCurrDestNode = spCurrDestNode->spNext)
- {
- spCurrDestNodeData = (SctpDest_S *) spCurrDestNode->vpData;
-
- DBG_PL(SelectRtxDest, "Dest: %p, Status: %d"),
- spCurrDestNodeData, spCurrDestNodeData->eStatus DBG_PR;
-
- /* Do not retransmit to an INACTIVE or PF destination.
- */
- if((spCurrDestNodeData->eStatus == SCTP_DEST_STATUS_INACTIVE) ||
- (spCurrDestNodeData->eStatus == SCTP_DEST_STATUS_POSSIBLY_FAILED))
- continue;
- DBG_PL(SelectRtxDest, "Dest: %p, Cwnd: %d, Out: %d"),
- spCurrDestNodeData, spCurrDestNodeData->iCwnd,
- spCurrDestNodeData->iOutstandingBytes DBG_PR;
-
- if ((int)uiHighestCwnd < spCurrDestNodeData->iCwnd)
- {
- uiHighestCwnd = spCurrDestNodeData->iCwnd;
- spRtxDest = spCurrDestNodeData;
- }
- else if ((int)uiHighestCwnd == spCurrDestNodeData->iCwnd)
- {
- // break ties with random selection
- if(Random::random()&01)
- spRtxDest = spCurrDestNodeData;
- }
- }
-
- /* No Active destinations found, set eFoundRtxDest to FALSE
- * to return NULL
- */
- if (spRtxDest == NULL)
- eFoundRtxDest = FALSE;
- else
- {
-
- DBG_PL(SelectRtxDest, "Dest: %p"), spRtxDest DBG_PR;
-
- /* Modified rtx mechanisms from SCTP. Allow ONE_PACKET_LIMIT
- * for dest which has suffered a loss, so that at least one dest
- * is allowed to retransmit one lost packet immediately. The rest
- * of the dests are bound by their resp. cwnds. Subsequent calls
- * triggering sending rtxs will be bound by the resp. cwnds.
- */
- if((eLimit == RTX_LIMIT_ONE_PACKET &&
- spRtxDest->eRtxLimit == RTX_LIMIT_ONE_PACKET &&
- spRtxDest->iNumPacketsSent < 1 ) ||
- (spRtxDest->iOutstandingBytes < spRtxDest->iCwnd))
- {
- /* rtx will be sent to rtxDest. Set eFoundRtxDest
- * to TRUE, to continue with the rtx procedure.
- */
- eFoundRtxDest = TRUE;
- DBG_PL(SelectRtxDest, "SelectedRtxDest: %p"), spRtxDest DBG_PR;
- }
- }
- break;
-
- }
-
- if(eFoundRtxDest == FALSE)
- spRtxDest = NULL;
- DBG_PL(SelectRtxDest, "Selected Dest: %p"), spRtxDest DBG_PR;
-
- DBG_X(SelectRtxDest);
- return spRtxDest;
- }
- /****** End CMT Change ******/
- /* returns a boolean of whether a fast retransmit is necessary
- */
- Boolean_E SctpCMTAgent::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;
-
- /****** Begin CMT Change ******/
- /* Initialize uiHighestTsnInSackForDest = current cumack, since it
- * has to be at least equal to the cumack for the destinations
- * which matter in the fast rtx process. uiHighestTsnInSackForDest
- * is used for HTNA also.
- */
- spCurrDestNodeData->uiHighestTsnInSackForDest =spSackChunk->uiCumAck;
- /****** End CMT Change ******/
- }
- for(spCurrNode = sSendBuffer.spHead;
- (spCurrNode != NULL) &&
- (usNumGapAcksProcessed != spSackChunk->usNumGapAckBlocks);
- spCurrNode = spCurrNode->spNext)
- {
- spCurrNodeData = (SctpSendBufferNode_S *) spCurrNode->vpData;
-
- /****** Begin CMT Change ******/
- /* CMT CUC algo: If expected-pseudo(-rtx)-cum is to be found,
- * check if this tsn has been previously acked. If not, set
- * expected-pseudo(-rtx)-cum to this tsn. uiExpectedPseudoCum
- * tracks the PCum for new txs on a dest, and
- * uiExpectedRtxPseudoCum tracks the pcum for rtxs on the dest.
- * NOTE: This cwnd algo assumes that the sendbuf is sorted
- * according to TSN.
- */
-
- if ((spCurrNodeData->spDest->eFindExpectedPseudoCum == TRUE) &&
- (spCurrNodeData->eGapAcked == FALSE) &&
- (spCurrNodeData->iNumTxs == 1))
- {
- spCurrNodeData->spDest->uiExpectedPseudoCum =
- spCurrNodeData->spChunk->uiTsn;
- spCurrNodeData->spDest->eFindExpectedPseudoCum = FALSE;
- }
- if ((spCurrNodeData->spDest->eFindExpectedRtxPseudoCum == TRUE) &&
- (spCurrNodeData->eGapAcked == FALSE) &&
- (spCurrNodeData->iNumTxs > 1))
- {
- spCurrNodeData->spDest->uiExpectedRtxPseudoCum =
- spCurrNodeData->spChunk->uiTsn;
- spCurrNodeData->spDest->eFindExpectedRtxPseudoCum = FALSE;
- }
- /****** End CMT Change ******/
- /* 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;