[NETFILTER]: x_tables: return new table from {arp,ip,ip6}t_register_table()
[pandora-kernel.git] / net / netfilter / x_tables.c
index cc2baa6..d7fbb1b 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/vmalloc.h>
 #include <linux/mutex.h>
 #include <linux/mm.h>
+#include <net/net_namespace.h>
 
 #include <linux/netfilter/x_tables.h>
 #include <linux/netfilter_arp.h>
@@ -33,12 +34,20 @@ MODULE_DESCRIPTION("[ip,ip6,arp]_tables backend module");
 
 #define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1))
 
+struct compat_delta {
+       struct compat_delta *next;
+       unsigned int offset;
+       short delta;
+};
+
 struct xt_af {
        struct mutex mutex;
        struct list_head match;
        struct list_head target;
-       struct list_head tables;
+#ifdef CONFIG_COMPAT
        struct mutex compat_mutex;
+       struct compat_delta *compat_offsets;
+#endif
 };
 
 static struct xt_af *xt;
@@ -334,6 +343,54 @@ int xt_check_match(const struct xt_match *match, unsigned short family,
 EXPORT_SYMBOL_GPL(xt_check_match);
 
 #ifdef CONFIG_COMPAT
+int xt_compat_add_offset(int af, unsigned int offset, short delta)
+{
+       struct compat_delta *tmp;
+
+       tmp = kmalloc(sizeof(struct compat_delta), GFP_KERNEL);
+       if (!tmp)
+               return -ENOMEM;
+
+       tmp->offset = offset;
+       tmp->delta = delta;
+
+       if (xt[af].compat_offsets) {
+               tmp->next = xt[af].compat_offsets->next;
+               xt[af].compat_offsets->next = tmp;
+       } else {
+               xt[af].compat_offsets = tmp;
+               tmp->next = NULL;
+       }
+       return 0;
+}
+EXPORT_SYMBOL_GPL(xt_compat_add_offset);
+
+void xt_compat_flush_offsets(int af)
+{
+       struct compat_delta *tmp, *next;
+
+       if (xt[af].compat_offsets) {
+               for (tmp = xt[af].compat_offsets; tmp; tmp = next) {
+                       next = tmp->next;
+                       kfree(tmp);
+               }
+               xt[af].compat_offsets = NULL;
+       }
+}
+EXPORT_SYMBOL_GPL(xt_compat_flush_offsets);
+
+short xt_compat_calc_jump(int af, unsigned int offset)
+{
+       struct compat_delta *tmp;
+       short delta;
+
+       for (tmp = xt[af].compat_offsets, delta = 0; tmp; tmp = tmp->next)
+               if (tmp->offset < offset)
+                       delta += tmp->delta;
+       return delta;
+}
+EXPORT_SYMBOL_GPL(xt_compat_calc_jump);
+
 int xt_compat_match_offset(struct xt_match *match)
 {
        u_int16_t csize = match->compatsize ? : match->matchsize;
@@ -341,8 +398,8 @@ int xt_compat_match_offset(struct xt_match *match)
 }
 EXPORT_SYMBOL_GPL(xt_compat_match_offset);
 
-void xt_compat_match_from_user(struct xt_entry_match *m, void **dstptr,
-                              int *size)
+int xt_compat_match_from_user(struct xt_entry_match *m, void **dstptr,
+                             int *size)
 {
        struct xt_match *match = m->u.kernel.match;
        struct compat_xt_entry_match *cm = (struct compat_xt_entry_match *)m;
@@ -364,6 +421,7 @@ void xt_compat_match_from_user(struct xt_entry_match *m, void **dstptr,
 
        *size += off;
        *dstptr += msize;
+       return 0;
 }
 EXPORT_SYMBOL_GPL(xt_compat_match_from_user);
 
@@ -376,7 +434,9 @@ int xt_compat_match_to_user(struct xt_entry_match *m, void __user **dstptr,
        u_int16_t msize = m->u.user.match_size - off;
 
        if (copy_to_user(cm, m, sizeof(*cm)) ||
-           put_user(msize, &cm->u.user.match_size))
+           put_user(msize, &cm->u.user.match_size) ||
+           copy_to_user(cm->u.user.name, m->u.kernel.match->name,
+                        strlen(m->u.kernel.match->name) + 1))
                return -EFAULT;
 
        if (match->compat_to_user) {
@@ -467,7 +527,9 @@ int xt_compat_target_to_user(struct xt_entry_target *t, void __user **dstptr,
        u_int16_t tsize = t->u.user.target_size - off;
 
        if (copy_to_user(ct, t, sizeof(*ct)) ||
-           put_user(tsize, &ct->u.user.target_size))
+           put_user(tsize, &ct->u.user.target_size) ||
+           copy_to_user(ct->u.user.name, t->u.kernel.target->name,
+                        strlen(t->u.kernel.target->name) + 1))
                return -EFAULT;
 
        if (target->compat_to_user) {
@@ -494,7 +556,7 @@ struct xt_table_info *xt_alloc_table_info(unsigned int size)
        if ((SMP_ALIGN(size) >> PAGE_SHIFT) + 2 > num_physpages)
                return NULL;
 
-       newinfo = kzalloc(sizeof(struct xt_table_info), GFP_KERNEL);
+       newinfo = kzalloc(XT_TABLE_INFO_SZ, GFP_KERNEL);
        if (!newinfo)
                return NULL;
 
@@ -534,14 +596,14 @@ void xt_free_table_info(struct xt_table_info *info)
 EXPORT_SYMBOL(xt_free_table_info);
 
 /* Find table by name, grabs mutex & ref.  Returns ERR_PTR() on error. */
-struct xt_table *xt_find_table_lock(int af, const char *name)
+struct xt_table *xt_find_table_lock(struct net *net, int af, const char *name)
 {
        struct xt_table *t;
 
        if (mutex_lock_interruptible(&xt[af].mutex) != 0)
                return ERR_PTR(-EINTR);
 
-       list_for_each_entry(t, &xt[af].tables, list)
+       list_for_each_entry(t, &net->xt.tables[af], list)
                if (strcmp(t->name, name) == 0 && try_module_get(t->me))
                        return t;
        mutex_unlock(&xt[af].mutex);
@@ -597,20 +659,27 @@ xt_replace_table(struct xt_table *table,
 }
 EXPORT_SYMBOL_GPL(xt_replace_table);
 
-int xt_register_table(struct xt_table *table,
-                     struct xt_table_info *bootstrap,
-                     struct xt_table_info *newinfo)
+struct xt_table *xt_register_table(struct net *net, struct xt_table *table,
+                                  struct xt_table_info *bootstrap,
+                                  struct xt_table_info *newinfo)
 {
        int ret;
        struct xt_table_info *private;
        struct xt_table *t;
 
+       /* Don't add one object to multiple lists. */
+       table = kmemdup(table, sizeof(struct xt_table), GFP_KERNEL);
+       if (!table) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
        ret = mutex_lock_interruptible(&xt[table->af].mutex);
        if (ret != 0)
-               return ret;
+               goto out_free;
 
        /* Don't autoload: we'd eat our tail... */
-       list_for_each_entry(t, &xt[table->af].tables, list) {
+       list_for_each_entry(t, &net->xt.tables[table->af], list) {
                if (strcmp(t->name, table->name) == 0) {
                        ret = -EEXIST;
                        goto unlock;
@@ -629,12 +698,16 @@ int xt_register_table(struct xt_table *table,
        /* save number of initial entries */
        private->initial_entries = private->number;
 
-       list_add(&table->list, &xt[table->af].tables);
+       list_add(&table->list, &net->xt.tables[table->af]);
+       mutex_unlock(&xt[table->af].mutex);
+       return table;
 
-       ret = 0;
  unlock:
        mutex_unlock(&xt[table->af].mutex);
-       return ret;
+out_free:
+       kfree(table);
+out:
+       return ERR_PTR(ret);
 }
 EXPORT_SYMBOL_GPL(xt_register_table);
 
@@ -646,6 +719,7 @@ void *xt_unregister_table(struct xt_table *table)
        private = table->private;
        list_del(&table->list);
        mutex_unlock(&xt[table->af].mutex);
+       kfree(table);
 
        return private;
 }
@@ -679,7 +753,7 @@ static struct list_head *type2list(u_int16_t af, u_int16_t type)
                list = &xt[af].match;
                break;
        case TABLE:
-               list = &xt[af].tables;
+               list = &init_net.xt.tables[af];
                break;
        default:
                list = NULL;
@@ -795,7 +869,7 @@ int xt_proto_init(int af)
 #ifdef CONFIG_PROC_FS
        strlcpy(buf, xt_prefix[af], sizeof(buf));
        strlcat(buf, FORMAT_TABLES, sizeof(buf));
-       proc = proc_net_fops_create(buf, 0440, &xt_file_ops);
+       proc = proc_net_fops_create(&init_net, buf, 0440, &xt_file_ops);
        if (!proc)
                goto out;
        proc->data = (void *) ((unsigned long) af | (TABLE << 16));
@@ -803,14 +877,14 @@ int xt_proto_init(int af)
 
        strlcpy(buf, xt_prefix[af], sizeof(buf));
        strlcat(buf, FORMAT_MATCHES, sizeof(buf));
-       proc = proc_net_fops_create(buf, 0440, &xt_file_ops);
+       proc = proc_net_fops_create(&init_net, buf, 0440, &xt_file_ops);
        if (!proc)
                goto out_remove_tables;
        proc->data = (void *) ((unsigned long) af | (MATCH << 16));
 
        strlcpy(buf, xt_prefix[af], sizeof(buf));
        strlcat(buf, FORMAT_TARGETS, sizeof(buf));
-       proc = proc_net_fops_create(buf, 0440, &xt_file_ops);
+       proc = proc_net_fops_create(&init_net, buf, 0440, &xt_file_ops);
        if (!proc)
                goto out_remove_matches;
        proc->data = (void *) ((unsigned long) af | (TARGET << 16));
@@ -822,12 +896,12 @@ int xt_proto_init(int af)
 out_remove_matches:
        strlcpy(buf, xt_prefix[af], sizeof(buf));
        strlcat(buf, FORMAT_MATCHES, sizeof(buf));
-       proc_net_remove(buf);
+       proc_net_remove(&init_net, buf);
 
 out_remove_tables:
        strlcpy(buf, xt_prefix[af], sizeof(buf));
        strlcat(buf, FORMAT_TABLES, sizeof(buf));
-       proc_net_remove(buf);
+       proc_net_remove(&init_net, buf);
 out:
        return -1;
 #endif
@@ -841,23 +915,35 @@ void xt_proto_fini(int af)
 
        strlcpy(buf, xt_prefix[af], sizeof(buf));
        strlcat(buf, FORMAT_TABLES, sizeof(buf));
-       proc_net_remove(buf);
+       proc_net_remove(&init_net, buf);
 
        strlcpy(buf, xt_prefix[af], sizeof(buf));
        strlcat(buf, FORMAT_TARGETS, sizeof(buf));
-       proc_net_remove(buf);
+       proc_net_remove(&init_net, buf);
 
        strlcpy(buf, xt_prefix[af], sizeof(buf));
        strlcat(buf, FORMAT_MATCHES, sizeof(buf));
-       proc_net_remove(buf);
+       proc_net_remove(&init_net, buf);
 #endif /*CONFIG_PROC_FS*/
 }
 EXPORT_SYMBOL_GPL(xt_proto_fini);
 
+static int __net_init xt_net_init(struct net *net)
+{
+       int i;
+
+       for (i = 0; i < NPROTO; i++)
+               INIT_LIST_HEAD(&net->xt.tables[i]);
+       return 0;
+}
+
+static struct pernet_operations xt_net_ops = {
+       .init = xt_net_init,
+};
 
 static int __init xt_init(void)
 {
-       int i;
+       int i, rv;
 
        xt = kmalloc(sizeof(struct xt_af) * NPROTO, GFP_KERNEL);
        if (!xt)
@@ -867,16 +953,20 @@ static int __init xt_init(void)
                mutex_init(&xt[i].mutex);
 #ifdef CONFIG_COMPAT
                mutex_init(&xt[i].compat_mutex);
+               xt[i].compat_offsets = NULL;
 #endif
                INIT_LIST_HEAD(&xt[i].target);
                INIT_LIST_HEAD(&xt[i].match);
-               INIT_LIST_HEAD(&xt[i].tables);
        }
-       return 0;
+       rv = register_pernet_subsys(&xt_net_ops);
+       if (rv < 0)
+               kfree(xt);
+       return rv;
 }
 
 static void __exit xt_fini(void)
 {
+       unregister_pernet_subsys(&xt_net_ops);
        kfree(xt);
 }