Commit 78222bac authored by Florian Westphal's avatar Florian Westphal Committed by Pablo Neira Ayuso
Browse files

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: default avatarFlorian Westphal <fw@strlen.de>
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent 1397af5b
Loading
Loading
Loading
Loading
+0 −8
Original line number Diff line number Diff line
@@ -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;
};
+28 −2
Original line number Diff line number Diff line
@@ -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),