u_int8_t af;            /* address/protocol family */
        int priority;           /* hook order */
 
+       /* called when table is needed in the given netns */
+       int (*table_init)(struct net *net);
+
        /* A unique name... */
        const char name[XT_TABLE_MAXNAMELEN];
 };
        return cnt;
 }
 
-struct nf_hook_ops *xt_hook_link(const struct xt_table *, nf_hookfn *);
-void xt_hook_unlink(const struct xt_table *, struct nf_hook_ops *);
+struct nf_hook_ops *xt_hook_ops_alloc(const struct xt_table *, nf_hookfn *);
 
 #ifdef CONFIG_COMPAT
 #include <net/compat.h>
 
        return ret;
 }
 
+static void __arpt_unregister_table(struct xt_table *table)
+{
+       struct xt_table_info *private;
+       void *loc_cpu_entry;
+       struct module *table_owner = table->me;
+       struct arpt_entry *iter;
+
+       private = xt_unregister_table(table);
+
+       /* Decrease module usage counts and free resources */
+       loc_cpu_entry = private->entries;
+       xt_entry_foreach(iter, loc_cpu_entry, private->size)
+               cleanup_entry(iter);
+       if (private->number > private->initial_entries)
+               module_put(table_owner);
+       xt_free_table_info(private);
+}
+
 int arpt_register_table(struct net *net,
                        const struct xt_table *table,
                        const struct arpt_replace *repl,
                goto out_free;
        }
 
+       /* set res now, will see skbs right after nf_register_net_hooks */
        WRITE_ONCE(*res, new_table);
 
+       ret = nf_register_net_hooks(net, ops, hweight32(table->valid_hooks));
+       if (ret != 0) {
+               __arpt_unregister_table(new_table);
+               *res = NULL;
+       }
+
        return ret;
 
 out_free:
 void arpt_unregister_table(struct net *net, struct xt_table *table,
                           const struct nf_hook_ops *ops)
 {
-       struct xt_table_info *private;
-       void *loc_cpu_entry;
-       struct module *table_owner = table->me;
-       struct arpt_entry *iter;
-
-       private = xt_unregister_table(table);
-
-       /* Decrease module usage counts and free resources */
-       loc_cpu_entry = private->entries;
-       xt_entry_foreach(iter, loc_cpu_entry, private->size)
-               cleanup_entry(iter);
-       if (private->number > private->initial_entries)
-               module_put(table_owner);
-       xt_free_table_info(private);
+       nf_unregister_net_hooks(net, ops, hweight32(table->valid_hooks));
+       __arpt_unregister_table(table);
 }
 
 /* The built-in targets: standard (NULL) and error. */
 
 #define FILTER_VALID_HOOKS ((1 << NF_ARP_IN) | (1 << NF_ARP_OUT) | \
                           (1 << NF_ARP_FORWARD))
 
+static int __net_init arptable_filter_table_init(struct net *net);
+
 static const struct xt_table packet_filter = {
        .name           = "filter",
        .valid_hooks    = FILTER_VALID_HOOKS,
        .me             = THIS_MODULE,
        .af             = NFPROTO_ARP,
        .priority       = NF_IP_PRI_FILTER,
+       .table_init     = arptable_filter_table_init,
 };
 
 /* The work comes in here from netfilter.c */
 
 static struct nf_hook_ops *arpfilter_ops __read_mostly;
 
-static int __net_init arptable_filter_net_init(struct net *net)
+static int __net_init arptable_filter_table_init(struct net *net)
 {
        struct arpt_replace *repl;
        int err;
 
+       if (net->ipv4.arptable_filter)
+               return 0;
+
        repl = arpt_alloc_initial_table(&packet_filter);
        if (repl == NULL)
                return -ENOMEM;
 
 static void __net_exit arptable_filter_net_exit(struct net *net)
 {
+       if (!net->ipv4.arptable_filter)
+               return;
        arpt_unregister_table(net, net->ipv4.arptable_filter, arpfilter_ops);
+       net->ipv4.arptable_filter = NULL;
 }
 
 static struct pernet_operations arptable_filter_net_ops = {
-       .init = arptable_filter_net_init,
        .exit = arptable_filter_net_exit,
 };
 
 {
        int ret;
 
+       arpfilter_ops = xt_hook_ops_alloc(&packet_filter, arptable_filter_hook);
+       if (IS_ERR(arpfilter_ops))
+               return PTR_ERR(arpfilter_ops);
+
        ret = register_pernet_subsys(&arptable_filter_net_ops);
-       if (ret < 0)
+       if (ret < 0) {
+               kfree(arpfilter_ops);
                return ret;
-
-       arpfilter_ops = xt_hook_link(&packet_filter, arptable_filter_hook);
-       if (IS_ERR(arpfilter_ops)) {
-               ret = PTR_ERR(arpfilter_ops);
-               goto cleanup_table;
        }
-       return ret;
 
-cleanup_table:
-       unregister_pernet_subsys(&arptable_filter_net_ops);
        return ret;
 }
 
 static void __exit arptable_filter_fini(void)
 {
-       xt_hook_unlink(&packet_filter, arpfilter_ops);
        unregister_pernet_subsys(&arptable_filter_net_ops);
+       kfree(arpfilter_ops);
 }
 
 module_init(arptable_filter_init);
 
        return ret;
 }
 
+static void __ipt_unregister_table(struct net *net, struct xt_table *table)
+{
+       struct xt_table_info *private;
+       void *loc_cpu_entry;
+       struct module *table_owner = table->me;
+       struct ipt_entry *iter;
+
+       private = xt_unregister_table(table);
+
+       /* Decrease module usage counts and free resources */
+       loc_cpu_entry = private->entries;
+       xt_entry_foreach(iter, loc_cpu_entry, private->size)
+               cleanup_entry(iter, net);
+       if (private->number > private->initial_entries)
+               module_put(table_owner);
+       xt_free_table_info(private);
+}
+
 int ipt_register_table(struct net *net, const struct xt_table *table,
                       const struct ipt_replace *repl,
                       const struct nf_hook_ops *ops, struct xt_table **res)
                goto out_free;
        }
 
