summaryrefslogtreecommitdiffstats
path: root/net/netfilter/nf_tables_api.c
diff options
context:
space:
mode:
authorFlorian Westphal2018-06-11 13:20:35 +0200
committerPablo Neira Ayuso2018-06-12 19:28:18 +0200
commit71ad00c50d77e507138c792a9646b53c16f22e11 (patch)
treec1e02e45c573ae8003390fc95923e48cc70440b0 /net/netfilter/nf_tables_api.c
parentnetfilter: nft_dynset: do not reject set updates with NFT_SET_EVAL (diff)
downloadkernel-qcow2-linux-71ad00c50d77e507138c792a9646b53c16f22e11.tar.gz
kernel-qcow2-linux-71ad00c50d77e507138c792a9646b53c16f22e11.tar.xz
kernel-qcow2-linux-71ad00c50d77e507138c792a9646b53c16f22e11.zip
netfilter: nf_tables: fix module unload race
We must first remove the nfnetlink protocol handler when nf_tables module is unloaded -- we don't want userspace to submit new change requests once we've started to tear down nft state. Furthermore, nfnetlink must not call any subsystem function after call_batch returned -EAGAIN. EAGAIN means the subsys mutex was dropped, so its unlikely but possible that nf_tables subsystem was removed due to 'rmmod nf_tables' on another cpu. Therefore, we must abort batch completely and not move on to next part of the batch. Last, we can't invoke ->abort unless we've checked that the subsystem is still registered. Change netns exit path of nf_tables to make sure any incompleted transaction gets removed on exit. Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Diffstat (limited to 'net/netfilter/nf_tables_api.c')
-rw-r--r--net/netfilter/nf_tables_api.c12
1 files changed, 9 insertions, 3 deletions
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index 7979095b69b0..ae312b31db28 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -6439,7 +6439,7 @@ static void nf_tables_abort_release(struct nft_trans *trans)
kfree(trans);
}
-static int nf_tables_abort(struct net *net, struct sk_buff *skb)
+static int __nf_tables_abort(struct net *net)
{
struct nft_trans *trans, *next;
struct nft_trans_elem *te;
@@ -6555,6 +6555,11 @@ static void nf_tables_cleanup(struct net *net)
nft_validate_state_update(net, NFT_VALIDATE_SKIP);
}
+static int nf_tables_abort(struct net *net, struct sk_buff *skb)
+{
+ return __nf_tables_abort(net);
+}
+
static bool nf_tables_valid_genid(struct net *net, u32 genid)
{
return net->nft.base_seq == genid;
@@ -7149,9 +7154,10 @@ static int __net_init nf_tables_init_net(struct net *net)
static void __net_exit nf_tables_exit_net(struct net *net)
{
+ if (!list_empty(&net->nft.commit_list))
+ __nf_tables_abort(net);
__nft_release_tables(net);
WARN_ON_ONCE(!list_empty(&net->nft.tables));
- WARN_ON_ONCE(!list_empty(&net->nft.commit_list));
}
static struct pernet_operations nf_tables_net_ops = {
@@ -7193,9 +7199,9 @@ err1:
static void __exit nf_tables_module_exit(void)
{
- unregister_pernet_subsys(&nf_tables_net_ops);
nfnetlink_subsys_unregister(&nf_tables_subsys);
unregister_netdevice_notifier(&nf_tables_flowtable_notifier);
+ unregister_pernet_subsys(&nf_tables_net_ops);
rcu_barrier();
nf_tables_core_module_exit();
kfree(info);