summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorNayana Bidari <nybidari@google.com>2021-02-25 16:27:30 -0800
committergVisor bot <gvisor-bot@google.com>2021-02-25 16:29:28 -0800
commitf3de211bb764d4e720879509debf918d37a71ce7 (patch)
tree8e0d82be4c21da30733297801de03bd0d6af15e3
parent67761345c8f2d01f41eb8bbeb0dc8f5d94b4e576 (diff)
RACK: recovery logic should check for receive window before re-transmitting.
Use maybeSendSegment while sending segments in RACK recovery which checks if the receiver has space and splits the segments when the segment size is greater than MSS. PiperOrigin-RevId: 359641097
-rw-r--r--pkg/tcpip/transport/tcp/rack.go6
-rw-r--r--pkg/tcpip/transport/tcp/tcp_rack_test.go68
2 files changed, 72 insertions, 2 deletions
diff --git a/pkg/tcpip/transport/tcp/rack.go b/pkg/tcpip/transport/tcp/rack.go
index ecabeceb4..0a0d5f7a1 100644
--- a/pkg/tcpip/transport/tcp/rack.go
+++ b/pkg/tcpip/transport/tcp/rack.go
@@ -475,9 +475,11 @@ func (rc *rackControl) DoRecovery(_ *segment, fastRetransmit bool) {
break
}
- snd.outstanding++
+ if sent := snd.maybeSendSegment(seg, int(snd.ep.scoreboard.SMSS()), snd.sndUna.Add(snd.sndWnd)); !sent {
+ break
+ }
dataSent = true
- snd.sendSegment(seg)
+ snd.outstanding += snd.pCount(seg, snd.maxPayloadSize)
}
snd.postXmit(dataSent, true /* shouldScheduleProbe */)
diff --git a/pkg/tcpip/transport/tcp/tcp_rack_test.go b/pkg/tcpip/transport/tcp/tcp_rack_test.go
index 3c13fc8a3..5cdd5b588 100644
--- a/pkg/tcpip/transport/tcp/tcp_rack_test.go
+++ b/pkg/tcpip/transport/tcp/tcp_rack_test.go
@@ -987,3 +987,71 @@ func TestRACKUpdateSackedOut(t *testing.T) {
// test completes.
<-probeDone
}
+
+// TestRACKWithWindowFull tests that RACK honors the receive window size.
+func TestRACKWithWindowFull(t *testing.T) {
+ c := context.New(t, uint32(mtu))
+ defer c.Cleanup()
+
+ setStackSACKPermitted(t, c, true)
+ setStackRACKPermitted(t, c)
+ createConnectedWithSACKAndTS(c)
+
+ seq := seqnum.Value(context.TestInitialSequenceNumber).Add(1)
+ const numPkts = 10
+ data := make([]byte, numPkts*maxPayload)
+ for i := range data {
+ data[i] = byte(i)
+ }
+
+ // Write the data.
+ var r bytes.Reader
+ r.Reset(data)
+ if _, err := c.EP.Write(&r, tcpip.WriteOptions{}); err != nil {
+ t.Fatalf("Write failed: %s", err)
+ }
+
+ bytesRead := 0
+ for i := 0; i < numPkts; i++ {
+ c.ReceiveAndCheckPacketWithOptions(data, bytesRead, maxPayload, tsOptionSize)
+ bytesRead += maxPayload
+ if i == 0 {
+ // Send ACK for the first packet to establish RTT.
+ c.SendAck(seq, maxPayload)
+ }
+ }
+
+ // SACK for #10 packet.
+ start := c.IRS.Add(seqnum.Size(1 + (numPkts-1)*maxPayload))
+ end := start.Add(seqnum.Size(maxPayload))
+ c.SendAckWithSACK(seq, 2*maxPayload, []header.SACKBlock{{start, end}})
+
+ var info tcpip.TCPInfoOption
+ if err := c.EP.GetSockOpt(&info); err != nil {
+ t.Fatalf("GetSockOpt failed: %v", err)
+ }
+ // Wait for RTT to trigger recovery.
+ time.Sleep(info.RTT)
+
+ // Expect retransmission of #2 packet.
+ c.ReceiveAndCheckPacketWithOptions(data, 2*maxPayload, maxPayload, tsOptionSize)
+
+ // Send ACK for #2 packet.
+ c.SendAck(seq, 3*maxPayload)
+
+ // Expect retransmission of #3 packet.
+ c.ReceiveAndCheckPacketWithOptions(data, 3*maxPayload, maxPayload, tsOptionSize)
+
+ // Send ACK with zero window size.
+ c.SendPacket(nil, &context.Headers{
+ SrcPort: context.TestPort,
+ DstPort: c.Port,
+ Flags: header.TCPFlagAck,
+ SeqNum: seq,
+ AckNum: c.IRS.Add(1 + 4*maxPayload),
+ RcvWnd: 0,
+ })
+
+ // No packet should be received as the receive window size is zero.
+ c.CheckNoPacket("unexpected packet received after userTimeout has expired")
+}