Merge branch 'master' of /repos/git/net-next-2.6
[safe/jmp/linux-2.6] / net / bridge / netfilter / ebtables.c
index 5bb88eb..208f4e3 100644 (file)
@@ -55,7 +55,6 @@
 
 
 static DEFINE_MUTEX(ebt_mutex);
-static LIST_HEAD(ebt_tables);
 
 static struct xt_target ebt_standard_target = {
        .name       = "standard",
@@ -80,18 +79,19 @@ static inline int ebt_do_match (struct ebt_entry_match *m,
 {
        par->match     = m->u.match;
        par->matchinfo = m->data;
-       return m->u.match->match(skb, par);
+       return m->u.match->match(skb, par) ? EBT_MATCH : EBT_NOMATCH;
 }
 
 static inline int ebt_dev_check(char *entry, const struct net_device *device)
 {
        int i = 0;
-       const char *devname = device->name;
+       const char *devname;
 
        if (*entry == '\0')
                return 0;
        if (!device)
                return 1;
+       devname = device->name;
        /* 1 is the wildcard token */
        while (entry[i] != '\0' && entry[i] != 1 && entry[i] == devname[i])
                i++;
@@ -142,6 +142,12 @@ static inline int ebt_basic_match(struct ebt_entry *e, struct ethhdr *h,
        return 0;
 }
 
+static inline __pure
+struct ebt_entry *ebt_next_entry(const struct ebt_entry *entry)
+{
+       return (void *)entry + entry->next_offset;
+}
+
 /* Do some firewalling */
 unsigned int ebt_do_table (unsigned int hook, struct sk_buff *skb,
    const struct net_device *in, const struct net_device *out,
@@ -164,7 +170,7 @@ unsigned int ebt_do_table (unsigned int hook, struct sk_buff *skb,
        mtpar.in      = tgpar.in  = in;
        mtpar.out     = tgpar.out = out;
        mtpar.hotdrop = &hotdrop;
-       tgpar.hooknum = hook;
+       mtpar.hooknum = tgpar.hooknum = hook;
 
        read_lock_bh(&table->lock);
        private = table->private;
@@ -249,8 +255,7 @@ letsreturn:
                /* jump to a udc */
                cs[sp].n = i + 1;
                cs[sp].chaininfo = chaininfo;
-               cs[sp].e = (struct ebt_entry *)
-                  (((char *)point) + point->next_offset);
+               cs[sp].e = ebt_next_entry(point);
                i = 0;
                chaininfo = (struct ebt_entries *) (base + verdict);
 #ifdef CONFIG_NETFILTER_DEBUG
@@ -266,8 +271,7 @@ letsreturn:
                sp++;
                continue;
 letscontinue:
-               point = (struct ebt_entry *)
-                  (((char *)point) + point->next_offset);
+               point = ebt_next_entry(point);
                i++;
        }
 
@@ -305,28 +309,21 @@ find_inlist_lock_noload(struct list_head *head, const char *name, int *error,
        return NULL;
 }
 
-#ifndef CONFIG_KMOD
-#define find_inlist_lock(h,n,p,e,m) find_inlist_lock_noload((h),(n),(e),(m))
-#else
 static void *
 find_inlist_lock(struct list_head *head, const char *name, const char *prefix,
    int *error, struct mutex *mutex)
 {
-       void *ret;
-
-       ret = find_inlist_lock_noload(head, name, error, mutex);
-       if (!ret) {
-               request_module("%s%s", prefix, name);
-               ret = find_inlist_lock_noload(head, name, error, mutex);
-       }
-       return ret;
+       return try_then_request_module(
+                       find_inlist_lock_noload(head, name, error, mutex),
+                       "%s%s", prefix, name);
 }
-#endif
 
 static inline struct ebt_table *
-find_table_lock(const char *name, int *error, struct mutex *mutex)
+find_table_lock(struct net *net, const char *name, int *error,
+               struct mutex *mutex)
 {
-       return find_inlist_lock(&ebt_tables, name, "ebtable_", error, mutex);
+       return find_inlist_lock(&net->xt.tables[NFPROTO_BRIDGE], name,
+                               "ebtable_", error, mutex);
 }
 
 static inline int
@@ -564,13 +561,14 @@ ebt_get_udc_positions(struct ebt_entry *e, struct ebt_table_info *newinfo,
 }
 
 static inline int
-ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i)
+ebt_cleanup_match(struct ebt_entry_match *m, struct net *net, unsigned int *i)
 {
        struct xt_mtdtor_param par;
 
        if (i && (*i)-- == 0)
                return 1;
 
+       par.net       = net;
        par.match     = m->u.match;
        par.matchinfo = m->data;
        par.family    = NFPROTO_BRIDGE;
@@ -581,13 +579,14 @@ ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i)
 }
 
 static inline int
-ebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i)
+ebt_cleanup_watcher(struct ebt_entry_watcher *w, struct net *net, unsigned int *i)
 {
        struct xt_tgdtor_param par;
 
        if (i && (*i)-- == 0)
                return 1;
 
+       par.net      = net;
        par.target   = w->u.watcher;
        par.targinfo = w->data;
        par.family   = NFPROTO_BRIDGE;
@@ -598,7 +597,7 @@ ebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i)
 }
 
 static inline int
-ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
+ebt_cleanup_entry(struct ebt_entry *e, struct net *net, unsigned int *cnt)
 {
        struct xt_tgdtor_param par;
        struct ebt_entry_target *t;
@@ -608,10 +607,11 @@ ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
        /* we're done */
        if (cnt && (*cnt)-- == 0)
                return 1;
-       EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, NULL);
-       EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL);
+       EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, net, NULL);
+       EBT_MATCH_ITERATE(e, ebt_cleanup_match, net, NULL);
        t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
 
+       par.net      = net;
        par.target   = t->u.target;
        par.targinfo = t->data;
        par.family   = NFPROTO_BRIDGE;
@@ -622,7 +622,9 @@ ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
 }
 
 static inline int
-ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
+ebt_check_entry(struct ebt_entry *e,
+   struct net *net,
+   struct ebt_table_info *newinfo,
    const char *name, unsigned int *cnt,
    struct ebt_cl_stack *cl_s, unsigned int udc_cnt)
 {
@@ -674,6 +676,7 @@ ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
        }
        i = 0;
 
+       mtpar.net       = tgpar.net       = net;
        mtpar.table     = tgpar.table     = name;
        mtpar.entryinfo = tgpar.entryinfo = e;
        mtpar.hook_mask = tgpar.hook_mask = hookmask;
@@ -729,9 +732,9 @@ ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
        (*cnt)++;
        return 0;
 cleanup_watchers:
-       EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, &j);
+       EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, net, &j);
 cleanup_matches:
-       EBT_MATCH_ITERATE(e, ebt_cleanup_match, &i);
+       EBT_MATCH_ITERATE(e, ebt_cleanup_match, net, &i);
        return ret;
 }
 
@@ -794,7 +797,7 @@ static int check_chainloops(struct ebt_entries *chain, struct ebt_cl_stack *cl_s
                        /* this can't be 0, so the loop test is correct */
                        cl_s[i].cs.n = pos + 1;
                        pos = 0;
-                       cl_s[i].cs.e = ((void *)e + e->next_offset);
+                       cl_s[i].cs.e = ebt_next_entry(e);
                        e = (struct ebt_entry *)(hlp2->data);
                        nentries = hlp2->nentries;
                        cl_s[i].from = chain_nr;
@@ -804,14 +807,15 @@ static int check_chainloops(struct ebt_entries *chain, struct ebt_cl_stack *cl_s
                        continue;
                }
 letscontinue:
-               e = (void *)e + e->next_offset;
+               e = ebt_next_entry(e);
                pos++;
        }
        return 0;
 }
 
 /* do the parsing of the table/chains/entries/matches/watchers/targets, heh */
