[NETFILTER]: x_tables: return new table from {arp,ip,ip6}t_register_table()
[pandora-kernel.git] / net / netfilter / x_tables.c
index d9a3bde..d7fbb1b 100644 (file)
@@ -34,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;
@@ -335,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;
@@ -342,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;
@@ -365,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);
 
@@ -377,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) {
@@ -468,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) {
@@ -495,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;
 
@@ -535,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);
@@ -598,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;
@@ -630,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);
 
@@ -647,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;
 }
@@ -680,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;
@@ -855,10 +928,22 @@ void xt_proto_fini(int af)
 }
 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)
@@ -868,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);
 }