{"record":{"id":"3a3fb656f03e3dfc","repo":"tailscale/tailscale","slug":"deleting-connmark-rule-in-mangle-output-w","errorCode":null,"errorMessage":"deleting connmark rule in mangle/OUTPUT: %w","messagePattern":"deleting connmark rule in mangle/OUTPUT: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"util/linuxfw/iptables_runner.go","lineNumber":632,"sourceCode":"\t\t\t\treturn fmt.Errorf(\"deleting connmark rule in mangle/PREROUTING: %w\", err)\n\t\t\t}\n\t\t\t// Rule doesn't exist - this is fine for idempotency\n\t\t}\n\n\t\t// Delete OUTPUT rule\n\t\targs = []string{\n\t\t\t\"-m\", \"conntrack\",\n\t\t\t\"--ctstate\", \"NEW\",\n\t\t\t\"-m\", \"mark\",\n\t\t\t\"!\", \"--mark\", \"0x0/\" + fwmarkMask,\n\t\t\t\"-j\", \"CONNMARK\",\n\t\t\t\"--save-mark\",\n\t\t\t\"--nfmask\", fwmarkMask,\n\t\t\t\"--ctmask\", fwmarkMask,\n\t\t}\n\t\tif err := ipt.Delete(\"mangle\", \"OUTPUT\", args...); err != nil {\n\t\t\tif !isNotExistError(err) {\n\t\t\t\treturn fmt.Errorf(\"deleting connmark rule in mangle/OUTPUT: %w\", err)\n\t\t\t}\n\t\t\t// Rule doesn't exist - this is fine for idempotency\n\t\t}\n\t}\n\treturn nil\n}\n\n// buildMagicsockPortRule generates the string slice containing the arguments\n// to describe a rule accepting traffic on a particular port to iptables. It is\n// separated out here to avoid repetition in AddMagicsockPortRule and\n// RemoveMagicsockPortRule, since it is important that the same rule is passed\n// to Append() and Delete().\nfunc buildMagicsockPortRule(port uint16) []string {\n\treturn []string{\"-p\", \"udp\", \"--dport\", strconv.FormatUint(uint64(port), 10), \"-j\", \"ACCEPT\"}\n}\n\n// AddMagicsockPortRule adds a rule to iptables to allow incoming traffic on\n// the specified UDP port, so magicsock can accept incoming connections.","sourceCodeStart":614,"sourceCodeEnd":650,"githubUrl":"https://github.com/tailscale/tailscale/blob/6e0912f97994f927632b34ae9e63b53d6516a6ac/util/linuxfw/iptables_runner.go#L614-L650","documentation":"The second step of iptablesRunner.DelConnmarkSaveRule (util/linuxfw/iptables_runner.go:632): deleting the OUTPUT rule that saved marks on NEW connections (conntrack --ctstate NEW, mark ! 0x0/0xff00, CONNMARK --save-mark). As with the PREROUTING delete, a 'rule does not exist' outcome is silently accepted, so this error means the iptables -t mangle -D OUTPUT call failed for a real reason such as missing CAP_NET_ADMIN, unavailable connmark support, or an iptables binary/backend problem. It surfaces during firewall teardown on both the IPv4 and IPv6 tables (the loop over getTables).","triggerScenarios":"DelConnmarkSaveRule invoked when the process cannot modify mangle/OUTPUT: no CAP_NET_ADMIN, missing xt_connmark/xt_conntrack modules, iptables binary missing, or rule text mismatch after a version upgrade (the Add and Del paths must build byte-identical args).","commonSituations":"Daemon shutdown inside an unprivileged container; downgrade/upgrade of tailscaled where fwmarkMask or rule construction changed; systems where another firewall tool rewrote or flushed chains mid-teardown.","solutions":["Run with root/CAP_NET_ADMIN so mangle/OUTPUT is writable","Check the rule manually: iptables-save -t mangle | grep save-mark, then delete the exact rule by hand","modprobe xt_connmark xt_conntrack if matches are missing","Treat as best-effort during shutdown and log it; the rule set is rebuilt on next start"],"exampleFix":"// before\nif err := ipt.DelConnmarkSaveRule(); err != nil {\n\treturn err\n}\n// after\nif err := ipt.DelConnmarkSaveRule(); err != nil {\n\tlogf(\"mangle/OUTPUT connmark cleanup failed: %v\", err)\n\t// continue shutdown; stale rules are replaced on next AddConnmarkSaveRule\n}","handlingStrategy":"try-catch","validationCode":"func mangleWritable(ipt *iptables.IPTables) bool {\n\treturn ipt.Probe() == nil // iptables binary reachable and responsive\n}","typeGuard":null,"tryCatchPattern":"if err := ipt.DelConnmarkSaveRule(); err != nil {\n\tlogf(\"mangle/OUTPUT connmark cleanup: %v\", err)\n\t// continue shutdown; do not abort the process over stale marks\n}","preventionTips":["Grant CAP_NET_ADMIN before any firewall teardown runs","Never mix versions between the process that adds and the one that deletes rules","Load xt_connmark/xt_conntrack on minimal hosts at boot","Log cleanup errors with the full wrapped chain for diagnosis"],"tags":["iptables","connmark","mangle-table","firewall-cleanup","permissions","go","tailscale"],"backgroundTag":"iptables-rule-deletion-failed","analyzedSha":"6e0912f97994f927632b34ae9e63b53d6516a6ac","analyzedAt":"2026-08-18T08:17:25.280Z","contentChangedAt":"2026-08-18T08:17:25.280Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}