]> git.neil.brown.name Git - history.git/commitdiff
[IPV6]: Fix packet quoting in icmpv6_send().
authorDavid S. Miller <davem@nuts.ninka.net>
Tue, 11 Nov 2003 17:34:37 +0000 (09:34 -0800)
committerDavid S. Miller <davem@nuts.ninka.net>
Tue, 11 Nov 2003 17:34:37 +0000 (09:34 -0800)
- 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)

net/ipv6/icmp.c

index 50d59d628c8ebecdcae1b7bd8d16daff117a85b0..47c773922e13f1c5068f0e5d97696014b24e1139 100644 (file)
@@ -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);