diff options
author | Nayana Bidari <nybidari@google.com> | 2021-02-25 16:27:30 -0800 |
---|---|---|
committer | gVisor bot <gvisor-bot@google.com> | 2021-02-25 16:29:28 -0800 |
commit | f3de211bb764d4e720879509debf918d37a71ce7 (patch) | |
tree | 8e0d82be4c21da30733297801de03bd0d6af15e3 | |
parent | 67761345c8f2d01f41eb8bbeb0dc8f5d94b4e576 (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.go | 6 | ||||
-rw-r--r-- | pkg/tcpip/transport/tcp/tcp_rack_test.go | 68 |
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") +} |