summaryrefslogtreecommitdiff
path: root/net/ipv6/ip6_input.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv6/ip6_input.c')
-rw-r--r--net/ipv6/ip6_input.c62
1 files changed, 42 insertions, 20 deletions
diff --git a/net/ipv6/ip6_input.c b/net/ipv6/ip6_input.c
index c4e3f6f1a99a..baa3321be0c8 100644
--- a/net/ipv6/ip6_input.c
+++ b/net/ipv6/ip6_input.c
@@ -6,7 +6,7 @@
* Pedro Roque <roque@di.fc.ul.pt>
* Ian P. Morris <I.P.Morris@soton.ac.uk>
*
- * $Id: ip6_input.c,v 1.18 2000/12/08 17:15:54 davem Exp $
+ * $Id: ip6_input.c,v 1.19 2000/12/13 18:31:50 davem Exp $
*
* Based in linux/net/ipv4/ip_input.c
*
@@ -44,7 +44,6 @@
static inline int ip6_rcv_finish( struct sk_buff *skb)
{
-
if (skb->dst == NULL)
ip6_route_input(skb);
@@ -69,9 +68,15 @@ int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt
*/
((struct inet6_skb_parm *)skb->cb)->iif = dev->ifindex;
+ if (skb->len < sizeof(struct ipv6hdr))
+ goto err;
+
+ if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
+ goto drop;
+
hdr = skb->nh.ipv6h;
- if (skb->len < sizeof(struct ipv6hdr) || hdr->version != 6)
+ if (hdr->version != 6)
goto err;
pkt_len = ntohs(hdr->payload_len);
@@ -80,16 +85,24 @@ int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt
if (pkt_len || hdr->nexthdr != NEXTHDR_HOP) {
if (pkt_len + sizeof(struct ipv6hdr) > skb->len)
goto truncated;
- skb_trim(skb, pkt_len + sizeof(struct ipv6hdr));
+ if (pkt_len + sizeof(struct ipv6hdr) < skb->len) {
+ if (__pskb_trim(skb, pkt_len + sizeof(struct ipv6hdr)))
+ goto drop;
+ hdr = skb->nh.ipv6h;
+ if (skb->ip_summed == CHECKSUM_HW)
+ skb->ip_summed = CHECKSUM_NONE;
+ }
}
if (hdr->nexthdr == NEXTHDR_HOP) {
skb->h.raw = (u8*)(hdr+1);
- if (!ipv6_parse_hopopts(skb, &hdr->nexthdr)) {
+ if (ipv6_parse_hopopts(skb, offsetof(struct ipv6hdr, nexthdr)) < 0) {
IP6_INC_STATS_BH(Ip6InHdrErrors);
return 0;
}
+ hdr = skb->nh.ipv6h;
}
+
return NF_HOOK(PF_INET6,NF_IP6_PRE_ROUTING, skb, dev, NULL, ip6_rcv_finish);
truncated:
IP6_INC_STATS_BH(Ip6InTruncatedPkts);
@@ -111,12 +124,11 @@ static inline int ip6_input_finish(struct sk_buff *skb)
struct ipv6hdr *hdr = skb->nh.ipv6h;
struct inet6_protocol *ipprot;
struct sock *raw_sk;
- __u8 *nhptr;
+ int nhoff;
int nexthdr;
int found = 0;
u8 hash;
- int len;
-
+
skb->h.raw = skb->nh.raw + sizeof(struct ipv6hdr);
/*
@@ -124,13 +136,13 @@ static inline int ip6_input_finish(struct sk_buff *skb)
*/
nexthdr = hdr->nexthdr;
- nhptr = &hdr->nexthdr;
+ nhoff = offsetof(struct ipv6hdr, nexthdr);
/* Skip hop-by-hop options, they are already parsed. */
if (nexthdr == NEXTHDR_HOP) {
- nhptr = (u8*)(hdr+1);
- nexthdr = *nhptr;
- skb->h.raw += (nhptr[1]+1)<<3;
+ nhoff = sizeof(struct ipv6hdr);
+ nexthdr = skb->h.raw[0];
+ skb->h.raw += (skb->h.raw[1]+1)<<3;
}
/* This check is sort of optimization.
@@ -138,17 +150,23 @@ static inline int ip6_input_finish(struct sk_buff *skb)
which are missing with probability of 200%
*/
if (nexthdr != IPPROTO_TCP && nexthdr != IPPROTO_UDP) {
- nhptr = ipv6_parse_exthdrs(&skb, nhptr);
- if (nhptr == NULL)
+ nhoff = ipv6_parse_exthdrs(&skb, nhoff);
+ if (nhoff < 0)
return 0;
- nexthdr = *nhptr;
+ nexthdr = skb->nh.raw[nhoff];
hdr = skb->nh.ipv6h;
}
- len = skb->tail - skb->h.raw;
+
+ if (!pskb_pull(skb, skb->h.raw - skb->data))
+ goto discard;
+
+ if (skb->ip_summed == CHECKSUM_HW)
+ skb->csum = csum_sub(skb->csum,
+ csum_partial(skb->nh.raw, skb->h.raw-skb->nh.raw, 0));
raw_sk = raw_v6_htable[nexthdr&(MAX_INET_PROTOS-1)];
if (raw_sk)
- raw_sk = ipv6_raw_deliver(skb, nexthdr, len);
+ raw_sk = ipv6_raw_deliver(skb, nexthdr);
hash = nexthdr & (MAX_INET_PROTOS - 1);
for (ipprot = (struct inet6_protocol *) inet6_protos[hash];
@@ -163,12 +181,12 @@ static inline int ip6_input_finish(struct sk_buff *skb)
buff = skb_clone(skb, GFP_ATOMIC);
if (buff)
- ipprot->handler(buff, len);
+ ipprot->handler(buff);
found = 1;
}
if (raw_sk) {
- rawv6_rcv(raw_sk, skb, len);
+ rawv6_rcv(raw_sk, skb);
sock_put(raw_sk);
found = 1;
}
@@ -178,10 +196,14 @@ static inline int ip6_input_finish(struct sk_buff *skb)
*/
if (!found) {
IP6_INC_STATS_BH(Ip6InUnknownProtos);
- icmpv6_param_prob(skb, ICMPV6_UNK_NEXTHDR, nhptr);
+ icmpv6_param_prob(skb, ICMPV6_UNK_NEXTHDR, nhoff);
}
return 0;
+
+discard:
+ kfree_skb(skb);
+ return 0;
}