| From d163a925ebbc6eb5b562b0f1d72c7e817aa75c40 Mon Sep 17 00:00:00 2001 |
| From: Florian Westphal <fw@strlen.de> |
| Date: Wed, 7 Apr 2021 21:43:40 +0200 |
| Subject: netfilter: arp_tables: add pre_exit hook for table unregister |
| |
| From: Florian Westphal <fw@strlen.de> |
| |
| commit d163a925ebbc6eb5b562b0f1d72c7e817aa75c40 upstream. |
| |
| Same problem that also existed in iptables/ip(6)tables, when |
| arptable_filter is removed there is no longer a wait period before the |
| table/ruleset is free'd. |
| |
| Unregister the hook in pre_exit, then remove the table in the exit |
| function. |
| This used to work correctly because the old nf_hook_unregister API |
| did unconditional synchronize_net. |
| |
| The per-net hook unregister function uses call_rcu instead. |
| |
| Fixes: b9e69e127397 ("netfilter: xtables: don't hook tables by default") |
| 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> |
| --- |
| include/linux/netfilter_arp/arp_tables.h | 5 +++-- |
| net/ipv4/netfilter/arp_tables.c | 9 +++++++-- |
| net/ipv4/netfilter/arptable_filter.c | 10 +++++++++- |
| 3 files changed, 19 insertions(+), 5 deletions(-) |
| |
| --- a/include/linux/netfilter_arp/arp_tables.h |
| +++ b/include/linux/netfilter_arp/arp_tables.h |
| @@ -52,8 +52,9 @@ extern void *arpt_alloc_initial_table(co |
| int arpt_register_table(struct net *net, const struct xt_table *table, |
| const struct arpt_replace *repl, |
| const struct nf_hook_ops *ops, struct xt_table **res); |
| -void arpt_unregister_table(struct net *net, struct xt_table *table, |
| - const struct nf_hook_ops *ops); |
| +void arpt_unregister_table(struct net *net, struct xt_table *table); |
| +void arpt_unregister_table_pre_exit(struct net *net, struct xt_table *table, |
| + const struct nf_hook_ops *ops); |
| extern unsigned int arpt_do_table(struct sk_buff *skb, |
| const struct nf_hook_state *state, |
| struct xt_table *table); |
| --- a/net/ipv4/netfilter/arp_tables.c |
| +++ b/net/ipv4/netfilter/arp_tables.c |
| @@ -1541,10 +1541,15 @@ out_free: |
| return ret; |
| } |
| |
| -void arpt_unregister_table(struct net *net, struct xt_table *table, |
| - const struct nf_hook_ops *ops) |
| +void arpt_unregister_table_pre_exit(struct net *net, struct xt_table *table, |
| + const struct nf_hook_ops *ops) |
| { |
| nf_unregister_net_hooks(net, ops, hweight32(table->valid_hooks)); |
| +} |
| +EXPORT_SYMBOL(arpt_unregister_table_pre_exit); |
| + |
| +void arpt_unregister_table(struct net *net, struct xt_table *table) |
| +{ |
| __arpt_unregister_table(net, table); |
| } |
| |
| --- a/net/ipv4/netfilter/arptable_filter.c |
| +++ b/net/ipv4/netfilter/arptable_filter.c |
| @@ -56,16 +56,24 @@ static int __net_init arptable_filter_ta |
| return err; |
| } |
| |
| +static void __net_exit arptable_filter_net_pre_exit(struct net *net) |
| +{ |
| + if (net->ipv4.arptable_filter) |
| + arpt_unregister_table_pre_exit(net, net->ipv4.arptable_filter, |
| + arpfilter_ops); |
| +} |
| + |
| static void __net_exit arptable_filter_net_exit(struct net *net) |
| { |
| if (!net->ipv4.arptable_filter) |
| return; |
| - arpt_unregister_table(net, net->ipv4.arptable_filter, arpfilter_ops); |
| + arpt_unregister_table(net, net->ipv4.arptable_filter); |
| net->ipv4.arptable_filter = NULL; |
| } |
| |
| static struct pernet_operations arptable_filter_net_ops = { |
| .exit = arptable_filter_net_exit, |
| + .pre_exit = arptable_filter_net_pre_exit, |
| }; |
| |
| static int __init arptable_filter_init(void) |