]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
netfilter: conntrack: register hooks in netns when needed by ruleset
[mirror_ubuntu-artful-kernel.git] / net / ipv6 / netfilter / nf_conntrack_l3proto_ipv6.c
index 389f712854f298dcbcec0bd7487f8420b7472774..72fe48075b7f45c654843bb6fe1914c6d8a36582 100644 (file)
 #include <net/netfilter/ipv6/nf_defrag_ipv6.h>
 #include <net/netfilter/nf_log.h>
 
+static int conntrack6_net_id;
+static DEFINE_MUTEX(register_ipv6_hooks);
+
+struct conntrack6_net {
+       unsigned int users;
+};
+
 static bool ipv6_pkt_to_tuple(const struct sk_buff *skb, unsigned int nhoff,
                              struct nf_conntrack_tuple *tuple)
 {
@@ -308,6 +315,36 @@ static int ipv6_nlattr_tuple_size(void)
 }
 #endif
 
+static int ipv6_hooks_register(struct net *net)
+{
+       struct conntrack6_net *cnet = net_generic(net, conntrack6_net_id);
+       int err = 0;
+
+       mutex_lock(&register_ipv6_hooks);
+       cnet->users++;
+       if (cnet->users > 1)
+               goto out_unlock;
+
+       err = nf_register_net_hooks(net, ipv6_conntrack_ops,
+                                   ARRAY_SIZE(ipv6_conntrack_ops));
+       if (err)
+               cnet->users = 0;
+ out_unlock:
+       mutex_unlock(&register_ipv6_hooks);
+       return err;
+}
+
+static void ipv6_hooks_unregister(struct net *net)
+{
+       struct conntrack6_net *cnet = net_generic(net, conntrack6_net_id);
+
+       mutex_lock(&register_ipv6_hooks);
+       if (cnet->users && (--cnet->users == 0))
+               nf_unregister_net_hooks(net, ipv6_conntrack_ops,
+                                       ARRAY_SIZE(ipv6_conntrack_ops));
+       mutex_unlock(&register_ipv6_hooks);
+}
+
 struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv6 __read_mostly = {
        .l3proto                = PF_INET6,
        .name                   = "ipv6",
@@ -321,6 +358,8 @@ struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv6 __read_mostly = {
        .nlattr_to_tuple        = ipv6_nlattr_to_tuple,
        .nla_policy             = ipv6_nla_policy,
 #endif
+       .net_ns_get             = ipv6_hooks_register,
+       .net_ns_put             = ipv6_hooks_unregister,
        .me                     = THIS_MODULE,
 };
 
@@ -379,6 +418,8 @@ static void ipv6_net_exit(struct net *net)
 static struct pernet_operations ipv6_net_ops = {
        .init = ipv6_net_init,
        .exit = ipv6_net_exit,
+       .id = &conntrack6_net_id,
+       .size = sizeof(struct conntrack6_net),
 };
 
 static int __init nf_conntrack_l3proto_ipv6_init(void)
@@ -398,18 +439,10 @@ static int __init nf_conntrack_l3proto_ipv6_init(void)
        if (ret < 0)
                goto cleanup_sockopt;
 
-       ret = nf_register_hooks(ipv6_conntrack_ops,
-                               ARRAY_SIZE(ipv6_conntrack_ops));
-       if (ret < 0) {
-               pr_err("nf_conntrack_ipv6: can't register pre-routing defrag "
-                      "hook.\n");
-               goto cleanup_pernet;
-       }
-
        ret = nf_ct_l4proto_register(builtin_l4proto6,
                                     ARRAY_SIZE(builtin_l4proto6));
        if (ret < 0)
-               goto cleanup_hooks;
+               goto cleanup_pernet;
 
        ret = nf_ct_l3proto_register(&nf_conntrack_l3proto_ipv6);
        if (ret < 0) {
@@ -420,8 +453,6 @@ static int __init nf_conntrack_l3proto_ipv6_init(void)
 cleanup_l4proto:
        nf_ct_l4proto_unregister(builtin_l4proto6,
                                 ARRAY_SIZE(builtin_l4proto6));
- cleanup_hooks:
-       nf_unregister_hooks(ipv6_conntrack_ops, ARRAY_SIZE(ipv6_conntrack_ops));
  cleanup_pernet:
        unregister_pernet_subsys(&ipv6_net_ops);
  cleanup_sockopt:
@@ -435,7 +466,6 @@ static void __exit nf_conntrack_l3proto_ipv6_fini(void)
        nf_ct_l3proto_unregister(&nf_conntrack_l3proto_ipv6);
        nf_ct_l4proto_unregister(builtin_l4proto6,
                                 ARRAY_SIZE(builtin_l4proto6));
-       nf_unregister_hooks(ipv6_conntrack_ops, ARRAY_SIZE(ipv6_conntrack_ops));
        unregister_pernet_subsys(&ipv6_net_ops);
        nf_unregister_sockopt(&so_getorigdst6);
 }