{"record":{"id":"54517485e37a9721","repo":"slackhq/nebula","slug":"unable-to-determine-ip-version-from-packet","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_ios.go","lineNumber":142,"sourceCode":"\t\treturn 0, syscall.EIO\n\t}\n\n\ttr.wMu.Lock()\n\tdefer tr.wMu.Unlock()\n\n\tif cap(tr.wBuf) < len(from)+4 {\n\t\ttr.wBuf = make([]byte, len(from)+4)\n\t}\n\ttr.wBuf = tr.wBuf[:len(from)+4]\n\n\t// Determine the IP Family for the NULL L2 Header\n\tipVer := from[0] >> 4\n\tif ipVer == 4 {\n\t\ttr.wBuf[3] = syscall.AF_INET\n\t} else if ipVer == 6 {\n\t\ttr.wBuf[3] = syscall.AF_INET6\n\t} else {\n\t\treturn 0, errors.New(\"unable to determine IP version from packet\")\n\t}\n\n\tcopy(tr.wBuf[4:], from)\n\n\tn, err := tr.f.Write(tr.wBuf)\n\treturn n - 4, err\n}\n\nfunc (tr *tunReadCloser) Close() error {\n\treturn tr.f.Close()\n}\n\nfunc (t *tun) Networks() []netip.Prefix {\n\treturn t.vpnNetworks\n}\n\nfunc (t *tun) Name() string {\n\treturn \"iOS\"","sourceCodeStart":124,"sourceCodeEnd":160,"githubUrl":"https://github.com/slackhq/nebula/blob/dd8f660c0ac37903ec4080ca4d3c861ba9342ceb/overlay/tun_ios.go#L124-L160","documentation":"The iOS tun transport's Write inspects the first nibble of the outgoing packet to select the address family (AF_INET/AF_INET6) stamped into the 4-byte write header. If the first byte's high nibble is neither 4 nor 6, the packet is not recognizable IP and Write returns errors.New(\"unable to determine IP version from packet\") with 0 bytes written.","triggerScenarios":"tun_ios Write (overlay/tun_ios.go:142) receives a from []byte whose first byte's top nibble is not 4 or 6 — e.g. empty/garbage buffer, non-IP frame, or a caller passing the wrong buffer to Write.","commonSituations":"Writing non-IP link-layer frames (ARP-like or vendor frames) to the tun; a preceding read returned a zero-length or corrupted buffer that was echoed back; slicing errors handing Write a wrong-offset buffer; iOS network extension receiving unexpected datagrams from NWPath/pcap-style sources.","solutions":["Check from[0]>>4 is 4 or 6 (and len(from) > 0) before calling Write and drop invalid packets.","Audit callers of Write to ensure they pass the start of an IP datagram, not a link-layer or offset frame.","Add validation on the read path so only well-formed IP packets are ever queued for writing.","Log the first bytes of offending packets to identify the source of non-IP frames.","Return io.ErrBadRequest-style sentinel so callers can distinguish this from write I/O errors."],"exampleFix":"// before: unconditional write\nn, err := tw.Write(buf)\n// after: pre-validate IP version\nif len(buf) == 0 || (buf[0]>>4 != 4 && buf[0]>>4 != 6) {\n    return // drop non-IP frame\n}\nn, err := tw.Write(buf)","handlingStrategy":"validation","validationCode":"func isIPPacket(b []byte) bool {\n    if len(b) == 0 {\n        return false\n    }\n    switch b[0] >> 4 {\n    case 4, 6:\n        return true\n    }\n    return false\n}\nif !isIPPacket(buf) {\n    return // drop; do not call tun Write\n}","typeGuard":null,"tryCatchPattern":"n, err := tw.Write(pkt)\nif err != nil && strings.Contains(err.Error(), \"unable to determine IP version\") {\n    log.Debug(\"dropping non-IP frame on ios tun write\", \"len\", len(pkt))\n    return nil\n} else if err != nil {\n    return err\n}","preventionTips":["Only queue buffers that begin with a valid IP version nibble toward Write.","Never echo zero-length or malformed read buffers back through Write.","Keep the write path IP-datagram-only; strip or handle any link-layer framing upstream.","Log the first bytes of rejected frames to spot sources of non-IP traffic."],"tags":["ios","tun","ipv4","ipv6","packet-parsing"],"backgroundTag":"packet-too-short","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"}