From 91a283f8fa1cea3c1476b27b2dc27b41a69c1a23 Mon Sep 17 00:00:00 2001 From: Kevin Krakauer Date: Mon, 8 Apr 2024 15:20:47 -0700 Subject: [PATCH] netstack: zero window probes must have a payload Note that this implements Linux-like behavior that differs from RFC 9293 3.8.6.1. We do not send the next byte in the stream as a probe. We instead use a previously ACKed sequence number with a junk payload that the receiver must respond to. Fixes #10243. PiperOrigin-RevId: 622966365 --- pkg/tcpip/transport/tcp/snd.go | 20 +++++++++++++++++--- pkg/tcpip/transport/tcp/test/e2e/tcp_test.go | 2 +- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/pkg/tcpip/transport/tcp/snd.go b/pkg/tcpip/transport/tcp/snd.go index 41b5b534e..f8d699fe4 100644 --- a/pkg/tcpip/transport/tcp/snd.go +++ b/pkg/tcpip/transport/tcp/snd.go @@ -20,6 +20,7 @@ import ( "sort" "time" + "gvisor.dev/gvisor/pkg/buffer" "gvisor.dev/gvisor/pkg/sync" "gvisor.dev/gvisor/pkg/tcpip" "gvisor.dev/gvisor/pkg/tcpip/header" @@ -904,12 +905,25 @@ func (s *sender) maybeSendSegment(seg *segment, limit int, end seqnum.Value) (se return true } +// zeroProbeJunk is data sent during zero window probes. Its value is +// irrelevant; since the sequence number has already been acknowledged it will +// be discarded. It's only here to avoid allocating. +var zeroProbeJunk = []byte{0} + // +checklocks:s.ep.mu func (s *sender) sendZeroWindowProbe() { s.unackZeroWindowProbes++ - // Send a zero window probe with sequence number pointing to - // the last acknowledged byte. - s.sendEmptySegment(header.TCPFlagAck, s.SndUna-1) + + // Send a zero window probe with sequence number pointing to the last + // acknowledged byte. Note that, like Linux, this isn't quite what RFC + // 9293 3.8.6.1 describes: we don't send the next byte in the stream, + // we re-send an ACKed byte to goad the receiver into responding. + pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{ + Payload: buffer.MakeWithData(zeroProbeJunk), + }) + defer pkt.DecRef() + s.sendSegmentFromPacketBuffer(pkt, header.TCPFlagAck, s.SndUna-1) + // Rearm the timer to continue probing. s.resendTimer.enable(s.RTO) } diff --git a/pkg/tcpip/transport/tcp/test/e2e/tcp_test.go b/pkg/tcpip/transport/tcp/test/e2e/tcp_test.go index 4b863b581..8140106f0 100644 --- a/pkg/tcpip/transport/tcp/test/e2e/tcp_test.go +++ b/pkg/tcpip/transport/tcp/test/e2e/tcp_test.go @@ -2914,7 +2914,7 @@ func TestZeroWindowSend(t *testing.T) { defer b.Release() iss := seqnum.Value(context.TestInitialSequenceNumber).Add(1) checker.IPv4(t, b, - checker.PayloadLen(header.TCPMinimumSize), + checker.PayloadLen(header.TCPMinimumSize+1), checker.TCP( checker.DstPort(context.TestPort), checker.TCPSeqNum(uint32(c.IRS)),