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 }