Remove obsolete #include <linux/config.h>
[safe/jmp/linux-2.6] / net / ipv4 / netfilter / ip_conntrack_core.c
index 07a80b5..aa45917 100644 (file)
@@ -17,7 +17,6 @@
  *     - export ip_conntrack[_expect]_{find_get,put} functions
  * */
 
-#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/icmp.h>
 #include <linux/ip.h>
@@ -50,7 +49,7 @@
 #include <linux/netfilter_ipv4/ip_conntrack_core.h>
 #include <linux/netfilter_ipv4/listhelp.h>
 
-#define IP_CONNTRACK_VERSION   "2.3"
+#define IP_CONNTRACK_VERSION   "2.4"
 
 #if 0
 #define DEBUGP printk
@@ -77,11 +76,11 @@ unsigned int ip_ct_log_invalid;
 static LIST_HEAD(unconfirmed);
 static int ip_conntrack_vmalloc;
 
-static unsigned int ip_conntrack_next_id = 1;
-static unsigned int ip_conntrack_expect_next_id = 1;
+static unsigned int ip_conntrack_next_id;
+static unsigned int ip_conntrack_expect_next_id;
 #ifdef CONFIG_IP_NF_CONNTRACK_EVENTS
-struct notifier_block *ip_conntrack_chain;
-struct notifier_block *ip_conntrack_expect_chain;
+ATOMIC_NOTIFIER_HEAD(ip_conntrack_chain);
+ATOMIC_NOTIFIER_HEAD(ip_conntrack_expect_chain);
 
 DEFINE_PER_CPU(struct ip_conntrack_ecache, ip_conntrack_ecache);
 