+       /* set res now, will see skbs right after nf_register_net_hooks */
        WRITE_ONCE(*res, new_table);
+
+       ret = nf_register_net_hooks(net, ops, hweight32(table->valid_hooks));
+       if (ret != 0) {
+               __ipt_unregister_table(net, new_table);
+               *res = NULL;
+       }
+
        return ret;
 
 out_free:
 void ipt_unregister_table(struct net *net, struct xt_table *table,
                          const struct nf_hook_ops *ops)
 {
-       struct xt_table_info *private;
-       void *loc_cpu_entry;
-       struct module *table_owner = table->me;
-       struct ipt_entry *iter;
-
-       private = xt_unregister_table(table);
-
-       /* Decrease module usage counts and free resources */
-       loc_cpu_entry = private->entries;
-       xt_entry_foreach(iter, loc_cpu_entry, private->size)
-               cleanup_entry(iter, net);
-       if (private->number > private->initial_entries)
-               module_put(table_owner);
-       xt_free_table_info(private);
+       nf_unregister_net_hooks(net, ops, hweight32(table->valid_hooks));
+       __ipt_unregister_table(net, table);
 }
 
 /* Returns 1 if the type and code is matched by the range, 0 otherwise */
 
 #define FILTER_VALID_HOOKS ((1 << NF_INET_LOCAL_IN) | \
                            (1 << NF_INET_FORWARD) | \
                            (1 << NF_INET_LOCAL_OUT))
+static int __net_init iptable_filter_table_init(struct net *net);
 
 static const struct xt_table packet_filter = {
        .name           = "filter",
        .me             = THIS_MODULE,
        .af             = NFPROTO_IPV4,
        .priority       = NF_IP_PRI_FILTER,
+       .table_init     = iptable_filter_table_init,
 };
 
 static unsigned int
 static struct nf_hook_ops *filter_ops __read_mostly;
 
 /* Default to forward because I got too much mail already. */
-static bool forward = true;
+static bool forward __read_mostly = true;
 module_param(forward, bool, 0000);
 
-static int __net_init iptable_filter_net_init(struct net *net)
+static int __net_init iptable_filter_table_init(struct net *net)
 {
        struct ipt_replace *repl;
        int err;
 
+       if (net->ipv4.iptable_filter)
+               return 0;
+
        repl = ipt_alloc_initial_table(&packet_filter);
        if (repl == NULL)
                return -ENOMEM;
        return err;
 }
 
+static int __net_init iptable_filter_net_init(struct net *net)
+{
+       if (net == &init_net || !forward)
+               return iptable_filter_table_init(net);
+
+       return 0;
+}
+
 static void __net_exit iptable_filter_net_exit(struct net *net)
 {
+       if (!net->ipv4.iptable_filter)
+               return;
        ipt_unregister_table(net, net->ipv4.iptable_filter, filter_ops);
+       net->ipv4.iptable_filter = NULL;
 }
 
 static struct pernet_operations iptable_filter_net_ops = {
 {
        int ret;
 
+       filter_ops = xt_hook_ops_alloc(&packet_filter, iptable_filter_hook);
+       if (IS_ERR(filter_ops))
+               return PTR_ERR(filter_ops);
+
        ret = register_pernet_subsys(&iptable_filter_net_ops);
        if (ret < 0)
-               return ret;
-
-       /* Register hooks */
-       filter_ops = xt_hook_link(&packet_filter, iptable_filter_hook);
-       if (IS_ERR(filter_ops)) {
-               ret = PTR_ERR(filter_ops);
-               unregister_pernet_subsys(&iptable_filter_net_ops);
-       }
+               kfree(filter_ops);
 
        return ret;
 }
 
 static void __exit iptable_filter_fini(void)
 {
-       xt_hook_unlink(&packet_filter, filter_ops);
        unregister_pernet_subsys(&iptable_filter_net_ops);
+       kfree(filter_ops);
 }
 
 module_init(iptable_filter_init);
 
                            (1 << NF_INET_LOCAL_OUT) | \
                            (1 << NF_INET_POST_ROUTING))
 
