netbirdio/netbird · error
add return masquerade rule: %v
Error message
add return masquerade rule: %v
What it means
Second rule of addPostroutingRules(): appending the return-traffic masquerade rule (match PreroutingFwmarkMasqueradeReturn, out the NetBird interface, MASQUERADE) to NETBIRD-RT-NAT. Failure aborts createContainers() with 'add static nat rules'; note rule1 may already be installed, leaving partially programmed state that only a later Reset cleans up.
Source
Thrown at client/firewall/iptables/router_linux.go:555
// First rule for outbound masquerade
rule1 := []string{
"-m", "mark", "--mark", fmt.Sprintf("%#x", nbnet.PreroutingFwmarkMasquerade),
"!", "-o", "lo",
"-j", routingFinalNatJump,
}
if err := r.iptablesClient.Append(tableNat, chainRTNAT, rule1...); err != nil {
return fmt.Errorf("add outbound masquerade rule: %v", err)
}
r.rules["static-nat-outbound"] = rule1
// Second rule for return traffic masquerade
rule2 := []string{
"-m", "mark", "--mark", fmt.Sprintf("%#x", nbnet.PreroutingFwmarkMasqueradeReturn),
"-o", r.wgIface.Name(),
"-j", routingFinalNatJump,
}
if err := r.iptablesClient.Append(tableNat, chainRTNAT, rule2...); err != nil {
return fmt.Errorf("add return masquerade rule: %v", err)
}
r.rules["static-nat-return"] = rule2
return nil
}
// addMSSClampingRules adds MSS clamping rules to prevent fragmentation for forwarded traffic.
func (r *router) addMSSClampingRules() error {
overhead := uint16(ipv4TCPHeaderSize)
if r.v6 {
overhead = ipv6TCPHeaderSize
}
mss := r.mtu - overhead
// Add jump rule from FORWARD chain in mangle table to our custom chain
jumpRule := []string{
"-j", chainRTMSSCLAMP,
}View on GitHub (pinned to 93e97f4bf1)
Solutions
- Check the agent log whether the outbound rule succeeded just before (if yes, suspect the interface-name or transient-lock angle)
- Reproduce: `iptables -t nat -A NETBIRD-RT-NAT -m mark --mark 0x... -o wt0 -j MASQUERADE`
- `modprobe iptable_nat xt_MASQUERADE xt_mark` and verify ip6table_nat for dual-stack
- Run `netbird down` (cleans the half-applied state) before retrying `netbird up`
- If the interface name changed, restart the daemon so wgIface.Name() is current
Example fix
// before: rule1 already installed; on rule2 failure the half-state persists
if err := r.iptablesClient.Append(tableNat, chainRTNAT, rule2...); err != nil {
return fmt.Errorf("add return masquerade rule: %v", err)
}
// after: roll rule1 back so setup fails atomically
if err := r.iptablesClient.Append(tableNat, chainRTNAT, rule2...); err != nil {
if delErr := r.iptablesClient.DeleteIfExists(tableNat, chainRTNAT, rule1...); delErr != nil {
log.Warnf("rollback outbound masquerade rule: %v", delErr)
}
delete(r.rules, "static-nat-outbound")
return fmt.Errorf("add return masquerade rule: %w", err)
} Defensive patterns
Strategy: try-catch
Validate before calling
func ifaceExists(name string) bool {
_, err := net.InterfaceByName(name)
return err == nil
} Try / catch
On rule2 failure, roll back rule1 (DeleteIfExists on the stored spec) before returning, keeping the nat chain either fully programmed or untouched.
Prevention
- Keep the NetBird interface stable across route updates; avoid down/up flapping while management pushes changes
- Preload the same modules as for the outbound rule
- After any failed `netbird up`, run `netbird down` to clear half-applied NAT chains
When it happens
Trigger: `iptables -t nat -A NETBIRD-RT-NAT -m mark --mark 0x... -o wt0 -j MASQUERADE` failing when the NetBird interface name just changed/disappeared (e.g., interface recreated between operations), or the same missing-module/permission/lock causes as the outbound rule.
Common situations: Interface name churn after a quick down/up cycle; IPv6 pairs where ip6table_nat is missing while IPv4 works; systems that resolved the first append but lost the xtables lock to a concurrent docker restart mid-setup.
Related errors
- add outbound masquerade rule: %v
- add nat rule: %w
- add inverse nat rule: %w
- add static nat rules: %w
- add nat postrouting jump rule: %v
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/64c8f5cef556edff.
Report an issue: GitHub.