@@ -92,7 +91,7 @@ __ip_ct_deliver_cached_events(struct ip_conntrack_ecache *ecache)
 {
        DEBUGP("ecache: delivering events for %p\n", ecache->ct);
        if (is_confirmed(ecache->ct) && !is_dying(ecache->ct) && ecache->events)
-               notifier_call_chain(&ip_conntrack_chain, ecache->events,
+               atomic_notifier_call_chain(&ip_conntrack_chain, ecache->events,
                                    ecache->ct);
        ecache->events = 0;
        ip_conntrack_put(ecache->ct);
@@ -133,7 +132,7 @@ static void ip_ct_event_cache_flush(void)
        struct ip_conntrack_ecache *ecache;
        int cpu;
 
-       for_each_cpu(cpu) {
+       for_each_possible_cpu(cpu) {
                ecache = &per_cpu(ip_conntrack_ecache, cpu);
                if (ecache->ct)
                        ip_conntrack_put(ecache->ct);
@@ -148,16 +147,20 @@ DEFINE_PER_CPU(struct ip_conntrack_stat, ip_conntrack_stat);
 static int ip_conntrack_hash_rnd_initted;
 static unsigned int ip_conntrack_hash_rnd;
 
-static u_int32_t
-hash_conntrack(const struct ip_conntrack_tuple *tuple)
+static u_int32_t __hash_conntrack(const struct ip_conntrack_tuple *tuple,
+                           unsigned int size, unsigned int rnd)
 {
-#if 0
-       dump_tuple(tuple);
-#endif
        return (jhash_3words(tuple->src.ip,
                             (tuple->dst.ip ^ tuple->dst.protonum),
                             (tuple->src.u.all | (tuple->dst.u.all << 16)),
-                            ip_conntrack_hash_rnd) % ip_conntrack_htable_size);
+                            rnd) % size);
+}
+
+static u_int32_t
+hash_conntrack(const struct ip_conntrack_tuple *tuple)
+{
+       return __hash_conntrack(tuple, ip_conntrack_htable_size,
+                               ip_conntrack_hash_rnd);
 }
 
 int
@@ -720,6 +723,9 @@ init_conntrack(struct ip_conntrack_tuple *tuple,
                /* this is ugly, but there is no other place where to put it */
                conntrack->nat.masq_index = exp->master->nat.masq_index;
 #endif
+#ifdef CONFIG_IP_NF_CONNTRACK_SECMARK
+               conntrack->secmark = exp->master->secmark;
+#endif
                nf_conntrack_get(&conntrack->master->ct_general);
                CONNTRACK_STAT_INC(expect_new);
        } else {
@@ -1126,6 +1132,12 @@ void __ip_ct_refresh_acct(struct ip_conntrack *ct,
 
        write_lock_bh(&ip_conntrack_lock);
 
+       /* Only update if this is not a fixed timeout */
+       if (test_bit(IPS_FIXED_TIMEOUT_BIT, &ct->status)) {
+               write_unlock_bh(&ip_conntrack_lock);
+               return;
+       }
+
        /* If not in hash table, timer will not be active yet */
        if (!is_confirmed(ct)) {
                ct->timeout.expires = extra_jiffies;
@@ -1314,6 +1326,7 @@ getorigdst(struct sock *sk, int optval, void __user *user, int *len)
                        .tuple.dst.u.tcp.port;
                sin.sin_addr.s_addr = ct->tuplehash[IP_CT_DIR_ORIGINAL]
                        .tuple.dst.ip;
+               memset(sin.sin_zero, 0, sizeof(sin.sin_zero));
 
                DEBUGP("SO_ORIGINAL_DST: %u.%u.%u.%u %u\n",
                       NIPQUAD(sin.sin_addr.s_addr), ntohs(sin.sin_port));
@@ -1341,18 +1354,26 @@ static int kill_all(struct ip_conntrack *i, void *data)
        return 1;
 }
 
-static void free_conntrack_hash(void)
+void ip_conntrack_flush(void)
+{
+       ip_ct_iterate_cleanup(kill_all, NULL);
+}
+
+static void free_conntrack_hash(struct list_head *hash, int vmalloced,int size)
 {
-       if (ip_conntrack_vmalloc)
-               vfree(ip_conntrack_hash);
+       if (vmalloced)
+               vfree(hash);
        else
-               free_pages((unsigned long)ip_conntrack_hash, 
-                          get_order(sizeof(struct list_head)
-                                    * ip_conntrack_htable_size));
+               free_pages((unsigned long)hash, 
+                          get_order(sizeof(struct list_head) * size));
 }
 
-void ip_conntrack_flush()
+/* Mishearing the voices in his head, our hero wonders how he's
+   supposed to kill the mall. */
+void ip_conntrack_cleanup(void)
 {
+       ip_ct_attach = NULL;
+
        /* This makes sure all current packets have passed through
            netfilter framework.  Roll on, two-stage module
            delete... */
@@ -1360,7 +1381,7 @@ void ip_conntrack_flush()
 
        ip_ct_event_cache_flush();
  i_see_dead_people:
-       ip_ct_iterate_cleanup(kill_all, NULL);
+       ip_conntrack_flush();
        if (atomic_read(&ip_conntrack_count) != 0) {
                schedule();
                goto i_see_dead_people;
@@ -1368,22 +1389,86 @@ void ip_conntrack_flush()
        /* wait until all references to ip_conntrack_untracked are dropped */
        while (atomic_read(&ip_conntrack_untracked.ct_general.use) > 1)
                schedule();
-}
 
-/* Mishearing the voices in his head, our hero wonders how he's
-   supposed to kill the mall. */
-void ip_conntrack_cleanup(void)
-{
-       ip_ct_attach = NULL;
-       ip_conntrack_flush();
        kmem_cache_destroy(ip_conntrack_cachep);
        kmem_cache_destroy(ip_conntrack_expect_cachep);
-       free_conntrack_hash();
+       free_conntrack_hash(ip_conntrack_hash, ip_conntrack_vmalloc,
+                           ip_conntrack_htable_size);
        nf_unregister_sockopt(&so_getorigdst);
 }
 
-static int hashsize;
-module_param(hashsize, int, 0400);
+static struct list_head *alloc_hashtable(int size, int *vmalloced)
+{
+       struct list_head *hash;
+       unsigned int i;
+
+       *vmalloced = 0; 
+       hash = (void*)__get_free_pages(GFP_KERNEL, 
+                                      get_order(sizeof(struct list_head)
+                                                * size));
+       if (!hash) { 
+               *vmalloced = 1;
+               printk(KERN_WARNING"ip_conntrack: falling back to vmalloc.\n");
+               hash = vmalloc(sizeof(struct list_head) * size);
+       }
+
+       if (hash)
+               for (i = 0; i < size; i++)
+                       INIT_LIST_HEAD(&hash[i]);
+
+       return hash;
+}
+
+static int set_hashsize(const char *val, struct kernel_param *kp)
+{
+       int i, bucket, hashsize, vmalloced;
+       int old_vmalloced, old_size;
+       int rnd;
+       struct list_head *hash, *old_hash;
+       struct ip_conntrack_tuple_hash *h;
+
+       /* On boot, we can set this without any fancy locking. */
+       if (!ip_conntrack_htable_size)
+               return param_set_int(val, kp);
+
+       hashsize = simple_strtol(val, NULL, 0);
+       if (!hashsize)
+               return -EINVAL;
+
+       hash = alloc_hashtable(hashsize, &vmalloced);
+       if (!hash)
+               return -ENOMEM;
+
+       /* We have to rehash for the new table anyway, so we also can 
+        * use a new random seed */
+       get_random_bytes(&rnd, 4);
+
+       write_lock_bh(&ip_conntrack_lock);
+       for (i = 0; i < ip_conntrack_htable_size; i++) {
+               while (!list_empty(&ip_conntrack_hash[i])) {
+                       h = list_entry(ip_conntrack_hash[i].next,
+                                      struct ip_conntrack_tuple_hash, list);
+                       list_del(&h->list);
+                       bucket = __hash_conntrack(&h->tuple, hashsize, rnd);
+                       list_add_tail(&h->list, &hash[bucket]);
+               }
+       }
+       old_size = ip_conntrack_htable_size;
+       old_vmalloced = ip_conntrack_vmalloc;
+       old_hash = ip_conntrack_hash;
+
+       ip_conntrack_htable_size = hashsize;
+       ip_conntrack_vmalloc = vmalloced;
+       ip_conntrack_hash = hash;
+       ip_conntrack_hash_rnd = rnd;
+       write_unlock_bh(&ip_conntrack_lock);
+
+       free_conntrack_hash(old_hash, old_vmalloced, old_size);
+       return 0;
+}
+
+module_param_call(hashsize, set_hashsize, param_get_uint,
+                 &ip_conntrack_htable_size, 0600);
 
 int __init ip_conntrack_init(void)
 {
@@ -1392,9 +1477,7 @@ int __init ip_conntrack_init(void)
 
        /* Idea from tcp.c: use 1/16384 of memory.  On i386: 32MB
         * machine has 256 buckets.  >= 1GB machines have 8192 buckets. */
-       if (hashsize) {
-               ip_conntrack_htable_size = hashsize;
-       } else {
+       if (!ip_conntrack_htable_size) {
                ip_conntrack_htable_size
                        = (((num_physpages << PAGE_SHIFT) / 16384)
                           / sizeof(struct list_head));
@@ -1416,20 +1499,8 @@ int __init ip_conntrack_init(void)
                return ret;
        }
 
-       /* AK: the hash table is twice as big than needed because it
-          uses list_head.  it would be much nicer to caches to use a
-          single pointer list head here. */
-       ip_conntrack_vmalloc = 0; 
-       ip_conntrack_hash 
-               =(void*)__get_free_pages(GFP_KERNEL, 
-                                        get_order(sizeof(struct list_head)
-                                                  *ip_conntrack_htable_size));
-       if (!ip_conntrack_hash) { 
-               ip_conntrack_vmalloc = 1;
-               printk(KERN_WARNING "ip_conntrack: falling back to vmalloc.\n");
-               ip_conntrack_hash = vmalloc(sizeof(struct list_head)
-                                           * ip_conntrack_htable_size);
-       }
+       ip_conntrack_hash = alloc_hashtable(ip_conntrack_htable_size,
+                                           &ip_conntrack_vmalloc);
        if (!ip_conntrack_hash) {
                printk(KERN_ERR "Unable to create ip_conntrack_hash\n");
                goto err_unreg_sockopt;
@@ -1461,9 +1532,6 @@ int __init ip_conntrack_init(void)
        ip_ct_protos[IPPROTO_ICMP] = &ip_conntrack_protocol_icmp;
        write_unlock_bh(&ip_conntrack_lock);
 
-       for (i = 0; i < ip_conntrack_htable_size; i++)
-               INIT_LIST_HEAD(&ip_conntrack_hash[i]);
-
        /* For use by ipt_REJECT */
        ip_ct_attach = ip_conntrack_attach;
 
@@ -1478,7 +1546,8 @@ int __init ip_conntrack_init(void)
 err_free_conntrack_slab:
        kmem_cache_destroy(ip_conntrack_cachep);
 err_free_hash:
-       free_conntrack_hash();
+       free_conntrack_hash(ip_conntrack_hash, ip_conntrack_vmalloc,
+                           ip_conntrack_htable_size);
 err_unreg_sockopt:
        nf_unregister_sockopt(&so_getorigdst);