{"record":{"id":"3cab59bc078f8f7f","repo":"netbirdio/netbird","slug":"mixed-address-families-src-s-dst-s","errorCode":null,"errorMessage":"mixed address families: src=%s dst=%s","messagePattern":"mixed address families: src=(.+?) dst=(.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/firewall/uspfilter/tracer.go","lineNumber":139,"sourceCode":"\tpktLayers := []gopacket.SerializableLayer{ipLayer}\n\n\ttransportLayer, err := p.buildTransportLayer(ipLayer)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tpktLayers = append(pktLayers, transportLayer...)\n\n\tif p.PayloadSize > 0 {\n\t\tpayload := make([]byte, p.PayloadSize)\n\t\tpktLayers = append(pktLayers, gopacket.Payload(payload))\n\t}\n\n\treturn serializePacket(pktLayers)\n}\n\nfunc (p *PacketBuilder) buildIPLayer() (gopacket.SerializableLayer, error) {\n\tif p.SrcIP.Is4() != p.DstIP.Is4() {\n\t\treturn nil, fmt.Errorf(\"mixed address families: src=%s dst=%s\", p.SrcIP, p.DstIP)\n\t}\n\tproto := getIPProtocolNumber(p.Protocol, p.SrcIP.Is6())\n\tif p.SrcIP.Is6() {\n\t\treturn &layers.IPv6{\n\t\t\tVersion:    6,\n\t\t\tHopLimit:   64,\n\t\t\tNextHeader: proto,\n\t\t\tSrcIP:      p.SrcIP.AsSlice(),\n\t\t\tDstIP:      p.DstIP.AsSlice(),\n\t\t}, nil\n\t}\n\treturn &layers.IPv4{\n\t\tVersion:  4,\n\t\tTTL:      64,\n\t\tProtocol: proto,\n\t\tSrcIP:    p.SrcIP.AsSlice(),\n\t\tDstIP:    p.DstIP.AsSlice(),\n\t}, nil","sourceCodeStart":121,"sourceCodeEnd":157,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/firewall/uspfilter/tracer.go#L121-L157","documentation":"The tracer's PacketBuilder refuses to synthesize an IP header when source and destination addresses belong to different address families (one Is4, the other not). gopacket cannot build a coherent IPv4 or IPv6 layer from mixed inputs, and the checksum/protocol selection logic (getIPProtocolNumber with Is6) would be ambiguous. Notably, an unset netip.Addr is neither Is4 nor Is6, so leaving DstIP (or SrcIP) at its zero value while setting the other to a real address also trips this check.","triggerScenarios":"Building a trace packet with an IPv4 SrcIP and an IPv6 DstIP or vice versa; omitting one of the two addresses so it stays as the invalid zero-value netip.Addr; copying addresses from a config map where one side was parsed with a different family than the other.","commonSituations":"Writing new trace/troubleshooting code on top of the uspfilter tracer and forgetting to set both endpoints; mixing a peer's v6 overlay address with the local v4 interface address in diagnostics tooling; parsing one address with netip.MustParseAddr and the other from a legacy net.IP slice of 16-byte v4-mapped form that was never Unmap()-ed.","solutions":["Validate both addresses before calling Build: ensure SrcIP.IsValid(), DstIP.IsValid(), and SrcIP.Is4() == DstIP.Is4()","Normalize inputs with Unmap() at the boundary so a v4-mapped v6 address becomes plain v4 and families line up","Default the unset side explicitly (e.g. copy the interface family) instead of leaving the zero value","Fail early at the API edge of your tooling with a clear message about which side is missing"],"exampleFix":"// before\nb := &tracer.PacketBuilder{SrcIP: localV4, DstIP: peerAddr, ...}\ndata, err := b.Build()\n\n// after\nif !b.SrcIP.IsValid() || !b.DstIP.IsValid() || b.SrcIP.Is4() != b.DstIP.Is4() {\n    return fmt.Errorf(\"trace endpoints must be valid and same-family: src=%s dst=%s\", b.SrcIP, b.DstIP)\n}\npeerAddr = peerAddr.Unmap()\ndata, err := b.Build()","handlingStrategy":"validation","validationCode":"func sameFamily(a, b netip.Addr) bool {\n    return a.IsValid() && b.IsValid() && a.Is4() == b.Is4()\n}\n\nif !sameFamily(b.SrcIP, b.DstIP) {\n    return fmt.Errorf(\"trace needs same-family endpoints: %s -> %s\", b.SrcIP, b.DstIP)\n}\ntrace, err := m.TracePacketFromBuilder(b)","typeGuard":"func validTraceAddrs(src, dst netip.Addr) bool {\n    return src.IsValid() && dst.IsValid() && src.Is4() == dst.Is4()\n}","tryCatchPattern":"if _, err := m.TracePacketFromBuilder(b); err != nil {\n    if strings.Contains(err.Error(), \"mixed address families\") {\n        // fix address selection (e.g. pick the v6 peer address for a v6 trace)\n    }\n}","preventionTips":["Unmap() every parsed address at the boundary so v4-mapped v6 never reaches the builder","Reject zero-value addresses early: unset netip.Addr is neither v4 nor v6","Default the missing endpoint from the interface address family instead of leaving it unset"],"tags":["go","netbird","gopacket","ipv6","validation","tracer"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}