[PATCH net-next 7/8] netfilter: nf_tables: call set ops .commit when building new ruleset blob
From: Pablo Neira Ayuso <pablo@netfilter.org>
Date: 2026-08-17 23:30:15
Also in:
netfilter-devel
Subsystem:
netfilter, networking [general], the rest · Maintainers:
Pablo Neira Ayuso, Florian Westphal, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds
The rbtree set only builds the b-search array after the new ruleset has
been published through set ops .commit.
This exposes an empty set for a short time span which results in a bogus
mismatch for the following batch:
destroy table ip x
table ip x {
...
}
The same problem also affects the pipapo set backend which also provides
a set ops .commit interface too.
This patch moves the set ops .commit call right before building and
publishing the chain blob. The commit path now performs an early
handling of the DELSETELEM command to remove stale elements from the
clone before it is published via rcu. Note that DELSETELEM notifications
are still delivered in order. NEWSETELEM commands are handled after the
set is published, since this clears the previous genbit to 1 to prepare
the element for the next control plane transaction. This comes at the
cost of one extra iteration over the transaction list.
Suggested-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
net/netfilter/nf_tables_api.c | 56 ++++++++++++++++++++++++++++++-----
1 file changed, 48 insertions(+), 8 deletions(-)
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index b51ba77b5151..c112ecc4fca3 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c@@ -7191,16 +7191,19 @@ static void nft_setelem_remove(const struct net *net, } static void nft_trans_elems_remove(const struct nft_ctx *ctx, - const struct nft_trans_elem *te) + const struct nft_trans_elem *te, + bool notify) { int i; for (i = 0; i < te->nelems; i++) { WARN_ON_ONCE(te->elems[i].update); - nf_tables_setelem_notify(ctx, te->set, - te->elems[i].priv, - te->nft_trans.msg_type); + if (notify) { + nf_tables_setelem_notify(ctx, te->set, + te->elems[i].priv, + te->nft_trans.msg_type); + } nft_setelem_remove(ctx->net, te->set, te->elems[i].priv); if (!nft_setelem_is_catchall(te->set, te->elems[i].priv)) {
@@ -7210,6 +7213,20 @@ static void nft_trans_elems_remove(const struct nft_ctx *ctx, } } +static void nft_trans_elems_remove_notify(const struct nft_ctx *ctx, + const struct nft_trans_elem *te) +{ + int i; + + for (i = 0; i < te->nelems; i++) { + WARN_ON_ONCE(te->elems[i].update); + + nf_tables_setelem_notify(ctx, te->set, + te->elems[i].priv, + te->nft_trans.msg_type); + } +} + static bool nft_setelem_valid_key_end(const struct nft_set *set, struct nlattr **nla, u32 flags) {
@@ -10863,9 +10880,29 @@ static void nf_tables_commit_audit_log(struct list_head *adl, u32 generation) } } -static void nft_set_commit_update(struct nftables_pernet *nft_net) +static void nft_set_commit_update(struct nft_ctx *ctx, + struct nftables_pernet *nft_net) { struct nft_set *set, *next; + struct nft_trans_elem *te; + struct nft_trans *trans; + + if (list_empty(&nft_net->set_update_list)) + return; + + list_for_each_entry(trans, &nft_net->commit_list, list) { + nft_ctx_update(ctx, trans); + + switch (trans->msg_type) { + case NFT_MSG_DELSETELEM: + te = nft_trans_container_elem(trans); + if (!te->set->ops->commit) + break; + + nft_trans_elems_remove(ctx, te, false); + break; + } + } list_for_each_entry_safe(set, next, &nft_net->set_update_list, pending_update) { list_del_init(&set->pending_update);
@@ -10974,6 +11011,8 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb) } /* step 2. Make rules_gen_X visible to packet path */ + nft_set_commit_update(&ctx, nft_net); + list_for_each_entry(table, &nft_net->tables, list) { list_for_each_entry(chain, &table->chains, list) nf_tables_commit_chain(net, chain);
@@ -11111,7 +11150,10 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb) case NFT_MSG_DELSETELEM: case NFT_MSG_DESTROYSETELEM: te = nft_trans_container_elem(trans); - nft_trans_elems_remove(&ctx, te); + if (te->set->ops->commit) + nft_trans_elems_remove_notify(&ctx, te); + else + nft_trans_elems_remove(&ctx, te, true); break; case NFT_MSG_NEWOBJ: if (nft_trans_obj_update(trans)) {
@@ -11180,8 +11222,6 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb) } } - nft_set_commit_update(nft_net); - nft_commit_notify(net, NETLINK_CB(skb).portid); nf_tables_gen_notify(net, skb, NFT_MSG_NEWGEN); nf_tables_commit_audit_log(&adl, nft_base_seq(net));
--
2.47.3