{"record":{"id":"efe5dadd64fc2b2b","repo":"netbirdio/netbird","slug":"enable-forwarding-w-efe5da","errorCode":null,"errorMessage":"enable forwarding: %w","messagePattern":"enable forwarding: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/firewall/nftables/router_linux.go","lineNumber":1571,"sourceCode":"}\n\nfunc (r *router) AddDNATRule(rule firewall.ForwardRule) (firewall.Rule, error) {\n\truleKey := rule.ID()\n\tif _, exists := r.rules[ruleKey+dnatSuffix]; exists {\n\t\treturn rule, nil\n\t}\n\n\tprotoNum, err := r.af.protoNum(rule.Protocol)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"convert protocol to number: %w\", err)\n\t}\n\n\t// Request forwarding before queueing rules: addDnatRedirect/addDnatMasq\n\t// buffer netlink messages on r.conn that the next caller's Flush would\n\t// commit if we returned without flushing them ourselves.\n\tv6 := r.af.tableFamily == nftables.TableFamilyIPv6\n\tif err := r.ipFwdState.RequestForwarding(v6); err != nil {\n\t\treturn nil, fmt.Errorf(\"enable forwarding: %w\", err)\n\t}\n\n\tif err := r.addDnatRedirect(rule, protoNum, ruleKey); err != nil {\n\t\tif rerr := r.ipFwdState.ReleaseForwarding(v6); rerr != nil {\n\t\t\tlog.Warnf(\"rollback forwarding refcount: %v\", rerr)\n\t\t}\n\t\treturn nil, err\n\t}\n\n\tr.addDnatMasq(rule, protoNum, ruleKey)\n\n\t// Unlike iptables, there's no point in adding \"out\" rules in the forward chain here as our policy is ACCEPT.\n\t// To overcome DROP policies in other chains, we'd have to add rules to the chains there.\n\t// We also cannot just add \"oif <iface> accept\" there and filter in our own table as we don't know what is supposed to be allowed.\n\t// TODO: find chains with drop policies and add rules there\n\n\tif err := r.conn.Flush(); err != nil {\n\t\tif rerr := r.ipFwdState.ReleaseForwarding(v6); rerr != nil {","sourceCodeStart":1553,"sourceCodeEnd":1589,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/firewall/nftables/router_linux.go#L1553-L1589","documentation":"AddDNATRule enables IP forwarding through ipfwdstate.IPForwardingState before queueing NAT rules. RequestForwarding writes net.ipv4.ip_forward or per-interface net.ipv6.conf.*.forwarding sysctls on the first reference and increments a refcount. The wrapped 'enable forwarding' error means the sysctl write failed, so no DNAT rule is added and no refcount is held.","triggerScenarios":"Running without root or CAP_NET_ADMIN; /proc/sys mounted read-only (unprivileged container, immutable OS); IPv6-family request on a kernel with IPv6 disabled; /proc not mounted at all.","commonSituations":"Agent in Docker without --privileged or cap_add NET_ADMIN; hardened or immutable hosts; ipv6.disable=1 on the kernel command line while a v6 route or DNAT rule is configured; security software blocking sysctl writes.","solutions":["Test manually: 'sudo sysctl -w net.ipv4.ip_forward=1'; if that fails, fix privileges or procfs first.","Run the agent as root or grant CAP_NET_ADMIN (containers: --cap-add=NET_ADMIN --sysctl net.ipv4.ip_forward=1).","Ensure /proc is mounted read-write and not masked by a read-only bind mount over /proc/sys.","For IPv6 failures confirm IPv6 is enabled (check /proc/cmdline for ipv6.disable=1, read net.ipv6.conf.all.forwarding) or drop the v6 configuration.","Note that the routing path downgrades v6 enable failures to warnings, but this DNAT path returns them: v6 DNAT requires a writable sysctl."],"exampleFix":"# before: container without privileges\n docker run netbird/netbird ... # AddDNATRule -> \"enable forwarding: ... permission denied\"\n\n# after\n docker run --cap-add=NET_ADMIN --sysctl net.ipv4.ip_forward=1 netbird/netbird ...","handlingStrategy":"validation","validationCode":"// Verify the forwarding sysctl is writable before attempting DNAT setup\nfunc forwardingSysctlWritable(v6 bool) error {\n    path := \"/proc/sys/net/ipv4/ip_forward\"\n    if v6 {\n        path = \"/proc/sys/net/ipv6/conf/all/forwarding\"\n    }\n    f, err := os.OpenFile(path, os.O_WRONLY, 0)\n    if err != nil {\n        return fmt.Errorf(\"sysctl %s not writable: %w\", path, err)\n    }\n    return f.Close()\n}","typeGuard":null,"tryCatchPattern":"if err := r.ipFwdState.RequestForwarding(v6); err != nil {\n    if errors.Is(err, unix.EACCES) || errors.Is(err, unix.EROFS) {\n        return fmt.Errorf(\"cannot enable forwarding (privileged, writable /proc/sys required): %w\", err)\n    }\n    return fmt.Errorf(\"enable forwarding: %w\", err)\n}","preventionTips":["Run the agent privileged: root or CAP_NET_ADMIN with a writable procfs.","In containers, pass --sysctl net.ipv4.ip_forward=1 and --cap-add=NET_ADMIN.","Do not configure v6 routes or DNAT on hosts booted with ipv6.disable=1.","Health-check forwarding sysctls during deployment rather than at first DNAT rule."],"tags":["sysctl","ip-forwarding","permissions","containers","linux"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}