]> git.proxmox.com Git - mirror_ubuntu-focal-kernel.git/commitdiff
netfilter: bridge: add pre_exit hooks for ebtable unregistration
authorFlorian Westphal <fw@strlen.de>
Wed, 7 Apr 2021 19:43:39 +0000 (21:43 +0200)
committerStefan Bader <stefan.bader@canonical.com>
Tue, 4 May 2021 14:13:34 +0000 (16:13 +0200)
BugLink: https://bugs.launchpad.net/bugs/1926493
commit 7ee3c61dcd28bf6e290e06ad382f13511dc790e9 upstream.

Just like ip/ip6/arptables, the hooks have to be removed, then
synchronize_rcu() has to be called to make sure no more packets are being
processed before the ruleset data is released.

Place the hook unregistration in the pre_exit hook, then call the new
ebtables pre_exit function from there.

Years ago, when first netns support got added for netfilter+ebtables,
this used an older (now removed) netfilter hook unregister API, that did
a unconditional synchronize_rcu().

Now that all is done with call_rcu, ebtable_{filter,nat,broute} pernet exit
handlers may free the ebtable ruleset while packets are still in flight.

This can only happens on module removal, not during netns exit.

The new function expects the table name, not the table struct.

This is because upcoming patch set (targeting -next) will remove all
net->xt.{nat,filter,broute}_table instances, this makes it necessary
to avoid external references to those member variables.

The existing APIs will be converted, so follow the upcoming scheme of
passing name + hook type instead.

Fixes: aee12a0a3727e ("ebtables: remove nf_hook_register usage")
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Kamal Mostafa <kamal@canonical.com>
Signed-off-by: Stefan Bader <stefan.bader@canonical.com>
include/linux/netfilter_bridge/ebtables.h
net/bridge/netfilter/ebtable_broute.c
net/bridge/netfilter/ebtable_filter.c
net/bridge/netfilter/ebtable_nat.c
net/bridge/netfilter/ebtables.c

index 162f59d0d17a282adf24c263e5de0e3a1a658ec9..db472c9cd8e9d11fedd2ae16ad0980dc229d3946 100644 (file)
@@ -110,8 +110,9 @@ extern int ebt_register_table(struct net *net,
                              const struct ebt_table *table,
                              const struct nf_hook_ops *ops,
                              struct ebt_table **res);
-extern void ebt_unregister_table(struct net *net, struct ebt_table *table,
-                                const struct nf_hook_ops *);
+extern void ebt_unregister_table(struct net *net, struct ebt_table *table);
+void ebt_unregister_table_pre_exit(struct net *net, const char *tablename,
+                                  const struct nf_hook_ops *ops);
 extern unsigned int ebt_do_table(struct sk_buff *skb,
                                 const struct nf_hook_state *state,
                                 struct ebt_table *table);
index 66e7af16549436a4c0f92172038d4c4bef82c592..32bc2821027f37d8445afcecd3b368a127d50ecc 100644 (file)
@@ -105,14 +105,20 @@ static int __net_init broute_net_init(struct net *net)
                                  &net->xt.broute_table);
 }
 
+static void __net_exit broute_net_pre_exit(struct net *net)
+{
+       ebt_unregister_table_pre_exit(net, "broute", &ebt_ops_broute);
+}
+
 static void __net_exit broute_net_exit(struct net *net)
 {
-       ebt_unregister_table(net, net->xt.broute_table, &ebt_ops_broute);
+       ebt_unregister_table(net, net->xt.broute_table);
 }
 
 static struct pernet_operations broute_net_ops = {
        .init = broute_net_init,
        .exit = broute_net_exit,
+       .pre_exit = broute_net_pre_exit,
 };
 
 static int __init ebtable_broute_init(void)
index 78cb9b21022d0acc115c3f4aa0007579dbccf7cd..bcf982e12f16b9a47205cc512dc5b487b6ea3ff8 100644 (file)
@@ -99,14 +99,20 @@ static int __net_init frame_filter_net_init(struct net *net)
                                  &net->xt.frame_filter);
 }
 
+static void __net_exit frame_filter_net_pre_exit(struct net *net)
+{
+       ebt_unregister_table_pre_exit(net, "filter", ebt_ops_filter);
+}
+
 static void __net_exit frame_filter_net_exit(struct net *net)
 {
-       ebt_unregister_table(net, net->xt.frame_filter, ebt_ops_filter);
+       ebt_unregister_table(net, net->xt.frame_filter);
 }
 
 static struct pernet_operations frame_filter_net_ops = {
        .init = frame_filter_net_init,
        .exit = frame_filter_net_exit,
+       .pre_exit = frame_filter_net_pre_exit,
 };
 
 static int __init ebtable_filter_init(void)
index 0888936ef8537e6ee00c5c400760a412652b386f..0d092773f81618e9bcb2a4a297674ac1db33e21b 100644 (file)
@@ -99,14 +99,20 @@ static int __net_init frame_nat_net_init(struct net *net)
                                  &net->xt.frame_nat);
 }
 
+static void __net_exit frame_nat_net_pre_exit(struct net *net)
+{
+       ebt_unregister_table_pre_exit(net, "nat", ebt_ops_nat);
+}
+
 static void __net_exit frame_nat_net_exit(struct net *net)
 {
-       ebt_unregister_table(net, net->xt.frame_nat, ebt_ops_nat);
+       ebt_unregister_table(net, net->xt.frame_nat);
 }
 
 static struct pernet_operations frame_nat_net_ops = {
        .init = frame_nat_net_init,
        .exit = frame_nat_net_exit,
+       .pre_exit = frame_nat_net_pre_exit,
 };
 
 static int __init ebtable_nat_init(void)
index e1256e03a9a86b5b1249895a0627f7fbfa8e3386..d9375c52f50e611b66adba1fa6714b38551a8049 100644 (file)
@@ -1237,10 +1237,34 @@ out:
        return ret;
 }
 
-void ebt_unregister_table(struct net *net, struct ebt_table *table,
-                         const struct nf_hook_ops *ops)
+static struct ebt_table *__ebt_find_table(struct net *net, const char *name)
+{
+       struct ebt_table *t;
+
+       mutex_lock(&ebt_mutex);
+
+       list_for_each_entry(t, &net->xt.tables[NFPROTO_BRIDGE], list) {
+               if (strcmp(t->name, name) == 0) {
+                       mutex_unlock(&ebt_mutex);
+                       return t;
+               }
+       }
+
+       mutex_unlock(&ebt_mutex);
+       return NULL;
+}
+
+void ebt_unregister_table_pre_exit(struct net *net, const char *name, const struct nf_hook_ops *ops)
+{
+       struct ebt_table *table = __ebt_find_table(net, name);
+
+       if (table)
+               nf_unregister_net_hooks(net, ops, hweight32(table->valid_hooks));
+}
+EXPORT_SYMBOL(ebt_unregister_table_pre_exit);
+
+void ebt_unregister_table(struct net *net, struct ebt_table *table)
 {
-       nf_unregister_net_hooks(net, ops, hweight32(table->valid_hooks));
        __ebt_unregister_table(net, table);
 }