#include <TCPBaseAlg.h>
Implements:
To be done:
Note: currently the timers and time calculations are done in double and NOT in Unix (200ms or 500ms) ticks. It's possible to write another TCPAlgorithm which uses ticks (or rather, factor out timer handling to separate methods, and redefine only those).
Congestion window is set to MSS when the connection is established, and not touched after that. Subclasses may redefine any of the virtual functions here to add their congestion control code.
Public Member Functions | |
TCPBaseAlg () | |
virtual | ~TCPBaseAlg () |
virtual void | initialize () |
virtual void | established (bool active) |
virtual void | connectionClosed () |
virtual void | processTimer (cMessage *timer, TCPEventCode &event) |
virtual void | sendCommandInvoked () |
virtual void | receivedOutOfOrderSegment () |
virtual void | receiveSeqChanged () |
virtual void | receivedDataAck (uint32 firstSeqAcked) |
virtual void | receivedDuplicateAck () |
virtual void | receivedAckForDataNotYetSent (uint32 seq) |
virtual void | ackSent () |
virtual void | dataSent (uint32 fromseq) |
Protected Member Functions | |
virtual void | startRexmitTimer () |
virtual void | rttMeasurementComplete (simtime_t tSent, simtime_t tAcked) |
virtual bool | sendData () |
cMessage * | cancelEvent (cMessage *msg) |
Process REXMIT, PERSIST, DELAYED-ACK and KEEP-ALIVE timers | |
virtual void | processRexmitTimer (TCPEventCode &event) |
virtual void | processPersistTimer (TCPEventCode &event) |
virtual void | processDelayedAckTimer (TCPEventCode &event) |
virtual void | processKeepAliveTimer (TCPEventCode &event) |
Protected Attributes | |
TCPBaseAlgStateVariables *& | state |
cMessage * | rexmitTimer |
cMessage * | persistTimer |
cMessage * | delayedAckTimer |
cMessage * | keepAliveTimer |
cOutVector * | cwndVector |
cOutVector * | ssthreshVector |
cOutVector * | rttVector |
cOutVector * | srttVector |
cOutVector * | rttvarVector |
cOutVector * | rtoVector |
TCPBaseAlg::TCPBaseAlg | ( | ) |
Ctor.
00089 : TCPAlgorithm(), 00090 state((TCPBaseAlgStateVariables *&)TCPAlgorithm::state) 00091 { 00092 rexmitTimer = persistTimer = delayedAckTimer = keepAliveTimer = NULL; 00093 cwndVector = ssthreshVector = rttVector = srttVector = rttvarVector = rtoVector = NULL; 00094 }
TCPBaseAlg::~TCPBaseAlg | ( | ) | [virtual] |
Virtual dtor.
00097 { 00098 // Note: don't delete "state" here, it'll be deleted from TCPConnection 00099 00100 // cancel and delete timers 00101 if (rexmitTimer) delete cancelEvent(rexmitTimer); 00102 if (persistTimer) delete cancelEvent(persistTimer); 00103 if (delayedAckTimer) delete cancelEvent(delayedAckTimer); 00104 if (keepAliveTimer) delete cancelEvent(keepAliveTimer); 00105 00106 // delete statistics objects 00107 delete cwndVector; 00108 delete ssthreshVector; 00109 delete rttVector; 00110 delete srttVector; 00111 delete rttvarVector; 00112 delete rtoVector; 00113 }
void TCPBaseAlg::processRexmitTimer | ( | TCPEventCode & | event | ) | [protected, virtual] |
Reimplemented in TCPNoCongestionControl, TCPReno, and TCPTahoe.
Referenced by TCPTahoe::processRexmitTimer(), TCPReno::processRexmitTimer(), TCPNoCongestionControl::processRexmitTimer(), and processTimer().
00179 { 00180 //" 00181 // For any state if the retransmission timeout expires on a segment in 00182 // the retransmission queue, send the segment at the front of the 00183 // retransmission queue again, reinitialize the retransmission timer, 00184 // and return. 00185 //" 00186 // Also: abort connection after max 12 retries. 00187 // 00188 // However, retransmission is actually more complicated than that 00189 // in RFC 793 above, we'll leave it to subclasses (e.g. TCPTahoe, TCPReno). 00190 // 00191 if (++state->rexmit_count > MAX_REXMIT_COUNT) 00192 { 00193 tcpEV << "Retransmission count exceeds " << MAX_REXMIT_COUNT << ", aborting connection\n"; 00194 conn->signalConnectionTimeout(); 00195 event = TCP_E_ABORT; // TBD maybe rather introduce a TCP_E_TIMEDOUT event 00196 return; 00197 } 00198 00199 tcpEV << "Performing retransmission #" << state->rexmit_count 00200 << "; increasing RTO from " << state->rexmit_timeout << "s "; 00201 00202 // 00203 // Karn's algorithm is implemented below: 00204 // (1) don't measure RTT for retransmitted packets. 00205 // (2) RTO should be doubled after retransmission ("exponential back-off") 00206 // 00207 00208 // restart the retransmission timer with twice the latest RTO value, or with the max, whichever is smaller 00209 state->rexmit_timeout += state->rexmit_timeout; 00210 if (state->rexmit_timeout > MAX_REXMIT_TIMEOUT) 00211 state->rexmit_timeout = MAX_REXMIT_TIMEOUT; 00212 conn->scheduleTimeout(rexmitTimer, state->rexmit_timeout); 00213 00214 tcpEV << " to " << state->rexmit_timeout << "s, and cancelling RTT measurement\n"; 00215 00216 // cancel round-trip time measurement 00217 state->rtseq_sendtime = 0; 00218 00219 // 00220 // Leave congestion window management and actual retransmission to 00221 // subclasses (e.g. TCPTahoe, TCPReno). 00222 // 00223 // That is, subclasses will redefine this method, call us, then perform 00224 // window adjustments and do the retransmission as they like. 00225 // 00226 }
void TCPBaseAlg::processPersistTimer | ( | TCPEventCode & | event | ) | [protected, virtual] |
Referenced by processTimer().
00229 { 00230 // FIXME TBD finish (currently Persist Timer never gets scheduled) 00231 conn->sendProbe(); 00232 }
void TCPBaseAlg::processDelayedAckTimer | ( | TCPEventCode & | event | ) | [protected, virtual] |
void TCPBaseAlg::processKeepAliveTimer | ( | TCPEventCode & | event | ) | [protected, virtual] |
void TCPBaseAlg::startRexmitTimer | ( | ) | [protected, virtual] |
Start REXMIT timer and initialize retransmission variables
Referenced by dataSent(), receivedDataAck(), TCPTahoe::receivedDuplicateAck(), and TCPReno::receivedDuplicateAck().
00245 { 00246 // start counting retransmissions for this seq number. 00247 // Note: state->rexmit_timeout is set from rttMeasurementComplete(). 00248 state->rexmit_count = 0; 00249 00250 // schedule timer 00251 conn->scheduleTimeout(rexmitTimer, state->rexmit_timeout); 00252 }
void TCPBaseAlg::rttMeasurementComplete | ( | simtime_t | tSent, | |
simtime_t | tAcked | |||
) | [protected, virtual] |
Update state vars with new measured RTT value. Passing two simtime_t's will allow rttMeasurementComplete() to do calculations in double or in 200ms/500ms ticks, as needed)
Referenced by receivedDataAck().
00255 { 00256 // 00257 // Jacobson's algorithm for estimating RTT and adaptively setting RTO. 00258 // 00259 // Note: this implementation calculates in doubles. An impl. which uses 00260 // 500ms ticks is available from old tcpmodule.cc:calcRetransTimer(). 00261 // 00262 00263 // update smoothed RTT estimate (srtt) and variance (rttvar) 00264 const double g = 0.125; // 1/8; (1-alpha) where alpha=7/8; 00265 simtime_t newRTT = tAcked-tSent; 00266 00267 simtime_t& srtt = state->srtt; 00268 simtime_t& rttvar = state->rttvar; 00269 00270 simtime_t err = newRTT - srtt; 00271 00272 srtt += g*err; 00273 rttvar += g*(fabs(err) - rttvar); 00274 00275 // assign RTO (here: rexmit_timeout) a new value 00276 simtime_t rto = srtt + 4*rttvar; 00277 if (rto>MAX_REXMIT_TIMEOUT) 00278 rto = MAX_REXMIT_TIMEOUT; 00279 else if (rto<MIN_REXMIT_TIMEOUT) 00280 rto = MIN_REXMIT_TIMEOUT; 00281 00282 state->rexmit_timeout = rto; 00283 00284 // record statistics 00285 tcpEV << "Measured RTT=" << (newRTT*1000) << "ms, updated SRTT=" << (srtt*1000) 00286 << "ms, new RTO=" << (rto*1000) << "ms\n"; 00287 if (rttVector) rttVector->record(newRTT); 00288 if (srttVector) srttVector->record(srtt); 00289 if (rttvarVector) rttvarVector->record(rttvar); 00290 if (rtoVector) rtoVector->record(rto); 00291 }
bool TCPBaseAlg::sendData | ( | ) | [protected, virtual] |
Send data, observing Nagle's algorithm and congestion window
Referenced by established(), TCPTahoe::receivedDataAck(), TCPReno::receivedDataAck(), TCPNoCongestionControl::receivedDataAck(), TCPReno::receivedDuplicateAck(), and sendCommandInvoked().
00294 { 00295 // 00296 // Nagle's algorithm: when a TCP connection has outstanding data that has not 00297 // yet been acknowledged, small segments cannot be sent until the outstanding 00298 // data is acknowledged. (In this case, small amounts of data are collected 00299 // by TCP and sent in a single segment.) 00300 // 00301 // FIXME there's also something like this: can still send if 00302 // "b) a segment that can be sent is at least half the size of 00303 // the largest window ever advertised by the receiver" 00304 00305 bool fullSegmentsOnly = state->nagle_enabled && state->snd_una!=state->snd_max; 00306 if (fullSegmentsOnly) 00307 tcpEV << "Nagle is enabled and there's unacked data: only full segments will be sent\n"; 00308 00309 // 00310 // Send window is effectively the minimum of the congestion window (cwnd) 00311 // and the advertised window (snd_wnd). 00312 // 00313 return conn->sendData(fullSegmentsOnly, state->snd_cwnd); 00314 }
cMessage* TCPBaseAlg::cancelEvent | ( | cMessage * | msg | ) | [inline, protected] |
Utility function
Referenced by ackSent(), connectionClosed(), receivedDataAck(), TCPTahoe::receivedDuplicateAck(), TCPReno::receivedDuplicateAck(), and ~TCPBaseAlg().
00132 {return conn->getTcpMain()->cancelEvent(msg);}
void TCPBaseAlg::initialize | ( | ) | [virtual] |
Create timers, etc.
Reimplemented from TCPAlgorithm.
Reimplemented in TCPNoCongestionControl.
Referenced by TCPNoCongestionControl::initialize().
00116 { 00117 TCPAlgorithm::initialize(); 00118 00119 rexmitTimer = new cMessage("REXMIT"); 00120 persistTimer = new cMessage("PERSIST"); 00121 delayedAckTimer = new cMessage("DELAYEDACK"); 00122 keepAliveTimer = new cMessage("KEEPALIVE"); 00123 00124 rexmitTimer->setContextPointer(conn); 00125 persistTimer->setContextPointer(conn); 00126 delayedAckTimer->setContextPointer(conn); 00127 keepAliveTimer->setContextPointer(conn); 00128 00129 if (conn->getTcpMain()->recordStatistics) 00130 { 00131 cwndVector = new cOutVector("cwnd"); 00132 ssthreshVector = new cOutVector("ssthresh"); 00133 rttVector = new cOutVector("measured RTT"); 00134 srttVector = new cOutVector("smoothed RTT"); 00135 rttvarVector = new cOutVector("RTTVAR"); 00136 rtoVector = new cOutVector("RTO"); 00137 } 00138 }
void TCPBaseAlg::established | ( | bool | active | ) | [virtual] |
Called when the connection is going to ESTABLISHED from SYN_SENT or SYN_RCVD. This is a place to initialize some variables (e.g. set cwnd to the MSS learned during connection setup). If we are on the active side, here we also have to finish the 3-way connection setup procedure by sending an ACK, possibly piggybacked on data.
Implements TCPAlgorithm.
00141 { 00142 // initialize cwnd (we may learn MSS during connection setup -- 00143 // this (MSS TCP option) is not implemented yet though) 00144 state->snd_cwnd = state->snd_mss; 00145 if (cwndVector) cwndVector->record(state->snd_cwnd); 00146 00147 if (active) 00148 { 00149 // finish connection setup with ACK (possibly piggybacked on data) 00150 tcpEV << "Completing connection setup by sending ACK (possibly piggybacked on data)\n"; 00151 if (!sendData()) 00152 conn->sendAck(); 00153 } 00154 }
void TCPBaseAlg::connectionClosed | ( | ) | [virtual] |
Called when the connection closes, it should cancel all running timers.
Implements TCPAlgorithm.
00157 { 00158 cancelEvent(rexmitTimer); 00159 cancelEvent(persistTimer); 00160 cancelEvent(delayedAckTimer); 00161 cancelEvent(keepAliveTimer); 00162 }
void TCPBaseAlg::processTimer | ( | cMessage * | timer, | |
TCPEventCode & | event | |||
) | [virtual] |
Process REXMIT, PERSIST, DELAYED-ACK and KEEP-ALIVE timers.
Implements TCPAlgorithm.
00165 { 00166 if (timer==rexmitTimer) 00167 processRexmitTimer(event); 00168 else if (timer==persistTimer) 00169 processPersistTimer(event); 00170 else if (timer==delayedAckTimer) 00171 processDelayedAckTimer(event); 00172 else if (timer==keepAliveTimer) 00173 processKeepAliveTimer(event); 00174 else 00175 throw cRuntimeError(timer, "unrecognized timer"); 00176 }
void TCPBaseAlg::sendCommandInvoked | ( | ) | [virtual] |
Called after user sent TCP_C_SEND command to us.
Implements TCPAlgorithm.
00317 { 00318 // try sending 00319 sendData(); 00320 }
void TCPBaseAlg::receivedOutOfOrderSegment | ( | ) | [virtual] |
Called after receiving data which are in the window, but not at its left edge (seq!=rcv_nxt). This indicates that either segments got re-ordered in the way, or one segment was lost. RFC1122 and RFC2001 recommend sending an immediate ACK here (Fast Retransmit relies on that).
Implements TCPAlgorithm.
00323 { 00324 tcpEV << "Out-of-order segment, sending immediate ACK\n"; 00325 conn->sendAck(); 00326 }
void TCPBaseAlg::receiveSeqChanged | ( | ) | [virtual] |
Called after rcv_nxt got advanced, either because we received in-sequence data ("text" in RFC 793 lingo) or a FIN. At this point, rcv_nxt has already been updated. This method should take care to send or schedule an ACK some time.
Implements TCPAlgorithm.
00329 { 00330 if (!state->delayed_acks_enabled) 00331 { 00332 tcpEV << "rcv_nxt changed to " << state->rcv_nxt << ", sending ACK now (delayed ACKs are disabled)\n"; 00333 conn->sendAck(); 00334 } 00335 else 00336 { 00337 // FIXME ACK should be generated for at least every second SMSS-sized segment! 00338 // schedule delayed ACK timer if not already running 00339 tcpEV << "rcv_nxt changed to " << state->rcv_nxt << ", scheduling ACK\n"; 00340 if (!delayedAckTimer->isScheduled()) 00341 conn->scheduleTimeout(delayedAckTimer, DELAYED_ACK_TIMEOUT); 00342 } 00343 }
void TCPBaseAlg::receivedDataAck | ( | uint32 | firstSeqAcked | ) | [virtual] |
Called after we received an ACK which acked some data (that is, we could advance snd_una). At this point the state variables (snd_una, snd_wnd) have already been updated. The argument firstSeqAcked is the previous snd_una value, that is, the number of bytes acked is (snd_una-firstSeqAcked). The dupack counter still reflects the old value (needed for Reno and NewReno); it'll be reset to 0 after this call returns.
Implements TCPAlgorithm.
Reimplemented in TCPNoCongestionControl, TCPReno, and TCPTahoe.
Referenced by TCPTahoe::receivedDataAck(), TCPReno::receivedDataAck(), and TCPNoCongestionControl::receivedDataAck().
00346 { 00347 // if round-trip time measurement is running, check if rtseq has been acked 00348 if (state->rtseq_sendtime!=0 && seqLess(state->rtseq, state->snd_una)) 00349 { 00350 // print value 00351 tcpEV << "Round-trip time measured on rtseq=" << state->rtseq << ": " 00352 << floor((simTime() - state->rtseq_sendtime)*1000+0.5) << "ms\n"; 00353 00354 // update RTT variables with new value 00355 rttMeasurementComplete(state->rtseq_sendtime, simTime()); 00356 00357 // measurement finished 00358 state->rtseq_sendtime = 0; 00359 } 00360 00361 // 00362 // handling of retransmission timer: if the ACK is for the last segment sent 00363 // (no data in flight), cancel the timer, otherwise restart the timer 00364 // with the current RTO value. 00365 // 00366 if (state->snd_una==state->snd_max) 00367 { 00368 if (rexmitTimer->isScheduled()) 00369 { 00370 tcpEV << "ACK acks all outstanding segments, cancel REXMIT timer\n"; 00371 cancelEvent(rexmitTimer); 00372 } 00373 else 00374 { 00375 tcpEV << "There were no outstanding segments, nothing new in this ACK.\n"; 00376 } 00377 } 00378 else 00379 { 00380 tcpEV << "ACK acks some but not all outstanding segments (" 00381 << (state->snd_max - state->snd_una) << " bytes outstanding), " 00382 << "restarting REXMIT timer\n"; 00383 cancelEvent(rexmitTimer); 00384 startRexmitTimer(); 00385 } 00386 00387 // 00388 // Leave congestion window management and possible sending data to 00389 // subclasses (e.g. TCPTahoe, TCPReno). 00390 // 00391 // That is, subclasses will redefine this method, call us, then perform 00392 // window adjustments and send data (if there's room in the window). 00393 // 00394 }
void TCPBaseAlg::receivedDuplicateAck | ( | ) | [virtual] |
Called after we received a duplicate ACK (that is: ackNo==snd_una, no data in segment, segment doesn't carry window update, and also, we have unacked data). The dupack counter got already updated when calling this method (i.e. dupack==1 on first duplicate ACK.)
Implements TCPAlgorithm.
Reimplemented in TCPReno, and TCPTahoe.
Referenced by TCPTahoe::receivedDuplicateAck(), and TCPReno::receivedDuplicateAck().
00397 { 00398 tcpEV << "Duplicate ACK #" << state->dupacks << "\n"; 00399 00400 // 00401 // Leave to subclasses (e.g. TCPTahoe, TCPReno) whatever they want to do 00402 // on duplicate Acks. 00403 // 00404 // That is, subclasses will redefine this method, call us, then perform 00405 // whatever action they want to do on dupAcks (e.g. retransmitting one segment). 00406 // 00407 }
void TCPBaseAlg::receivedAckForDataNotYetSent | ( | uint32 | seq | ) | [virtual] |
Called after we received an ACK for data not yet sent. According to RFC 793 this function should send an ACK.
Implements TCPAlgorithm.
00410 { 00411 tcpEV << "ACK acks something not yet sent, sending immediate ACK\n"; 00412 conn->sendAck(); 00413 }
void TCPBaseAlg::ackSent | ( | ) | [virtual] |
Called after we sent an ACK. This hook can be used to cancel the delayed-ACK timer.
Implements TCPAlgorithm.
00416 { 00417 // if delayed ACK timer is running, cancel it 00418 if (delayedAckTimer->isScheduled()) 00419 cancelEvent(delayedAckTimer); 00420 }
void TCPBaseAlg::dataSent | ( | uint32 | fromseq | ) | [virtual] |
Called after we sent data. This hook can be used to schedule the retransmission timer, to start round-trip time measurement, etc. The argument is the seqno of the first byte sent.
Implements TCPAlgorithm.
00423 { 00424 // if retransmission timer not running, schedule it 00425 if (!rexmitTimer->isScheduled()) 00426 { 00427 tcpEV << "Starting REXMIT timer\n"; 00428 startRexmitTimer(); 00429 } 00430 00431 // start round-trip time measurement (if not already running) 00432 if (state->rtseq_sendtime==0) 00433 { 00434 // remember this sequence number and when it was sent 00435 state->rtseq = fromseq; 00436 state->rtseq_sendtime = simTime(); 00437 tcpEV << "Starting rtt measurement on seq=" << state->rtseq << "\n"; 00438 } 00439 }
TCPBaseAlgStateVariables*& TCPBaseAlg::state [protected] |
Reimplemented from TCPAlgorithm.
Reimplemented in TCPNoCongestionControl, TCPReno, TCPTahoe, and TCPTahoeRenoFamily.
Referenced by dataSent(), established(), processRexmitTimer(), receivedDataAck(), receivedDuplicateAck(), receiveSeqChanged(), rttMeasurementComplete(), sendData(), and startRexmitTimer().
cMessage* TCPBaseAlg::rexmitTimer [protected] |
cMessage* TCPBaseAlg::persistTimer [protected] |
Referenced by connectionClosed(), initialize(), processTimer(), TCPBaseAlg(), and ~TCPBaseAlg().
cMessage* TCPBaseAlg::delayedAckTimer [protected] |
Referenced by ackSent(), connectionClosed(), initialize(), processTimer(), receiveSeqChanged(), TCPBaseAlg(), and ~TCPBaseAlg().
cMessage* TCPBaseAlg::keepAliveTimer [protected] |
Referenced by connectionClosed(), initialize(), processTimer(), TCPBaseAlg(), and ~TCPBaseAlg().
cOutVector* TCPBaseAlg::cwndVector [protected] |
cOutVector* TCPBaseAlg::ssthreshVector [protected] |
cOutVector* TCPBaseAlg::rttVector [protected] |
Referenced by initialize(), rttMeasurementComplete(), TCPBaseAlg(), and ~TCPBaseAlg().
cOutVector* TCPBaseAlg::srttVector [protected] |
Referenced by initialize(), rttMeasurementComplete(), TCPBaseAlg(), and ~TCPBaseAlg().
cOutVector* TCPBaseAlg::rttvarVector [protected] |
Referenced by initialize(), rttMeasurementComplete(), TCPBaseAlg(), and ~TCPBaseAlg().
cOutVector* TCPBaseAlg::rtoVector [protected] |
Referenced by initialize(), rttMeasurementComplete(), TCPBaseAlg(), and ~TCPBaseAlg().