netfilter: xtables: add struct xt_mtdtor_param::net
[safe/jmp/linux-2.6] / net / ipv4 / netfilter / ip_tables.c
index 7b35c0b..cfaba0e 100644 (file)
@@ -8,6 +8,7 @@
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 #include <linux/cache.h>
 #include <linux/capability.h>
 #include <linux/skbuff.h>
@@ -88,9 +89,9 @@ ip_packet_match(const struct iphdr *ip,
 #define FWINV(bool, invflg) ((bool) ^ !!(ipinfo->invflags & (invflg)))
 
        if (FWINV((ip->saddr&ipinfo->smsk.s_addr) != ipinfo->src.s_addr,
-                 IPT_INV_SRCIP)
-           || FWINV((ip->daddr&ipinfo->dmsk.s_addr) != ipinfo->dst.s_addr,
-                    IPT_INV_DSTIP)) {
+                 IPT_INV_SRCIP) ||
+           FWINV((ip->daddr&ipinfo->dmsk.s_addr) != ipinfo->dst.s_addr,
+                 IPT_INV_DSTIP)) {
                dprintf("Source or dest mismatch.\n");
 
                dprintf("SRC: %pI4. Mask: %pI4. Target: %pI4.%s\n",
@@ -121,8 +122,8 @@ ip_packet_match(const struct iphdr *ip,
        }
 
        /* Check specific protocol */
-       if (ipinfo->proto
-           && FWINV(ip->protocol != ipinfo->proto, IPT_INV_PROTO)) {
+       if (ipinfo->proto &&
+           FWINV(ip->protocol != ipinfo->proto, IPT_INV_PROTO)) {
                dprintf("Packet protocol %hi does not match %hi.%s\n",
                        ip->protocol, ipinfo->proto,
                        ipinfo->invflags&IPT_INV_PROTO ? " (INV)":"");
@@ -190,16 +191,11 @@ get_entry(void *base, unsigned int offset)
 
 /* All zeroes == unconditional rule. */
 /* Mildly perf critical (only if packet tracing is on) */
-static inline int
-unconditional(const struct ipt_ip *ip)
+static inline bool unconditional(const struct ipt_ip *ip)
 {
-       unsigned int i;
-
-       for (i = 0; i < sizeof(*ip)/sizeof(__u32); i++)
-               if (((__u32 *)ip)[i])
-                       return 0;
+       static const struct ipt_ip uncond;
 
-       return 1;
+       return memcmp(ip, &uncond, sizeof(uncond)) == 0;
 #undef FWINV
 }
 
@@ -250,11 +246,11 @@ get_chainname_rulenum(struct ipt_entry *s, struct ipt_entry *e,
        } else if (s == e) {
                (*rulenum)++;
 
-               if (s->target_offset == sizeof(struct ipt_entry)
-                  && strcmp(t->target.u.kernel.target->name,
-                            IPT_STANDARD_TARGET) == 0
-                  && t->verdict < 0
-                  && unconditional(&s->ip)) {
+               if (s->target_offset == sizeof(struct ipt_entry) &&
+                   strcmp(t->target.u.kernel.target->name,
+                          IPT_STANDARD_TARGET) == 0 &&
+                  t->verdict < 0 &&
+                  unconditional(&s->ip)) {
                        /* Tail of chains: STANDARD target (return/policy) */
                        *comment = *chainname == hookname
                                ? comments[NF_IP_TRACE_COMMENT_POLICY]
@@ -315,7 +311,6 @@ ipt_do_table(struct sk_buff *skb,
 
        static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
        const struct iphdr *ip;
-       u_int16_t datalen;
        bool hotdrop = false;
        /* Initializing verdict to NF_DROP keeps gcc happy. */
        unsigned int verdict = NF_DROP;
@@ -328,7 +323,6 @@ ipt_do_table(struct sk_buff *skb,
 
        /* Initialization */
        ip = ip_hdr(skb);
-       datalen = skb->len - ip->ihl * 4;
        indev = in ? in->name : nulldevname;
        outdev = out ? out->name : nulldevname;
        /* We handle fragments by dealing with the first fragment as
@@ -343,7 +337,7 @@ ipt_do_table(struct sk_buff *skb,
        mtpar.in      = tgpar.in  = in;
        mtpar.out     = tgpar.out = out;
        mtpar.family  = tgpar.family = NFPROTO_IPV4;
-       tgpar.hooknum = hook;
+       mtpar.hooknum = tgpar.hooknum = hook;
 
        IP_NF_ASSERT(table->valid_hooks & (1 << hook));
        xt_info_rdlock_bh();
@@ -394,8 +388,8 @@ ipt_do_table(struct sk_buff *skb,
                                back = get_entry(table_base, back->comefrom);
                                continue;
                        }
-                       if (table_base + v != ipt_next_entry(e)
-                           && !(e->ip.flags & IPT_F_GOTO)) {
+                       if (table_base + v != ipt_next_entry(e) &&
+                           !(e->ip.flags & IPT_F_GOTO)) {
                                /* Save old back ptr in next entry */
                                struct ipt_entry *next = ipt_next_entry(e);
                                next->comefrom = (void *)back - table_base;
@@ -418,7 +412,7 @@ ipt_do_table(struct sk_buff *skb,
 #endif
                verdict = t->u.kernel.target->target(skb, &tgpar);
 #ifdef CONFIG_NETFILTER_DEBUG
-               if (comefrom != 0xeeeeeeec && verdict == IPT_CONTINUE) {
+               if (tb_comefrom != 0xeeeeeeec && verdict == IPT_CONTINUE) {
                        printk("Target %s reentered!\n",
                               t->u.kernel.target->name);
                        verdict = NF_DROP;
@@ -427,8 +421,6 @@ ipt_do_table(struct sk_buff *skb,
 #endif
                /* Target might have changed stuff. */
                ip = ip_hdr(skb);
-               datalen = skb->len - ip->ihl * 4;
-
                if (verdict == IPT_CONTINUE)
                        e = ipt_next_entry(e);
                else
@@ -481,11 +473,11 @@ mark_source_chains(struct xt_table_info *newinfo,
                        e->comefrom |= ((1 << hook) | (1 << NF_INET_NUMHOOKS));
 
                        /* Unconditional return/END. */
-                       if ((e->target_offset == sizeof(struct ipt_entry)
-                           && (strcmp(t->target.u.user.name,
-                                      IPT_STANDARD_TARGET) == 0)
-                           && t->verdict < 0
-                           && unconditional(&e->ip)) || visited) {
+                       if ((e->target_offset == sizeof(struct ipt_entry) &&
+                            (strcmp(t->target.u.user.name,
+                                    IPT_STANDARD_TARGET) == 0) &&
+                            t->verdict < 0 && unconditional(&e->ip)) ||
+                           visited) {
                                unsigned int oldpos, size;
 
                                if ((strcmp(t->target.u.user.name,
@@ -532,8 +524,8 @@ mark_source_chains(struct xt_table_info *newinfo,
                                int newpos = t->verdict;
 
                                if (strcmp(t->target.u.user.name,
-                                          IPT_STANDARD_TARGET) == 0
-                                   && newpos >= 0) {
+                                          IPT_STANDARD_TARGET) == 0 &&
+                                   newpos >= 0) {
                                        if (newpos > newinfo->size -
                                                sizeof(struct ipt_entry)) {
                                                duprintf("mark_source_chains: "
@@ -561,13 +553,14 @@ mark_source_chains(struct xt_table_info *newinfo,
 }
 
 static int
-cleanup_match(struct ipt_entry_match *m, unsigned int *i)
+cleanup_match(struct ipt_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.kernel.match;
        par.matchinfo = m->data;
        par.family    = NFPROTO_IPV4;
@@ -669,8 +662,8 @@ static int check_target(struct ipt_entry *e, const char *name)
 }
 
 static int
-find_check_entry(struct ipt_entry *e, const char *name, unsigned int size,
-                unsigned int *i)
+find_check_entry(struct ipt_entry *e, struct net *net, const char *name,
+                unsigned int size, unsigned int *i)
 {
        struct ipt_entry_target *t;
        struct xt_target *target;
@@ -683,6 +676,7 @@ find_check_entry(struct ipt_entry *e, const char *name, unsigned int size,
                return ret;
 
        j = 0;
+       mtpar.net       = net;
        mtpar.table     = name;
        mtpar.entryinfo = &e->ip;
        mtpar.hook_mask = e->comefrom;
@@ -712,10 +706,25 @@ find_check_entry(struct ipt_entry *e, const char *name, unsigned int size,
  err:
        module_put(t->u.kernel.target->me);
  cleanup_matches:
-       IPT_MATCH_ITERATE(e, cleanup_match, &j);
+       IPT_MATCH_ITERATE(e, cleanup_match, net, &j);
        return ret;
 }
 
+static bool check_underflow(struct ipt_entry *e)
+{
+       const struct ipt_entry_target *t;
+       unsigned int verdict;
+
+       if (!unconditional(&e->ip))
+               return false;
+       t = ipt_get_target(e);
+       if (strcmp(t->u.user.name, XT_STANDARD_TARGET) != 0)
+               return false;
+       verdict = ((struct ipt_standard_target *)t)->verdict;
+       verdict = -verdict - 1;
+       return verdict == NF_DROP || verdict == NF_ACCEPT;
+}
+
 static int
 check_entry_size_and_hooks(struct ipt_entry *e,
                           struct xt_table_info *newinfo,
@@ -723,12 +732,13 @@ check_entry_size_and_hooks(struct ipt_entry *e,
                           unsigned char *limit,
                           const unsigned int *hook_entries,
                           const unsigned int *underflows,
+                          unsigned int valid_hooks,
                           unsigned int *i)
 {
        unsigned int h;
 
-       if ((unsigned long)e % __alignof__(struct ipt_entry) != 0
-           || (unsigned char *)e + sizeof(struct ipt_entry) >= limit) {
+       if ((unsigned long)e % __alignof__(struct ipt_entry) != 0 ||
+           (unsigned char *)e + sizeof(struct ipt_entry) >= limit) {
                duprintf("Bad offset %p\n", e);
                return -EINVAL;
        }
@@ -742,15 +752,21 @@ check_entry_size_and_hooks(struct ipt_entry *e,
 
        /* Check hooks & underflows */
        for (h = 0; h < NF_INET_NUMHOOKS; h++) {
+               if (!(valid_hooks & (1 << h)))
+                       continue;
                if ((unsigned char *)e - base == hook_entries[h])
                        newinfo->hook_entry[h] = hook_entries[h];
-               if ((unsigned char *)e - base == underflows[h])
+               if ((unsigned char *)e - base == underflows[h]) {
+                       if (!check_underflow(e)) {
+                               pr_err("Underflows must be unconditional and "
+                                      "use the STANDARD target with "
+                                      "ACCEPT/DROP\n");
+                               return -EINVAL;
+                       }
                        newinfo->underflow[h] = underflows[h];
+               }
        }
 
-       /* FIXME: underflows must be unconditional, standard verdicts
-          < 0 (not IPT_RETURN). --RR */
-
        /* Clear counters and comefrom */
        e->counters = ((struct xt_counters) { 0, 0 });
        e->comefrom = 0;
@@ -760,7 +776,7 @@ check_entry_size_and_hooks(struct ipt_entry *e,
 }
 
 static int
-cleanup_entry(struct ipt_entry *e, unsigned int *i)
+cleanup_entry(struct ipt_entry *e, struct net *net, unsigned int *i)
 {
        struct xt_tgdtor_param par;
        struct ipt_entry_target *t;
@@ -769,7 +785,7 @@ cleanup_entry(struct ipt_entry *e, unsigned int *i)
                return 1;
 
        /* Cleanup all matches */
-       IPT_MATCH_ITERATE(e, cleanup_match, NULL);
+       IPT_MATCH_ITERATE(e, cleanup_match, net, NULL);
        t = ipt_get_target(e);
 
        par.target   = t->u.kernel.target;
@@ -784,7 +800,8 @@ cleanup_entry(struct ipt_entry *e, unsigned int *i)
 /* Checks and translates the user-supplied table segment (held in
    newinfo) */
 static int
-translate_table(const char *name,
+translate_table(struct net *net,
+               const char *name,
                unsigned int valid_hooks,
                struct xt_table_info *newinfo,
                void *entry0,
@@ -813,7 +830,7 @@ translate_table(const char *name,
                                newinfo,
                                entry0,
                                entry0 + size,
-                               hook_entries, underflows, &i);
+                               hook_entries, underflows, valid_hooks, &i);
        if (ret != 0)
                return ret;
 
@@ -846,11 +863,11 @@ translate_table(const char *name,
        /* Finally, each sanity check must pass */
        i = 0;
        ret = IPT_ENTRY_ITERATE(entry0, newinfo->size,
-                               find_check_entry, name, size, &i);
+                               find_check_entry, net, name, size, &i);
 
        if (ret != 0) {
                IPT_ENTRY_ITERATE(entry0, newinfo->size,
-                               cleanup_entry, &i);
+                               cleanup_entry, net, &i);
                return ret;
        }
 
@@ -1244,7 +1261,7 @@ __do_replace(struct net *net, const char *name, unsigned int valid_hooks,
        /* Decrease module usage counts and free resource */
        loc_cpu_old_entry = oldinfo->entries[raw_smp_processor_id()];
        IPT_ENTRY_ITERATE(loc_cpu_old_entry, oldinfo->size, cleanup_entry,
-                         NULL);
+                         net, NULL);
        xt_free_table_info(oldinfo);
        if (copy_to_user(counters_ptr, counters,
                         sizeof(struct xt_counters) * num_counters) != 0)
@@ -1289,7 +1306,7 @@ do_replace(struct net *net, void __user *user, unsigned int len)
                goto free_newinfo;
        }
 
-       ret = translate_table(tmp.name, tmp.valid_hooks,
+       ret = translate_table(net, tmp.name, tmp.valid_hooks,
                              newinfo, loc_cpu_entry, tmp.size, tmp.num_entries,
                              tmp.hook_entry, tmp.underflow);
        if (ret != 0)
@@ -1304,7 +1321,7 @@ do_replace(struct net *net, void __user *user, unsigned int len)
        return 0;
 
  free_newinfo_untrans:
-       IPT_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry, NULL);
+       IPT_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry, net, NULL);
  free_newinfo:
        xt_free_table_info(newinfo);
        return ret;
@@ -1534,8 +1551,8 @@ check_compat_entry_size_and_hooks(struct compat_ipt_entry *e,
        int ret, off, h;
 
        duprintf("check_compat_entry_size_and_hooks %p\n", e);
-       if ((unsigned long)e % __alignof__(struct compat_ipt_entry) != 0
-           || (unsigned char *)e + sizeof(struct compat_ipt_entry) >= limit) {
+       if ((unsigned long)e % __alignof__(struct compat_ipt_entry) != 0 ||
+           (unsigned char *)e + sizeof(struct compat_ipt_entry) >= limit) {
                duprintf("Bad offset %p, limit = %p\n", e, limit);
                return -EINVAL;
        }
@@ -1641,7 +1658,7 @@ compat_copy_entry_from_user(struct compat_ipt_entry *e, void **dstptr,
 }
 
 static int
-compat_check_entry(struct ipt_entry *e, const char *name,
+compat_check_entry(struct ipt_entry *e, struct net *net, const char *name,
                                     unsigned int *i)
 {
        struct xt_mtchk_param mtpar;
@@ -1649,6 +1666,7 @@ compat_check_entry(struct ipt_entry *e, const char *name,
        int ret;
 
        j = 0;
+       mtpar.net       = net;
        mtpar.table     = name;
        mtpar.entryinfo = &e->ip;
        mtpar.hook_mask = e->comefrom;
@@ -1665,12 +1683,13 @@ compat_check_entry(struct ipt_entry *e, const char *name,
        return 0;
 
  cleanup_matches:
-       IPT_MATCH_ITERATE(e, cleanup_match, &j);
+       IPT_MATCH_ITERATE(e, cleanup_match, net, &j);
        return ret;
 }
 
 static int
-translate_compat_table(const char *name,
+translate_compat_table(struct net *net,
+                      const char *name,
                       unsigned int valid_hooks,
                       struct xt_table_info **pinfo,
                       void **pentry0,
@@ -1759,12 +1778,12 @@ translate_compat_table(const char *name,
 
        i = 0;
        ret = IPT_ENTRY_ITERATE(entry1, newinfo->size, compat_check_entry,
-                               name, &i);
+                               net, name, &i);
        if (ret) {
                j -= i;
                COMPAT_IPT_ENTRY_ITERATE_CONTINUE(entry0, newinfo->size, i,
                                                  compat_release_entry, &j);
-               IPT_ENTRY_ITERATE(entry1, newinfo->size, cleanup_entry, &i);
+               IPT_ENTRY_ITERATE(entry1, newinfo->size, cleanup_entry, net, &i);
                xt_free_table_info(newinfo);
                return ret;
        }
@@ -1819,7 +1838,7 @@ compat_do_replace(struct net *net, void __user *user, unsigned int len)
                goto free_newinfo;
        }
 
-       ret = translate_compat_table(tmp.name, tmp.valid_hooks,
+       ret = translate_compat_table(net, tmp.name, tmp.valid_hooks,
                                     &newinfo, &loc_cpu_entry, tmp.size,
                                     tmp.num_entries, tmp.hook_entry,
                                     tmp.underflow);
@@ -1835,7 +1854,7 @@ compat_do_replace(struct net *net, void __user *user, unsigned int len)
        return 0;
 
  free_newinfo_untrans:
-       IPT_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry, NULL);
+       IPT_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry, net, NULL);
  free_newinfo:
        xt_free_table_info(newinfo);
        return ret;
@@ -2051,7 +2070,8 @@ do_ipt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
        return ret;
 }
 
-struct xt_table *ipt_register_table(struct net *net, struct xt_table *table,
+struct xt_table *ipt_register_table(struct net *net,
+                                   const struct xt_table *table,
                                    const struct ipt_replace *repl)
 {
        int ret;
@@ -2071,7 +2091,7 @@ struct xt_table *ipt_register_table(struct net *net, struct xt_table *table,
        loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
        memcpy(loc_cpu_entry, repl->entries, repl->size);
 
-       ret = translate_table(table->name, table->valid_hooks,
+       ret = translate_table(net, table->name, table->valid_hooks,
                              newinfo, loc_cpu_entry, repl->size,
                              repl->num_entries,
                              repl->hook_entry,
@@ -2093,7 +2113,7 @@ out:
        return ERR_PTR(ret);
 }
 
-void ipt_unregister_table(struct xt_table *table)
+void ipt_unregister_table(struct net *net, struct xt_table *table)
 {
        struct xt_table_info *private;
        void *loc_cpu_entry;
@@ -2103,7 +2123,7 @@ void ipt_unregister_table(struct xt_table *table)
 
        /* Decrease module usage counts and free resources */
        loc_cpu_entry = private->entries[raw_smp_processor_id()];
-       IPT_ENTRY_ITERATE(loc_cpu_entry, private->size, cleanup_entry, NULL);
+       IPT_ENTRY_ITERATE(loc_cpu_entry, private->size, cleanup_entry, net, NULL);
        if (private->number > private->initial_entries)
                module_put(table_owner);
        xt_free_table_info(private);