{"record":{"id":"4b71a10a80175a25","repo":"slackhq/nebula","slug":"unable-to-determine-ip-version-from-packet-4b71a1","errorCode":null,"errorMessage":"unable to determine IP version from packet","messagePattern":"unable to determine IP version from packet","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"overlay/tun_freebsd.go","lineNumber":205,"sourceCode":"\t}\n}\n\n// Write is only valid for single threaded use\nfunc (t *tun) Write(from []byte) (int, error) {\n\tif len(from) <= 1 {\n\t\treturn 0, syscall.EIO\n\t}\n\n\tipVer := from[0] >> 4\n\tvar head [4]byte\n\t// first 4 bytes is protocol family, in network byte order\n\tswitch ipVer {\n\tcase 4:\n\t\thead[3] = syscall.AF_INET\n\tcase 6:\n\t\thead[3] = syscall.AF_INET6\n\tdefault:\n\t\treturn 0, fmt.Errorf(\"unable to determine IP version from packet\")\n\t}\n\n\tiovecs := [2]syscall.Iovec{\n\t\t{&head[0], 4},\n\t\t{&from[0], uint64(len(from))},\n\t}\n\tfor {\n\t\tn, _, errno := syscall.Syscall(syscall.SYS_WRITEV, uintptr(t.fd), uintptr(unsafe.Pointer(&iovecs[0])), 2)\n\t\tif errno == 0 {\n\t\t\treturn int(n) - 4, nil\n\t\t}\n\t\tswitch errno {\n\t\tcase unix.EAGAIN:\n\t\t\tif err := t.blockOnWrite(); err != nil {\n\t\t\t\treturn 0, err\n\t\t\t}\n\t\tcase unix.EINTR:\n\t\t\t// retry","sourceCodeStart":187,"sourceCodeEnd":223,"githubUrl":"https://github.com/slackhq/nebula/blob/dd8f660c0ac37903ec4080ca4d3c861ba9342ceb/overlay/tun_freebsd.go#L187-L223","documentation":"The tun device write path (tun.Write on FreeBSD) prepends a 4-byte BSD header whose byte 3 encodes the address family (AF_INET or AF_INET6). The first byte of the outgoing packet is inspected to determine the IP version; if it is neither 4 nor 6 the packet is malformed and the write fails with this error. The library refuses to guess the family because writing it into the tun header incorrectly would corrupt routing on the FreeBSD tun interface.","triggerScenarios":"Calling Write() on the FreeBSD tun with a buffer that does not begin with a valid IP header — the first nibble of the first byte must be 0x4 (IPv4) or 0x6 (IPv6). This happens with zero-length packets, packets containing Ethernet frames instead of raw IP, or corrupted/partial buffers.","commonSituations":"Passing L2 (Ethernet-framed) traffic from a layer-2 VPN mode into an L3 tun; reading packets from a socket and slicing off too many header bytes; a peer sending non-IP garbage over a raw channel; an empty or uninitialized buffer after a failed read upstream.","solutions":["Verify the packet buffer begins with a valid IP version nibble before calling Write: (b[0]>>4) must equal 4 or 6","If your source traffic is Ethernet-framed, strip the 14-byte Ethernet header and pass only the IP payload","Check upstream code for off-by-one slicing that shifts the IP header out of position","Drop the malformed packet and log it instead of propagating an error into the write loop"],"exampleFix":"// before\n_, err := tunDev.Write(buf)\n// after\nif len(buf) == 0 || (buf[0]>>4 != 4 && buf[0]>>4 != 6) {\n    log.Warn(\"dropping non-IP packet\", \"firstByte\", buf)\n    continue\n}\n_, err := tunDev.Write(buf)","handlingStrategy":"validation","validationCode":"func isValidIPPacket(b []byte) bool {\n    return len(b) >= 1 && (b[0]>>4 == 4 || b[0]>>4 == 6)\n}\n// call site:\nif !isValidIPPacket(buf) { log.Warn(\"drop non-IP packet\"); continue }","typeGuard":"func isIPv4(b []byte) bool { return len(b) >= 20 && b[0]>>4 == 4 }\nfunc isIPv6(b []byte) bool { return len(b) >= 40 && b[0]>>4 == 6 }","tryCatchPattern":"n, err := tunDev.Write(buf)\nif err != nil {\n    if strings.Contains(err.Error(), \"unable to determine IP version\") {\n        log.Warn(\"skipping malformed packet\", \"err\", err)\n        continue\n    }\n    return err\n}","preventionTips":["Validate the first nibble of every packet before writing to the tun","Strip Ethernet headers from any L2-sourced traffic before handing it to an L3 tun","Never write empty or partially-read buffers","Log and drop malformed packets instead of failing the write loop"],"tags":["freebsd","tun","packet-format","ipv6","network"],"backgroundTag":"invalid-ip-packet-header","analyzedSha":"dd8f660c0ac37903ec4080ca4d3c861ba9342ceb","analyzedAt":"2026-09-03T11:13:55.444Z","contentChangedAt":"2026-09-03T11:13:55.444Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}