netbirdio/netbird · error
set network layer for UDP checksum: %w
Error message
set network layer for UDP checksum: %w
What it means
Same gopacket constraint as the TCP variant: UDP's SetNetworkLayerForChecksum errors when the network layer address size does not match the address family needed for the UDP pseudo-header (4 bytes for IPv4, 16 for IPv6). The tracer sets it so SerializeOptions{ComputeChecksums:true} produces a valid UDP checksum, which matters because many stacks silently drop UDP with a zero/computed-wrong checksum over IPv6.
Source
Thrown at client/firewall/uspfilter/tracer.go:200
PSH: p.TCPState != nil && p.TCPState.PSH,
URG: p.TCPState != nil && p.TCPState.URG,
}
if nl, ok := ipLayer.(gopacket.NetworkLayer); ok {
if err := tcp.SetNetworkLayerForChecksum(nl); err != nil {
return nil, fmt.Errorf("set network layer for TCP checksum: %w", err)
}
}
return []gopacket.SerializableLayer{tcp}, nil
}
func (p *PacketBuilder) buildUDPLayer(ipLayer gopacket.SerializableLayer) ([]gopacket.SerializableLayer, error) {
udp := &layers.UDP{
SrcPort: layers.UDPPort(p.SrcPort),
DstPort: layers.UDPPort(p.DstPort),
}
if nl, ok := ipLayer.(gopacket.NetworkLayer); ok {
if err := udp.SetNetworkLayerForChecksum(nl); err != nil {
return nil, fmt.Errorf("set network layer for UDP checksum: %w", err)
}
}
return []gopacket.SerializableLayer{udp}, nil
}
func (p *PacketBuilder) buildICMPLayer(ipLayer gopacket.SerializableLayer) ([]gopacket.SerializableLayer, error) {
if p.SrcIP.Is6() || p.DstIP.Is6() {
icmp := &layers.ICMPv6{
TypeCode: layers.CreateICMPv6TypeCode(p.ICMPType, p.ICMPCode),
}
if nl, ok := ipLayer.(gopacket.NetworkLayer); ok {
_ = icmp.SetNetworkLayerForChecksum(nl)
}
if p.ICMPType == layers.ICMPv6TypeEchoRequest || p.ICMPType == layers.ICMPv6TypeEchoReply {
echo := &layers.ICMPv6Echo{
Identifier: 1,
SeqNumber: 1,
}View on GitHub (pinned to 93e97f4bf1)
Solutions
- Always call buildUDPLayer with the exact ipLayer produced by buildIPLayer in the same Build invocation
- Validate SrcIP/DstIP family equality up front (the buildIPLayer guard) so mismatched input never reaches layer assembly
- Write a trace test covering UDP over both IPv4 and IPv6 to lock the behavior
- Check the gopacket error detail to see which side (4 vs 16 bytes) is inconsistent
Defensive patterns
Strategy: try-catch
Validate before calling
if b.SrcIP.Is4() != b.DstIP.Is4() || !b.SrcIP.IsValid() {
return errors.New("addresses must be valid and same-family")
} Type guard
func (p *PacketBuilder) udpBuildable() bool {
return p.SrcIP.IsValid() && p.DstIP.IsValid() && p.SrcIP.Is4() == p.DstIP.Is4() &&
(p.Protocol == fw.ProtocolUDP || p.Protocol == fw.ProtocolTCP || p.Protocol == fw.ProtocolICMP)
} Try / catch
if _, err := b.Build(); err != nil {
var se interface{ Error() string }
_ = se
if strings.Contains(err.Error(), "UDP checksum") {
return fmt.Errorf("rebuild packet with matched families: %w", err)
}
return err
} Prevention
- Always pair the UDP layer with the ipLayer from the same Build invocation
- Cover UDP over IPv6 in tests: a wrong pseudo-header is silent until a peer drops the packet
- Normalize all addresses with Unmap() before entering the builder
When it happens
Trigger: Handing the UDP builder an ipLayer whose family differs from the address data used for the ports/addresses, or invoking buildUDPLayer directly with a mismatched layer; realistically only after modifying the layer assembly order in the tracer.
Common situations: Custom diagnostics built on PacketBuilder that assemble layers manually; gopacket dependency upgrades altering NetworkLayer address handling.
Related errors
- set network layer for TCP checksum: %w
- mixed address families: src=%s dst=%s
- serialize packet: %w
- build packet: %w
- set network layer for checksum: %w
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/2f3ef103191795de.
Report an issue: GitHub.