Commit a331b78a authored by Phil Sutter's avatar Phil Sutter Committed by Pablo Neira Ayuso
Browse files

netfilter: nf_tables: Respect NETDEV_REGISTER events



Hook into new devices if their name matches the hook spec.

Signed-off-by: default avatarPhil Sutter <phil@nwl.cc>
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent 104031ac
Loading
Loading
Loading
Loading
+32 −5
Original line number Diff line number Diff line
@@ -9689,7 +9689,7 @@ struct nf_hook_ops *nft_hook_find_ops_rcu(const struct nft_hook *hook,
}
EXPORT_SYMBOL_GPL(nft_hook_find_ops_rcu);

static void nft_flowtable_event(unsigned long event, struct net_device *dev,
static int nft_flowtable_event(unsigned long event, struct net_device *dev,
			       struct nft_flowtable *flowtable)
{
	struct nf_hook_ops *ops;
@@ -9708,9 +9708,32 @@ static void nft_flowtable_event(unsigned long event, struct net_device *dev,
			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;
		}
		break;
	}
	return 0;
}

static int nf_tables_flowtable_event(struct notifier_block *this,
@@ -9722,15 +9745,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);
+28 −4
Original line number Diff line number Diff line
@@ -318,7 +318,7 @@ static const struct nft_chain_type nft_chain_filter_netdev = {
	},
};

static void nft_netdev_event(unsigned long event, struct net_device *dev,
static int nft_netdev_event(unsigned long event, struct net_device *dev,
			    struct nft_base_chain *basechain)
{
	struct nft_table *table = basechain->chain.table;
@@ -338,9 +338,29 @@ static void nft_netdev_event(unsigned long event, struct net_device *dev,
			list_del_rcu(&ops->list);
			kfree_rcu(ops, rcu);
			break;
		case NETDEV_REGISTER:
			if (strcmp(hook->ifname, dev->name))
				continue;

			ops = kmemdup(&basechain->ops,
				      sizeof(struct nf_hook_ops),
				      GFP_KERNEL_ACCOUNT);
			if (!ops)
				return 1;

			ops->dev = dev;

			if (!(table->flags & NFT_TABLE_F_DORMANT) &&
			    nf_register_net_hook(dev_net(dev), ops)) {
				kfree(ops);
				return 1;
			}
			list_add_tail_rcu(&ops->list, &hook->ops_list);
			break;
		}
		break;
	}
	return 0;
}

static int nf_tables_netdev_event(struct notifier_block *this,
@@ -352,7 +372,8 @@ static int nf_tables_netdev_event(struct notifier_block *this,
	struct nft_chain *chain;
	struct nft_table *table;

	if (event != NETDEV_UNREGISTER)
	if (event != NETDEV_REGISTER &&
	    event != NETDEV_UNREGISTER)
		return NOTIFY_DONE;

	nft_net = nft_pernet(dev_net(dev));
@@ -371,7 +392,10 @@ static int nf_tables_netdev_event(struct notifier_block *this,
			    basechain->ops.hooknum != NF_INET_INGRESS)
				continue;

			nft_netdev_event(event, dev, basechain);
			if (nft_netdev_event(event, dev, basechain)) {
				mutex_unlock(&nft_net->commit_mutex);
				return NOTIFY_BAD;
			}
		}
	}
	mutex_unlock(&nft_net->commit_mutex);