netbirdio/netbird · error

add jump to MSS clamp chain: %w

Error message

add jump to MSS clamp chain: %w

What it means

addMSSClampingRules() inserts `-j NETBIRD-RT-MSSCLAMP` at position 1 of the mangle FORWARD chain so forwarded TCP SYNs get clamped to the interface MTU. A failure here is only logged by createContainers() (log.Errorf), not propagated, so the agent starts but forwarded TCP over the overlay may suffer fragmentation/blackholes on links with smaller MTUs.

Source

Thrown at client/firewall/iptables/router_linux.go:575

	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,
	}
	if err := r.iptablesClient.Insert(tableMangle, chainFORWARD, 1, jumpRule...); err != nil {
		return fmt.Errorf("add jump to MSS clamp chain: %w", err)
	}
	r.rules[jumpMSSClamp] = jumpRule

	ruleOut := []string{
		"-o", r.wgIface.Name(),
		"-p", "tcp",
		"--tcp-flags", "SYN,RST", "SYN",
		"-j", "TCPMSS",
		"--set-mss", fmt.Sprintf("%d", mss),
	}
	if err := r.iptablesClient.Append(tableMangle, chainRTMSSCLAMP, ruleOut...); err != nil {
		return fmt.Errorf("add outbound MSS clamp rule: %w", err)
	}
	r.rules["mss-clamp-out"] = ruleOut

	return nil
}

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Grep the agent log for 'failed to add MSS clamping rules' after `netbird up` (the error is not returned to the CLI)
  2. Run `sudo iptables -t mangle -I FORWARD 1 -j NETBIRD-RT-MSSCLAMP` to see the real error
  3. `modprobe iptable_mangle`
  4. Reduce churn on the FORWARD chain (stop firewalld rewrites) or restart the agent after firewall changes
  5. Verify afterwards: `sudo iptables -t mangle -S FORWARD | head` should show the NETBIRD-RT-MSSCLAMP jump

Example fix

// before
if err := r.iptablesClient.Insert(tableMangle, chainFORWARD, 1, jumpRule...); err != nil {
    return fmt.Errorf("add jump to MSS clamp chain: %w", err)
}

// after: tolerate an identical jump inserted by a previous run
exists, _ := r.iptablesClient.Exists(tableMangle, chainFORWARD, jumpRule...)
if !exists {
    if err := r.iptablesClient.Insert(tableMangle, chainFORWARD, 1, jumpRule...); err != nil {
        return fmt.Errorf("add jump to MSS clamp chain: %w", err)
    }
}
r.rules[jumpMSSClamp] = jumpRule
Defensive patterns

Strategy: validation

Validate before calling

func forwardChainProgrammable(ipt *iptables.IPTables) bool {
    probe := []string{"-j", "RETURN"}
    if err := ipt.Insert("mangle", "FORWARD", 1, probe...); err != nil {
        return false
    }
    _ = ipt.DeleteIfExists("mangle", "FORWARD", probe...)
    return true
}

Try / catch

This failure is log-only by design; catch it at the ops layer by grepping for 'failed to add MSS clamping rules' and re-running addMSSClampingRules on the next reconciliation instead of crashing.

Prevention

When it happens

Trigger: `iptables -t mangle -I FORWARD 1 -j NETBIRD-RT-MSSCLAMP` failing when iptable_mangle is not loaded, CAP_NET_ADMIN is missing, another tool rewrote FORWARD concurrently making the insert race, or the xtables lock is held.

Common situations: Stripped container hosts without the mangle table; hosts running firewalld that replaces the FORWARD chain wholesale; MTU-sensitive links (PPP, tunnels) where clamping mattered and silent failure shows as stalled TCP handshakes for routed traffic.

Related errors


AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16). Data as JSON: /api/errors/f3ccb8c29545df1f. Report an issue: GitHub.