|
INET Framework for OMNeT++/OMNEST
|
#include <TCPReno.h>
Public Member Functions | |
| TCPReno () | |
| virtual void | receivedDataAck (uint32 firstSeqAcked) |
| virtual void | receivedDuplicateAck () |
Protected Member Functions | |
| virtual TCPStateVariables * | createStateVariables () |
| virtual void | recalculateSlowStartThreshold () |
| virtual void | processRexmitTimer (TCPEventCode &event) |
Protected Attributes | |
| TCPRenoStateVariables *& | state |
Implements TCP Reno.
| TCPReno::TCPReno | ( | ) |
Ctor
: TCPTahoeRenoFamily(), state((TCPRenoStateVariables *&)TCPAlgorithm::state) { }
| virtual TCPStateVariables* TCPReno::createStateVariables | ( | ) | [inline, protected, virtual] |
Create and return a TCPRenoStateVariables object.
Implements TCPAlgorithm.
{
return new TCPRenoStateVariables();
}
| void TCPReno::processRexmitTimer | ( | TCPEventCode & | event | ) | [protected, virtual] |
Redefine what should happen on retransmission
Reimplemented from TCPBaseAlg.
{
TCPTahoeRenoFamily::processRexmitTimer(event);
if (event==TCP_E_ABORT)
return;
// After REXMIT timeout TCP Reno should start slow start with snd_cwnd = snd_mss.
//
// If calling "retransmitData();" there is no rexmit limitation (bytesToSend > snd_cwnd)
// therefore "sendData();" has been modified and is called to rexmit outstanding data.
//
// RFC 2581, page 5:
// "Furthermore, upon a timeout cwnd MUST be set to no more than the loss
// window, LW, which equals 1 full-sized segment (regardless of the
// value of IW). Therefore, after retransmitting the dropped segment
// the TCP sender uses the slow start algorithm to increase the window
// from 1 full-sized segment to the new value of ssthresh, at which
// point congestion avoidance again takes over."
// begin Slow Start (RFC 2581)
recalculateSlowStartThreshold();
state->snd_cwnd = state->snd_mss;
if (cwndVector) cwndVector->record(state->snd_cwnd);
tcpEV << "Begin Slow Start: resetting cwnd to " << state->snd_cwnd
<< ", ssthresh=" << state->ssthresh << "\n";
state->afterRto = true;
conn->retransmitOneSegment(true);
}
| void TCPReno::recalculateSlowStartThreshold | ( | ) | [protected, virtual] |
Utility function to recalculate ssthresh
Referenced by processRexmitTimer(), and receivedDuplicateAck().
{
// RFC 2581, page 4:
// "When a TCP sender detects segment loss using the retransmission
// timer, the value of ssthresh MUST be set to no more than the value
// given in equation 3:
//
// ssthresh = max (FlightSize / 2, 2*SMSS) (3)
//
// As discussed above, FlightSize is the amount of outstanding data in
// the network."
// set ssthresh to flight size/2, but at least 2 SMSS
// (the formula below practically amounts to ssthresh=cwnd/2 most of the time)
uint32 flight_size = std::min(state->snd_cwnd, state->snd_wnd); // FIXME TODO - Does this formula computes the amount of outstanding data?
// uint32 flight_size = state->snd_max - state->snd_una;
state->ssthresh = std::max(flight_size/2, 2*state->snd_mss);
if (ssthreshVector) ssthreshVector->record(state->ssthresh);
}
| void TCPReno::receivedDataAck | ( | uint32 | firstSeqAcked | ) | [virtual] |
Redefine what should happen when data got acked, to add congestion window management
Reimplemented from TCPBaseAlg.
{
TCPTahoeRenoFamily::receivedDataAck(firstSeqAcked);
if (state->dupacks>=DUPTHRESH) // DUPTHRESH = 3
{
//
// Perform Fast Recovery: set cwnd to ssthresh (deflating the window).
//
tcpEV << "Fast Recovery: setting cwnd to ssthresh=" << state->ssthresh << "\n";
state->snd_cwnd = state->ssthresh;
if (cwndVector) cwndVector->record(state->snd_cwnd);
}
else
{
//
// Perform slow start and congestion avoidance.
//
if (state->snd_cwnd < state->ssthresh)
{
tcpEV << "cwnd<=ssthresh: Slow Start: increasing cwnd by one SMSS bytes to ";
// perform Slow Start. RFC 2581: "During slow start, a TCP increments cwnd
// by at most SMSS bytes for each ACK received that acknowledges new data."
state->snd_cwnd += state->snd_mss;
// Note: we could increase cwnd based on the number of bytes being
// acknowledged by each arriving ACK, rather than by the number of ACKs
// that arrive. This is called "Appropriate Byte Counting" (ABC) and is
// described in RFC 3465. This RFC is experimental and probably not
// implemented in real-life TCPs, hence it's commented out. Also, the ABC
// RFC would require other modifications as well in addition to the
// two lines below.
//
// int bytesAcked = state->snd_una - firstSeqAcked;
// state->snd_cwnd += bytesAcked*state->snd_mss;
if (cwndVector) cwndVector->record(state->snd_cwnd);
tcpEV << "cwnd=" << state->snd_cwnd << "\n";
}
else
{
// perform Congestion Avoidance (RFC 2581)
uint32 incr = state->snd_mss * state->snd_mss / state->snd_cwnd;
if (incr==0)
incr = 1;
state->snd_cwnd += incr;
if (cwndVector) cwndVector->record(state->snd_cwnd);
//
// Note: some implementations use extra additive constant mss/8 here
// which is known to be incorrect (RFC 2581 p5)
//
// Note 2: RFC 3465 (experimental) "Appropriate Byte Counting" (ABC)
// would require maintaining a bytes_acked variable here which we don't do
//
tcpEV << "cwnd>ssthresh: Congestion Avoidance: increasing cwnd linearly, to " << state->snd_cwnd << "\n";
}
}
if (state->sack_enabled && state->lossRecovery)
{
// RFC 3517, page 7: "Once a TCP is in the loss recovery phase the following procedure MUST
// be used for each arriving ACK:
//
// (A) An incoming cumulative ACK for a sequence number greater than
// RecoveryPoint signals the end of loss recovery and the loss
// recovery phase MUST be terminated. Any information contained in
// the scoreboard for sequence numbers greater than the new value of
// HighACK SHOULD NOT be cleared when leaving the loss recovery
// phase."
if (seqGE(state->snd_una, state->recoveryPoint))
{
tcpEV << "Loss Recovery terminated.\n";
state->lossRecovery = false;
}
// RFC 3517, page 7: "(B) Upon receipt of an ACK that does not cover RecoveryPoint the
//following actions MUST be taken:
//
// (B.1) Use Update () to record the new SACK information conveyed
// by the incoming ACK.
//
// (B.2) Use SetPipe () to re-calculate the number of octets still
// in the network."
else
{
// update of scoreboard (B.1) has already be done in readHeaderOptions()
conn->setPipe();
// RFC 3517, page 7: "(C) If cwnd - pipe >= 1 SMSS the sender SHOULD transmit one or more
// segments as follows:"
if (((int)state->snd_cwnd - (int)state->pipe) >= (int)state->snd_mss) // Note: Typecast needed to avoid prohibited transmissions
conn->sendDataDuringLossRecoveryPhase(state->snd_cwnd);
}
}
// RFC 3517, pages 7 and 8: "5.1 Retransmission Timeouts
// (...)
// If there are segments missing from the receiver's buffer following
// processing of the retransmitted segment, the corresponding ACK will
// contain SACK information. In this case, a TCP sender SHOULD use this
// SACK information when determining what data should be sent in each
// segment of the slow start. The exact algorithm for this selection is
// not specified in this document (specifically NextSeg () is
// inappropriate during slow start after an RTO). A relatively
// straightforward approach to "filling in" the sequence space reported
// as missing should be a reasonable approach."
sendData();
}
| void TCPReno::receivedDuplicateAck | ( | ) | [virtual] |
Redefine what should happen when dupAck was received, to add congestion window management
Reimplemented from TCPBaseAlg.
{
TCPTahoeRenoFamily::receivedDuplicateAck();
if (state->dupacks==DUPTHRESH) // DUPTHRESH = 3
{
tcpEV << "Reno on dupAck=DUPTHRESH(=3): perform Fast Retransmit, and enter Fast Recovery:";
if (state->sack_enabled)
{
// RFC 3517, page 6: "When a TCP sender receives the duplicate ACK corresponding to
// DupThresh ACKs, the scoreboard MUST be updated with the new SACK
// information (via Update ()). If no previous loss event has occurred
// on the connection or the cumulative acknowledgment point is beyond
// the last value of RecoveryPoint, a loss recovery phase SHOULD be
// initiated, per the fast retransmit algorithm outlined in [RFC2581].
// The following steps MUST be taken:
//
// (1) RecoveryPoint = HighData
//
// When the TCP sender receives a cumulative ACK for this data octet
// the loss recovery phase is terminated."
// RFC 3517, page 8: "If an RTO occurs during loss recovery as specified in this document,
// RecoveryPoint MUST be set to HighData. Further, the new value of
// RecoveryPoint MUST be preserved and the loss recovery algorithm
// outlined in this document MUST be terminated. In addition, a new
// recovery phase (as described in section 5) MUST NOT be initiated
// until HighACK is greater than or equal to the new value of
// RecoveryPoint."
if (state->recoveryPoint == 0 || seqGE(state->snd_una, state->recoveryPoint)) // HighACK = snd_una
{
state->recoveryPoint = state->snd_max; // HighData = snd_max
state->lossRecovery = true;
tcpEV << " recoveryPoint=" << state->recoveryPoint;
}
}
// RFC 2581, page 5:
// "After the fast retransmit algorithm sends what appears to be the
// missing segment, the "fast recovery" algorithm governs the
// transmission of new data until a non-duplicate ACK arrives.
// (...) the TCP sender can continue to transmit new
// segments (although transmission must continue using a reduced cwnd)."
// enter Fast Recovery
recalculateSlowStartThreshold();
// "set cwnd to ssthresh plus 3*SMSS." (RFC 2581)
state->snd_cwnd = state->ssthresh + 3*state->snd_mss; // 20051129 (1)
if (cwndVector) cwndVector->record(state->snd_cwnd);
tcpEV << " set cwnd=" << state->snd_cwnd << ", ssthresh=" << state->ssthresh << "\n";
// Fast Retransmission: retransmit missing segment without waiting
// for the REXMIT timer to expire
conn->retransmitOneSegment(false);
// Do not restart REXMIT timer.
// Note: Restart of REXMIT timer on retransmission is not part of RFC 2581, however optional in RFC 3517 if sent during recovery.
// Resetting the REXMIT timer is discussed in RFC 2582/3782 (NewReno) and RFC 2988.
if (state->sack_enabled)
{
// RFC 3517, page 7: "(4) Run SetPipe ()
//
// Set a "pipe" variable to the number of outstanding octets
// currently "in the pipe"; this is the data which has been sent by
// the TCP sender but for which no cumulative or selective
// acknowledgment has been received and the data has not been
// determined to have been dropped in the network. It is assumed
// that the data is still traversing the network path."
conn->setPipe();
// RFC 3517, page 7: "(5) In order to take advantage of potential additional available
// cwnd, proceed to step (C) below."
if (state->lossRecovery)
{
// RFC 3517, page 9: "Therefore we give implementers the latitude to use the standard
// [RFC2988] style RTO management or, optionally, a more careful variant
// that re-arms the RTO timer on each retransmission that is sent during
// recovery MAY be used. This provides a more conservative timer than
// specified in [RFC2988], and so may not always be an attractive
// alternative. However, in some cases it may prevent needless
// retransmissions, go-back-N transmission and further reduction of the
// congestion window."
// Note: Restart of REXMIT timer on retransmission is not part of RFC 2581, however optional in RFC 3517 if sent during recovery.
tcpEV << "Retransmission sent during recovery, restarting REXMIT timer.\n";
restartRexmitTimer();
// RFC 3517, page 7: "(C) If cwnd - pipe >= 1 SMSS the sender SHOULD transmit one or more
// segments as follows:"
if (((int)state->snd_cwnd - (int)state->pipe) >= (int)state->snd_mss) // Note: Typecast needed to avoid prohibited transmissions
conn->sendDataDuringLossRecoveryPhase(state->snd_cwnd);
}
}
// try to transmit new segments (RFC 2581)
sendData();
}
else if (state->dupacks > DUPTHRESH) // DUPTHRESH = 3
{
//
// Reno: For each additional duplicate ACK received, increment cwnd by SMSS.
// This artificially inflates the congestion window in order to reflect the
// additional segment that has left the network
//
state->snd_cwnd += state->snd_mss;
tcpEV << "Reno on dupAck>DUPTHRESH(=3): Fast Recovery: inflating cwnd by SMSS, new cwnd=" << state->snd_cwnd << "\n";
if (cwndVector) cwndVector->record(state->snd_cwnd);
// 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")
// should not be used here!
// RFC 3517, pages 7 and 8: "5.1 Retransmission Timeouts
// (...)
// If there are segments missing from the receiver's buffer following
// processing of the retransmitted segment, the corresponding ACK will
// contain SACK information. In this case, a TCP sender SHOULD use this
// SACK information when determining what data should be sent in each
// segment of the slow start. The exact algorithm for this selection is
// not specified in this document (specifically NextSeg () is
// inappropriate during slow start after an RTO). A relatively
// straightforward approach to "filling in" the sequence space reported
// as missing should be a reasonable approach."
sendData();
}
}
TCPRenoStateVariables*& TCPReno::state [protected] |
Reimplemented from TCPTahoeRenoFamily.
Referenced by processRexmitTimer(), recalculateSlowStartThreshold(), receivedDataAck(), and receivedDuplicateAck().