netbirdio/netbird · error

add IPv6 protocol address: %s

Error message

add IPv6 protocol address: %s

What it means

Returned by forwarder.New when the gVisor netstack rejects the IPv6 address assignment for the NIC (stack.AddProtocolAddress). gVisor returns tcpip.ErrDuplicateLocalAddress when the address was already registered on that NIC, or an invalid-prefix error when PrefixLen exceeds the protocol's bit width. At this point in the constructor the IPv4 address has already been added and the NIC created, so the whole userspace forwarder fails to come up.

Source

Thrown at client/firewall/uspfilter/forwarder/forwarder.go:108

			Address:   tcpip.AddrFrom4(iface.Address().IP.As4()),
			PrefixLen: iface.Address().Network.Bits(),
		},
	}

	if err := s.AddProtocolAddress(nicID, protoAddr, stack.AddressProperties{}); err != nil {
		return nil, fmt.Errorf("failed to add protocol address: %s", err)
	}

	if v6 := iface.Address().IPv6; v6.IsValid() {
		v6Addr := tcpip.ProtocolAddress{
			Protocol: ipv6.ProtocolNumber,
			AddressWithPrefix: tcpip.AddressWithPrefix{
				Address:   tcpip.AddrFrom16(v6.As16()),
				PrefixLen: iface.Address().IPv6Net.Bits(),
			},
		}
		if err := s.AddProtocolAddress(nicID, v6Addr, stack.AddressProperties{}); err != nil {
			return nil, fmt.Errorf("add IPv6 protocol address: %s", err)
		}
	}

	defaultSubnet, err := tcpip.NewSubnet(
		tcpip.AddrFrom4([4]byte{0, 0, 0, 0}),
		tcpip.MaskFromBytes([]byte{0, 0, 0, 0}),
	)
	if err != nil {
		return nil, fmt.Errorf("creating default subnet: %w", err)
	}

	defaultSubnetV6, err := tcpip.NewSubnet(
		tcpip.AddrFrom16([16]byte{}),
		tcpip.MaskFromBytes(make([]byte, 16)),
	)
	if err != nil {
		return nil, fmt.Errorf("creating default v6 subnet: %w", err)
	}

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Check the IPv6 address and prefix reported by iface.Address() before constructing the Forwarder: IPv6.IsValid() must be true and IPv6Net.Bits() must be 0..128
  2. Ensure only one Forwarder instance per stack/NIC lifecycle; tear down the old forwarder (cancel + stack cleanup) before creating a new one
  3. If the overlay was configured without IPv6, verify the address mapper is not returning a stale v6 address after a reconfiguration
  4. Upgrade gvisor.dev/gvisor if the wrapped error text points at an AddressProperties validation added in a newer version

Example fix

// before
if v6 := iface.Address().IPv6; v6.IsValid() {
    // ... AddProtocolAddress
}

// after
if v6 := iface.Address().IPv6; v6.IsValid() {
    if bits := iface.Address().IPv6Net.Bits(); bits < 0 || bits > 128 {
        return nil, fmt.Errorf("invalid IPv6 prefix length %d", bits)
    }
    // ... AddProtocolAddress
}
Defensive patterns

Strategy: validation

Validate before calling

// before constructing the forwarder
a := iface.Address()
if a.IPv6.IsValid() {
    if bits := a.IPv6Net.Bits(); bits < 0 || bits > 128 {
        return fmt.Errorf("invalid IPv6 prefix %s", a.IPv6Net)
    }
}
f, err := forwarder.New(iface, logger, flowLogger, netstack, mtu)

Type guard

func validV6Assignment(a wgaddr.Address) bool {
    v6 := a.IPv6
    if !v6.IsValid() {
        return true // v4-only is fine; the v6 block is skipped
    }
    bits := a.IPv6Net.Bits()
    return bits >= 0 && bits <= 128 && v6.Is6()
}

Try / catch

if _, err := forwarder.New(...); err != nil {
    if strings.Contains(err.Error(), "add IPv6 protocol address") {
        // re-check address state, recreate interface, retry once
    }
    return err
}

Prevention

When it happens

Trigger: Calling forwarder.New with an interface whose Address().IPv6 is valid but whose IPv6Net prefix bits are invalid (>128 or negative from a misparsed CIDR), or calling New twice against state where the same v6 address already exists; also a teardown race where the NIC was removed between CreateNIC and this call.

Common situations: IPv6 overlay enabled in NetBird while the interface address/prefix were populated from a malformed config or a management network range that is not a real /prefix; concurrent interface re-creation (BindUpdate/engine restart) racing the forwarder constructor; gVisor version change tightening AddProtocolAddress validation.

Related errors


AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16). Data as JSON: /api/errors/2e20423e23be5d43. Report an issue: GitHub.