Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > fc11cd6e1c513a17304da94a5390f3cd > files > 2540

kernel-2.6.18-194.11.1.el5.src.rpm

From: Thomas Graf <tgraf@redhat.com>
Subject: [RHEL5 BZ212839] netfilter: service iptables stop fails because ip_conntrack cannot be unloaded.
Date: Tue, 2 Jan 2007 16:15:16 +0100
Bugzilla: 212839
Message-Id: <20070102151516.GA4882@lsx.localdomain>
Changelog: netfilter: iptables stop fails because ip_conntrack cannot unload.


commit 5251e2d2125407bbff0c39394a4011be9ed8b5d0
Author: Pablo Neira Ayuso <pablo@netfilter.org>

    [NETFILTER]: conntrack: fix race condition in early_drop
    
    On SMP environments the maximum number of conntracks can be overpassed
    under heavy stress situations due to an existing race condition.
    
            CPU A                   CPU B
         atomic_read()               ...
         early_drop()                ...
            ...                  atomic_read()
       allocate conntrack      allocate conntrack
         atomic_inc()             atomic_inc()
    
    This patch moves the counter incrementation before the early drop stage.
    
    Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
    Signed-off-by: Patrick McHardy <kaber@trash.net>
    Signed-off-by: David S. Miller <davem@davemloft.net>

Index: linux-2.6.18.noarch/net/ipv4/netfilter/ip_conntrack_core.c
===================================================================
--- linux-2.6.18.noarch.orig/net/ipv4/netfilter/ip_conntrack_core.c	2006-09-20 05:42:06.000000000 +0200
+++ linux-2.6.18.noarch/net/ipv4/netfilter/ip_conntrack_core.c	2007-01-02 16:11:42.000000000 +0100
@@ -640,11 +640,15 @@ struct ip_conntrack *ip_conntrack_alloc(
 		ip_conntrack_hash_rnd_initted = 1;
 	}
 
+	/* We don't want any race condition at early drop stage */
+	atomic_inc(&ip_conntrack_count);
+
 	if (ip_conntrack_max
-	    && atomic_read(&ip_conntrack_count) >= ip_conntrack_max) {
+	    && atomic_read(&ip_conntrack_count) > ip_conntrack_max) {
 		unsigned int hash = hash_conntrack(orig);
 		/* Try dropping from this hash chain. */
 		if (!early_drop(&ip_conntrack_hash[hash])) {
+			atomic_dec(&ip_conntrack_count);
 			if (net_ratelimit())
 				printk(KERN_WARNING
 				       "ip_conntrack: table full, dropping"
@@ -656,6 +660,7 @@ struct ip_conntrack *ip_conntrack_alloc(
 	conntrack = kmem_cache_alloc(ip_conntrack_cachep, GFP_ATOMIC);
 	if (!conntrack) {
 		DEBUGP("Can't allocate conntrack.\n");
+		atomic_dec(&ip_conntrack_count);
 		return ERR_PTR(-ENOMEM);
 	}
 
@@ -669,8 +674,6 @@ struct ip_conntrack *ip_conntrack_alloc(
 	conntrack->timeout.data = (unsigned long)conntrack;
 	conntrack->timeout.function = death_by_timeout;
 
-	atomic_inc(&ip_conntrack_count);
-
 	return conntrack;
 }
 
Index: linux-2.6.18.noarch/net/netfilter/nf_conntrack_core.c
===================================================================
--- linux-2.6.18.noarch.orig/net/netfilter/nf_conntrack_core.c	2006-09-20 05:42:06.000000000 +0200
+++ linux-2.6.18.noarch/net/netfilter/nf_conntrack_core.c	2007-01-02 16:11:42.000000000 +0100
@@ -866,11 +866,15 @@ __nf_conntrack_alloc(const struct nf_con
 		nf_conntrack_hash_rnd_initted = 1;
 	}
 
+	/* We don't want any race condition at early drop stage */
+	atomic_inc(&nf_conntrack_count);
+
 	if (nf_conntrack_max
-	    && atomic_read(&nf_conntrack_count) >= nf_conntrack_max) {
+	    && atomic_read(&nf_conntrack_count) > nf_conntrack_max) {
 		unsigned int hash = hash_conntrack(orig);
 		/* Try dropping from this hash chain. */
 		if (!early_drop(&nf_conntrack_hash[hash])) {
+			atomic_dec(&nf_conntrack_count);
 			if (net_ratelimit())
 				printk(KERN_WARNING
 				       "nf_conntrack: table full, dropping"
@@ -921,10 +925,12 @@ __nf_conntrack_alloc(const struct nf_con
 	init_timer(&conntrack->timeout);
 	conntrack->timeout.data = (unsigned long)conntrack;
 	conntrack->timeout.function = death_by_timeout;
+	read_unlock_bh(&nf_ct_cache_lock);
 
-	atomic_inc(&nf_conntrack_count);
+	return conntrack;
 out:
 	read_unlock_bh(&nf_ct_cache_lock);
+	atomic_dec(&nf_conntrack_count);
 	return conntrack;
 }