summaryrefslogtreecommitdiffhomepage
path: root/pkg/tcpip/transport/tcp/snd.go
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/tcpip/transport/tcp/snd.go')
-rw-r--r--pkg/tcpip/transport/tcp/snd.go75
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