diff --git a/pkg/tcpip/transport/tcp/rack.go b/pkg/tcpip/transport/tcp/rack.go index 66ea6e5b0..35e6cc725 100644 --- a/pkg/tcpip/transport/tcp/rack.go +++ b/pkg/tcpip/transport/tcp/rack.go @@ -357,11 +357,13 @@ func (rc *rackControl) detectLoss(rcvTime tcpip.MonotonicTime) int { var timeout time.Duration numLost := 0 for seg := rc.snd.writeList.Front(); seg != nil && seg.xmitCount != 0; seg = seg.Next() { - if rc.snd.ep.scoreboard.IsSACKED(seg.sackBlock()) { + // xmitCount can be 0 for packets that are broken up for PMTUD. + // The initial transmission "doesn't count" WRT loss detection. + if rc.snd.ep.scoreboard.IsSACKED(seg.sackBlock()) || seg.xmitCount == 0 { continue } - if seg.lost && seg.xmitCount == 1 { + if seg.lost && seg.xmitCount > 1 { numLost++ continue } diff --git a/pkg/tcpip/transport/tcp/snd.go b/pkg/tcpip/transport/tcp/snd.go index 5dccf81d1..4e51d8b03 100644 --- a/pkg/tcpip/transport/tcp/snd.go +++ b/pkg/tcpip/transport/tcp/snd.go @@ -342,10 +342,16 @@ func (s *sender) updateMaxPayloadSize(mtu, count int) { break } - if nextSeg == s.writeNext && seg.payloadSize() > m { - // We found a segment exceeding the MTU. Rewind - // writeNext and try to retransmit it. - nextSeg = seg + if seg.payloadSize() > m { + // xmitCount is used for loss detection, but + // retransmission doesn't indicate congestion here, + // it's just PMTUD. + seg.xmitCount = 0 + if nextSeg == s.writeNext { + // We found a segment exceeding the MTU. Rewind + // writeNext and try to retransmit it. + nextSeg = seg + } } if s.ep.SACKPermitted && s.ep.scoreboard.IsSACKED(seg.sackBlock()) { diff --git a/pkg/tcpip/transport/tcp/test/e2e/tcp_test.go b/pkg/tcpip/transport/tcp/test/e2e/tcp_test.go index 9dcb8de85..5981be60f 100644 --- a/pkg/tcpip/transport/tcp/test/e2e/tcp_test.go +++ b/pkg/tcpip/transport/tcp/test/e2e/tcp_test.go @@ -5739,6 +5739,7 @@ func TestPathMTUDiscovery(t *testing.T) { } checker.IPv4(t, p, checker.PayloadLen(size+header.TCPMinimumSize), + checker.FragmentFlags(header.IPv4FlagDontFragment), checker.TCP( checker.DstPort(context.TestPort), checker.TCPSeqNum(seqNum),