From 73d9739258dc33f1d7e5db916ae9209853e4f8f1 Mon Sep 17 00:00:00 2001
From: Martine Lenders <mlenders@inf.fu-berlin.de>
Date: Wed, 8 Apr 2015 11:56:10 +0200
Subject: [PATCH] ng_sixlowpan: Initial import of datagram fragmentation

---
 Makefile.dep                                  |   5 +
 sys/Makefile                                  |   3 +
 sys/include/net/ng_sixlowpan.h                |   2 +
 sys/include/net/ng_sixlowpan/frag.h           | 127 ++++++++
 .../network_layer/ng_sixlowpan/frag/Makefile  |   3 +
 .../ng_sixlowpan/frag/ng_sixlowpan_frag.c     | 264 +++++++++++++++
 .../network_layer/ng_sixlowpan/frag/rbuf.c    | 307 ++++++++++++++++++
 .../network_layer/ng_sixlowpan/frag/rbuf.h    | 101 ++++++
 .../network_layer/ng_sixlowpan/ng_sixlowpan.c |  18 +-
 9 files changed, 828 insertions(+), 2 deletions(-)
 create mode 100644 sys/include/net/ng_sixlowpan/frag.h
 create mode 100644 sys/net/network_layer/ng_sixlowpan/frag/Makefile
 create mode 100644 sys/net/network_layer/ng_sixlowpan/frag/ng_sixlowpan_frag.c
 create mode 100644 sys/net/network_layer/ng_sixlowpan/frag/rbuf.c
 create mode 100644 sys/net/network_layer/ng_sixlowpan/frag/rbuf.h

diff --git a/Makefile.dep b/Makefile.dep
index 0dcbe17d48..9ca785984d 100644
--- a/Makefile.dep
+++ b/Makefile.dep
@@ -48,6 +48,11 @@ ifneq (,$(filter sixlowpan,$(USEMODULE)))
   USEMODULE += vtimer
 endif
 
+ifneq (,$(filter ng_sixlowpan_frag,$(USEMODULE)))
+  USEMODULE += ng_sixlowpan
+  USEMODULE += vtimer
+endif
+
 ifneq (,$(filter ng_sixlowpan,$(USEMODULE)))
   USEMODULE += ng_sixlowpan_netif
   USEMODULE += ng_netbase
diff --git a/sys/Makefile b/sys/Makefile
index ab68cf1977..56defb83a3 100644
--- a/sys/Makefile
+++ b/sys/Makefile
@@ -101,6 +101,9 @@ endif
 ifneq (,$(filter ng_sixlowpan_ctx,$(USEMODULE)))
     DIRS += net/network_layer/ng_sixlowpan/ctx
 endif
+ifneq (,$(filter ng_sixlowpan_frag,$(USEMODULE)))
+    DIRS += net/network_layer/ng_sixlowpan/frag
+endif
 ifneq (,$(filter ng_sixlowpan_netif,$(USEMODULE)))
     DIRS += net/network_layer/ng_sixlowpan/netif
 endif
diff --git a/sys/include/net/ng_sixlowpan.h b/sys/include/net/ng_sixlowpan.h
index edc09a46a4..5af14b2018 100644
--- a/sys/include/net/ng_sixlowpan.h
+++ b/sys/include/net/ng_sixlowpan.h
@@ -24,6 +24,8 @@
 
 #include "kernel_types.h"
 
