diff mbox series

[iptables] nft: Fix for zeroing non-existent builtin chains

Message ID 20240717104353.8915-1-phil@nwl.cc
State Accepted
Headers show
Series [iptables] nft: Fix for zeroing non-existent builtin chains | expand

Commit Message

Phil Sutter July 17, 2024, 10:43 a.m. UTC
Trying to zero a specific rule in an entirely empty ruleset caused an
error:

| # nft flush ruleset
| # iptables-nft -Z INPUT
| iptables v1.8.10 (nf_tables):  CHAIN_ZERO failed (No such file or directory): chain INPUT

To fix this, start by faking any non-existing builtin chains so verbose
mode prints all the would-be-flushed chains. Later set 'skip' flag if
given chain is a fake one (indicated by missing HANDLE attribute).
Finally cover for concurrent ruleset updates by checking whether the
chain exists.

This bug seems to exist for a long time already, Fixes tag identified
via git-bisect. This patch won't apply to such old trees though, but
calling nft_xt_builtin_init() from nft_chain_zero_counters() should work
there.

Fixes: a6ce0c65d3a39 ("xtables: Optimize nft_chain_zero_counters()")
Signed-off-by: Phil Sutter <phil@nwl.cc>
---
 iptables/nft.c                                | 22 +++++++++++++++++--
 .../nft-only/0013-zero-non-existent_0         | 17 ++++++++++++++
 2 files changed, 37 insertions(+), 2 deletions(-)
 create mode 100755 iptables/tests/shell/testcases/nft-only/0013-zero-non-existent_0

Comments

Phil Sutter July 27, 2024, 12:33 p.m. UTC | #1
On Wed, Jul 17, 2024 at 12:43:53PM +0200, Phil Sutter wrote:
> Trying to zero a specific rule in an entirely empty ruleset caused an
> error:
> 
> | # nft flush ruleset
> | # iptables-nft -Z INPUT
> | iptables v1.8.10 (nf_tables):  CHAIN_ZERO failed (No such file or directory): chain INPUT
> 
> To fix this, start by faking any non-existing builtin chains so verbose
> mode prints all the would-be-flushed chains. Later set 'skip' flag if
> given chain is a fake one (indicated by missing HANDLE attribute).
> Finally cover for concurrent ruleset updates by checking whether the
> chain exists.
> 
> This bug seems to exist for a long time already, Fixes tag identified
> via git-bisect. This patch won't apply to such old trees though, but
> calling nft_xt_builtin_init() from nft_chain_zero_counters() should work
> there.
> 
> Fixes: a6ce0c65d3a39 ("xtables: Optimize nft_chain_zero_counters()")
> Signed-off-by: Phil Sutter <phil@nwl.cc>

Patch applied.
diff mbox series

Patch

diff --git a/iptables/nft.c b/iptables/nft.c
index 83fb81439ccb1..a9d97d4cef8e0 100644
--- a/iptables/nft.c
+++ b/iptables/nft.c
@@ -3167,9 +3167,21 @@  static void nft_refresh_transaction(struct nft_handle *h)
 				break;
 			n->skip = !nft_may_delete_chain(n->chain);
 			break;
+		case NFT_COMPAT_CHAIN_ZERO:
+			tablename = nftnl_chain_get_str(n->chain, NFTNL_CHAIN_TABLE);
+			if (!tablename)
+				continue;
+
+			chainname = nftnl_chain_get_str(n->chain, NFTNL_CHAIN_NAME);
+			if (!chainname)
+				continue;
+
+			n->skip = nftnl_chain_is_set(n->chain,
+						     NFTNL_CHAIN_HOOKNUM) &&
+				  !nft_chain_find(h, tablename, chainname);
+			break;
 		case NFT_COMPAT_TABLE_ADD:
 		case NFT_COMPAT_CHAIN_ADD:
-		case NFT_COMPAT_CHAIN_ZERO:
 		case NFT_COMPAT_CHAIN_USER_FLUSH:
 		case NFT_COMPAT_CHAIN_UPDATE:
 		case NFT_COMPAT_CHAIN_RENAME:
@@ -3817,6 +3829,7 @@  static int __nft_chain_zero_counters(struct nft_chain *nc, void *data)
 	struct nft_handle *h = d->handle;
 	struct nftnl_rule_iter *iter;
 	struct nftnl_rule *r;
+	struct obj_update *o;
 
 	if (d->verbose)
 		fprintf(stdout, "Zeroing chain `%s'\n",
@@ -3827,8 +3840,11 @@  static int __nft_chain_zero_counters(struct nft_chain *nc, void *data)
 		nftnl_chain_set_u64(c, NFTNL_CHAIN_PACKETS, 0);
 		nftnl_chain_set_u64(c, NFTNL_CHAIN_BYTES, 0);
 		nftnl_chain_unset(c, NFTNL_CHAIN_HANDLE);
-		if (!batch_chain_add(h, NFT_COMPAT_CHAIN_ZERO, c))
+		o = batch_chain_add(h, NFT_COMPAT_CHAIN_ZERO, c);
+		if (!o)
 			return -1;
+		/* may skip if it is a fake entry */
+		o->skip = !nftnl_chain_is_set(c, NFTNL_CHAIN_HANDLE);
 	}
 
 	iter = nftnl_rule_iter_create(c);
@@ -3892,6 +3908,8 @@  int nft_chain_zero_counters(struct nft_handle *h, const char *chain,
 	struct nft_chain *c;
 	int ret = 0;
 
+	nft_xt_fake_builtin_chains(h, table, chain);
+
 	if (chain) {
 		c = nft_chain_find(h, table, chain);
 		if (!c) {
diff --git a/iptables/tests/shell/testcases/nft-only/0013-zero-non-existent_0 b/iptables/tests/shell/testcases/nft-only/0013-zero-non-existent_0
new file mode 100755
index 0000000000000..bbf1af760837d
--- /dev/null
+++ b/iptables/tests/shell/testcases/nft-only/0013-zero-non-existent_0
@@ -0,0 +1,17 @@ 
+#!/bin/bash
+
+[[ $XT_MULTI == *xtables-nft-multi ]] || { echo "skip $XT_MULTI"; exit 0; }
+nft --version >/dev/null 2>&1 || { echo "skip nft"; exit 0; }
+
+set -e
+
+nft flush ruleset
+$XT_MULTI iptables -Z INPUT
+
+EXP="Zeroing chain \`INPUT'"
+diff -u <(echo "$EXP") <($XT_MULTI iptables -v -Z INPUT)
+
+EXP="Zeroing chain \`INPUT'
+Zeroing chain \`FORWARD'
+Zeroing chain \`OUTPUT'"
+diff -u <(echo "$EXP") <($XT_MULTI iptables -v -Z)