6lowpan: fix first fragment (FRAG1) handling
authorTony Cheneau <tony.cheneau@amnesiak.org>
Mon, 25 Mar 2013 17:59:26 +0000 (17:59 +0000)
committerDavid S. Miller <davem@davemloft.net>
Tue, 26 Mar 2013 16:37:57 +0000 (12:37 -0400)
The first fragment, FRAG1, must contain some payload according to the
specs. However, as it is currently written, the first fragment will
remain empty and only contain the 6lowpan headers.

This patch also extracts the transport layer information from the first
fragment. This information is used later on when uncompressing UDP
header.

Thanks to Wolf-Bastian Pöttner for noticing that the offset value was
not properly initialized.

Signed-off-by: Tony Cheneau <tony.cheneau@amnesiak.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/ieee802154/6lowpan.c

index 0eebb960e11d8555ed5af7405c4df601999f80a0..4a62289669b10a129a8e7f9737eeedbc1269573f 100644 (file)
@@ -661,7 +661,7 @@ static void lowpan_fragment_timer_expired(unsigned long entry_addr)
 }
 
 static struct lowpan_fragment *
-lowpan_alloc_new_frame(struct sk_buff *skb, u8 len, u16 tag)
+lowpan_alloc_new_frame(struct sk_buff *skb, u16 len, u16 tag)
 {
        struct lowpan_fragment *frame;
 
@@ -731,7 +731,7 @@ lowpan_process_data(struct sk_buff *skb)
        {
                struct lowpan_fragment *frame;
                /* slen stores the rightmost 8 bits of the 11 bits length */
-               u8 slen, offset;
+               u8 slen, offset = 0;
                u16 len, tag;
                bool found = false;
 
@@ -742,6 +742,12 @@ lowpan_process_data(struct sk_buff *skb)
                /* adds the 3 MSB to the 8 LSB to retrieve the 11 bits length */
                len = ((iphc0 & 7) << 8) | slen;
 
+               /* FRAGN */
+               if ((iphc0 & LOWPAN_DISPATCH_MASK) != LOWPAN_DISPATCH_FRAG1) {
+                       if (lowpan_fetch_skb_u8(skb, &offset))
+                               goto unlock_and_drop;
+               }
+
                /*
                 * check if frame assembling with the same tag is
                 * already in progress
@@ -761,12 +767,6 @@ lowpan_process_data(struct sk_buff *skb)
                                goto unlock_and_drop;
                }
 
-               if ((iphc0 & LOWPAN_DISPATCH_MASK) == LOWPAN_DISPATCH_FRAG1)
-                       goto unlock_and_drop;
-
-               if (lowpan_fetch_skb_u8(skb, &offset)) /* fetch offset */
-                       goto unlock_and_drop;
-
                /* if payload fits buffer, copy it */
                if (likely((offset * 8 + skb->len) <= frame->length))
                        skb_copy_to_linear_data_offset(frame->skb, offset * 8,
@@ -982,13 +982,13 @@ static int lowpan_get_mac_header_length(struct sk_buff *skb)
 
 static int
 lowpan_fragment_xmit(struct sk_buff *skb, u8 *head,
-                       int mlen, int plen, int offset)
+                       int mlen, int plen, int offset, int type)
 {
        struct sk_buff *frag;
        int hlen, ret;
 
-       /* if payload length is zero, therefore it's a first fragment */
-       hlen = (plen == 0 ? LOWPAN_FRAG1_HEAD_SIZE :  LOWPAN_FRAGN_HEAD_SIZE);
+       hlen = (type == LOWPAN_DISPATCH_FRAG1) ?
+                       LOWPAN_FRAG1_HEAD_SIZE : LOWPAN_FRAGN_HEAD_SIZE;
 
        lowpan_raw_dump_inline(__func__, "6lowpan fragment header", head, hlen);
 
@@ -1031,7 +1031,13 @@ lowpan_skb_fragmentation(struct sk_buff *skb)
        head[2] = tag >> 8;
        head[3] = tag & 0xff;
 
-       err = lowpan_fragment_xmit(skb, head, header_length, 0, 0);
+       err = lowpan_fragment_xmit(skb, head, header_length, LOWPAN_FRAG_SIZE,
+                                  0, LOWPAN_DISPATCH_FRAG1);
+
+       if (err)
+               goto exit;
+
+       offset = LOWPAN_FRAG_SIZE;
 
        /* next fragment header */
        head[0] &= ~LOWPAN_DISPATCH_FRAG1;
@@ -1046,10 +1052,14 @@ lowpan_skb_fragmentation(struct sk_buff *skb)
                        len = payload_length - offset;
 
                err = lowpan_fragment_xmit(skb, head, header_length,
-                                                       len, offset);
+                                          len, offset, LOWPAN_DISPATCH_FRAGN);
+               if (err)
+                       goto exit;
+
                offset += len;
        }
 
+exit:
        return err;
 }