netfilter: cttimeout: decouple unlink and free on netns destruction
Make it so netns pre_exit unlinks the objects from the pernet list, so they cannot be found anymore. netns core issues a synchronize_rcu() before calling the exit hooks so any the time the exit hooks run unconfirmed nf_conn entries have been free'd or they have been committed to the hashtable. The exit hook still tags unconfirmed entries as dying, this can now be removed in a followup change. Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
This commit is contained in:
		
							parent
							
								
									1397af5bfd
								
							
						
					
					
						commit
						78222bacfc
					
				| @ -17,14 +17,6 @@ struct nf_ct_timeout { | ||||
| 	char			data[]; | ||||
| }; | ||||
| 
 | ||||
| struct ctnl_timeout { | ||||
| 	struct list_head	head; | ||||
| 	struct rcu_head		rcu_head; | ||||
| 	refcount_t		refcnt; | ||||
| 	char			name[CTNL_TIMEOUT_NAME_MAX]; | ||||
| 	struct nf_ct_timeout	timeout; | ||||
| }; | ||||
| 
 | ||||
| struct nf_conn_timeout { | ||||
| 	struct nf_ct_timeout __rcu *timeout; | ||||
| }; | ||||
|  | ||||
| @ -33,8 +33,19 @@ | ||||
| 
 | ||||
| static unsigned int nfct_timeout_id __read_mostly; | ||||
| 
 | ||||
| struct ctnl_timeout { | ||||
| 	struct list_head	head; | ||||
| 	struct rcu_head		rcu_head; | ||||
| 	refcount_t		refcnt; | ||||
| 	char			name[CTNL_TIMEOUT_NAME_MAX]; | ||||
| 	struct nf_ct_timeout	timeout; | ||||
| 
 | ||||
| 	struct list_head	free_head; | ||||
| }; | ||||
| 
 | ||||
| struct nfct_timeout_pernet { | ||||
| 	struct list_head	nfct_timeout_list; | ||||
| 	struct list_head	nfct_timeout_freelist; | ||||
| }; | ||||
| 
 | ||||
| MODULE_LICENSE("GPL"); | ||||
| @ -574,10 +585,24 @@ static int __net_init cttimeout_net_init(struct net *net) | ||||
| 	struct nfct_timeout_pernet *pernet = nfct_timeout_pernet(net); | ||||
| 
 | ||||
| 	INIT_LIST_HEAD(&pernet->nfct_timeout_list); | ||||
| 	INIT_LIST_HEAD(&pernet->nfct_timeout_freelist); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void __net_exit cttimeout_net_pre_exit(struct net *net) | ||||
| { | ||||
| 	struct nfct_timeout_pernet *pernet = nfct_timeout_pernet(net); | ||||
| 	struct ctnl_timeout *cur, *tmp; | ||||
| 
 | ||||
| 	list_for_each_entry_safe(cur, tmp, &pernet->nfct_timeout_list, head) { | ||||
| 		list_del_rcu(&cur->head); | ||||
| 		list_add(&cur->free_head, &pernet->nfct_timeout_freelist); | ||||
| 	} | ||||
| 
 | ||||
| 	/* core calls synchronize_rcu() after this */ | ||||
| } | ||||
| 
 | ||||
| static void __net_exit cttimeout_net_exit(struct net *net) | ||||
| { | ||||
| 	struct nfct_timeout_pernet *pernet = nfct_timeout_pernet(net); | ||||
| @ -586,8 +611,8 @@ static void __net_exit cttimeout_net_exit(struct net *net) | ||||
| 	nf_ct_unconfirmed_destroy(net); | ||||
| 	nf_ct_untimeout(net, NULL); | ||||
| 
 | ||||
| 	list_for_each_entry_safe(cur, tmp, &pernet->nfct_timeout_list, head) { | ||||
| 		list_del_rcu(&cur->head); | ||||
| 	list_for_each_entry_safe(cur, tmp, &pernet->nfct_timeout_freelist, head) { | ||||
| 		list_del(&cur->free_head); | ||||
| 
 | ||||
| 		if (refcount_dec_and_test(&cur->refcnt)) | ||||
| 			kfree_rcu(cur, rcu_head); | ||||
| @ -596,6 +621,7 @@ static void __net_exit cttimeout_net_exit(struct net *net) | ||||
| 
 | ||||
| static struct pernet_operations cttimeout_ops = { | ||||
| 	.init	= cttimeout_net_init, | ||||
| 	.pre_exit = cttimeout_net_pre_exit, | ||||
| 	.exit	= cttimeout_net_exit, | ||||
| 	.id     = &nfct_timeout_id, | ||||
| 	.size   = sizeof(struct nfct_timeout_pernet), | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user