slackhq/nebula · warning

packet is too short

Error message

packet is too short

What it means

CorrectHdrLen returns errors.New("packet is too short") when the packet is not long enough to contain the TCP data-offset byte at hdr.CsumStart+tcpDataOffOff, so the transport header length cannot be computed for a GSO packet. This is a sibling guard to errPacketTooShort but is an ad-hoc errors.New raised inside the public CorrectHdrLen path (called by decodeRead).

Source

Thrown at overlay/tio/virtio/segment_linux.go:134

		}
	}

	return nil
}

// CorrectHdrLen rewrites hdr.HdrLen based on the actual transport header length read out of pkt.
// The kernel's hdr.HdrLen on the FORWARD path can be the length of the entire first packet, so we don't trust it.
func CorrectHdrLen(pkt []byte, hdr *Hdr) error {
	// Thank you wireguard-go for documenting these edge-cases
	// Don't trust hdr.hdrLen from the kernel as it can be equal to the length
	// of the entire first packet when the kernel is handling it as part of a FORWARD path.
	// Instead, parse the transport header length and add it onto csumStart, which is synonymous for IP header length.

	if hdr.GSOType() == unix.VIRTIO_NET_HDR_GSO_UDP_L4 {
		hdr.HdrLen = hdr.CsumStart + 8
	} else {
		if len(pkt) <= int(hdr.CsumStart+tcpDataOffOff) {
			return errors.New("packet is too short")
		}

		tcpHLen := uint16(pkt[hdr.CsumStart+tcpDataOffOff] >> 4 * 4)
		if tcpHLen < tcpHeaderMinLen || tcpHLen > tcpHeaderMaxLen {
			return fmt.Errorf("tcp header len is invalid: %d", tcpHLen)
		}
		hdr.HdrLen = hdr.CsumStart + tcpHLen
	}

	if len(pkt) < int(hdr.HdrLen) {
		return fmt.Errorf("length of packet (%d) < virtioNetHdr.HdrLen (%d)", len(pkt), hdr.HdrLen)
	}

	if hdr.HdrLen < hdr.CsumStart {
		return fmt.Errorf("virtioNetHdr.HdrLen (%d) < virtioNetHdr.CsumStart (%d)", hdr.HdrLen, hdr.CsumStart)
	}
	cSumAt := int(hdr.CsumStart + hdr.CsumOffset)
	if cSumAt+1 >= len(pkt) {

View on GitHub (pinned to dd8f660c0a)

Solutions

  1. Validate len(pkt) against hdr.CsumStart + TCP header start before calling CorrectHdrLen.
  2. Verify CsumStart/HdrLen values in the virtio header are sane for the received frame.
  3. Drop the packet and continue the read loop instead of propagating the error up decodeRead.
  4. Consolidate with errPacketTooShort sentinel so callers can errors.Is-match instead of string comparison.
  5. Capture the packet hex dump on occurrence to identify the traffic source producing short frames.

Example fix

// before: ad-hoc error string
return errors.New("packet is too short")
// after: sentinel error comparable with errors.Is
var errPacketTooShort = errors.New("packet is too short")
if len(pkt) <= int(hdr.CsumStart+tcpDataOffOff) {
    return errPacketTooShort
}
Defensive patterns

Strategy: validation

Validate before calling

if len(pkt) <= int(hdr.CsumStart+tcpDataOffOff) {
    stats.Malformed++
    return // cannot read TCP data offset
}

Try / catch

if err := seg.CorrectHdrLen(pkt, hdr); err != nil {
    if strings.Contains(err.Error(), "packet is too short") {
        stats.Malformed++
        continue // drop frame, keep read loop alive
    }
    return err
}

Prevention

When it happens

Trigger: CorrectHdrLen is called on a packet where len(pkt) <= int(hdr.CsumStart+tcpDataOffOff), i.e. the buffer ends before the TCP header's data-offset field. Reached via decodeRead when a received GSO frame is truncated or CsumStart is bogus.

Common situations: Malformed packets delivered on the Rx path with inconsistent virtio header metadata; truncated reads from the tun/tap device; CsumStart miscomputed by an earlier parse; hostile local traffic crafting short frames.

Related errors


AI-assisted analysis of slackhq/nebula@dd8f660c0a (2026-09-03). Data as JSON: /api/errors/22cce0bb82e9febf. Report an issue: GitHub.