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