{"record":{"id":"6f50da3f6f92999e","repo":"netbirdio/netbird","slug":"add-nat-rule-w","errorCode":null,"errorMessage":"add NAT rule: %w","messagePattern":"add NAT rule: %w","errorType":"exception","errorClass":"firewall.ErrIPv6NotInitialized","httpStatus":null,"severity":"error","filePath":"client/firewall/iptables/manager_linux.go","lineNumber":273,"sourceCode":"\t}\n\treturn m.router.DeleteRouteRule(rule)\n}\n\nfunc (m *Manager) IsServerRouteSupported() bool {\n\treturn true\n}\n\nfunc (m *Manager) IsStateful() bool {\n\treturn true\n}\n\nfunc (m *Manager) AddNatRule(pair firewall.RouterPair) error {\n\tm.mutex.Lock()\n\tdefer m.mutex.Unlock()\n\n\tif pair.Destination.IsPrefix() && pair.Destination.Prefix.Addr().Is6() {\n\t\tif !m.hasIPv6() {\n\t\t\treturn fmt.Errorf(\"add NAT rule: %w\", firewall.ErrIPv6NotInitialized)\n\t\t}\n\t\treturn m.router6.AddNatRule(pair)\n\t}\n\n\tif err := m.router.AddNatRule(pair); err != nil {\n\t\treturn err\n\t}\n\n\t// Dynamic routes need NAT in both tables since resolved IPs can be\n\t// either v4 or v6. This covers both DomainSet (modern) and the legacy\n\t// wildcard 0.0.0.0/0 destination where the client resolves DNS.\n\tif m.hasIPv6() && pair.Dynamic {\n\t\tv6Pair := firewall.ToV6NatPair(pair)\n\t\tif err := m.router6.AddNatRule(v6Pair); err != nil {\n\t\t\treturn fmt.Errorf(\"add v6 NAT rule: %w\", err)\n\t\t}\n\t}\n","sourceCodeStart":255,"sourceCodeEnd":291,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/firewall/iptables/manager_linux.go#L255-L291","documentation":"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.","triggerScenarios":"Calling AddNatRule(pair) where pair.Destination.IsPrefix() && pair.Destination.Prefix.Addr().Is6() while m.ipv6Client == nil.","commonSituations":"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.","solutions":["Enable IPv6 for the peer (v6 address in the management network) and reconnect so Create builds router6","Exclude v4-only peers from the v6 route's groups","Skip v6-destination pairs in the caller when !wgIface.Address().HasIPv6()"],"exampleFix":"// before\nif err := mgr.AddNatRule(pair); err != nil {\n\treturn err\n}\n\n// after\nif pair.Destination.IsPrefix() && pair.Destination.Prefix.Addr().Is6() && !wgIface.Address().HasIPv6() {\n\tlog.Debugf(\"skipping v6 NAT pair: no v6 overlay\")\n\treturn nil\n}\nif err := mgr.AddNatRule(pair); err != nil {\n\treturn err\n}","handlingStrategy":"validation","validationCode":"if pair.Destination.IsPrefix() && pair.Destination.Prefix.Addr().Is6() && !wgIface.Address().HasIPv6() {\n    return nil // v6 NAT pair cannot be programmed; nothing attempted yet\n}","typeGuard":null,"tryCatchPattern":"if err := mgr.AddNatRule(pair); err != nil {\n    if errors.Is(err, firewall.ErrIPv6NotInitialized) {\n        log.Debugf(\"v6 NAT pair skipped: no v6 firewall\")\n        return nil\n    }\n    return err\n}","preventionTips":["Check the pair's destination family against HasIPv6() before AddNatRule","Handle the sentinel explicitly with errors.Is instead of failing the whole route apply","Keep the peer's overlay address family aligned with the routes it receives"],"tags":["iptables","ipv6","firewall","nat","netbird","go"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}