#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. |
1.4.1