netbirdio/netbird · error · firewall.ErrIPv6NotInitialized

add NAT rule: %w

Error message

add NAT rule: %w

What it means

AddNatRule received a RouterPair whose destination is an IPv6 prefix, but the manager was created without the v6 half. Wraps the sentinel firewall.ErrIPv6NotInitialized; the NAT rule is refused before any iptables call is made. Note the sibling path: a v4-destination dynamic pair also mirrors into v6 when available - this error is the pure-v6 destination variant.

Source

Thrown at client/firewall/iptables/manager_linux.go:273

	}
	return m.router.DeleteRouteRule(rule)
}

func (m *Manager) IsServerRouteSupported() bool {
	return true
}

func (m *Manager) IsStateful() bool {
	return true
}

func (m *Manager) AddNatRule(pair firewall.RouterPair) error {
	m.mutex.Lock()
	defer m.mutex.Unlock()

	if pair.Destination.IsPrefix() && pair.Destination.Prefix.Addr().Is6() {
		if !m.hasIPv6() {
			return fmt.Errorf("add NAT rule: %w", firewall.ErrIPv6NotInitialized)
		}
		return m.router6.AddNatRule(pair)
	}

	if err := m.router.AddNatRule(pair); err != nil {
		return err
	}

	// Dynamic routes need NAT in both tables since resolved IPs can be
	// either v4 or v6. This covers both DomainSet (modern) and the legacy
	// wildcard 0.0.0.0/0 destination where the client resolves DNS.
	if m.hasIPv6() && pair.Dynamic {
		v6Pair := firewall.ToV6NatPair(pair)
		if err := m.router6.AddNatRule(v6Pair); err != nil {
			return fmt.Errorf("add v6 NAT rule: %w", err)
		}
	}

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Enable IPv6 for the peer (v6 address in the management network) and reconnect so Create builds router6
  2. Exclude v4-only peers from the v6 route's groups
  3. Skip v6-destination pairs in the caller when !wgIface.Address().HasIPv6()

Example fix

// before
if err := mgr.AddNatRule(pair); err != nil {
	return err
}

// after
if pair.Destination.IsPrefix() && pair.Destination.Prefix.Addr().Is6() && !wgIface.Address().HasIPv6() {
	log.Debugf("skipping v6 NAT pair: no v6 overlay")
	return nil
}
if err := mgr.AddNatRule(pair); err != nil {
	return err
}
Defensive patterns

Strategy: validation

Validate before calling

if pair.Destination.IsPrefix() && pair.Destination.Prefix.Addr().Is6() && !wgIface.Address().HasIPv6() {
    return nil // v6 NAT pair cannot be programmed; nothing attempted yet
}

Try / catch

if err := mgr.AddNatRule(pair); err != nil {
    if errors.Is(err, firewall.ErrIPv6NotInitialized) {
        log.Debugf("v6 NAT pair skipped: no v6 firewall")
        return nil
    }
    return err
}

Prevention

When it happens

Trigger: Calling AddNatRule(pair) where pair.Destination.IsPrefix() && pair.Destination.Prefix.Addr().Is6() while m.ipv6Client == nil.

Common situations: A v6 network route (masquerade/NAT) applied to a peer with a v4-only overlay address; routes pushed to groups that include v4-only peers.

Related errors


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