flavours/TCPReno.cc

Go to the documentation of this file.
00001 //
00002 // Copyright (C) 2004-2005 Andras Varga
00003 // Copyright (C) 2009 Thomas Reschka
00004 //
00005 // This program is free software; you can redistribute it and/or
00006 // modify it under the terms of the GNU Lesser General Public License
00007 // as published by the Free Software Foundation; either version 2
00008 // of the License, or (at your option) any later version.
00009 //
00010 // This program is distributed in the hope that it will be useful,
00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013 // GNU Lesser General Public License for more details.
00014 //
00015 // You should have received a copy of the GNU Lesser General Public License
00016 // along with this program; if not, see <http://www.gnu.org/licenses/>.
00017 //
00018 
00019 #include <algorithm>   // min,max
00020 #include "TCPReno.h"
00021 #include "TCP.h"
00022 
00023 
00024 Register_Class(TCPReno);
00025 
00026 
00027 TCPReno::TCPReno() : TCPTahoeRenoFamily(),
00028   state((TCPRenoStateVariables *&)TCPAlgorithm::state)
00029 {
00030 }
00031 
00032 void TCPReno::recalculateSlowStartThreshold()
00033 {
00034     // RFC 2581, page 4:
00035     // "When a TCP sender detects segment loss using the retransmission
00036     // timer, the value of ssthresh MUST be set to no more than the value
00037     // given in equation 3:
00038     //
00039     //   ssthresh = max (FlightSize / 2, 2*SMSS)            (3)
00040     //
00041     // As discussed above, FlightSize is the amount of outstanding data in
00042     // the network."
00043 
00044     // set ssthresh to flight size/2, but at least 2 SMSS
00045     // (the formula below practically amounts to ssthresh=cwnd/2 most of the time)
00046     uint32 flight_size = std::min(state->snd_cwnd, state->snd_wnd); // FIXME TODO - Does this formula computes the amount of outstanding data?
00047     // uint32 flight_size = state->snd_max - state->snd_una;
00048     state->ssthresh = std::max(flight_size/2, 2*state->snd_mss);
00049     if (ssthreshVector) ssthreshVector->record(state->ssthresh);
00050 }
00051 
00052 void TCPReno::processRexmitTimer(TCPEventCode& event)
00053 {
00054     TCPTahoeRenoFamily::processRexmitTimer(event);
00055     if (event==TCP_E_ABORT)
00056         return;
00057 
00058     // After REXMIT timeout TCP Reno should start slow start with snd_cwnd = snd_mss.
00059     //
00060     // If calling "retransmitData();" there is no rexmit limitation (bytesToSend > snd_cwnd)
00061     // therefore "sendData();" has been modified and is called to rexmit outstanding data.
00062     //
00063     // RFC 2581, page 5:
00064     // "Furthermore, upon a timeout cwnd MUST be set to no more than the loss
00065     // window, LW, which equals 1 full-sized segment (regardless of the
00066     // value of IW).  Therefore, after retransmitting the dropped segment
00067     // the TCP sender uses the slow start algorithm to increase the window
00068     // from 1 full-sized segment to the new value of ssthresh, at which
00069     // point congestion avoidance again takes over."
00070 
00071     // begin Slow Start (RFC 2581)
00072     recalculateSlowStartThreshold();
00073     state->snd_cwnd = state->snd_mss;
00074     if (cwndVector) cwndVector->record(state->snd_cwnd);
00075     tcpEV << "Begin Slow Start: resetting cwnd to " << state->snd_cwnd
00076           << ", ssthresh=" << state->ssthresh << "\n";
00077 
00078     state->afterRto = true;
00079 
00080     conn->retransmitOneSegment(true);
00081 }
00082 
00083 void TCPReno::receivedDataAck(uint32 firstSeqAcked)
00084 {
00085     TCPTahoeRenoFamily::receivedDataAck(firstSeqAcked);
00086 
00087     if (state->dupacks>=DUPTHRESH) // DUPTHRESH = 3
00088     {
00089         //
00090         // Perform Fast Recovery: set cwnd to ssthresh (deflating the window).
00091         //
00092         tcpEV << "Fast Recovery: setting cwnd to ssthresh=" << state->ssthresh << "\n";
00093         state->snd_cwnd = state->ssthresh;
00094         if (cwndVector) cwndVector->record(state->snd_cwnd);
00095     }
00096     else
00097     {
00098         //
00099         // Perform slow start and congestion avoidance.
00100         //
00101         if (state->snd_cwnd < state->ssthresh)
00102         {
00103             tcpEV << "cwnd<=ssthresh: Slow Start: increasing cwnd by one SMSS bytes to ";
00104 
00105             // perform Slow Start. RFC 2581: "During slow start, a TCP increments cwnd
00106             // by at most SMSS bytes for each ACK received that acknowledges new data."
00107             state->snd_cwnd += state->snd_mss;
00108 
00109             // Note: we could increase cwnd based on the number of bytes being
00110             // acknowledged by each arriving ACK, rather than by the number of ACKs
00111             // that arrive. This is called "Appropriate Byte Counting" (ABC) and is
00112             // described in RFC 3465. This RFC is experimental and probably not
00113             // implemented in real-life TCPs, hence it's commented out. Also, the ABC
00114             // RFC would require other modifications as well in addition to the
00115             // two lines below.
00116             //
00117             // int bytesAcked = state->snd_una - firstSeqAcked;
00118             // state->snd_cwnd += bytesAcked*state->snd_mss;
00119 
00120             if (cwndVector) cwndVector->record(state->snd_cwnd);
00121 
00122             tcpEV << "cwnd=" << state->snd_cwnd << "\n";
00123         }
00124         else
00125         {
00126             // perform Congestion Avoidance (RFC 2581)
00127             uint32 incr = state->snd_mss * state->snd_mss / state->snd_cwnd;
00128             if (incr==0)
00129                 incr = 1;
00130             state->snd_cwnd += incr;
00131             if (cwndVector) cwndVector->record(state->snd_cwnd);
00132 
00133             //
00134             // Note: some implementations use extra additive constant mss/8 here
00135             // which is known to be incorrect (RFC 2581 p5)
00136             //
00137             // Note 2: RFC 3465 (experimental) "Appropriate Byte Counting" (ABC)
00138             // would require maintaining a bytes_acked variable here which we don't do
00139             //
00140 
00141             tcpEV << "cwnd>ssthresh: Congestion Avoidance: increasing cwnd linearly, to " << state->snd_cwnd << "\n";
00142         }
00143     }
00144 
00145     if (state->sack_enabled && state->lossRecovery)
00146     {
00147         // RFC 3517, page 7: "Once a TCP is in the loss recovery phase the following procedure MUST
00148         // be used for each arriving ACK:
00149         //
00150         // (A) An incoming cumulative ACK for a sequence number greater than
00151         // RecoveryPoint signals the end of loss recovery and the loss
00152         // recovery phase MUST be terminated.  Any information contained in
00153         // the scoreboard for sequence numbers greater than the new value of
00154         // HighACK SHOULD NOT be cleared when leaving the loss recovery
00155         // phase."
00156         if (seqGE(state->snd_una, state->recoveryPoint))
00157         {
00158             tcpEV << "Loss Recovery terminated.\n";
00159             state->lossRecovery = false;
00160         }
00161         // RFC 3517, page 7: "(B) Upon receipt of an ACK that does not cover RecoveryPoint the
00162         //following actions MUST be taken:
00163         //
00164         // (B.1) Use Update () to record the new SACK information conveyed
00165         // by the incoming ACK.
00166         //
00167         // (B.2) Use SetPipe () to re-calculate the number of octets still
00168         // in the network."
00169         else
00170         {
00171             // update of scoreboard (B.1) has already be done in readHeaderOptions()
00172             conn->setPipe();
00173 
00174             // RFC 3517, page 7: "(C) If cwnd - pipe >= 1 SMSS the sender SHOULD transmit one or more
00175             // segments as follows:"
00176             if (((int)state->snd_cwnd - (int)state->pipe) >= (int)state->snd_mss) // Note: Typecast needed to avoid prohibited transmissions
00177                 conn->sendDataDuringLossRecoveryPhase(state->snd_cwnd);
00178         }
00179     }
00180 
00181     // RFC 3517, pages 7 and 8: "5.1 Retransmission Timeouts
00182     // (...)
00183     // If there are segments missing from the receiver's buffer following
00184     // processing of the retransmitted segment, the corresponding ACK will
00185     // contain SACK information.  In this case, a TCP sender SHOULD use this
00186     // SACK information when determining what data should be sent in each
00187     // segment of the slow start.  The exact algorithm for this selection is
00188     // not specified in this document (specifically NextSeg () is
00189     // inappropriate during slow start after an RTO).  A relatively
00190     // straightforward approach to "filling in" the sequence space reported
00191     // as missing should be a reasonable approach."
00192     sendData();
00193 }
00194 
00195 void TCPReno::receivedDuplicateAck()
00196 {
00197     TCPTahoeRenoFamily::receivedDuplicateAck();
00198 
00199     if (state->dupacks==DUPTHRESH) // DUPTHRESH = 3
00200     {
00201         tcpEV << "Reno on dupAck=DUPTHRESH(=3): perform Fast Retransmit, and enter Fast Recovery:";
00202 
00203         if (state->sack_enabled)
00204         {
00205             // RFC 3517, page 6: "When a TCP sender receives the duplicate ACK corresponding to
00206             // DupThresh ACKs, the scoreboard MUST be updated with the new SACK
00207             // information (via Update ()).  If no previous loss event has occurred
00208             // on the connection or the cumulative acknowledgment point is beyond
00209             // the last value of RecoveryPoint, a loss recovery phase SHOULD be
00210             // initiated, per the fast retransmit algorithm outlined in [RFC2581].
00211             // The following steps MUST be taken:
00212             //
00213             // (1) RecoveryPoint = HighData
00214             //
00215             // When the TCP sender receives a cumulative ACK for this data octet
00216             // the loss recovery phase is terminated."
00217 
00218             // RFC 3517, page 8: "If an RTO occurs during loss recovery as specified in this document,
00219             // RecoveryPoint MUST be set to HighData.  Further, the new value of
00220             // RecoveryPoint MUST be preserved and the loss recovery algorithm
00221             // outlined in this document MUST be terminated.  In addition, a new
00222             // recovery phase (as described in section 5) MUST NOT be initiated
00223             // until HighACK is greater than or equal to the new value of
00224             // RecoveryPoint."
00225             if (state->recoveryPoint == 0 || seqGE(state->snd_una, state->recoveryPoint)) // HighACK = snd_una
00226             {
00227                 state->recoveryPoint = state->snd_max; // HighData = snd_max
00228                 state->lossRecovery = true;
00229                 tcpEV << " recoveryPoint=" << state->recoveryPoint;
00230             }
00231         }
00232         // RFC 2581, page 5:
00233         // "After the fast retransmit algorithm sends what appears to be the
00234         // missing segment, the "fast recovery" algorithm governs the
00235         // transmission of new data until a non-duplicate ACK arrives.
00236         // (...) the TCP sender can continue to transmit new
00237         // segments (although transmission must continue using a reduced cwnd)."
00238 
00239         // enter Fast Recovery
00240         recalculateSlowStartThreshold();
00241         // "set cwnd to ssthresh plus 3*SMSS." (RFC 2581)
00242         state->snd_cwnd = state->ssthresh + 3*state->snd_mss; // 20051129 (1)
00243         if (cwndVector) cwndVector->record(state->snd_cwnd);
00244 
00245         tcpEV << " set cwnd=" << state->snd_cwnd << ", ssthresh=" << state->ssthresh << "\n";
00246 
00247         // Fast Retransmission: retransmit missing segment without waiting
00248         // for the REXMIT timer to expire
00249         conn->retransmitOneSegment(false);
00250 
00251         // Do not restart REXMIT timer.
00252         // Note: Restart of REXMIT timer on retransmission is not part of RFC 2581, however optional in RFC 3517 if sent during recovery.
00253         // Resetting the REXMIT timer is discussed in RFC 2582/3782 (NewReno) and RFC 2988.
00254 
00255         if (state->sack_enabled)
00256         {
00257             // RFC 3517, page 7: "(4) Run SetPipe ()
00258             //
00259             // Set a "pipe" variable  to the number of outstanding octets
00260             // currently "in the pipe"; this is the data which has been sent by
00261             // the TCP sender but for which no cumulative or selective
00262             // acknowledgment has been received and the data has not been
00263             // determined to have been dropped in the network.  It is assumed
00264             // that the data is still traversing the network path."
00265             conn->setPipe();
00266             // RFC 3517, page 7: "(5) In order to take advantage of potential additional available
00267             // cwnd, proceed to step (C) below."
00268             if (state->lossRecovery)
00269             {
00270                 // RFC 3517, page 9: "Therefore we give implementers the latitude to use the standard
00271                 // [RFC2988] style RTO management or, optionally, a more careful variant
00272                 // that re-arms the RTO timer on each retransmission that is sent during
00273                 // recovery MAY be used.  This provides a more conservative timer than
00274                 // specified in [RFC2988], and so may not always be an attractive
00275                 // alternative.  However, in some cases it may prevent needless
00276                 // retransmissions, go-back-N transmission and further reduction of the
00277                 // congestion window."
00278                 // Note: Restart of REXMIT timer on retransmission is not part of RFC 2581, however optional in RFC 3517 if sent during recovery.
00279                 tcpEV << "Retransmission sent during recovery, restarting REXMIT timer.\n";
00280                 restartRexmitTimer();
00281 
00282                 // RFC 3517, page 7: "(C) If cwnd - pipe >= 1 SMSS the sender SHOULD transmit one or more
00283                 // segments as follows:"
00284                 if (((int)state->snd_cwnd - (int)state->pipe) >= (int)state->snd_mss) // Note: Typecast needed to avoid prohibited transmissions
00285                     conn->sendDataDuringLossRecoveryPhase(state->snd_cwnd);
00286             }
00287         }
00288 
00289         // try to transmit new segments (RFC 2581)
00290         sendData();
00291     }
00292     else if (state->dupacks > DUPTHRESH) // DUPTHRESH = 3
00293     {
00294         //
00295         // Reno: For each additional duplicate ACK received, increment cwnd by SMSS.
00296         // This artificially inflates the congestion window in order to reflect the
00297         // additional segment that has left the network
00298         //
00299         state->snd_cwnd += state->snd_mss;
00300         tcpEV << "Reno on dupAck>DUPTHRESH(=3): Fast Recovery: inflating cwnd by SMSS, new cwnd=" << state->snd_cwnd << "\n";
00301         if (cwndVector) cwndVector->record(state->snd_cwnd);
00302 
00303         // Note: Steps (A) - (C) of RFC 3517, page 7 ("Once a TCP is in the loss recovery phase the following procedure MUST be used for each arriving ACK")
00304         // should not be used here!
00305 
00306         // RFC 3517, pages 7 and 8: "5.1 Retransmission Timeouts
00307         // (...)
00308         // If there are segments missing from the receiver's buffer following
00309         // processing of the retransmitted segment, the corresponding ACK will
00310         // contain SACK information.  In this case, a TCP sender SHOULD use this
00311         // SACK information when determining what data should be sent in each
00312         // segment of the slow start.  The exact algorithm for this selection is
00313         // not specified in this document (specifically NextSeg () is
00314         // inappropriate during slow start after an RTO).  A relatively
00315         // straightforward approach to "filling in" the sequence space reported
00316         // as missing should be a reasonable approach."
00317         sendData();
00318     }
00319 }