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
This commit is contained in:
Kevin Krakauer
2023-03-17 12:13:27 -07:00
committed by gVisor bot
parent d3cc1c4136
commit edd7fd2e60
+3 -2
View File
@@ -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
}