Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[firefly-linux-kernel-4.4.55.git] / net / netfilter / nf_conntrack_core.c
index e4a0c4fb3a7cef64d1f15c9173d5a3e62ad616b4..ebb81d64436c947577f1021df981b390787ff117 100644 (file)
@@ -5,6 +5,7 @@
 /* (C) 1999-2001 Paul `Rusty' Russell
  * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
  * (C) 2003,2004 USAGI/WIDE Project <http://www.linux-ipv6.org>
+ * (C) 2005-2012 Patrick McHardy <kaber@trash.net>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
 #include <net/netfilter/nf_conntrack_zones.h>
 #include <net/netfilter/nf_conntrack_timestamp.h>
 #include <net/netfilter/nf_conntrack_timeout.h>
+#include <net/netfilter/nf_conntrack_labels.h>
 #include <net/netfilter/nf_nat.h>
 #include <net/netfilter/nf_nat_core.h>
+#include <net/netfilter/nf_nat_helper.h>
 
 #define NF_CONNTRACK_VERSION   "0.5.0"
 
@@ -763,6 +766,7 @@ void nf_conntrack_free(struct nf_conn *ct)
 }
 EXPORT_SYMBOL_GPL(nf_conntrack_free);
 
+
 /* Allocate a new conntrack: we return -ENOMEM if classification
    failed due to stress.  Otherwise it really is unclassifiable. */
 static struct nf_conntrack_tuple_hash *
@@ -809,6 +813,7 @@ init_conntrack(struct net *net, struct nf_conn *tmpl,
 
        nf_ct_acct_ext_add(ct, GFP_ATOMIC);
        nf_ct_tstamp_ext_add(ct, GFP_ATOMIC);
+       nf_ct_labels_ext_add(ct);
 
        ecache = tmpl ? nf_ct_ecache_find(tmpl) : NULL;
        nf_ct_ecache_ext_add(ct, ecache ? ecache->ctmask : 0,
@@ -1256,7 +1261,7 @@ void nf_ct_iterate_cleanup(struct net *net,
 EXPORT_SYMBOL_GPL(nf_ct_iterate_cleanup);
 
 struct __nf_ct_flush_report {
-       u32 pid;
+       u32 portid;
        int report;
 };
 
@@ -1271,7 +1276,7 @@ static int kill_report(struct nf_conn *i, void *data)
 
        /* If we fail to deliver the event, death_by_timeout() will retry */
        if (nf_conntrack_event_report(IPCT_DESTROY, i,
-                                     fr->pid, fr->report) < 0)
+                                     fr->portid, fr->report) < 0)
                return 1;
 
        /* Avoid the delivery of the destroy event in death_by_timeout(). */
@@ -1294,10 +1299,10 @@ void nf_ct_free_hashtable(void *hash, unsigned int size)
 }
 EXPORT_SYMBOL_GPL(nf_ct_free_hashtable);
 
-void nf_conntrack_flush_report(struct net *net, u32 pid, int report)
+void nf_conntrack_flush_report(struct net *net, u32 portid, int report)
 {
        struct __nf_ct_flush_report fr = {
-               .pid    = pid,
+               .portid = portid,
                .report = report,
        };
        nf_ct_iterate_cleanup(net, kill_report, &fr);
@@ -1331,57 +1336,78 @@ static int untrack_refs(void)
        return cnt;
 }
 
-static void nf_conntrack_cleanup_init_net(void)
+void nf_conntrack_cleanup_start(void)
+{
+       RCU_INIT_POINTER(ip_ct_attach, NULL);
+}
+
+void nf_conntrack_cleanup_end(void)
 {
+       RCU_INIT_POINTER(nf_ct_destroy, NULL);
        while (untrack_refs() > 0)
                schedule();
 
 #ifdef CONFIG_NF_CONNTRACK_ZONES
        nf_ct_extend_unregister(&nf_ct_zone_extend);
 #endif
+       nf_conntrack_proto_fini();
+       nf_conntrack_labels_fini();
+       nf_conntrack_helper_fini();
+       nf_conntrack_timeout_fini();
+       nf_conntrack_ecache_fini();
+       nf_conntrack_tstamp_fini();
+       nf_conntrack_acct_fini();
+       nf_conntrack_expect_fini();
 }
 
-static void nf_conntrack_cleanup_net(struct net *net)
+/*
+ * Mishearing the voices in his head, our hero wonders how he's
+ * supposed to kill the mall.
+ */
+void nf_conntrack_cleanup_net(struct net *net)
 {
- i_see_dead_people:
-       nf_ct_iterate_cleanup(net, kill_all, NULL);
-       nf_ct_release_dying_list(net);
-       if (atomic_read(&net->ct.count) != 0) {
-               schedule();
-               goto i_see_dead_people;
-       }
+       LIST_HEAD(single);
 
-       nf_ct_free_hashtable(net->ct.hash, net->ct.htable_size);
-       nf_conntrack_helper_fini(net);
-       nf_conntrack_timeout_fini(net);
-       nf_conntrack_ecache_fini(net);
-       nf_conntrack_tstamp_fini(net);
-       nf_conntrack_acct_fini(net);
-       nf_conntrack_expect_fini(net);
-       kmem_cache_destroy(net->ct.nf_conntrack_cachep);
-       kfree(net->ct.slabname);
-       free_percpu(net->ct.stat);
+       list_add(&net->exit_list, &single);
+       nf_conntrack_cleanup_net_list(&single);
 }
 
