{"record":{"id":"e680ad0d0d3c882e","repo":"netbirdio/netbird","slug":"error-while-adding-marking-rule-for-s-v","errorCode":null,"errorMessage":"error while adding marking rule for %s: %v","messagePattern":"error while adding marking rule for (.+?): (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/firewall/iptables/router_linux.go","lineNumber":706,"sourceCode":"\tif err != nil {\n\t\treturn fmt.Errorf(\"apply network -s: %w\", err)\n\t}\n\tdestExp, err := r.applyNetwork(\"-d\", pair.Destination, nil)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"apply network -d: %w\", err)\n\t}\n\n\trule = append(rule, sourceExp...)\n\trule = append(rule, destExp...)\n\trule = append(rule,\n\t\t\"-j\", \"MARK\", \"--set-mark\", fmt.Sprintf(\"%#x\", markValue),\n\t)\n\n\t// Ensure nat rules come first, so the mark can be overwritten.\n\t// Currently overwritten by the dst-type LOCAL rules for redirected traffic.\n\tif err := r.iptablesClient.Insert(tableMangle, chainRTPRE, 1, rule...); err != nil {\n\t\t// TODO: rollback ipset counter\n\t\treturn fmt.Errorf(\"error while adding marking rule for %s: %v\", pair.Destination, err)\n\t}\n\n\tr.rules[ruleKey] = rule\n\n\tr.updateState()\n\treturn nil\n}\n\nfunc (r *router) removeNatRule(pair firewall.RouterPair) error {\n\truleKey := firewall.GenKey(firewall.NatFormat, pair)\n\n\tif rule, exists := r.rules[ruleKey]; exists {\n\t\tif err := r.iptablesClient.DeleteIfExists(tableMangle, chainRTPRE, rule...); err != nil {\n\t\t\treturn fmt.Errorf(\"error while removing marking rule for %s: %v\", pair.Destination, err)\n\t\t}\n\t\tdelete(r.rules, ruleKey)\n\n\t\tif err := r.decrementSetCounter(rule); err != nil {","sourceCodeStart":688,"sourceCodeEnd":724,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/firewall/iptables/router_linux.go#L688-L724","documentation":"addNatRule() inserts the MARK rule built for the RouterPair at position 1 of NETBIRD-RT-PRE (mangle table), stamping NEW connections with PreroutingFwmarkMasquerade or ...MasqueradeReturn so the later postrouting rules masquerade them. Failure aborts the pair add; note the source comment admits the ipset refcount incremented earlier is NOT rolled back (TODO), so failed adds can leak ipset references.","triggerScenarios":"`iptables -t mangle -I NETBIRD-RT-PRE 1 <rule>` failing: xt_mark target unavailable, mangle table inaccessible, CAP_NET_ADMIN missing, xtables lock held, or the chain NETBIRD-RT-PRE vanished because init/createContainers never ran or was externally undone. Rule contents (mark match, conntrack, ipset match) are appended before the insert, so an invalid ipset name also surfaces here.","commonSituations":"Route update storms from management hitting xtables lock contention; agents on hosts where xt_mark is a module that was never loaded; containers running an embedded client (client/embed) without full NET_ADMIN; leftover refcounts accumulating after repeated failures (the TODO in the source).","solutions":["Reproduce with the rule spec from the log: `sudo iptables -t mangle -I NETBIRD-RT-PRE 1 ...`","`modprobe xt_mark iptable_mangle` (plus ip6 variants for v6 pairs)","Confirm NETBIRD-RT-PRE exists; if the router was never initialized, look for the earlier createContainers error instead","Ensure no process holds /run/xtables.lock during route updates","After repeated failures, check for orphaned ipsets (`ipset list -n | grep -i nb`) and report the refcount leak upstream"],"exampleFix":"// before: ipset refcount incremented in applyNetwork is never rolled back\nif err := r.iptablesClient.Insert(tableMangle, chainRTPRE, 1, rule...); err != nil {\n    // TODO: rollback ipset counter\n    return fmt.Errorf(\"error while adding marking rule for %s: %v\", pair.Destination, err)\n}\n\n// after: decrement the refcount so failed adds do not leak ipsets\nif err := r.iptablesClient.Insert(tableMangle, chainRTPRE, 1, rule...); err != nil {\n    if derr := r.decrementSetCounter(rule); derr != nil {\n        log.Warnf(\"rollback ipset refcount: %v\", derr)\n    }\n    return fmt.Errorf(\"add marking rule for %s: %w\", pair.Destination, err)\n}","handlingStrategy":"validation","validationCode":"func markTargetSupported(ipt *iptables.IPTables) error {\n    if err := ipt.NewChain(\"mangle\", \"NB-PROBE\"); err != nil {\n        return err\n    }\n    defer ipt.ClearAndDeleteChain(\"mangle\", \"NB-PROBE\")\n    return ipt.Append(\"mangle\", \"NB-PROBE\", \"-j\", \"MARK\", \"--set-mark\", \"0x1\")\n}","typeGuard":null,"tryCatchPattern":"Catch in the route manager: roll back the ipset refcount taken during applyNetwork, keep prior rules intact, and surface the pair destination so operators can retry that single route.","preventionTips":["modprobe xt_mark before applying set-based routes","Keep the NETBIRD-RT-PRE chain untouched by external tooling","Watch for orphaned NB ipsets after repeated pair-add failures"],"tags":["network","linux","iptables","mangle","routing"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}