netfilter: bridge: namespace bridge netfilter sysctls
[linux-2.6-microblaze.git] / net / bridge / br_netfilter_hooks.c
index 3c67754..995a498 100644 (file)
 
 static unsigned int brnf_net_id __read_mostly;
 
-#ifdef CONFIG_SYSCTL
-static struct ctl_table_header *brnf_sysctl_header;
-#endif
-
 struct brnf_net {
        bool enabled;
 
+#ifdef CONFIG_SYSCTL
+       struct ctl_table_header *ctl_hdr;
+#endif
+
        /* default value is 1 */
        int call_iptables;
        int call_ip6tables;
@@ -980,23 +980,6 @@ static int brnf_device_event(struct notifier_block *unused, unsigned long event,
        return NOTIFY_OK;
 }
 
-static void __net_exit brnf_exit_net(struct net *net)
-{
-       struct brnf_net *brnet = net_generic(net, brnf_net_id);
-
-       if (!brnet->enabled)
-               return;
-
-       nf_unregister_net_hooks(net, br_nf_ops, ARRAY_SIZE(br_nf_ops));
-       brnet->enabled = false;
-}
-
-static struct pernet_operations brnf_net_ops __read_mostly = {
-       .exit = brnf_exit_net,
-       .id   = &brnf_net_id,
-       .size = sizeof(struct brnf_net),
-};
-
 static struct notifier_block brnf_notifier __read_mostly = {
        .notifier_call = brnf_device_event,
 };
@@ -1102,12 +1085,79 @@ static inline void br_netfilter_sysctl_default(struct brnf_net *brnf)
        brnf->pass_vlan_indev = 0;
 }
 
+static int br_netfilter_sysctl_init_net(struct net *net)
+{
+       struct ctl_table *table = brnf_table;
+       struct brnf_net *brnet;
+
+       if (!net_eq(net, &init_net)) {
+               table = kmemdup(table, sizeof(brnf_table), GFP_KERNEL);
+               if (!table)
+                       return -ENOMEM;
+       }
+
+       brnet = net_generic(net, brnf_net_id);
+       table[0].data = &brnet->call_arptables;
+       table[1].data = &brnet->call_iptables;
+       table[2].data = &brnet->call_ip6tables;
+       table[3].data = &brnet->filter_vlan_tagged;
+       table[4].data = &brnet->filter_pppoe_tagged;
+       table[5].data = &brnet->pass_vlan_indev;
+
+       br_netfilter_sysctl_default(brnet);
+
+       brnet->ctl_hdr = register_net_sysctl(net, "net/bridge", table);
+       if (!brnet->ctl_hdr) {
+               if (!net_eq(net, &init_net))
+                       kfree(table);
+
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+static void br_netfilter_sysctl_exit_net(struct net *net,
+                                        struct brnf_net *brnet)
+{
+       unregister_net_sysctl_table(brnet->ctl_hdr);
+       if (!net_eq(net, &init_net))
+               kfree(brnet->ctl_hdr->ctl_table_arg);
+}
+
+static int __net_init brnf_init_net(struct net *net)
+{
+       return br_netfilter_sysctl_init_net(net);
+}
+#endif
+
+static void __net_exit brnf_exit_net(struct net *net)
+{
+       struct brnf_net *brnet;
+
+       brnet = net_generic(net, brnf_net_id);
+       if (brnet->enabled) {
+               nf_unregister_net_hooks(net, br_nf_ops, ARRAY_SIZE(br_nf_ops));
+               brnet->enabled = false;
+       }
+
+#ifdef CONFIG_SYSCTL
+       br_netfilter_sysctl_exit_net(net, brnet);
 #endif
+}
+
+static struct pernet_operations brnf_net_ops __read_mostly = {
+#ifdef CONFIG_SYSCTL
+       .init = brnf_init_net,
+#endif
+       .exit = brnf_exit_net,
+       .id   = &brnf_net_id,
+       .size = sizeof(struct brnf_net),
+};
 
 static int __init br_netfilter_init(void)
 {
        int ret;
-       struct brnf_net *brnet;
 
        ret = register_pernet_subsys(&brnf_net_ops);
        if (ret < 0)
@@ -1119,26 +1169,6 @@ static int __init br_netfilter_init(void)
                return ret;
        }
 
-#ifdef CONFIG_SYSCTL
-       brnet = net_generic(&init_net, brnf_net_id);
-       brnf_table[0].data = &brnet->call_arptables;
-       brnf_table[1].data = &brnet->call_iptables;
-       brnf_table[2].data = &brnet->call_ip6tables;
-       brnf_table[3].data = &brnet->filter_vlan_tagged;
-       brnf_table[4].data = &brnet->filter_pppoe_tagged;
-       brnf_table[5].data = &brnet->pass_vlan_indev;
-
-       br_netfilter_sysctl_default(brnet);
-
-       brnf_sysctl_header = register_net_sysctl(&init_net, "net/bridge", brnf_table);
-       if (brnf_sysctl_header == NULL) {
-               printk(KERN_WARNING
-                      "br_netfilter: can't register to sysctl.\n");
-               unregister_netdevice_notifier(&brnf_notifier);
-               unregister_pernet_subsys(&brnf_net_ops);
-               return -ENOMEM;
-       }
-#endif
        RCU_INIT_POINTER(nf_br_ops, &br_ops);
        printk(KERN_NOTICE "Bridge firewalling registered\n");
        return 0;
@@ -1149,9 +1179,6 @@ static void __exit br_netfilter_fini(void)
        RCU_INIT_POINTER(nf_br_ops, NULL);
        unregister_netdevice_notifier(&brnf_notifier);
        unregister_pernet_subsys(&brnf_net_ops);
-#ifdef CONFIG_SYSCTL
-       unregister_net_sysctl_table(brnf_sysctl_header);
-#endif
 }
 
 module_init(br_netfilter_init);