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 e65bf6ea8c0093cbe3209d417dc866105f829e51..a765cb8bf530428ae539cf30e83831ea38f0821b 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 @@ -572,82 +572,124 @@ void gnrc_sixlowpan_iphc_recv(gnrc_pktsnip_t *sixlo, void *rbuf_ptr, } #ifdef MODULE_GNRC_SIXLOWPAN_IPHC_NHC -static inline size_t iphc_nhc_udp_encode(gnrc_pktsnip_t *udp, ipv6_hdr_t *ipv6_hdr) +static inline size_t iphc_nhc_udp_encode(uint8_t *nhc_data, + const gnrc_pktsnip_t *udp) { - udp_hdr_t *udp_hdr = udp->data; - network_uint16_t *src_port = &(udp_hdr->src_port); - network_uint16_t *dst_port = &(udp_hdr->dst_port); - uint8_t *udp_data = udp->data; - size_t nhc_len = 0; - - /* TODO: Add support for elided checksum. */ - + const udp_hdr_t *udp_hdr = udp->data; + uint16_t src_port = byteorder_ntohs(udp_hdr->src_port); + uint16_t dst_port = byteorder_ntohs(udp_hdr->dst_port); + size_t nhc_len = 1; /* skip over NHC header */ + + /* Set UDP NHC header type + * (see https://tools.ietf.org/html/rfc6282#section-4.3). */ + nhc_data[0] = NHC_UDP_ID; /* Compressing UDP ports, follow the same sequence as the linux kernel (nhc_udp module). */ - if (((byteorder_ntohs(*src_port) & NHC_UDP_4BIT_MASK) == NHC_UDP_4BIT_PORT) && - ((byteorder_ntohs(*dst_port) & NHC_UDP_4BIT_MASK) == NHC_UDP_4BIT_PORT)) { + if (((src_port & NHC_UDP_4BIT_MASK) == NHC_UDP_4BIT_PORT) && + ((dst_port & NHC_UDP_4BIT_MASK) == NHC_UDP_4BIT_PORT)) { DEBUG("6lo iphc nhc: elide src and dst\n"); - ipv6_hdr->nh = NHC_UDP_SD_ELIDED; - udp_data[nhc_len++] = byteorder_ntohs(*dst_port) - NHC_UDP_4BIT_PORT + - ((byteorder_ntohs(*src_port) - NHC_UDP_4BIT_PORT) << 4); - udp_data[nhc_len++] = udp_hdr->checksum.u8[0]; - udp_data[nhc_len++] = udp_hdr->checksum.u8[1]; + nhc_data[0] |= NHC_UDP_SD_ELIDED; + nhc_data[nhc_len++] = dst_port - NHC_UDP_4BIT_PORT + + ((src_port - NHC_UDP_4BIT_PORT) << 4); } - else if ((byteorder_ntohs(*dst_port) & NHC_UDP_8BIT_MASK) == NHC_UDP_8BIT_PORT) { + else if ((dst_port & NHC_UDP_8BIT_MASK) == NHC_UDP_8BIT_PORT) { DEBUG("6lo iphc nhc: elide dst\n"); - ipv6_hdr->nh = NHC_UDP_S_INLINE; - nhc_len += 2; /* keep src_port */ - udp_data[nhc_len++] = byteorder_ntohs(*dst_port) - NHC_UDP_8BIT_PORT; - udp_data[nhc_len++] = udp_hdr->checksum.u8[0]; - udp_data[nhc_len++] = udp_hdr->checksum.u8[1]; + nhc_data[0] |= NHC_UDP_S_INLINE; + nhc_data[nhc_len++] = udp_hdr->src_port.u8[0]; + nhc_data[nhc_len++] = udp_hdr->src_port.u8[1]; + nhc_data[nhc_len++] = dst_port - NHC_UDP_8BIT_PORT; } - else if ((byteorder_ntohs(*src_port) & NHC_UDP_8BIT_MASK) == NHC_UDP_8BIT_PORT) { + else if ((src_port & NHC_UDP_8BIT_MASK) == NHC_UDP_8BIT_PORT) { DEBUG("6lo iphc nhc: elide src\n"); - ipv6_hdr->nh = NHC_UDP_D_INLINE; - udp_data[nhc_len++] = byteorder_ntohs(*src_port) - NHC_UDP_8BIT_PORT; - udp_data[nhc_len++] = udp_hdr->dst_port.u8[0]; - udp_data[nhc_len++] = udp_hdr->dst_port.u8[1]; - udp_data[nhc_len++] = udp_hdr->checksum.u8[0]; - udp_data[nhc_len++] = udp_hdr->checksum.u8[1]; + nhc_data[0] |= NHC_UDP_D_INLINE; + nhc_data[nhc_len++] = src_port - NHC_UDP_8BIT_PORT; + nhc_data[nhc_len++] = udp_hdr->dst_port.u8[0]; + nhc_data[nhc_len++] = udp_hdr->dst_port.u8[1]; } else { DEBUG("6lo iphc nhc: src and dst inline\n"); - ipv6_hdr->nh = NHC_UDP_SD_INLINE; - nhc_len = sizeof(udp_hdr_t) - 4; /* skip src + dst and elide length */ - udp_data[nhc_len++] = udp_hdr->checksum.u8[0]; - udp_data[nhc_len++] = udp_hdr->checksum.u8[1]; + nhc_data[0] |= NHC_UDP_SD_INLINE; + nhc_data[nhc_len++] = udp_hdr->src_port.u8[0]; + nhc_data[nhc_len++] = udp_hdr->src_port.u8[1]; + nhc_data[nhc_len++] = udp_hdr->dst_port.u8[0]; + nhc_data[nhc_len++] = udp_hdr->dst_port.u8[1]; } - /* Set UDP header ID (rfc6282#section-5). */ - ipv6_hdr->nh |= NHC_UDP_ID; - - /* In case payload is in this snip (e.g. a forwarded packet): - * move data to right place */ - size_t diff = sizeof(udp_hdr_t) - nhc_len; - for (size_t i = nhc_len; i < (udp->size - diff); i++) { - udp_data[i] = udp_data[i + diff]; - } - /* NOTE: gnrc_pktbuf_realloc_data overflow if (udp->size - diff) < 4 */ - gnrc_pktbuf_realloc_data(udp, (udp->size - diff)); + /* TODO: Add support for elided checksum. */ + nhc_data[nhc_len++] = udp_hdr->checksum.u8[0]; + nhc_data[nhc_len++] = udp_hdr->checksum.u8[1]; return nhc_len; } #endif +static inline bool _compressible(gnrc_pktsnip_t *hdr) +{ + switch (hdr->type) { + case GNRC_NETTYPE_UNDEF: /* when forwarded */ + case GNRC_NETTYPE_IPV6: +#if defined(MODULE_GNRC_SIXLOWPAN_IPHC_NHC) && defined(MODULE_GNRC_UDP) + case GNRC_NETTYPE_UDP: + return true; +#endif + default: + return false; + } +} + void gnrc_sixlowpan_iphc_send(gnrc_pktsnip_t *pkt, void *ctx, unsigned page) { assert(pkt != NULL); gnrc_netif_hdr_t *netif_hdr = pkt->data; - ipv6_hdr_t *ipv6_hdr = pkt->next->data; + ipv6_hdr_t *ipv6_hdr; uint8_t *iphc_hdr; gnrc_sixlowpan_ctx_t *src_ctx = NULL, *dst_ctx = NULL; - gnrc_pktsnip_t *dispatch = gnrc_pktbuf_add(NULL, NULL, pkt->next->size, - GNRC_NETTYPE_SIXLOWPAN); - bool addr_comp = false, nhc_comp = false; + gnrc_pktsnip_t *dispatch, *ptr = pkt->next; + bool addr_comp = false; + size_t dispatch_size = 0; /* datagram size before compression */ size_t orig_datagram_size = gnrc_pkt_len(pkt->next); uint16_t inline_pos = SIXLOWPAN_IPHC_HDR_LEN; (void)ctx; + dispatch = NULL; /* use dispatch as temporary pointer for prev */ + /* determine maximum dispatch size and write protect all headers until + * then because they will be removed */ + while (_compressible(ptr)) { + gnrc_pktsnip_t *tmp = gnrc_pktbuf_start_write(ptr); + + if (tmp == NULL) { + DEBUG("6lo iphc: unable to write protect compressible header\n"); + if (addr_comp) { /* addr_comp was used as release indicator */ + gnrc_pktbuf_release(pkt); + } + return; + } + ptr = tmp; + if (dispatch == NULL) { + /* pkt was already write protected in gnrc_sixlowpan.c:_send so + * we shouldn't do it again */ + pkt->next = ptr; /* reset original packet */ + } + else { + dispatch->next = ptr; + } + if (ptr->type == GNRC_NETTYPE_UNDEF) { + /* most likely UDP for now so use that (XXX: extend if extension + * headers make problems) */ + dispatch_size += sizeof(udp_hdr_t); + break; /* nothing special after UDP so quit even if more UNDEF + * come */ + } + else { + dispatch_size += ptr->size; + } + dispatch = ptr; /* use dispatch as temporary point for prev */ + ptr = ptr->next; + } + ipv6_hdr = pkt->next->data; + dispatch = gnrc_pktbuf_add(NULL, NULL, dispatch_size, + GNRC_NETTYPE_SIXLOWPAN); + if (dispatch == NULL) { DEBUG("6lo iphc: error allocating dispatch space\n"); gnrc_pktbuf_release(pkt); @@ -724,13 +766,11 @@ void gnrc_sixlowpan_iphc_send(gnrc_pktsnip_t *pkt, void *ctx, unsigned page) iphc_hdr[inline_pos++] = (uint8_t)((ipv6_hdr_get_fl(ipv6_hdr) & 0x000000ff) >> 8); } - /* compress next header */ + /* check for compressible next header */ switch (ipv6_hdr->nh) { #ifdef MODULE_GNRC_SIXLOWPAN_IPHC_NHC case PROTNUM_UDP: - iphc_nhc_udp_encode(pkt->next->next, ipv6_hdr); iphc_hdr[IPHC1_IDX] |= SIXLOWPAN_IPHC1_NH; - nhc_comp = true; break; #endif @@ -934,9 +974,31 @@ void gnrc_sixlowpan_iphc_send(gnrc_pktsnip_t *pkt, void *ctx, unsigned page) inline_pos += 16; } - if (nhc_comp) { - iphc_hdr[inline_pos++] = ipv6_hdr->nh; +#ifdef MODULE_GNRC_SIXLOWPAN_IPHC_NHC + switch (ipv6_hdr->nh) { + case PROTNUM_UDP: { + gnrc_pktsnip_t *udp = pkt->next->next; + + assert(udp->size >= sizeof(udp_hdr_t)); + inline_pos += iphc_nhc_udp_encode(&iphc_hdr[inline_pos], udp); + /* remove UDP header */ + if (udp->size > sizeof(udp_hdr_t)) { + udp = gnrc_pktbuf_mark(udp, sizeof(udp_hdr_t), + GNRC_NETTYPE_UNDEF); + + if (udp == NULL) { + DEBUG("gnrc_sixlowpan_iphc_encode: unable to mark UDP header\n"); + gnrc_pktbuf_release(dispatch); + return; + } + } + gnrc_pktbuf_remove_snip(pkt, udp); + break; + } + default: + break; } +#endif /* shrink dispatch allocation to final size */ /* NOTE: Since this only shrinks the data nothing bad SHOULD happen ;-) */