mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	netfilter: nf_tables_netdev: unregister hooks on net_device removal
In case the net_device is gone, we have to unregister the hooks and put back the reference on the net_device object. Once it comes back, register them again. This also covers the device rename case. This patch also adds a new flag to indicate that the basechain is disabled, so their hooks are not registered. This flag is used by the netdev family to handle the case where the net_device object is gone. Currently this flag is not exposed to userspace. Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
This commit is contained in:
		
							parent
							
								
									d8ee8f7c56
								
							
						
					
					
						commit
						835b803377
					
				
					 3 changed files with 94 additions and 4 deletions
				
			
		| 
						 | 
					@ -781,6 +781,7 @@ struct nft_stats {
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define NFT_HOOK_OPS_MAX		2
 | 
					#define NFT_HOOK_OPS_MAX		2
 | 
				
			||||||
 | 
					#define NFT_BASECHAIN_DISABLED		(1 << 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 *	struct nft_base_chain - nf_tables base chain
 | 
					 *	struct nft_base_chain - nf_tables base chain
 | 
				
			||||||
| 
						 | 
					@ -798,6 +799,7 @@ struct nft_base_chain {
 | 
				
			||||||
	possible_net_t			pnet;
 | 
						possible_net_t			pnet;
 | 
				
			||||||
	const struct nf_chain_type	*type;
 | 
						const struct nf_chain_type	*type;
 | 
				
			||||||
	u8				policy;
 | 
						u8				policy;
 | 
				
			||||||
 | 
						u8				flags;
 | 
				
			||||||
	struct nft_stats __percpu	*stats;
 | 
						struct nft_stats __percpu	*stats;
 | 
				
			||||||
	struct nft_chain		chain;
 | 
						struct nft_chain		chain;
 | 
				
			||||||
	char 				dev_name[IFNAMSIZ];
 | 
						char 				dev_name[IFNAMSIZ];
 | 
				
			||||||
| 
						 | 
					@ -808,6 +810,11 @@ static inline struct nft_base_chain *nft_base_chain(const struct nft_chain *chai
 | 
				
			||||||
	return container_of(chain, struct nft_base_chain, chain);
 | 
						return container_of(chain, struct nft_base_chain, chain);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int nft_register_basechain(struct nft_base_chain *basechain,
 | 
				
			||||||
 | 
								   unsigned int hook_nops);
 | 
				
			||||||
 | 
					void nft_unregister_basechain(struct nft_base_chain *basechain,
 | 
				
			||||||
 | 
								      unsigned int hook_nops);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
unsigned int nft_do_chain(struct nft_pktinfo *pkt,
 | 
					unsigned int nft_do_chain(struct nft_pktinfo *pkt,
 | 
				
			||||||
			  const struct nf_hook_ops *ops);
 | 
								  const struct nf_hook_ops *ops);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -127,17 +127,25 @@ static void nft_trans_destroy(struct nft_trans *trans)
 | 
				
			||||||
	kfree(trans);
 | 
						kfree(trans);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int nft_register_basechain(struct nft_base_chain *basechain,
 | 
					int nft_register_basechain(struct nft_base_chain *basechain,
 | 
				
			||||||
				  unsigned int hook_nops)
 | 
								   unsigned int hook_nops)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						if (basechain->flags & NFT_BASECHAIN_DISABLED)
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nf_register_hooks(basechain->ops, hook_nops);
 | 
						return nf_register_hooks(basechain->ops, hook_nops);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					EXPORT_SYMBOL_GPL(nft_register_basechain);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void nft_unregister_basechain(struct nft_base_chain *basechain,
 | 
					void nft_unregister_basechain(struct nft_base_chain *basechain,
 | 
				
			||||||
				     unsigned int hook_nops)
 | 
								      unsigned int hook_nops)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						if (basechain->flags & NFT_BASECHAIN_DISABLED)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	nf_unregister_hooks(basechain->ops, hook_nops);
 | 
						nf_unregister_hooks(basechain->ops, hook_nops);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					EXPORT_SYMBOL_GPL(nft_unregister_basechain);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int nf_tables_register_hooks(const struct nft_table *table,
 | 
					static int nf_tables_register_hooks(const struct nft_table *table,
 | 
				
			||||||
				    struct nft_chain *chain,
 | 
									    struct nft_chain *chain,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,6 +8,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <linux/init.h>
 | 
					#include <linux/init.h>
 | 
				
			||||||
#include <linux/module.h>
 | 
					#include <linux/module.h>
 | 
				
			||||||
 | 
					#include <linux/netdevice.h>
 | 
				
			||||||
#include <net/netfilter/nf_tables.h>
 | 
					#include <net/netfilter/nf_tables.h>
 | 
				
			||||||
#include <linux/ip.h>
 | 
					#include <linux/ip.h>
 | 
				
			||||||
#include <linux/ipv6.h>
 | 
					#include <linux/ipv6.h>
 | 
				
			||||||
| 
						 | 
					@ -157,6 +158,77 @@ static const struct nf_chain_type nft_filter_chain_netdev = {
 | 
				
			||||||
	.hook_mask	= (1 << NF_NETDEV_INGRESS),
 | 
						.hook_mask	= (1 << NF_NETDEV_INGRESS),
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void nft_netdev_event(unsigned long event, struct nft_af_info *afi,
 | 
				
			||||||
 | 
								     struct net_device *dev, struct nft_table *table,
 | 
				
			||||||
 | 
								     struct nft_base_chain *basechain)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						switch (event) {
 | 
				
			||||||
 | 
						case NETDEV_REGISTER:
 | 
				
			||||||
 | 
							if (strcmp(basechain->dev_name, dev->name) != 0)
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							BUG_ON(!(basechain->flags & NFT_BASECHAIN_DISABLED));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							dev_hold(dev);
 | 
				
			||||||
 | 
							basechain->ops[0].dev = dev;
 | 
				
			||||||
 | 
							basechain->flags &= ~NFT_BASECHAIN_DISABLED;
 | 
				
			||||||
 | 
							if (!(table->flags & NFT_TABLE_F_DORMANT))
 | 
				
			||||||
 | 
								nft_register_basechain(basechain, afi->nops);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case NETDEV_UNREGISTER:
 | 
				
			||||||
 | 
							if (strcmp(basechain->dev_name, dev->name) != 0)
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							BUG_ON(basechain->flags & NFT_BASECHAIN_DISABLED);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!(table->flags & NFT_TABLE_F_DORMANT))
 | 
				
			||||||
 | 
								nft_unregister_basechain(basechain, afi->nops);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							dev_put(basechain->ops[0].dev);
 | 
				
			||||||
 | 
							basechain->ops[0].dev = NULL;
 | 
				
			||||||
 | 
							basechain->flags |= NFT_BASECHAIN_DISABLED;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case NETDEV_CHANGENAME:
 | 
				
			||||||
 | 
							if (dev->ifindex != basechain->ops[0].dev->ifindex)
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							strncpy(basechain->dev_name, dev->name, IFNAMSIZ);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int nf_tables_netdev_event(struct notifier_block *this,
 | 
				
			||||||
 | 
									  unsigned long event, void *ptr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct net_device *dev = netdev_notifier_info_to_dev(ptr);
 | 
				
			||||||
 | 
						struct nft_af_info *afi;
 | 
				
			||||||
 | 
						struct nft_table *table;
 | 
				
			||||||
 | 
						struct nft_chain *chain;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						nfnl_lock(NFNL_SUBSYS_NFTABLES);
 | 
				
			||||||
 | 
						list_for_each_entry(afi, &dev_net(dev)->nft.af_info, list) {
 | 
				
			||||||
 | 
							if (afi->family != NFPROTO_NETDEV)
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							list_for_each_entry(table, &afi->tables, list) {
 | 
				
			||||||
 | 
								list_for_each_entry(chain, &table->chains, list) {
 | 
				
			||||||
 | 
									if (!(chain->flags & NFT_BASE_CHAIN))
 | 
				
			||||||
 | 
										continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									nft_netdev_event(event, afi, dev, table,
 | 
				
			||||||
 | 
											 nft_base_chain(chain));
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						nfnl_unlock(NFNL_SUBSYS_NFTABLES);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return NOTIFY_DONE;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct notifier_block nf_tables_netdev_notifier = {
 | 
				
			||||||
 | 
						.notifier_call	= nf_tables_netdev_event,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int __init nf_tables_netdev_init(void)
 | 
					static int __init nf_tables_netdev_init(void)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
| 
						 | 
					@ -166,11 +238,14 @@ static int __init nf_tables_netdev_init(void)
 | 
				
			||||||
	if (ret < 0)
 | 
						if (ret < 0)
 | 
				
			||||||
		nft_unregister_chain_type(&nft_filter_chain_netdev);
 | 
							nft_unregister_chain_type(&nft_filter_chain_netdev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						register_netdevice_notifier(&nf_tables_netdev_notifier);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void __exit nf_tables_netdev_exit(void)
 | 
					static void __exit nf_tables_netdev_exit(void)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						unregister_netdevice_notifier(&nf_tables_netdev_notifier);
 | 
				
			||||||
	unregister_pernet_subsys(&nf_tables_netdev_net_ops);
 | 
						unregister_pernet_subsys(&nf_tables_netdev_net_ops);
 | 
				
			||||||
	nft_unregister_chain_type(&nft_filter_chain_netdev);
 | 
						nft_unregister_chain_type(&nft_filter_chain_netdev);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue