EXPORT_SYMBOL_GPL(nft_hook_find_ops_rcu);
static int nft_flowtable_event(unsigned long event, struct net_device *dev,
- struct nft_flowtable *flowtable)
+ struct nft_flowtable *flowtable, bool changename)
{
struct nf_hook_ops *ops;
struct nft_hook *hook;
+ bool match;
list_for_each_entry(hook, &flowtable->hook_list, list) {
+ ops = nft_hook_find_ops(hook, dev);
+ match = !strcmp(hook->ifname, dev->name);
+
switch (event) {
case NETDEV_UNREGISTER:
- ops = nft_hook_find_ops(hook, dev);
- if (!ops)
+ /* NOP if not found or new name still matching */
+ if (!ops || (changename && match))
continue;
/* flow_offload_netdev_event() cleans up entries for us. */
kfree_rcu(ops, rcu);
break;
case NETDEV_REGISTER:
- if (strcmp(hook->ifname, dev->name))
+ /* NOP if not matching or already registered */
+ if (!match || (changename && ops))
continue;
ops = kzalloc(sizeof(struct nf_hook_ops),
}
static int __nf_tables_flowtable_event(unsigned long event,
- struct net_device *dev)
+ struct net_device *dev,
+ bool changename)
{
struct nftables_pernet *nft_net = nft_pernet(dev_net(dev));
struct nft_flowtable *flowtable;
list_for_each_entry(table, &nft_net->tables, list) {
list_for_each_entry(flowtable, &table->flowtables, list) {
- if (nft_flowtable_event(event, dev, flowtable))
+ if (nft_flowtable_event(event, dev,
+ flowtable, changename))
return 1;
}
}
struct net *net;
if (event != NETDEV_REGISTER &&
- event != NETDEV_UNREGISTER)
+ event != NETDEV_UNREGISTER &&
+ event != NETDEV_CHANGENAME)
return NOTIFY_DONE;
net = dev_net(dev);
nft_net = nft_pernet(net);
mutex_lock(&nft_net->commit_mutex);
- if (__nf_tables_flowtable_event(event, dev))
+ if (event == NETDEV_CHANGENAME) {
+ if (__nf_tables_flowtable_event(NETDEV_REGISTER, dev, true)) {
+ ret = NOTIFY_BAD;
+ goto out_unlock;
+ }
+ __nf_tables_flowtable_event(NETDEV_UNREGISTER, dev, true);
+ } else if (__nf_tables_flowtable_event(event, dev, false)) {
ret = NOTIFY_BAD;
-
+ }
+out_unlock:
mutex_unlock(&nft_net->commit_mutex);
return ret;
}
};
static int nft_netdev_event(unsigned long event, struct net_device *dev,
- struct nft_base_chain *basechain)
+ struct nft_base_chain *basechain, bool changename)
{
struct nft_table *table = basechain->chain.table;
struct nf_hook_ops *ops;
struct nft_hook *hook;
+ bool match;
list_for_each_entry(hook, &basechain->hook_list, list) {
+ ops = nft_hook_find_ops(hook, dev);
+ match = !strcmp(hook->ifname, dev->name);
+
switch (event) {
case NETDEV_UNREGISTER:
- ops = nft_hook_find_ops(hook, dev);
- if (!ops)
+ /* NOP if not found or new name still matching */
+ if (!ops || (changename && match))
continue;
if (!(table->flags & NFT_TABLE_F_DORMANT))
kfree_rcu(ops, rcu);
break;
case NETDEV_REGISTER:
- if (strcmp(hook->ifname, dev->name))
+ /* NOP if not matching or already registered */
+ if (!match || (changename && ops))
continue;
ops = kmemdup(&basechain->ops,
return 0;
}
-static int __nf_tables_netdev_event(unsigned long event, struct net_device *dev)
+static int __nf_tables_netdev_event(unsigned long event,
+ struct net_device *dev,
+ bool changename)
{
struct nft_base_chain *basechain;
struct nftables_pernet *nft_net;
basechain->ops.hooknum != NF_INET_INGRESS)
continue;
- if (nft_netdev_event(event, dev, basechain))
+ if (nft_netdev_event(event, dev, basechain, changename))
return 1;
}
}
int ret = NOTIFY_DONE;
if (event != NETDEV_REGISTER &&
- event != NETDEV_UNREGISTER)
+ event != NETDEV_UNREGISTER &&
+ event != NETDEV_CHANGENAME)
return NOTIFY_DONE;
nft_net = nft_pernet(dev_net(dev));
mutex_lock(&nft_net->commit_mutex);
- if (__nf_tables_netdev_event(event, dev))
+ if (event == NETDEV_CHANGENAME) {
+ if (__nf_tables_netdev_event(NETDEV_REGISTER, dev, true)) {
+ ret = NOTIFY_BAD;
+ goto out_unlock;
+ }
+ __nf_tables_netdev_event(NETDEV_UNREGISTER, dev, true);
+ } else if (__nf_tables_netdev_event(event, dev, false)) {
ret = NOTIFY_BAD;
-
+ }
+out_unlock:
mutex_unlock(&nft_net->commit_mutex);
return ret;
}