+#include "net/ng_sixlowpan/frag.h"
+
 #ifdef __cplusplus
 extern "C" {
 #endif
diff --git a/sys/include/net/ng_sixlowpan/frag.h b/sys/include/net/ng_sixlowpan/frag.h
new file mode 100644
index 0000000000..9315d5f8a7
--- /dev/null
+++ b/sys/include/net/ng_sixlowpan/frag.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2015 Martine Lenders <mlenders@inf.fu-berlin.de>
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @defgroup    net_ng_sixlowpan_frag   6LoWPAN Fragmentation
+ * @ingroup     net_ng_sixlowpan
+ * @brief       6LoWPAN Fragmentation headers and functionality
+ * @see <a href="https://tools.ietf.org/html/rfc4944#section-5.3">
+ *          RFC 4944, section 5.3
+ *      </a>
+ * @{
+ *
+ * @file
+ * @brief   6LoWPAN Fragmentation definitions
+ *
+ * @author  Martine Lenders <mlenders@inf.fu-berlin.de>
+ */
+#ifndef NG_SIXLOWPAN_FRAG_H_
+#define NG_SIXLOWPAN_FRAG_H_
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include "byteorder.h"
+#include "kernel_types.h"
+#include "net/ng_pkt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define NG_SIXLOWPAN_FRAG_DISP_MASK     (0xf8)      /**< mask for fragmentation
+                                                     *   dispatch */
+#define NG_SIXLOWPAN_FRAG_1_DISP        (0xc0)      /**< dispatch for 1st fragment */
+#define NG_SIXLOWPAN_FRAG_N_DISP        (0xe0)      /**< dispatch for subsequent
+                                                     *   fragments */
+#define NG_SIXLOWPAN_FRAG_SIZE_MASK     (0x07ff)    /**< mask for datagram size */
+
+/**
+ * @brief   General and 1st 6LoWPAN fragmentation header
+ *
+ * @see <a href="https://tools.ietf.org/html/rfc4944#section-5.1">
+ *          RFC 4944, section 5.1
+ *      </a>
+ */
+typedef struct __attribute__((packed)) {
+    /**
+     * @brief   Dispatch and datagram size.
+     *
+     * @details The 5 most significant bits are the dispatch, the remaining
+     *          bits are the size.
+     */
+    network_uint16_t disp_size;
+    network_uint16_t tag;       /**< datagram tag */
+} ng_sixlowpan_frag_t;
+
+/**
+ * @brief   Subsequent 6LoWPAN fragmentation header
+ *
+ * @see <a href="https://tools.ietf.org/html/rfc4944#section-5.3">
+ *          RFC 4944, section 5.3
+ *      </a>
+ *
+ * @extends ng_sixlowpan_frag_t
+ */
+typedef struct __attribute__((packed)) {
+    /**
+     * @brief   Dispatch and datagram size.
+     *
+     * @details The 5 most significant bits are the dispatch, the remaining
+     *          bits are the size.
+     */
+    network_uint16_t disp_size;
+    network_uint16_t tag;       /**< datagram tag */
+    uint8_t offset;             /**< offset */
+} ng_sixlowpan_frag_n_t;
+
+/**
+ * @brief   Checks if a given fragment is a 6LoWPAN fragment.
+ *
+ * @param[in] hdr   A 6LoWPAN fragmentation header.
+ *
+ * @return  true, if given fragment is a 6LoWPAN fragment.
+ * @return  false, if given fragment is not a 6LoWPAN fragment.
+ */
+static inline bool ng_sixlowpan_frag_is(ng_sixlowpan_frag_t *hdr)
+{
+    return ((hdr->disp_size.u8[0] & NG_SIXLOWPAN_FRAG_DISP_MASK) ==
+            NG_SIXLOWPAN_FRAG_1_DISP) ||
+           ((hdr->disp_size.u8[0] & NG_SIXLOWPAN_FRAG_DISP_MASK) ==
+            NG_SIXLOWPAN_FRAG_N_DISP);
+}
+
+/**
+ * @brief   Sends a packet fragmented.
+ *
+ * @param[in] pid           The interface to send the packet over.
+ * @param[in] pkt           The packet to send.
+ * @param[in] payload_len   The length of the payload to send (IPv6 packet size
+ *                          + inner 6LoWPAN dispatches).
+ *                          This value is purely given to not calculate the
+ *                          payload length using @ref ng_pkt_len() repeatedly.
+ * @param[in] datagram_size The length of just the IPv6 packet. It is the value
+ *                          set that the ng_sixlowpan_frag_t::disp_size will be
+ *                          set to.
+ */
+void ng_sixlowpan_frag_send(kernel_pid_t pid, ng_pktsnip_t *pkt,
+                            size_t payload_len, size_t datagram_size);
+
+/**
+ * @brief   Handles a packet containing a fragment header.
+ *
+ * @param[in] pkt   The packet to handle.
+ */
+void ng_sixlowpan_frag_handle_pkt(ng_pktsnip_t *pkt);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NG_SIXLOWPAN_FRAG_H_ */
+/** @} */
diff --git a/sys/net/network_layer/ng_sixlowpan/frag/Makefile b/sys/net/network_layer/ng_sixlowpan/frag/Makefile
new file mode 100644
index 0000000000..1192513573
--- /dev/null
+++ b/sys/net/network_layer/ng_sixlowpan/frag/Makefile
@@ -0,0 +1,3 @@
+MODULE = ng_sixlowpan_frag
+
+include $(RIOTBASE)/Makefile.base
diff --git a/sys/net/network_layer/ng_sixlowpan/frag/ng_sixlowpan_frag.c b/sys/net/network_layer/ng_sixlowpan/frag/ng_sixlowpan_frag.c
new file mode 100644
index 0000000000..bdf85049a4
--- /dev/null
+++ b/sys/net/network_layer/ng_sixlowpan/frag/ng_sixlowpan_frag.c
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2015 Martine Lenders <mlenders@inf.fu-berlin.de>
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @{
+ *
+ * @file
+ */
+
+#include "kernel_types.h"
+#include "net/ng_pktbuf.h"
+#include "net/ng_netapi.h"
+#include "net/ng_netif/hdr.h"
+#include "net/ng_sixlowpan/frag.h"
+#include "net/ng_sixlowpan/netif.h"
+#include "utlist.h"
+
+#include "rbuf.h"
+
+#define ENABLE_DEBUG    (0)
+#include "debug.h"
+
+static uint16_t _tag;
+
+static inline uint16_t _floor8(uint16_t length)
+{
+    return length & 0xf8U;
+}
+
+static inline size_t _min(size_t a, size_t b)
+{
+    return (a < b) ? a : b;
+}
+
+static ng_pktsnip_t *_build_frag_pkt(ng_pktsnip_t *pkt, size_t payload_len,
+                                     size_t size)
+{
+    ng_netif_hdr_t *hdr = pkt->data, *new_hdr;
+    ng_pktsnip_t *netif, *frag;
+
+    netif = ng_netif_hdr_build(ng_netif_hdr_get_src_addr(hdr), hdr->src_l2addr_len,
+                               ng_netif_hdr_get_dst_addr(hdr), hdr->dst_l2addr_len);
+
+    if (netif == NULL) {
+        DEBUG("6lo frag: error allocating new link-layer header\n");
+        return NULL;
+    }
+
+    new_hdr = netif->data;
+    new_hdr->if_pid = hdr->if_pid;
+    new_hdr->flags = hdr->flags;
+    new_hdr->rssi = hdr->rssi;
+    new_hdr->lqi = hdr->lqi;
+
+    frag = ng_pktbuf_add(NULL, NULL, _min(size, payload_len),
+                         NG_NETTYPE_SIXLOWPAN);
+
+    if (frag == NULL) {
+        DEBUG("6lo frag: error allocating first fragment\n");
+        ng_pktbuf_release(netif);
+        return NULL;
+    }
+
+    LL_PREPEND(frag, netif);
+
+    return frag;
+}
+
+static uint16_t _send_1st_fragment(ng_sixlowpan_netif_t *iface, ng_pktsnip_t *pkt,
+                                   size_t payload_len, size_t datagram_size)
+{
+    ng_pktsnip_t *frag;
+    uint16_t max_frag_size = _floor8(iface->max_frag_size -
+                                     (payload_len - datagram_size) -
+                                     sizeof(ng_sixlowpan_frag_t));
+    uint16_t local_offset = 0;
+    ng_sixlowpan_frag_t *hdr;
+    uint8_t *data;
+
+    DEBUG("6lo frag: determined max_frag_size = %" PRIu16 "\n", max_frag_size);
+
+    /* 6LoWPAN dispatches don't count into that */
+    max_frag_size += (payload_len - datagram_size);
+
+    frag = _build_frag_pkt(pkt, payload_len,
+                           max_frag_size + sizeof(ng_sixlowpan_frag_t));
+
+    if (frag == NULL) {
+        return 0;
+    }
+
+    hdr = frag->next->data;
+    data = (uint8_t *)(hdr + 1);
+
+    hdr->disp_size = byteorder_htons((uint16_t)datagram_size);
+    hdr->disp_size.u8[0] |= NG_SIXLOWPAN_FRAG_1_DISP;
+    hdr->tag = byteorder_htons(_tag);
+
+    pkt = pkt->next;    /* don't copy netif header */
+
+    while (pkt != NULL) {
+        size_t clen = _min(max_frag_size - local_offset, pkt->size);
+
+        memcpy(data + local_offset, pkt->data, clen);
+        local_offset += clen;
+
+        if (local_offset >= max_frag_size) {
+            break;
+        }
+
+        pkt = pkt->next;
+    }
+
+    DEBUG("6lo frag: send first fragment (datagram size: %u, "
+          "datagram tag: %" PRIu16 ", fragment size: %" PRIu16 ")\n",
+          (unsigned int)datagram_size, _tag, local_offset);
+    ng_netapi_send(iface->pid, frag);
+
+    return local_offset;
+}
+
+static uint16_t _send_nth_fragment(ng_sixlowpan_netif_t *iface, ng_pktsnip_t *pkt,
+                                   size_t payload_len, size_t datagram_size,
+                                   uint16_t offset)
+{
+    ng_pktsnip_t *frag;
+    uint16_t max_frag_size = _floor8(iface->max_frag_size - sizeof(ng_sixlowpan_frag_n_t));
+    uint16_t local_offset = 0, offset_count = 0;
+    ng_sixlowpan_frag_n_t *hdr;
+    uint8_t *data;
+
+    DEBUG("6lo frag: determined max_frag_size = %" PRIu16 "\n", max_frag_size);
+
+    frag = _build_frag_pkt(pkt,
+                           payload_len - offset + sizeof(ng_sixlowpan_frag_n_t),
+                           max_frag_size + sizeof(ng_sixlowpan_frag_n_t));
+
+    if (frag == NULL) {
+        return 0;
+    }
+
+    hdr = frag->next->data;
+    data = (uint8_t *)(hdr + 1);
+
+    /* XXX: truncation of datagram_size > 4095 may happen here */
+    hdr->disp_size = byteorder_htons((uint16_t)datagram_size);
+    hdr->disp_size.u8[0] |= NG_SIXLOWPAN_FRAG_N_DISP;
+    hdr->tag = byteorder_htons(_tag);
+    hdr->offset = (uint8_t)(offset >> 3);
+    pkt = pkt->next;    /* don't copy netif header */
+
+    while ((pkt != NULL) || (offset_count == offset)) {   /* go to offset */
+        offset_count += (uint16_t)pkt->size;
+
+        if (offset_count > offset) {    /* we overshot */
+            /* => copy rest of partly send packet snip */
+            uint16_t pkt_offset = offset - (offset_count - ((uint16_t)pkt->size));
+            size_t clen = _min(max_frag_size, pkt->size - pkt_offset);
+
+            memcpy(data, ((uint8_t *)pkt->data) + pkt_offset, clen);
+            local_offset = clen;
+            pkt = pkt->next;
+            break;
+        }
+
+        pkt = pkt->next;
+    }
+
+    if (local_offset < max_frag_size) { /* copy other packet snips */
+        while (pkt != NULL) {
+            size_t clen = _min(max_frag_size - local_offset, pkt->size);
+
+            memcpy(data + local_offset, pkt->data, clen);
+            local_offset += clen;
+
+            if (local_offset == max_frag_size) {
+                break;
+            }
+
+            pkt = pkt->next;
+        }
+    }
+
+    DEBUG("6lo frag: send first fragment (datagram size: %u, "
+          "datagram tag: %" PRIu16 ", offset: %" PRIu8 " (%u bytes), "
+          "fragment size: %" PRIu16 ")\n",
+          (unsigned int)datagram_size, _tag, hdr->offset, hdr->offset << 3,
+          local_offset);
+    ng_netapi_send(iface->pid, frag);
+
+    return local_offset;
+}
+
+void ng_sixlowpan_frag_send(kernel_pid_t pid, ng_pktsnip_t *pkt,
+                            size_t payload_len, size_t datagram_size)
+{
+    ng_sixlowpan_netif_t *iface = ng_sixlowpan_netif_get(pid);
+    uint16_t offset = 0, res;
+
+#if defined(DEVELHELP) && defined(ENABLE_DEBUG)
+    if (iface == NULL) {
+        DEBUG("6lo frag: iface == NULL, expect segmentation fault.\n");
+    }
+#endif
+
+    if ((res = _send_1st_fragment(iface, pkt, payload_len, datagram_size)) == 0) {
+        /* error sending first fragment */
+        ng_pktbuf_release(pkt);
+        return;
+    }
+
+    offset += res;
+
+    while (offset < datagram_size) {
+        if ((res = _send_nth_fragment(iface, pkt, payload_len, datagram_size,
+                                      offset)) == 0) {
+            /* error sending first fragment */
+            ng_pktbuf_release(pkt);
+            return;
+        }
+
+        offset += res;
+    }
+
+    /* remove original packet from packet buffer */
+    ng_pktbuf_release(pkt);
+    _tag++;
+}
+
+void ng_sixlowpan_frag_handle_pkt(ng_pktsnip_t *pkt)
+{
+    ng_netif_hdr_t *hdr = pkt->next->data;
+    ng_sixlowpan_frag_t *frag = pkt->data;
+    uint16_t offset = 0;
+    size_t frag_size;
+
+    switch (frag->disp_size.u8[0] & NG_SIXLOWPAN_FRAG_DISP_MASK) {
+        case NG_SIXLOWPAN_FRAG_1_DISP:
+            frag_size = (pkt->size - sizeof(ng_sixlowpan_frag_t));
+            break;
+
+        case NG_SIXLOWPAN_FRAG_N_DISP:
+            offset = (((ng_sixlowpan_frag_n_t *)frag)->offset * 8);
+            frag_size = (pkt->size - sizeof(ng_sixlowpan_frag_n_t));
+            break;
+
+        default:
+            DEBUG("6lo rbuf: Not a fragment header.\n");
+            ng_pktbuf_release(pkt);
+
+            return;
+    }
+
+    rbuf_add(hdr, frag, frag_size, offset);
+
+    ng_pktbuf_release(pkt);
+}
+
+/** @} */
diff --git a/sys/net/network_layer/ng_sixlowpan/frag/rbuf.c b/sys/net/network_layer/ng_sixlowpan/frag/rbuf.c
new file mode 100644
index 0000000000..dab51fb62c
--- /dev/null
+++ b/sys/net/network_layer/ng_sixlowpan/frag/rbuf.c
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2015 Martine Lenders <mlenders@inf.fu-berlin.de>
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @{
+ *
+ * @file
+ */
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include "rbuf.h"
+#include "net/ng_netapi.h"
+#include "net/ng_netif.h"
+#include "net/ng_netif/hdr.h"
+#include "net/ng_pktbuf.h"
+#include "net/ng_ipv6/netif.h"
+#include "net/ng_sixlowpan.h"
+#include "net/ng_sixlowpan/frag.h"
+#include "thread.h"
+#include "timex.h"
+#include "vtimer.h"
+#include "utlist.h"
+
+#define ENABLE_DEBUG    (0)
+#include "debug.h"
+
+#ifndef RBUF_INT_SIZE
+#define RBUF_INT_SIZE   (NG_IPV6_NETIF_DEFAULT_MTU * RBUF_SIZE / 127)
+#endif
+
+static rbuf_int_t rbuf_int[RBUF_INT_SIZE];
+
+static rbuf_t rbuf[RBUF_SIZE];
+
+#if ENABLE_DEBUG
+static char l2addr_str[3 * RBUF_L2ADDR_MAX_LEN];
+#endif
+
+/* ------------------------------------
+ * internal function definitions
+ * ------------------------------------*/
+/* checks whether start and end are in given interval i */
+static inline bool _rbuf_int_in(rbuf_int_t *i, uint16_t start, uint16_t end);
+/* gets a free entry from interval buffer */
+static rbuf_int_t *_rbuf_int_get_free(void);
+/* remove entry from reassembly buffer */
+static void _rbuf_rem(rbuf_t *entry);
+/* update interval buffer of entry */
+static bool _rbuf_update_ints(rbuf_t *entry, uint16_t offset, size_t frag_size);
+/* checks timeouts and removes entries if necessary (oldest if full) */
+static void _rbuf_gc(void);
+/* gets an entry identified by its tupel */
+static rbuf_t *_rbuf_get(const void *src, size_t src_len,
+                         const void *dst, size_t dst_len,
+                         size_t size, uint16_t tag);
+
+void rbuf_add(ng_netif_hdr_t *netif_hdr, ng_sixlowpan_frag_t *frag,
+              size_t frag_size, size_t offset)
+{
+    rbuf_t *entry;
+    rbuf_int_t *ptr;
+    uint8_t *data = ((uint8_t *)frag) + sizeof(ng_sixlowpan_frag_t);
+
+    _rbuf_gc();
+    entry = _rbuf_get(ng_netif_hdr_get_src_addr(netif_hdr), netif_hdr->src_l2addr_len,
+                      ng_netif_hdr_get_dst_addr(netif_hdr), netif_hdr->dst_l2addr_len,
+                      byteorder_ntohs(frag->disp_size) & NG_SIXLOWPAN_FRAG_SIZE_MASK,
+                      byteorder_ntohs(frag->tag));
+
+    if (entry == NULL) {
+        DEBUG("6lo rbuf: reassembly buffer full.\n");
+        return;
+    }
+
+    ptr = entry->ints;
+
+    while (ptr != NULL) {
+        if (_rbuf_int_in(ptr, offset, offset + frag_size - 1)) {
+            DEBUG("6lo rfrag: overlapping or same intervals, discarding datagram\n");
+            ng_pktbuf_release(entry->pkt);
+            _rbuf_rem(entry);
+            return;
+        }
+
+        ptr = ptr->next;
+    }
+
+    if (_rbuf_update_ints(entry, offset, frag_size)) {
+        if (offset == 0) {
+            /* some dispatches do not count to datagram size and we need
+             * more space because of that */
+            switch (data[0]) {
+                case NG_SIXLOWPAN_UNCOMPRESSED:
+                    if (ng_pktbuf_realloc_data(entry->pkt, entry->pkt->size + 1) < 0) {
+                        DEBUG("6lo rbuf: could not reallocate packet data.\n");
+                        return;
+                    }
+
+                    /* move already inserted fragments 1 to the right */
+                    for (int i = entry->pkt->size - 1; i > 0; i--) {
+                        uint8_t *d = ((uint8_t *)(entry->pkt->data)) + i;
+                        *d = *(d - 1);
+                    }
+
+                default:
+                    break;
+            }
+        }
+        else {
+            data += 1;  /* skip offset field in fragmentation header */
+        }
+
+        /* also adapt offset according to stored dispatch */
+        /* above case only applies for first fragment incoming, this for all */
+        switch (*((uint8_t *)entry->pkt->data)) {
+            case NG_SIXLOWPAN_UNCOMPRESSED:
+                offset++;
+                break;
+
+            default:
+                break;
+        }
+
+        DEBUG("6lo rbuf: add fragment data\n");
+        memcpy(((uint8_t *)entry->pkt->data) + offset, data, frag_size);
+        entry->cur_size += (uint16_t)frag_size;
+    }
+
+    if (entry->cur_size == entry->pkt->size) {
+        kernel_pid_t iface = netif_hdr->if_pid;
+        ng_pktsnip_t *netif = ng_netif_hdr_build(entry->src, entry->src_len,
+                              entry->dst, entry->dst_len);
+
+        if (netif == NULL) {
+            DEBUG("6lo rbuf: error allocating netif header\n");
+            ng_pktbuf_release(entry->pkt);
+            return;
+        }
+
+        netif_hdr = netif->data;
+        netif_hdr->if_pid = iface;
+        entry->pkt->next = netif;
+
+        DEBUG("6lo rbuf: datagram complete, send to self\n");
+        ng_netapi_receive(thread_getpid(), entry->pkt);
+        _rbuf_rem(entry);
+    }
+}
+
+static inline bool _rbuf_int_in(rbuf_int_t *i, uint16_t start, uint16_t end)
+{
+    return (((i->start < start) && (start <= i->end)) ||
+            ((start < i->start) && (i->start <= end)) ||
+            ((i->start == start) && (i->end == end)));
+}
+
+static rbuf_int_t *_rbuf_int_get_free(void)
+{
+    for (int i = 0; i < RBUF_INT_SIZE; i++) {
+        if (rbuf_int[i].end == 0) { /* start must be smaller than end anyways*/
+            return rbuf_int + i;
+        }
+    }
+
+    return NULL;
+}
+
+static void _rbuf_rem(rbuf_t *entry)
+{
+    while (entry->ints != NULL) {
+        rbuf_int_t *next = entry->ints->next;
+
+        entry->ints->start = 0;
+        entry->ints->end = 0;
+        entry->ints->next = NULL;
+        entry->ints = next;
+    }
+
+    entry->pkt = NULL;
+}
+
+static bool _rbuf_update_ints(rbuf_t *entry, uint16_t offset, size_t frag_size)
+{
+    rbuf_int_t *new;
+    uint16_t end = (uint16_t)(offset + frag_size - 1);
+
+    new = _rbuf_int_get_free();
+
+    if (new == NULL) {
+        DEBUG("6lo rfrag: no space left in rbuf interval buffer.\n");
+        return false;
+    }
+
+    new->start = offset;
+    new->end = end;
+
+    DEBUG("6lo rfrag: add interval (%" PRIu16 ", %" PRIu16 ") to entry (%s, ",
+          new->start, new->end, ng_netif_addr_to_str(l2addr_str,
+                  sizeof(l2addr_str), entry->src, entry->src_len));
+    DEBUG("%s, %zu, %" PRIu16 ")\n", ng_netif_addr_to_str(l2addr_str,
+            sizeof(l2addr_str), entry->dst, entry->dst_len), entry->pkt->size,
+          entry->tag);
+
+    LL_PREPEND(entry->ints, new);
+
+    return true;
+}
+
+static void _rbuf_gc(void)
+{
+    rbuf_t *oldest = NULL;
+    timex_t now;
+    int i;
+
+    vtimer_now(&now);
+
+    for (i = 0; i < RBUF_SIZE; i++) {
+        if ((rbuf[i].pkt != NULL) &&
+            ((now.seconds - rbuf[i].arrival) > RBUF_TIMEOUT)) {
+            DEBUG("6lo rfrag: entry (%s, ", ng_netif_addr_to_str(l2addr_str,
+                    sizeof(l2addr_str), rbuf[i].src, rbuf[i].src_len));
+            DEBUG("%s, %zu, %" PRIu16 ") timed out\n",
+                  ng_netif_addr_to_str(l2addr_str, sizeof(l2addr_str), rbuf[i].dst,
+                                       rbuf[i].dst_len),
+                  rbuf[i].pkt->size, rbuf[i].tag);
+
+            ng_pktbuf_release(rbuf[i].pkt);
+            _rbuf_rem(&(rbuf[i]));
+        }
+        else if ((oldest == NULL) || (rbuf[i].arrival < oldest->arrival)) {
+            oldest = &(rbuf[i]);
+        }
+    }
+
+    if (((i >= RBUF_SIZE) && (oldest != NULL))) {
+        DEBUG("6lo rfrag: reassembly buffer full, remove oldest entry");
+        ng_pktbuf_release(oldest->pkt);
+        _rbuf_rem(oldest);
+    }
+}
+
+static rbuf_t *_rbuf_get(const void *src, size_t src_len,
+                         const void *dst, size_t dst_len,
+                         size_t size, uint16_t tag)
+{
+    rbuf_t *res = NULL;
+    timex_t now;
+
+    vtimer_now(&now);
+
+    for (int i = 0; i < RBUF_SIZE; i++) {
+        /* check first if entry already available */
+        if ((rbuf[i].pkt != NULL) && (rbuf[i].pkt->size == size) &&
+            (rbuf[i].tag == tag) && (rbuf[i].src_len == src_len) &&
+            (rbuf[i].dst_len == dst_len) &&
+            (memcmp(rbuf[i].src, src, src_len) == 0) &&
+            (memcmp(rbuf[i].dst, dst, dst_len) == 0)) {
+            DEBUG("6lo rfrag: entry (%s, ", ng_netif_addr_to_str(l2addr_str,
+                  sizeof(l2addr_str), rbuf[i].src, rbuf[i].src_len));
+            DEBUG("%s, %zu, %" PRIu16 ") found\n",
+                  ng_netif_addr_to_str(l2addr_str, sizeof(l2addr_str),
+                                       rbuf[i].dst, rbuf[i].dst_len),
+                  rbuf[i].pkt->size, rbuf[i].tag);
+            res->arrival = now.seconds;
+            return &(rbuf[i]);
+        }
+
+        /* if there is a free spot: remember it */
+        if ((res == NULL) && (rbuf[i].cur_size == 0)) {
+            res = &(rbuf[i]);
+        }
+    }
+
+    if (res != NULL) { /* entry not in buffer but found empty spot */
+        res->pkt = ng_pktbuf_add(NULL, NULL, size, NG_NETTYPE_SIXLOWPAN);
+
+        if (res->pkt == NULL) {
+            DEBUG("6lo rfrag: can not allocate reassembly buffer space.\n");
+            return NULL;
+        }
+
+        res->arrival = now.seconds;
+        memcpy(res->src, src, src_len);
+        memcpy(res->dst, dst, dst_len);
+        res->src_len = src_len;
+        res->dst_len = dst_len;
+        res->tag = tag;
+        res->cur_size = 0;
+
+        DEBUG("6lo rfrag: entry (%s, ", ng_netif_addr_to_str(l2addr_str,
+                sizeof(l2addr_str), res->src, res->src_len));
+        DEBUG("%s, %zu, %" PRIu16 ") created\n",
+              ng_netif_addr_to_str(l2addr_str, sizeof(l2addr_str), res->dst,
+                                   res->dst_len), res->pkt->size, res->tag);
+    }
+
+    return res;
+}
+
+/** @} */
diff --git a/sys/net/network_layer/ng_sixlowpan/frag/rbuf.h b/sys/net/network_layer/ng_sixlowpan/frag/rbuf.h
new file mode 100644
index 0000000000..bee9711184
--- /dev/null
+++ b/sys/net/network_layer/ng_sixlowpan/frag/rbuf.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2015 Martine Lenders <mlenders@inf.fu-berlin.de>
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @ingroup     net_ng_sixlowpan_frag
+ * @{
+ *
+ * @file
+ * @internal
+ * @brief   6LoWPAN reassembly buffer
+ *
+ * @author  Martine Lenders <mlenders@inf.fu-berlin.de>
+ */
+#ifndef NG_SIXLOWPAN_FRAG_RBUF_H_
+#define NG_SIXLOWPAN_FRAG_RBUF_H_
+
+#include <inttypes.h>
+
+#include "net/ng_netif/hdr.h"
+#include "net/ng_pkt.h"
+#include "timex.h"
+
+#include "net/ng_sixlowpan/frag.h"
+#ifdef __cplusplus
+
+extern "C" {
+#endif
+
+#define RBUF_L2ADDR_MAX_LEN (8U)    /**< maximum length for link-layer addresses */
+#define RBUF_SIZE           (4U)    /**< size of the reassembly buffer */
+#define RBUF_TIMEOUT        (3U)    /**< timeout for reassembly in seconds */
+
+/**
+ * @brief   Fragment intervals to identify limits of fragments.
+ *
+ * @note    Fragments MUST NOT overlap and overlapping fragments are to be
+ *          discarded
+ *
+ * @see <a href="https://tools.ietf.org/html/rfc4944#section-5.3">
+ *          RFC 4944, section 5.3
+ *      </a>
+ */
+typedef struct rbuf_int {
+    struct rbuf_int *next;  /**< next element in interval list */
+    uint16_t start;         /**< start byte of interval */
+    uint16_t end;           /**< end byte of interval */
+} rbuf_int_t;
+
+/**
+ * @brief   An entry in the 6LoWPAN reassembly buffer.
+ *
+ * @details A receipient of a fragment SHALL use
+ *
+ * 1. the source address,
+ * 2. the destination address,
+ * 3. the datagram size (ng_pktsnip_t::size of rbuf_t::pkt), and
+ * 4. the datagram tag
+ *
+ * to identify all fragments that belong to the given datagram.
+ *
+ * @see <a href="https://tools.ietf.org/html/rfc4944#section-5.3">
+ *          RFC 4944, section 5.3
+ *      </a>
+ */
+typedef struct {
+    rbuf_int_t *ints;                   /**< intervals of the fragment */
+    ng_pktsnip_t *pkt;                  /**< the reassembled packet in packet buffer */
+    uint32_t arrival;                   /**< time in seconds of arrival of last
+                                         *   received fragment */
+    uint8_t src[RBUF_L2ADDR_MAX_LEN];   /**< source address */
+    uint8_t dst[RBUF_L2ADDR_MAX_LEN];   /**< destination address */
+    uint8_t src_len;                    /**< length of source address */
+    uint8_t dst_len;                    /**< length of destination address */
+    uint16_t tag;                       /**< the datagram's tag */
+    uint16_t cur_size;                  /**< the datagram's current size */
+} rbuf_t;
+
+/**
+ * @brief   Adds a new fragment to the reassembly buffer.
+ *
+ * @param[in] netif_hdr     The interface header of the fragment, with
+ *                          ng_netif_hdr_t::if_pid and its source and
+ *                          destination address set.
+ * @param[in] frag          The fragment to add.
+ * @param[in] frag_size     The fragment's size.
+ * @param[in] offset        The fragment's offset.
+ */
+void rbuf_add(ng_netif_hdr_t *netif_hdr, ng_sixlowpan_frag_t *frag,
+              size_t frag_size, size_t offset);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NG_SIXLOWPAN_FRAG_RBUF_H_ */
+/** @} */
diff --git a/sys/net/network_layer/ng_sixlowpan/ng_sixlowpan.c b/sys/net/network_layer/ng_sixlowpan/ng_sixlowpan.c
index af9f973878..83a3c351dc 100644
--- a/sys/net/network_layer/ng_sixlowpan/ng_sixlowpan.c
+++ b/sys/net/network_layer/ng_sixlowpan/ng_sixlowpan.c
@@ -18,6 +18,7 @@
 #include "utlist.h"
 
 #include "net/ng_sixlowpan.h"
+#include "net/ng_sixlowpan/frag.h"
 #include "net/ng_sixlowpan/netif.h"
 
 #define ENABLE_DEBUG    (0)
@@ -80,6 +81,12 @@ void _receive(ng_pktsnip_t *pkt)
         LL_DELETE(pkt, sixlowpan);
         ng_pktbuf_release(sixlowpan);
     }
+#ifdef MODULE_NG_SIXLOWPAN_FRAG
+    else if (ng_sixlowpan_frag_is((ng_sixlowpan_frag_t *)dispatch)) {
+        DEBUG("6lo: received 6LoWPAN fragment\n");
+        ng_sixlowpan_frag_handle_pkt(pkt);
+    }
+#endif
     else {
         DEBUG("6lo: dispatch %02x ... is not supported\n",
               dispatch[0]);
@@ -137,8 +144,6 @@ void _send(ng_pktsnip_t *pkt)
      * length is the length of the IPv6 datagram + 6LoWPAN dispatches,
      * while the datagram size is the size of only the IPv6 datagram */
     payload_len = ng_pkt_len(ipv6);
-    /* cppcheck: datagram_size will be read by ng_sixlowpan_frag implementation */
-    /* cppcheck-suppress unreadVariable */
     datagram_size = (uint16_t)payload_len;
 
     /* use sixlowpan packet snip as temporary one */
@@ -197,8 +202,17 @@ void _send(ng_pktsnip_t *pkt)
 
         return;
     }
+#ifdef MODULE_NG_SIXLOWPAN_FRAG
+    else {
+        DEBUG("6lo: Send fragmented (%u > %" PRIu16 ")\n",
+              (unsigned int)payload_len, max_frag_size);
+        ng_sixlowpan_frag_send(hdr->if_pid, pkt, payload_len, datagram_size);
+    }
+#else
+    (void)datagram_size;
     DEBUG("6lo: packet too big (%u> %" PRIu16 ")\n",
           (unsigned int)payload_len, max_frag_size);
+#endif
 }
 
 static void *_event_loop(void *args)
-- 
GitLab