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

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
...@@ -17,14 +17,6 @@ struct nf_ct_timeout { ...@@ -17,14 +17,6 @@ struct nf_ct_timeout {
char data[]; 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_conn_timeout {
struct nf_ct_timeout __rcu *timeout; struct nf_ct_timeout __rcu *timeout;
}; };
......
...@@ -33,8 +33,19 @@ ...@@ -33,8 +33,19 @@
static unsigned int nfct_timeout_id __read_mostly; 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 nfct_timeout_pernet {
struct list_head nfct_timeout_list; struct list_head nfct_timeout_list;
struct list_head nfct_timeout_freelist;
}; };
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
...@@ -574,10 +585,24 @@ static int __net_init cttimeout_net_init(struct net *net) ...@@ -574,10 +585,24 @@ static int __net_init cttimeout_net_init(struct net *net)
struct nfct_timeout_pernet *pernet = nfct_timeout_pernet(net); struct nfct_timeout_pernet *pernet = nfct_timeout_pernet(net);
INIT_LIST_HEAD(&pernet->nfct_timeout_list); INIT_LIST_HEAD(&pernet->nfct_timeout_list);
INIT_LIST_HEAD(&pernet->nfct_timeout_freelist);
return 0; 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) static void __net_exit cttimeout_net_exit(struct net *net)
{ {
struct nfct_timeout_pernet *pernet = nfct_timeout_pernet(net); struct nfct_timeout_pernet *pernet = nfct_timeout_pernet(net);
...@@ -586,8 +611,8 @@ static void __net_exit cttimeout_net_exit(struct net *net) ...@@ -586,8 +611,8 @@ static void __net_exit cttimeout_net_exit(struct net *net)
nf_ct_unconfirmed_destroy(net); nf_ct_unconfirmed_destroy(net);
nf_ct_untimeout(net, NULL); nf_ct_untimeout(net, NULL);
list_for_each_entry_safe(cur, tmp, &pernet->nfct_timeout_list, head) { list_for_each_entry_safe(cur, tmp, &pernet->nfct_timeout_freelist, head) {
list_del_rcu(&cur->head); list_del(&cur->free_head);
if (refcount_dec_and_test(&cur->refcnt)) if (refcount_dec_and_test(&cur->refcnt))
kfree_rcu(cur, rcu_head); kfree_rcu(cur, rcu_head);
...@@ -596,6 +621,7 @@ static void __net_exit cttimeout_net_exit(struct net *net) ...@@ -596,6 +621,7 @@ static void __net_exit cttimeout_net_exit(struct net *net)
static struct pernet_operations cttimeout_ops = { static struct pernet_operations cttimeout_ops = {
.init = cttimeout_net_init, .init = cttimeout_net_init,
.pre_exit = cttimeout_net_pre_exit,
.exit = cttimeout_net_exit, .exit = cttimeout_net_exit,
.id = &nfct_timeout_id, .id = &nfct_timeout_id,
.size = sizeof(struct nfct_timeout_pernet), .size = sizeof(struct nfct_timeout_pernet),
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment