Skip to content

Commit

Permalink
netfilter: bridge: add pre_exit hooks for ebtable unregistration
Browse files Browse the repository at this point in the history
commit 7ee3c61 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: aee12a0 ("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>
  • Loading branch information
Florian Westphal authored and gregkh committed Apr 21, 2021
1 parent e839472 commit f4e3e82
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 8 deletions.
5 changes: 3 additions & 2 deletions include/linux/netfilter_bridge/ebtables.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
8 changes: 7 additions & 1 deletion net/bridge/netfilter/ebtable_broute.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
8 changes: 7 additions & 1 deletion net/bridge/netfilter/ebtable_filter.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
8 changes: 7 additions & 1 deletion net/bridge/netfilter/ebtable_nat.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
30 changes: 27 additions & 3 deletions net/bridge/netfilter/ebtables.c
Original file line number Diff line number Diff line change
Expand Up @@ -1232,10 +1232,34 @@ int ebt_register_table(struct net *net, const struct ebt_table *input_table,
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);
}

Expand Down

0 comments on commit f4e3e82

Please sign in to comment.