{"record":{"id":"ef02eb7b0316da40","repo":"netbirdio/netbird","slug":"add-v6-nat-rule-w","errorCode":null,"errorMessage":"add v6 NAT rule: %w","messagePattern":"add v6 NAT rule: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/firewall/iptables/manager_linux.go","lineNumber":288,"sourceCode":"\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\n\treturn nil\n}\n\nfunc (m *Manager) RemoveNatRule(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 nil\n\t\t}\n\t\treturn m.router6.RemoveNatRule(pair)\n\t}\n\n\tvar merr *multierror.Error","sourceCodeStart":270,"sourceCodeEnd":306,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/firewall/iptables/manager_linux.go#L270-L306","documentation":"Dynamic-route NAT is mirrored into both families: the v4 rule was installed successfully, then the v6 mirror (firewall.ToV6NatPair) failed inside router6.AddNatRule. This leaves partial state - v4 NAT present, v6 NAT missing - so dynamic destinations resolving to v6 would not be masqueraded. The wrapped error comes from ip6tables operations on the nat table chains (NETBIRD-RT-NAT / POSTROUTING).","triggerScenarios":"AddNatRule with pair.Dynamic true on a manager with hasIPv6() true, and a failing ip6tables Append - typically the NETBIRD chains were flushed by an external tool between Init and this call, or the kernel lacks the nat table/module.","commonSituations":"Docker/Podman/firewalld reload or iptables-restore running concurrently and wiping custom chains; containers with iptables but incomplete netfilter modules for v6.","solutions":["Roll back the v4 half with RemoveNatRule(pair) so state is consistent, then retry AddNatRule","If persistent, inspect ip6tables -t nat -S | grep NETBIRD to see whether the routing chains still exist","Restart the agent: Init re-creates all NETBIRD chains and the route manager re-applies pairs","Check daemon logs for the wrapped ip6tables error to identify the missing module or chain"],"exampleFix":"// before\nif err := mgr.AddNatRule(pair); err != nil {\n\treturn err // v4 rule stays installed, v6 missing\n}\n\n// after\nif err := mgr.AddNatRule(pair); err != nil {\n\tif rmErr := mgr.RemoveNatRule(pair); rmErr != nil {\n\t\tlog.Warnf(\"rollback v4 NAT after failed add: %v\", rmErr)\n\t}\n\treturn err\n}","handlingStrategy":"retry","validationCode":"// sanity-check the v6 chains still exist before mirroring dynamic NAT\nfunc v6ChainsPresent() bool {\n    out, err := exec.Command(\"ip6tables\", \"-t\", \"nat\", \"-S\").Output()\n    return err == nil && strings.Contains(string(out), \"NETBIRD-RT-NAT\")\n}","typeGuard":null,"tryCatchPattern":"if err := mgr.AddNatRule(pair); err != nil {\n    if !errors.Is(err, firewall.ErrIPv6NotInitialized) {\n        // v4 half is installed; roll it back before surfacing the error\n        if rmErr := mgr.RemoveNatRule(pair); rmErr != nil {\n            log.Warnf(\"rollback v4 NAT: %v\", rmErr)\n        }\n    }\n    return err\n}","preventionTips":["Roll back the v4 half when the v6 mirror fails so NAT state stays symmetric","Re-add is idempotent (rules are keyed by pair content), so retry after transient ip6tables errors","Prevent external tools from flushing NETBIRD chains while the agent runs"],"tags":["iptables","ipv6","firewall","nat","partial-state","netbird","go"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}