632 lines
21 KiB
Diff
632 lines
21 KiB
Diff
From 56d449ab32e8a5457a93885e2d7a3e8d141ad11c Mon Sep 17 00:00:00 2001
|
|
From: Florian Westphal <fwestpha@redhat.com>
|
|
Date: Wed, 13 May 2026 17:23:03 +0200
|
|
Subject: [PATCH] netfilter: nf_tables: fix netdev hook allocation memleak with
|
|
dormant tables
|
|
|
|
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
|
Upstream Status: commit 63bac0278603
|
|
|
|
commit 63bac027860308d1344f761cb47aabb3b30973fd
|
|
Author: Florian Westphal <fw@strlen.de>
|
|
Date: Wed Apr 29 08:21:35 2026 +0200
|
|
|
|
netfilter: nf_tables: fix netdev hook allocation memleak with dormant tables
|
|
|
|
sashiko says:
|
|
could the related code in __nf_tables_abort() leak the struct nft_hook objects when the table is dormant?
|
|
|
|
In __nf_tables_abort(), when rolling back a NEWCHAIN transaction that
|
|
updates hooks, the code conditionally unregisters and frees the hooks only
|
|
if the table is not dormant [..]
|
|
if (!(table->flags & NFT_TABLE_F_DORMANT)) {
|
|
nft_netdev_unregister_hooks(net,
|
|
&nft_trans_chain_hooks(trans),
|
|
true);
|
|
}
|
|
...
|
|
nft_trans_destroy(trans);
|
|
|
|
Unfortunately netdev family mixes hook registration and allocation.
|
|
Push table struct down and only check for the flag to unregister.
|
|
|
|
Fixes: 216e7bf7402c ("netfilter: nf_tables: skip netdev hook unregistration if table is dormant")
|
|
Signed-off-by: Florian Westphal <fw@strlen.de>
|
|
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
|
|
|
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
|
|
|
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
|
|
index 188a621..9883e64 100644
|
|
--- a/net/netfilter/nf_tables_api.c
|
|
+++ b/net/netfilter/nf_tables_api.c
|
|
@@ -379,7 +379,34 @@ static void nft_netdev_hook_unlink_free_rcu(struct nft_hook *hook)
|
|
nft_netdev_hook_free_rcu(hook);
|
|
}
|
|
|
|
+static void nft_trans_hook_destroy(struct nft_trans_hook *trans_hook)
|
|
+{
|
|
+ list_del(&trans_hook->list);
|
|
+ kfree(trans_hook);
|
|
+}
|
|
+
|
|
+static void nft_netdev_unregister_trans_hook(struct net *net,
|
|
+ const struct nft_table *table,
|
|
+ struct list_head *hook_list)
|
|
+{
|
|
+ struct nft_trans_hook *trans_hook, *next;
|
|
+ struct nf_hook_ops *ops;
|
|
+ struct nft_hook *hook;
|
|
+
|
|
+ list_for_each_entry_safe(trans_hook, next, hook_list, list) {
|
|
+ hook = trans_hook->hook;
|
|
+
|
|
+ if (!(table->flags & NFT_TABLE_F_DORMANT)) {
|
|
+ list_for_each_entry(ops, &hook->ops_list, list)
|
|
+ nf_unregister_net_hook(net, ops);
|
|
+ }
|
|
+ nft_netdev_hook_unlink_free_rcu(hook);
|
|
+ nft_trans_hook_destroy(trans_hook);
|
|
+ }
|
|
+}
|
|
+
|
|
static void nft_netdev_unregister_hooks(struct net *net,
|
|
+ const struct nft_table *table,
|
|
struct list_head *hook_list,
|
|
bool release_netdev)
|
|
{
|
|
@@ -387,8 +414,10 @@ static void nft_netdev_unregister_hooks(struct net *net,
|
|
struct nf_hook_ops *ops;
|
|
|
|
list_for_each_entry_safe(hook, next, hook_list, list) {
|
|
- list_for_each_entry(ops, &hook->ops_list, list)
|
|
- nf_unregister_net_hook(net, ops);
|
|
+ if (!(table->flags & NFT_TABLE_F_DORMANT)) {
|
|
+ list_for_each_entry(ops, &hook->ops_list, list)
|
|
+ nf_unregister_net_hook(net, ops);
|
|
+ }
|
|
if (release_netdev)
|
|
nft_netdev_hook_unlink_free_rcu(hook);
|
|
}
|
|
@@ -425,20 +454,25 @@ static void __nf_tables_unregister_hook(struct net *net,
|
|
struct nft_base_chain *basechain;
|
|
const struct nf_hook_ops *ops;
|
|
|
|
- if (table->flags & NFT_TABLE_F_DORMANT ||
|
|
- !nft_is_base_chain(chain))
|
|
+ if (!nft_is_base_chain(chain))
|
|
return;
|
|
basechain = nft_base_chain(chain);
|
|
ops = &basechain->ops;
|
|
|
|
+ /* must also be called for dormant tables */
|
|
+ if (nft_base_chain_netdev(table->family, basechain->ops.hooknum)) {
|
|
+ nft_netdev_unregister_hooks(net, table, &basechain->hook_list,
|
|
+ release_netdev);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (table->flags & NFT_TABLE_F_DORMANT)
|
|
+ return;
|
|
+
|
|
if (basechain->type->ops_unregister)
|
|
return basechain->type->ops_unregister(net, ops);
|
|
|
|
- if (nft_base_chain_netdev(table->family, basechain->ops.hooknum))
|
|
- nft_netdev_unregister_hooks(net, &basechain->hook_list,
|
|
- release_netdev);
|
|
- else
|
|
- nf_unregister_net_hook(net, &basechain->ops);
|
|
+ nf_unregister_net_hook(net, &basechain->ops);
|
|
}
|
|
|
|
static void nf_tables_unregister_hook(struct net *net,
|
|
@@ -1991,15 +2025,69 @@ static int nft_nla_put_hook_dev(struct sk_buff *skb, struct nft_hook *hook)
|
|
return nla_put_string(skb, attr, hook->ifname);
|
|
}
|
|
|
|
+struct nft_hook_dump_ctx {
|
|
+ struct nft_hook *first;
|
|
+ int n;
|
|
+};
|
|
+
|
|
+static int nft_dump_basechain_hook_one(struct sk_buff *skb,
|
|
+ struct nft_hook *hook,
|
|
+ struct nft_hook_dump_ctx *dump_ctx)
|
|
+{
|
|
+ if (!dump_ctx->first)
|
|
+ dump_ctx->first = hook;
|
|
+
|
|
+ if (nft_nla_put_hook_dev(skb, hook))
|
|
+ return -1;
|
|
+
|
|
+ dump_ctx->n++;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int nft_dump_basechain_hook_list(struct sk_buff *skb,
|
|
+ const struct net *net,
|
|
+ const struct list_head *hook_list,
|
|
+ struct nft_hook_dump_ctx *dump_ctx)
|
|
+{
|
|
+ struct nft_hook *hook;
|
|
+ int err;
|
|
+
|
|
+ list_for_each_entry_rcu(hook, hook_list, list,
|
|
+ lockdep_commit_lock_is_held(net)) {
|
|
+ err = nft_dump_basechain_hook_one(skb, hook, dump_ctx);
|
|
+ if (err < 0)
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int nft_dump_basechain_trans_hook_list(struct sk_buff *skb,
|
|
+ const struct list_head *trans_hook_list,
|
|
+ struct nft_hook_dump_ctx *dump_ctx)
|
|
+{
|
|
+ struct nft_trans_hook *trans_hook;
|
|
+ int err;
|
|
+
|
|
+ list_for_each_entry(trans_hook, trans_hook_list, list) {
|
|
+ err = nft_dump_basechain_hook_one(skb, trans_hook->hook, dump_ctx);
|
|
+ if (err < 0)
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static int nft_dump_basechain_hook(struct sk_buff *skb,
|
|
const struct net *net, int family,
|
|
const struct nft_base_chain *basechain,
|
|
- const struct list_head *hook_list)
|
|
+ const struct list_head *hook_list,
|
|
+ const struct list_head *trans_hook_list)
|
|
{
|
|
const struct nf_hook_ops *ops = &basechain->ops;
|
|
- struct nft_hook *hook, *first = NULL;
|
|
+ struct nft_hook_dump_ctx dump_hook_ctx = {};
|
|
struct nlattr *nest, *nest_devs;
|
|
- int n = 0;
|
|
|
|
nest = nla_nest_start_noflag(skb, NFTA_CHAIN_HOOK);
|
|
if (nest == NULL)
|
|
@@ -2014,23 +2102,23 @@ static int nft_dump_basechain_hook(struct sk_buff *skb,
|
|
if (!nest_devs)
|
|
goto nla_put_failure;
|
|
|
|
- if (!hook_list)
|
|
+ if (!hook_list && !trans_hook_list)
|
|
hook_list = &basechain->hook_list;
|
|
|
|
- list_for_each_entry_rcu(hook, hook_list, list,
|
|
- lockdep_commit_lock_is_held(net)) {
|
|
- if (!first)
|
|
- first = hook;
|
|
-
|
|
- if (nft_nla_put_hook_dev(skb, hook))
|
|
- goto nla_put_failure;
|
|
- n++;
|
|
+ if (hook_list &&
|
|
+ nft_dump_basechain_hook_list(skb, net, hook_list, &dump_hook_ctx)) {
|
|
+ goto nla_put_failure;
|
|
+ } else if (trans_hook_list &&
|
|
+ nft_dump_basechain_trans_hook_list(skb, trans_hook_list,
|
|
+ &dump_hook_ctx)) {
|
|
+ goto nla_put_failure;
|
|
}
|
|
+
|
|
nla_nest_end(skb, nest_devs);
|
|
|
|
- if (n == 1 &&
|
|
- !hook_is_prefix(first) &&
|
|
- nla_put_string(skb, NFTA_HOOK_DEV, first->ifname))
|
|
+ if (dump_hook_ctx.n == 1 &&
|
|
+ !hook_is_prefix(dump_hook_ctx.first) &&
|
|
+ nla_put_string(skb, NFTA_HOOK_DEV, dump_hook_ctx.first->ifname))
|
|
goto nla_put_failure;
|
|
}
|
|
nla_nest_end(skb, nest);
|
|
@@ -2044,7 +2132,8 @@ static int nf_tables_fill_chain_info(struct sk_buff *skb, struct net *net,
|
|
u32 portid, u32 seq, int event, u32 flags,
|
|
int family, const struct nft_table *table,
|
|
const struct nft_chain *chain,
|
|
- const struct list_head *hook_list)
|
|
+ const struct list_head *hook_list,
|
|
+ const struct list_head *trans_hook_list)
|
|
{
|
|
struct nlmsghdr *nlh;
|
|
|
|
@@ -2060,7 +2149,7 @@ static int nf_tables_fill_chain_info(struct sk_buff *skb, struct net *net,
|
|
NFTA_CHAIN_PAD))
|
|
goto nla_put_failure;
|
|
|
|
- if (event == NFT_MSG_DELCHAIN && !hook_list) {
|
|
+ if (event == NFT_MSG_DELCHAIN && !hook_list && !trans_hook_list) {
|
|
nlmsg_end(skb, nlh);
|
|
return 0;
|
|
}
|
|
@@ -2069,7 +2158,8 @@ static int nf_tables_fill_chain_info(struct sk_buff *skb, struct net *net,
|
|
const struct nft_base_chain *basechain = nft_base_chain(chain);
|
|
struct nft_stats __percpu *stats;
|
|
|
|
- if (nft_dump_basechain_hook(skb, net, family, basechain, hook_list))
|
|
+ if (nft_dump_basechain_hook(skb, net, family, basechain,
|
|
+ hook_list, trans_hook_list))
|
|
goto nla_put_failure;
|
|
|
|
if (nla_put_be32(skb, NFTA_CHAIN_POLICY,
|
|
@@ -2105,7 +2195,8 @@ static int nf_tables_fill_chain_info(struct sk_buff *skb, struct net *net,
|
|
}
|
|
|
|
static void nf_tables_chain_notify(const struct nft_ctx *ctx, int event,
|
|
- const struct list_head *hook_list)
|
|
+ const struct list_head *hook_list,
|
|
+ const struct list_head *trans_hook_list)
|
|
{
|
|
struct nftables_pernet *nft_net;
|
|
struct sk_buff *skb;
|
|
@@ -2125,7 +2216,7 @@ static void nf_tables_chain_notify(const struct nft_ctx *ctx, int event,
|
|
|
|
err = nf_tables_fill_chain_info(skb, ctx->net, ctx->portid, ctx->seq,
|
|
event, flags, ctx->family, ctx->table,
|
|
- ctx->chain, hook_list);
|
|
+ ctx->chain, hook_list, trans_hook_list);
|
|
if (err < 0) {
|
|
kfree_skb(skb);
|
|
goto err;
|
|
@@ -2171,7 +2262,7 @@ static int nf_tables_dump_chains(struct sk_buff *skb,
|
|
NFT_MSG_NEWCHAIN,
|
|
NLM_F_MULTI,
|
|
table->family, table,
|
|
- chain, NULL) < 0)
|
|
+ chain, NULL, NULL) < 0)
|
|
goto done;
|
|
|
|
nl_dump_check_consistent(cb, nlmsg_hdr(skb));
|
|
@@ -2225,7 +2316,7 @@ static int nf_tables_getchain(struct sk_buff *skb, const struct nfnl_info *info,
|
|
|
|
err = nf_tables_fill_chain_info(skb2, net, NETLINK_CB(skb).portid,
|
|
info->nlh->nlmsg_seq, NFT_MSG_NEWCHAIN,
|
|
- 0, family, table, chain, NULL);
|
|
+ 0, family, table, chain, NULL, NULL);
|
|
if (err < 0)
|
|
goto err_fill_chain_info;
|
|
|
|
@@ -2388,8 +2479,12 @@ static struct nft_hook *nft_hook_list_find(struct list_head *hook_list,
|
|
|
|
list_for_each_entry(hook, hook_list, list) {
|
|
if (!strncmp(hook->ifname, this->ifname,
|
|
- min(hook->ifnamelen, this->ifnamelen)))
|
|
+ min(hook->ifnamelen, this->ifnamelen))) {
|
|
+ if (hook->flags & NFT_HOOK_REMOVE)
|
|
+ continue;
|
|
+
|
|
return hook;
|
|
+ }
|
|
}
|
|
|
|
return NULL;
|
|
@@ -3148,6 +3243,32 @@ static int nf_tables_newchain(struct sk_buff *skb, const struct nfnl_info *info,
|
|
return nf_tables_addchain(&ctx, family, policy, flags, extack);
|
|
}
|
|
|
|
+static int nft_trans_delhook(struct nft_hook *hook,
|
|
+ struct list_head *del_list)
|
|
+{
|
|
+ struct nft_trans_hook *trans_hook;
|
|
+
|
|
+ trans_hook = kmalloc(sizeof(*trans_hook), GFP_KERNEL);
|
|
+ if (!trans_hook)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ trans_hook->hook = hook;
|
|
+ list_add_tail(&trans_hook->list, del_list);
|
|
+ hook->flags |= NFT_HOOK_REMOVE;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void nft_trans_delhook_abort(struct list_head *del_list)
|
|
+{
|
|
+ struct nft_trans_hook *trans_hook, *next;
|
|
+
|
|
+ list_for_each_entry_safe(trans_hook, next, del_list, list) {
|
|
+ trans_hook->hook->flags &= ~NFT_HOOK_REMOVE;
|
|
+ nft_trans_hook_destroy(trans_hook);
|
|
+ }
|
|
+}
|
|
+
|
|
static int nft_delchain_hook(struct nft_ctx *ctx,
|
|
struct nft_base_chain *basechain,
|
|
struct netlink_ext_ack *extack)
|
|
@@ -3174,7 +3295,10 @@ static int nft_delchain_hook(struct nft_ctx *ctx,
|
|
err = -ENOENT;
|
|
goto err_chain_del_hook;
|
|
}
|
|
- list_move(&hook->list, &chain_del_list);
|
|
+ if (nft_trans_delhook(hook, &chain_del_list) < 0) {
|
|
+ err = -ENOMEM;
|
|
+ goto err_chain_del_hook;
|
|
+ }
|
|
}
|
|
|
|
trans = nft_trans_alloc_chain(ctx, NFT_MSG_DELCHAIN);
|
|
@@ -3194,7 +3318,7 @@ static int nft_delchain_hook(struct nft_ctx *ctx,
|
|
return 0;
|
|
|
|
err_chain_del_hook:
|
|
- list_splice(&chain_del_list, &basechain->hook_list);
|
|
+ nft_trans_delhook_abort(&chain_del_list);
|
|
nft_chain_release_hook(&chain_hook);
|
|
|
|
return err;
|
|
@@ -9139,6 +9263,24 @@ static void nft_hooks_destroy(struct list_head *hook_list)
|
|
nft_netdev_hook_unlink_free_rcu(hook);
|
|
}
|
|
|
|
+static void nft_flowtable_unregister_trans_hook(struct net *net,
|
|
+ struct nft_flowtable *flowtable,
|
|
+ struct list_head *hook_list)
|
|
+{
|
|
+ struct nft_trans_hook *trans_hook, *next;
|
|
+ struct nf_hook_ops *ops;
|
|
+ struct nft_hook *hook;
|
|
+
|
|
+ list_for_each_entry_safe(trans_hook, next, hook_list, list) {
|
|
+ hook = trans_hook->hook;
|
|
+ list_for_each_entry(ops, &hook->ops_list, list)
|
|
+ nft_unregister_flowtable_ops(net, flowtable, ops);
|
|
+
|
|
+ nft_netdev_hook_unlink_free_rcu(hook);
|
|
+ nft_trans_hook_destroy(trans_hook);
|
|
+ }
|
|
+}
|
|
+
|
|
static int nft_flowtable_update(struct nft_ctx *ctx, const struct nlmsghdr *nlh,
|
|
struct nft_flowtable *flowtable,
|
|
struct netlink_ext_ack *extack)
|
|
@@ -9397,7 +9539,10 @@ static int nft_delflowtable_hook(struct nft_ctx *ctx,
|
|
err = -ENOENT;
|
|
goto err_flowtable_del_hook;
|
|
}
|
|
- list_move(&hook->list, &flowtable_del_list);
|
|
+ if (nft_trans_delhook(hook, &flowtable_del_list) < 0) {
|
|
+ err = -ENOMEM;
|
|
+ goto err_flowtable_del_hook;
|
|
+ }
|
|
}
|
|
|
|
trans = nft_trans_alloc(ctx, NFT_MSG_DELFLOWTABLE,
|
|
@@ -9418,7 +9563,7 @@ static int nft_delflowtable_hook(struct nft_ctx *ctx,
|
|
return 0;
|
|
|
|
err_flowtable_del_hook:
|
|
- list_splice(&flowtable_del_list, &flowtable->hook_list);
|
|
+ nft_trans_delhook_abort(&flowtable_del_list);
|
|
nft_flowtable_hook_release(&flowtable_hook);
|
|
|
|
return err;
|
|
@@ -9483,8 +9628,10 @@ static int nf_tables_fill_flowtable_info(struct sk_buff *skb, struct net *net,
|
|
u32 portid, u32 seq, int event,
|
|
u32 flags, int family,
|
|
struct nft_flowtable *flowtable,
|
|
- struct list_head *hook_list)
|
|
+ struct list_head *hook_list,
|
|
+ struct list_head *trans_hook_list)
|
|
{
|
|
+ struct nft_trans_hook *trans_hook;
|
|
struct nlattr *nest, *nest_devs;
|
|
struct nft_hook *hook;
|
|
struct nlmsghdr *nlh;
|
|
@@ -9501,7 +9648,7 @@ static int nf_tables_fill_flowtable_info(struct sk_buff *skb, struct net *net,
|
|
NFTA_FLOWTABLE_PAD))
|
|
goto nla_put_failure;
|
|
|
|
- if (event == NFT_MSG_DELFLOWTABLE && !hook_list) {
|
|
+ if (event == NFT_MSG_DELFLOWTABLE && !hook_list && !trans_hook_list) {
|
|
nlmsg_end(skb, nlh);
|
|
return 0;
|
|
}
|
|
@@ -9521,13 +9668,20 @@ static int nf_tables_fill_flowtable_info(struct sk_buff *skb, struct net *net,
|
|
if (!nest_devs)
|
|
goto nla_put_failure;
|
|
|
|
- if (!hook_list)
|
|
+ if (!hook_list && !trans_hook_list)
|
|
hook_list = &flowtable->hook_list;
|
|
|
|
- list_for_each_entry_rcu(hook, hook_list, list,
|
|
- lockdep_commit_lock_is_held(net)) {
|
|
- if (nft_nla_put_hook_dev(skb, hook))
|
|
- goto nla_put_failure;
|
|
+ if (hook_list) {
|
|
+ list_for_each_entry_rcu(hook, hook_list, list,
|
|
+ lockdep_commit_lock_is_held(net)) {
|
|
+ if (nft_nla_put_hook_dev(skb, hook))
|
|
+ goto nla_put_failure;
|
|
+ }
|
|
+ } else if (trans_hook_list) {
|
|
+ list_for_each_entry(trans_hook, trans_hook_list, list) {
|
|
+ if (nft_nla_put_hook_dev(skb, trans_hook->hook))
|
|
+ goto nla_put_failure;
|
|
+ }
|
|
}
|
|
nla_nest_end(skb, nest_devs);
|
|
nla_nest_end(skb, nest);
|
|
@@ -9581,7 +9735,7 @@ static int nf_tables_dump_flowtable(struct sk_buff *skb,
|
|
NFT_MSG_NEWFLOWTABLE,
|
|
NLM_F_MULTI | NLM_F_APPEND,
|
|
table->family,
|
|
- flowtable, NULL) < 0)
|
|
+ flowtable, NULL, NULL) < 0)
|
|
goto done;
|
|
|
|
nl_dump_check_consistent(cb, nlmsg_hdr(skb));
|
|
@@ -9681,7 +9835,7 @@ static int nf_tables_getflowtable(struct sk_buff *skb,
|
|
err = nf_tables_fill_flowtable_info(skb2, net, NETLINK_CB(skb).portid,
|
|
info->nlh->nlmsg_seq,
|
|
NFT_MSG_NEWFLOWTABLE, 0, family,
|
|
- flowtable, NULL);
|
|
+ flowtable, NULL, NULL);
|
|
if (err < 0)
|
|
goto err_fill_flowtable_info;
|
|
|
|
@@ -9694,7 +9848,9 @@ static int nf_tables_getflowtable(struct sk_buff *skb,
|
|
|
|
static void nf_tables_flowtable_notify(struct nft_ctx *ctx,
|
|
struct nft_flowtable *flowtable,
|
|
- struct list_head *hook_list, int event)
|
|
+ struct list_head *hook_list,
|
|
+ struct list_head *trans_hook_list,
|
|
+ int event)
|
|
{
|
|
struct nftables_pernet *nft_net = nft_pernet(ctx->net);
|
|
struct sk_buff *skb;
|
|
@@ -9714,7 +9870,8 @@ static void nf_tables_flowtable_notify(struct nft_ctx *ctx,
|
|
|
|
err = nf_tables_fill_flowtable_info(skb, ctx->net, ctx->portid,
|
|
ctx->seq, event, flags,
|
|
- ctx->family, flowtable, hook_list);
|
|
+ ctx->family, flowtable,
|
|
+ hook_list, trans_hook_list);
|
|
if (err < 0) {
|
|
kfree_skb(skb);
|
|
goto err;
|
|
@@ -10248,9 +10405,7 @@ static void nft_commit_release(struct nft_trans *trans)
|
|
break;
|
|
case NFT_MSG_DELCHAIN:
|
|
case NFT_MSG_DESTROYCHAIN:
|
|
- if (nft_trans_chain_update(trans))
|
|
- nft_hooks_destroy(&nft_trans_chain_hooks(trans));
|
|
- else
|
|
+ if (!nft_trans_chain_update(trans))
|
|
nf_tables_chain_destroy(nft_trans_chain(trans));
|
|
break;
|
|
case NFT_MSG_DELRULE:
|
|
@@ -10271,9 +10426,7 @@ static void nft_commit_release(struct nft_trans *trans)
|
|
break;
|
|
case NFT_MSG_DELFLOWTABLE:
|
|
case NFT_MSG_DESTROYFLOWTABLE:
|
|
- if (nft_trans_flowtable_update(trans))
|
|
- nft_hooks_destroy(&nft_trans_flowtable_hooks(trans));
|
|
- else
|
|
+ if (!nft_trans_flowtable_update(trans))
|
|
nf_tables_flowtable_destroy(nft_trans_flowtable(trans));
|
|
break;
|
|
}
|
|
@@ -11048,31 +11201,28 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
|
|
if (nft_trans_chain_update(trans)) {
|
|
nft_chain_commit_update(nft_trans_container_chain(trans));
|
|
nf_tables_chain_notify(&ctx, NFT_MSG_NEWCHAIN,
|
|
- &nft_trans_chain_hooks(trans));
|
|
+ &nft_trans_chain_hooks(trans), NULL);
|
|
list_splice_rcu(&nft_trans_chain_hooks(trans),
|
|
&nft_trans_basechain(trans)->hook_list);
|
|
/* trans destroyed after rcu grace period */
|
|
} else {
|
|
nft_chain_commit_drop_policy(nft_trans_container_chain(trans));
|
|
nft_clear(net, nft_trans_chain(trans));
|
|
- nf_tables_chain_notify(&ctx, NFT_MSG_NEWCHAIN, NULL);
|
|
+ nf_tables_chain_notify(&ctx, NFT_MSG_NEWCHAIN, NULL, NULL);
|
|
nft_trans_destroy(trans);
|
|
}
|
|
break;
|
|
case NFT_MSG_DELCHAIN:
|
|
case NFT_MSG_DESTROYCHAIN:
|
|
if (nft_trans_chain_update(trans)) {
|
|
- nf_tables_chain_notify(&ctx, NFT_MSG_DELCHAIN,
|
|
+ nf_tables_chain_notify(&ctx, NFT_MSG_DELCHAIN, NULL,
|
|
&nft_trans_chain_hooks(trans));
|
|
- if (!(table->flags & NFT_TABLE_F_DORMANT)) {
|
|
- nft_netdev_unregister_hooks(net,
|
|
- &nft_trans_chain_hooks(trans),
|
|
- true);
|
|
- }
|
|
+ nft_netdev_unregister_trans_hook(net, table,
|
|
+ &nft_trans_chain_hooks(trans));
|
|
} else {
|
|
nft_chain_del(nft_trans_chain(trans));
|
|
nf_tables_chain_notify(&ctx, NFT_MSG_DELCHAIN,
|
|
- NULL);
|
|
+ NULL, NULL);
|
|
nf_tables_unregister_hook(ctx.net, ctx.table,
|
|
nft_trans_chain(trans));
|
|
}
|
|
@@ -11178,6 +11328,7 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
|
|
nf_tables_flowtable_notify(&ctx,
|
|
nft_trans_flowtable(trans),
|
|
&nft_trans_flowtable_hooks(trans),
|
|
+ NULL,
|
|
NFT_MSG_NEWFLOWTABLE);
|
|
list_splice_rcu(&nft_trans_flowtable_hooks(trans),
|
|
&nft_trans_flowtable(trans)->hook_list);
|
|
@@ -11186,6 +11337,7 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
|
|
nf_tables_flowtable_notify(&ctx,
|
|
nft_trans_flowtable(trans),
|
|
NULL,
|
|
+ NULL,
|
|
NFT_MSG_NEWFLOWTABLE);
|
|
}
|
|
nft_trans_destroy(trans);
|
|
@@ -11195,16 +11347,18 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
|
|
if (nft_trans_flowtable_update(trans)) {
|
|
nf_tables_flowtable_notify(&ctx,
|
|
nft_trans_flowtable(trans),
|
|
+ NULL,
|
|
&nft_trans_flowtable_hooks(trans),
|
|
trans->msg_type);
|
|
- nft_unregister_flowtable_net_hooks(net,
|
|
- nft_trans_flowtable(trans),
|
|
- &nft_trans_flowtable_hooks(trans));
|
|
+ nft_flowtable_unregister_trans_hook(net,
|
|
+ nft_trans_flowtable(trans),
|
|
+ &nft_trans_flowtable_hooks(trans));
|
|
} else {
|
|
list_del_rcu(&nft_trans_flowtable(trans)->list);
|
|
nf_tables_flowtable_notify(&ctx,
|
|
nft_trans_flowtable(trans),
|
|
NULL,
|
|
+ NULL,
|
|
trans->msg_type);
|
|
nft_unregister_flowtable_net_hooks(net,
|
|
nft_trans_flowtable(trans),
|
|
@@ -11346,11 +11500,9 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
|
|
break;
|
|
case NFT_MSG_NEWCHAIN:
|
|
if (nft_trans_chain_update(trans)) {
|
|
- if (!(table->flags & NFT_TABLE_F_DORMANT)) {
|
|
- nft_netdev_unregister_hooks(net,
|
|
- &nft_trans_chain_hooks(trans),
|
|
- true);
|
|
- }
|
|
+ nft_netdev_unregister_hooks(net, table,
|
|
+ &nft_trans_chain_hooks(trans),
|
|
+ true);
|
|
free_percpu(nft_trans_chain_stats(trans));
|
|
kfree(nft_trans_chain_name(trans));
|
|
nft_trans_destroy(trans);
|
|
@@ -11368,8 +11520,7 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
|
|
case NFT_MSG_DELCHAIN:
|
|
case NFT_MSG_DESTROYCHAIN:
|
|
if (nft_trans_chain_update(trans)) {
|
|
- list_splice(&nft_trans_chain_hooks(trans),
|
|
- &nft_trans_basechain(trans)->hook_list);
|
|
+ nft_trans_delhook_abort(&nft_trans_chain_hooks(trans));
|
|
} else {
|
|
nft_use_inc_restore(&table->use);
|
|
nft_clear(trans->net, nft_trans_chain(trans));
|
|
@@ -11483,8 +11634,7 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
|
|
case NFT_MSG_DELFLOWTABLE:
|
|
case NFT_MSG_DESTROYFLOWTABLE:
|
|
if (nft_trans_flowtable_update(trans)) {
|
|
- list_splice(&nft_trans_flowtable_hooks(trans),
|
|
- &nft_trans_flowtable(trans)->hook_list);
|
|
+ nft_trans_delhook_abort(&nft_trans_flowtable_hooks(trans));
|
|
} else {
|
|
nft_use_inc_restore(&table->use);
|
|
nft_clear(trans->net, nft_trans_flowtable(trans));
|