From 312ff073e117d6207c3ba304ca01532e723d6e54 Mon Sep 17 00:00:00 2001 From: Phil Sutter Date: Wed, 14 Aug 2024 14:23:33 +0200 Subject: [PATCH] nft: Fix for zeroing non-existent builtin chains JIRA: https://issues.redhat.com/browse/RHEL-49497 Upstream Status: iptables commit f462975fb8049b9b565cb35cc98302331dbd1548 commit f462975fb8049b9b565cb35cc98302331dbd1548 Author: Phil Sutter Date: Tue Jul 16 21:07:31 2024 +0200 nft: Fix for zeroing non-existent builtin chains 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 Signed-off-by: Phil Sutter --- 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 c4caf29..82eab48 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -3109,9 +3109,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: @@ -3748,6 +3760,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", @@ -3758,8 +3771,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); @@ -3823,6 +3839,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 0000000..bbf1af7 --- /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)