netbirdio/netbird · error

add legacy forwarding rule %s -> %s: %v

Error message

add legacy forwarding rule %s -> %s: %v

What it means

Returned by router.addLegacyRouteRule when iptablesClient.Append(tableFilter, NETBIRD-RT-FWD-IN, '-s <src> -d <dst> -j ACCEPT') fails while installing the permissive legacy rule for pre-route-ACL management. Formatted with %v (not %w), so the cause is embedded as text and is not unwrappable by callers. The rule allows all traffic from pair.Source to pair.Destination, which is why the warning above it matters.

Source

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

		return fmt.Errorf("remove legacy routing rule: %w", err)
	}

	r.updateState()

	return nil
}

// addLegacyRouteRule adds a legacy routing rule for mgmt servers pre route acls
func (r *router) addLegacyRouteRule(pair firewall.RouterPair) error {
	ruleKey := firewall.GenKey(firewall.ForwardingFormat, pair)

	if err := r.removeLegacyRouteRule(pair); err != nil {
		return err
	}

	rule := []string{"-s", pair.Source.String(), "-d", pair.Destination.String(), "-j", routingFinalForwardJump}
	if err := r.iptablesClient.Append(tableFilter, chainRTFWDIN, rule...); err != nil {
		return fmt.Errorf("add legacy forwarding rule %s -> %s: %v", pair.Source, pair.Destination, err)
	}

	r.rules[ruleKey] = rule

	return nil
}

func (r *router) removeLegacyRouteRule(pair firewall.RouterPair) error {
	ruleKey := firewall.GenKey(firewall.ForwardingFormat, pair)

	if rule, exists := r.rules[ruleKey]; exists {
		if err := r.iptablesClient.DeleteIfExists(tableFilter, chainRTFWDIN, rule...); err != nil {
			return fmt.Errorf("remove legacy forwarding rule %s -> %s: %v", pair.Source, pair.Destination, err)
		}
		delete(r.rules, ruleKey)

		if err := r.decrementSetCounter(rule); err != nil {
			return fmt.Errorf("decrement ipset counter: %w", err)

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Ensure NETBIRD-RT-FWD-IN exists (sudo iptables -S NETBIRD-RT-FWD-IN); restart agent to re-init if missing
  2. Check the log for the earlier createContainers/init failure and fix root cause first
  3. Upgrade management so the peer leaves legacy mode and uses route ACLs instead
  4. Confirm root/CAP_NET_ADMIN for the agent process

Example fix

// before (router_linux.go:322): cause not wrappable
return fmt.Errorf("add legacy forwarding rule %s -> %s: %v", pair.Source, pair.Destination, err)
// after
return fmt.Errorf("add legacy forwarding rule %s -> %s: %w", pair.Source, pair.Destination, err)
Defensive patterns

Strategy: validation

Validate before calling

if ok, _ := r.iptablesClient.ChainExists(tableFilter, chainRTFWDIN); !ok {
	return fmt.Errorf("chain %s missing; run init first", chainRTFWDIN)
}

Prevention

When it happens

Trigger: AddNatRule with legacyManagement on, after a successful removeLegacyRouteRule. Append fails when NETBIRD-RT-FWD-IN does not exist (init/createContainers failed or chains were flushed), CAP_NET_ADMIN is missing, or the iptables invocation errors.

Common situations: Old management version plus a host where custom chains were removed manually or by a crashed run; containers where capabilities were dropped after start; nft backend mismatch producing 'No chain/target/match by that name'.

Related errors


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