TCPBaseAlg Class Reference

#include <TCPBaseAlg.h>

Inheritance diagram for TCPBaseAlg:

TCPAlgorithm TCPNoCongestionControl TCPTahoeRenoFamily TCPReno TCPTahoe

List of all members.


Detailed Description

Includes basic TCP algorithms: adaptive retransmission, persist timer, keep-alive, delayed acks -- EXCLUDING congestion control. Congestion control is implemented in subclasses such as TCPTahoeAlg or TCPRenoAlg.

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


Constructor & Destructor Documentation

TCPBaseAlg::TCPBaseAlg (  ) 

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 }


Member Function Documentation

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]

Referenced by processTimer().

00235 {
00236     conn->sendAck();
00237 }

void TCPBaseAlg::processKeepAliveTimer ( TCPEventCode event  )  [protected, virtual]

Referenced by processTimer().

00240 {
00241     // FIXME TBD
00242 }

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]

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.

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 }


Member Data Documentation

cMessage* TCPBaseAlg::rexmitTimer [protected]

cMessage* TCPBaseAlg::persistTimer [protected]

cMessage* TCPBaseAlg::delayedAckTimer [protected]

cMessage* TCPBaseAlg::keepAliveTimer [protected]

cOutVector* TCPBaseAlg::cwndVector [protected]

cOutVector* TCPBaseAlg::ssthreshVector [protected]

cOutVector* TCPBaseAlg::rttVector [protected]

cOutVector* TCPBaseAlg::srttVector [protected]

cOutVector* TCPBaseAlg::rttvarVector [protected]

cOutVector* TCPBaseAlg::rtoVector [protected]


The documentation for this class was generated from the following files:

Generated on Fri Mar 20 18:51:22 2009 for INET Framework for OMNeT++/OMNEST by  doxygen 1.5.5