linux 2.6.16.38 w/ vs2.0.3-rc1
[linux-2.6.git] / net / ipv6 / netfilter / ip6_tables.c
index 751548a..e2bb9ac 100644 (file)
  */
 
 #include <linux/capability.h>
+#include <linux/config.h>
 #include <linux/in.h>
 #include <linux/skbuff.h>
 #include <linux/kmod.h>
 #include <linux/vmalloc.h>
 #include <linux/netdevice.h>
 #include <linux/module.h>
-#include <linux/poison.h>
 #include <linux/icmpv6.h>
 #include <net/ipv6.h>
 #include <asm/uaccess.h>
-#include <linux/mutex.h>
+#include <asm/semaphore.h>
 #include <linux/proc_fs.h>
 #include <linux/cpumask.h>
 
@@ -94,6 +94,19 @@ do {                                                         \
 #define up(x) do { printk("UP:%u:" #x "\n", __LINE__); up(x); } while(0)
 #endif
 
+int
+ip6_masked_addrcmp(const struct in6_addr *addr1, const struct in6_addr *mask,
+                   const struct in6_addr *addr2)
+{
+       int i;
+       for( i = 0; i < 16; i++){
+               if((addr1->s6_addr[i] & mask->s6_addr[i]) != 
+                  (addr2->s6_addr[i] & mask->s6_addr[i]))
+                       return 1;
+       }
+       return 0;
+}
+
 /* Check for an extension */
 int 
 ip6t_ext_hdr(u8 nexthdr)
@@ -114,7 +127,7 @@ ip6_packet_match(const struct sk_buff *skb,
                 const char *outdev,
                 const struct ip6t_ip6 *ip6info,
                 unsigned int *protoff,
-                int *fragoff)
+                int *fragoff, int *hotdrop)
 {
        size_t i;
        unsigned long ret;
@@ -122,10 +135,10 @@ ip6_packet_match(const struct sk_buff *skb,
 
 #define FWINV(bool,invflg) ((bool) ^ !!(ip6info->invflags & invflg))
 
-       if (FWINV(ipv6_masked_addr_cmp(&ipv6->saddr, &ip6info->smsk,
-                                      &ip6info->src), IP6T_INV_SRCIP)
-           || FWINV(ipv6_masked_addr_cmp(&ipv6->daddr, &ip6info->dmsk,
-                                         &ip6info->dst), IP6T_INV_DSTIP)) {
+       if (FWINV(ip6_masked_addrcmp(&ipv6->saddr, &ip6info->smsk,
+                                    &ip6info->src), IP6T_INV_SRCIP)
+           || FWINV(ip6_masked_addrcmp(&ipv6->daddr, &ip6info->dmsk,
+                                       &ip6info->dst), IP6T_INV_DSTIP)) {
                dprintf("Source or dest mismatch.\n");
 /*
                dprintf("SRC: %u. Mask: %u. Target: %u.%s\n", ip->saddr,
@@ -172,9 +185,11 @@ ip6_packet_match(const struct sk_buff *skb,
                unsigned short _frag_off;
 
                protohdr = ipv6_find_hdr(skb, protoff, -1, &_frag_off);
-               if (protohdr < 0)
+               if (protohdr < 0) {
+                       if (_frag_off == 0)
+                               *hotdrop = 1;
                        return 0;
-
+               }
                *fragoff = _frag_off;
 
                dprintf("Packet protocol %hi ?= %s%hi.\n",
@@ -219,7 +234,6 @@ ip6t_error(struct sk_buff **pskb,
          const struct net_device *in,
          const struct net_device *out,
          unsigned int hooknum,
-         const struct xt_target *target,
          const void *targinfo,
          void *userinfo)
 {
@@ -239,7 +253,7 @@ int do_match(struct ip6t_entry_match *m,
             int *hotdrop)
 {
        /* Stop iteration if it doesn't match */
-       if (!m->u.kernel.match->match(skb, in, out, m->u.kernel.match, m->data,
+       if (!m->u.kernel.match->match(skb, in, out, m->data,
                                      offset, protoff, hotdrop))
                return 1;
        else
@@ -288,6 +302,19 @@ ip6t_do_table(struct sk_buff **pskb,
        table_base = (void *)private->entries[smp_processor_id()];
        e = get_entry(table_base, private->hook_entry[hook]);
 
+#ifdef CONFIG_NETFILTER_DEBUG
+       /* Check noone else using our table */
+       if (((struct ip6t_entry *)table_base)->comefrom != 0xdead57ac
+           && ((struct ip6t_entry *)table_base)->comefrom != 0xeeeeeeec) {
+               printk("ASSERT: CPU #%u, %s comefrom(%p) = %X\n",
+                      smp_processor_id(),
+                      table->name,
+                      &((struct ip6t_entry *)table_base)->comefrom,
+                      ((struct ip6t_entry *)table_base)->comefrom);
+       }
+       ((struct ip6t_entry *)table_base)->comefrom = 0x57acc001;
+#endif
+
        /* For return from builtin chain */
        back = get_entry(table_base, private->underflow[hook]);
 
@@ -295,7 +322,7 @@ ip6t_do_table(struct sk_buff **pskb,
                IP_NF_ASSERT(e);
                IP_NF_ASSERT(back);
                if (ip6_packet_match(*pskb, indev, outdev, &e->ipv6,
-                       &protoff, &offset)) {
+                       &protoff, &offset, &hotdrop)) {
                        struct ip6t_entry_target *t;
 
                        if (IP6T_MATCH_ITERATE(e, do_match,
@@ -348,7 +375,6 @@ ip6t_do_table(struct sk_buff **pskb,
                                verdict = t->u.kernel.target->target(pskb,
                                                                     in, out,
                                                                     hook,
-                                                                    t->u.kernel.target,
                                                                     t->data,
                                                                     userdata);
 
@@ -377,7 +403,7 @@ ip6t_do_table(struct sk_buff **pskb,
        } while (!hotdrop);
 
 #ifdef CONFIG_NETFILTER_DEBUG
-       ((struct ip6t_entry *)table_base)->comefrom = NETFILTER_LINK_POISON;
+       ((struct ip6t_entry *)table_base)->comefrom = 0xdead57ac;
 #endif
        read_unlock_bh(&table->lock);
 
@@ -444,13 +470,6 @@ mark_source_chains(struct xt_table_info *newinfo,
                            && unconditional(&e->ipv6)) {
                                unsigned int oldpos, size;
 
-                               if (t->verdict < -NF_MAX_VERDICT - 1) {
-                                       duprintf("mark_source_chains: bad "
-                                               "negative verdict (%i)\n",
-                                                               t->verdict);
-                                       return 0;
-                               }
-
                                /* Return: backtrack through the last
                                   big jump. */
                                do {
@@ -488,13 +507,6 @@ mark_source_chains(struct xt_table_info *newinfo,
                                if (strcmp(t->target.u.user.name,
                                           IP6T_STANDARD_TARGET) == 0
                                    && newpos >= 0) {
-                                       if (newpos > newinfo->size -
-                                               sizeof(struct ip6t_entry)) {
-                                               duprintf("mark_source_chains: "
-                                                       "bad verdict (%i)\n",
-                                                               newpos);
-                                               return 0;
-                                       }
                                        /* This a jump; chase it. */
                                        duprintf("Jump rule %u -> %u\n",
                                                 pos, newpos);
@@ -521,12 +533,42 @@ cleanup_match(struct ip6t_entry_match *m, unsigned int *i)
                return 1;
 
        if (m->u.kernel.match->destroy)
-               m->u.kernel.match->destroy(m->u.kernel.match, m->data,
+               m->u.kernel.match->destroy(m->data,
                                           m->u.match_size - sizeof(*m));
        module_put(m->u.kernel.match->me);
        return 0;
 }
 
+static inline int
+standard_check(const struct ip6t_entry_target *t,
+              unsigned int max_offset)
+{
+       struct ip6t_standard_target *targ = (void *)t;
+
+       /* Check standard info. */
+       if (t->u.target_size
+           != IP6T_ALIGN(sizeof(struct ip6t_standard_target))) {
+               duprintf("standard_check: target size %u != %u\n",
+                        t->u.target_size,
+                        IP6T_ALIGN(sizeof(struct ip6t_standard_target)));
+               return 0;
+       }
+
+       if (targ->verdict >= 0
+           && targ->verdict > max_offset - sizeof(struct ip6t_entry)) {
+               duprintf("ip6t_standard_check: bad verdict (%i)\n",
+                        targ->verdict);
+               return 0;
+       }
+
+       if (targ->verdict < -NF_MAX_VERDICT - 1) {
+               duprintf("ip6t_standard_check: bad negative verdict (%i)\n",
+                        targ->verdict);
+               return 0;
+       }
+       return 1;
+}
+
 static inline int
 check_match(struct ip6t_entry_match *m,
            const char *name,
@@ -535,7 +577,6 @@ check_match(struct ip6t_entry_match *m,
            unsigned int *i)
 {
        struct ip6t_match *match;
-       int ret;
 
        match = try_then_request_module(xt_find_match(AF_INET6, m->u.user.name,
                                        m->u.user.revision),
@@ -546,27 +587,18 @@ check_match(struct ip6t_entry_match *m,
        }
        m->u.kernel.match = match;
 
-       ret = xt_check_match(match, AF_INET6, m->u.match_size - sizeof(*m),
-                            name, hookmask, ipv6->proto,
-                            ipv6->invflags & IP6T_INV_PROTO);
-       if (ret)
-               goto err;
-
        if (m->u.kernel.match->checkentry
-           && !m->u.kernel.match->checkentry(name, ipv6, match,  m->data,
+           && !m->u.kernel.match->checkentry(name, ipv6, m->data,
                                              m->u.match_size - sizeof(*m),
                                              hookmask)) {
+               module_put(m->u.kernel.match->me);
                duprintf("ip_tables: check failed for `%s'.\n",
                         m->u.kernel.match->name);
-               ret = -EINVAL;
-               goto err;
+               return -EINVAL;
        }
 
        (*i)++;
        return 0;
-err:
-       module_put(m->u.kernel.match->me);
-       return ret;
 }
 
 static struct ip6t_target ip6t_standard_target;
@@ -585,19 +617,12 @@ check_entry(struct ip6t_entry *e, const char *name, unsigned int size,
                return -EINVAL;
        }
 
-       if (e->target_offset + sizeof(struct ip6t_entry_target) >
-                                                               e->next_offset)
-               return -EINVAL;
-
        j = 0;
        ret = IP6T_MATCH_ITERATE(e, check_match, name, &e->ipv6, e->comefrom, &j);
        if (ret != 0)
                goto cleanup_matches;
 
        t = ip6t_get_target(e);
-       ret = -EINVAL;
-       if (e->target_offset + t->u.target_size > e->next_offset)
-                       goto cleanup_matches;
        target = try_then_request_module(xt_find_target(AF_INET6,
                                                        t->u.user.name,
                                                        t->u.user.revision),
@@ -609,27 +634,26 @@ check_entry(struct ip6t_entry *e, const char *name, unsigned int size,
        }
        t->u.kernel.target = target;
 
-       ret = xt_check_target(target, AF_INET6, t->u.target_size - sizeof(*t),
-                             name, e->comefrom, e->ipv6.proto,
-                             e->ipv6.invflags & IP6T_INV_PROTO);
-       if (ret)
-               goto err;
-
-       if (t->u.kernel.target->checkentry
-                  && !t->u.kernel.target->checkentry(name, e, target, t->data,
+       if (t->u.kernel.target == &ip6t_standard_target) {
+               if (!standard_check(t, size)) {
+                       ret = -EINVAL;
+                       goto cleanup_matches;
+               }
+       } else if (t->u.kernel.target->checkentry
+                  && !t->u.kernel.target->checkentry(name, e, t->data,
                                                      t->u.target_size
                                                      - sizeof(*t),
                                                      e->comefrom)) {
+               module_put(t->u.kernel.target->me);
                duprintf("ip_tables: check failed for `%s'.\n",
                         t->u.kernel.target->name);
                ret = -EINVAL;
-               goto err;
+               goto cleanup_matches;
        }
 
        (*i)++;
        return 0;
- err:
-       module_put(t->u.kernel.target->me);
+
  cleanup_matches:
        IP6T_MATCH_ITERATE(e, cleanup_match, &j);
        return ret;
@@ -690,7 +714,7 @@ cleanup_entry(struct ip6t_entry *e, unsigned int *i)
        IP6T_MATCH_ITERATE(e, cleanup_match, NULL);
        t = ip6t_get_target(e);
        if (t->u.kernel.target->destroy)
-               t->u.kernel.target->destroy(t->u.kernel.target, t->data,
+               t->u.kernel.target->destroy(t->data,
                                            t->u.target_size - sizeof(*t));
        module_put(t->u.kernel.target->me);
        return 0;
@@ -765,17 +789,17 @@ translate_table(const char *name,
 
        if (ret != 0) {
                IP6T_ENTRY_ITERATE(entry0, newinfo->size,
-                                  cleanup_entry, &i);
+                                 cleanup_entry, &i);
                return ret;
        }
 
        /* And one copy for every other CPU */
-       for_each_possible_cpu(i) {
+       for_each_cpu(i) {
                if (newinfo->entries[i] && newinfo->entries[i] != entry0)
                        memcpy(newinfo->entries[i], entry0, newinfo->size);
        }
 
-       return 0;
+       return ret;
 }
 
 /* Gets counters. */
@@ -823,7 +847,7 @@ get_counters(const struct xt_table_info *t,
                           counters,
                           &i);
 
-       for_each_possible_cpu(cpu) {
+       for_each_cpu(cpu) {
                if (cpu == curcpu)
                        continue;
                i = 0;
@@ -1276,8 +1300,7 @@ int ip6t_register_table(struct xt_table *table,
                return ret;
        }
 
-       ret = xt_register_table(table, &bootstrap, newinfo);
-       if (ret != 0) {
+       if (xt_register_table(table, &bootstrap, newinfo) != 0) {
                xt_free_table_info(newinfo);
                return ret;
        }
@@ -1312,7 +1335,6 @@ static int
 icmp6_match(const struct sk_buff *skb,
           const struct net_device *in,
           const struct net_device *out,
-          const struct xt_match *match,
           const void *matchinfo,
           int offset,
           unsigned int protoff,
@@ -1345,29 +1367,28 @@ icmp6_match(const struct sk_buff *skb,
 static int
 icmp6_checkentry(const char *tablename,
           const void *entry,
-          const struct xt_match *match,
           void *matchinfo,
           unsigned int matchsize,
           unsigned int hook_mask)
 {
+       const struct ip6t_ip6 *ipv6 = entry;
        const struct ip6t_icmp *icmpinfo = matchinfo;
 
-       /* Must specify no unknown invflags */
-       return !(icmpinfo->invflags & ~IP6T_ICMP_INV);
+       /* Must specify proto == ICMP, and no unknown invflags */
+       return ipv6->proto == IPPROTO_ICMPV6
+               && !(ipv6->invflags & IP6T_INV_PROTO)
+               && matchsize == IP6T_ALIGN(sizeof(struct ip6t_icmp))
+               && !(icmpinfo->invflags & ~IP6T_ICMP_INV);
 }
 
 /* The built-in targets: standard (NULL) and error. */
 static struct ip6t_target ip6t_standard_target = {
        .name           = IP6T_STANDARD_TARGET,
-       .targetsize     = sizeof(int),
-       .family         = AF_INET6,
 };
 
 static struct ip6t_target ip6t_error_target = {
        .name           = IP6T_ERROR_TARGET,
        .target         = ip6t_error,
-       .targetsize     = IP6T_FUNCTION_MAXNAMELEN,
-       .family         = AF_INET6,
 };
 
 static struct nf_sockopt_ops ip6t_sockopts = {
@@ -1383,57 +1404,38 @@ static struct nf_sockopt_ops ip6t_sockopts = {
 static struct ip6t_match icmp6_matchstruct = {
        .name           = "icmp6",
        .match          = &icmp6_match,
-       .matchsize      = sizeof(struct ip6t_icmp),
-       .checkentry     = icmp6_checkentry,
-       .proto          = IPPROTO_ICMPV6,
-       .family         = AF_INET6,
+       .checkentry     = &icmp6_checkentry,
 };
 
-static int __init ip6_tables_init(void)
+static int __init init(void)
 {
        int ret;
 
-       ret = xt_proto_init(AF_INET6);
-       if (ret < 0)
-               goto err1;
+       xt_proto_init(AF_INET6);
 
        /* Noone else will be downing sem now, so we won't sleep */
-       ret = xt_register_target(&ip6t_standard_target);
-       if (ret < 0)
-               goto err2;
-       ret = xt_register_target(&ip6t_error_target);
-       if (ret < 0)
-               goto err3;
-       ret = xt_register_match(&icmp6_matchstruct);
-       if (ret < 0)
-               goto err4;
+       xt_register_target(AF_INET6, &ip6t_standard_target);
+       xt_register_target(AF_INET6, &ip6t_error_target);
+       xt_register_match(AF_INET6, &icmp6_matchstruct);
 
        /* Register setsockopt */
        ret = nf_register_sockopt(&ip6t_sockopts);
-       if (ret < 0)
-               goto err5;
+       if (ret < 0) {
+               duprintf("Unable to register sockopts.\n");
+               xt_proto_fini(AF_INET6);
+               return ret;
+       }
 
        printk("ip6_tables: (C) 2000-2006 Netfilter Core Team\n");
        return 0;
-
-err5:
-       xt_unregister_match(&icmp6_matchstruct);
-err4:
-       xt_unregister_target(&ip6t_error_target);
-err3:
-       xt_unregister_target(&ip6t_standard_target);
-err2:
-       xt_proto_fini(AF_INET6);
-err1:
-       return ret;
 }
 
-static void __exit ip6_tables_fini(void)
+static void __exit fini(void)
 {
        nf_unregister_sockopt(&ip6t_sockopts);
-       xt_unregister_match(&icmp6_matchstruct);
-       xt_unregister_target(&ip6t_error_target);
-       xt_unregister_target(&ip6t_standard_target);
+       xt_unregister_match(AF_INET6, &icmp6_matchstruct);
+       xt_unregister_target(AF_INET6, &ip6t_error_target);
+       xt_unregister_target(AF_INET6, &ip6t_standard_target);
        xt_proto_fini(AF_INET6);
 }
 
@@ -1445,6 +1447,9 @@ static void __exit ip6_tables_fini(void)
  * If target header is found, its offset is set in *offset and return protocol
  * number. Otherwise, return -1.
  *
+ * If the first fragment doesn't contain the final protocol header or
+ * NEXTHDR_NONE it is considered invalid.
+ *
  * Note that non-1st fragment is special case that "the protocol number
  * of last header" is "next header" field in Fragment header. In this case,
  * *offset is meaningless and fragment offset is stored in *fragoff if fragoff
@@ -1468,12 +1473,12 @@ int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
                if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) {
                        if (target < 0)
                                break;
-                       return -1;
+                       return -ENOENT;
                }
 
                hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
                if (hp == NULL)
-                       return -1;
+                       return -EBADMSG;
                if (nexthdr == NEXTHDR_FRAGMENT) {
                        unsigned short _frag_off, *fp;
                        fp = skb_header_pointer(skb,
@@ -1482,7 +1487,7 @@ int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
                                                sizeof(_frag_off),
                                                &_frag_off);
                        if (fp == NULL)
-                               return -1;
+                               return -EBADMSG;
 
                        _frag_off = ntohs(*fp) & ~0x7;
                        if (_frag_off) {
@@ -1493,7 +1498,7 @@ int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
                                                *fragoff = _frag_off;
                                        return hp->nexthdr;
                                }
-                               return -1;
+                               return -ENOENT;
                        }
                        hdrlen = 8;
                } else if (nexthdr == NEXTHDR_AUTH)
@@ -1515,6 +1520,7 @@ EXPORT_SYMBOL(ip6t_unregister_table);
 EXPORT_SYMBOL(ip6t_do_table);
 EXPORT_SYMBOL(ip6t_ext_hdr);
 EXPORT_SYMBOL(ipv6_find_hdr);
+EXPORT_SYMBOL(ip6_masked_addrcmp);
 
-module_init(ip6_tables_init);
-module_exit(ip6_tables_fini);
+module_init(init);
+module_exit(fini);