Merge pull request #53618 from thaJeztah/fix_iptables_rm

iptables: handle missing rules on delete
This commit is contained in:
Paweł Gronowski authored and GitHub committed 2026-09-14 21:20:44 +02:00
commit f29c5d8007
2 files changed
+58 -2

No files matched your search

+28 -2
View File
@@ -440,7 +440,11 @@ func (iptable IPTable) DeleteJumpRule(table Table, fromChain, toChain string, ru
rule = append(rule, "-j", toChain)
if iptable.Exists(table, fromChain, rule...) {
if err := iptable.RawCombinedOutput(append([]string{"-t", string(table), "-D", fromChain}, rule...)...); err != nil {
return fmt.Errorf("unable to remove jump to %s rule in %s chain: %v", toChain, fromChain, err)
// Another process may have removed the rule between Exists and Delete.
if isRuleNotFoundError(err) {
return nil
}
return fmt.Errorf("unable to remove jump to %s rule in %s chain: %w", toChain, fromChain, err)
}
}
return nil
@@ -500,7 +504,14 @@ func (r Rule) Delete() error {
if !r.Exists() {
return nil
}
return r.exec(Delete)
if err := r.exec(Delete); err != nil {
// Another process may have removed the rule between Exists and Delete.
if isRuleNotFoundError(err) {
return nil
}
return err
}
return nil
}
func (r Rule) String() string {
@@ -510,3 +521,18 @@ func (r Rule) String() string {
}
return strings.Join(cmd, " ")
}
// isRuleNotFoundError reports whether err indicates that an iptables
// delete could not find a matching rule.
//
// The iptables-nft backend maps ENOENT from nft_rule_delete to this
// diagnostic:
//
// iptables: Bad rule (does a matching rule exist in that chain?).
//
// see: https://git.netfilter.org/iptables/tree/iptables/nft.c?id=384958620abab397062b67fb2763e813b63f74f0#n2745
// ref: https://git.netfilter.org/iptables/commit/?id=384958620abab397062b67fb2763e813b63f74f0
func isRuleNotFoundError(err error) bool {
return err != nil &&
strings.Contains(err.Error(), "Bad rule (does a matching rule exist in that chain?)")
}
@@ -336,3 +336,33 @@ func TestFlushChain(t *testing.T) {
// Cleanup
_ = iptable.RemoveExistingChain(chain, table)
}
// TestDeleteNonExistingRule verifies the diagnostic returned by iptables when
// deleting a rule that does not exist. This is used to handle the race where a
// rule is removed by another process between checking for it and deleting it.
//
// Reproducing that race directly would be timing-dependent, so exercise the
// delete error deterministically with a rule in a freshly-created chain.
func TestDeleteNonExistingRule(t *testing.T) {
if UsingFirewalld() {
t.Skip("firewalld in host netns cannot create rules in the test's netns")
}
defer netnsutils.SetupTestOSContext(t)()
iptable := GetIptable(IPv4)
const chain = "TEST-DELETEMISSING"
_, err := iptable.NewChain(chain, Filter)
assert.NilError(t, err)
defer func() { _ = iptable.RemoveExistingChain(chain, Filter) }()
// Use TEST-NET-1 to make the rule clearly test-only. The freshly-created
// chain guarantees that the rule does not already exist.
err = iptable.RawCombinedOutput(
"-t", string(Filter),
"-D", chain,
"-s", "192.0.2.1",
"-j", "ACCEPT",
)
assert.Assert(t, err != nil)
assert.Assert(t, isRuleNotFoundError(err), err)
}