netbirdio/netbird · error

apply network -d: %w

Error message

apply network -d: %w

What it means

Destination-side twin of the source error in addNatRule(): applyNetwork("-d", pair.Destination, nil) fails while creating/refcounting an ipset for a destination network set. With plain prefixes this path cannot error, so seeing this message always implies a set-based destination route and an ipset subsystem failure, wrapped as 'apply network -d'.

Source

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

		markValue = nbnet.PreroutingFwmarkMasqueradeReturn
	}

	rule := []string{"-i", r.wgIface.Name()}
	if pair.Inverse {
		rule = []string{"!", "-i", r.wgIface.Name()}
	}

	rule = append(rule,
		"-m", "conntrack",
		"--ctstate", "NEW",
	)
	sourceExp, err := r.applyNetwork("-s", pair.Source, nil)
	if err != nil {
		return fmt.Errorf("apply network -s: %w", err)
	}
	destExp, err := r.applyNetwork("-d", pair.Destination, nil)
	if err != nil {
		return fmt.Errorf("apply network -d: %w", err)
	}

	rule = append(rule, sourceExp...)
	rule = append(rule, destExp...)
	rule = append(rule,
		"-j", "MARK", "--set-mark", fmt.Sprintf("%#x", markValue),
	)

	// Ensure nat rules come first, so the mark can be overwritten.
	// Currently overwritten by the dst-type LOCAL rules for redirected traffic.
	if err := r.iptablesClient.Insert(tableMangle, chainRTPRE, 1, rule...); err != nil {
		// TODO: rollback ipset counter
		return fmt.Errorf("error while adding marking rule for %s: %v", pair.Destination, err)
	}

	r.rules[ruleKey] = rule

	r.updateState()

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Confirm the failing side is the destination set from the log context, then `modprobe ip_set ip_set_hash_net`
  2. Test manually: `sudo ipset create test hash:net` (and destroy it after)
  3. Grant CAP_NET_ADMIN / relax seccomp for the agent container so netlink ipset calls work
  4. Check the set name length in the preceding 'create or get ipset' log line; report over-length names as a management/agent bug
  5. Retry the route application once modules and permissions are fixed

Example fix

// before: destination and source failures are indistinguishable in ops output
destExp, err := r.applyNetwork("-d", pair.Destination, nil)
if err != nil {
    return fmt.Errorf("apply network -d: %w", err)
}

// after: carry the set name into the error for direct diagnosis
destExp, err := r.applyNetwork("-d", pair.Destination, nil)
if err != nil {
    if pair.Destination.IsSet() {
        return fmt.Errorf("apply destination set %s: %w", pair.Destination.Set.HashedName(), err)
    }
    return fmt.Errorf("apply network -d: %w", err)
}
Defensive patterns

Strategy: validation

Validate before calling

func setApplicable(net firewall.Network) error {
    if !net.IsSet() {
        return nil
    }
    if _, err := os.Stat("/proc/net/ip_set"); err != nil {
        return fmt.Errorf("ipset unavailable for set-based networks: %w", err)
    }
    return nil
}

Try / catch

Same as the source variant: wrap the pair add, distinguish set failures (ipset subsystem) from prefix failures (cannot happen), and requeue the route for the next reconciliation cycle.

Prevention

When it happens

Trigger: pair.Destination.IsSet() true and ipsetCounter.Increment failing: ipset modules missing, CAP_NET_ADMIN absent, hashed set name too long for the kernel, or concurrent set creation racing (duplicate-create netlink error not treated as benign). Sources with a plain prefix succeed, isolating the failure to destination sets.

Common situations: Management applies routes whose destination is a network set (e.g. 'all peers' groups) to hosts without ipset support; hardened containers with seccomp blocking NETLINK_NETFILTER; older kernels with stricter ipset name/type validation; one-off breakage after ipset packages were updated on the host.

Related errors


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