netbirdio/netbird · error
enable forwarding: %w
Error message
enable forwarding: %w
What it means
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.
Source
Thrown at client/firewall/nftables/router_linux.go:1571
}
func (r *router) AddDNATRule(rule firewall.ForwardRule) (firewall.Rule, error) {
ruleKey := rule.ID()
if _, exists := r.rules[ruleKey+dnatSuffix]; exists {
return rule, nil
}
protoNum, err := r.af.protoNum(rule.Protocol)
if err != nil {
return nil, fmt.Errorf("convert protocol to number: %w", err)
}
// Request forwarding before queueing rules: addDnatRedirect/addDnatMasq
// buffer netlink messages on r.conn that the next caller's Flush would
// commit if we returned without flushing them ourselves.
v6 := r.af.tableFamily == nftables.TableFamilyIPv6
if err := r.ipFwdState.RequestForwarding(v6); err != nil {
return nil, fmt.Errorf("enable forwarding: %w", err)
}
if err := r.addDnatRedirect(rule, protoNum, ruleKey); err != nil {
if rerr := r.ipFwdState.ReleaseForwarding(v6); rerr != nil {
log.Warnf("rollback forwarding refcount: %v", rerr)
}
return nil, err
}
r.addDnatMasq(rule, protoNum, ruleKey)
// Unlike iptables, there's no point in adding "out" rules in the forward chain here as our policy is ACCEPT.
// To overcome DROP policies in other chains, we'd have to add rules to the chains there.
// 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.
// TODO: find chains with drop policies and add rules there
if err := r.conn.Flush(); err != nil {
if rerr := r.ipFwdState.ReleaseForwarding(v6); rerr != nil {View on GitHub (pinned to 93e97f4bf1)
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.
Example fix
# before: container without privileges docker run netbird/netbird ... # AddDNATRule -> "enable forwarding: ... permission denied" # after docker run --cap-add=NET_ADMIN --sysctl net.ipv4.ip_forward=1 netbird/netbird ...
Defensive patterns
Strategy: validation
Validate before calling
// Verify the forwarding sysctl is writable before attempting DNAT setup
func forwardingSysctlWritable(v6 bool) error {
path := "/proc/sys/net/ipv4/ip_forward"
if v6 {
path = "/proc/sys/net/ipv6/conf/all/forwarding"
}
f, err := os.OpenFile(path, os.O_WRONLY, 0)
if err != nil {
return fmt.Errorf("sysctl %s not writable: %w", path, err)
}
return f.Close()
} Try / catch
if err := r.ipFwdState.RequestForwarding(v6); err != nil {
if errors.Is(err, unix.EACCES) || errors.Is(err, unix.EROFS) {
return fmt.Errorf("cannot enable forwarding (privileged, writable /proc/sys required): %w", err)
}
return fmt.Errorf("enable forwarding: %w", err)
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/efe5dadd64fc2b2b.
Report an issue: GitHub.