diff --git a/sys/include/net/gnrc/sixlowpan/iphc.h b/sys/include/net/gnrc/sixlowpan/iphc.h
index 2280ea970c2fdb61f68b3da725c2cdb4cb16cfed..7e60d25a1af9fe87e2aeb5c4e3300f4f73fddb20 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 ed42258ec626cc4357c3025544deee6d0f672270..f2197eb97d95c55d36752997f7e0fa8b7ac9a1e9 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 7c149d8acd8b08134e8229bd63bf3b00e067193d..baa343a73964d4b1aed64c9455a4f2f0053b057a 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 592f6fcf0574bd447e1f712ff35faf7207ca8e05..0aeb901220fd886ca11f629a38334a7a2b1b4171 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 b04718f54c6f8fd80c00a8bd2295ccf91e8c4094..e65bf6ea8c0093cbe3209d417dc866105f829e51 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;
+    assert(sixlo != NULL);
+    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));
+    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