{"record":{"id":"979df23f0f73dbf0","repo":"slackhq/nebula","slug":"unable-to-determine-ip-version-from-packet-979df2","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":"warning","filePath":"overlay/tun_darwin.go","lineNumber":569,"sourceCode":"}\n\n// Write pushes one IP packet onto the utun device. Safe for concurrent use:\n// the AF prefix and iovecs are per-call stack state, and the fd write itself\n// serializes on the runtime's fd mutex (see the Queue contract in tio.go).\nfunc (t *tun) Write(from []byte) (int, error) {\n\tif len(from) == 0 {\n\t\treturn 0, syscall.EIO\n\t}\n\n\tipVer := from[0] >> 4\n\tvar head [4]byte\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\t// Grab rc as a local so the compiler can devirtualize the call and keep the closure on the stack.\n\trc, err := t.f.SyscallConn()\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\tvar n int\n\tvar callErr error\n\terr = rc.Write(func(fd uintptr) bool {\n\t\tiovecs := []unix.Iovec{\n\t\t\t{Base: &head[0], Len: 4},\n\t\t\t{Base: &from[0], Len: uint64(len(from))},\n\t\t}\n\t\tn, callErr = tunWritev(int(fd), iovecs)\n\t\t// Type-assert to syscall.Errno so the EAGAIN/EWOULDBLOCK/EINTR check doesn't box the errno\n\t\t// constants into error interfaces on every call.","sourceCodeStart":551,"sourceCodeEnd":587,"githubUrl":"https://github.com/slackhq/nebula/blob/dd8f660c0ac37903ec4080ca4d3c861ba9342ceb/overlay/tun_darwin.go#L551-L587","documentation":"tun.Write() prepends a 4-byte Darwin utun protocol header whose 4th byte must be the address family (AF_INET or AF_INET6) of the packet being injected into the interface. This error is returned when the first nibble of the packet is neither 4 nor 6, so nebula cannot determine which family byte to write into the utun header.","triggerScenarios":"Write() called with a packet whose first byte's high nibble is not 0x4 (IPv4) or 0x6 (IPv6) — i.e. corrupted, truncated, non-IP, or garbage data handed to the tun writer.","commonSituations":"Packet corruption or memory issues upstream; a bug in another component feeding non-IP frames into the tun writer; encrypted/garbage payloads misrouted to the tun; truncated packets read from the wire.","solutions":["Treat the packet as undeliverable and drop it (the error is per-packet; nebula continues serving traffic).","Check the upstream path for corruption: verify MTU settings match across the tunnel (tun.mtu) so packets aren't truncated.","Ensure you're on a current nebula version; upstream fixes handle malformed packets more gracefully.","If running an embedded/custom build, confirm the caller of tun.Write only passes full IP packets, not link-layer frames."],"exampleFix":"// before: writing raw frames\ntun.Write(ethernetFrame)\n// after: strip the link layer and write full IP packets only\nipPacket := stripEthernetHeader(ethernetFrame)\ntun.Write(ipPacket)","handlingStrategy":"validation","validationCode":"func isIPPacket(b []byte) bool {\n    if len(b) < 1 {\n        return false\n    }\n    switch b[0] >> 4 {\n    case 4, 6:\n        return true\n    default:\n        return false\n    }\n}\n// call before handing data to the tun writer","typeGuard":"func isIPPacket(b []byte) bool {\n    return len(b) >= 1 && (b[0]>>4 == 4 || b[0]>>4 == 6)\n}","tryCatchPattern":"n, err := iface.Write(packet)\nif err != nil {\n    if strings.Contains(err.Error(), \"unable to determine IP version from packet\") {\n        log.Debug(\"dropping non-IP packet\", \"len\", len(packet))\n        return // per-packet error; do not crash\n    }\n    return err\n}","preventionTips":["Only pass full IP packets (not Ethernet frames) to tun.Write","Match tun.mtu across the overlay so packets aren't truncated","Keep nebula updated for upstream malformed-packet handling","Log and drop bad packets rather than retrying them"],"tags":["network","darwin","packet","tun-device","malformed-packet"],"backgroundTag":"invalid-ip-packet-version","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"}