-/* Mishearing the voices in his head, our hero wonders how he's
-   supposed to kill the mall. */
-void nf_conntrack_cleanup(struct net *net)
+void nf_conntrack_cleanup_net_list(struct list_head *net_exit_list)
 {
-       if (net_eq(net, &init_net))
-               RCU_INIT_POINTER(ip_ct_attach, NULL);
+       int busy;
+       struct net *net;
 
-       /* This makes sure all current packets have passed through
-          netfilter framework.  Roll on, two-stage module
-          delete... */
+       /*
+        * This makes sure all current packets have passed through
+        *  netfilter framework.  Roll on, two-stage module
+        *  delete...
+        */
        synchronize_net();
-       nf_conntrack_proto_fini(net);
-       nf_conntrack_cleanup_net(net);
-}
+i_see_dead_people:
+       busy = 0;
+       list_for_each_entry(net, net_exit_list, exit_list) {
+               nf_ct_iterate_cleanup(net, kill_all, NULL);
+               nf_ct_release_dying_list(net);
+               if (atomic_read(&net->ct.count) != 0)
+                       busy = 1;
+       }
+       if (busy) {
+               schedule();
+               goto i_see_dead_people;
+       }
 
-void nf_conntrack_cleanup_end(void)
-{
-       RCU_INIT_POINTER(nf_ct_destroy, NULL);
-       nf_conntrack_cleanup_init_net();
+       list_for_each_entry(net, net_exit_list, exit_list) {
+               nf_ct_free_hashtable(net->ct.hash, net->ct.htable_size);
+               nf_conntrack_proto_pernet_fini(net);
+               nf_conntrack_helper_pernet_fini(net);
+               nf_conntrack_ecache_pernet_fini(net);
+               nf_conntrack_tstamp_pernet_fini(net);
+               nf_conntrack_acct_pernet_fini(net);
+               nf_conntrack_expect_pernet_fini(net);
+               kmem_cache_destroy(net->ct.nf_conntrack_cachep);
+               kfree(net->ct.slabname);
+               free_percpu(net->ct.stat);
+       }
 }
 
 void *nf_ct_alloc_hashtable(unsigned int *sizep, int nulls)
@@ -1474,7 +1500,7 @@ void nf_ct_untracked_status_or(unsigned long bits)
 }
 EXPORT_SYMBOL_GPL(nf_ct_untracked_status_or);
 
