From 607d01cdaee918d801157ac110530b4b92d3d11c Mon Sep 17 00:00:00 2001 From: Jordan Whited Date: Wed, 11 Mar 2026 10:13:49 -0700 Subject: [PATCH] net/batching: clarify & simplify single packet read limitations ReadFromUDPAddrPort worked if UDP GRO was unsupported, but we don't actually want attempted usage, nor does any exist today. Future work on tailscale/corp#37679 would have required more complexity in this method, vs clarifying the API intents. Updates tailscale/corp#37679 Signed-off-by: Jordan Whited --- net/batching/conn.go | 12 +++++++++++- net/batching/conn_linux.go | 11 +---------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/net/batching/conn.go b/net/batching/conn.go index 1631c33cf..1843a2cfc 100644 --- a/net/batching/conn.go +++ b/net/batching/conn.go @@ -19,14 +19,24 @@ var ( _ ipv6.Message = ipv4.Message{} ) -// Conn is a nettype.PacketConn that provides batched i/o using +// Conn is a [nettype.PacketConn] that provides batched i/o using // platform-specific optimizations, e.g. {recv,send}mmsg & UDP GSO/GRO. // +// Conn does not support single packet reads (see ReadFromUDPAddrPort docs). It +// is the caller's responsibility to use the appropriate read API where a +// [nettype.PacketConn] has been upgraded to support batched i/o. +// // Conn originated from (and is still used by) magicsock where its API was // strongly influenced by [wireguard-go/conn.Bind] constraints, namely // wireguard-go's ownership of packet memory. type Conn interface { nettype.PacketConn + // ReadFromUDPAddrPort always returns an error, as UDP GRO is incompatible + // with single packet reads. A single datagram may be multiple, coalesced + // datagrams, and this API lacks the ability to pass that context. + // + // TODO: consider detaching Conn from [nettype.PacketConn] + ReadFromUDPAddrPort([]byte) (int, netip.AddrPort, error) // ReadBatch reads messages from [Conn] into msgs. It returns the number of // messages the caller should evaluate for nonzero len, as a zero len // message may fall on either side of a nonzero. diff --git a/net/batching/conn_linux.go b/net/batching/conn_linux.go index 373625b77..70f91cfb6 100644 --- a/net/batching/conn_linux.go +++ b/net/batching/conn_linux.go @@ -61,16 +61,7 @@ type linuxBatchingConn struct { } func (c *linuxBatchingConn) ReadFromUDPAddrPort(p []byte) (n int, addr netip.AddrPort, err error) { - if c.rxOffload { - // UDP_GRO is opt-in on Linux via setsockopt(). Once enabled you may - // receive a "monster datagram" from any read call. The ReadFrom() API - // does not support passing the GSO size and is unsafe to use in such a - // case. Other platforms may vary in behavior, but we go with the most - // conservative approach to prevent this from becoming a footgun in the - // future. - return 0, netip.AddrPort{}, errors.New("rx UDP offload is enabled on this socket, single packet reads are unavailable") - } - return c.pc.ReadFromUDPAddrPort(p) + return 0, netip.AddrPort{}, errors.New("single packet reads are unsupported") } func (c *linuxBatchingConn) SetDeadline(t time.Time) error {