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@xxxxxx> --- 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 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) -- 2.43.0