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 }