+static int __net_init iptable_mangle_table_init(struct net *net);
+
 static const struct xt_table packet_mangler = {
        .name           = "mangle",
        .valid_hooks    = MANGLE_VALID_HOOKS,
        .me             = THIS_MODULE,
        .af             = NFPROTO_IPV4,
        .priority       = NF_IP_PRI_MANGLE,
+       .table_init     = iptable_mangle_table_init,
 };
 
 static unsigned int
 }
 
 static struct nf_hook_ops *mangle_ops __read_mostly;
-
-static int __net_init iptable_mangle_net_init(struct net *net)
+static int __net_init iptable_mangle_table_init(struct net *net)
 {
        struct ipt_replace *repl;
        int ret;
 
+       if (net->ipv4.iptable_mangle)
+               return 0;
+
        repl = ipt_alloc_initial_table(&packet_mangler);
        if (repl == NULL)
                return -ENOMEM;
 
 static void __net_exit iptable_mangle_net_exit(struct net *net)
 {
+       if (!net->ipv4.iptable_mangle)
+               return;
        ipt_unregister_table(net, net->ipv4.iptable_mangle, mangle_ops);
+       net->ipv4.iptable_mangle = NULL;
 }
 
 static struct pernet_operations iptable_mangle_net_ops = {
-       .init = iptable_mangle_net_init,
        .exit = iptable_mangle_net_exit,
 };
 
 {
        int ret;
 
+       mangle_ops = xt_hook_ops_alloc(&packet_mangler, iptable_mangle_hook);
+       if (IS_ERR(mangle_ops)) {
+               ret = PTR_ERR(mangle_ops);
+               return ret;
+       }
+
        ret = register_pernet_subsys(&iptable_mangle_net_ops);
-       if (ret < 0)
+       if (ret < 0) {
+               kfree(mangle_ops);
                return ret;
+       }
 
-       /* Register hooks */
-       mangle_ops = xt_hook_link(&packet_mangler, iptable_mangle_hook);
-       if (IS_ERR(mangle_ops)) {
-               ret = PTR_ERR(mangle_ops);
+       ret = iptable_mangle_table_init(&init_net);
+       if (ret) {
                unregister_pernet_subsys(&iptable_mangle_net_ops);
+               kfree(mangle_ops);
        }
 
        return ret;
 
 static void __exit iptable_mangle_fini(void)
 {
-       xt_hook_unlink(&packet_mangler, mangle_ops);
        unregister_pernet_subsys(&iptable_mangle_net_ops);
+       kfree(mangle_ops);
 }
 
 module_init(iptable_mangle_init);
 
 #include <net/netfilter/nf_nat_core.h>
 #include <net/netfilter/nf_nat_l3proto.h>
 
+static int __net_init iptable_nat_table_init(struct net *net);
+
 static const struct xt_table nf_nat_ipv4_table = {
        .name           = "nat",
        .valid_hooks    = (1 << NF_INET_PRE_ROUTING) |
                          (1 << NF_INET_LOCAL_IN),
        .me             = THIS_MODULE,
        .af             = NFPROTO_IPV4,
+       .table_init     = iptable_nat_table_init,
 };
 
 static unsigned int iptable_nat_do_chain(void *priv,
        },
 };
 
-static int __net_init iptable_nat_net_init(struct net *net)
+static int __net_init iptable_nat_table_init(struct net *net)
 {
        struct ipt_replace *repl;
        int ret;
 
+       if (net->ipv4.nat_table)
+               return 0;
+
        repl = ipt_alloc_initial_table(&nf_nat_ipv4_table);
        if (repl == NULL)
                return -ENOMEM;
 
 static void __net_exit iptable_nat_net_exit(struct net *net)
 {
+       if (!net->ipv4.nat_table)
+               return;
        ipt_unregister_table(net, net->ipv4.nat_table, nf_nat_ipv4_ops);
+       net->ipv4.nat_table = NULL;
 }
 
 static struct pernet_operations iptable_nat_net_ops = {
-       .init   = iptable_nat_net_init,
        .exit   = iptable_nat_net_exit,
 };
 
 static int __init iptable_nat_init(void)
 {
-       int err;
-
-       err = register_pernet_subsys(&iptable_nat_net_ops);
-       if (err < 0)
-               goto err1;
+       int ret = register_pernet_subsys(&iptable_nat_net_ops);
 
-       err = nf_register_hooks(nf_nat_ipv4_ops, ARRAY_SIZE(nf_nat_ipv4_ops));
-       if (err < 0)
-               goto err2;
-       return 0;
+       if (ret)
+               return ret;
 
-err2:
-       unregister_pernet_subsys(&iptable_nat_net_ops);
-err1:
-       return err;
+       ret = iptable_nat_table_init(&init_net);
+       if (ret)
+               unregister_pernet_subsys(&iptable_nat_net_ops);
+       return ret;
 }
 
 static void __exit iptable_nat_exit(void)
 {
-       nf_unregister_hooks(nf_nat_ipv4_ops, ARRAY_SIZE(nf_nat_ipv4_ops));
        unregister_pernet_subsys(&iptable_nat_net_ops);
 }
 
 
 
 #define RAW_VALID_HOOKS ((1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_LOCAL_OUT))
 
+static int __net_init iptable_raw_table_init(struct net *net);
+
 static const struct xt_table packet_raw = {
        .name = "raw",
        .valid_hooks =  RAW_VALID_HOOKS,
        .me = THIS_MODULE,
        .af = NFPROTO_IPV4,
        .priority = NF_IP_PRI_RAW,
+       .table_init = iptable_raw_table_init,
 };
 
 /* The work comes in here from netfilter.c. */
 
 static struct nf_hook_ops *rawtable_ops __read_mostly;
 
-static int __net_init iptable_raw_net_init(struct net *net)
+static int __net_init iptable_raw_table_init(struct net *net)
 {
        struct ipt_replace *repl;
        int ret;
 
+       if (net->ipv4.iptable_raw)
+               return 0;
+
        repl = ipt_alloc_initial_table(&packet_raw);
        if (repl == NULL)
                return -ENOMEM;
 
 static void __net_exit iptable_raw_net_exit(struct net *net)
 {
+       if (!net->ipv4.iptable_raw)
+               return;
        ipt_unregister_table(net, net->ipv4.iptable_raw, rawtable_ops);
+       net->ipv4.iptable_raw = NULL;
 }
 
 static struct pernet_operations iptable_raw_net_ops = {
-       .init = iptable_raw_net_init,
        .exit = iptable_raw_net_exit,
 };
 
 {
        int ret;
 
+       rawtable_ops = xt_hook_ops_alloc(&packet_raw, iptable_raw_hook);
+       if (IS_ERR(rawtable_ops))
+               return PTR_ERR(rawtable_ops);
+
        ret = register_pernet_subsys(&iptable_raw_net_ops);
-       if (ret < 0)
+       if (ret < 0) {
+               kfree(rawtable_ops);
                return ret;
+       }
 
-       /* Register hooks */
-       rawtable_ops = xt_hook_link(&packet_raw, iptable_raw_hook);
-       if (IS_ERR(rawtable_ops)) {
-               ret = PTR_ERR(rawtable_ops);
+       ret = iptable_raw_table_init(&init_net);
+       if (ret) {
                unregister_pernet_subsys(&iptable_raw_net_ops);
+               kfree(rawtable_ops);
        }
 
        return ret;
 
 static void __exit iptable_raw_fini(void)
 {
-       xt_hook_unlink(&packet_raw, rawtable_ops);
        unregister_pernet_subsys(&iptable_raw_net_ops);
+       kfree(rawtable_ops);
 }
 
 module_init(iptable_raw_init);
 
                                (1 << NF_INET_FORWARD) | \
                                (1 << NF_INET_LOCAL_OUT)
 
+static int __net_init iptable_security_table_init(struct net *net);
+
 static const struct xt_table security_table = {
        .name           = "security",
        .valid_hooks    = SECURITY_VALID_HOOKS,
        .me             = THIS_MODULE,
        .af             = NFPROTO_IPV4,
        .priority       = NF_IP_PRI_SECURITY,
+       .table_init     = iptable_security_table_init,
 };
 
 static unsigned int
 
 static struct nf_hook_ops *sectbl_ops __read_mostly;
 
-static int __net_init iptable_security_net_init(struct net *net)
+static int __net_init iptable_security_table_init(struct net *net)
 {
        struct ipt_replace *repl;
        int ret;
 
+       if (net->ipv4.iptable_security)
+               return 0;
+
        repl = ipt_alloc_initial_table(&security_table);
        if (repl == NULL)
                return -ENOMEM;
 
 static void __net_exit iptable_security_net_exit(struct net *net)
 {
+       if (!net->ipv4.iptable_security)
+               return;
+
        ipt_unregister_table(net, net->ipv4.iptable_security, sectbl_ops);
+       net->ipv4.iptable_security = NULL;
 }
 
 static struct pernet_operations iptable_security_net_ops = {
-       .init = iptable_security_net_init,
        .exit = iptable_security_net_exit,
 };
 
 {
        int ret;
 
+       sectbl_ops = xt_hook_ops_alloc(&security_table, iptable_security_hook);
+       if (IS_ERR(sectbl_ops))
+               return PTR_ERR(sectbl_ops);
+
        ret = register_pernet_subsys(&iptable_security_net_ops);
-       if (ret < 0)
+       if (ret < 0) {
+               kfree(sectbl_ops);
                return ret;
-
-       sectbl_ops = xt_hook_link(&security_table, iptable_security_hook);
-       if (IS_ERR(sectbl_ops)) {
-               ret = PTR_ERR(sectbl_ops);
-               goto cleanup_table;
        }
 
-       return ret;
+       ret = iptable_security_table_init(&init_net);
+       if (ret) {
+               unregister_pernet_subsys(&iptable_security_net_ops);
+               kfree(sectbl_ops);
+       }
 
-cleanup_table:
-       unregister_pernet_subsys(&iptable_security_net_ops);
        return ret;
 }
 
 static void __exit iptable_security_fini(void)
 {
-       xt_hook_unlink(&security_table, sectbl_ops);
        unregister_pernet_subsys(&iptable_security_net_ops);
+       kfree(sectbl_ops);
 }
 
 module_init(iptable_security_init);
 
        return ret;
 }
 
+static void __ip6t_unregister_table(struct net *net, struct xt_table *table)
+{
+       struct xt_table_info *private;
+       void *loc_cpu_entry;
+       struct module *table_owner = table->me;
+       struct ip6t_entry *iter;
+
+       private = xt_unregister_table(table);
+
+       /* Decrease module usage counts and free resources */
+       loc_cpu_entry = private->entries;
+       xt_entry_foreach(iter, loc_cpu_entry, private->size)
+               cleanup_entry(iter, net);
+       if (private->number > private->initial_entries)
+               module_put(table_owner);
+       xt_free_table_info(private);
+}
+
 int ip6t_register_table(struct net *net, const struct xt_table *table,
                        const struct ip6t_replace *repl,
                        const struct nf_hook_ops *ops,
                goto out_free;
        }
 
+       /* set res now, will see skbs right after nf_register_net_hooks */
        WRITE_ONCE(*res, new_table);
+
+       ret = nf_register_net_hooks(net, ops, hweight32(table->valid_hooks));
+       if (ret != 0) {
+               __ip6t_unregister_table(net, new_table);
+               *res = NULL;
+       }
+
        return ret;
 
 out_free:
 void ip6t_unregister_table(struct net *net, struct xt_table *table,
                           const struct nf_hook_ops *ops)
 {
-       struct xt_table_info *private;
-       void *loc_cpu_entry;
-       struct module *table_owner = table->me;
-       struct ip6t_entry *iter;
-
-       private = xt_unregister_table(table);
-
-       /* Decrease module usage counts and free resources */
-       loc_cpu_entry = private->entries;
-       xt_entry_foreach(iter, loc_cpu_entry, private->size)
-               cleanup_entry(iter, net);
-       if (private->number > private->initial_entries)
-               module_put(table_owner);
-       xt_free_table_info(private);
+       nf_unregister_net_hooks(net, ops, hweight32(table->valid_hooks));
+       __ip6t_unregister_table(net, table);
 }
 
 /* Returns 1 if the type and code is matched by the range, 0 otherwise */
 
                            (1 << NF_INET_FORWARD) | \
                            (1 << NF_INET_LOCAL_OUT))
 
+static int __net_init ip6table_filter_table_init(struct net *net);
+
 static const struct xt_table packet_filter = {
        .name           = "filter",
        .valid_hooks    = FILTER_VALID_HOOKS,
        .me             = THIS_MODULE,
        .af             = NFPROTO_IPV6,
        .priority       = NF_IP6_PRI_FILTER,
+       .table_init     = ip6table_filter_table_init,
 };
 
 /* The work comes in here from netfilter.c. */
 static bool forward = true;
 module_param(forward, bool, 0000);
 
-static int __net_init ip6table_filter_net_init(struct net *net)
+static int __net_init ip6table_filter_table_init(struct net *net)
 {
        struct ip6t_replace *repl;
        int err;
 
+       if (net->ipv6.ip6table_filter)
+               return 0;
+
        repl = ip6t_alloc_initial_table(&packet_filter);
        if (repl == NULL)
                return -ENOMEM;
        return err;
 }
 
+static int __net_init ip6table_filter_net_init(struct net *net)
+{
+       if (net == &init_net || !forward)
+               return ip6table_filter_table_init(net);
+
+       return 0;
+}
+
 static void __net_exit ip6table_filter_net_exit(struct net *net)
 {
+       if (!net->ipv6.ip6table_filter)
+               return;
        ip6t_unregister_table(net, net->ipv6.ip6table_filter, filter_ops);
+       net->ipv6.ip6table_filter = NULL;
 }
 
 static struct pernet_operations ip6table_filter_net_ops = {
 {
        int ret;
 
+       filter_ops = xt_hook_ops_alloc(&packet_filter, ip6table_filter_hook);
+       if (IS_ERR(filter_ops))
+               return PTR_ERR(filter_ops);
+
        ret = register_pernet_subsys(&ip6table_filter_net_ops);
        if (ret < 0)
-               return ret;
-
-       /* Register hooks */
-       filter_ops = xt_hook_link(&packet_filter, ip6table_filter_hook);
-       if (IS_ERR(filter_ops)) {
-               ret = PTR_ERR(filter_ops);
-               goto cleanup_table;
-       }
+               kfree(filter_ops);
 
        return ret;
-
- cleanup_table:
-       unregister_pernet_subsys(&ip6table_filter_net_ops);
-       return ret;
 }
 
 static void __exit ip6table_filter_fini(void)
 {
-       xt_hook_unlink(&packet_filter, filter_ops);
        unregister_pernet_subsys(&ip6table_filter_net_ops);
+       kfree(filter_ops);
 }
 
 module_init(ip6table_filter_init);
 
                            (1 << NF_INET_LOCAL_OUT) | \
                            (1 << NF_INET_POST_ROUTING))
 
+static int __net_init ip6table_mangle_table_init(struct net *net);
+
 static const struct xt_table packet_mangler = {
        .name           = "mangle",
        .valid_hooks    = MANGLE_VALID_HOOKS,
        .me             = THIS_MODULE,
        .af             = NFPROTO_IPV6,
        .priority       = NF_IP6_PRI_MANGLE,
+       .table_init     = ip6table_mangle_table_init,
 };
 
 static unsigned int
 }
 
 static struct nf_hook_ops *mangle_ops __read_mostly;
-static int __net_init ip6table_mangle_net_init(struct net *net)
+static int __net_init ip6table_mangle_table_init(struct net *net)
 {
        struct ip6t_replace *repl;
        int ret;
 
+       if (net->ipv6.ip6table_mangle)
+               return 0;
+
        repl = ip6t_alloc_initial_table(&packet_mangler);
        if (repl == NULL)
                return -ENOMEM;
 
 static void __net_exit ip6table_mangle_net_exit(struct net *net)
 {
+       if (!net->ipv6.ip6table_mangle)
+               return;
+
        ip6t_unregister_table(net, net->ipv6.ip6table_mangle, mangle_ops);
+       net->ipv6.ip6table_mangle = NULL;
 }
 
 static struct pernet_operations ip6table_mangle_net_ops = {
-       .init = ip6table_mangle_net_init,
        .exit = ip6table_mangle_net_exit,
 };
 
 {
        int ret;
 
+       mangle_ops = xt_hook_ops_alloc(&packet_mangler, ip6table_mangle_hook);
+       if (IS_ERR(mangle_ops))
+               return PTR_ERR(mangle_ops);
+
        ret = register_pernet_subsys(&ip6table_mangle_net_ops);
-       if (ret < 0)
+       if (ret < 0) {
+               kfree(mangle_ops);
                return ret;
-
-       /* Register hooks */
-       mangle_ops = xt_hook_link(&packet_mangler, ip6table_mangle_hook);
-       if (IS_ERR(mangle_ops)) {
-               ret = PTR_ERR(mangle_ops);
-               goto cleanup_table;
        }
 
-       return ret;
-
- cleanup_table:
-       unregister_pernet_subsys(&ip6table_mangle_net_ops);
+       ret = ip6table_mangle_table_init(&init_net);
+       if (ret) {
+               unregister_pernet_subsys(&ip6table_mangle_net_ops);
+               kfree(mangle_ops);
+       }
        return ret;
 }
 
 static void __exit ip6table_mangle_fini(void)
 {
-       xt_hook_unlink(&packet_mangler, mangle_ops);
        unregister_pernet_subsys(&ip6table_mangle_net_ops);
+       kfree(mangle_ops);
 }
 
 module_init(ip6table_mangle_init);
 
 #include <net/netfilter/nf_nat_core.h>
 #include <net/netfilter/nf_nat_l3proto.h>
 
+static int __net_init ip6table_nat_table_init(struct net *net);
+
 static const struct xt_table nf_nat_ipv6_table = {
        .name           = "nat",
        .valid_hooks    = (1 << NF_INET_PRE_ROUTING) |
                          (1 << NF_INET_LOCAL_IN),
        .me             = THIS_MODULE,
        .af             = NFPROTO_IPV6,
+       .table_init     = ip6table_nat_table_init,
 };
 
 static unsigned int ip6table_nat_do_chain(void *priv,
        },
 };
 
-static int __net_init ip6table_nat_net_init(struct net *net)
+static int __net_init ip6table_nat_table_init(struct net *net)
 {
        struct ip6t_replace *repl;
        int ret;
 
+       if (net->ipv6.ip6table_nat)
+               return 0;
+
        repl = ip6t_alloc_initial_table(&nf_nat_ipv6_table);
        if (repl == NULL)
                return -ENOMEM;
 
 static void __net_exit ip6table_nat_net_exit(struct net *net)
 {
+       if (!net->ipv6.ip6table_nat)
+               return;
        ip6t_unregister_table(net, net->ipv6.ip6table_nat, nf_nat_ipv6_ops);
+       net->ipv6.ip6table_nat = NULL;
 }
 
 static struct pernet_operations ip6table_nat_net_ops = {
-       .init   = ip6table_nat_net_init,
        .exit   = ip6table_nat_net_exit,
 };
 
 static int __init ip6table_nat_init(void)
 {
-       int err;
-
-       err = register_pernet_subsys(&ip6table_nat_net_ops);
-       if (err < 0)
-               goto err1;
+       int ret = register_pernet_subsys(&ip6table_nat_net_ops);
 
-       err = nf_register_hooks(nf_nat_ipv6_ops, ARRAY_SIZE(nf_nat_ipv6_ops));
-       if (err < 0)
-               goto err2;
-       return 0;
+       if (ret)
+               return ret;
 
-err2:
-       unregister_pernet_subsys(&ip6table_nat_net_ops);
-err1:
-       return err;
+       ret = ip6table_nat_table_init(&init_net);
+       if (ret)
+               unregister_pernet_subsys(&ip6table_nat_net_ops);
+       return ret;
 }
 
 static void __exit ip6table_nat_exit(void)
 {
-       nf_unregister_hooks(nf_nat_ipv6_ops, ARRAY_SIZE(nf_nat_ipv6_ops));
        unregister_pernet_subsys(&ip6table_nat_net_ops);
 }
 
 
 
 #define RAW_VALID_HOOKS ((1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_LOCAL_OUT))
 
+static int __net_init ip6table_raw_table_init(struct net *net);
+
 static const struct xt_table packet_raw = {
        .name = "raw",
        .valid_hooks = RAW_VALID_HOOKS,
        .me = THIS_MODULE,
        .af = NFPROTO_IPV6,
        .priority = NF_IP6_PRI_RAW,
+       .table_init = ip6table_raw_table_init,
 };
 
 /* The work comes in here from netfilter.c. */
 
 static struct nf_hook_ops *rawtable_ops __read_mostly;
 
-static int __net_init ip6table_raw_net_init(struct net *net)
+static int __net_init ip6table_raw_table_init(struct net *net)
 {
        struct ip6t_replace *repl;
        int ret;
 
+       if (net->ipv6.ip6table_raw)
+               return 0;
+
        repl = ip6t_alloc_initial_table(&packet_raw);
        if (repl == NULL)
                return -ENOMEM;
 
 static void __net_exit ip6table_raw_net_exit(struct net *net)
 {
+       if (!net->ipv6.ip6table_raw)
+               return;
        ip6t_unregister_table(net, net->ipv6.ip6table_raw, rawtable_ops);
+       net->ipv6.ip6table_raw = NULL;
 }
 
 static struct pernet_operations ip6table_raw_net_ops = {
-       .init = ip6table_raw_net_init,
        .exit = ip6table_raw_net_exit,
 };
 
 {
        int ret;
 
+       /* Register hooks */
+       rawtable_ops = xt_hook_ops_alloc(&packet_raw, ip6table_raw_hook);
+       if (IS_ERR(rawtable_ops))
+               return PTR_ERR(rawtable_ops);
+
        ret = register_pernet_subsys(&ip6table_raw_net_ops);
-       if (ret < 0)
+       if (ret < 0) {
+               kfree(rawtable_ops);
                return ret;
-
-       /* Register hooks */
-       rawtable_ops = xt_hook_link(&packet_raw, ip6table_raw_hook);
-       if (IS_ERR(rawtable_ops)) {
-               ret = PTR_ERR(rawtable_ops);
-               goto cleanup_table;
        }
 
-       return ret;
-
- cleanup_table:
-       unregister_pernet_subsys(&ip6table_raw_net_ops);
+       ret = ip6table_raw_table_init(&init_net);
+       if (ret) {
+               unregister_pernet_subsys(&ip6table_raw_net_ops);
+               kfree(rawtable_ops);
+       }
        return ret;
 }
 
 static void __exit ip6table_raw_fini(void)
 {
-       xt_hook_unlink(&packet_raw, rawtable_ops);
        unregister_pernet_subsys(&ip6table_raw_net_ops);
+       kfree(rawtable_ops);
 }
 
 module_init(ip6table_raw_init);
 
                                (1 << NF_INET_FORWARD) | \
                                (1 << NF_INET_LOCAL_OUT)
 
+static int __net_init ip6table_security_table_init(struct net *net);
+
 static const struct xt_table security_table = {
        .name           = "security",
        .valid_hooks    = SECURITY_VALID_HOOKS,
        .me             = THIS_MODULE,
        .af             = NFPROTO_IPV6,
        .priority       = NF_IP6_PRI_SECURITY,
+       .table_init     = ip6table_security_table_init,
 };
 
 static unsigned int
 
 static struct nf_hook_ops *sectbl_ops __read_mostly;
 
-static int __net_init ip6table_security_net_init(struct net *net)
+static int __net_init ip6table_security_table_init(struct net *net)
 {
        struct ip6t_replace *repl;
        int ret;
 
+       if (net->ipv6.ip6table_security)
+               return 0;
+
        repl = ip6t_alloc_initial_table(&security_table);
        if (repl == NULL)
                return -ENOMEM;
 
 static void __net_exit ip6table_security_net_exit(struct net *net)
 {
+       if (!net->ipv6.ip6table_security)
+               return;
        ip6t_unregister_table(net, net->ipv6.ip6table_security, sectbl_ops);
+       net->ipv6.ip6table_security = NULL;
 }
 
 static struct pernet_operations ip6table_security_net_ops = {
-       .init = ip6table_security_net_init,
        .exit = ip6table_security_net_exit,
 };
 
 {
        int ret;
 
+       sectbl_ops = xt_hook_ops_alloc(&security_table, ip6table_security_hook);
+       if (IS_ERR(sectbl_ops))
+               return PTR_ERR(sectbl_ops);
+
        ret = register_pernet_subsys(&ip6table_security_net_ops);
-       if (ret < 0)
+       if (ret < 0) {
+               kfree(sectbl_ops);
                return ret;
-
-       sectbl_ops = xt_hook_link(&security_table, ip6table_security_hook);
-       if (IS_ERR(sectbl_ops)) {
-               ret = PTR_ERR(sectbl_ops);
-               goto cleanup_table;
        }
 
-       return ret;
-
-cleanup_table:
-       unregister_pernet_subsys(&ip6table_security_net_ops);
+       ret = ip6table_security_table_init(&init_net);
+       if (ret) {
+               unregister_pernet_subsys(&ip6table_security_net_ops);
+               kfree(sectbl_ops);
+       }
        return ret;
 }
 
 static void __exit ip6table_security_fini(void)
 {
-       xt_hook_unlink(&security_table, sectbl_ops);
        unregister_pernet_subsys(&ip6table_security_net_ops);
+       kfree(sectbl_ops);
 }
 
 module_init(ip6table_security_init);
 
 struct xt_table *xt_find_table_lock(struct net *net, u_int8_t af,
                                    const char *name)
 {
-       struct xt_table *t;
+       struct xt_table *t, *found = NULL;
 
        mutex_lock(&xt[af].mutex);
        list_for_each_entry(t, &net->xt.tables[af], list)
                if (strcmp(t->name, name) == 0 && try_module_get(t->me))
                        return t;
+
+       if (net == &init_net)
+               goto out;
+
+       /* Table doesn't exist in this netns, re-try init */
+       list_for_each_entry(t, &init_net.xt.tables[af], list) {
+               if (strcmp(t->name, name))
+                       continue;
+               if (!try_module_get(t->me))
+                       return NULL;
+
+               mutex_unlock(&xt[af].mutex);
+               if (t->table_init(net) != 0) {
+                       module_put(t->me);
+                       return NULL;
+               }
+
+               found = t;
+
+               mutex_lock(&xt[af].mutex);
+               break;
+       }
+
+       if (!found)
+               goto out;
+
+       /* and once again: */
+       list_for_each_entry(t, &net->xt.tables[af], list)
+               if (strcmp(t->name, name) == 0)
+                       return t;
+
+       module_put(found->me);
+ out:
        mutex_unlock(&xt[af].mutex);
        return NULL;
 }
 #endif /* CONFIG_PROC_FS */
 
 /**
- * xt_hook_link - set up hooks for a new table
+ * xt_hook_ops_alloc - set up hooks for a new table
  * @table:     table with metadata needed to set up hooks
  * @fn:                Hook function
  *
- * This function will take care of creating and registering the necessary
- * Netfilter hooks for XT tables.
+ * This function will create the nf_hook_ops that the x_table needs
+ * to hand to xt_hook_link_net().
  */
-struct nf_hook_ops *xt_hook_link(const struct xt_table *table, nf_hookfn *fn)
+struct nf_hook_ops *
+xt_hook_ops_alloc(const struct xt_table *table, nf_hookfn *fn)
 {
        unsigned int hook_mask = table->valid_hooks;
        uint8_t i, num_hooks = hweight32(hook_mask);
        uint8_t hooknum;
        struct nf_hook_ops *ops;
-       int ret;
 
        ops = kmalloc(sizeof(*ops) * num_hooks, GFP_KERNEL);
        if (ops == NULL)
                ++i;
        }
 
-       ret = nf_register_hooks(ops, num_hooks);
-       if (ret < 0) {
-               kfree(ops);
-               return ERR_PTR(ret);
-       }
-
        return ops;
 }
-EXPORT_SYMBOL_GPL(xt_hook_link);
-
-/**
- * xt_hook_unlink - remove hooks for a table
- * @ops:       nf_hook_ops array as returned by nf_hook_link
- * @hook_mask: the very same mask that was passed to nf_hook_link
- */
-void xt_hook_unlink(const struct xt_table *table, struct nf_hook_ops *ops)
-{
-       nf_unregister_hooks(ops, hweight32(table->valid_hooks));
-       kfree(ops);
-}
-EXPORT_SYMBOL_GPL(xt_hook_unlink);
+EXPORT_SYMBOL_GPL(xt_hook_ops_alloc);
 
 int xt_proto_init(struct net *net, u_int8_t af)
 {