From 8d879a3f9856fe6c6e27853a96c0beaed03acb2c Mon Sep 17 00:00:00 2001 From: Tony Cheneau Date: Mon, 25 Mar 2013 17:59:21 +0000 Subject: [PATCH 01/12] 6lowpan: lowpan_is_iid_16_bit_compressable() does not detect compressible address correctly The current test is not RFC6282 compliant. The same issue has been found and fixed in Contiki. This patch is basically a port of their fix. Signed-off-by: Tony Cheneau Signed-off-by: David S. Miller --- net/ieee802154/6lowpan.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/net/ieee802154/6lowpan.h b/net/ieee802154/6lowpan.h index bba5f8336317..4b8f917658b5 100644 --- a/net/ieee802154/6lowpan.h +++ b/net/ieee802154/6lowpan.h @@ -92,9 +92,10 @@ */ #define lowpan_is_iid_16_bit_compressable(a) \ ((((a)->s6_addr16[4]) == 0) && \ - (((a)->s6_addr16[5]) == 0) && \ - (((a)->s6_addr16[6]) == 0) && \ - ((((a)->s6_addr[14]) & 0x80) == 0)) + (((a)->s6_addr[10]) == 0) && \ + (((a)->s6_addr[11]) == 0xff) && \ + (((a)->s6_addr[12]) == 0xfe) && \ + (((a)->s6_addr[13]) == 0)) /* multicast address */ #define is_addr_mcast(a) (((a)->s6_addr[0]) == 0xFF) From f5c20f58d950002a4a5a77f60484a51e5af6498c Mon Sep 17 00:00:00 2001 From: Tony Cheneau Date: Mon, 25 Mar 2013 17:59:22 +0000 Subject: [PATCH 02/12] 6lowpan: next header is not properly set upon decompression of a UDP header. This causes a drop of the UDP packet. Signed-off-by: Tony Cheneau Signed-off-by: David S. Miller --- net/ieee802154/6lowpan.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/ieee802154/6lowpan.c b/net/ieee802154/6lowpan.c index 43b95ca61114..9f53904b5261 100644 --- a/net/ieee802154/6lowpan.c +++ b/net/ieee802154/6lowpan.c @@ -918,9 +918,11 @@ lowpan_process_data(struct sk_buff *skb) } /* UDP data uncompression */ - if (iphc0 & LOWPAN_IPHC_NH_C) + if (iphc0 & LOWPAN_IPHC_NH_C) { if (lowpan_uncompress_udp_header(skb)) goto drop; + hdr.nexthdr = UIP_PROTO_UDP; + } /* Not fragmented package */ hdr.payload_len = htons(skb->len); From f333a15a3eaf831067f9bfed35f1bb5fe0670b9f Mon Sep 17 00:00:00 2001 From: Tony Cheneau Date: Mon, 25 Mar 2013 17:59:23 +0000 Subject: [PATCH 03/12] 6lowpan: always enable link-layer acknowledgments This feature is especially important when using fragmentation, because the reassembly mechanism cannot recover from the loss of a fragment. Note that some hardware ignore this flag and not will not transmit acknowledgments even if this is set. Signed-off-by: Tony Cheneau Signed-off-by: David S. Miller --- net/ieee802154/6lowpan.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/ieee802154/6lowpan.c b/net/ieee802154/6lowpan.c index 9f53904b5261..e7f61de2459a 100644 --- a/net/ieee802154/6lowpan.c +++ b/net/ieee802154/6lowpan.c @@ -584,6 +584,10 @@ static int lowpan_header_create(struct sk_buff *skb, mac_cb(skb)->flags = IEEE802154_FC_TYPE_DATA; + /* request acknowledgment when possible */ + if (!lowpan_is_addr_broadcast(daddr)) + mac_cb(skb)->flags |= MAC_CB_FLAG_ACKREQ; + return dev_hard_header(skb, lowpan_dev_info(dev)->real_dev, type, (void *)&da, (void *)&sa, skb->len); } From cf692061d0d575f1b9b614555ca392d8b8eabab3 Mon Sep 17 00:00:00 2001 From: Tony Cheneau Date: Mon, 25 Mar 2013 17:59:24 +0000 Subject: [PATCH 04/12] mac802154: turn on ACK when enabled by the upper layers Signed-off-by: Tony Cheneau Signed-off-by: David S. Miller --- net/mac802154/wpan.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/mac802154/wpan.c b/net/mac802154/wpan.c index d20c6d3c247d..7d3f6594ed4f 100644 --- a/net/mac802154/wpan.c +++ b/net/mac802154/wpan.c @@ -145,6 +145,8 @@ static int mac802154_header_create(struct sk_buff *skb, head[pos++] = mac_cb(skb)->seq; /* DSN/BSN */ fc = mac_cb_type(skb); + if (mac_cb_is_ackreq(skb)) + fc |= IEEE802154_FC_ACK_REQ; if (!saddr) { spin_lock_bh(&priv->mib_lock); From 58ef67c318ac1e83a7a333f66403d777e7447461 Mon Sep 17 00:00:00 2001 From: Tony Cheneau Date: Mon, 25 Mar 2013 17:59:25 +0000 Subject: [PATCH 05/12] 6lowpan: use short IEEE 802.15.4 addresses for broadcast destination The IEEE 802.15.4 standard uses the 0xFFFF short address (2 bytes) for message broadcasting. Signed-off-by: Tony Cheneau Signed-off-by: David S. Miller --- net/ieee802154/6lowpan.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/net/ieee802154/6lowpan.c b/net/ieee802154/6lowpan.c index e7f61de2459a..0eebb960e11d 100644 --- a/net/ieee802154/6lowpan.c +++ b/net/ieee802154/6lowpan.c @@ -572,21 +572,28 @@ static int lowpan_header_create(struct sk_buff *skb, * this isn't implemented in mainline yet, so currently we assign 0xff */ { + mac_cb(skb)->flags = IEEE802154_FC_TYPE_DATA; + /* prepare wpan address data */ sa.addr_type = IEEE802154_ADDR_LONG; sa.pan_id = 0xff; - - da.addr_type = IEEE802154_ADDR_LONG; - da.pan_id = 0xff; - - memcpy(&(da.hwaddr), daddr, 8); memcpy(&(sa.hwaddr), saddr, 8); - mac_cb(skb)->flags = IEEE802154_FC_TYPE_DATA; + da.pan_id = 0xff; + /* + * if the destination address is the broadcast address, use the + * corresponding short address + */ + if (lowpan_is_addr_broadcast(daddr)) { + da.addr_type = IEEE802154_ADDR_SHORT; + da.short_addr = IEEE802154_ADDR_BROADCAST; + } else { + da.addr_type = IEEE802154_ADDR_LONG; + memcpy(&(da.hwaddr), daddr, 8); - /* request acknowledgment when possible */ - if (!lowpan_is_addr_broadcast(daddr)) + /* request acknowledgment */ mac_cb(skb)->flags |= MAC_CB_FLAG_ACKREQ; + } return dev_hard_header(skb, lowpan_dev_info(dev)->real_dev, type, (void *)&da, (void *)&sa, skb->len); From d991b98f5006e36b7ee9f8ef89ed3a8636692d69 Mon Sep 17 00:00:00 2001 From: Tony Cheneau Date: Mon, 25 Mar 2013 17:59:26 +0000 Subject: [PATCH 06/12] 6lowpan: fix first fragment (FRAG1) handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Signed-off-by: David S. Miller --- net/ieee802154/6lowpan.c | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/net/ieee802154/6lowpan.c b/net/ieee802154/6lowpan.c index 0eebb960e11d..4a62289669b1 100644 --- a/net/ieee802154/6lowpan.c +++ b/net/ieee802154/6lowpan.c @@ -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; } From 9da2924c4ba8da5f41285c98eb1ba9aee99344a4 Mon Sep 17 00:00:00 2001 From: Tony Cheneau Date: Mon, 25 Mar 2013 17:59:27 +0000 Subject: [PATCH 07/12] 6lowpan: add debug messages for 6LoWPAN fragmentation Add pr_debug() call in order to debug 6LoWPAN fragmentation and reassembly. Signed-off-by: Tony Cheneau Signed-off-by: David S. Miller --- net/ieee802154/6lowpan.c | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/net/ieee802154/6lowpan.c b/net/ieee802154/6lowpan.c index 4a62289669b1..61eee9d0dd21 100644 --- a/net/ieee802154/6lowpan.c +++ b/net/ieee802154/6lowpan.c @@ -742,10 +742,16 @@ 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 ((iphc0 & LOWPAN_DISPATCH_MASK) == LOWPAN_DISPATCH_FRAG1) { + pr_debug("%s received a FRAG1 packet (tag: %d, " + "size of the entire IP packet: %d)", + __func__, tag, len); + } else { /* FRAGN */ if (lowpan_fetch_skb_u8(skb, &offset)) goto unlock_and_drop; + pr_debug("%s received a FRAGN packet (tag: %d, " + "size of the entire IP packet: %d, " + "offset: %d)", __func__, tag, len, offset * 8); } /* @@ -762,6 +768,8 @@ lowpan_process_data(struct sk_buff *skb) /* alloc new frame structure */ if (!found) { + pr_debug("%s first fragment received for tag %d, " + "begin packet reassembly", __func__, tag); frame = lowpan_alloc_new_frame(skb, len, tag); if (!frame) goto unlock_and_drop; @@ -784,6 +792,9 @@ lowpan_process_data(struct sk_buff *skb) list_del(&frame->list); spin_unlock_bh(&flist_lock); + pr_debug("%s successfully reassembled fragment " + "(tag %d)", __func__, tag); + dev_kfree_skb(skb); skb = frame->skb; kfree(frame); @@ -1034,8 +1045,11 @@ lowpan_skb_fragmentation(struct sk_buff *skb) err = lowpan_fragment_xmit(skb, head, header_length, LOWPAN_FRAG_SIZE, 0, LOWPAN_DISPATCH_FRAG1); - if (err) + if (err) { + pr_debug("%s unable to send FRAG1 packet (tag: %d)", + __func__, tag); goto exit; + } offset = LOWPAN_FRAG_SIZE; @@ -1053,8 +1067,11 @@ lowpan_skb_fragmentation(struct sk_buff *skb) err = lowpan_fragment_xmit(skb, head, header_length, len, offset, LOWPAN_DISPATCH_FRAGN); - if (err) + if (err) { + pr_debug("%s unable to send a subsequent FRAGN packet " + "(tag: %d, offset: %d", __func__, tag, offset); goto exit; + } offset += len; } From d4ac32365dcbfd341a87eae444c26679f889249a Mon Sep 17 00:00:00 2001 From: Tony Cheneau Date: Mon, 25 Mar 2013 17:59:28 +0000 Subject: [PATCH 08/12] 6lowpan: store fragment tag values per device instead of net stack wide Signed-off-by: Tony Cheneau Signed-off-by: David S. Miller --- net/ieee802154/6lowpan.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/net/ieee802154/6lowpan.c b/net/ieee802154/6lowpan.c index 61eee9d0dd21..f9524513847a 100644 --- a/net/ieee802154/6lowpan.c +++ b/net/ieee802154/6lowpan.c @@ -104,6 +104,7 @@ static const u8 lowpan_llprefix[] = {0xfe, 0x80}; struct lowpan_dev_info { struct net_device *real_dev; /* real WPAN device ptr */ struct mutex dev_list_mtx; /* mutex for list ops */ + unsigned short fragment_tag; }; struct lowpan_dev_record { @@ -120,7 +121,6 @@ struct lowpan_fragment { struct list_head list; /* fragments list */ }; -static unsigned short fragment_tag; static LIST_HEAD(lowpan_fragments); static DEFINE_SPINLOCK(flist_lock); @@ -1027,14 +1027,14 @@ lowpan_fragment_xmit(struct sk_buff *skb, u8 *head, } static int -lowpan_skb_fragmentation(struct sk_buff *skb) +lowpan_skb_fragmentation(struct sk_buff *skb, struct net_device *dev) { int err, header_length, payload_length, tag, offset = 0; u8 head[5]; header_length = lowpan_get_mac_header_length(skb); payload_length = skb->len - header_length; - tag = fragment_tag++; + tag = lowpan_dev_info(dev)->fragment_tag++; /* first fragment header */ head[0] = LOWPAN_DISPATCH_FRAG1 | ((payload_length >> 8) & 0x7); @@ -1099,7 +1099,7 @@ static netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *dev) } pr_debug("frame is too big, fragmentation is needed\n"); - err = lowpan_skb_fragmentation(skb); + err = lowpan_skb_fragmentation(skb, dev); error: dev_kfree_skb(skb); out: @@ -1243,6 +1243,7 @@ static int lowpan_newlink(struct net *src_net, struct net_device *dev, return -ENODEV; lowpan_dev_info(dev)->real_dev = real_dev; + lowpan_dev_info(dev)->fragment_tag = 0; mutex_init(&lowpan_dev_info(dev)->dev_list_mtx); entry = kzalloc(sizeof(struct lowpan_dev_record), GFP_KERNEL); From 0483546a3de329cad7705d42962edb09a28794c6 Mon Sep 17 00:00:00 2001 From: Tony Cheneau Date: Mon, 25 Mar 2013 17:59:29 +0000 Subject: [PATCH 09/12] mac802154: add mac802154_dev_get_dsn() Bring-over mac802154_dev_get_dsn() function that was present in the Linux ZigBee kernel. This function is called by the 6LoWPAN code in order to properly set the DSN (Data Sequence Number) value in the IEEE 802.15.4 frame. Signed-off-by: Tony Cheneau Signed-off-by: David S. Miller --- net/mac802154/mac802154.h | 1 + net/mac802154/mac_cmd.c | 1 + net/mac802154/mib.c | 9 +++++++++ 3 files changed, 11 insertions(+) diff --git a/net/mac802154/mac802154.h b/net/mac802154/mac802154.h index a4dcaf1dd4b6..21fa386f4675 100644 --- a/net/mac802154/mac802154.h +++ b/net/mac802154/mac802154.h @@ -114,5 +114,6 @@ void mac802154_dev_set_ieee_addr(struct net_device *dev); u16 mac802154_dev_get_pan_id(const struct net_device *dev); void mac802154_dev_set_pan_id(struct net_device *dev, u16 val); void mac802154_dev_set_page_channel(struct net_device *dev, u8 page, u8 chan); +u8 mac802154_dev_get_dsn(const struct net_device *dev); #endif /* MAC802154_H */ diff --git a/net/mac802154/mac_cmd.c b/net/mac802154/mac_cmd.c index d8d277006089..a99910d4d52f 100644 --- a/net/mac802154/mac_cmd.c +++ b/net/mac802154/mac_cmd.c @@ -73,4 +73,5 @@ struct ieee802154_mlme_ops mac802154_mlme_wpan = { .start_req = mac802154_mlme_start_req, .get_pan_id = mac802154_dev_get_pan_id, .get_short_addr = mac802154_dev_get_short_addr, + .get_dsn = mac802154_dev_get_dsn, }; diff --git a/net/mac802154/mib.c b/net/mac802154/mib.c index f47781ab0ccc..f03e55f2ebf0 100644 --- a/net/mac802154/mib.c +++ b/net/mac802154/mib.c @@ -159,6 +159,15 @@ void mac802154_dev_set_pan_id(struct net_device *dev, u16 val) } } +u8 mac802154_dev_get_dsn(const struct net_device *dev) +{ + struct mac802154_sub_if_data *priv = netdev_priv(dev); + + BUG_ON(dev->type != ARPHRD_IEEE802154); + + return priv->dsn++; +} + static void phy_chan_notify(struct work_struct *work) { struct phy_chan_notify_work *nw = container_of(work, From c7d0ab28b4c51de50c1fded2502e47a37771b6c3 Mon Sep 17 00:00:00 2001 From: Tony Cheneau Date: Mon, 25 Mar 2013 17:59:30 +0000 Subject: [PATCH 10/12] 6lowpan: obtain IEEE802.15.4 sequence number from the MAC layer Sets the sequence number in the frame format. Without this fix, the sequence number is always set to 0. This makes trafic analysis very hard. Signed-off-by: Tony Cheneau Signed-off-by: David S. Miller --- net/ieee802154/6lowpan.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/net/ieee802154/6lowpan.c b/net/ieee802154/6lowpan.c index f9524513847a..d1d4ee69da27 100644 --- a/net/ieee802154/6lowpan.c +++ b/net/ieee802154/6lowpan.c @@ -573,6 +573,7 @@ static int lowpan_header_create(struct sk_buff *skb, */ { mac_cb(skb)->flags = IEEE802154_FC_TYPE_DATA; + mac_cb(skb)->seq = ieee802154_mlme_ops(dev)->get_dsn(dev); /* prepare wpan address data */ sa.addr_type = IEEE802154_ADDR_LONG; @@ -1127,6 +1128,12 @@ static u16 lowpan_get_short_addr(const struct net_device *dev) return ieee802154_mlme_ops(real_dev)->get_short_addr(real_dev); } +static u8 lowpan_get_dsn(const struct net_device *dev) +{ + struct net_device *real_dev = lowpan_dev_info(dev)->real_dev; + return ieee802154_mlme_ops(real_dev)->get_dsn(real_dev); +} + static struct header_ops lowpan_header_ops = { .create = lowpan_header_create, }; @@ -1140,6 +1147,7 @@ static struct ieee802154_mlme_ops lowpan_mlme = { .get_pan_id = lowpan_get_pan_id, .get_phy = lowpan_get_phy, .get_short_addr = lowpan_get_short_addr, + .get_dsn = lowpan_get_dsn, }; static void lowpan_setup(struct net_device *dev) From 43de7aa6ac4e37eaf0a5e83adc0a79d61fe3a55a Mon Sep 17 00:00:00 2001 From: Tony Cheneau Date: Mon, 25 Mar 2013 17:59:31 +0000 Subject: [PATCH 11/12] 6lowpan: use the PANID provided by the device instead of a static value Signed-off-by: Tony Cheneau Signed-off-by: David S. Miller --- net/ieee802154/6lowpan.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/net/ieee802154/6lowpan.c b/net/ieee802154/6lowpan.c index d1d4ee69da27..276971ba3ade 100644 --- a/net/ieee802154/6lowpan.c +++ b/net/ieee802154/6lowpan.c @@ -577,10 +577,12 @@ static int lowpan_header_create(struct sk_buff *skb, /* prepare wpan address data */ sa.addr_type = IEEE802154_ADDR_LONG; - sa.pan_id = 0xff; - memcpy(&(sa.hwaddr), saddr, 8); + sa.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev); + + memcpy(&(sa.hwaddr), saddr, 8); + /* intra-PAN communications */ + da.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev); - da.pan_id = 0xff; /* * if the destination address is the broadcast address, use the * corresponding short address From 24363b67328733ad11ceadffe72a9721a37a3e9e Mon Sep 17 00:00:00 2001 From: Tony Cheneau Date: Mon, 25 Mar 2013 17:59:32 +0000 Subject: [PATCH 12/12] 6lowpan: modify udp compression/uncompression to match the standard The previous code would just compress the UDP header and send the compressed UDP header along with the uncompressed one. Signed-off-by: Tony Cheneau Signed-off-by: David S. Miller --- net/ieee802154/6lowpan.c | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/net/ieee802154/6lowpan.c b/net/ieee802154/6lowpan.c index 276971ba3ade..c9c3f3d18c41 100644 --- a/net/ieee802154/6lowpan.c +++ b/net/ieee802154/6lowpan.c @@ -284,6 +284,9 @@ lowpan_compress_udp_header(u8 **hc06_ptr, struct sk_buff *skb) /* checksum is always inline */ memcpy(*hc06_ptr, &uh->check, 2); *hc06_ptr += 2; + + /* skip the UDP header */ + skb_pull(skb, sizeof(struct udphdr)); } static inline int lowpan_fetch_skb_u8(struct sk_buff *skb, u8 *val) @@ -309,9 +312,8 @@ static inline int lowpan_fetch_skb_u16(struct sk_buff *skb, u16 *val) } static int -lowpan_uncompress_udp_header(struct sk_buff *skb) +lowpan_uncompress_udp_header(struct sk_buff *skb, struct udphdr *uh) { - struct udphdr *uh = udp_hdr(skb); u8 tmp; if (!uh) @@ -358,6 +360,14 @@ lowpan_uncompress_udp_header(struct sk_buff *skb) /* copy checksum */ memcpy(&uh->check, &skb->data[0], 2); skb_pull(skb, 2); + + /* + * UDP lenght needs to be infered from the lower layers + * here, we obtain the hint from the remaining size of the + * frame + */ + uh->len = htons(skb->len + sizeof(struct udphdr)); + pr_debug("uncompressed UDP length: src = %d", uh->len); } else { pr_debug("ERROR: unsupported NH format\n"); goto err; @@ -944,8 +954,31 @@ lowpan_process_data(struct sk_buff *skb) /* UDP data uncompression */ if (iphc0 & LOWPAN_IPHC_NH_C) { - if (lowpan_uncompress_udp_header(skb)) + struct udphdr uh; + struct sk_buff *new; + if (lowpan_uncompress_udp_header(skb, &uh)) goto drop; + + /* + * replace the compressed UDP head by the uncompressed UDP + * header + */ + new = skb_copy_expand(skb, sizeof(struct udphdr), + skb_tailroom(skb), GFP_ATOMIC); + kfree_skb(skb); + + if (!new) + return -ENOMEM; + + skb = new; + + skb_push(skb, sizeof(struct udphdr)); + skb_reset_transport_header(skb); + skb_copy_to_linear_data(skb, &uh, sizeof(struct udphdr)); + + lowpan_raw_dump_table(__func__, "raw UDP header dump", + (u8 *)&uh, sizeof(uh)); + hdr.nexthdr = UIP_PROTO_UDP; }