patch-2.4.4 linux/net/ipv6/exthdrs.c
Next file: linux/net/ipv6/icmp.c
Previous file: linux/net/ipv6/datagram.c
Back to the patch index
Back to the overall index
- Lines: 518
- Date:
Thu Apr 12 12:11:39 2001
- Orig file:
v2.4.3/linux/net/ipv6/exthdrs.c
- Orig date:
Sat Jan 8 21:36:21 2000
diff -u --recursive --new-file v2.4.3/linux/net/ipv6/exthdrs.c linux/net/ipv6/exthdrs.c
@@ -7,7 +7,7 @@
* Andi Kleen <ak@muc.de>
* Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
*
- * $Id: exthdrs.c,v 1.10 2000/01/09 02:19:55 davem Exp $
+ * $Id: exthdrs.c,v 1.12 2001/01/22 02:36:37 davem Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -15,6 +15,11 @@
* 2 of the License, or (at your option) any later version.
*/
+/* Changes:
+ * yoshfuji : ensure not to overrun while parsing
+ * tlv options.
+ */
+
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/socket.h>
@@ -41,15 +46,15 @@
/*
* Parsing inbound headers.
*
- * Parsing function "func" returns pointer to the place,
+ * Parsing function "func" returns offset wrt skb->nh of the place,
* where next nexthdr value is stored or NULL, if parsing
- * failed. It should also update skb->h.
+ * failed. It should also update skb->h tp point at the next header.
*/
struct hdrtype_proc
{
int type;
- u8* (*func) (struct sk_buff **, u8 *ptr);
+ int (*func) (struct sk_buff **, int offset);
};
/*
@@ -63,7 +68,7 @@
struct tlvtype_proc
{
int type;
- int (*func) (struct sk_buff *, __u8 *ptr);
+ int (*func) (struct sk_buff *, int offset);
};
/*********************
@@ -72,12 +77,12 @@
/* An unknown option is detected, decide what to do */
-int ip6_tlvopt_unknown(struct sk_buff *skb, u8 *opt)
+int ip6_tlvopt_unknown(struct sk_buff *skb, int optoff)
{
- switch ((opt[0] & 0xC0) >> 6) {
+ switch ((skb->nh.raw[optoff] & 0xC0) >> 6) {
case 0: /* ignore */
return 1;
-
+
case 1: /* drop packet */
break;
@@ -88,7 +93,7 @@
if (ipv6_addr_is_multicast(&skb->nh.ipv6h->daddr))
break;
case 2: /* send ICMP PARM PROB regardless and drop packet */
- icmpv6_param_prob(skb, ICMPV6_UNK_OPTION, opt);
+ icmpv6_param_prob(skb, ICMPV6_UNK_OPTION, optoff);
return 0;
};
@@ -98,24 +103,21 @@
/* Parse tlv encoded option header (hop-by-hop or destination) */
-static int ip6_parse_tlv(struct tlvtype_proc *procs, struct sk_buff *skb,
- __u8 *nhptr)
+static int ip6_parse_tlv(struct tlvtype_proc *procs, struct sk_buff *skb)
{
struct tlvtype_proc *curr;
- u8 *ptr = skb->h.raw;
- int len = ((ptr[1]+1)<<3) - 2;
+ int off = skb->h.raw - skb->nh.raw;
+ int len = ((skb->h.raw[1]+1)<<3);
- ptr += 2;
+ if ((skb->h.raw + len) - skb->data > skb_headlen(skb))
+ goto bad;
- if (skb->tail - (ptr + len) < 0) {
- kfree_skb(skb);
- return 0;
- }
+ len -= 2;
while (len > 0) {
- int optlen = ptr[1]+2;
+ int optlen = skb->nh.raw[off+1]+2;
- switch (ptr[0]) {
+ switch (skb->nh.raw[off]) {
case IPV6_TLV_PAD0:
optlen = 1;
break;
@@ -124,24 +126,30 @@
break;
default: /* Other TLV code so scan list */
+ if (optlen > len)
+ goto bad;
for (curr=procs; curr->type >= 0; curr++) {
- if (curr->type == ptr[0]) {
- if (curr->func(skb, ptr) == 0)
+ if (curr->type == skb->nh.raw[off]) {
+ /* type specific length/alignment
+ checks will be perfomed in the
+ func(). */
+ if (curr->func(skb, off) == 0)
return 0;
break;
}
}
if (curr->type < 0) {
- if (ip6_tlvopt_unknown(skb, ptr) == 0)
+ if (ip6_tlvopt_unknown(skb, off) == 0)
return 0;
}
break;
}
- ptr += optlen;
+ off += optlen;
len -= optlen;
}
if (len == 0)
return 1;
+bad:
kfree_skb(skb);
return 0;
}
@@ -155,37 +163,42 @@
{-1, NULL}
};
-static u8 *ipv6_dest_opt(struct sk_buff **skb_ptr, u8 *nhptr)
+static int ipv6_dest_opt(struct sk_buff **skb_ptr, int nhoff)
{
struct sk_buff *skb=*skb_ptr;
struct inet6_skb_parm *opt = (struct inet6_skb_parm *)skb->cb;
- struct ipv6_destopt_hdr *hdr = (struct ipv6_destopt_hdr *) skb->h.raw;
- opt->dst1 = (u8*)hdr - skb->nh.raw;
+ if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+8) ||
+ !pskb_may_pull(skb, (skb->h.raw-skb->data)+((skb->h.raw[1]+1)<<3))) {
+ kfree_skb(skb);
+ return -1;
+ }
+
+ opt->dst1 = skb->h.raw - skb->nh.raw;
- if (ip6_parse_tlv(tlvprocdestopt_lst, skb, nhptr)) {
- skb->h.raw += ((hdr->hdrlen+1)<<3);
- return &hdr->nexthdr;
+ if (ip6_parse_tlv(tlvprocdestopt_lst, skb)) {
+ skb->h.raw += ((skb->h.raw[1]+1)<<3);
+ return opt->dst1;
}
- return NULL;
+ return -1;
}
/********************************
NONE header. No data in packet.
********************************/
-static u8 *ipv6_nodata(struct sk_buff **skb_ptr, u8 *nhptr)
+static int ipv6_nodata(struct sk_buff **skb_ptr, int nhoff)
{
kfree_skb(*skb_ptr);
- return NULL;
+ return -1;
}
/********************************
Routing header.
********************************/
-static u8* ipv6_routing_header(struct sk_buff **skb_ptr, u8 *nhptr)
+static int ipv6_routing_header(struct sk_buff **skb_ptr, int nhoff)
{
struct sk_buff *skb = *skb_ptr;
struct inet6_skb_parm *opt = (struct inet6_skb_parm *)skb->cb;
@@ -194,36 +207,38 @@
int addr_type;
int n, i;
- struct ipv6_rt_hdr *hdr = (struct ipv6_rt_hdr *) skb->h.raw;
+ struct ipv6_rt_hdr *hdr;
struct rt0_hdr *rthdr;
- if (((hdr->hdrlen+1)<<3) > skb->tail - skb->h.raw) {
+ if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+8) ||
+ !pskb_may_pull(skb, (skb->h.raw-skb->data)+((skb->h.raw[1]+1)<<3))) {
IP6_INC_STATS_BH(Ip6InHdrErrors);
kfree_skb(skb);
- return NULL;
+ return -1;
+ }
+
+ hdr = (struct ipv6_rt_hdr *) skb->h.raw;
+
+ if ((ipv6_addr_type(&skb->nh.ipv6h->daddr)&IPV6_ADDR_MULTICAST) ||
+ skb->pkt_type != PACKET_HOST) {
+ kfree_skb(skb);
+ return -1;
}
looped_back:
if (hdr->segments_left == 0) {
- opt->srcrt = (u8*)hdr - skb->nh.raw;
+ opt->srcrt = skb->h.raw - skb->nh.raw;
skb->h.raw += (hdr->hdrlen + 1) << 3;
opt->dst0 = opt->dst1;
opt->dst1 = 0;
- return &hdr->nexthdr;
+ return (&hdr->nexthdr) - skb->nh.raw;
}
- if (hdr->type != IPV6_SRCRT_TYPE_0 || hdr->hdrlen & 0x01) {
- u8 *pos = (u8*) hdr;
-
- if (hdr->type != IPV6_SRCRT_TYPE_0)
- pos += 2;
- else
- pos += 1;
-
- icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, pos);
- return NULL;
+ if (hdr->type != IPV6_SRCRT_TYPE_0 || (hdr->hdrlen & 0x01)) {
+ icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, hdr->type != IPV6_SRCRT_TYPE_0 ? 2 : 1);
+ return -1;
}
-
+
/*
* This is the routing header forwarding algorithm from
* RFC 1883, page 17.
@@ -232,8 +247,8 @@
n = hdr->hdrlen >> 1;
if (hdr->segments_left > n) {
- icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, &hdr->segments_left);
- return NULL;
+ icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, (&hdr->segments_left) - skb->nh.raw);
+ return -1;
}
/* We are about to mangle packet header. Be careful!
@@ -243,12 +258,15 @@
struct sk_buff *skb2 = skb_copy(skb, GFP_ATOMIC);
kfree_skb(skb);
if (skb2 == NULL)
- return NULL;
+ return -1;
*skb_ptr = skb = skb2;
opt = (struct inet6_skb_parm *)skb2->cb;
hdr = (struct ipv6_rt_hdr *) skb2->h.raw;
}
+ if (skb->ip_summed == CHECKSUM_HW)
+ skb->ip_summed = CHECKSUM_NONE;
+
i = n - --hdr->segments_left;
rthdr = (struct rt0_hdr *) hdr;
@@ -257,9 +275,9 @@
addr_type = ipv6_addr_type(addr);
- if (addr_type == IPV6_ADDR_MULTICAST) {
+ if (addr_type&IPV6_ADDR_MULTICAST) {
kfree_skb(skb);
- return NULL;
+ return -1;
}
ipv6_addr_copy(&daddr, addr);
@@ -270,21 +288,21 @@
ip6_route_input(skb);
if (skb->dst->error) {
skb->dst->input(skb);
- return NULL;
+ return -1;
}
if (skb->dst->dev->flags&IFF_LOOPBACK) {
if (skb->nh.ipv6h->hop_limit <= 1) {
icmpv6_send(skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_HOPLIMIT,
0, skb->dev);
kfree_skb(skb);
- return NULL;
+ return -1;
}
skb->nh.ipv6h->hop_limit--;
goto looped_back;
}
skb->dst->input(skb);
- return NULL;
+ return -1;
}
/*
@@ -374,20 +392,30 @@
and opt->hdrlen is even. Shit! --ANK (980730)
*/
-static u8 *ipv6_auth_hdr(struct sk_buff **skb_ptr, u8 *nhptr)
+static int ipv6_auth_hdr(struct sk_buff **skb_ptr, int nhoff)
{
struct sk_buff *skb=*skb_ptr;
struct inet6_skb_parm *opt = (struct inet6_skb_parm *)skb->cb;
- struct ipv6_opt_hdr *hdr = (struct ipv6_opt_hdr *)skb->h.raw;
- int len = (hdr->hdrlen+2)<<2;
+ int len;
+
+ if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+8))
+ goto fail;
+
+ len = (skb->h.raw[1]+1)<<2;
if (len&7)
- return NULL;
- opt->auth = (u8*)hdr - skb->nh.raw;
- if (skb->h.raw + len > skb->tail)
- return NULL;
+ goto fail;
+
+ if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+len))
+ goto fail;
+
+ opt->auth = skb->h.raw - skb->nh.raw;
skb->h.raw += len;
- return &hdr->nexthdr;
+ return opt->auth;
+
+fail:
+ kfree_skb(skb);
+ return -1;
}
/* This list MUST NOT contain entry for NEXTHDR_HOP.
@@ -408,22 +436,22 @@
{-1, NULL}
};
-u8 *ipv6_parse_exthdrs(struct sk_buff **skb_in, u8 *nhptr)
+int ipv6_parse_exthdrs(struct sk_buff **skb_in, int nhoff)
{
struct hdrtype_proc *hdrt;
- u8 nexthdr = *nhptr;
+ u8 nexthdr = (*skb_in)->nh.raw[nhoff];
restart:
for (hdrt=hdrproc_lst; hdrt->type >= 0; hdrt++) {
if (hdrt->type == nexthdr) {
- if ((nhptr = hdrt->func(skb_in, nhptr)) != NULL) {
- nexthdr = *nhptr;
+ if ((nhoff = hdrt->func(skb_in, nhoff)) >= 0) {
+ nexthdr = (*skb_in)->nh.raw[nhoff];
goto restart;
}
- return NULL;
+ return -1;
}
}
- return nhptr;
+ return nhoff;
}
@@ -433,37 +461,37 @@
/* Router Alert as of draft-ietf-ipngwg-ipv6router-alert-04 */
-static int ipv6_hop_ra(struct sk_buff *skb, u8 *ptr)
+static int ipv6_hop_ra(struct sk_buff *skb, int optoff)
{
- if (ptr[1] == 2) {
- ((struct inet6_skb_parm*)skb->cb)->ra = ptr - skb->nh.raw;
+ if (skb->nh.raw[optoff+1] == 2) {
+ ((struct inet6_skb_parm*)skb->cb)->ra = optoff;
return 1;
}
if (net_ratelimit())
- printk(KERN_DEBUG "ipv6_hop_ra: wrong RA length %d\n", ptr[1]);
+ printk(KERN_DEBUG "ipv6_hop_ra: wrong RA length %d\n", skb->nh.raw[optoff+1]);
kfree_skb(skb);
return 0;
}
/* Jumbo payload */
-static int ipv6_hop_jumbo(struct sk_buff *skb, u8 *ptr)
+static int ipv6_hop_jumbo(struct sk_buff *skb, int optoff)
{
u32 pkt_len;
- if (ptr[1] != 4 || ((ptr-skb->nh.raw)&3) != 2) {
+ if (skb->nh.raw[optoff+1] != 4 || (optoff&3) != 2) {
if (net_ratelimit())
- printk(KERN_DEBUG "ipv6_hop_jumbo: wrong jumbo opt length/alignment %d\n", ptr[1]);
+ printk(KERN_DEBUG "ipv6_hop_jumbo: wrong jumbo opt length/alignment %d\n", skb->nh.raw[optoff+1]);
goto drop;
}
- pkt_len = ntohl(*(u32*)(ptr+2));
+ pkt_len = ntohl(*(u32*)(skb->nh.raw+optoff+2));
if (pkt_len < 0x10000) {
- icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, ptr+2);
+ icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff+2);
return 0;
}
if (skb->nh.ipv6h->payload_len) {
- icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, ptr);
+ icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff);
return 0;
}
@@ -471,7 +499,11 @@
IP6_INC_STATS_BH(Ip6InTruncatedPkts);
goto drop;
}
- skb_trim(skb, pkt_len + sizeof(struct ipv6hdr));
+ if (pkt_len + sizeof(struct ipv6hdr) < skb->len) {
+ __pskb_trim(skb, pkt_len + sizeof(struct ipv6hdr));
+ if (skb->ip_summed == CHECKSUM_HW)
+ skb->ip_summed = CHECKSUM_NONE;
+ }
return 1;
drop:
@@ -485,12 +517,12 @@
{-1, NULL}
};
-u8 * ipv6_parse_hopopts(struct sk_buff *skb, u8 *nhptr)
+int ipv6_parse_hopopts(struct sk_buff *skb, int nhoff)
{
((struct inet6_skb_parm*)skb->cb)->hop = sizeof(struct ipv6hdr);
- if (ip6_parse_tlv(tlvprochopopt_lst, skb, nhptr))
- return nhptr+((nhptr[1]+1)<<3);
- return NULL;
+ if (ip6_parse_tlv(tlvprochopopt_lst, skb))
+ return sizeof(struct ipv6hdr);
+ return -1;
}
/*
@@ -684,7 +716,7 @@
* find out if nexthdr is a well-known extension header or a protocol
*/
-static __inline__ int ipv6_ext_hdr(u8 nexthdr)
+int ipv6_ext_hdr(u8 nexthdr)
{
/*
* find out if nexthdr is an extension header or a protocol
@@ -739,33 +771,36 @@
* --ANK (980726)
*/
-u8 *ipv6_skip_exthdr(struct ipv6_opt_hdr *hdr, u8 *nexthdrp, int len)
+int ipv6_skip_exthdr(struct sk_buff *skb, int start, u8 *nexthdrp, int len)
{
u8 nexthdr = *nexthdrp;
while (ipv6_ext_hdr(nexthdr)) {
- int hdrlen;
+ struct ipv6_opt_hdr hdr;
+ int hdrlen;
- if (len < sizeof(struct ipv6_opt_hdr))
- return NULL;
+ if (len < (int)sizeof(struct ipv6_opt_hdr))
+ return -1;
if (nexthdr == NEXTHDR_NONE)
- return NULL;
+ return -1;
+ if (skb_copy_bits(skb, start, &hdr, sizeof(hdr)))
+ BUG();
if (nexthdr == NEXTHDR_FRAGMENT) {
- struct frag_hdr *fhdr = (struct frag_hdr *) hdr;
+ struct frag_hdr *fhdr = (struct frag_hdr *) &hdr;
if (ntohs(fhdr->frag_off) & ~0x7)
break;
hdrlen = 8;
} else if (nexthdr == NEXTHDR_AUTH)
- hdrlen = (hdr->hdrlen+2)<<2;
+ hdrlen = (hdr.hdrlen+2)<<2;
else
- hdrlen = ipv6_optlen(hdr);
+ hdrlen = ipv6_optlen(&hdr);
- nexthdr = hdr->nexthdr;
- hdr = (struct ipv6_opt_hdr *) ((u8*)hdr + hdrlen);
+ nexthdr = hdr.nexthdr;
len -= hdrlen;
+ start += hdrlen;
}
*nexthdrp = nexthdr;
- return (u8*)hdr;
+ return start;
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)