/* Returns whether matches rule or not. */
static inline int
ip6_packet_match(const struct sk_buff *skb,
+ const struct ipv6hdr *ipv6,
const char *indev,
const char *outdev,
const struct ip6t_ip6 *ip6info,
- unsigned int *protoff,
- int *fragoff)
+ int isfrag)
{
size_t i;
unsigned long ret;
- const struct ipv6hdr *ipv6 = skb->nh.ipv6h;
#define FWINV(bool,invflg) ((bool) ^ !!(ip6info->invflags & invflg))
/* look for the desired protocol header */
if((ip6info->flags & IP6T_F_PROTO)) {
u_int8_t currenthdr = ipv6->nexthdr;
- struct ipv6_opt_hdr _hdr, *hp;
+ struct ipv6_opt_hdr *hdrptr;
u_int16_t ptr; /* Header offset in skb */
u_int16_t hdrlen; /* Header */
- u_int16_t _fragoff = 0, *fp = NULL;
ptr = IPV6_HDR_LEN;
(currenthdr == IPPROTO_ESP))
return 0;
- hp = skb_header_pointer(skb, ptr, sizeof(_hdr), &_hdr);
- BUG_ON(hp == NULL);
+ hdrptr = (struct ipv6_opt_hdr *)(skb->data + ptr);
/* Size calculation */
if (currenthdr == IPPROTO_FRAGMENT) {
- fp = skb_header_pointer(skb,
- ptr+offsetof(struct frag_hdr,
- frag_off),
- sizeof(_fragoff),
- &_fragoff);
- if (fp == NULL)
- return 0;
-
- _fragoff = ntohs(*fp) & ~0x7;
hdrlen = 8;
} else if (currenthdr == IPPROTO_AH)
- hdrlen = (hp->hdrlen+2)<<2;
+ hdrlen = (hdrptr->hdrlen+2)<<2;
else
- hdrlen = ipv6_optlen(hp);
+ hdrlen = ipv6_optlen(hdrptr);
- currenthdr = hp->nexthdr;
+ currenthdr = hdrptr->nexthdr;
ptr += hdrlen;
/* ptr is too large */
if ( ptr > skb->len )
return 0;
- if (_fragoff) {
- if (ip6t_ext_hdr(currenthdr))
- return 0;
- break;
- }
}
- *protoff = ptr;
- *fragoff = _fragoff;
-
/* currenthdr contains the protocol header */
dprintf("Packet protocol %hi ?= %s%hi.\n",
static unsigned int
ip6t_error(struct sk_buff **pskb,
+ unsigned int hooknum,
const struct net_device *in,
const struct net_device *out,
- unsigned int hooknum,
const void *targinfo,
void *userinfo)
{
const struct net_device *in,
const struct net_device *out,
int offset,
- unsigned int protoff,
+ const void *hdr,
+ u_int16_t datalen,
int *hotdrop)
{
/* Stop iteration if it doesn't match */
if (!m->u.kernel.match->match(skb, in, out, m->data,
- offset, protoff, hotdrop))
+ offset, hdr, datalen, hotdrop))
return 1;
else
return 0;
void *userdata)
{
static const char nulldevname[IFNAMSIZ];
- int offset = 0;
- unsigned int protoff = 0;
+ u_int16_t offset = 0;
+ struct ipv6hdr *ipv6;
+ void *protohdr;
+ u_int16_t datalen;
int hotdrop = 0;
/* Initializing verdict to NF_DROP keeps gcc happy. */
unsigned int verdict = NF_DROP;
return NF_DROP;
/* Initialization */
+ ipv6 = (*pskb)->nh.ipv6h;
+ protohdr = (u_int32_t *)((char *)ipv6 + IPV6_HDR_LEN);
+ datalen = (*pskb)->len - IPV6_HDR_LEN;
indev = in ? in->name : nulldevname;
outdev = out ? out->name : nulldevname;
IP_NF_ASSERT(e);
IP_NF_ASSERT(back);
(*pskb)->nfcache |= e->nfcache;
- if (ip6_packet_match(*pskb, indev, outdev, &e->ipv6,
- &protoff, &offset)) {
+ if (ip6_packet_match(*pskb, ipv6, indev, outdev,
+ &e->ipv6, offset)) {
struct ip6t_entry_target *t;
if (IP6T_MATCH_ITERATE(e, do_match,
*pskb, in, out,
- offset, protoff, &hotdrop) != 0)
+ offset, protohdr,
+ datalen, &hotdrop) != 0)
goto no_match;
- ADD_COUNTER(e->counters,
- ntohs((*pskb)->nh.ipv6h->payload_len)
- + IPV6_HDR_LEN,
- 1);
+ ADD_COUNTER(e->counters, ntohs(ipv6->payload_len) + IPV6_HDR_LEN, 1);
t = ip6t_get_target(e);
IP_NF_ASSERT(t->u.kernel.target);
= 0xeeeeeeec;
#endif
verdict = t->u.kernel.target->target(pskb,
- in, out,
hook,
+ in, out,
t->data,
userdata);
((struct ip6t_entry *)table_base)->comefrom
= 0x57acc001;
#endif
+ /* Target might have changed stuff. */
+ ipv6 = (*pskb)->nh.ipv6h;
+ protohdr = (u_int32_t *)((void *)ipv6 + IPV6_HDR_LEN);
+ datalen = (*pskb)->len - IPV6_HDR_LEN;
+
if (verdict == IP6T_CONTINUE)
e = (void *)e + e->next_offset;
else
/* save number of initial entries */
table->private->initial_entries = table->private->number;
- rwlock_init(&table->lock);
+ table->lock = RW_LOCK_UNLOCKED;
list_prepend(&ip6t_tables, table);
unlock:
static int
tcp_find_option(u_int8_t option,
- const struct sk_buff *skb,
- unsigned int tcpoff,
- unsigned int optlen,
+ const struct tcphdr *tcp,
+ u_int16_t datalen,
int invert,
int *hotdrop)
{
- /* tcp.doff is only 4 bits, ie. max 15 * 4 bytes */
- u_int8_t _opt[60 - sizeof(struct tcphdr)], *op;
- unsigned int i;
+ unsigned int i = sizeof(struct tcphdr);
+ const u_int8_t *opt = (u_int8_t *)tcp;
duprintf("tcp_match: finding option\n");
- if (!optlen)
- return invert;
/* If we don't have the whole header, drop packet. */
- op = skb_header_pointer(skb, tcpoff + sizeof(struct tcphdr), optlen,
- _opt);
- if (op == NULL) {
+ if (tcp->doff * 4 < sizeof(struct tcphdr) ||
+ tcp->doff * 4 > datalen) {
*hotdrop = 1;
return 0;
}
- for (i = 0; i < optlen; ) {
- if (op[i] == option) return !invert;
- if (op[i] < 2) i++;
- else i += op[i+1]?:1;
+ while (i < tcp->doff * 4) {
+ if (opt[i] == option) return !invert;
+ if (opt[i] < 2) i++;
+ else i += opt[i+1]?:1;
}
return invert;
const struct net_device *out,
const void *matchinfo,
int offset,
- unsigned int protoff,
+ const void *hdr,
+ u_int16_t datalen,
int *hotdrop)
{
- struct tcphdr _tcph, *th;
+ const struct tcphdr *tcp;
const struct ip6t_tcp *tcpinfo = matchinfo;
+ int tcpoff;
+ u8 nexthdr = skb->nh.ipv6h->nexthdr;
- if (offset) {
- /* To quote Alan:
-
- Don't allow a fragment of TCP 8 bytes in. Nobody normal
- causes this. Its a cracker trying to break in by doing a
- flag overwrite to pass the direction checks.
- */
- if (offset == 1) {
- duprintf("Dropping evil TCP offset=1 frag.\n");
- *hotdrop = 1;
- }
- /* Must not be a fragment. */
- return 0;
- }
+ /* To quote Alan:
-#define FWINVTCP(bool,invflg) ((bool) ^ !!(tcpinfo->invflags & invflg))
+ Don't allow a fragment of TCP 8 bytes in. Nobody normal
+ causes this. Its a cracker trying to break in by doing a
+ flag overwrite to pass the direction checks.
+ */
- th = skb_header_pointer(skb, protoff, sizeof(_tcph), &_tcph);
- if (th == NULL) {
+ if (offset == 1) {
+ duprintf("Dropping evil TCP offset=1 frag.\n");
+ *hotdrop = 1;
+ return 0;
+ } else if (offset == 0 && datalen < sizeof(struct tcphdr)) {
/* We've been asked to examine this packet, and we
can't. Hence, no choice but to drop. */
duprintf("Dropping evil TCP offset=0 tinygram.\n");
return 0;
}
- if (!port_match(tcpinfo->spts[0], tcpinfo->spts[1],
- ntohs(th->source),
- !!(tcpinfo->invflags & IP6T_TCP_INV_SRCPT)))
+ tcpoff = (u8*)(skb->nh.ipv6h + 1) - skb->data;
+ tcpoff = ipv6_skip_exthdr(skb, tcpoff, &nexthdr, skb->len - tcpoff);
+ if (tcpoff < 0 || tcpoff > skb->len) {
+ duprintf("tcp_match: cannot skip exthdr. Dropping.\n");
+ *hotdrop = 1;
return 0;
- if (!port_match(tcpinfo->dpts[0], tcpinfo->dpts[1],
- ntohs(th->dest),
- !!(tcpinfo->invflags & IP6T_TCP_INV_DSTPT)))
+ } else if (nexthdr == IPPROTO_FRAGMENT)
return 0;
- if (!FWINVTCP((((unsigned char *)th)[13] & tcpinfo->flg_mask)
- == tcpinfo->flg_cmp,
- IP6T_TCP_INV_FLAGS))
+ else if (nexthdr != IPPROTO_TCP ||
+ skb->len - tcpoff < sizeof(struct tcphdr)) {
+ /* cannot be occured */
+ duprintf("tcp_match: cannot get TCP header. Dropping.\n");
+ *hotdrop = 1;
return 0;
- if (tcpinfo->option) {
- if (th->doff * 4 < sizeof(_tcph)) {
- *hotdrop = 1;
- return 0;
- }
- if (!tcp_find_option(tcpinfo->option, skb, protoff,
- th->doff*4 - sizeof(*th),
- tcpinfo->invflags & IP6T_TCP_INV_OPTION,
- hotdrop))
- return 0;
}
- return 1;
+
+ tcp = (struct tcphdr *)(skb->data + tcpoff);
+
+ /* FIXME: Try tcp doff >> packet len against various stacks --RR */
+
+#define FWINVTCP(bool,invflg) ((bool) ^ !!(tcpinfo->invflags & invflg))
+
+ /* Must not be a fragment. */
+ return !offset
+ && port_match(tcpinfo->spts[0], tcpinfo->spts[1],
+ ntohs(tcp->source),
+ !!(tcpinfo->invflags & IP6T_TCP_INV_SRCPT))
+ && port_match(tcpinfo->dpts[0], tcpinfo->dpts[1],
+ ntohs(tcp->dest),
+ !!(tcpinfo->invflags & IP6T_TCP_INV_DSTPT))
+ && FWINVTCP((((unsigned char *)tcp)[13]
+ & tcpinfo->flg_mask)
+ == tcpinfo->flg_cmp,
+ IP6T_TCP_INV_FLAGS)
+ && (!tcpinfo->option
+ || tcp_find_option(tcpinfo->option, tcp, datalen,
+ tcpinfo->invflags
+ & IP6T_TCP_INV_OPTION,
+ hotdrop));
}
/* Called when user tries to insert an entry of this type. */
const struct net_device *out,
const void *matchinfo,
int offset,
- unsigned int protoff,
+ const void *hdr,
+ u_int16_t datalen,
int *hotdrop)
{
- struct udphdr _udph, *uh;
+ const struct udphdr *udp;
const struct ip6t_udp *udpinfo = matchinfo;
+ int udpoff;
+ u8 nexthdr = skb->nh.ipv6h->nexthdr;
- /* Must not be a fragment. */
- if (offset)
- return 0;
-
- uh = skb_header_pointer(skb, protoff, sizeof(_udph), &_udph);
- if (uh == NULL) {
+ if (offset == 0 && datalen < sizeof(struct udphdr)) {
/* We've been asked to examine this packet, and we
can't. Hence, no choice but to drop. */
duprintf("Dropping evil UDP tinygram.\n");
return 0;
}
- return port_match(udpinfo->spts[0], udpinfo->spts[1],
- ntohs(uh->source),
- !!(udpinfo->invflags & IP6T_UDP_INV_SRCPT))
+ udpoff = (u8*)(skb->nh.ipv6h + 1) - skb->data;
+ udpoff = ipv6_skip_exthdr(skb, udpoff, &nexthdr, skb->len - udpoff);
+ if (udpoff < 0 || udpoff > skb->len) {
+ duprintf("udp_match: cannot skip exthdr. Dropping.\n");
+ *hotdrop = 1;
+ return 0;
+ } else if (nexthdr == IPPROTO_FRAGMENT)
+ return 0;
+ else if (nexthdr != IPPROTO_UDP ||
+ skb->len - udpoff < sizeof(struct udphdr)) {
+ duprintf("udp_match: cannot get UDP header. Dropping.\n");
+ *hotdrop = 1;
+ return 0;
+ }
+
+ udp = (struct udphdr *)(skb->data + udpoff);
+
+ /* Must not be a fragment. */
+ return !offset
+ && port_match(udpinfo->spts[0], udpinfo->spts[1],
+ ntohs(udp->source),
+ !!(udpinfo->invflags & IP6T_UDP_INV_SRCPT))
&& port_match(udpinfo->dpts[0], udpinfo->dpts[1],
- ntohs(uh->dest),
+ ntohs(udp->dest),
!!(udpinfo->invflags & IP6T_UDP_INV_DSTPT));
}
const struct net_device *out,
const void *matchinfo,
int offset,
- unsigned int protoff,
+ const void *hdr,
+ u_int16_t datalen,
int *hotdrop)
{
- struct icmp6hdr _icmp, *ic;
+ const struct icmp6hdr *icmp = hdr;
const struct ip6t_icmp *icmpinfo = matchinfo;
- /* Must not be a fragment. */
- if (offset)
- return 0;
-
- ic = skb_header_pointer(skb, protoff, sizeof(_icmp), &_icmp);
- if (ic == NULL) {
+ if (offset == 0 && datalen < 2) {
/* We've been asked to examine this packet, and we
can't. Hence, no choice but to drop. */
duprintf("Dropping evil ICMP tinygram.\n");
return 0;
}
- return icmp6_type_code_match(icmpinfo->type,
- icmpinfo->code[0],
- icmpinfo->code[1],
- ic->icmp6_type, ic->icmp6_code,
- !!(icmpinfo->invflags&IP6T_ICMP_INV));
+ /* Must not be a fragment. */
+ return !offset
+ && icmp6_type_code_match(icmpinfo->type,
+ icmpinfo->code[0],
+ icmpinfo->code[1],
+ icmp->icmp6_type, icmp->icmp6_code,
+ !!(icmpinfo->invflags&IP6T_ICMP_INV));
}
/* Called when user tries to insert an entry of this type. */