This patch fix the fragmentation on sending side according to rfc4944.

Also add improvement to use the full payload of a PDU which calculate the
nearest divided to 8 payload length for the fragmentation datagram
offset attribute.

The main issue is that the datagram_size of fragmentation header use the
ipv6 payload length, but rfc4944 says it's the ipv6 payload length inclusive
network header size (and transport header size if compressed).

Signed-off-by: Alexander Aring <alex.ar...@gmail.com>
---
 net/ieee802154/6lowpan.c | 42 ++++++++++++++++++++++++++++++++----------
 1 file changed, 32 insertions(+), 10 deletions(-)

diff --git a/net/ieee802154/6lowpan.c b/net/ieee802154/6lowpan.c
index 426b5df..7182072 100644
--- a/net/ieee802154/6lowpan.c
+++ b/net/ieee802154/6lowpan.c
@@ -374,6 +374,7 @@ lowpan_compress_udp_header(u8 **hc06_ptr, struct sk_buff 
*skb)
        memcpy(*hc06_ptr, &uh->check, 2);
        *hc06_ptr += 2;
 
+       mac_cb(skb)->frag_info.d_offset += sizeof(struct udphdr);
        /* skip the UDP header */
        skb_pull(skb, sizeof(struct udphdr));
 }
@@ -484,6 +485,7 @@ static int lowpan_header_create(struct sk_buff *skb,
        if (type != ETH_P_IPV6)
                return 0;
 
+       mac_cb(skb)->frag_info.d_offset = 0;
        hdr = ipv6_hdr(skb);
        hc06_ptr = head + 2;
 
@@ -653,6 +655,11 @@ static int lowpan_header_create(struct sk_buff *skb,
        head[0] = iphc0;
        head[1] = iphc1;
 
+       /* Before replace ipv6hdr to 6lowpan header we save the dgram_size */
+       mac_cb(skb)->frag_info.d_size = ntohs(hdr->payload_len) +
+                                       sizeof(struct ipv6hdr);
+       mac_cb(skb)->frag_info.d_offset += sizeof(struct ipv6hdr);
+
        skb_pull(skb, sizeof(struct ipv6hdr));
        skb_reset_transport_header(skb);
        memcpy(skb_push(skb, hc06_ptr - head), head, hc06_ptr - head);
@@ -1141,44 +1148,58 @@ lowpan_fragment_xmit(struct sk_buff *skb, u8 *head,
 static int
 lowpan_skb_fragmentation(struct sk_buff *skb, struct net_device *dev)
 {
-       int  err, header_length, payload_length, tag, offset = 0;
+       int err, header_length, payload_length, tag, dgram_size,
+           dgram_offset, lowpan_size, frag_plen, offset = 0;
        u8 head[5];
 
        header_length = skb->mac_len;
        payload_length = skb->len - header_length;
        tag = lowpan_dev_info(dev)->fragment_tag++;
+       lowpan_size = skb_network_header_len(skb);
+       dgram_size = mac_cb(skb)->frag_info.d_size;
 
        /* first fragment header */
-       head[0] = LOWPAN_DISPATCH_FRAG1 | ((payload_length >> 8) & 0x7);
-       head[1] = payload_length & 0xff;
+       head[0] = LOWPAN_DISPATCH_FRAG1 | ((dgram_size >> 8) & 0x7);
+       head[1] = dgram_size & 0xff;
        head[2] = tag >> 8;
        head[3] = tag & 0xff;
 
-       err = lowpan_fragment_xmit(skb, head, header_length, LOWPAN_FRAG_SIZE,
-                                  0, LOWPAN_DISPATCH_FRAG1);
+       /* calc the nearest payload length(divided to 8) for first fragment
+        * which fits into a IEEE802154_MTU
+        */
+       frag_plen = round_down(IEEE802154_MTU - header_length -
+                              LOWPAN_FRAG1_HEAD_SIZE - lowpan_size -
+                              IEEE802154_MFR_SIZE, 8);
 
+       err = lowpan_fragment_xmit(skb, head, header_length,
+                                  frag_plen + lowpan_size, 0,
+                                  LOWPAN_DISPATCH_FRAG1);
        if (err) {
                pr_debug("%s unable to send FRAG1 packet (tag: %d)",
                         __func__, tag);
                goto exit;
        }
 
-       offset = LOWPAN_FRAG_SIZE;
+       offset = lowpan_size + frag_plen;
+       dgram_offset = mac_cb(skb)->frag_info.d_offset + frag_plen;
 
        /* next fragment header */
        head[0] &= ~LOWPAN_DISPATCH_FRAG1;
        head[0] |= LOWPAN_DISPATCH_FRAGN;
 
+       frag_plen = round_down(IEEE802154_MTU - header_length -
+                              LOWPAN_FRAGN_HEAD_SIZE - IEEE802154_MFR_SIZE, 8);
+
        while (payload_length - offset > 0) {
-               int len = LOWPAN_FRAG_SIZE;
+               int len = frag_plen;
 
-               head[4] = offset / 8;
+               head[4] = dgram_offset / 8;
 
                if (payload_length - offset < len)
                        len = payload_length - offset;
 
-               err = lowpan_fragment_xmit(skb, head, header_length,
-                                          len, offset, LOWPAN_DISPATCH_FRAGN);
+               err = lowpan_fragment_xmit(skb, head, header_length, len,
+                                          offset, LOWPAN_DISPATCH_FRAGN);
                if (err) {
                        pr_debug("%s unable to send a subsequent FRAGN packet "
                                 "(tag: %d, offset: %d", __func__, tag, offset);
@@ -1186,6 +1207,7 @@ lowpan_skb_fragmentation(struct sk_buff *skb, struct 
net_device *dev)
                }
 
                offset += len;
+               dgram_offset += len;
        }
 
 exit:
-- 
1.8.4.2


------------------------------------------------------------------------------
Sponsored by Intel(R) XDK 
Develop, test and display web and hybrid apps with a single code base.
Download it for free now!
http://pubads.g.doubleclick.net/gampad/clk?id=111408631&iu=/4140/ostg.clktrk
_______________________________________________
Linux-zigbee-devel mailing list
Linux-zigbee-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linux-zigbee-devel

Reply via email to