]> err.no Git - linux-2.6/blobdiff - net/netfilter/nf_conntrack_proto.c
[NETFILTER]: nf_conntrack: EXPORT_SYMBOL cleanup
[linux-2.6] / net / netfilter / nf_conntrack_proto.c
index 330b9acc62d8be5402c00f2728fc69e68d3ffdaf..1a61b72712cd53bbc36a238454959227bbaefb45 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/types.h>
 #include <linux/netfilter.h>
 #include <linux/module.h>
+#include <linux/mutex.h>
 #include <linux/skbuff.h>
 #include <linux/vmalloc.h>
 #include <linux/stddef.h>
 #include <net/netfilter/nf_conntrack_core.h>
 
 struct nf_conntrack_l4proto **nf_ct_protos[PF_MAX] __read_mostly;
-struct nf_conntrack_l3proto *nf_ct_l3protos[PF_MAX] __read_mostly;
+struct nf_conntrack_l3proto *nf_ct_l3protos[AF_MAX] __read_mostly;
+EXPORT_SYMBOL_GPL(nf_ct_l3protos);
+
+#ifdef CONFIG_SYSCTL
+static DEFINE_MUTEX(nf_ct_proto_sysctl_mutex);
+
+static int
+nf_ct_register_sysctl(struct ctl_table_header **header, struct ctl_table *path,
+                     struct ctl_table *table, unsigned int *users)
+{
+       if (*header == NULL) {
+               *header = nf_register_sysctl_table(path, table);
+               if (*header == NULL)
+                       return -ENOMEM;
+       }
+       if (users != NULL)
+               (*users)++;
+       return 0;
+}
+
+static void
+nf_ct_unregister_sysctl(struct ctl_table_header **header,
+                       struct ctl_table *table, unsigned int *users)
+{
+       if (users != NULL && --*users > 0)
+               return;
+       nf_unregister_sysctl_table(*header, table);
+       *header = NULL;
+}
+#endif
 
 struct nf_conntrack_l4proto *
 __nf_ct_l4proto_find(u_int16_t l3proto, u_int8_t l4proto)
@@ -38,6 +68,7 @@ __nf_ct_l4proto_find(u_int16_t l3proto, u_int8_t l4proto)
 
        return nf_ct_protos[l3proto][l4proto];
 }
+EXPORT_SYMBOL_GPL(__nf_ct_l4proto_find);
 
 /* this is guaranteed to always return a valid protocol helper, since
  * it falls back to generic_protocol */
@@ -54,11 +85,13 @@ nf_ct_l4proto_find_get(u_int16_t l3proto, u_int8_t l4proto)
 
        return p;
 }
+EXPORT_SYMBOL_GPL(nf_ct_l4proto_find_get);
 
 void nf_ct_l4proto_put(struct nf_conntrack_l4proto *p)
 {
        module_put(p->me);
 }
+EXPORT_SYMBOL_GPL(nf_ct_l4proto_put);
 
 struct nf_conntrack_l3proto *
 nf_ct_l3proto_find_get(u_int16_t l3proto)
@@ -73,11 +106,13 @@ nf_ct_l3proto_find_get(u_int16_t l3proto)
 
        return p;
 }
+EXPORT_SYMBOL_GPL(nf_ct_l3proto_find_get);
 
 void nf_ct_l3proto_put(struct nf_conntrack_l3proto *p)
 {
        module_put(p->me);
 }
+EXPORT_SYMBOL_GPL(nf_ct_l3proto_put);
 
 int
 nf_ct_l3proto_try_module_get(unsigned short l3proto)
@@ -96,6 +131,7 @@ retry:       p = nf_ct_l3proto_find_get(l3proto);
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(nf_ct_l3proto_try_module_get);
 
 void nf_ct_l3proto_module_put(unsigned short l3proto)
 {
@@ -107,6 +143,7 @@ void nf_ct_l3proto_module_put(unsigned short l3proto)
 
        module_put(p->me);
 }
