Hook into new devices if their name matches the hook spec. Signed-off-by: Phil Sutter <phil@xxxxxx> --- Changes since v3: - Use list_add_tail_rcu() to avoid breaking readers. - Return NOTIFY_BAD upon error instead of printing an error message as per Florian. --- net/netfilter/nf_tables_api.c | 56 +++++++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 13 deletions(-) diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index f3b0bc2fe0e3..2684990dd3dc 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -9317,23 +9317,49 @@ struct nf_hook_ops *nft_hook_find_ops(const struct nft_hook *hook, } EXPORT_SYMBOL_GPL(nft_hook_find_ops); -static void nft_flowtable_event(unsigned long event, struct net_device *dev, - struct nft_flowtable *flowtable) +static int nft_flowtable_event(unsigned long event, struct net_device *dev, + struct nft_flowtable *flowtable) { struct nf_hook_ops *ops; struct nft_hook *hook; list_for_each_entry(hook, &flowtable->hook_list, list) { - ops = nft_hook_find_ops(hook, dev); - if (!ops) - continue; + switch (event) { + case NETDEV_UNREGISTER: + ops = nft_hook_find_ops(hook, dev); + if (!ops) + continue; - /* flow_offload_netdev_event() cleans up entries for us. */ - nft_unregister_flowtable_ops(dev_net(dev), flowtable, ops); - list_del_rcu(&ops->list); - kfree_rcu(ops, rcu); - break; + /* flow_offload_netdev_event() cleans up entries for us. */ + nft_unregister_flowtable_ops(dev_net(dev), + flowtable, ops); + list_del_rcu(&ops->list); + kfree_rcu(ops, rcu); + break; + case NETDEV_REGISTER: + if (strcmp(hook->ifname, dev->name)) + continue; + ops = kzalloc(sizeof(struct nf_hook_ops), + GFP_KERNEL_ACCOUNT); + if (!ops) + return 1; + + ops->pf = NFPROTO_NETDEV; + ops->hooknum = flowtable->hooknum; + ops->priority = flowtable->data.priority; + ops->priv = &flowtable->data; + ops->hook = flowtable->data.type->hook; + ops->dev = dev; + if (nft_register_flowtable_ops(dev_net(dev), + flowtable, ops)) { + kfree(ops); + return 1; + } + list_add_tail_rcu(&ops->list, &hook->ops_list); + break; + } } + return 0; } static int nf_tables_flowtable_event(struct notifier_block *this, @@ -9345,15 +9371,19 @@ static int nf_tables_flowtable_event(struct notifier_block *this, struct nft_table *table; struct net *net; - if (event != NETDEV_UNREGISTER) - return 0; + if (event != NETDEV_REGISTER && + event != NETDEV_UNREGISTER) + return NOTIFY_DONE; net = dev_net(dev); nft_net = nft_pernet(net); mutex_lock(&nft_net->commit_mutex); list_for_each_entry(table, &nft_net->tables, list) { list_for_each_entry(flowtable, &table->flowtables, list) { - nft_flowtable_event(event, dev, flowtable); + if (nft_flowtable_event(event, dev, flowtable)) { + mutex_unlock(&nft_net->commit_mutex); + return NOTIFY_BAD; + } } } mutex_unlock(&nft_net->commit_mutex); -- 2.43.0