|
INET Framework for OMNeT++/OMNEST
|
00001 // 00002 // Copyright (C) 2005-2010 Irene Ruengeler 00003 // Copyright (C) 2009-2010 Thomas Dreibholz 00004 // 00005 // This program is free software; you can redistribute it and/or 00006 // modify it under the terms of the GNU General Public License 00007 // as published by the Free Software Foundation; either version 2 00008 // of the License, or (at your option) any later version. 00009 // 00010 // This program is distributed in the hope that it will be useful, 00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00013 // GNU General Public License for more details. 00014 // 00015 // You should have received a copy of the GNU General Public License 00016 // along with this program; if not, see <http://www.gnu.org/licenses/>. 00017 // 00018 00019 #include <string.h> 00020 #include <assert.h> 00021 #include "SCTP.h" 00022 #include "SCTPAssociation.h" 00023 #include "SCTPCommand_m.h" 00024 #include "SCTPMessage_m.h" 00025 #include "SCTPQueue.h" 00026 #include "SCTPAlgorithm.h" 00027 #include "IPv4InterfaceData.h" 00028 #include "IPv6InterfaceData.h" 00029 #include "IPControlInfo_m.h" 00030 00031 00032 void SCTPAssociation::decreaseOutstandingBytes(SCTPDataVariables* chunk) 00033 { 00034 SCTPPathVariables* lastPath = chunk->getLastDestinationPath(); 00035 00036 assert(lastPath->outstandingBytes >= chunk->booksize); 00037 lastPath->outstandingBytes -= chunk->booksize; 00038 state->outstandingBytes -= chunk->booksize; 00039 assert((int64)state->outstandingBytes >= 0); 00040 00041 chunk->countsAsOutstanding = false; 00042 00043 CounterMap::iterator iterator = qCounter.roomRetransQ.find(lastPath->remoteAddress); 00044 iterator->second -= ADD_PADDING(chunk->booksize + SCTP_DATA_CHUNK_LENGTH); 00045 00046 } 00047 00048 00049 bool SCTPAssociation::process_RCV_Message(SCTPMessage* sctpmsg, 00050 const IPvXAddress& src, 00051 const IPvXAddress& dest) 00052 { 00053 // ====== Header checks ================================================== 00054 sctpEV3 << getFullPath() << " SCTPAssociationRcvMessage:process_RCV_Message" 00055 << " localAddr=" << localAddr 00056 << " remoteAddr=" << remoteAddr << endl; 00057 if ((sctpmsg->hasBitError() || !sctpmsg->getChecksumOk())) 00058 { 00059 if (((SCTPChunk*)(sctpmsg->getChunks(0)))->getChunkType() == INIT_ACK) { 00060 stopTimer(T1_InitTimer); 00061 sctpEV3 << "InitAck with bit-error. Retransmit Init" << endl; 00062 retransmitInit(); 00063 startTimer(T1_InitTimer,state->initRexmitTimeout); 00064 } 00065 if (((SCTPChunk*)(sctpmsg->getChunks(0)))->getChunkType() == COOKIE_ACK) { 00066 stopTimer(T1_InitTimer); 00067 sctpEV3 << "CookieAck with bit-error. Retransmit CookieEcho" << endl; 00068 retransmitCookieEcho(); 00069 startTimer(T1_InitTimer,state->initRexmitTimeout); 00070 } 00071 } 00072 00073 SCTPPathVariables* path = getPath(src); 00074 const uint16 srcPort = sctpmsg->getDestPort(); 00075 const uint16 destPort = sctpmsg->getSrcPort(); 00076 const uint32 numberOfChunks = sctpmsg->getChunksArraySize(); 00077 sctpEV3 << "numberOfChunks=" <<numberOfChunks << endl; 00078 00079 state->sctpmsg = (SCTPMessage*)sctpmsg->dup(); 00080 00081 // ====== Handle chunks ================================================== 00082 bool trans = true; 00083 bool sendAllowed = false; 00084 bool dupReceived = false; 00085 bool dataChunkReceived = false; 00086 bool dataChunkDelivered = false; 00087 bool shutdownCalled = false; 00088 for (uint32 i = 0; i < numberOfChunks; i++) { 00089 const SCTPChunk* header = (const SCTPChunk*)(sctpmsg->removeChunk()); 00090 const uint8 type = header->getChunkType(); 00091 00092 if ((type != INIT) && 00093 (type != ABORT) && 00094 (type != ERRORTYPE) && 00095 (sctpmsg->getTag() != peerVTag)) { 00096 sctpEV3 << " VTag "<< sctpmsg->getTag() << " incorrect. Should be " 00097 << peerVTag << " localVTag=" << localVTag << endl; 00098 return true; 00099 } 00100 00101 switch (type) { 00102 case INIT: 00103 sctpEV3 << "SCTPAssociationRcvMessage: INIT received" << endl; 00104 SCTPInitChunk* initChunk; 00105 initChunk = check_and_cast<SCTPInitChunk*>(header); 00106 if ((initChunk->getNoInStreams() != 0) && 00107 (initChunk->getNoOutStreams() != 0) && 00108 (initChunk->getInitTag() != 0)) { 00109 trans = processInitArrived(initChunk, srcPort, destPort); 00110 } 00111 i = numberOfChunks-1; 00112 delete initChunk; 00113 break; 00114 case INIT_ACK: 00115 sctpEV3 << "SCTPAssociationRcvMessage: INIT_ACK received" << endl; 00116 if (fsm->getState() == SCTP_S_COOKIE_WAIT) { 00117 SCTPInitAckChunk* initAckChunk; 00118 initAckChunk = check_and_cast<SCTPInitAckChunk*>(header); 00119 if ((initAckChunk->getNoInStreams() != 0) && 00120 (initAckChunk->getNoOutStreams() != 0) && 00121 (initAckChunk->getInitTag() != 0)) { 00122 trans = processInitAckArrived(initAckChunk); 00123 } 00124 else if (initAckChunk->getInitTag() == 0) { 00125 sendAbort(); 00126 sctpMain->removeAssociation(this); 00127 } 00128 i = numberOfChunks-1; 00129 delete initAckChunk; 00130 } 00131 else { 00132 sctpEV3 << "INIT_ACK will be ignored" << endl; 00133 } 00134 break; 00135 case COOKIE_ECHO: 00136 sctpEV3 << "SCTPAssociationRcvMessage: COOKIE_ECHO received" << endl; 00137 SCTPCookieEchoChunk* cookieEchoChunk; 00138 cookieEchoChunk = check_and_cast<SCTPCookieEchoChunk*>(header); 00139 trans = processCookieEchoArrived(cookieEchoChunk,src); 00140 delete cookieEchoChunk; 00141 break; 00142 case COOKIE_ACK: 00143 sctpEV3 << "SCTPAssociationRcvMessage: COOKIE_ACK received" << endl; 00144 if (fsm->getState() == SCTP_S_COOKIE_ECHOED) { 00145 SCTPCookieAckChunk* cookieAckChunk; 00146 cookieAckChunk = check_and_cast<SCTPCookieAckChunk*>(header); 00147 trans = processCookieAckArrived(); 00148 delete cookieAckChunk; 00149 } 00150 break; 00151 case DATA: 00152 sctpEV3 << "SCTPAssociationRcvMessage: DATA received" << endl; 00153 if (fsm->getState() == SCTP_S_COOKIE_ECHOED) { 00154 trans = performStateTransition(SCTP_E_RCV_COOKIE_ACK); 00155 } 00156 if (!(fsm->getState() == SCTP_S_SHUTDOWN_RECEIVED || fsm->getState() == SCTP_S_SHUTDOWN_ACK_SENT)) { 00157 SCTPDataChunk* dataChunk; 00158 dataChunk = check_and_cast<SCTPDataChunk*>(header); 00159 if ((dataChunk->getByteLength()- 16) > 0) { 00160 const SCTPEventCode event = processDataArrived(dataChunk); 00161 if (event == SCTP_E_DELIVERED) { 00162 dataChunkReceived = true; 00163 dataChunkDelivered = true; 00164 state->sackAllowed = true; 00165 } 00166 else if (event==SCTP_E_SEND || event==SCTP_E_IGNORE) { 00167 dataChunkReceived = true; 00168 state->sackAllowed = true; 00169 } 00170 else if (event==SCTP_E_DUP_RECEIVED) { 00171 dupReceived = true; 00172 } 00173 } 00174 else { 00175 sendAbort(); 00176 sctpMain->removeAssociation(this); 00177 } 00178 delete dataChunk; 00179 } 00180 trans = true; 00181 break; 00182 case SACK: 00183 { 00184 sctpEV3 << "SCTPAssociationRcvMessage: SACK received" << endl; 00185 const int32 scount = qCounter.roomSumSendStreams; 00186 SCTPSackChunk* sackChunk; 00187 sackChunk = check_and_cast<SCTPSackChunk*>(header); 00188 processSackArrived(sackChunk); 00189 trans = true; 00190 sendAllowed = true; 00191 delete sackChunk; 00192 if (getOutstandingBytes()==0 && transmissionQ->getQueueSize()==0 && scount==0) { 00193 if (fsm->getState() == SCTP_S_SHUTDOWN_PENDING) { 00194 sctpEV3 << "No more packets: send SHUTDOWN" << endl; 00195 sendShutdown(); 00196 trans = performStateTransition(SCTP_E_NO_MORE_OUTSTANDING); 00197 shutdownCalled = true; 00198 } 00199 else if (fsm->getState() == SCTP_S_SHUTDOWN_RECEIVED) { 00200 sctpEV3 << "No more outstanding" << endl; 00201 sendShutdownAck(remoteAddr); 00202 } 00203 } 00204 break; 00205 } 00206 case ABORT: 00207 SCTPAbortChunk* abortChunk; 00208 abortChunk = check_and_cast<SCTPAbortChunk*>(header); 00209 sctpEV3 << "SCTPAssociationRcvMessage: ABORT with T-Bit " 00210 << abortChunk->getT_Bit() << " received" << endl; 00211 if (sctpmsg->getTag() == localVTag || sctpmsg->getTag() == peerVTag) { 00212 sendIndicationToApp(SCTP_I_ABORT); 00213 trans = performStateTransition(SCTP_E_ABORT); 00214 } 00215 delete abortChunk; 00216 break; 00217 case HEARTBEAT: 00218 sctpEV3 << "SCTPAssociationRcvMessage: HEARTBEAT received" << endl; 00219 SCTPHeartbeatChunk* heartbeatChunk; 00220 heartbeatChunk = check_and_cast<SCTPHeartbeatChunk*>(header); 00221 if (!(fsm->getState() == SCTP_S_CLOSED)) { 00222 sendHeartbeatAck(heartbeatChunk, dest, src); 00223 } 00224 trans = true; 00225 delete heartbeatChunk; 00226 if (path) { 00227 path->numberOfHeartbeatsRcvd++; 00228 path->pathRcvdHb->record(path->numberOfHeartbeatsRcvd); 00229 } 00230 break; 00231 case HEARTBEAT_ACK: 00232 sctpEV3 << "SCTPAssociationRcvMessage: HEARTBEAT_ACK received" << endl; 00233 if (fsm->getState() == SCTP_S_COOKIE_ECHOED) { 00234 trans = performStateTransition(SCTP_E_RCV_COOKIE_ACK); 00235 } 00236 SCTPHeartbeatAckChunk* heartbeatAckChunk; 00237 heartbeatAckChunk = check_and_cast<SCTPHeartbeatAckChunk*>(header); 00238 if (path) { 00239 processHeartbeatAckArrived(heartbeatAckChunk, path); 00240 } 00241 trans = true; 00242 delete heartbeatAckChunk; 00243 break; 00244 case SHUTDOWN: 00245 sctpEV3 << "SCTPAssociationRcvMessage: SHUTDOWN received" << endl; 00246 SCTPShutdownChunk* shutdownChunk; 00247 shutdownChunk = check_and_cast<SCTPShutdownChunk*>(header); 00248 if (shutdownChunk->getCumTsnAck()>state->lastTsnAck) { 00249 simtime_t rttEstimation = MAXTIME; 00250 dequeueAckedChunks(shutdownChunk->getCumTsnAck(), 00251 getPath(remoteAddr), rttEstimation); 00252 state->lastTsnAck = shutdownChunk->getCumTsnAck(); 00253 } 00254 trans = performStateTransition(SCTP_E_RCV_SHUTDOWN); 00255 sendIndicationToApp(SCTP_I_SHUTDOWN_RECEIVED); 00256 trans = true; 00257 delete shutdownChunk; 00258 break; 00259 case SHUTDOWN_ACK: 00260 sctpEV3 << "SCTPAssociationRcvMessage: SHUTDOWN_ACK received" << endl; 00261 if (fsm->getState()!=SCTP_S_ESTABLISHED) { 00262 SCTPShutdownAckChunk* shutdownAckChunk; 00263 shutdownAckChunk = check_and_cast<SCTPShutdownAckChunk*>(header); 00264 sendShutdownComplete(); 00265 stopTimers(); 00266 stopTimer(T2_ShutdownTimer); 00267 stopTimer(T5_ShutdownGuardTimer); 00268 sctpEV3 << "state=" << stateName(fsm->getState()) << endl; 00269 if ((fsm->getState() == SCTP_S_SHUTDOWN_SENT) || 00270 (fsm->getState() == SCTP_S_SHUTDOWN_ACK_SENT)) { 00271 trans = performStateTransition(SCTP_E_RCV_SHUTDOWN_ACK); 00272 sendIndicationToApp(SCTP_I_CLOSED); 00273 delete state->shutdownChunk; 00274 } 00275 delete shutdownAckChunk; 00276 } 00277 break; 00278 case SHUTDOWN_COMPLETE: 00279 sctpEV3<<"Shutdown Complete arrived" << endl; 00280 SCTPShutdownCompleteChunk* shutdownCompleteChunk; 00281 shutdownCompleteChunk = check_and_cast<SCTPShutdownCompleteChunk*>(header); 00282 trans = performStateTransition(SCTP_E_RCV_SHUTDOWN_COMPLETE); 00283 sendIndicationToApp(SCTP_I_PEER_CLOSED); // necessary for NAT-Rendezvous 00284 if (trans == true) { 00285 stopTimers(); 00286 } 00287 stopTimer(T2_ShutdownTimer); 00288 stopTimer(T5_ShutdownGuardTimer); 00289 delete state->shutdownAckChunk; 00290 delete shutdownCompleteChunk; 00291 break; 00292 default: sctpEV3<<"different type" << endl; 00293 break; 00294 } 00295 00296 if (i==numberOfChunks-1 && (dataChunkReceived || dupReceived)) { 00297 sendAllowed=true; 00298 sctpEV3 << "i=" << i << " sendAllowed=true; scheduleSack" << endl; 00299 scheduleSack(); 00300 if (fsm->getState() == SCTP_S_SHUTDOWN_SENT && state->ackState>=sackFrequency) { 00301 sendSack(); 00302 } 00303 } 00304 00305 // Send any new DATA chunks, SACK chunks, HEARTBEAT chunks etc. 00306 sctpEV3 << "SCTPAssociationRcvMessage: send new data? state=" << stateName(fsm->getState()) 00307 << " sendAllowed=" << sendAllowed 00308 << " shutdownCalled=" << shutdownCalled << endl; 00309 if (((fsm->getState() == SCTP_S_ESTABLISHED) || 00310 (fsm->getState() == SCTP_S_SHUTDOWN_PENDING) || 00311 (fsm->getState() == SCTP_S_SHUTDOWN_RECEIVED)) && 00312 (sendAllowed) && 00313 (!shutdownCalled)) { 00314 sendOnAllPaths(state->getPrimaryPath()); 00315 } 00316 } 00317 00318 // ====== Clean-up ======================================================= 00319 disposeOf(state->sctpmsg); 00320 return trans; 00321 } 00322 00323 bool SCTPAssociation::processInitArrived(SCTPInitChunk* initchunk, int32 srcPort, int32 destPort) 00324 { 00325 SCTPAssociation* assoc; 00326 char timerName[128]; 00327 bool trans = false; 00328 InterfaceTableAccess interfaceTableAccess; 00329 AddressVector adv; 00330 sctpEV3<<"processInitArrived\n"; 00331 if (fsm->getState() == SCTP_S_CLOSED) 00332 { 00333 sctpEV3<<"fork="<<state->fork<<" initReceived="<<state->initReceived<<"\n"; 00334 if (state->fork && !state->initReceived) 00335 { 00336 sctpEV3<<"cloneAssociation\n"; 00337 assoc = cloneAssociation(); 00338 sctpEV3<<"addForkedAssociation\n"; 00339 sctpMain->addForkedAssociation(this, assoc, localAddr, remoteAddr, srcPort, destPort); 00340 00341 sctpEV3 << "Connection forked: this connection got new assocId=" << assocId << ", " 00342 "spinoff keeps LISTENing with assocId=" << assoc->assocId << "\n"; 00343 snprintf(timerName, sizeof(timerName), "T2_SHUTDOWN of assoc %d", assocId); 00344 T2_ShutdownTimer->setName(timerName); 00345 snprintf(timerName, sizeof(timerName), "T5_SHUTDOWN_GUARD of assoc %d", assocId); 00346 T5_ShutdownGuardTimer->setName(timerName); 00347 snprintf(timerName, sizeof(timerName), "SACK_TIMER of assoc %d", assocId); 00348 SackTimer->setName(timerName); 00349 snprintf(timerName, sizeof(timerName), "T1_INIT of assoc %d", assocId); 00350 T1_InitTimer->setName(timerName); 00351 } 00352 else 00353 { 00354 sctpMain->updateSockPair(this, localAddr, remoteAddr, srcPort, destPort); 00355 00356 } 00357 if (!state->initReceived) 00358 { 00359 state->initReceived = true; 00360 state->initialPrimaryPath = remoteAddr; 00361 state->setPrimaryPath(getPath(remoteAddr)); 00362 if (initchunk->getAddressesArraySize()==0) 00363 { 00364 sctpEV3<<__LINE__<<" get new path for "<<remoteAddr<<"\n"; 00365 SCTPPathVariables* rPath = new SCTPPathVariables(remoteAddr, this); 00366 sctpPathMap[rPath->remoteAddress] = rPath; 00367 qCounter.roomTransQ[rPath->remoteAddress] = 0; 00368 qCounter.bookedTransQ[rPath->remoteAddress] = 0; 00369 qCounter.roomRetransQ[rPath->remoteAddress] = 0; 00370 } 00371 initPeerTsn=initchunk->getInitTSN(); 00372 state->cTsnAck = initPeerTsn - 1; 00373 state->initialPeerRwnd = initchunk->getA_rwnd(); 00374 state->peerRwnd = state->initialPeerRwnd; 00375 localVTag= initchunk->getInitTag(); 00376 numberOfRemoteAddresses =initchunk->getAddressesArraySize(); 00377 IInterfaceTable *ift = interfaceTableAccess.get(); 00378 state->localAddresses.clear(); 00379 if (localAddressList.front() == IPvXAddress("0.0.0.0")) 00380 { 00381 for (int32 i=0; i<ift->getNumInterfaces(); ++i) 00382 { 00383 if (ift->getInterface(i)->ipv4Data()!=NULL) 00384 { 00385 adv.push_back(ift->getInterface(i)->ipv4Data()->getIPAddress()); 00386 } 00387 else if (ift->getInterface(i)->ipv6Data()!=NULL) 00388 { 00389 adv.push_back(ift->getInterface(i)->ipv6Data()->getAddress(0)); 00390 } 00391 } 00392 } 00393 else 00394 { 00395 adv = localAddressList; 00396 } 00397 uint32 rlevel = getLevel(remoteAddr); 00398 if (rlevel>0) 00399 for (AddressVector::iterator i=adv.begin(); i!=adv.end(); ++i) 00400 { 00401 if (getLevel((*i))>=rlevel) 00402 { 00403 sctpMain->addLocalAddress(this, (*i)); 00404 state->localAddresses.push_back((*i)); 00405 } 00406 } 00407 for (uint32 j=0; j<initchunk->getAddressesArraySize(); j++) 00408 { 00409 // skip IPv6 because we can't send to them yet 00410 if (initchunk->getAddresses(j).isIPv6()) 00411 continue; 00412 // set path variables for this pathlocalAddresses 00413 if (!getPath(initchunk->getAddresses(j))) 00414 { 00415 SCTPPathVariables* path = new SCTPPathVariables(initchunk->getAddresses(j), this); 00416 sctpEV3<<__LINE__<<" get new path for "<<initchunk->getAddresses(j)<<" ptr="<<path<<"\n"; 00417 for (AddressVector::iterator k=state->localAddresses.begin(); k!=state->localAddresses.end(); ++k) 00418 { 00419 sctpMain->addRemoteAddress(this,(*k), initchunk->getAddresses(j)); 00420 this->remoteAddressList.push_back(initchunk->getAddresses(j)); 00421 } 00422 sctpPathMap[path->remoteAddress] = path; 00423 qCounter.roomTransQ[path->remoteAddress] = 0; 00424 qCounter.bookedTransQ[path->remoteAddress] = 0; 00425 qCounter.roomRetransQ[path->remoteAddress] = 0; 00426 } 00427 } 00428 SCTPPathMap::iterator ite=sctpPathMap.find(remoteAddr); 00429 if (ite==sctpPathMap.end()) 00430 { 00431 SCTPPathVariables* path = new SCTPPathVariables(remoteAddr, this); 00432 sctpEV3<<__LINE__<<" get new path for "<<remoteAddr<<" ptr="<<path<<"\n"; 00433 sctpPathMap[remoteAddr] = path; 00434 qCounter.roomTransQ[remoteAddr] = 0; 00435 qCounter.bookedTransQ[remoteAddr] = 0; 00436 qCounter.roomRetransQ[remoteAddr] = 0; 00437 } 00438 trans = performStateTransition(SCTP_E_RCV_INIT); 00439 if (trans) { 00440 sendInitAck(initchunk); 00441 } 00442 } 00443 else if (fsm->getState() == SCTP_S_CLOSED) 00444 { 00445 trans=performStateTransition(SCTP_E_RCV_INIT); 00446 if (trans) { 00447 sendInitAck(initchunk); 00448 } 00449 } 00450 else { 00451 trans = true; 00452 } 00453 } 00454 else if (fsm->getState() == SCTP_S_COOKIE_WAIT) //INIT-Collision 00455 { 00456 sctpEV3<<"INIT collision: send Init-Ack\n"; 00457 sendInitAck(initchunk); 00458 trans=true; 00459 } 00460 else if (fsm->getState() == SCTP_S_COOKIE_ECHOED || fsm->getState() == SCTP_S_ESTABLISHED) 00461 { 00462 // check, whether a new address has been added 00463 bool addressPresent = false; 00464 for (uint32 j=0; j<initchunk->getAddressesArraySize(); j++) 00465 { 00466 if (initchunk->getAddresses(j).isIPv6()) 00467 continue; 00468 for (AddressVector::iterator k=remoteAddressList.begin(); k!=remoteAddressList.end(); ++k) 00469 { 00470 if ((*k)==(initchunk->getAddresses(j))) 00471 { 00472 addressPresent = true; 00473 break; 00474 } 00475 } 00476 if (!addressPresent) 00477 { 00478 sendAbort(); 00479 return true; 00480 } 00481 } 00482 sendInitAck(initchunk); 00483 trans=true; 00484 } 00485 else if (fsm->getState() == SCTP_S_SHUTDOWN_ACK_SENT) 00486 trans = true; 00487 printSctpPathMap(); 00488 return trans; 00489 } 00490 00491 00492 bool SCTPAssociation::processInitAckArrived(SCTPInitAckChunk* initAckChunk) 00493 { 00494 bool trans = false; 00495 if (fsm->getState() == SCTP_S_COOKIE_WAIT) 00496 { 00497 sctpEV3<<"State is COOKIE_WAIT, Cookie_Echo has to be sent\n"; 00498 stopTimer(T1_InitTimer); 00499 state->initRexmitTimeout = SCTP_TIMEOUT_INIT_REXMIT; 00500 trans=performStateTransition(SCTP_E_RCV_INIT_ACK); 00501 //delete state->initChunk; will be deleted when state ESTABLISHED is entered 00502 if (trans) 00503 { 00504 state->initialPrimaryPath = remoteAddr; 00505 state->setPrimaryPath(getPath(remoteAddr)); 00506 initPeerTsn=initAckChunk->getInitTSN(); 00507 localVTag= initAckChunk->getInitTag(); 00508 state->cTsnAck = initPeerTsn - 1; 00509 state->initialPeerRwnd = initAckChunk->getA_rwnd(); 00510 state->peerRwnd = state->initialPeerRwnd; 00511 remoteAddressList.clear(); 00512 numberOfRemoteAddresses =initAckChunk->getAddressesArraySize(); 00513 sctpEV3<<"number of remote addresses in initAck="<<numberOfRemoteAddresses<<"\n"; 00514 for (uint32 j=0; j<numberOfRemoteAddresses; j++) 00515 { 00516 if (initAckChunk->getAddresses(j).isIPv6()) 00517 continue; 00518 for (AddressVector::iterator k=state->localAddresses.begin(); k!=state->localAddresses.end(); ++k) 00519 { 00520 if (!((*k).isUnspecified())) 00521 { 00522 sctpEV3<<"addPath "<<initAckChunk->getAddresses(j)<<"\n"; 00523 sctpMain->addRemoteAddress(this,(*k), initAckChunk->getAddresses(j)); 00524 this->remoteAddressList.push_back(initAckChunk->getAddresses(j)); 00525 addPath(initAckChunk->getAddresses(j)); 00526 } 00527 } 00528 } 00529 SCTPPathMap::iterator ite=sctpPathMap.find(remoteAddr); 00530 if (ite==sctpPathMap.end()) 00531 { 00532 sctpEV3<<__LINE__<<" get new path for "<<remoteAddr<<"\n"; 00533 SCTPPathVariables* path = new SCTPPathVariables(remoteAddr, this); 00534 sctpPathMap[remoteAddr] = path; 00535 qCounter.roomTransQ[remoteAddr] = 0; 00536 qCounter.roomRetransQ[remoteAddr] = 0; 00537 qCounter.bookedTransQ[remoteAddr] = 0; 00538 } 00539 inboundStreams = ((initAckChunk->getNoOutStreams()<inboundStreams)?initAckChunk->getNoOutStreams():inboundStreams); 00540 outboundStreams = ((initAckChunk->getNoInStreams()<outboundStreams)?initAckChunk->getNoInStreams():outboundStreams); 00541 (this->*ssFunctions.ssInitStreams)(inboundStreams, outboundStreams); 00542 sendCookieEcho(initAckChunk); 00543 } 00544 startTimer(T1_InitTimer, state->initRexmitTimeout); 00545 } 00546 else 00547 sctpEV3<<"State="<<fsm->getState()<<"\n"; 00548 printSctpPathMap(); 00549 return trans; 00550 } 00551 00552 00553 00554 bool SCTPAssociation::processCookieEchoArrived(SCTPCookieEchoChunk* cookieEcho, IPvXAddress addr) 00555 { 00556 bool trans = false; 00557 SCTPCookie* cookie = check_and_cast<SCTPCookie*>(cookieEcho->getStateCookie()); 00558 if (cookie->getCreationTime()+(int32)sctpMain->par("validCookieLifetime")<simTime()) 00559 { 00560 sctpEV3<<"stale Cookie: sendAbort\n"; 00561 sendAbort(); 00562 delete cookie; 00563 return trans; 00564 } 00565 if (fsm->getState() == SCTP_S_CLOSED) 00566 { 00567 if (cookie->getLocalTag()!=localVTag || cookie->getPeerTag() != peerVTag) 00568 { 00569 bool same=true; 00570 for (int32 i=0; i<32; i++) 00571 { 00572 if (cookie->getLocalTieTag(i)!=state->localTieTag[i]) 00573 { 00574 same = false; 00575 break; 00576 } 00577 if (cookie->getPeerTieTag(i)!=state->peerTieTag[i]) 00578 { 00579 same = false; 00580 break; 00581 } 00582 } 00583 if (!same) 00584 { 00585 sendAbort(); 00586 delete cookie; 00587 return trans; 00588 } 00589 } 00590 sctpEV3<<"State is CLOSED, Cookie_Ack has to be sent\n"; 00591 trans=performStateTransition(SCTP_E_RCV_VALID_COOKIE_ECHO); 00592 if (trans) 00593 sendCookieAck(addr);//send to address 00594 } 00595 else if (fsm->getState() == SCTP_S_ESTABLISHED || fsm->getState() == SCTP_S_COOKIE_WAIT || fsm->getState() == SCTP_S_COOKIE_ECHOED) 00596 { 00597 sctpEV3<<"State is not CLOSED, but COOKIE_ECHO received. Compare the Tags\n"; 00598 // case A: Peer restarted, retrieve information from cookie 00599 if (cookie->getLocalTag()!=localVTag && cookie->getPeerTag() != peerVTag ) 00600 { 00601 bool same=true; 00602 for (int32 i=0; i<32; i++) 00603 { 00604 if (cookie->getLocalTieTag(i)!=state->localTieTag[i]) 00605 { 00606 same = false; 00607 break; 00608 } 00609 if (cookie->getPeerTieTag(i)!=state->peerTieTag[i]) 00610 { 00611 same = false; 00612 break; 00613 } 00614 } 00615 if (same) 00616 { 00617 localVTag = cookie->getLocalTag(); 00618 peerVTag = cookie->getPeerTag(); 00619 sendCookieAck(addr); 00620 } 00621 } 00622 // case B: Setup collision, retrieve information from cookie 00623 else if (cookie->getPeerTag()==peerVTag && (cookie->getLocalTag()!=localVTag || cookie->getLocalTag()==0)) 00624 { 00625 localVTag = cookie->getLocalTag(); 00626 peerVTag = cookie->getPeerTag(); 00627 performStateTransition(SCTP_E_RCV_VALID_COOKIE_ECHO); 00628 sendCookieAck(addr); 00629 } 00630 else if (cookie->getPeerTag()==peerVTag && cookie->getLocalTag()==localVTag) 00631 { 00632 sendCookieAck(addr); //send to address src 00633 } 00634 trans=true; 00635 } 00636 else 00637 { 00638 sctpEV3<<"State="<<fsm->getState()<<"\n"; 00639 trans = true; 00640 } 00641 delete cookie; 00642 return trans; 00643 } 00644 00645 bool SCTPAssociation::processCookieAckArrived() 00646 { 00647 bool trans=false; 00648 00649 if (fsm->getState() == SCTP_S_COOKIE_ECHOED) 00650 { 00651 stopTimer(T1_InitTimer); 00652 trans=performStateTransition(SCTP_E_RCV_COOKIE_ACK); 00653 if (state->cookieChunk->getCookieArraySize()==0) 00654 { 00655 delete state->cookieChunk->getStateCookie(); 00656 } 00657 delete state->cookieChunk; 00658 return trans; 00659 } 00660 else 00661 sctpEV3<<"State="<<fsm->getState()<<"\n"; 00662 00663 return trans; 00664 } 00665 00666 void SCTPAssociation::tsnWasReneged(SCTPDataVariables* chunk, 00667 const int type) 00668 { 00669 00670 #ifdef PKT 00671 if (transmissionQ->getQueueSize() > 0) { 00672 for (SCTPQueue::PayloadQueue::iterator tq = transmissionQ->payloadQueue.begin(); 00673 tq != transmissionQ->payloadQueue.end(); tq++) { 00674 if (tq->second->tsn > chunk->tsn) { 00675 if (!transmissionQ->checkAndInsertChunk(chunk->tsn, chunk)) { 00676 sctpEV3 << "tsnWasReneged: cannot add message/chunk (TSN=" 00677 << chunk->tsn <<") to the transmissionQ" << endl; 00678 } 00679 else { 00680 chunk->enqueuedInTransmissionQ = true; 00681 chunk->setNextDestination(chunk->getLastDestinationPath()); 00682 CounterMap::iterator q = qCounter.roomTransQ.find(chunk->getNextDestination()); 00683 q->second+=ADD_PADDING(chunk->len/8+SCTP_DATA_CHUNK_LENGTH); 00684 CounterMap::iterator qb=qCounter.bookedTransQ.find(chunk->getNextDestination()); 00685 qb->second+=chunk->booksize; 00686 return; 00687 } 00688 } 00689 } 00690 } 00691 #endif 00692 sctpEV3 << "TSN " << chunk->tsn << " has been reneged (type " 00693 << type << ")" << endl; 00694 unackChunk(chunk); 00695 if (chunk->countsAsOutstanding) { 00696 decreaseOutstandingBytes(chunk); 00697 } 00698 chunk->hasBeenReneged = true; 00699 chunk->gapReports = 1; 00700 if (!chunk->getLastDestinationPath()->T3_RtxTimer->isScheduled()) { 00701 startTimer(chunk->getLastDestinationPath()->T3_RtxTimer, 00702 chunk->getLastDestinationPath()->pathRto); 00703 } 00704 } 00705 00706 00707 00708 00709 SCTPEventCode SCTPAssociation::processSackArrived(SCTPSackChunk* sackChunk) 00710 { 00711 simtime_t rttEstimation = MAXTIME; 00712 bool ctsnaAdvanced = false; 00713 SCTPPathVariables* path = getPath(remoteAddr); // Path for *this* SACK! 00714 const uint64 arwnd = sackChunk->getA_rwnd(); 00715 const uint32 tsna = sackChunk->getCumTsnAck(); 00716 uint32 highestNewAck = tsna; // Highest newly acked TSN 00717 const uint16 numGaps = sackChunk->getNumGaps(); 00718 bool getChunkFastFirstTime = true; 00719 00720 // ====== Print some information ========================================= 00721 sctpEV3 << "##### SACK Processing: TSNa=" << tsna << " #####" << endl; 00722 for(SCTPPathMap::iterator piter = sctpPathMap.begin(); piter != sctpPathMap.end(); piter++) { 00723 SCTPPathVariables* myPath = piter->second; 00724 sctpEV3 << "Path " << myPath->remoteAddress << ":\t" 00725 << "outstanding=" << path->outstandingBytes << "\t" 00726 << "T3scheduled=" << path->T3_RtxTimer->getArrivalTime() << " " 00727 << (path->T3_RtxTimer->isScheduled() ? "[ok]" : "[NOT SCHEDULED]") << "\t" 00728 << endl; 00729 } 00730 00731 00732 00733 // ====== Initialize some variables ====================================== 00734 for(SCTPPathMap::iterator piter = sctpPathMap.begin(); piter != sctpPathMap.end(); piter++) { 00735 SCTPPathVariables* myPath = piter->second; 00736 // T.D. 26.03.09: Remember outstanding bytes before this update 00737 // Values are necessary for updating the congestion window! 00738 myPath->outstandingBytesBeforeUpdate = myPath->outstandingBytes; // T.D. 14.11.09: Bugfix - copy from myPath, not from path! 00739 myPath->requiresRtx = false; 00740 myPath->lowestTSNRetransmitted = false; 00741 myPath->findLowestTSN = true; 00742 myPath->gapAcksInLastSACK = 0; 00743 myPath->gapNAcksInLastSACK = 0; 00744 myPath->newlyAckedBytes = 0; 00745 if (myPath == path) { 00746 myPath->lastAckTime = simTime(); 00747 } 00748 } 00749 00750 00751 // ====== Zero Window Probing ============================================ 00752 if ( (state->zeroWindowProbing) && (arwnd > 0) ) { 00753 state->zeroWindowProbing = false; 00754 } 00755 00756 00757 // ####################################################################### 00758 // #### Processing of CumAck #### 00759 // ####################################################################### 00760 00761 if (tsnGt(tsna, state->lastTsnAck)) { // Handle new CumAck 00762 sctpEV3 << "===== Handling new CumAck for TSN " << tsna << " =====" << endl; 00763 00764 00765 // We have got new chunks acked, and our cum ack point is advanced ... 00766 // Depending on the parameter osbWithHeader ackedBytes are with or without the header bytes. 00767 // T.D. 23.03.09: path->newlyAckedBytes is updated in dequeueAckedChunks()! 00768 dequeueAckedChunks(tsna, path, rttEstimation); // chunks with tsn between lastTsnAck and tsna are removed from the transmissionQ and the retransmissionQ; outstandingBytes are decreased 00769 00770 state->lastTsnAck = tsna; 00771 ctsnaAdvanced = true; 00772 } 00773 else if (tsnLt(tsna, state->lastTsnAck)) { 00774 sctpEV3 << "Stale CumAck (" << tsna << " < " << state->lastTsnAck << ")" 00775 << endl; 00776 return SCTP_E_IGNORE; 00777 } 00778 00779 00780 // ====== Handle reneging ================================================ 00781 if ((numGaps == 0) && (tsnLt(tsna, state->highestTsnAcked))) { 00782 // Reneging, type 0: 00783 // In a previous SACK, chunks up to highestTsnAcked have been acked. 00784 // This SACK contains a CumAck < highestTsnAcked 00785 // => rereg TSNs from CumAck+1 to highestTsnAcked 00786 // => new highestTsnAcked = CumAck 00787 sctpEV3 << "numGaps=0 && tsna " << tsna 00788 << " < highestTsnAcked " << state->highestTsnAcked << endl; 00789 uint32 i = state->highestTsnAcked; 00790 while (i >= tsna + 1) { 00791 SCTPDataVariables* myChunk = retransmissionQ->getChunk(i); 00792 if(myChunk) { 00793 if(chunkHasBeenAcked(myChunk)) { 00794 tsnWasReneged(myChunk, 00795 0); 00796 } 00797 } 00798 i--; 00799 } 00800 state->highestTsnAcked = tsna; 00801 } 00802 00803 00804 // ####################################################################### 00805 // #### Processing of GapAcks #### 00806 // ####################################################################### 00807 00808 if ((numGaps > 0) && (!retransmissionQ->payloadQueue.empty()) ) { 00809 sctpEV3 << "===== Handling GapAcks after CumTSNAck " << tsna << " =====" << endl; 00810 sctpEV3 << "We got " << numGaps << " gap reports" << endl; 00811 00812 // We got fragment reports... check for newly acked chunks. 00813 const uint32 queuedChunks = retransmissionQ->payloadQueue.size(); 00814 sctpEV3 << "Number of chunks in retransmissionQ: " << queuedChunks 00815 <<" highestGapStop: " << sackChunk->getGapStop(numGaps-1) 00816 <<" highestTsnAcked: " << state->highestTsnAcked << endl; 00817 00818 // ====== Handle reneging ============================================= 00819 // highest gapStop smaller than highestTsnAcked: there might have been reneging 00820 if (tsnLt(sackChunk->getGapStop(numGaps-1), state->highestTsnAcked)) { 00821 // Reneging, type 2: 00822 // In a previous SACK, chunks up to highestTsnAcked have been acked. 00823 // This SACK contains a last gap ack < highestTsnAcked 00824 // => rereg TSNs from last gap ack to highestTsnAcked 00825 // => new highestTsnAcked = last gap ack 00826 uint32 i = state->highestTsnAcked; 00827 while (i >= sackChunk->getGapStop(numGaps - 1) + 1) { 00828 // ====== Looking up TSN in retransmission queue ================ 00829 SCTPDataVariables* myChunk = retransmissionQ->getChunk(i); 00830 if(myChunk) { 00831 if (chunkHasBeenAcked(myChunk)) { 00832 sctpEV3 << "TSN " << i << " was found. It has been un-acked." << endl; 00833 tsnWasReneged(myChunk, 00834 2); 00835 sctpEV3 << "highestTsnAcked now " << state->highestTsnAcked << endl; 00836 } 00837 } 00838 else { 00839 sctpEV3 << "TSN " << i << " not found in retransmissionQ" << endl; 00840 } 00841 i--; 00842 } 00843 state->highestTsnAcked = sackChunk->getGapStop(numGaps - 1); 00844 } 00845 00846 // ====== Looking for changes in the gap reports ====================== 00847 sctpEV3 << "Looking for changes in gap reports" << endl; 00848 for (int32 key = 0;key < numGaps; key++) { 00849 const uint32 lo = sackChunk->getGapStart(key); 00850 const uint32 hi = sackChunk->getGapStop(key); 00851 00852 00853 // ====== Iterate over TSNs in gap reports ========================= 00854 sctpEV3 << "Examine TSNs between " << lo << " and " << hi << endl; 00855 for (uint32 pos = lo; pos <= hi; pos++) { 00856 SCTPDataVariables* myChunk = retransmissionQ->getChunkFast(pos, getChunkFastFirstTime); 00857 if (myChunk) { 00858 if(chunkHasBeenAcked(myChunk) == false) { 00859 SCTPPathVariables* myChunkLastPath = myChunk->getLastDestinationPath(); 00860 assert(myChunkLastPath != NULL); 00861 // T.D. 02.02.2010: This chunk has been acked newly. 00862 // Let's process this new acknowledgement! 00863 handleChunkReportedAsAcked(highestNewAck, rttEstimation, myChunk, 00864 path /* i.e. the SACK path for RTT measurement! */); 00865 } 00866 } 00867 } 00868 } 00869 state->highestTsnAcked = sackChunk->getGapStop(numGaps-1); 00870 00871 // ====== Examine chunks between the gap reports ====================== 00872 // They might have to be retransmitted or they could have been removed 00873 uint32 lo = tsna; 00874 for (int32 key = 0; key < numGaps; key++) { 00875 const uint32 hi = sackChunk->getGapStart(key); 00876 for (uint32 pos = lo+1; pos <= hi - 1; pos++) { 00877 SCTPDataVariables* myChunk = retransmissionQ->getChunkFast(pos, getChunkFastFirstTime); 00878 if(myChunk) { 00879 handleChunkReportedAsMissing(sackChunk, highestNewAck, myChunk, 00880 path /* i.e. the SACK path for RTT measurement! */); 00881 } 00882 else { 00883 sctpEV3 << "TSN " << pos << " not found in retransmissionQ" << endl; 00884 } 00885 } 00886 lo = sackChunk->getGapStop(key); 00887 } 00888 00889 00890 // ====== Validity checks ============================================= 00891 } 00892 00893 00894 // ====== Update Fast Recovery status, according to SACK ================= 00895 updateFastRecoveryStatus(state->lastTsnAck); 00896 00897 // ====== Update RTT measurement for newly acked data chunks ============= 00898 sctpEV3 << simTime() << ": SACK: rtt=" << rttEstimation 00899 << ", path=" << path->remoteAddress << endl; 00900 pmRttMeasurement(path, rttEstimation); 00901 00902 00903 00904 // ####################################################################### 00905 // #### Receiver Window Management #### 00906 // ####################################################################### 00907 00908 const uint32 osb = getOutstandingBytes(); 00909 state->peerRwnd = arwnd - osb; 00910 00911 // position of statement changed 20.07.05 I.R. 00912 if ((int32)(state->peerRwnd) < 0) { 00913 state->peerRwnd = 0; 00914 } 00915 if (state->peerRwnd > state->initialPeerRwnd) { 00916 state->peerRwnd = state->initialPeerRwnd; 00917 } 00918 if (arwnd == 1 || state->peerRwnd < state->swsLimit || arwnd == 0) { 00919 sctpEV3 << "processSackArrived: arwnd=" << arwnd 00920 << " state->peerRwnd=" << state->peerRwnd 00921 << " set peerWindowFull" << endl; 00922 state->peerWindowFull = true; 00923 } 00924 else 00925 { 00926 state->peerWindowFull = false; 00927 state->zeroWindowProbing = false; 00928 } 00929 #ifdef PVIATE 00930 advancePeerTsn(); 00931 #endif 00932 00933 // ====== Need for zero-window probing? ================================== 00934 if(osb == 0) { 00935 if (arwnd == 0) 00936 state->zeroWindowProbing = true; 00937 } 00938 00939 00940 // ####################################################################### 00941 // #### Congestion Window Management #### 00942 // ####################################################################### 00943 00944 sctpEV3 << "Before ccUpdateBytesAcked: "; 00945 for(SCTPPathMap::iterator piter = sctpPathMap.begin(); piter != sctpPathMap.end(); piter++) { 00946 SCTPPathVariables* myPath = piter->second; 00947 const IPvXAddress& myPathId = myPath->remoteAddress; 00948 00949 00950 if(myPath->newlyAckedBytes > 0) { 00951 // T.D. 07.10.2009: Only call ccUpdateBytesAcked() when there are 00952 // acked bytes on this path! 00953 const bool advanceWindow = myPath->newCumAck; 00954 00955 sctpEV3 << simTime() << ":\tCC " << myPath->newlyAckedBytes 00956 << " newly acked on path " << myPathId << ";" 00957 << "\t->\tadvanceWindow=" << advanceWindow << endl; 00958 00959 (this->*ccFunctions.ccUpdateBytesAcked)(myPath, myPath->newlyAckedBytes, advanceWindow); 00960 } 00961 } 00962 00963 // ====== Update congestion windows on paths (handling decreases) ======== 00964 sctpEV3 << "Before ccUpdateAfterSack with tsna=" << tsna << endl; 00965 // ccUpdateAfterSack() will iterate over all paths. 00966 (this->*ccFunctions.ccUpdateAfterSack)(); 00967 00968 00969 // ####################################################################### 00970 // #### Path Management #### 00971 // ####################################################################### 00972 00973 // ====== Need to stop or restart T3 timer? ============================== 00974 for(SCTPPathMap::iterator piter = sctpPathMap.begin(); piter != sctpPathMap.end(); piter++) { 00975 SCTPPathVariables* myPath = piter->second; 00976 const IPvXAddress& myPathId = myPath->remoteAddress; 00977 00978 if (myPath->outstandingBytes == 0) { 00979 // T.D. 07.01.2010: Only stop T3 timer when there is nothing more to send on this path! 00980 if(qCounter.roomTransQ.find(myPath->remoteAddress)->second == 0) { 00981 // Stop T3 timer, if there are no more outstanding bytes. 00982 stopTimer(myPath->T3_RtxTimer); 00983 } 00984 } 00985 else if (myPath->newCumAck) { 00986 stopTimer(myPath->T3_RtxTimer); 00987 startTimer(myPath->T3_RtxTimer, myPath->pathRto); 00988 } 00989 else { 00990 /* Also restart T3 timer, when lowest TSN is rtx'ed */ 00991 if(myPath->lowestTSNRetransmitted == true) { 00992 sctpEV3 << "Lowest TSN retransmitted => restart of T3 timer for path " 00993 << myPathId << endl; 00994 stopTimer(myPath->T3_RtxTimer); 00995 startTimer(myPath->T3_RtxTimer, myPath->pathRto); 00996 } 00997 } 00998 00999 // ====== Clear error counter if TSNs on path have been acked ========= 01000 if (myPath->newlyAckedBytes > 0) { 01001 pmClearPathCounter(myPath); 01002 } 01003 } 01004 01005 01006 01007 return SCTP_E_IGNORE; 01008 } 01009 01010 01011 void SCTPAssociation::handleChunkReportedAsAcked(uint32& highestNewAck, 01012 simtime_t& rttEstimation, 01013 SCTPDataVariables* myChunk, 01014 SCTPPathVariables* sackPath) 01015 { 01016 SCTPPathVariables* myChunkLastPath = myChunk->getLastDestinationPath(); 01017 if ( (myChunk->numberOfTransmissions == 1) && 01018 (myChunk->hasBeenReneged == false) ) { 01019 if (myChunkLastPath == sackPath) { 01020 const simtime_t timeDifference = simTime() - myChunk->sendTime; 01021 if((timeDifference < rttEstimation) || (rttEstimation == -1.0)) { 01022 rttEstimation = timeDifference; 01023 } 01024 sctpEV3 << simTime() << " processSackArrived: computed rtt time diff == " 01025 << timeDifference << " for TSN "<< myChunk->tsn << endl; 01026 } 01027 } 01028 if ( (myChunk->hasBeenAbandoned == false) && 01029 (myChunk->hasBeenReneged == false) ) { 01030 myChunkLastPath->newlyAckedBytes += (myChunk->booksize); 01031 01032 sctpEV3 << simTime() << ": GapAcked TSN " << myChunk->tsn 01033 << " on path " << myChunkLastPath->remoteAddress << endl; 01034 01035 if (myChunk->tsn > highestNewAck) { 01036 highestNewAck = myChunk->tsn; 01037 } 01038 ackChunk(myChunk); 01039 if (myChunk->countsAsOutstanding) { 01040 decreaseOutstandingBytes(myChunk); 01041 } 01042 if (transmissionQ->getChunk(myChunk->tsn)) { // I.R. 02.01.07 01043 sctpEV3 << "Found TSN " << myChunk->tsn << " in transmissionQ -> remote it" << endl; 01044 transmissionQ->removeMsg(myChunk->tsn); 01045 myChunk->enqueuedInTransmissionQ = false; 01046 CounterMap::iterator q = qCounter.roomTransQ.find(myChunk->getNextDestination()); 01047 q->second -= ADD_PADDING(myChunk->len/8+SCTP_DATA_CHUNK_LENGTH); 01048 CounterMap::iterator qb = qCounter.bookedTransQ.find(myChunk->getNextDestination()); 01049 qb->second -= myChunk->booksize; 01050 } 01051 myChunk->gapReports = 0; 01052 } 01053 } 01054 01055 01056 void SCTPAssociation::handleChunkReportedAsMissing(const SCTPSackChunk* sackChunk, 01057 const uint32 highestNewAck, 01058 SCTPDataVariables* myChunk, 01059 const SCTPPathVariables* sackPath) 01060 { 01061 SCTPPathVariables* myChunkLastPath = myChunk->getLastDestinationPath(); 01062 sctpEV3 << "TSN " << myChunk->tsn << " is missing in gap reports (last " 01063 << myChunkLastPath->remoteAddress << ") "; 01064 if (!chunkHasBeenAcked(myChunk)) { 01065 sctpEV3 << "has not been acked, highestNewAck=" << highestNewAck 01066 << " countsAsOutstanding=" << myChunk->countsAsOutstanding << endl; 01067 const uint32 chunkReportedAsMissing = (highestNewAck > myChunk->tsn) ? 1 : 0; 01068 if (chunkReportedAsMissing > 0) { 01069 // T.D. 15.04.09: Increase gapReports by chunkReportedAsMissing. 01070 // Fixed bug here: gapReports += chunkReportedAsMissing instead of gapReports = chunkReportedAsMissing. 01071 /* 01072 printf("GapReports for TSN %u [ret=%d,fast=%s] at t=%s: %d --> %d by %d\n", 01073 myChunk->tsn, 01074 myChunk->numberOfRetransmissions, 01075 (myChunk->hasBeenFastRetransmitted == true) ? "YES" : "no", 01076 simTime().str().c_str(), 01077 myChunk->gapReports, 01078 myChunk->gapReports + chunkReportedAsMissing, 01079 chunkReportedAsMissing); 01080 */ 01081 myChunk->gapReports += chunkReportedAsMissing; 01082 01083 myChunkLastPath->gapNAcksInLastSACK++; 01084 01085 if (myChunk->gapReports >= state->numGapReports) { 01086 bool fastRtx = false; 01087 fastRtx = ((myChunk->hasBeenFastRetransmitted == false) && 01088 (myChunk->numberOfRetransmissions == 0)); 01089 if (fastRtx) { 01090 01091 // ====== Add chunk to transmission queue ======== 01092 SCTPQueue::PayloadQueue::iterator it = transmissionQ->payloadQueue.find(myChunk->tsn); 01093 if (transmissionQ->getChunk(myChunk->tsn) == NULL) { 01094 SCTP::AssocStat* assocStat = sctpMain->getAssocStat(assocId); 01095 if(assocStat) { 01096 assocStat->numFastRtx++; 01097 } 01098 myChunk->hasBeenFastRetransmitted = true; 01099 01100 sctpEV3 << simTime() << ": Fast RTX for TSN " 01101 << myChunk->tsn << " on path " << myChunk->getLastDestination() << endl; 01102 myChunkLastPath->numberOfFastRetransmissions++; 01103 01104 myChunk->setNextDestination(getNextDestination(myChunk)); 01105 SCTPPathVariables* myChunkNextPath = myChunk->getNextDestinationPath(); 01106 assert(myChunkNextPath != NULL); 01107 01108 if (myChunk->countsAsOutstanding) { 01109 decreaseOutstandingBytes(myChunk); 01110 } 01111 if (!transmissionQ->checkAndInsertChunk(myChunk->tsn, myChunk)) { 01112 sctpEV3 << "Fast RTX: cannot add message/chunk (TSN=" 01113 << myChunk->tsn << ") to the transmissionQ" << endl; 01114 } 01115 else { 01116 myChunk->enqueuedInTransmissionQ = true; 01117 CounterMap::iterator q = qCounter.roomTransQ.find(myChunk->getNextDestination()); 01118 q->second += ADD_PADDING(myChunk->len/8+SCTP_DATA_CHUNK_LENGTH); 01119 CounterMap::iterator qb = qCounter.bookedTransQ.find(myChunk->getNextDestination()); 01120 qb->second += myChunk->booksize; 01121 } 01122 myChunkNextPath->requiresRtx = true; 01123 if(myChunkNextPath->findLowestTSN == true) { 01124 // TD 08.12.09: fixed detection of lowest TSN retransmitted 01125 myChunkNextPath->lowestTSNRetransmitted = true; 01126 } 01127 } 01128 } 01129 } 01130 } 01131 else { 01132 myChunk->hasBeenFastRetransmitted = false; 01133 sctpEV3 << "TSN " << myChunk->tsn << " countsAsOutstanding=" 01134 << myChunk->countsAsOutstanding << endl; 01135 if (highestNewAck > myChunk->tsn) { 01136 myChunk->gapReports++; 01137 } 01138 myChunkLastPath->gapAcksInLastSACK++; 01139 } 01140 myChunkLastPath->findLowestTSN = false; 01141 } 01142 else { 01143 // Reneging, type 1: 01144 // A chunk in the gap blocks has been un-acked => reneg it. 01145 tsnWasReneged(myChunk, 01146 1); 01147 } 01148 } 01149 01150 01151 uint32 SCTPAssociation::dequeueAckedChunks(const uint32 tsna, 01152 SCTPPathVariables* sackPath, 01153 simtime_t& rttEstimation) 01154 { 01155 SCTP::AssocStat* assocStat = sctpMain->getAssocStat(assocId); 01156 uint32 newlyAckedBytes = 0; 01157 uint64 sendBufferBeforeUpdate = state->sendBuffer; 01158 01159 // Set it ridiculously high 01160 rttEstimation = MAXTIME; 01161 01162 // Are there chunks in the retransmission queue ? If Yes -> check for dequeue. 01163 SCTPQueue::PayloadQueue::iterator iterator = retransmissionQ->payloadQueue.begin(); 01164 while (iterator != retransmissionQ->payloadQueue.end()) { 01165 SCTPDataVariables* chunk = iterator->second; 01166 if (tsnGe(tsna, chunk->tsn)) { 01167 // Dequeue chunk, cause it has been acked 01168 if (transmissionQ->getChunk(chunk->tsn)) { 01169 transmissionQ->removeMsg(chunk->tsn); 01170 chunk->enqueuedInTransmissionQ = false; 01171 CounterMap::iterator q = qCounter.roomTransQ.find(chunk->getNextDestination()); 01172 q->second -= ADD_PADDING(chunk->len/8+SCTP_DATA_CHUNK_LENGTH); 01173 CounterMap::iterator qb = qCounter.bookedTransQ.find(chunk->getNextDestination()); 01174 qb->second -= chunk->booksize; 01175 } 01176 01177 chunk = retransmissionQ->getAndExtractChunk(chunk->tsn); 01178 state->sendBuffer -= chunk->len/8; 01179 01180 SCTPPathVariables* lastPath = chunk->getLastDestinationPath(); 01181 assert(lastPath != NULL); 01182 01183 if (!chunkHasBeenAcked(chunk)) { 01184 newlyAckedBytes += (chunk->booksize); 01185 01186 sctpEV3 << simTime() << ": CumAcked TSN " << chunk->tsn 01187 << " on path " << chunk->getLastDestination() << endl; 01188 01189 lastPath->newlyAckedBytes += (chunk->booksize); 01190 01191 // T.D. 05.12.09: CumAck affects lastPath -> reset its T3 timer later. 01192 lastPath->newCumAck = true; 01193 } 01194 01195 01196 if(assocStat) { 01197 assocStat->ackedBytes += chunk->len/8; 01198 } 01199 01200 if ((chunkHasBeenAcked(chunk) == false) && (chunk->countsAsOutstanding)) { 01201 ackChunk(chunk); 01202 if ((chunk->numberOfTransmissions == 1) && (chunk->getLastDestinationPath() == sackPath)) { 01203 const simtime_t timeDifference = simTime() - chunk->sendTime; 01204 if ((timeDifference < rttEstimation) || (rttEstimation == MAXTIME)) { 01205 rttEstimation = timeDifference; 01206 } 01207 } 01208 decreaseOutstandingBytes(chunk); 01209 } 01210 if (chunk->userData != NULL) { 01211 delete chunk->userData; 01212 } 01213 delete chunk; 01214 } 01215 else { 01216 break; 01217 } 01218 iterator = retransmissionQ->payloadQueue.begin(); 01219 } 01220 01221 if(sendBufferBeforeUpdate != state->sendBuffer && state->sendBuffer < state->sendQueueLimit) { 01222 // T.D. 06.02.2010: Just send SCTP_I_SENDQUEUE_ABATED once, after all newly acked 01223 // chunks have been dequeued. 01224 // I.R. only send indication if the sendBuffer size has dropped below the sendQueueLimit 01225 assert(state->lastSendQueueAbated < simTime()); 01226 state->appSendAllowed = true; 01227 sctpEV3 << simTime() << ":\tSCTP_I_SENDQUEUE_ABATED(" 01228 << sendBufferBeforeUpdate - state->sendBuffer << ") to refill buffer " 01229 << state->sendBuffer << "/" << state->sendQueueLimit << endl; 01230 sendIndicationToApp(SCTP_I_SENDQUEUE_ABATED, sendBufferBeforeUpdate - state->sendBuffer); 01231 state->lastSendQueueAbated = simTime(); 01232 } 01233 01234 sctpEV3 << "dequeueAckedChunks(): newlyAckedBytes=" << newlyAckedBytes 01235 << ", rttEstimation=" << rttEstimation << endl; 01236 01237 return (newlyAckedBytes); 01238 } 01239 01240 01241 01242 SCTPEventCode SCTPAssociation::processDataArrived(SCTPDataChunk* dataChunk) 01243 { 01244 bool checkCtsnaChange = false; 01245 const uint32 tsn = dataChunk->getTsn(); 01246 SCTPPathVariables* path = getPath(remoteAddr); 01247 01248 state->newChunkReceived = false; 01249 state->lastTsnReceived = tsn; 01250 state->lastDataSourceAddress = remoteAddr; 01251 01252 sctpEV3 << simTime() << " SCTPAssociation::processDataArrived TSN=" << tsn << endl; 01253 path->pathRcvdTSN->record(tsn); 01254 01255 SCTPSimpleMessage* smsg = check_and_cast <SCTPSimpleMessage*>(dataChunk->decapsulate()); 01256 dataChunk->setBitLength(SCTP_DATA_CHUNK_LENGTH*8); 01257 dataChunk->encapsulate(smsg); 01258 const uint32 payloadLength = dataChunk->getBitLength()/8-SCTP_DATA_CHUNK_LENGTH; 01259 sctpEV3 << "state->bytesRcvd=" << state->bytesRcvd << endl; 01260 state->bytesRcvd+=payloadLength; 01261 sctpEV3 << "state->bytesRcvd now=" << state->bytesRcvd << endl; 01262 SCTP::AssocStatMap::iterator iter=sctpMain->assocStatMap.find(assocId); 01263 iter->second.rcvdBytes+=dataChunk->getBitLength()/8-SCTP_DATA_CHUNK_LENGTH; 01264 01265 if (state->numGaps == 0) { 01266 state->highestTsnReceived = state->cTsnAck; 01267 } 01268 else { 01269 state->highestTsnReceived = state->gapStopList[state->numGaps-1]; 01270 } 01271 if (state->stopReceiving) { 01272 return SCTP_E_IGNORE; 01273 } 01274 01275 if (tsnLe(tsn, state->cTsnAck)) { 01276 sctpEV3 << "Duplicate TSN " << tsn << " (smaller than CumAck)" << endl; 01277 state->dupList.push_back(tsn); 01278 state->dupList.unique(); 01279 delete check_and_cast <SCTPSimpleMessage*>(dataChunk->decapsulate()); 01280 return SCTP_E_DUP_RECEIVED; 01281 } 01282 01283 01284 if ((int32)(state->localRwnd-state->queuedReceivedBytes) <= 0) { 01285 if (tsnGt(tsn, state->highestTsnReceived)) { 01286 return SCTP_E_IGNORE; 01287 } 01288 // changed 06.07.05 I.R. 01289 else if ((!tsnIsDuplicate(tsn)) && 01290 (tsn < state->highestTsnStored)) { 01291 if (!makeRoomForTsn(tsn, dataChunk->getBitLength()-SCTP_DATA_CHUNK_LENGTH*8, dataChunk->getUBit())) { 01292 delete check_and_cast <SCTPSimpleMessage*>(dataChunk->decapsulate()); 01293 return SCTP_E_IGNORE; 01294 } 01295 } 01296 } 01297 if (tsnGt(tsn, state->highestTsnReceived)) { 01298 sctpEV3 << "highestTsnReceived=" << state->highestTsnReceived 01299 << " tsn=" << tsn << endl; 01300 state->highestTsnReceived = state->highestTsnStored = tsn; 01301 if (tsn == initPeerTsn) { 01302 state->cTsnAck = tsn; 01303 } 01304 else { 01305 sctpEV3 << "Update fragment list" << endl; 01306 checkCtsnaChange = updateGapList(tsn); 01307 } 01308 state->newChunkReceived = true; 01309 } 01310 else if (tsnIsDuplicate(tsn)) { 01311 // TSN value is duplicate within a fragment 01312 sctpEV3 << "Duplicate TSN " << tsn << " (copy)" << endl; 01313 state->dupList.push_back(tsn); 01314 state->dupList.unique(); 01315 return SCTP_E_IGNORE; 01316 } 01317 else { 01318 checkCtsnaChange = updateGapList(tsn); 01319 } 01320 if (state->swsAvoidanceInvoked) { 01321 // swsAvoidanceInvoked => schedule a SACK to be sent at once in this case 01322 sctpEV3 << "swsAvoidanceInvoked" << endl; 01323 state->ackState = sackFrequency; 01324 } 01325 01326 if (checkCtsnaChange) { 01327 advanceCtsna(); 01328 } 01329 sctpEV3 << "cTsnAck=" << state->cTsnAck 01330 << " highestTsnReceived=" << state->highestTsnReceived << endl; 01331 01332 SCTPEventCode event = SCTP_E_SEND; 01333 if (state->newChunkReceived) { 01334 SCTPReceiveStreamMap::iterator iter = receiveStreams.find(dataChunk->getSid()); 01335 const int ret = iter->second->enqueueNewDataChunk(makeVarFromMsg(dataChunk)); 01336 if (ret > 0) { 01337 state->queuedReceivedBytes+=payloadLength; 01338 01339 event = SCTP_E_DELIVERED; 01340 if (ret < 3) { 01341 sendDataArrivedNotification(dataChunk->getSid()); 01342 putInDeliveryQ(dataChunk->getSid()); 01343 } 01344 } 01345 state->newChunkReceived = false; 01346 } 01347 01348 return(event); 01349 } 01350 01351 01352 SCTPEventCode SCTPAssociation::processHeartbeatAckArrived(SCTPHeartbeatAckChunk* hback, 01353 SCTPPathVariables* path) 01354 { 01355 path->numberOfHeartbeatAcksRcvd++; 01356 path->pathRcvdHbAck->record(path->numberOfHeartbeatAcksRcvd); 01357 /* hb-ack goes to pathmanagement, reset error counters, stop timeout timer */ 01358 const IPvXAddress addr = hback->getRemoteAddr(); 01359 const simtime_t hbTimeField = hback->getTimeField(); 01360 stopTimer(path->HeartbeatTimer); 01361 /* assume a valid RTT measurement on this path */ 01362 simtime_t rttEstimation = simTime() - hbTimeField; 01363 pmRttMeasurement(path, rttEstimation); 01364 pmClearPathCounter(path); 01365 path->confirmed = true; 01366 path->lastAckTime = simTime(); 01367 if (path->primaryPathCandidate == true) { 01368 state->setPrimaryPath(getPath(addr)); 01369 path->primaryPathCandidate = false; 01370 if (path->pmtu < state->assocPmtu) { 01371 state->assocPmtu = path->pmtu; 01372 } 01373 path->ssthresh = state->peerRwnd; 01374 recordCwndUpdate(path); 01375 path->heartbeatTimeout = (double)sctpMain->par("hbInterval") + path->pathRto; 01376 } 01377 01378 if (path->activePath == false) { 01379 sctpEV3 << "HB ACK arrived activePath=false. remoteAddress=" <<path->remoteAddress 01380 << " initialPP=" << state->initialPrimaryPath << endl; 01381 path->activePath = true; 01382 if (state->reactivatePrimaryPath && path->remoteAddress == state->initialPrimaryPath) { 01383 state->setPrimaryPath(path); 01384 } 01385 sctpEV3 << "primaryPath now " << state->getPrimaryPathIndex() << endl; 01386 } 01387 sctpEV3 << "Received HB ACK chunk...resetting error counters on path " << addr 01388 <<", rttEstimation=" << rttEstimation << endl; 01389 path->pathErrorCount = 0; 01390 return SCTP_E_IGNORE; 01391 } 01392 01393 01394 01395 void SCTPAssociation::process_TIMEOUT_INIT_REXMIT(SCTPEventCode& event) 01396 { 01397 if (++state->initRetransCounter > (int32)sctpMain->par("maxInitRetrans")) 01398 { 01399 sctpEV3 << "Retransmission count during connection setup exceeds " << (int32)sctpMain->par("maxInitRetrans") << ", giving up\n"; 01400 sendIndicationToApp(SCTP_I_CLOSED); 01401 sendAbort(); 01402 sctpMain->removeAssociation(this); 01403 return; 01404 } 01405 sctpEV3<< "Performing retransmission #" << state->initRetransCounter << "\n"; 01406 switch(fsm->getState()) 01407 { 01408 case SCTP_S_COOKIE_WAIT: retransmitInit(); break; 01409 case SCTP_S_COOKIE_ECHOED: retransmitCookieEcho(); break; 01410 default: opp_error("Internal error: INIT-REXMIT timer expired while in state %s", stateName(fsm->getState())); 01411 } 01412 state->initRexmitTimeout *= 2; 01413 if (state->initRexmitTimeout > SCTP_TIMEOUT_INIT_REXMIT_MAX) 01414 state->initRexmitTimeout = SCTP_TIMEOUT_INIT_REXMIT_MAX; 01415 startTimer(T1_InitTimer,state->initRexmitTimeout); 01416 } 01417 01418 void SCTPAssociation::process_TIMEOUT_SHUTDOWN(SCTPEventCode& event) 01419 { 01420 01421 if (++state->errorCount > (uint32)sctpMain->par("assocMaxRetrans")) 01422 { 01423 sendIndicationToApp(SCTP_I_CONN_LOST); 01424 sendAbort(); 01425 sctpMain->removeAssociation(this); 01426 return; 01427 } 01428 01429 sctpEV3 << "Performing shutdown retransmission. Assoc error count now "<<state->errorCount<<" \n"; 01430 if(fsm->getState() == SCTP_S_SHUTDOWN_SENT) 01431 { 01432 retransmitShutdown(); 01433 } 01434 else if (fsm->getState() == SCTP_S_SHUTDOWN_ACK_SENT) 01435 retransmitShutdownAck(); 01436 01437 state->initRexmitTimeout *= 2; 01438 if (state->initRexmitTimeout > SCTP_TIMEOUT_INIT_REXMIT_MAX) 01439 state->initRexmitTimeout = SCTP_TIMEOUT_INIT_REXMIT_MAX; 01440 startTimer(T2_ShutdownTimer,state->initRexmitTimeout); 01441 } 01442 01443 01444 void SCTPAssociation::process_TIMEOUT_HEARTBEAT_INTERVAL(SCTPPathVariables* path, bool force) 01445 { 01446 01447 sctpEV3<<"HB Interval timer expired -- sending new HB REQ on path "<<path->remoteAddress<<"\n"; 01448 /* restart hb_send_timer on this path */ 01449 stopTimer(path->HeartbeatIntervalTimer); 01450 stopTimer(path->HeartbeatTimer); 01451 path->heartbeatIntervalTimeout = (double)sctpMain->par("hbInterval") + path->pathRto; 01452 path->heartbeatTimeout = path->pathRto; 01453 startTimer(path->HeartbeatIntervalTimer, path->heartbeatIntervalTimeout); 01454 if ((simTime() - path->lastAckTime > path->heartbeatIntervalTimeout/2) || path->forceHb) 01455 { 01456 sendHeartbeat(path); 01457 startTimer(path->HeartbeatTimer, path->heartbeatTimeout); 01458 01459 path->forceHb = false; 01460 } 01461 } 01462 01463 01464 void SCTPAssociation::process_TIMEOUT_HEARTBEAT(SCTPPathVariables* path) 01465 { 01466 bool oldState; 01467 01468 /* check if error counters must be increased */ 01469 if (path->activePath) 01470 { 01471 state->errorCount++; 01472 path->pathErrorCount++; 01473 01474 sctpEV3<<"HB timeout timer expired for path "<<path->remoteAddress<<" --> Increase Error Counters (Assoc: "<<state->errorCount<<", Path: "<<path->pathErrorCount<<")\n"; 01475 } 01476 01477 /* RTO must be doubled for this path ! */ 01478 path->pathRto = (simtime_t)min(2 * path->pathRto.dbl(), sctpMain->par("rtoMax")); 01479 path->statisticsPathRTO->record(path->pathRto); 01480 /* check if any thresholds are exceeded, and if so, check if ULP must be notified */ 01481 if (state->errorCount > (uint32)sctpMain->par("assocMaxRetrans")) 01482 { 01483 sendIndicationToApp(SCTP_I_CONN_LOST); 01484 sendAbort(); 01485 sctpMain->removeAssociation(this); 01486 return; 01487 } 01488 else 01489 { 01490 /* set path state to INACTIVE, if the path error counter is exceeded */ 01491 if (path->pathErrorCount > (uint32)sctpMain->par("pathMaxRetrans")) 01492 { 01493 oldState = path->activePath; 01494 path->activePath = false; 01495 if (path == state->getPrimaryPath()) { 01496 state->setPrimaryPath(getNextPath(path)); 01497 } 01498 sctpEV3 << "pathErrorCount now "<< path->pathErrorCount 01499 << "; PP now " << state->getPrimaryPathIndex() << endl; 01500 } 01501 /* then: we can check, if all paths are INACTIVE ! */ 01502 if (allPathsInactive()) 01503 { 01504 sctpEV3<<"sctp_do_hb_to_timer() : ALL PATHS INACTIVE --> closing ASSOC\n"; 01505 sendIndicationToApp(SCTP_I_CONN_LOST); 01506 return; 01507 01508 } else if (path->activePath == false && oldState == true) 01509 { 01510 /* notify the application, in case the PATH STATE has changed from ACTIVE to INACTIVE */ 01511 pathStatusIndication(path, false); 01512 } 01513 01514 } 01515 } 01516 01517 void SCTPAssociation::stopTimers() 01518 { 01519 for (SCTPPathMap::iterator j = sctpPathMap.begin(); j != sctpPathMap.end(); j++) { 01520 stopTimer(j->second->HeartbeatTimer); 01521 stopTimer(j->second->HeartbeatIntervalTimer); 01522 } 01523 } 01524 01525 void SCTPAssociation::stopTimer(cMessage* timer) 01526 { 01527 01528 ev << "stopTimer " << timer->getName() << endl; 01529 if (timer->isScheduled()) { 01530 cancelEvent(timer); 01531 } 01532 } 01533 01534 void SCTPAssociation::startTimer(cMessage* timer, const simtime_t& timeout) 01535 { 01536 sctpEV3 << "startTimer " << timer->getName() << " with timeout " 01537 << timeout << " to expire at " << simTime() + timeout << endl; 01538 scheduleTimeout(timer, timeout); 01539 } 01540 01541 01542 01543 int32 SCTPAssociation::updateCounters(SCTPPathVariables* path) 01544 { 01545 bool notifyUlp = false; 01546 if (++state->errorCount >= (uint32)sctpMain->par("assocMaxRetrans")) 01547 { 01548 sctpEV3 << "Retransmission count during connection setup exceeds " << (int32)sctpMain->par("assocMaxRetrans") << ", giving up\n"; 01549 sendIndicationToApp(SCTP_I_CLOSED); 01550 sendAbort(); 01551 sctpMain->removeAssociation(this); 01552 return 0; 01553 } 01554 else if (++path->pathErrorCount >= (uint32)sctpMain->par("pathMaxRetrans")) 01555 { 01556 if (path->activePath) 01557 { 01558 /* tell the source */ 01559 notifyUlp = true; 01560 } 01561 01562 path->activePath = false; 01563 if (path == state->getPrimaryPath()) { 01564 state->setPrimaryPath(getNextPath(path)); 01565 } 01566 sctpEV3<<"process_TIMEOUT_RESET("<<(path->remoteAddress)<<") : PATH ERROR COUNTER EXCEEDED, path status is INACTIVE\n"; 01567 if (allPathsInactive()) 01568 { 01569 sctpEV3<<"process_TIMEOUT_RESET : ALL PATHS INACTIVE --> closing ASSOC\n"; 01570 sendIndicationToApp(SCTP_I_CONN_LOST); 01571 sendAbort(); 01572 sctpMain->removeAssociation(this); 01573 return 0; 01574 } 01575 else if (notifyUlp) 01576 { 01577 /* notify the application */ 01578 pathStatusIndication(path, false); 01579 } 01580 sctpEV3<<"process_TIMEOUT_RESET("<<(path->remoteAddress)<<") : PATH ERROR COUNTER now "<<path->pathErrorCount<<"\n"; 01581 return 2; 01582 } 01583 return 1; 01584 } 01585 01586 01587 01588 int32 SCTPAssociation::process_TIMEOUT_RTX(SCTPPathVariables* path) 01589 { 01590 sctpEV3 << "Processing retransmission timeout ..." << endl; 01591 01592 // ====== Increase the RTO (by doubling it) ============================== 01593 path->pathRto = min(2 * path->pathRto.dbl(), sctpMain->par("rtoMax")); 01594 path->statisticsPathRTO->record(path->pathRto); 01595 sctpEV3 << "Schedule T3 based retransmission for path "<< path->remoteAddress << endl; 01596 01597 // ====== Update congestion window ======================================= 01598 (this->*ccFunctions.ccUpdateAfterRtxTimeout)(path); 01599 01600 01601 // ====== Error Counter Handling ========================================= 01602 if (!state->zeroWindowProbing) { 01603 state->errorCount++; 01604 path->pathErrorCount++; 01605 sctpEV3 << "RTX-Timeout: errorCount increased to "<<path->pathErrorCount<<" state->errorCount="<<state->errorCount<<"\n"; 01606 } 01607 if (state->errorCount >= (uint32)sctpMain->par("assocMaxRetrans")) { 01608 /* error counter exceeded terminate the association -- create an SCTPC_EV_CLOSE event and send it to myself */ 01609 01610 sctpEV3 << "process_TIMEOUT_RTX : ASSOC ERROR COUNTER EXCEEDED, closing ASSOC" << endl; 01611 sendIndicationToApp(SCTP_I_CONN_LOST); 01612 sendAbort(); 01613 sctpMain->removeAssociation(this); 01614 return 0; 01615 01616 } 01617 else { 01618 if (path->pathErrorCount > (uint32)sctpMain->par("pathMaxRetrans")) { 01619 bool notifyUlp = false; 01620 01621 sctpEV3 << "pathErrorCount exceeded\n"; 01622 if (path->activePath) { 01623 /* tell the source */ 01624 notifyUlp = true; 01625 } 01626 path->activePath = false; 01627 if (path->remoteAddress == state->getPrimaryPathIndex()) { 01628 SCTPPathVariables* nextPath = getNextPath(path); 01629 if (nextPath != NULL) { 01630 state->setPrimaryPath(nextPath); 01631 } 01632 } 01633 sctpEV3 << "process_TIMEOUT_RTX(" << path->remoteAddress 01634 << ") : PATH ERROR COUNTER EXCEEDED, path status is INACTIVE" << endl; 01635 if (allPathsInactive()) { 01636 sctpEV3 << "process_TIMEOUT_RTX: ALL PATHS INACTIVE --> connection LOST!" << endl; 01637 sendIndicationToApp(SCTP_I_CONN_LOST); 01638 sendAbort(); 01639 sctpMain->removeAssociation(this); 01640 return 0; 01641 } 01642 else if (notifyUlp) { 01643 // Send notification to the application 01644 pathStatusIndication(path, false); 01645 } 01646 } 01647 sctpEV3 << "process_TIMEOUT_RTX(" << path->remoteAddress 01648 << ") : PATH ERROR COUNTER now " << path->pathErrorCount << endl; 01649 } 01650 01651 01652 // ====== Do Retransmission ============================================== 01653 // dequeue all chunks not acked so far and put them in the TransmissionQ 01654 if (!retransmissionQ->payloadQueue.empty()) { 01655 sctpEV3 << "Still " << retransmissionQ->payloadQueue.size() 01656 << " chunks in retransmissionQ" << endl; 01657 01658 for (SCTPQueue::PayloadQueue::iterator iterator = retransmissionQ->payloadQueue.begin(); 01659 iterator != retransmissionQ->payloadQueue.end(); iterator++) { 01660 SCTPDataVariables* chunk = iterator->second; 01661 assert(chunk != NULL); 01662 01663 // ====== Insert chunks into TransmissionQ ============================ 01664 // Only insert chunks that were sent to the path that has timed out 01665 if ( ((chunkHasBeenAcked(chunk) == false && chunk->countsAsOutstanding) || chunk->hasBeenReneged) && 01666 (chunk->getLastDestinationPath() == path) ) { 01667 sctpEV3 << simTime() << ": Timer-Based RTX for TSN " 01668 << chunk->tsn << " on path " << chunk->getLastDestination() << endl; 01669 chunk->getLastDestinationPath()->numberOfTimerBasedRetransmissions++; 01670 SCTP::AssocStatMap::iterator iter = sctpMain->assocStatMap.find(assocId); 01671 iter->second.numT3Rtx++; 01672 01673 moveChunkToOtherPath(chunk, getNextDestination(chunk)); 01674 } 01675 } 01676 } 01677 01678 01679 SCTPPathVariables* nextPath = getNextPath(path); 01680 sctpEV3 << "TimeoutRTX: sendOnAllPaths()" << endl; 01681 sendOnAllPaths(nextPath); 01682 01683 return 0; 01684 } 01685 01686 01687 void SCTPAssociation::moveChunkToOtherPath(SCTPDataVariables* chunk, 01688 SCTPPathVariables* newPath) 01689 { 01690 // ====== Prepare next destination ======================================= 01691 SCTPPathVariables* lastPath = chunk->getLastDestinationPath(); 01692 chunk->hasBeenFastRetransmitted = false; 01693 chunk->gapReports = 0; 01694 chunk->setNextDestination(newPath); 01695 sctpEV3 << simTime() << ": Timer-Based RTX for TSN " << chunk->tsn 01696 << ": lastDestination=" << chunk->getLastDestination() 01697 << " nextDestination=" << chunk->getNextDestination() << endl; 01698 01699 // ======= Remove chunk's booksize from outstanding bytes ================ 01700 // T.D. 12.02.2010: This case may happen when using sender queue control! 01701 if(chunk->countsAsOutstanding) { 01702 assert(lastPath->outstandingBytes >= chunk->booksize); 01703 lastPath->outstandingBytes -= chunk->booksize; 01704 assert((int32)lastPath->outstandingBytes >= 0); 01705 state->outstandingBytes -= chunk->booksize; 01706 assert((int64)state->outstandingBytes >= 0); 01707 chunk->countsAsOutstanding = false; 01708 // T.D. 12.02.2010: No Timer-Based RTX is necessary any more when there 01709 // are no outstanding bytes! 01710 if(lastPath->outstandingBytes == 0) { 01711 stopTimer(lastPath->T3_RtxTimer); 01712 } 01713 } 01714 01715 01716 // ====== Perform bookkeeping ============================================ 01717 // Check, if chunk_ptr->tsn is already in transmission queue. 01718 // This can happen in case multiple timeouts occur in succession. 01719 if (!transmissionQ->checkAndInsertChunk(chunk->tsn, chunk)) { 01720 sctpEV3 << "TSN " << chunk->tsn << " already in transmissionQ" << endl; 01721 return; 01722 } 01723 else { 01724 chunk->enqueuedInTransmissionQ = true; 01725 sctpEV3 << "Inserting TSN " << chunk->tsn << " into transmissionQ" << endl; 01726 CounterMap::iterator q = qCounter.roomTransQ.find(chunk->getNextDestination()); 01727 q->second += ADD_PADDING(chunk->len/8+SCTP_DATA_CHUNK_LENGTH); 01728 CounterMap::iterator qb = qCounter.bookedTransQ.find(chunk->getNextDestination()); 01729 qb->second += chunk->booksize; 01730 01731 if (chunk->countsAsOutstanding) { 01732 decreaseOutstandingBytes(chunk); 01733 } 01734 state->peerRwnd += (chunk->booksize); 01735 } 01736 if (state->peerRwnd > state->initialPeerRwnd) { 01737 state->peerRwnd = state->initialPeerRwnd; 01738 } 01739 sctpEV3 << "T3 Timeout: Chunk (TSN=" << chunk->tsn 01740 << ") has been requeued in transmissionQ, rwnd was set to " 01741 << state->peerRwnd << endl; 01742 }