+EXPORT_SYMBOL_GPL(nf_ct_l3proto_module_put);
 
 static int kill_l3proto(struct nf_conn *i, void *data)
 {
@@ -124,33 +161,142 @@ static int kill_l4proto(struct nf_conn *i, void *data)
                        l4proto->l3proto);
 }
 
+static int nf_ct_l3proto_register_sysctl(struct nf_conntrack_l3proto *l3proto)
+{
+       int err = 0;
+
+#ifdef CONFIG_SYSCTL
+       mutex_lock(&nf_ct_proto_sysctl_mutex);
+       if (l3proto->ctl_table != NULL) {
+               err = nf_ct_register_sysctl(&l3proto->ctl_table_header,
+                                           l3proto->ctl_table_path,
+                                           l3proto->ctl_table, NULL);
+       }
+       mutex_unlock(&nf_ct_proto_sysctl_mutex);
+#endif
+       return err;
+}
+
+static void nf_ct_l3proto_unregister_sysctl(struct nf_conntrack_l3proto *l3proto)
+{
+#ifdef CONFIG_SYSCTL
+       mutex_lock(&nf_ct_proto_sysctl_mutex);
+       if (l3proto->ctl_table_header != NULL)
+               nf_ct_unregister_sysctl(&l3proto->ctl_table_header,
+                                       l3proto->ctl_table, NULL);
+       mutex_unlock(&nf_ct_proto_sysctl_mutex);
+#endif
+}
+
 int nf_conntrack_l3proto_register(struct nf_conntrack_l3proto *proto)
 {
        int ret = 0;
 
+       if (proto->l3proto >= AF_MAX) {
+               ret = -EBUSY;
+               goto out;
+       }
+
        write_lock_bh(&nf_conntrack_lock);
        if (nf_ct_l3protos[proto->l3proto] != &nf_conntrack_l3proto_generic) {
                ret = -EBUSY;
-               goto out;
+               goto out_unlock;
        }
        nf_ct_l3protos[proto->l3proto] = proto;
-out:
        write_unlock_bh(&nf_conntrack_lock);
 
+       ret = nf_ct_l3proto_register_sysctl(proto);
+       if (ret < 0)
+               nf_conntrack_l3proto_unregister(proto);
+       return ret;
+
+out_unlock:
+       write_unlock_bh(&nf_conntrack_lock);
+out:
        return ret;
 }
+EXPORT_SYMBOL_GPL(nf_conntrack_l3proto_register);
 
