Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf-next
[linux-2.6-block.git] / net / ipv4 / netfilter / ipt_CLUSTERIP.c
index a2e2b61cd7da230475f8ce7b9818eba67090bf05..2510c02c2d2168ffcd27f286c7eab3ca75165f26 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/netfilter_ipv4/ipt_CLUSTERIP.h>
 #include <net/netfilter/nf_conntrack.h>
 #include <net/net_namespace.h>
+#include <net/netns/generic.h>
 #include <net/checksum.h>
 #include <net/ip.h>
 
@@ -57,15 +58,21 @@ struct clusterip_config {
        struct rcu_head rcu;
 };
 
-static LIST_HEAD(clusterip_configs);
+#ifdef CONFIG_PROC_FS
+static const struct file_operations clusterip_proc_fops;
+#endif
 
-/* clusterip_lock protects the clusterip_configs list */
-static DEFINE_SPINLOCK(clusterip_lock);
+static int clusterip_net_id __read_mostly;
+
+struct clusterip_net {
+       struct list_head configs;
+       /* lock protects the configs list */
+       spinlock_t lock;
 
 #ifdef CONFIG_PROC_FS
-static const struct file_operations clusterip_proc_fops;
-static struct proc_dir_entry *clusterip_procdir;
+       struct proc_dir_entry *procdir;
 #endif
+};
 
 static inline void
 clusterip_config_get(struct clusterip_config *c)
