From f6ecdc82264aa3c2a99098faf8e74923a26ef24d Mon Sep 17 00:00:00 2001 From: Martine Lenders Date: Thu, 28 Jun 2018 19:26:43 +0200 Subject: [PATCH] gnrc_sixlowpan_iphc: refactor reception for #8511 This refactors reception/decoding part of `gnrc_sixlowpan_iphc` to the more layered approach modeled in #8511. Since the reception part is already complicated enough I decided to divide send and receive up into separate changes. --- sys/include/net/gnrc/sixlowpan/iphc.h | 24 +-- .../sixlowpan/frag/gnrc_sixlowpan_frag.c | 2 - .../gnrc/network_layer/sixlowpan/frag/rbuf.c | 49 ++--- .../network_layer/sixlowpan/gnrc_sixlowpan.c | 29 +-- .../sixlowpan/iphc/gnrc_sixlowpan_iphc.c | 191 +++++++++++------- 5 files changed, 155 insertions(+), 140 deletions(-) diff --git a/sys/include/net/gnrc/sixlowpan/iphc.h b/sys/include/net/gnrc/sixlowpan/iphc.h index 2280ea970c2fd..7e60d25a1af9f 100644 --- a/sys/include/net/gnrc/sixlowpan/iphc.h +++ b/sys/include/net/gnrc/sixlowpan/iphc.h @@ -32,23 +32,17 @@ extern "C" { /** * @brief Decompresses a received 6LoWPAN IPHC frame. * - * @pre (dec_hdr != NULL) && (*dec_hdr != NULL) && ((*dec_hdr)->size >= sizeof(gnrc_ipv6_hdr_t)) - * - * @param[out] dec_hdr A pre-allocated IPv6 header. Will not be inserted into - * @p pkt. May change due to next headers being added in NHC. - * @param[in] pkt A received 6LoWPAN IPHC frame. IPHC dispatch will not - * be marked. - * @param[in] datagram_size Size of the full uncompressed IPv6 datagram. May be 0, if @p pkt - * contains the full (unfragmented) IPv6 datagram. - * @param[in] offset Offset of the IPHC dispatch in 6LoWPaN frame. - * @param[in, out] nh_len Pointer to next header length + * @pre (pkt != NULL) * - * @return length of the HC dispatches + inline values on success. - * @return 0 on error. + * @param[in] pkt A received 6LoWPAN IPHC frame. The first snip is to + * be expected to start with the IPHC dispatch. + * @param[in,out] ctx Context for the packet. May be NULL. If not NULL it + * is expected to be of type + * @ref gnrc_sixlowpan_rbuf_t. This function might + * change the content of that. + * @param[in] page Current 6Lo dispatch parsing page. */ -size_t gnrc_sixlowpan_iphc_decode(gnrc_pktsnip_t **dec_hdr, gnrc_pktsnip_t *pkt, - size_t datagram_size, size_t offset, - size_t *nh_len); +void gnrc_sixlowpan_iphc_recv(gnrc_pktsnip_t *pkt, void *ctx, unsigned page); /** * @brief Compresses a 6LoWPAN for IPHC. diff --git a/sys/net/gnrc/network_layer/sixlowpan/frag/gnrc_sixlowpan_frag.c b/sys/net/gnrc/network_layer/sixlowpan/frag/gnrc_sixlowpan_frag.c index ed42258ec626c..f2197eb97d95c 100644 --- a/sys/net/gnrc/network_layer/sixlowpan/frag/gnrc_sixlowpan_frag.c +++ b/sys/net/gnrc/network_layer/sixlowpan/frag/gnrc_sixlowpan_frag.c @@ -312,8 +312,6 @@ void gnrc_sixlowpan_frag_recv(gnrc_pktsnip_t *pkt, void *ctx, unsigned page) } rbuf_add(hdr, pkt, offset, page); - - gnrc_pktbuf_release(pkt); } void gnrc_sixlowpan_frag_rbuf_gc(void) diff --git a/sys/net/gnrc/network_layer/sixlowpan/frag/rbuf.c b/sys/net/gnrc/network_layer/sixlowpan/frag/rbuf.c index 7c149d8acd8b0..baa343a73964d 100644 --- a/sys/net/gnrc/network_layer/sixlowpan/frag/rbuf.c +++ b/sys/net/gnrc/network_layer/sixlowpan/frag/rbuf.c @@ -69,9 +69,6 @@ void rbuf_add(gnrc_netif_hdr_t *netif_hdr, gnrc_pktsnip_t *pkt, size_t offset, unsigned page) { rbuf_t *entry; - /* cppcheck-suppress variableScope - * (reason: cppcheck is clearly wrong here) */ - unsigned int data_offset = 0; sixlowpan_frag_t *frag = pkt->data; rbuf_int_t *ptr; uint8_t *data = ((uint8_t *)pkt->data) + sizeof(sixlowpan_frag_t); @@ -96,27 +93,6 @@ void rbuf_add(gnrc_netif_hdr_t *netif_hdr, gnrc_pktsnip_t *pkt, if (data[0] == SIXLOWPAN_UNCOMP) { frag_size--; } -#ifdef MODULE_GNRC_SIXLOWPAN_IPHC - else if (sixlowpan_iphc_is(data)) { - size_t iphc_len, nh_len = 0; - iphc_len = gnrc_sixlowpan_iphc_decode(&entry->super.pkt, pkt, - entry->super.pkt->size, - sizeof(sixlowpan_frag_t), - &nh_len); - if (iphc_len == 0) { - DEBUG("6lo rfrag: could not decode IPHC dispatch\n"); - gnrc_pktbuf_release(entry->super.pkt); - rbuf_rm(entry); - return; - } - data += iphc_len; /* take remaining data as data */ - frag_size -= iphc_len; /* and reduce frag size by IPHC dispatch length */ - /* but add IPv6 header + next header lengths */ - frag_size += sizeof(ipv6_hdr_t) + nh_len; - /* start copying after IPv6 header and next headers */ - data_offset += sizeof(ipv6_hdr_t) + nh_len; - } -#endif } else { frag_size = pkt->size - sizeof(sixlowpan_frag_n_t); @@ -153,11 +129,30 @@ void rbuf_add(gnrc_netif_hdr_t *netif_hdr, gnrc_pktsnip_t *pkt, if (_rbuf_update_ints(entry, offset, frag_size)) { DEBUG("6lo rbuf: add fragment data\n"); entry->super.current_size += (uint16_t)frag_size; - memcpy(((uint8_t *)entry->super.pkt->data) + offset + data_offset, data, - frag_size - data_offset); + if (offset == 0) { +#ifdef MODULE_GNRC_SIXLOWPAN_IPHC + if (sixlowpan_iphc_is(data)) { + gnrc_pktsnip_t *frag_hdr = gnrc_pktbuf_mark(pkt, + sizeof(sixlowpan_frag_t), GNRC_NETTYPE_SIXLOWPAN); + if (frag_hdr == NULL) { + gnrc_pktbuf_release(entry->super.pkt); + rbuf_rm(entry); + return; + } + gnrc_sixlowpan_iphc_recv(pkt, &entry->super, 0); + return; + } + else +#endif + if (data[0] == SIXLOWPAN_UNCOMP) { + data++; + } + } + memcpy(((uint8_t *)entry->super.pkt->data) + offset, data, + frag_size); } - gnrc_sixlowpan_frag_rbuf_dispatch_when_complete(&entry->super, netif_hdr); + gnrc_pktbuf_release(pkt); } static inline bool _rbuf_int_overlap_partially(rbuf_int_t *i, uint16_t start, uint16_t end) diff --git a/sys/net/gnrc/network_layer/sixlowpan/gnrc_sixlowpan.c b/sys/net/gnrc/network_layer/sixlowpan/gnrc_sixlowpan.c index 592f6fcf0574b..0aeb901220fd8 100644 --- a/sys/net/gnrc/network_layer/sixlowpan/gnrc_sixlowpan.c +++ b/sys/net/gnrc/network_layer/sixlowpan/gnrc_sixlowpan.c @@ -211,32 +211,9 @@ static void _receive(gnrc_pktsnip_t *pkt) #endif #ifdef MODULE_GNRC_SIXLOWPAN_IPHC else if (sixlowpan_iphc_is(dispatch)) { - size_t dispatch_size, nh_len; - gnrc_pktsnip_t *sixlowpan; - gnrc_pktsnip_t *dec_hdr = gnrc_pktbuf_add(NULL, NULL, sizeof(ipv6_hdr_t), - GNRC_NETTYPE_IPV6); - if ((dec_hdr == NULL) || - (dispatch_size = gnrc_sixlowpan_iphc_decode(&dec_hdr, pkt, 0, 0, - &nh_len)) == 0) { - DEBUG("6lo: error on IPHC decoding\n"); - if (dec_hdr != NULL) { - gnrc_pktbuf_release(dec_hdr); - } - gnrc_pktbuf_release(pkt); - return; - } - sixlowpan = gnrc_pktbuf_mark(pkt, dispatch_size, GNRC_NETTYPE_SIXLOWPAN); - if (sixlowpan == NULL) { - DEBUG("6lo: error on marking IPHC dispatch\n"); - gnrc_pktbuf_release(dec_hdr); - gnrc_pktbuf_release(pkt); - return; - } - - /* Remove IPHC dispatches */ - /* Insert decoded header instead */ - pkt = gnrc_pktbuf_replace_snip(pkt, sixlowpan, dec_hdr); - payload->type = GNRC_NETTYPE_UNDEF; + DEBUG("6lo: received 6LoWPAN IPHC comressed datagram\n"); + gnrc_sixlowpan_iphc_recv(pkt, NULL, 0); + return; } #endif else { diff --git a/sys/net/gnrc/network_layer/sixlowpan/iphc/gnrc_sixlowpan_iphc.c b/sys/net/gnrc/network_layer/sixlowpan/iphc/gnrc_sixlowpan_iphc.c index b04718f54c6f8..4db159dc375f6 100644 --- a/sys/net/gnrc/network_layer/sixlowpan/iphc/gnrc_sixlowpan_iphc.c +++ b/sys/net/gnrc/network_layer/sixlowpan/iphc/gnrc_sixlowpan_iphc.c @@ -22,7 +22,9 @@ #include "net/ieee802154.h" #include "net/ipv6/hdr.h" #include "net/gnrc.h" +#include "net/gnrc/sixlowpan.h" #include "net/gnrc/sixlowpan/ctx.h" +#include "net/gnrc/sixlowpan/frag.h" #include "net/gnrc/sixlowpan/internal.h" #include "net/sixlowpan.h" #include "utlist.h" @@ -112,36 +114,47 @@ static inline bool _context_overlaps_iid(gnrc_sixlowpan_ctx_t *ctx, } #ifdef MODULE_GNRC_SIXLOWPAN_IPHC_NHC -static inline size_t iphc_nhc_udp_decode(gnrc_pktsnip_t *pkt, gnrc_pktsnip_t **dec_hdr, - size_t datagram_size, size_t offset) +/** + * @brief Decodes UDP NHC + * + * @param[in] pkt The IPHC encoded packet + * @param[in] offset The offset of the NHC encoded header + * @param[in] ipv6_payload_len Length of the unencoded, reassembled IPv6 + * datagram in @p ipv6 with out the outer-most + * IPv6 header + * @param[out] ipv6 The packet to write the decoded data to + * @param[in,out] uncomp_hdr_len Number of bytes already decoded into @p ipv6 + * by IPHC and other NHC. Adds size of @ref + * udp_hdr_t after successful UDP header + * decompression + * + * @return The offset after UDP NHC header on success. + * @return 0 on error. + */ +static size_t _iphc_nhc_udp_decode(gnrc_pktsnip_t *sixlo, size_t offset, + gnrc_pktsnip_t *ipv6, size_t *uncomp_hdr_len) { - uint8_t *payload = pkt->data; - gnrc_pktsnip_t *ipv6 = *dec_hdr; - ipv6_hdr_t *ipv6_hdr = ipv6->data; -#ifdef MODULE_GNRC_UDP - const gnrc_nettype_t snip_type = GNRC_NETTYPE_UDP; -#else - const gnrc_nettype_t snip_type = GNRC_NETTYPE_UNDEF; -#endif - gnrc_pktsnip_t *udp = NULL; + uint8_t *payload = sixlo->data; + ipv6_hdr_t *ipv6_hdr; + udp_hdr_t *udp_hdr; + bool frag = true; /* datagram is fragmented => infer payload length from + * ipv6 snip (== reassembly buffer space) */ + uint16_t payload_len; uint8_t udp_nhc = payload[offset++]; uint8_t tmp; - udp_hdr_t *udp_hdr; - if (datagram_size == 0) { /* received packet is not fragmented */ - udp = gnrc_pktbuf_add(NULL, NULL, sizeof(udp_hdr_t), - snip_type); - if (udp == NULL) { - DEBUG("6lo: error on IPHC NHC UDP decoding\n"); + /* realloc size for uncompressed snip, if too small */ + if (ipv6->size < (*uncomp_hdr_len + sizeof(udp_hdr_t))) { + if (gnrc_pktbuf_realloc_data(ipv6, + *uncomp_hdr_len + sizeof(udp_hdr_t))) { + DEBUG("6lo: unable to decode UDP NHC (not enough buffer space)\n"); return 0; } - udp_hdr = udp->data; - } - else { /* received packet is fragmented */ - /* reassembly is in-place => don't allocate new packet snip */ - /* TODO: account for extension headers */ - udp_hdr = (udp_hdr_t *)(ipv6_hdr + 1); + frag = false; /* datagram was not fragmented => infer payload length + * from original 6Lo packet*/ } + ipv6_hdr = ipv6->data; + udp_hdr = (udp_hdr_t *)((uint8_t *)ipv6->data + *uncomp_hdr_len); network_uint16_t *src_port = &(udp_hdr->src_port); network_uint16_t *dst_port = &(udp_hdr->dst_port); @@ -182,7 +195,6 @@ static inline size_t iphc_nhc_udp_decode(gnrc_pktsnip_t *pkt, gnrc_pktsnip_t **d if ((udp_nhc & NHC_UDP_C_ELIDED) != 0) { DEBUG("6lo iphc nhc: unsupported elided checksum\n"); - gnrc_pktbuf_release(udp); return 0; } else { @@ -190,43 +202,58 @@ static inline size_t iphc_nhc_udp_decode(gnrc_pktsnip_t *pkt, gnrc_pktsnip_t **d udp_hdr->checksum.u8[1] = payload[offset++]; } - /* TODO subtract extension header length */ - if (udp != NULL) { - udp_hdr->length = byteorder_htons(pkt->size - offset + sizeof(udp_hdr_t)); + if (frag) { + payload_len = ipv6->size - *uncomp_hdr_len; } else { - udp_hdr->length = byteorder_htons(datagram_size - sizeof(ipv6_hdr_t)); + payload_len = sixlo->size + sizeof(udp_hdr_t) - offset; } + udp_hdr->length = byteorder_htons(payload_len); + *uncomp_hdr_len += sizeof(udp_hdr_t); ipv6_hdr->nh = PROTNUM_UDP; - ipv6_hdr->len = udp_hdr->length; - - if (udp != NULL) { /* prepend udp header in case of packet not being fragmented */ - udp->next = ipv6; - *dec_hdr = udp; - } return offset; } #endif -size_t gnrc_sixlowpan_iphc_decode(gnrc_pktsnip_t **dec_hdr, gnrc_pktsnip_t *pkt, - size_t datagram_size, size_t offset, - size_t *nh_len) +static inline void _recv_error_release(gnrc_pktsnip_t *sixlo, + gnrc_pktsnip_t *ipv6, + gnrc_sixlowpan_rbuf_t *rbuf) { + if (rbuf != NULL) { + gnrc_sixlowpan_frag_rbuf_remove(rbuf); + } + gnrc_pktbuf_release(ipv6); + gnrc_pktbuf_release(sixlo); +} + +void gnrc_sixlowpan_iphc_recv(gnrc_pktsnip_t *sixlo, void *rbuf_ptr, + unsigned page) { - gnrc_pktsnip_t *ipv6; - gnrc_netif_hdr_t *netif_hdr = pkt->next->data; + gnrc_pktsnip_t *ipv6, *netif; + gnrc_netif_hdr_t *netif_hdr; ipv6_hdr_t *ipv6_hdr; - uint8_t *iphc_hdr = pkt->data; + uint8_t *iphc_hdr = sixlo->data; size_t payload_offset = SIXLOWPAN_IPHC_HDR_LEN; + size_t uncomp_hdr_len = sizeof(ipv6_hdr_t); gnrc_sixlowpan_ctx_t *ctx = NULL; + gnrc_sixlowpan_rbuf_t *rbuf = rbuf_ptr; - assert(dec_hdr != NULL); - ipv6 = *dec_hdr; - assert(ipv6 != NULL); - assert(ipv6->size >= sizeof(ipv6_hdr_t)); + assert(sixlo != NULL); + if (rbuf != NULL) { + ipv6 = rbuf->pkt; + assert(ipv6 != NULL); + } + else { + ipv6 = gnrc_pktbuf_add(NULL, NULL, sizeof(ipv6_hdr_t), + GNRC_NETTYPE_IPV6); + if (ipv6 == NULL) { + gnrc_pktbuf_release(sixlo); + return; + } + } + assert(ipv6->size >= sizeof(ipv6_hdr_t)); ipv6_hdr = ipv6->data; - iphc_hdr += offset; if (iphc_hdr[IPHC2_IDX] & SIXLOWPAN_IPHC2_CID_EXT) { payload_offset++; @@ -295,11 +322,15 @@ size_t gnrc_sixlowpan_iphc_decode(gnrc_pktsnip_t **dec_hdr, gnrc_pktsnip_t *pkt, if (ctx == NULL) { DEBUG("6lo iphc: could not find source context\n"); - return 0; + _recv_error_release(sixlo, ipv6, rbuf); + return; } } } + netif = gnrc_pktsnip_search_type(sixlo, GNRC_NETTYPE_NETIF); + assert(netif != NULL); + netif_hdr = netif->data; switch (iphc_hdr[IPHC2_IDX] & (SIXLOWPAN_IPHC2_SAC | SIXLOWPAN_IPHC2_SAM)) { case IPHC_SAC_SAM_FULL: @@ -373,7 +404,8 @@ size_t gnrc_sixlowpan_iphc_decode(gnrc_pktsnip_t **dec_hdr, gnrc_pktsnip_t *pkt, if (ctx == NULL) { DEBUG("6lo iphc: could not find destination context\n"); - return 0; + _recv_error_release(sixlo, ipv6, rbuf); + return; } } } @@ -482,42 +514,61 @@ size_t gnrc_sixlowpan_iphc_decode(gnrc_pktsnip_t **dec_hdr, gnrc_pktsnip_t *pkt, default: DEBUG("6lo iphc: unspecified or reserved M, DAC, DAM combination\n"); - return 0; - + break; } - /* set IPv6 header payload length field to the length of whatever is left - * after removing the 6LoWPAN header */ - if (datagram_size == 0) { - ipv6_hdr->len = byteorder_htons((uint16_t)(pkt->size - payload_offset)); - } - else { - ipv6_hdr->len = byteorder_htons((uint16_t)(datagram_size - sizeof(ipv6_hdr_t))); - } #ifdef MODULE_GNRC_SIXLOWPAN_IPHC_NHC if (iphc_hdr[IPHC1_IDX] & SIXLOWPAN_IPHC1_NH) { switch (iphc_hdr[payload_offset] & NHC_ID_MASK) { - case NHC_UDP_ID: - payload_offset = iphc_nhc_udp_decode(pkt, dec_hdr, datagram_size, - payload_offset + offset); - - if (payload_offset != 0) { - payload_offset -= offset; + case NHC_UDP_ID: { + payload_offset = _iphc_nhc_udp_decode(sixlo, payload_offset, + ipv6, &uncomp_hdr_len); + if (payload_offset == 0) { + _recv_error_release(sixlo, ipv6, rbuf); + return; } - - *nh_len += sizeof(udp_hdr_t); break; - + } default: break; } } -#else - (void)nh_len; #endif - - return payload_offset; + uint16_t payload_len; + if (rbuf != NULL) { + /* for a fragmented datagram we know the overall length already */ + payload_len = (uint16_t)(rbuf->pkt->size - sizeof(ipv6_hdr_t)); + } + else { + /* set IPv6 header payload length field to the length of whatever is left + * after removing the 6LoWPAN header and adding uncompressed headers */ + payload_len = (sixlo->size + uncomp_hdr_len - + payload_offset - sizeof(ipv6_hdr_t)); + } + if ((rbuf == NULL) && + (gnrc_pktbuf_realloc_data(ipv6, uncomp_hdr_len + payload_len) != 0)) { + DEBUG("6lo iphc: no space left to copy payload\n"); + _recv_error_release(sixlo, ipv6, rbuf); + return; + } + /* re-assign IPv6 header in case realloc changed the address */ + ipv6_hdr = ipv6->data; + ipv6_hdr->len = byteorder_htons(payload_len); + memcpy(((uint8_t *)ipv6->data) + uncomp_hdr_len, + ((uint8_t *)sixlo->data) + payload_offset, + sixlo->size - payload_offset); + if (rbuf != NULL) { + rbuf->current_size += (uncomp_hdr_len - payload_offset); + gnrc_sixlowpan_frag_rbuf_dispatch_when_complete(rbuf, netif_hdr); + } + else { + LL_DELETE(sixlo, netif); + LL_APPEND(ipv6, netif); + gnrc_sixlowpan_dispatch_recv(ipv6, NULL, page); + } + gnrc_pktbuf_release(sixlo); + return; } #ifdef MODULE_GNRC_SIXLOWPAN_IPHC_NHC