-static int nf_conntrack_init_init_net(void)
+int nf_conntrack_init_start(void)
 {
        int max_factor = 8;
        int ret, cpu;
@@ -1501,11 +1527,44 @@ static int nf_conntrack_init_init_net(void)
        printk(KERN_INFO "nf_conntrack version %s (%u buckets, %d max)\n",
               NF_CONNTRACK_VERSION, nf_conntrack_htable_size,
               nf_conntrack_max);
+
+       ret = nf_conntrack_expect_init();
+       if (ret < 0)
+               goto err_expect;
+
+       ret = nf_conntrack_acct_init();
+       if (ret < 0)
+               goto err_acct;
+
+       ret = nf_conntrack_tstamp_init();
+       if (ret < 0)
+               goto err_tstamp;
+
+       ret = nf_conntrack_ecache_init();
+       if (ret < 0)
+               goto err_ecache;
+
+       ret = nf_conntrack_timeout_init();
+       if (ret < 0)
+               goto err_timeout;
+
+       ret = nf_conntrack_helper_init();
+       if (ret < 0)
+               goto err_helper;
+
+       ret = nf_conntrack_labels_init();
+       if (ret < 0)
+               goto err_labels;
+
 #ifdef CONFIG_NF_CONNTRACK_ZONES
        ret = nf_ct_extend_register(&nf_ct_zone_extend);
        if (ret < 0)
                goto err_extend;
 #endif
+       ret = nf_conntrack_proto_init();
+       if (ret < 0)
+               goto err_proto;
+
        /* Set up fake conntrack: to never be deleted, not in any hashes */
        for_each_possible_cpu(cpu) {
                struct nf_conn *ct = &per_cpu(nf_conntrack_untracked, cpu);
@@ -1516,12 +1575,38 @@ static int nf_conntrack_init_init_net(void)
        nf_ct_untracked_status_or(IPS_CONFIRMED | IPS_UNTRACKED);
        return 0;
 
+err_proto:
 #ifdef CONFIG_NF_CONNTRACK_ZONES
+       nf_ct_extend_unregister(&nf_ct_zone_extend);
 err_extend:
 #endif
+       nf_conntrack_labels_fini();
+err_labels:
+       nf_conntrack_helper_fini();
+err_helper:
+       nf_conntrack_timeout_fini();
+err_timeout:
+       nf_conntrack_ecache_fini();
+err_ecache:
+       nf_conntrack_tstamp_fini();
+err_tstamp:
+       nf_conntrack_acct_fini();
+err_acct:
+       nf_conntrack_expect_fini();
+err_expect:
        return ret;
 }
 
+void nf_conntrack_init_end(void)
+{
+       /* For use by REJECT target */
+       RCU_INIT_POINTER(ip_ct_attach, nf_conntrack_attach);
+       RCU_INIT_POINTER(nf_ct_destroy, destroy_conntrack);
+
+       /* Howto get NAT offsets */
+       RCU_INIT_POINTER(nf_ct_nat_offset, NULL);
+}
+
 /*
  * We need to use special "null" values, not used in hash table
  */
@@ -1529,7 +1614,7 @@ err_extend:
 #define DYING_NULLS_VAL                ((1<<30)+1)
 #define TEMPLATE_NULLS_VAL     ((1<<30)+2)
 
-static int nf_conntrack_init_net(struct net *net)
+int nf_conntrack_init_net(struct net *net)
 {
        int ret;
 
@@ -1565,35 +1650,36 @@ static int nf_conntrack_init_net(struct net *net)
                printk(KERN_ERR "Unable to create nf_conntrack_hash\n");
                goto err_hash;
        }
-       ret = nf_conntrack_expect_init(net);
+       ret = nf_conntrack_expect_pernet_init(net);
        if (ret < 0)
                goto err_expect;
-       ret = nf_conntrack_acct_init(net);
+       ret = nf_conntrack_acct_pernet_init(net);
        if (ret < 0)
                goto err_acct;
-       ret = nf_conntrack_tstamp_init(net);
+       ret = nf_conntrack_tstamp_pernet_init(net);
        if (ret < 0)
                goto err_tstamp;
-       ret = nf_conntrack_ecache_init(net);
+       ret = nf_conntrack_ecache_pernet_init(net);
        if (ret < 0)
                goto err_ecache;
-       ret = nf_conntrack_timeout_init(net);
-       if (ret < 0)
-               goto err_timeout;
-       ret = nf_conntrack_helper_init(net);
+       ret = nf_conntrack_helper_pernet_init(net);
        if (ret < 0)
                goto err_helper;
+       ret = nf_conntrack_proto_pernet_init(net);
+       if (ret < 0)
+               goto err_proto;
        return 0;
+
+err_proto:
+       nf_conntrack_helper_pernet_fini(net);
 err_helper:
-       nf_conntrack_timeout_fini(net);
-err_timeout:
-       nf_conntrack_ecache_fini(net);
+       nf_conntrack_ecache_pernet_fini(net);
 err_ecache:
-       nf_conntrack_tstamp_fini(net);
+       nf_conntrack_tstamp_pernet_fini(net);
 err_tstamp:
-       nf_conntrack_acct_fini(net);
+       nf_conntrack_acct_pernet_fini(net);
 err_acct:
-       nf_conntrack_expect_fini(net);
+       nf_conntrack_expect_pernet_fini(net);
 err_expect:
        nf_ct_free_hashtable(net->ct.hash, net->ct.htable_size);
 err_hash:
@@ -1610,38 +1696,3 @@ s16 (*nf_ct_nat_offset)(const struct nf_conn *ct,
                        enum ip_conntrack_dir dir,
                        u32 seq);
 EXPORT_SYMBOL_GPL(nf_ct_nat_offset);
-
-int nf_conntrack_init(struct net *net)
-{
-       int ret;
-
-       if (net_eq(net, &init_net)) {
-               ret = nf_conntrack_init_init_net();
-               if (ret < 0)
-                       goto out_init_net;
-       }
-       ret = nf_conntrack_proto_init(net);
-       if (ret < 0)
-               goto out_proto;
-       ret = nf_conntrack_init_net(net);
-       if (ret < 0)
-               goto out_net;
-
-       if (net_eq(net, &init_net)) {
-               /* For use by REJECT target */
-               RCU_INIT_POINTER(ip_ct_attach, nf_conntrack_attach);
-               RCU_INIT_POINTER(nf_ct_destroy, destroy_conntrack);
-
-               /* Howto get NAT offsets */
-               RCU_INIT_POINTER(nf_ct_nat_offset, NULL);
-       }
-       return 0;
-
-out_net:
-       nf_conntrack_proto_fini(net);
-out_proto:
-       if (net_eq(net, &init_net))
-               nf_conntrack_cleanup_init_net();
-out_init_net:
-       return ret;
-}