diff --git a/daemon/libnetwork/iptables/iptables.go b/daemon/libnetwork/iptables/iptables.go index 72095af329..ecb10e86f1 100644 --- a/daemon/libnetwork/iptables/iptables.go +++ b/daemon/libnetwork/iptables/iptables.go @@ -396,6 +396,14 @@ func (iptable IPTable) ExistChain(chain string, table Table) bool { return err == nil } +// FlushChain flush chain if it exists +func (iptable IPTable) FlushChain(table Table, chain string) error { + if !iptable.ExistChain(chain, table) { + return nil + } + return iptable.RawCombinedOutput("-t", string(table), "-F", chain) +} + // SetDefaultPolicy sets the passed default policy for the table/chain func (iptable IPTable) SetDefaultPolicy(table Table, chain string, policy Policy) error { if err := iptable.RawCombinedOutput("-t", string(table), "-P", chain, string(policy)); err != nil { diff --git a/daemon/libnetwork/iptables/iptables_test.go b/daemon/libnetwork/iptables/iptables_test.go index 1cbdcf3b51..92c1831c60 100644 --- a/daemon/libnetwork/iptables/iptables_test.go +++ b/daemon/libnetwork/iptables/iptables_test.go @@ -3,6 +3,7 @@ package iptables import ( + "fmt" "net" "net/netip" "os/exec" @@ -308,3 +309,38 @@ func mustDumpChain(t *testing.T, table Table, chain string) string { assert.NilError(t, err, "output:\n%s", out) return string(out) } + +func TestFlushChain(t *testing.T) { + _ = firewalldInit() + if UsingFirewalld() { + t.Skip("firewalld in host netns cannot create rules in the test's netns") + } + defer netnsutils.SetupTestOSContext(t)() + + iptable := GetIptable(IPv4) + chain := "TESTFLUSHCHAIN" + table := Filter + + // Ensure the chain exists + assert.NilError(t, iptable.RemoveExistingChain(chain, table)) + _, err := iptable.NewChain(chain, table) + assert.NilError(t, err) + + // Add a rule to the chain + rule := Rule{IPVer: IPv4, Table: table, Chain: chain, + Args: []string{"-j", "ACCEPT"}} + assert.NilError(t, rule.Insert()) + + // Flush the chain + assert.NilError(t, iptable.FlushChain(table, chain)) + + // Check that the chain exists and is empty (only the chain definition remains) + out, err := exec.Command("iptables", "-t", string(table), "-S", chain).CombinedOutput() + assert.NilError(t, err) + + rulesCount := strings.Count(string(out), fmt.Sprintf("-A %s ", chain)) + assert.Check(t, rulesCount == 0) + + // Cleanup + _ = iptable.RemoveExistingChain(chain, table) +}