@@ -92,10 +99,13 @@ clusterip_config_put(struct clusterip_config *c)
 static inline void
 clusterip_config_entry_put(struct clusterip_config *c)
 {
+       struct net *net = dev_net(c->dev);
+       struct clusterip_net *cn = net_generic(net, clusterip_net_id);
+
        local_bh_disable();
-       if (atomic_dec_and_lock(&c->entries, &clusterip_lock)) {
+       if (atomic_dec_and_lock(&c->entries, &cn->lock)) {
                list_del_rcu(&c->list);
-               spin_unlock(&clusterip_lock);
+               spin_unlock(&cn->lock);
                local_bh_enable();
 
                dev_mc_del(c->dev, c->clustermac);
@@ -113,11 +123,12 @@ clusterip_config_entry_put(struct clusterip_config *c)
 }
 
 static struct clusterip_config *
-__clusterip_config_find(__be32 clusterip)
+__clusterip_config_find(struct net *net, __be32 clusterip)
 {
        struct clusterip_config *c;
+       struct clusterip_net *cn = net_generic(net, clusterip_net_id);
 
-       list_for_each_entry_rcu(c, &clusterip_configs, list) {
+       list_for_each_entry_rcu(c, &cn->configs, list) {
                if (c->clusterip == clusterip)
                        return c;
        }
@@ -126,12 +137,12 @@ __clusterip_config_find(__be32 clusterip)
 }
 
 static inline struct clusterip_config *
-clusterip_config_find_get(__be32 clusterip, int entry)
+clusterip_config_find_get(struct net *net, __be32 clusterip, int entry)
 {
        struct clusterip_config *c;
 
        rcu_read_lock_bh();
-       c = __clusterip_config_find(clusterip);
+       c = __clusterip_config_find(net, clusterip);
        if (c) {
                if (unlikely(!atomic_inc_not_zero(&c->refcount)))
                        c = NULL;
@@ -158,6 +169,7 @@ clusterip_config_init(const struct ipt_clusterip_tgt_info *i, __be32 ip,
                        struct net_device *dev)
 {
        struct clusterip_config *c;
+       struct clusterip_net *cn = net_generic(dev_net(dev), clusterip_net_id);
 
        c = kzalloc(sizeof(*c), GFP_ATOMIC);
        if (!c)
@@ -180,7 +192,7 @@ clusterip_config_init(const struct ipt_clusterip_tgt_info *i, __be32 ip,
                /* create proc dir entry */
                sprintf(buffer, "%pI4", &ip);
                c->pde = proc_create_data(buffer, S_IWUSR|S_IRUSR,
-                                         clusterip_procdir,
+                                         cn->procdir,
                                          &clusterip_proc_fops, c);
                if (!c->pde) {
                        kfree(c);
@@ -189,9 +201,9 @@ clusterip_config_init(const struct ipt_clusterip_tgt_info *i, __be32 ip,
        }
 #endif
 
-       spin_lock_bh(&clusterip_lock);
-       list_add_rcu(&c->list, &clusterip_configs);
-       spin_unlock_bh(&clusterip_lock);
+       spin_lock_bh(&cn->lock);
+       list_add_rcu(&c->list, &cn->configs);
+       spin_unlock_bh(&cn->lock);
 
        return c;
 }
@@ -370,7 +382,7 @@ static int clusterip_tg_check(const struct xt_tgchk_param *par)
 
        /* FIXME: further sanity checks */
 
-       config = clusterip_config_find_get(e->ip.dst.s_addr, 1);
+       config = clusterip_config_find_get(par->net, e->ip.dst.s_addr, 1);
        if (!config) {
                if (!(cipinfo->flags & CLUSTERIP_FLAG_NEW)) {
                        pr_info("no config found for %pI4, need 'new'\n",
@@ -384,7 +396,7 @@ static int clusterip_tg_check(const struct xt_tgchk_param *par)
                                return -EINVAL;
                        }
 
-                       dev = dev_get_by_name(&init_net, e->ip.iniface);
+                       dev = dev_get_by_name(par->net, e->ip.iniface);
                        if (!dev) {
                                pr_info("no such interface %s\n",
                                        e->ip.iniface);
@@ -492,6 +504,7 @@ arp_mangle(const struct nf_hook_ops *ops,
        struct arphdr *arp = arp_hdr(skb);
        struct arp_payload *payload;
        struct clusterip_config *c;
+       struct net *net = dev_net(in ? in : out);
 
        /* we don't care about non-ethernet and non-ipv4 ARP */
        if (arp->ar_hrd != htons(ARPHRD_ETHER) ||
@@ -508,7 +521,7 @@ arp_mangle(const struct nf_hook_ops *ops,
 
        /* if there is no clusterip configuration for the arp reply's
         * source ip, we don't want to mangle it */
-       c = clusterip_config_find_get(payload->src_ip, 0);
+       c = clusterip_config_find_get(net, payload->src_ip, 0);
        if (!c)
                return NF_ACCEPT;
 
@@ -698,48 +711,75 @@ static const struct file_operations clusterip_proc_fops = {
 
 #endif /* CONFIG_PROC_FS */
 
+static int clusterip_net_init(struct net *net)
+{
+       struct clusterip_net *cn = net_generic(net, clusterip_net_id);
+
+       INIT_LIST_HEAD(&cn->configs);
+
+       spin_lock_init(&cn->lock);
+
+#ifdef CONFIG_PROC_FS
+       cn->procdir = proc_mkdir("ipt_CLUSTERIP", net->proc_net);
+       if (!cn->procdir) {
+               pr_err("Unable to proc dir entry\n");
+               return -ENOMEM;
+       }
+#endif /* CONFIG_PROC_FS */
+
+       return 0;
+}
+
+static void clusterip_net_exit(struct net *net)
+{
+#ifdef CONFIG_PROC_FS
+       struct clusterip_net *cn = net_generic(net, clusterip_net_id);
+       proc_remove(cn->procdir);
+#endif
+}
+
+static struct pernet_operations clusterip_net_ops = {
+       .init = clusterip_net_init,
+       .exit = clusterip_net_exit,
+       .id   = &clusterip_net_id,
+       .size = sizeof(struct clusterip_net),
+};
+
 static int __init clusterip_tg_init(void)
 {
        int ret;
 
-       ret = xt_register_target(&clusterip_tg_reg);
+       ret = register_pernet_subsys(&clusterip_net_ops);
        if (ret < 0)
                return ret;
 
+       ret = xt_register_target(&clusterip_tg_reg);
+       if (ret < 0)
+               goto cleanup_subsys;
+
        ret = nf_register_hook(&cip_arp_ops);
        if (ret < 0)
                goto cleanup_target;
 
-#ifdef CONFIG_PROC_FS
-       clusterip_procdir = proc_mkdir("ipt_CLUSTERIP", init_net.proc_net);
-       if (!clusterip_procdir) {
-               pr_err("Unable to proc dir entry\n");
-               ret = -ENOMEM;
-               goto cleanup_hook;
-       }
-#endif /* CONFIG_PROC_FS */
-
        pr_info("ClusterIP Version %s loaded successfully\n",
                CLUSTERIP_VERSION);
+
        return 0;
 
-#ifdef CONFIG_PROC_FS
-cleanup_hook:
-       nf_unregister_hook(&cip_arp_ops);
-#endif /* CONFIG_PROC_FS */
 cleanup_target:
        xt_unregister_target(&clusterip_tg_reg);
+cleanup_subsys:
+       unregister_pernet_subsys(&clusterip_net_ops);
        return ret;
 }
 
 static void __exit clusterip_tg_exit(void)
 {
        pr_info("ClusterIP Version %s unloading\n", CLUSTERIP_VERSION);
-#ifdef CONFIG_PROC_FS
-       proc_remove(clusterip_procdir);
-#endif
+
        nf_unregister_hook(&cip_arp_ops);
        xt_unregister_target(&clusterip_tg_reg);
+       unregister_pernet_subsys(&clusterip_net_ops);
 
        /* Wait for completion of call_rcu_bh()'s (clusterip_config_rcu_free) */
        rcu_barrier_bh();