TCPNewReno.cc

Go to the documentation of this file.
00001 //
00002 // Copyright (C) 2009 Thomas Reschka
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 <algorithm>   // min,max
00019 #include "TCPNewReno.h"
00020 #include "TCP.h"
00021 
00022 
00023 Register_Class(TCPNewReno);
00024 
00025 
00026 TCPNewReno::TCPNewReno() : TCPTahoeRenoFamily(),
00027   state((TCPNewRenoStateVariables *&)TCPAlgorithm::state)
00028 {
00029 }
00030 
00031 void TCPNewReno::recalculateSlowStartThreshold()
00032 {
00033     // RFC 2581, page 4:
00034     // "When a TCP sender detects segment loss using the retransmission
00035     // timer, the value of ssthresh MUST be set to no more than the value
00036     // given in equation 3:
00037     //
00038     //   ssthresh = max (FlightSize / 2, 2*SMSS)            (3)
00039     //
00040     // As discussed above, FlightSize is the amount of outstanding data in
00041     // the network."
00042 
00043     // set ssthresh to flight size/2, but at least 2 SMSS
00044     // (the formula below practically amounts to ssthresh=cwnd/2 most of the time)
00045     uint32 flight_size = std::min(state->snd_cwnd, state->snd_wnd); // FIXME TODO - Does this formula computes the amount of outstanding data?
00046     // uint32 flight_size = state->snd_max - state->snd_una;
00047     state->ssthresh = std::max(flight_size/2, 2*state->snd_mss);
00048     if (ssthreshVector) ssthreshVector->record(state->ssthresh);
00049 }
00050 
00051 void TCPNewReno::processRexmitTimer(TCPEventCode& event)
00052 {
00053     TCPTahoeRenoFamily::processRexmitTimer(event);
00054     if (event==TCP_E_ABORT)
00055         return;
00056 
00057     // RFC 3782, page 6:
00058     // "6)  Retransmit timeouts:
00059     // After a retransmit timeout, record the highest sequence number
00060     // transmitted in the variable "recover" and exit the Fast Recovery
00061     // procedure if applicable."
00062     state->recover = (state->snd_max - 1);
00063     tcpEV << "recover=" << state->recover << "\n";
00064     state->lossRecovery = false;
00065     state->firstPartialACK = false;
00066     tcpEV << "Loss Recovery terminated.\n";
00067 
00068     // After REXMIT timeout TCP NewReno should start slow start with snd_cwnd = snd_mss.
00069     //
00070     // If calling "retransmitData();" there is no rexmit limitation (bytesToSend > snd_cwnd)
00071     // therefore "sendData();" has been modified and is called to rexmit outstanding data.
00072     //
00073     // RFC 2581, page 5:
00074     // "Furthermore, upon a timeout cwnd MUST be set to no more than the loss
00075     // window, LW, which equals 1 full-sized segment (regardless of the
00076     // value of IW).  Therefore, after retransmitting the dropped segment
00077     // the TCP sender uses the slow start algorithm to increase the window
00078     // from 1 full-sized segment to the new value of ssthresh, at which
00079     // point congestion avoidance again takes over."
00080 
00081     // begin Slow Start (RFC 2581)
00082     recalculateSlowStartThreshold();
00083     state->snd_cwnd = state->snd_mss;
00084     if (cwndVector) cwndVector->record(state->snd_cwnd);
00085     tcpEV << "Begin Slow Start: resetting cwnd to " << state->snd_cwnd
00086           << ", ssthresh=" << state->ssthresh << "\n";
00087 
00088     state->afterRto = true;
00089 
00090     conn->retransmitOneSegment(true);
00091 }
00092 
00093 void TCPNewReno::receivedDataAck(uint32 firstSeqAcked)
00094 {
00095     TCPTahoeRenoFamily::receivedDataAck(firstSeqAcked);
00096 
00097     // RFC 3782, page 5:
00098     // "5) When an ACK arrives that acknowledges new data, this ACK could be
00099     // the acknowledgment elicited by the retransmission from step 2, or
00100     // elicited by a later retransmission.
00101     //
00102     // Full acknowledgements:
00103     // If this ACK acknowledges all of the data up to and including
00104     // "recover", then the ACK acknowledges all the intermediate
00105     // segments sent between the original transmission of the lost
00106     // segment and the receipt of the third duplicate ACK.  Set cwnd to
00107     // either (1) min (ssthresh, FlightSize + SMSS) or (2) ssthresh,
00108     // where ssthresh is the value set in step 1; this is termed
00109     // "deflating" the window.  (We note that "FlightSize" in step 1
00110     // referred to the amount of data outstanding in step 1, when Fast
00111     // Recovery was entered, while "FlightSize" in step 5 refers to the
00112     // amount of data outstanding in step 5, when Fast Recovery is
00113     // exited.)  If the second option is selected, the implementation is
00114     // encouraged to take measures to avoid a possible burst of data, in
00115     // case the amount of data outstanding in the network is much less
00116     // than the new congestion window allows.  A simple mechanism is to
00117     // limit the number of data packets that can be sent in response to
00118     // a single acknowledgement; this is known as "maxburst_" in the NS
00119     // simulator.  Exit the Fast Recovery procedure."
00120     if (state->lossRecovery)
00121     {
00122         if (seqGE(state->snd_una-1, state->recover))
00123         {
00124             // Exit Fast Recovery: deflating cwnd
00125             //
00126             // option (1): set cwnd to min (ssthresh, FlightSize + SMSS)
00127             uint32 flight_size = state->snd_max - state->snd_una;
00128             state->snd_cwnd = std::min (state->ssthresh, flight_size + state->snd_mss);
00129             tcpEV << "Fast Recovery - Full ACK received: Exit Fast Recovery, setting cwnd to " << state->snd_cwnd << "\n";
00130             // option (2): set cwnd to ssthresh
00131             // state->snd_cwnd = state->ssthresh;
00132             // tcpEV << "Fast Recovery - Full ACK received: Exit Fast Recovery, setting cwnd to ssthresh=" << state->ssthresh << "\n";
00133             // TODO - If the second option (2) is selected, take measures to avoid a possible burst of data (maxburst)!
00134             if (cwndVector) cwndVector->record(state->snd_cwnd);
00135 
00136             state->lossRecovery = false;
00137             state->firstPartialACK = false;
00138             tcpEV << "Loss Recovery terminated.\n";
00139         }
00140         else
00141         {
00142             // RFC 3782, page 5:
00143             // "Partial acknowledgements:
00144             // If this ACK does *not* acknowledge all of the data up to and
00145             // including "recover", then this is a partial ACK.  In this case,
00146             // retransmit the first unacknowledged segment.  Deflate the
00147             // congestion window by the amount of new data acknowledged by the
00148             // cumulative acknowledgement field.  If the partial ACK
00149             // acknowledges at least one SMSS of new data, then add back SMSS
00150             // bytes to the congestion window.  As in Step 3, this artificially
00151             // inflates the congestion window in order to reflect the additional
00152             // segment that has left the network.  Send a new segment if
00153             // permitted by the new value of cwnd.  This "partial window
00154             // deflation" attempts to ensure that, when Fast Recovery eventually
00155             // ends, approximately ssthresh amount of data will be outstanding
00156             // in the network.  Do not exit the Fast Recovery procedure (i.e.,
00157             // if any duplicate ACKs subsequently arrive, execute Steps 3 and 4
00158             // above).
00159             //
00160             // For the first partial ACK that arrives during Fast Recovery, also
00161             // reset the retransmit timer.  Timer management is discussed in
00162             // more detail in Section 4."
00163 
00164             tcpEV << "Fast Recovery - Partial ACK received: retransmitting the first unacknowledged segment\n";
00165             // retransmit first unacknowledged segment
00166             conn->retransmitOneSegment(false);
00167 
00168             // deflate cwnd by amount of new data acknowledged by cumulative acknowledgement field
00169             state->snd_cwnd -= state->snd_una - firstSeqAcked;
00170             if (cwndVector) cwndVector->record(state->snd_cwnd);
00171             tcpEV << "Fast Recovery: deflating cwnd by amount of new data acknowledged, new cwnd=" << state->snd_cwnd << "\n";
00172 
00173             // if the partial ACK acknowledges at least one SMSS of new data, then add back SMSS bytes to the cwnd
00174             if (state->snd_una - firstSeqAcked >= state->snd_mss)
00175             {
00176                 state->snd_cwnd += state->snd_mss;
00177                 if (cwndVector) cwndVector->record(state->snd_cwnd);
00178                 tcpEV << "Fast Recovery: inflating cwnd by SMSS, new cwnd=" << state->snd_cwnd << "\n";
00179             }
00180 
00181             // try to send a new segment if permitted by the new value of cwnd
00182             sendData();
00183 
00184             // reset REXMIT timer for the first partial ACK that arrives during Fast Recovery
00185             if (state->lossRecovery)
00186             {
00187                 if (!state->firstPartialACK)
00188                 {
00189                     state->firstPartialACK = true;
00190                     tcpEV << "First partial ACK arrived during recovery, restarting REXMIT timer.\n";
00191                     restartRexmitTimer();
00192                 }
00193             }
00194         }
00195     }
00196     else
00197     {
00198         //
00199         // Perform slow start and congestion avoidance.
00200         //
00201         if (state->snd_cwnd < state->ssthresh)
00202         {
00203             tcpEV << "cwnd<=ssthresh: Slow Start: increasing cwnd by SMSS bytes to ";
00204 
00205             // perform Slow Start. RFC 2581: "During slow start, a TCP increments cwnd
00206             // by at most SMSS bytes for each ACK received that acknowledges new data."
00207             state->snd_cwnd += state->snd_mss;
00208 
00209             // Note: we could increase cwnd based on the number of bytes being
00210             // acknowledged by each arriving ACK, rather than by the number of ACKs
00211             // that arrive. This is called "Appropriate Byte Counting" (ABC) and is
00212             // described in RFC 3465. This RFC is experimental and probably not
00213             // implemented in real-life TCPs, hence it's commented out. Also, the ABC
00214             // RFC would require other modifications as well in addition to the
00215             // two lines below.
00216             //
00217             // int bytesAcked = state->snd_una - firstSeqAcked;
00218             // state->snd_cwnd += bytesAcked*state->snd_mss;
00219 
00220             if (cwndVector) cwndVector->record(state->snd_cwnd);
00221 
00222             tcpEV << "cwnd=" << state->snd_cwnd << "\n";
00223         }
00224         else
00225         {
00226             // perform Congestion Avoidance (RFC 2581)
00227             uint32 incr = state->snd_mss * state->snd_mss / state->snd_cwnd;
00228             if (incr==0)
00229                 incr = 1;
00230             state->snd_cwnd += incr;
00231             if (cwndVector) cwndVector->record(state->snd_cwnd);
00232 
00233             //
00234             // Note: some implementations use extra additive constant mss/8 here
00235             // which is known to be incorrect (RFC 2581 p5)
00236             //
00237             // Note 2: RFC 3465 (experimental) "Appropriate Byte Counting" (ABC)
00238             // would require maintaining a bytes_acked variable here which we don't do
00239             //
00240 
00241             tcpEV << "cwnd>ssthresh: Congestion Avoidance: increasing cwnd linearly, to " << state->snd_cwnd << "\n";
00242         }
00243 
00244         // RFC 3782, page 13:
00245         // "When not in Fast Recovery, the value of the state variable "recover"
00246         // should be pulled along with the value of the state variable for
00247         // acknowledgments (typically, "snd_una") so that, when large amounts of
00248         // data have been sent and acked, the sequence space does not wrap and
00249         // falsely indicate that Fast Recovery should not be entered (Section 3,
00250         // step 1, last paragraph)."
00251         state->recover = (state->snd_una - 2);
00252     }
00253 
00254     sendData();
00255 }
00256 
00257 void TCPNewReno::receivedDuplicateAck()
00258 {
00259     TCPTahoeRenoFamily::receivedDuplicateAck();
00260 
00261     if (state->dupacks==DUPTHRESH) // DUPTHRESH = 3
00262     {
00263         if (!state->lossRecovery)
00264         {
00265             // RFC 3782, page 4:
00266             // "1) Three duplicate ACKs:
00267             // When the third duplicate ACK is received and the sender is not
00268             // already in the Fast Recovery procedure, check to see if the
00269             // Cumulative Acknowledgement field covers more than "recover".  If
00270             // so, go to Step 1A.  Otherwise, go to Step 1B."
00271             //
00272             // RFC 3782, page 6:
00273             // "Step 1 specifies a check that the Cumulative Acknowledgement field
00274             // covers more than "recover".  Because the acknowledgement field
00275             // contains the sequence number that the sender next expects to receive,
00276             // the acknowledgement "ack_number" covers more than "recover" when:
00277             //      ack_number - 1 > recover;"
00278             if (state->snd_una - 1 > state->recover)
00279             {
00280                 tcpEV << "NewReno on dupAck=DUPTHRESH(=3): perform Fast Retransmit, and enter Fast Recovery:";
00281 
00282                 // RFC 3782, page 4:
00283                 // "1A) Invoking Fast Retransmit:
00284                 // If so, then set ssthresh to no more than the value given in
00285                 // equation 1 below.  (This is equation 3 from [RFC2581]).
00286                 //      ssthresh = max (FlightSize / 2, 2*SMSS)           (1)
00287                 // In addition, record the highest sequence number transmitted in
00288                 // the variable "recover", and go to Step 2."
00289                 recalculateSlowStartThreshold();
00290                 state->recover = (state->snd_max - 1);
00291                 state->firstPartialACK = false;
00292                 state->lossRecovery = true;
00293                 tcpEV << " set recover=" << state->recover;
00294 
00295                 // RFC 3782, page 4:
00296                 // "2) Entering Fast Retransmit:
00297                 // Retransmit the lost segment and set cwnd to ssthresh plus 3*SMSS.
00298                 // This artificially "inflates" the congestion window by the number
00299                 // of segments (three) that have left the network and the receiver
00300                 // has buffered."
00301                 state->snd_cwnd = state->ssthresh + 3*state->snd_mss;
00302                 if (cwndVector) cwndVector->record(state->snd_cwnd);
00303                 tcpEV << " , cwnd=" << state->snd_cwnd << ", ssthresh=" << state->ssthresh << "\n";
00304                 conn->retransmitOneSegment(false);
00305 
00306                 // RFC 3782, page 5:
00307                 // "4) Fast Recovery, continued:
00308                 // Transmit a segment, if allowed by the new value of cwnd and the
00309                 // receiver's advertised window."
00310                 sendData();
00311             }
00312             else
00313             {
00314                 tcpEV << "NewReno on dupAck=DUPTHRESH(=3): not invoking Fast Retransmit and Fast Recovery\n";
00315 
00316                 // RFC 3782, page 4:
00317                 // "1B) Not invoking Fast Retransmit:
00318                 // Do not enter the Fast Retransmit and Fast Recovery procedure.  In
00319                 // particular, do not change ssthresh, do not go to Step 2 to
00320                 // retransmit the "lost" segment, and do not execute Step 3 upon
00321                 // subsequent duplicate ACKs."
00322             }
00323         }
00324         tcpEV << "NewReno on dupAck=DUPTHRESH(=3): TCP is already in Fast Recovery procedure\n";
00325     }
00326     else if (state->dupacks > DUPTHRESH) // DUPTHRESH = 3
00327     {
00328         if (state->lossRecovery)
00329         {
00330             // RFC 3782, page 4:
00331             // "3) Fast Recovery:
00332             // For each additional duplicate ACK received while in Fast
00333             // Recovery, increment cwnd by SMSS.  This artificially inflates the
00334             // congestion window in order to reflect the additional segment that
00335             // has left the network."
00336             state->snd_cwnd += state->snd_mss;
00337             if (cwndVector) cwndVector->record(state->snd_cwnd);
00338             tcpEV << "NewReno on dupAck>DUPTHRESH(=3): Fast Recovery: inflating cwnd by SMSS, new cwnd=" << state->snd_cwnd << "\n";
00339 
00340             // RFC 3782, page 5:
00341             // "4) Fast Recovery, continued:
00342             // Transmit a segment, if allowed by the new value of cwnd and the
00343             // receiver's advertised window."
00344             sendData();
00345         }
00346     }
00347 }