-void nf_conntrack_l3proto_unregister(struct nf_conntrack_l3proto *proto)
+int nf_conntrack_l3proto_unregister(struct nf_conntrack_l3proto *proto)
 {
+       int ret = 0;
+
+       if (proto->l3proto >= AF_MAX) {
+               ret = -EBUSY;
+               goto out;
+       }
+
        write_lock_bh(&nf_conntrack_lock);
+       if (nf_ct_l3protos[proto->l3proto] != proto) {
+               write_unlock_bh(&nf_conntrack_lock);
+               ret = -EBUSY;
+               goto out;
+       }
+
        nf_ct_l3protos[proto->l3proto] = &nf_conntrack_l3proto_generic;
        write_unlock_bh(&nf_conntrack_lock);
 
+       nf_ct_l3proto_unregister_sysctl(proto);
+
        /* Somebody could be still looking at the proto in bh. */
        synchronize_net();
 
        /* Remove all contrack entries for this protocol */
        nf_ct_iterate_cleanup(kill_l3proto, proto);
+
+out:
+       return ret;
+}
+EXPORT_SYMBOL_GPL(nf_conntrack_l3proto_unregister);
+
+static int nf_ct_l4proto_register_sysctl(struct nf_conntrack_l4proto *l4proto)
+{
+       int err = 0;
+
+#ifdef CONFIG_SYSCTL
+       mutex_lock(&nf_ct_proto_sysctl_mutex);
+       if (l4proto->ctl_table != NULL) {
+               err = nf_ct_register_sysctl(l4proto->ctl_table_header,
+                                           nf_net_netfilter_sysctl_path,
+                                           l4proto->ctl_table,
+                                           l4proto->ctl_table_users);
+               if (err < 0)
+                       goto out;
+       }
+#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT
+       if (l4proto->ctl_compat_table != NULL) {
+               err = nf_ct_register_sysctl(&l4proto->ctl_compat_table_header,
+                                           nf_net_ipv4_netfilter_sysctl_path,
+                                           l4proto->ctl_compat_table, NULL);
+               if (err == 0)
+                       goto out;
+               nf_ct_unregister_sysctl(l4proto->ctl_table_header,
+                                       l4proto->ctl_table,
+                                       l4proto->ctl_table_users);
+       }
+#endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */
+out:
+       mutex_unlock(&nf_ct_proto_sysctl_mutex);
+#endif /* CONFIG_SYSCTL */
+       return err;
+}
+
+static void nf_ct_l4proto_unregister_sysctl(struct nf_conntrack_l4proto *l4proto)
+{
+#ifdef CONFIG_SYSCTL
+       mutex_lock(&nf_ct_proto_sysctl_mutex);
+       if (l4proto->ctl_table_header != NULL &&
+           *l4proto->ctl_table_header != NULL)
+               nf_ct_unregister_sysctl(l4proto->ctl_table_header,
+                                       l4proto->ctl_table,
+                                       l4proto->ctl_table_users);
+#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT
+       if (l4proto->ctl_compat_table_header != NULL)
+               nf_ct_unregister_sysctl(&l4proto->ctl_compat_table_header,
+                                       l4proto->ctl_compat_table, NULL);
+#endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */
+       mutex_unlock(&nf_ct_proto_sysctl_mutex);
+#endif /* CONFIG_SYSCTL */
 }
 
 /* FIXME: Allow NULL functions and sub in pointers to generic for
@@ -159,6 +305,14 @@ int nf_conntrack_l4proto_register(struct nf_conntrack_l4proto *l4proto)
 {
        int ret = 0;
 
+       if (l4proto->l3proto >= PF_MAX) {
+               ret = -EBUSY;
+               goto out;
+       }
+
+       if (l4proto == &nf_conntrack_l4proto_generic)
+               return nf_ct_l4proto_register_sysctl(l4proto);
+
 retry:
        write_lock_bh(&nf_conntrack_lock);
        if (nf_ct_protos[l4proto->l3proto]) {
@@ -203,23 +357,54 @@ retry:
        }
 
        nf_ct_protos[l4proto->l3proto][l4proto->l4proto] = l4proto;
+       write_unlock_bh(&nf_conntrack_lock);
+
+       ret = nf_ct_l4proto_register_sysctl(l4proto);
+       if (ret < 0)
+               nf_conntrack_l4proto_unregister(l4proto);
+       return ret;
 
 out_unlock:
        write_unlock_bh(&nf_conntrack_lock);
 out:
        return ret;
 }
+EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_register);
 
-void nf_conntrack_l4proto_unregister(struct nf_conntrack_l4proto *l4proto)
+int nf_conntrack_l4proto_unregister(struct nf_conntrack_l4proto *l4proto)
 {
+       int ret = 0;
+
+       if (l4proto->l3proto >= PF_MAX) {
+               ret = -EBUSY;
+               goto out;
+       }
+
+       if (l4proto == &nf_conntrack_l4proto_generic) {
+               nf_ct_l4proto_unregister_sysctl(l4proto);
+               goto out;
+       }
+
        write_lock_bh(&nf_conntrack_lock);
+       if (nf_ct_protos[l4proto->l3proto][l4proto->l4proto]
+           != l4proto) {
+               write_unlock_bh(&nf_conntrack_lock);
+               ret = -EBUSY;
+               goto out;
+       }
        nf_ct_protos[l4proto->l3proto][l4proto->l4proto]
                = &nf_conntrack_l4proto_generic;
        write_unlock_bh(&nf_conntrack_lock);
 
+       nf_ct_l4proto_unregister_sysctl(l4proto);
+
        /* Somebody could be still looking at the proto in bh. */
        synchronize_net();
 
        /* Remove all contrack entries for this protocol */
        nf_ct_iterate_cleanup(kill_l4proto, l4proto);
+
+out:
+       return ret;
 }
+EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_unregister);