netbirdio/netbird · error

add mangle prerouting jump rule: %v

Error message

add mangle prerouting jump rule: %v

What it means

Second insert in addJumpRules(): `-j NETBIRD-RT-PRE` at position 1 of mangle PREROUTING, feeding prerouting marks (used by routing decisions and the later NAT marking rules) into NetBird's chain. Failure aborts createContainers() with 'add jump rules'; prerouting marks, and therefore the whole mark-based routing/NAT design, cannot work.

Source

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

	ruleKey := "established-" + chain
	r.rules[ruleKey] = establishedRule

	return nil
}

func (r *router) addJumpRules() error {
	// Jump to nat chain
	natRule := []string{"-j", chainRTNAT}
	if err := r.iptablesClient.Insert(tableNat, chainPOSTROUTING, 1, natRule...); err != nil {
		return fmt.Errorf("add nat postrouting jump rule: %v", err)
	}
	r.rules[jumpNatPost] = natRule

	// Jump to mangle prerouting chain
	preRule := []string{"-j", chainRTPRE}
	if err := r.iptablesClient.Insert(tableMangle, chainPREROUTING, 1, preRule...); err != nil {
		return fmt.Errorf("add mangle prerouting jump rule: %v", err)
	}
	r.rules[jumpManglePre] = preRule

	// Jump to nat prerouting chain
	rdrRule := []string{"-j", chainRTRDR}
	if err := r.iptablesClient.Insert(tableNat, chainPREROUTING, 1, rdrRule...); err != nil {
		return fmt.Errorf("add nat prerouting jump rule: %v", err)
	}
	r.rules[jumpNatPre] = rdrRule

	return nil
}

func (r *router) cleanJumpRules() error {
	for _, ruleKey := range []string{jumpNatPost, jumpManglePre, jumpNatPre, jumpMSSClamp} {
		if rule, exists := r.rules[ruleKey]; exists {
			var table, chain string
			switch ruleKey {

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Reproduce: `sudo iptables -t mangle -I PREROUTING 1 -j NETBIRD-RT-PRE`
  2. `modprobe iptable_mangle` and for the v6 router `modprobe ip6table_mangle`
  3. Confirm the NETBIRD-RT-PRE chain exists (`iptables -t mangle -S`) and check earlier createContainers errors if not
  4. Verify daemon privileges and xtables lock availability
  5. Retry `netbird down && netbird up` after fixes

Example fix

// before
if err := r.iptablesClient.Insert(tableMangle, chainPREROUTING, 1, preRule...); err != nil {
    return fmt.Errorf("add mangle prerouting jump rule: %v", err)
}

// after: probe the mangle table first so the cause is explicit
if _, err := r.iptablesClient.List(tableMangle, chainPREROUTING); err != nil {
    return fmt.Errorf("mangle table unavailable: %w", err)
}
if err := r.iptablesClient.Insert(tableMangle, chainPREROUTING, 1, preRule...); err != nil {
    return fmt.Errorf("add mangle prerouting jump rule: %w", err)
}
Defensive patterns

Strategy: validation

Validate before calling

func chainReady(ipt *iptables.IPTables, table, chain string) error {
    if ok, err := ipt.ChainExists(table, chain); err != nil {
        return err
    } else if !ok {
        return fmt.Errorf("chain %s missing in %s", chain, table)
    }
    return nil
}

// usage: chainReady(ipt, "mangle", "NETBIRD-RT-PRE") before addJumpRules

Try / catch

Fatal-for-init pattern; unwind prior jump inserts (nat POSTROUTING) when the mangle insert fails so built-in chains are left clean.

Prevention

When it happens

Trigger: `iptables -t mangle -I PREROUTING 1 -j NETBIRD-RT-PRE` failing due to a missing iptable_mangle module, no CAP_NET_ADMIN, xtables lock contention, or the NETBIRD-RT-PRE chain not existing because an earlier NewChain in createContainers partially failed.

Common situations: Minimal container hosts lacking mangle support; unprivileged daemon; concurrent firewall rewrites; systems where the v4 router succeeded but the v6 router instance fails because ip6table_mangle is absent while ip6table_filter loads fine.

Related errors


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