tio: accept VIRTIO_NET_HDR_GSO_ECN-qualified superpackets

TUN_F_TSO_ECN is negotiated, so once ECN feedback flows the kernel hands
us TSO superpackets typed TCPV4|GSO_ECN (CWR set). protoFromGSOType
treated the qualifier bit as an unknown type and the read path dropped
every such superpacket - a latent bug that only fires when a congested
hop CE-marks the flow, exactly when drops hurt most. The segmenter
already handles CWR (first segment only); just mask the bit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
JackDoan
2026-07-14 17:08:12 -05:00
parent 3e2a4499a5
commit f87c48c6c6
2 changed files with 38 additions and 1 deletions
+8 -1
View File
@@ -14,8 +14,15 @@ import (
// protoFromGSOType maps a virtio_net_hdr GSOType to the GSOProto value the
// segment-time helpers use. Returns an error for GSO_NONE or any unknown
// value — the caller should only invoke this on a confirmed superpacket.
//
// VIRTIO_NET_HDR_GSO_ECN is a qualifier bit, not a type: it marks a TSO
// superpacket whose TCP header has CWR set (SKB_GSO_TCP_ECN) — we asked for
// these via TUN_F_TSO_ECN. The segmenter already emits CWR on the first
// segment only, so the bit just needs masking here. It only appears when
// ECN feedback is actually flowing (a congested hop CE-marked the flow),
// which is precisely when dropping the sender's superpackets hurts most.
func protoFromGSOType(t uint8) (GSOProto, error) {
switch t {
switch t &^ unix.VIRTIO_NET_HDR_GSO_ECN {
case unix.VIRTIO_NET_HDR_GSO_TCPV4, unix.VIRTIO_NET_HDR_GSO_TCPV6:
return GSOProtoTCP, nil
case unix.VIRTIO_NET_HDR_GSO_UDP_L4: