summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Dumazet <eric.dumazet@gmail.com>2009-06-22 14:13:55 +0200
committerPatrick McHardy <kaber@trash.net>2009-06-22 14:13:55 +0200
commit8cc20198cfccd06cef705c14fd50bde603e2e306 (patch)
tree95934002dceac963afa7b135b39a9b2b6bdce368
parentf6b24caaf933a466397915a08e30e885a32f905a (diff)
downloadlinux-stable-8cc20198cfccd06cef705c14fd50bde603e2e306.tar.gz
linux-stable-8cc20198cfccd06cef705c14fd50bde603e2e306.tar.bz2
linux-stable-8cc20198cfccd06cef705c14fd50bde603e2e306.zip
netfilter: nf_conntrack: death_by_timeout() fix
death_by_timeout() might delete a conntrack from hash list and insert it in dying list. nf_ct_delete_from_lists(ct); nf_ct_insert_dying_list(ct); I believe a (lockless) reader could *catch* ct while doing a lookup and miss the end of its chain. (nulls lookup algo must check the null value at the end of lookup and should restart if the null value is not the expected one. cf Documentation/RCU/rculist_nulls.txt for details) We need to change nf_conntrack_init_net() and use a different "null" value, guaranteed not being used in regular lists. Choose very large values, since hash table uses [0..size-1] null values. Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com> Acked-by: Pablo Neira Ayuso <pablo@netfilter.org> Signed-off-by: Patrick McHardy <kaber@trash.net>
-rw-r--r--net/netfilter/nf_conntrack_core.c10
1 files changed, 8 insertions, 2 deletions
diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c
index 5f72b94b4918..5276a2dd56fe 100644
--- a/net/netfilter/nf_conntrack_core.c
+++ b/net/netfilter/nf_conntrack_core.c
@@ -1267,13 +1267,19 @@ err_cache:
return ret;
}
+/*
+ * We need to use special "null" values, not used in hash table
+ */
+#define UNCONFIRMED_NULLS_VAL ((1<<30)+0)
+#define DYING_NULLS_VAL ((1<<30)+1)
+
static int nf_conntrack_init_net(struct net *net)
{
int ret;
atomic_set(&net->ct.count, 0);
- INIT_HLIST_NULLS_HEAD(&net->ct.unconfirmed, 0);
- INIT_HLIST_NULLS_HEAD(&net->ct.dying, 0);
+ INIT_HLIST_NULLS_HEAD(&net->ct.unconfirmed, UNCONFIRMED_NULLS_VAL);
+ INIT_HLIST_NULLS_HEAD(&net->ct.dying, DYING_NULLS_VAL);
net->ct.stat = alloc_percpu(struct ip_conntrack_stat);
if (!net->ct.stat) {
ret = -ENOMEM;