From: David S. Miller Date: Tue, 11 Nov 2003 17:34:37 +0000 (-0800) Subject: [IPV6]: Fix packet quoting in icmpv6_send(). X-Git-Tag: v2.6.0-test10~48^2~1 X-Git-Url: http://git.neil.brown.name/?a=commitdiff_plain;h=821ea9309d173e5c5a01628c0bd4567d505ba256;p=history.git [IPV6]: Fix packet quoting in icmpv6_send(). - Mucking with the original skb pointers with push/pull around the packet quoting was wrong, muching with these pointers could cause problems with others using the SKB. It was also buggy, it only handled the case where skb->nh.raw was ahead of or equal to skb->data - The fix is to record skb->nh.raw - skb->data and use this as a base offset in calls to skb_copy_and_csum_bits(). This is what the pre-IPSEC code did. This fixes IPV6 oopses and packet corruption on 64-bit platforms when sending UDP port unreachable ICMP messages. Reported and analyzed by Jan Oravec (jan.oravec@6com.sk) --- diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index 50d59d628c8e..47c773922e13 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -86,15 +86,6 @@ static struct inet6_protocol icmpv6_protocol = { .flags = INET6_PROTO_FINAL, }; -struct icmpv6_msg { - struct icmp6hdr icmph; - struct sk_buff *skb; - int offset; - struct in6_addr *daddr; - int len; - __u32 csum; -}; - static __inline__ int icmpv6_xmit_lock(void) { local_bh_disable(); @@ -258,11 +249,19 @@ out: return err; } +struct icmpv6_msg { + struct sk_buff *skb; + int offset; +}; + static int icmpv6_getfrag(void *from, char *to, int offset, int len, int odd, struct sk_buff *skb) { - struct sk_buff *org_skb = (struct sk_buff *)from; + struct icmpv6_msg *msg = (struct icmpv6_msg *) from; + struct sk_buff *org_skb = msg->skb; __u32 csum = 0; - csum = skb_copy_and_csum_bits(org_skb, offset, to, len, csum); + + csum = skb_copy_and_csum_bits(org_skb, msg->offset + offset, + to, len, csum); skb->csum = csum_block_add(skb->csum, csum, odd); return 0; } @@ -281,9 +280,10 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info, struct dst_entry *dst; struct icmp6hdr tmp_hdr; struct flowi fl; + struct icmpv6_msg msg; int iif = 0; int addr_type = 0; - int len, plen; + int len; int hlimit = -1; int err = 0; @@ -379,27 +379,29 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info, hlimit = dst_metric(dst, RTAX_HOPLIMIT); } - plen = skb->nh.raw - skb->data; - __skb_pull(skb, plen); + msg.skb = skb; + msg.offset = skb->nh.raw - skb->data; + len = skb->len; len = min_t(unsigned int, len, IPV6_MIN_MTU - sizeof(struct ipv6hdr) -sizeof(struct icmp6hdr)); if (len < 0) { if (net_ratelimit()) printk(KERN_DEBUG "icmp: len problem\n"); - __skb_push(skb, plen); goto out_dst_release; } idev = in6_dev_get(skb->dev); - err = ip6_append_data(sk, icmpv6_getfrag, skb, len + sizeof(struct icmp6hdr), sizeof(struct icmp6hdr), - hlimit, NULL, &fl, (struct rt6_info*)dst, MSG_DONTWAIT); + err = ip6_append_data(sk, icmpv6_getfrag, &msg, + len + sizeof(struct icmp6hdr), + sizeof(struct icmp6hdr), + hlimit, NULL, &fl, (struct rt6_info*)dst, + MSG_DONTWAIT); if (err) { ip6_flush_pending_frames(sk); goto out_put; } err = icmpv6_push_pending_frames(sk, &fl, &tmp_hdr, len + sizeof(struct icmp6hdr)); - __skb_push(skb, plen); if (type >= ICMPV6_DEST_UNREACH && type <= ICMPV6_PARAMPROB) ICMP6_INC_STATS_OFFSET_BH(idev, Icmp6OutDestUnreachs, type - ICMPV6_DEST_UNREACH); @@ -423,6 +425,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb) struct icmp6hdr *icmph = (struct icmp6hdr *) skb->h.raw; struct icmp6hdr tmp_hdr; struct flowi fl; + struct icmpv6_msg msg; struct dst_entry *dst; int err = 0; int hlimit = -1; @@ -464,7 +467,10 @@ static void icmpv6_echo_reply(struct sk_buff *skb) idev = in6_dev_get(skb->dev); - err = ip6_append_data(sk, icmpv6_getfrag, skb, skb->len + sizeof(struct icmp6hdr), + msg.skb = skb; + msg.offset = 0; + + err = ip6_append_data(sk, icmpv6_getfrag, &msg, skb->len + sizeof(struct icmp6hdr), sizeof(struct icmp6hdr), hlimit, NULL, &fl, (struct rt6_info*)dst, MSG_DONTWAIT);