old/flavours/TCPBaseAlg.cc

Go to the documentation of this file.
00001 //
00002 // Copyright (C) 2004 Andras Varga
00003 //
00004 // This program is free software; you can redistribute it and/or
00005 // modify it under the terms of the GNU Lesser General Public License
00006 // as published by the Free Software Foundation; either version 2
00007 // of the License, or (at your option) any later version.
00008 //
00009 // This program is distributed in the hope that it will be useful,
00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 // GNU Lesser General Public License for more details.
00013 //
00014 // You should have received a copy of the GNU Lesser General Public License
00015 // along with this program; if not, see <http://www.gnu.org/licenses/>.
00016 //
00017 
00018 #include "TCPBaseAlg_old.h"
00019 #include "TCP_old.h"
00020 
00021 using namespace tcp_old;
00022 
00023 //
00024 // Some constants below. MIN_REXMIT_TIMEOUT is the minimum allowed retransmit
00025 // interval.  It is currently one second but e.g. a FreeBSD kernel comment says
00026 // it "will ultimately be reduced to 3 ticks for algorithmic stability,
00027 // leaving the 200ms variance to deal with delayed-acks, protocol overheads.
00028 // A 1 second minimum badly breaks throughput on any network faster then
00029 // a modem that has minor but continuous packet loss unrelated to congestion,
00030 // such as on a wireless network."
00031 //
00032 #define DELAYED_ACK_TIMEOUT   0.2   // 200ms
00033 #define MAX_REXMIT_COUNT       12   // 12 retries
00034 #define MIN_REXMIT_TIMEOUT    1.0   // 1s
00035 //#define MIN_REXMIT_TIMEOUT    0.6   // 600ms (3 ticks)
00036 #define MAX_REXMIT_TIMEOUT    240   // 2*MSL (RFC1122)
00037 
00038 
00039 
00040 TCPBaseAlgStateVariables::TCPBaseAlgStateVariables()
00041 {
00042     // We disable delayed acks, since it appears that it isn't used in real-life TCPs.
00043     //
00044     // In SSFNet test suite http://www.ssfnet.org/Exchange/tcp/test/f5.html
00045     // the rule for delayed ACK is:
00046     //   An ACK must be sent immediatly when either of the following conditions exist:
00047     //    * Two full-sized packets received (to avoid too few ACKs).
00048     //    * Out of order packets received (to help trigger fast retransmission).
00049     //    * Received packet fills in all gap or part of gap of out of order data.
00050     // We do not implement this rule. In our measurements on network traffic, we
00051     // never encountered delayed ACKs.
00052     //
00053     delayed_acks_enabled = false;
00054 
00055     nagle_enabled = true; // FIXME this should be parameter eventually
00056 
00057     rexmit_count = 0;
00058     rexmit_timeout = 3.0;
00059 
00060     snd_cwnd = 0; // will be set to MSS when connection is established
00061 
00062     rtseq = 0;
00063     rtseq_sendtime = 0;
00064 
00065     // Jacobson's alg: srtt must be initialized to 0, rttvar to a value which
00066     // will yield rto = 3s initially.
00067     srtt = 0;
00068     rttvar = 3.0/4.0;
00069 }
00070 
00071 std::string TCPBaseAlgStateVariables::info() const
00072 {
00073     std::stringstream out;
00074     out << TCPStateVariables::info();
00075     out << " snd_cwnd=" << snd_cwnd;
00076     out << " rto=" << rexmit_timeout;
00077     return out.str();
00078 }
00079 
00080 std::string TCPBaseAlgStateVariables::detailedInfo() const
00081 {
00082     std::stringstream out;
00083     out << TCPStateVariables::detailedInfo();
00084     out << "snd_cwnd = " << snd_cwnd << "\n";
00085     out << "rto = " << rexmit_timeout << "\n";
00086     // TBD add others too
00087     return out.str();
00088 }
00089 
00090 TCPBaseAlg::TCPBaseAlg() : TCPAlgorithm(),
00091   state((TCPBaseAlgStateVariables *&)TCPAlgorithm::state)
00092 {
00093     rexmitTimer = persistTimer = delayedAckTimer = keepAliveTimer = NULL;
00094     cwndVector = ssthreshVector = rttVector = srttVector = rttvarVector = rtoVector = NULL;
00095 }
00096 
00097 TCPBaseAlg::~TCPBaseAlg()
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 }
00115 
00116 void TCPBaseAlg::initialize()
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 }
00140 
00141 void TCPBaseAlg::established(bool active)
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 }
00156 
00157 void TCPBaseAlg::connectionClosed()
00158 {
00159     cancelEvent(rexmitTimer);
00160     cancelEvent(persistTimer);
00161     cancelEvent(delayedAckTimer);
00162     cancelEvent(keepAliveTimer);
00163 }
00164 
00165 void TCPBaseAlg::processTimer(cMessage *timer, TCPEventCode& event)
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 cRuntimeError(timer, "unrecognized timer");
00177 }
00178 
00179 void TCPBaseAlg::processRexmitTimer(TCPEventCode& event)
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 }
00228 
00229 void TCPBaseAlg::processPersistTimer(TCPEventCode& event)
00230 {
00231     // FIXME TBD finish (currently Persist Timer never gets scheduled)
00232     conn->sendProbe();
00233 }
00234 
00235 void TCPBaseAlg::processDelayedAckTimer(TCPEventCode& event)
00236 {
00237     conn->sendAck();
00238 }
00239 
00240 void TCPBaseAlg::processKeepAliveTimer(TCPEventCode& event)
00241 {
00242     // FIXME TBD
00243 }
00244 
00245 void TCPBaseAlg::startRexmitTimer()
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 }
00254 
00255 void TCPBaseAlg::rttMeasurementComplete(simtime_t tSent, simtime_t tAcked)
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     simtime_t newRTT = tAcked-tSent;
00267 
00268     simtime_t& srtt = state->srtt;
00269     simtime_t& rttvar = state->rttvar;
00270 
00271     simtime_t 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     simtime_t 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 }
00293 
00294 bool TCPBaseAlg::sendData()
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 }
00316 
00317 void TCPBaseAlg::sendCommandInvoked()
00318 {
00319     // try sending
00320     sendData();
00321 }
00322 
00323 void TCPBaseAlg::receivedOutOfOrderSegment()
00324 {
00325     tcpEV << "Out-of-order segment, sending immediate ACK\n";
00326     conn->sendAck();
00327 }
00328 
00329 void TCPBaseAlg::receiveSeqChanged()
00330 {
00331     // If we send a data segment already (with the updated seqNo) there is no need to send an additional ACK
00332     if (state->last_ack_sent == state->rcv_nxt && !delayedAckTimer->isScheduled()) // ackSent?
00333     {
00334         // tcpEV << "ACK has already been sent (possibly piggybacked on data)\n";
00335     }
00336     else
00337     {
00338 
00339         if (!state->delayed_acks_enabled)
00340         {
00341             tcpEV << "rcv_nxt changed to " << state->rcv_nxt << ", sending ACK now (delayed ACKs are disabled)\n";
00342             conn->sendAck();
00343         }
00344         else
00345         {
00346             // FIXME ACK should be generated for at least every second SMSS-sized segment!
00347             // schedule delayed ACK timer if not already running
00348             tcpEV << "rcv_nxt changed to " << state->rcv_nxt << ", scheduling ACK\n";
00349             if (!delayedAckTimer->isScheduled())
00350                 conn->scheduleTimeout(delayedAckTimer, DELAYED_ACK_TIMEOUT);
00351         }
00352     }
00353 }
00354 
00355 void TCPBaseAlg::receivedDataAck(uint32 firstSeqAcked)
00356 {
00357     // if round-trip time measurement is running, check if rtseq has been acked
00358     if (state->rtseq_sendtime!=0 && seqLess(state->rtseq, state->snd_una))
00359     {
00360         // print value
00361         tcpEV << "Round-trip time measured on rtseq=" << state->rtseq << ": "
00362               << floor((simTime() - state->rtseq_sendtime)*1000+0.5) << "ms\n";
00363 
00364         // update RTT variables with new value
00365         rttMeasurementComplete(state->rtseq_sendtime, simTime());
00366 
00367         // measurement finished
00368         state->rtseq_sendtime = 0;
00369     }
00370 
00371     //
00372     // handling of retransmission timer: if the ACK is for the last segment sent
00373     // (no data in flight), cancel the timer, otherwise restart the timer
00374     // with the current RTO value.
00375     //
00376     if (state->snd_una==state->snd_max)
00377     {
00378         if (rexmitTimer->isScheduled())
00379         {
00380             tcpEV << "ACK acks all outstanding segments, cancel REXMIT timer\n";
00381             cancelEvent(rexmitTimer);
00382         }
00383         else
00384         {
00385             tcpEV << "There were no outstanding segments, nothing new in this ACK.\n";
00386         }
00387     }
00388     else
00389     {
00390         tcpEV << "ACK acks some but not all outstanding segments ("
00391               << (state->snd_max - state->snd_una) << " bytes outstanding), "
00392               << "restarting REXMIT timer\n";
00393         cancelEvent(rexmitTimer);
00394         startRexmitTimer();
00395     }
00396 
00397     //
00398     // Leave congestion window management and possible sending data to
00399     // subclasses (e.g. TCPTahoe, TCPReno).
00400     //
00401     // That is, subclasses will redefine this method, call us, then perform
00402     // window adjustments and send data (if there's room in the window).
00403     //
00404 }
00405 
00406 void TCPBaseAlg::receivedDuplicateAck()
00407 {
00408     tcpEV << "Duplicate ACK #" << state->dupacks << "\n";
00409 
00410     //
00411     // Leave to subclasses (e.g. TCPTahoe, TCPReno) whatever they want to do
00412     // on duplicate Acks.
00413     //
00414     // That is, subclasses will redefine this method, call us, then perform
00415     // whatever action they want to do on dupAcks (e.g. retransmitting one segment).
00416     //
00417 }
00418 
00419 void TCPBaseAlg::receivedAckForDataNotYetSent(uint32 seq)
00420 {
00421     tcpEV << "ACK acks something not yet sent, sending immediate ACK\n";
00422     conn->sendAck();
00423 }
00424 
00425 void TCPBaseAlg::ackSent()
00426 {
00427     state->last_ack_sent = state->rcv_nxt;
00428     // if delayed ACK timer is running, cancel it
00429     if (delayedAckTimer->isScheduled())
00430         cancelEvent(delayedAckTimer);
00431 }
00432 
00433 void TCPBaseAlg::dataSent(uint32 fromseq)
00434 {
00435     // if retransmission timer not running, schedule it
00436     if (!rexmitTimer->isScheduled())
00437     {
00438         tcpEV << "Starting REXMIT timer\n";
00439         startRexmitTimer();
00440     }
00441 
00442     // start round-trip time measurement (if not already running)
00443     if (state->rtseq_sendtime==0)
00444     {
00445         // remember this sequence number and when it was sent
00446         state->rtseq = fromseq;
00447         state->rtseq_sendtime = simTime();
00448         tcpEV << "Starting rtt measurement on seq=" << state->rtseq << "\n";
00449     }
00450 }
00451 
00452 void TCPBaseAlg::restartRexmitTimer()
00453 {
00454     if (rexmitTimer->isScheduled())
00455         cancelEvent(rexmitTimer);
00456     startRexmitTimer();
00457 }
00458 
00459