From edd7fd2e6022e209c10d40eb594fc0ffb22b8bab Mon Sep 17 00:00:00 2001 From: Kevin Krakauer Date: Fri, 17 Mar 2023 12:10:50 -0700 Subject: [PATCH] gro: fix bug where handshake packets would be stuck waiting in GRO GRO would not immediately flush the final ACK in the SYN-SYN/ACK-ACK handshake. This could lead to a situation where - Client A calls connect(), which returns once the final ACK of the handshake is sent and A reaches state ESTABLISHED - The ACK gets GRO'd, delaying it from reaching the server - Client B calls non-blocking connect() - Client B's ACK gets GRO'd as well - Client B is marked as ESTABLISHED - The server, with accept queue size 1, is only going to accept one connection, but two clients are ESTABLISHED. It now immediately flushes packets with no payload, as they are important to TCP connection state and management. PiperOrigin-RevId: 517474519 --- pkg/tcpip/stack/gro.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/tcpip/stack/gro.go b/pkg/tcpip/stack/gro.go index a3b708fd5..29a1b7061 100644 --- a/pkg/tcpip/stack/gro.go +++ b/pkg/tcpip/stack/gro.go @@ -226,6 +226,8 @@ func (gb *groBucket) found(gd *groDispatcher, groPkt *groPacket, flushGROPkt boo // Flush groPkt or merge the packets. pktSize := pkt.Data().Size() flags := tcpHdr.Flags() + dataOff := tcpHdr.DataOffset() + tcpPayloadSize := pkt.Data().Size() - len(ipHdr) - int(dataOff) if flushGROPkt { // Flush the existing GRO packet. Don't hold bucket.mu while // processing the packet. @@ -239,12 +241,10 @@ func (gb *groBucket) found(gd *groDispatcher, groPkt *groPacket, flushGROPkt boo } else if groPkt != nil { // Merge pkt in to GRO packet. buf := pkt.Data().ToBuffer() - dataOff := tcpHdr.DataOffset() buf.TrimFront(int64(len(ipHdr)) + int64(dataOff)) groPkt.pkt.Data().MergeBuffer(&buf) buf.Release() // Update the IP total length. - tcpPayloadSize := pkt.Data().Size() - len(ipHdr) - int(dataOff) updateIPHdr(groPkt.ipHdr, tcpPayloadSize) // Add flags from the packet to the GRO packet. groPkt.tcpHdr.SetFlags(uint8(groPkt.tcpHdr.Flags() | (flags & (header.TCPFlagFin | header.TCPFlagPsh)))) @@ -261,6 +261,7 @@ func (gb *groBucket) found(gd *groDispatcher, groPkt *groPacket, flushGROPkt boo // malformed, a local GSO packet, or has already been handled by host // GRO. flush := header.TCPFlags(flags)&(header.TCPFlagUrg|header.TCPFlagPsh|header.TCPFlagRst|header.TCPFlagSyn|header.TCPFlagFin) != 0 + flush = flush || tcpPayloadSize == 0 if groPkt != nil { flush = flush || pktSize != groPkt.initialLength }