*/
#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>
#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)
const char *outdev,
const struct ip6t_ip6 *ip6info,
unsigned int *protoff,
- int *fragoff)
+ int *fragoff, int *hotdrop)
{
size_t i;
unsigned long ret;
#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,
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",
const struct net_device *in,
const struct net_device *out,
unsigned int hooknum,
- const struct xt_target *target,
const void *targinfo,
void *userinfo)
{
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
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]);
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,
verdict = t->u.kernel.target->target(pskb,
in, out,
hook,
- t->u.kernel.target,
t->data,
userdata);
} 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);
&& 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 {
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);
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,
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),
}
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;
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),
}
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;
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;
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. */
counters,
&i);
- for_each_possible_cpu(cpu) {
+ for_each_cpu(cpu) {
if (cpu == curcpu)
continue;
i = 0;
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;
}
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,
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 = {
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);
}
* 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
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,
sizeof(_frag_off),
&_frag_off);
if (fp == NULL)
- return -1;
+ return -EBADMSG;
_frag_off = ntohs(*fp) & ~0x7;
if (_frag_off) {
*fragoff = _frag_off;
return hp->nexthdr;
}
- return -1;
+ return -ENOENT;
}
hdrlen = 8;
} else if (nexthdr == NEXTHDR_AUTH)
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);