-static int translate_table(char *name, struct ebt_table_info *newinfo)
+static int translate_table(struct net *net, char *name,
+                          struct ebt_table_info *newinfo)
 {
        unsigned int i, j, k, udc_cnt;
        int ret;
@@ -920,10 +924,10 @@ static int translate_table(char *name, struct ebt_table_info *newinfo)
        /* used to know what we need to clean up if something goes wrong */
        i = 0;
        ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
-          ebt_check_entry, newinfo, name, &i, cl_s, udc_cnt);
+          ebt_check_entry, net, newinfo, name, &i, cl_s, udc_cnt);
        if (ret != 0) {
                EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
-                  ebt_cleanup_entry, &i);
+                                 ebt_cleanup_entry, net, &i);
        }
        vfree(cl_s);
        return ret;
@@ -953,7 +957,7 @@ static void get_counters(struct ebt_counter *oldcounters,
 }
 
 /* replace the table */
-static int do_replace(void __user *user, unsigned int len)
+static int do_replace(struct net *net, void __user *user, unsigned int len)
 {
        int ret, i, countersize;
        struct ebt_table_info *newinfo;
@@ -1020,12 +1024,12 @@ static int do_replace(void __user *user, unsigned int len)
        if (ret != 0)
                goto free_counterstmp;
 
-       ret = translate_table(tmp.name, newinfo);
+       ret = translate_table(net, tmp.name, newinfo);
 
        if (ret != 0)
                goto free_counterstmp;
 
-       t = find_table_lock(tmp.name, &ret, &ebt_mutex);
+       t = find_table_lock(net, tmp.name, &ret, &ebt_mutex);
        if (!t) {
                ret = -ENOENT;
                goto free_iterate;
@@ -1073,7 +1077,7 @@ static int do_replace(void __user *user, unsigned int len)
 
        /* decrease module count and free resources */
        EBT_ENTRY_ITERATE(table->entries, table->entries_size,
-          ebt_cleanup_entry, NULL);
+                         ebt_cleanup_entry, net, NULL);
 
        vfree(table->entries);
        if (table->chainstack) {
@@ -1090,7 +1094,7 @@ free_unlock:
        mutex_unlock(&ebt_mutex);
 free_iterate:
        EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
-          ebt_cleanup_entry, NULL);
+                         ebt_cleanup_entry, net, NULL);
 free_counterstmp:
        vfree(counterstmp);
        /* can be initialized in translate_table() */
@@ -1106,26 +1110,34 @@ free_newinfo:
        return ret;
 }
 
