diff options
Diffstat (limited to 'pkg/tcpip/transport/tcp/snd.go')
-rw-r--r-- | pkg/tcpip/transport/tcp/snd.go | 75 |
1 files changed, 31 insertions, 44 deletions
diff --git a/pkg/tcpip/transport/tcp/snd.go b/pkg/tcpip/transport/tcp/snd.go index 7dfbf6384..e38686e1b 100644 --- a/pkg/tcpip/transport/tcp/snd.go +++ b/pkg/tcpip/transport/tcp/snd.go @@ -31,8 +31,28 @@ const ( // InitialCwnd is the initial congestion window. InitialCwnd = 10 + + // nDupAckThreshold is the number of duplicate ACK's required + // before fast-retransmit is entered. + nDupAckThreshold = 3 ) +// congestionControl is an interface that must be implemented by any supported +// congestion control algorithm. +type congestionControl interface { + // HandleNDupAcks is invoked when sender.dupAckCount >= nDupAckThreshold + // just before entering fast retransmit. + HandleNDupAcks() + + // HandleRTOExpired is invoked when the retransmit timer expires. + HandleRTOExpired() + + // Update is invoked when processing inbound acks. It's passed the + // number of packet's that were acked by the most recent cumulative + // acknowledgement. + Update(packetsAcked int) +} + // sender holds the state necessary to send TCP segments. type sender struct { ep *endpoint @@ -107,6 +127,9 @@ type sender struct { // maxSentAck is the maxium acknowledgement actually sent. maxSentAck seqnum.Value + + // cc is the congestion control algorithm in use for this sender. + cc congestionControl } // fastRecovery holds information related to fast recovery from a packet loss. @@ -147,6 +170,8 @@ func newSender(ep *endpoint, iss, irs seqnum.Value, sndWnd seqnum.Size, mss uint }, } + s.cc = newRenoCC(s) + // A negative sndWndScale means that no scaling is in use, otherwise we // store the scaling value. if sndWndScale > 0 { @@ -251,15 +276,6 @@ func (s *sender) resendSegment() { } } -// reduceSlowStartThreshold reduces the slow-start threshold per RFC 5681, -// page 6, eq. 4. It is called when we detect congestion in the network. -func (s *sender) reduceSlowStartThreshold() { - s.sndSsthresh = s.outstanding / 2 - if s.sndSsthresh < 2 { - s.sndSsthresh = 2 - } -} - // retransmitTimerExpired is called when the retransmit timer expires, and // unacknowledged segments are assumed lost, and thus need to be resent. // Returns true if the connection is still usable, or false if the connection @@ -292,13 +308,7 @@ func (s *sender) retransmitTimerExpired() bool { // we were not in fast recovery. s.fr.last = s.sndNxt - 1 - // We lost a packet, so reduce ssthresh. - s.reduceSlowStartThreshold() - - // Reduce the congestion window to 1, i.e., enter slow-start. Per - // RFC 5681, page 7, we must use 1 regardless of the value of the - // initial congestion window. - s.sndCwnd = 1 + s.cc.HandleRTOExpired() // Mark the next segment to be sent as the first unacknowledged one and // start sending again. Set the number of outstanding packets to 0 so @@ -396,8 +406,6 @@ func (s *sender) sendData() { func (s *sender) enterFastRecovery() { // Save state to reflect we're now in fast recovery. - s.reduceSlowStartThreshold() - // Save state to reflect we're now in fast recovery. // See : https://tools.ietf.org/html/rfc5681#section-3.2 Step 3. // We inflat the cwnd by 3 to account for the 3 packets which triggered // the 3 duplicate ACKs and are now not in flight. @@ -474,9 +482,9 @@ func (s *sender) checkDuplicateAck(seg *segment) bool { return false } - // Enter fast recovery when we reach 3 dups. s.dupAckCount++ - if s.dupAckCount != 3 { + // Do not enter fast recovery until we reach nDupAckThreshold. + if s.dupAckCount < nDupAckThreshold { return false } @@ -489,6 +497,8 @@ func (s *sender) checkDuplicateAck(seg *segment) bool { s.dupAckCount = 0 return false } + + s.cc.HandleNDupAcks() s.enterFastRecovery() s.dupAckCount = 0 return true @@ -497,29 +507,6 @@ func (s *sender) checkDuplicateAck(seg *segment) bool { // updateCwnd updates the congestion window based on the number of packets that // were acknowledged. func (s *sender) updateCwnd(packetsAcked int) { - if s.sndCwnd < s.sndSsthresh { - // Don't let the congestion window cross into the congestion - // avoidance range. - newcwnd := s.sndCwnd + packetsAcked - if newcwnd >= s.sndSsthresh { - newcwnd = s.sndSsthresh - s.sndCAAckCount = 0 - } - - packetsAcked -= newcwnd - s.sndCwnd - s.sndCwnd = newcwnd - if packetsAcked == 0 { - // We've consumed all ack'd packets. - return - } - } - - // Consume the packets in congestion avoidance mode. - s.sndCAAckCount += packetsAcked - if s.sndCAAckCount >= s.sndCwnd { - s.sndCwnd += s.sndCAAckCount / s.sndCwnd - s.sndCAAckCount = s.sndCAAckCount % s.sndCwnd - } } // handleRcvdSegment is called when a segment is received; it is responsible for @@ -580,7 +567,7 @@ func (s *sender) handleRcvdSegment(seg *segment) { // If we are not in fast recovery then update the congestion // window based on the number of acknowledged packets. if !s.fr.active { - s.updateCwnd(originalOutstanding - s.outstanding) + s.cc.Update(originalOutstanding - s.outstanding) } // It is possible for s.outstanding to drop below zero if we get |