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