X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=net%2Fipv6%2Fnetfilter%2Fip6_tables.c;fp=net%2Fipv6%2Fnetfilter%2Fip6_tables.c;h=e2bb9acd241c9fb9906d8aeec8416b564c4c163d;hb=64ba3f394c830ec48a1c31b53dcae312c56f1604;hp=751548a42bf525e3b2fffcc17a9d38b0e5223eb1;hpb=be1e6109ac94a859551f8e1774eb9a8469fe055c;p=linux-2.6.git diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index 751548a42..e2bb9acd2 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -19,17 +19,17 @@ */ #include +#include #include #include #include #include #include #include -#include #include #include #include -#include +#include #include #include @@ -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);