-int ebt_register_table(struct ebt_table *table)
+struct ebt_table *
+ebt_register_table(struct net *net, const struct ebt_table *input_table)
 {
        struct ebt_table_info *newinfo;
-       struct ebt_table *t;
+       struct ebt_table *t, *table;
        struct ebt_replace_kernel *repl;
        int ret, i, countersize;
        void *p;
 
-       if (!table || !(repl = table->table) || !repl->entries ||
-           repl->entries_size == 0 ||
-           repl->counters || table->private) {
+       if (input_table == NULL || (repl = input_table->table) == NULL ||
+           repl->entries == 0 || repl->entries_size == 0 ||
+           repl->counters != NULL || input_table->private != NULL) {
                BUGPRINT("Bad table data for ebt_register_table!!!\n");
-               return -EINVAL;
+               return ERR_PTR(-EINVAL);
+       }
+
+       /* Don't add one table to multiple lists. */
+       table = kmemdup(input_table, sizeof(struct ebt_table), GFP_KERNEL);
+       if (!table) {
+               ret = -ENOMEM;
+               goto out;
        }
 
        countersize = COUNTER_OFFSET(repl->nentries) * nr_cpu_ids;
        newinfo = vmalloc(sizeof(*newinfo) + countersize);
        ret = -ENOMEM;
        if (!newinfo)
-               return -ENOMEM;
+               goto free_table;
 
        p = vmalloc(repl->entries_size);
        if (!p)
@@ -1149,7 +1161,7 @@ int ebt_register_table(struct ebt_table *table)
                        newinfo->hook_entry[i] = p +
                                ((char *)repl->hook_entry[i] - repl->entries);
        }
-       ret = translate_table(repl->name, newinfo);
+       ret = translate_table(net, repl->name, newinfo);
        if (ret != 0) {
                BUGPRINT("Translate_table failed\n");
                goto free_chainstack;
@@ -1157,7 +1169,7 @@ int ebt_register_table(struct ebt_table *table)
 
        if (table->check && table->check(newinfo, table->valid_hooks)) {
                BUGPRINT("The table doesn't like its own initial data, lol\n");
-               return -EINVAL;
+               return ERR_PTR(-EINVAL);
        }
 
        table->private = newinfo;
@@ -1166,7 +1178,7 @@ int ebt_register_table(struct ebt_table *table)
        if (ret != 0)
                goto free_chainstack;
 
-       list_for_each_entry(t, &ebt_tables, list) {
+       list_for_each_entry(t, &net->xt.tables[NFPROTO_BRIDGE], list) {
                if (strcmp(t->name, table->name) == 0) {
                        ret = -EEXIST;
                        BUGPRINT("Table name already exists\n");
@@ -1179,9 +1191,9 @@ int ebt_register_table(struct ebt_table *table)
                ret = -ENOENT;
                goto free_unlock;
        }
-       list_add(&table->list, &ebt_tables);
+       list_add(&table->list, &net->xt.tables[NFPROTO_BRIDGE]);
        mutex_unlock(&ebt_mutex);
-       return 0;
+       return table;
 free_unlock:
        mutex_unlock(&ebt_mutex);
 free_chainstack:
@@ -1193,10 +1205,13 @@ free_chainstack:
        vfree(newinfo->entries);
 free_newinfo:
        vfree(newinfo);
-       return ret;
+free_table:
+       kfree(table);
+out:
+       return ERR_PTR(ret);
 }
 
-void ebt_unregister_table(struct ebt_table *table)
+void ebt_unregister_table(struct net *net, struct ebt_table *table)
 {
        int i;
 
@@ -1207,6 +1222,10 @@ void ebt_unregister_table(struct ebt_table *table)
        mutex_lock(&ebt_mutex);
        list_del(&table->list);
        mutex_unlock(&ebt_mutex);
+       EBT_ENTRY_ITERATE(table->private->entries, table->private->entries_size,
+                         ebt_cleanup_entry, net, NULL);
+       if (table->private->nentries)
+               module_put(table->me);
        vfree(table->private->entries);
        if (table->private->chainstack) {
                for_each_possible_cpu(i)
@@ -1214,10 +1233,11 @@ void ebt_unregister_table(struct ebt_table *table)
                vfree(table->private->chainstack);
        }
        vfree(table->private);
+       kfree(table);
 }
 
 /* userspace just supplied us with counters */
-static int update_counters(void __user *user, unsigned int len)
+static int update_counters(struct net *net, void __user *user, unsigned int len)
 {
        int i, ret;
        struct ebt_counter *tmp;
@@ -1237,7 +1257,7 @@ static int update_counters(void __user *user, unsigned int len)
                return -ENOMEM;
        }
 
-       t = find_table_lock(hlp.name, &ret, &ebt_mutex);
+       t = find_table_lock(net, hlp.name, &ret, &ebt_mutex);
        if (!t)
                goto free_tmp;
 
@@ -1393,12 +1413,15 @@ static int do_ebt_set_ctl(struct sock *sk,
 {
        int ret;
 
+       if (!capable(CAP_NET_ADMIN))
+               return -EPERM;
+
        switch(cmd) {
        case EBT_SO_SET_ENTRIES:
-               ret = do_replace(user, len);
+               ret = do_replace(sock_net(sk), user, len);
                break;
        case EBT_SO_SET_COUNTERS:
-               ret = update_counters(user, len);
+               ret = update_counters(sock_net(sk), user, len);
                break;
        default:
                ret = -EINVAL;
@@ -1412,10 +1435,13 @@ static int do_ebt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
        struct ebt_replace tmp;
        struct ebt_table *t;
 
+       if (!capable(CAP_NET_ADMIN))
+               return -EPERM;
+
        if (copy_from_user(&tmp, user, sizeof(tmp)))
                return -EFAULT;
 
-       t = find_table_lock(tmp.name, &ret, &ebt_mutex);
+       t = find_table_lock(sock_net(sk), tmp.name, &ret, &ebt_mutex);
        if (!t)
                return ret;