mirror of
https://github.com/slackhq/nebula.git
synced 2026-08-16 15:17:02 +02:00
overlay/tio: derive WriteGSO geometry from non-empty fragments
GSOSize came from len(pays[0]) while the iovec build skips empty fragments, so a leading empty fragment emitted a TSO/USO header with gso_size == 0 -- virtio_net_hdr_to_skb rejects that with EINVAL and the whole superpacket is lost. Compute gso_size from the first non-empty fragment and use the non-empty count to decide superpacket vs plain. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -334,14 +334,28 @@ func (r *Offload) WriteGSO(hdr []byte, transportHdr []byte, pays [][]byte, proto
|
|||||||
default:
|
default:
|
||||||
csumOff = 16
|
csumOff = 16
|
||||||
}
|
}
|
||||||
|
// GSO geometry comes from the non-empty fragments only: the iovec loop
|
||||||
|
// below skips empties, so gso_size must never be derived from one. A
|
||||||
|
// leading empty fragment would otherwise stamp a superpacket header
|
||||||
|
// with gso_size == 0, which the kernel rejects with EINVAL.
|
||||||
|
segSize, segCount := 0, 0
|
||||||
|
for _, p := range pays {
|
||||||
|
if len(p) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if segCount == 0 {
|
||||||
|
segSize = len(p)
|
||||||
|
}
|
||||||
|
segCount++
|
||||||
|
}
|
||||||
vhdr := virtio.Hdr{
|
vhdr := virtio.Hdr{
|
||||||
Flags: unix.VIRTIO_NET_HDR_F_NEEDS_CSUM,
|
Flags: unix.VIRTIO_NET_HDR_F_NEEDS_CSUM,
|
||||||
HdrLen: uint16(len(hdr) + len(transportHdr)),
|
HdrLen: uint16(len(hdr) + len(transportHdr)),
|
||||||
GSOSize: uint16(len(pays[0])),
|
GSOSize: uint16(segSize),
|
||||||
CsumStart: uint16(len(hdr)),
|
CsumStart: uint16(len(hdr)),
|
||||||
CsumOffset: csumOff,
|
CsumOffset: csumOff,
|
||||||
}
|
}
|
||||||
if len(pays) > 1 {
|
if segCount > 1 {
|
||||||
ipVer := hdr[0] >> 4
|
ipVer := hdr[0] >> 4
|
||||||
switch {
|
switch {
|
||||||
case proto == GSOProtoUDP && (ipVer == 4 || ipVer == 6):
|
case proto == GSOProtoUDP && (ipVer == 4 || ipVer == 6):
|
||||||
|
|||||||
@@ -866,3 +866,46 @@ func TestOffloadWriteZeroLength(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestWriteGSOLeadingEmptyFragmentGeometry: gso_size must be derived from
|
||||||
|
// the first non-empty fragment. Deriving it from pays[0] stamped a
|
||||||
|
// superpacket header with gso_size == 0 when the leading fragment was
|
||||||
|
// empty -- the kernel rejects that with EINVAL and the burst is lost.
|
||||||
|
// Write through a pipe and decode the vnet header the kernel would see.
|
||||||
|
func TestWriteGSOLeadingEmptyFragmentGeometry(t *testing.T) {
|
||||||
|
var pfds [2]int
|
||||||
|
if err := unix.Pipe(pfds[:]); err != nil {
|
||||||
|
t.Fatalf("pipe: %v", err)
|
||||||
|
}
|
||||||
|
t.Cleanup(func() { unix.Close(pfds[0]); unix.Close(pfds[1]) })
|
||||||
|
|
||||||
|
o := &Offload{fd: pfds[1], gsoIovs: make([]unix.Iovec, 2, gsoMaxIovs)}
|
||||||
|
o.gsoIovs[0].Base = &o.gsoHdrBuf[0]
|
||||||
|
o.gsoIovs[0].SetLen(virtio.Size)
|
||||||
|
|
||||||
|
ipHdr := make([]byte, 20)
|
||||||
|
ipHdr[0] = 0x45
|
||||||
|
udpHdr := make([]byte, 8)
|
||||||
|
seg := make([]byte, 1200)
|
||||||
|
|
||||||
|
if err := o.WriteGSO(ipHdr, udpHdr, [][]byte{{}, seg, seg}, GSOProtoUDP); err != nil {
|
||||||
|
t.Fatalf("WriteGSO with leading empty fragment: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]byte, virtio.Size+len(ipHdr)+len(udpHdr)+2*len(seg)+64)
|
||||||
|
n, err := unix.Read(pfds[0], buf)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("read pipe: %v", err)
|
||||||
|
}
|
||||||
|
var vhdr virtio.Hdr
|
||||||
|
vhdr.Decode(buf[:virtio.Size])
|
||||||
|
if vhdr.GSOType != unix.VIRTIO_NET_HDR_GSO_UDP_L4 {
|
||||||
|
t.Errorf("GSOType=%d want UDP_L4", vhdr.GSOType)
|
||||||
|
}
|
||||||
|
if vhdr.GSOSize != 1200 {
|
||||||
|
t.Errorf("GSOSize=%d want 1200 (first non-empty fragment)", vhdr.GSOSize)
|
||||||
|
}
|
||||||
|
if want := virtio.Size + len(ipHdr) + len(udpHdr) + 2*len(seg); n != want {
|
||||||
|
t.Errorf("wrote %d bytes want %d (empty fragment must not add an iovec)", n, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user