{"record":{"id":"ca6f000dcd638061","repo":"tailscale/tailscale","slug":"error-listing-rules-v","errorCode":null,"errorMessage":"error listing rules: %v","messagePattern":"error listing rules: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"util/linuxfw/iptables_runner.go","lineNumber":278,"sourceCode":"\t\treturn fmt.Errorf(\"adding %v in v4/filter/ts-forward: %w\", args, err)\n\t}\n\n\treturn nil\n}\n\nfunc (i *iptablesRunner) AddDNATRule(origDst, dst netip.Addr) error {\n\ttable := i.getIPTByAddr(dst)\n\treturn table.Insert(\"nat\", \"PREROUTING\", 1, \"--destination\", origDst.String(), \"-j\", \"DNAT\", \"--to-destination\", dst.String())\n}\n\n// EnsureSNATForDst sets up firewall to ensure that all traffic aimed for dst, has its source ip set to src:\n// - creates a SNAT rule if not already present\n// - ensures that any no longer valid SNAT rules for the same dst are removed\nfunc (i *iptablesRunner) EnsureSNATForDst(src, dst netip.Addr) error {\n\ttable := i.getIPTByAddr(dst)\n\trules, err := table.List(\"nat\", \"POSTROUTING\")\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error listing rules: %v\", err)\n\t}\n\t// iptables accept either address or a CIDR value for the --destination flag, but converts an address to /32\n\t// CIDR. Explicitly passing a /32 CIDR made it possible to test this rule.\n\tdstPrefix, err := dst.Prefix(32)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error calculating prefix of dst %v: %v\", dst, err)\n\t}\n\n\t// wantsArgsPrefix is the prefix of the SNAT rule for the provided destination.\n\t// We should only have one POSTROUTING rule with this prefix.\n\twantsArgsPrefix := fmt.Sprintf(\"-d %s -j SNAT --to-source\", dstPrefix.String())\n\t// wantsArgs is the actual SNAT rule that we want.\n\twantsArgs := fmt.Sprintf(\"%s %s\", wantsArgsPrefix, src.String())\n\tfor _, r := range rules {\n\t\targs := argsFromPostRoutingRule(r)\n\t\tif strings.HasPrefix(args, wantsArgsPrefix) {\n\t\t\tif strings.HasPrefix(args, wantsArgs) {\n\t\t\t\treturn nil","sourceCodeStart":260,"sourceCodeEnd":296,"githubUrl":"https://github.com/tailscale/tailscale/blob/6e0912f97994f927632b34ae9e63b53d6516a6ac/util/linuxfw/iptables_runner.go#L260-L296","documentation":"EnsureSNATForDst failed listing existing nat/POSTROUTING rules (table.List) with %v; without the listing it cannot decide which SNAT rules are stale versus present. Causes: iptables execution failure, missing nat table/module, or lost privileges.","triggerScenarios":"Thrown at util/linuxfw/iptables_runner.go:278 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Verify CAP_NET_ADMIN and that the nat table is available (iptables_nat module)","Retry listing — transient failures under concurrent netfilter access occur","Log the raw iptables output captured in the error to pinpoint the failing invocation"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"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"}