mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
gnrc_ipv6_ext_frag: Initial import of IPv6 reassembly
This commit is contained in:
parent
409e04267f
commit
f4b8ba32f3
@ -264,6 +264,11 @@ ifneq (,$(filter gnrc_rpl_srh,$(USEMODULE)))
|
||||
USEMODULE += gnrc_ipv6_ext_rh
|
||||
endif
|
||||
|
||||
ifneq (,$(filter gnrc_ipv6_ext_frag,$(USEMODULE)))
|
||||
USEMODULE += gnrc_ipv6_ext
|
||||
USEMODULE += xtimer
|
||||
endif
|
||||
|
||||
ifneq (,$(filter gnrc_ipv6_ext_rh,$(USEMODULE)))
|
||||
USEMODULE += gnrc_ipv6_ext
|
||||
endif
|
||||
|
@ -29,6 +29,7 @@
|
||||
|
||||
#include "net/gnrc/pkt.h"
|
||||
#include "net/ipv6/ext.h"
|
||||
#include "timex.h"
|
||||
|
||||
#ifdef MODULE_GNRC_IPV6_EXT_RH
|
||||
#include "net/gnrc/ipv6/ext/rh.h"
|
||||
@ -38,6 +39,45 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @defgroup net_gnrc_ipv6_ext_conf IPv6 extension header compile configurations
|
||||
* @ingroup net_gnrc_ipv6_ext
|
||||
* @ingroup config
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* @brief IPv6 fragmentation reassembly buffer size
|
||||
*
|
||||
* This limits the total amount of datagrams that can be reassembled at the same time.
|
||||
*
|
||||
* @note Only applicable with [gnrc_ipv6_ext_frag](@ref net_gnrc_ipv6_ext_frag) module
|
||||
*/
|
||||
#ifndef GNRC_IPV6_EXT_FRAG_RBUF_SIZE
|
||||
#define GNRC_IPV6_EXT_FRAG_RBUF_SIZE (1U)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief The number of total allocatable @ref gnrc_ipv6_ext_frag_limits_t objects
|
||||
*
|
||||
* This is the maximum number of receivable fragments, shared between all
|
||||
* fragmented datagrams
|
||||
*
|
||||
* @note Only applicable with [gnrc_ipv6_ext_frag](@ref net_gnrc_ipv6_ext_frag) module
|
||||
*/
|
||||
#ifndef GNRC_IPV6_EXT_FRAG_LIMITS_POOL_SIZE
|
||||
#define GNRC_IPV6_EXT_FRAG_LIMITS_POOL_SIZE (GNRC_IPV6_EXT_FRAG_RBUF_SIZE * 2U)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Timeout for IPv6 fragmentation reassembly buffer entries in microseconds
|
||||
*
|
||||
* @note Only applicable with [gnrc_ipv6_ext_frag](@ref net_gnrc_ipv6_ext_frag) module
|
||||
*/
|
||||
#ifndef GNRC_IPV6_EXT_FRAG_RBUF_TIMEOUT_US
|
||||
#define GNRC_IPV6_EXT_FRAG_RBUF_TIMEOUT_US (10U * US_PER_SEC)
|
||||
#endif
|
||||
/** @} **/
|
||||
|
||||
/**
|
||||
* @brief Builds an extension header for sending.
|
||||
*
|
||||
|
147
sys/include/net/gnrc/ipv6/ext/frag.h
Normal file
147
sys/include/net/gnrc/ipv6/ext/frag.h
Normal file
@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Freie Universität Berlin
|
||||
*
|
||||
* 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_gnrc_ipv6_ext_frag Support for IPv6 fragmentation extension
|
||||
* @ingroup net_gnrc_ipv6_ext
|
||||
* @brief GNRC implementation of IPv6 fragmentation extension
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief GNRC fragmentation extension definitions
|
||||
*
|
||||
* @author Martine Lenders <m.lenders@fu-berlin.de>
|
||||
*/
|
||||
#ifndef NET_GNRC_IPV6_EXT_FRAG_H
|
||||
#define NET_GNRC_IPV6_EXT_FRAG_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "clist.h"
|
||||
#include "net/gnrc/pkt.h"
|
||||
#include "net/gnrc/pktbuf.h"
|
||||
#include "net/ipv6/hdr.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Message type to time reassembly buffer garbage collection
|
||||
*/
|
||||
#define GNRC_IPV6_EXT_FRAG_RBUF_GC (0xfe00U)
|
||||
|
||||
/**
|
||||
* @brief Data type to describe limits of a single fragment in the reassembly
|
||||
* buffer
|
||||
*/
|
||||
typedef struct gnrc_ipv6_ext_frag_limits {
|
||||
struct gnrc_ipv6_ext_frag_limits *next; /**< limits of next fragment */
|
||||
uint16_t start; /**< the start (= offset) of the fragment */
|
||||
uint16_t end; /**< the exclusive end (= offset + length) of the
|
||||
* fragment */
|
||||
} gnrc_ipv6_ext_frag_limits_t;
|
||||
|
||||
/**
|
||||
* @brief A reassembly buffer entry
|
||||
*/
|
||||
typedef struct {
|
||||
gnrc_pktsnip_t *pkt; /**< the (partly) reassembled packet */
|
||||
ipv6_hdr_t *ipv6; /**< the IPv6 header of gnrc_ipv6_ext_frag_rbuf_t::pkt */
|
||||
/**
|
||||
* @brief The limits of the fragments in the reassembled packet
|
||||
*
|
||||
* @note Members of this list can be cast to gnrc_ipv6_ext_frag_limits_t.
|
||||
*/
|
||||
clist_node_t limits;
|
||||
uint32_t id; /**< the identification from the fragment headers */
|
||||
uint32_t arrival; /**< arrival time of last received fragment */
|
||||
uint16_t pkt_len; /**< length of gnrc_ipv6_ext_frag_rbuf_t::pkt */
|
||||
uint8_t last; /**< received last fragment */
|
||||
} gnrc_ipv6_ext_frag_rbuf_t;
|
||||
|
||||
/**
|
||||
* @brief Initializes IPv6 fragmentation and reassembly
|
||||
* @internal
|
||||
*/
|
||||
void gnrc_ipv6_ext_frag_init(void);
|
||||
|
||||
/**
|
||||
* @brief Reassemble fragmented IPv6 packet
|
||||
*
|
||||
* @param[in] pkt A fragment of the IPv6 packet to be reassembled containing
|
||||
* the fragment header in the first snip.
|
||||
*
|
||||
* @return The reassembled packet when @p pkt completed the reassembly
|
||||
* @return NULL, when there are still fragments missing or an error occured
|
||||
* during reassembly
|
||||
*/
|
||||
gnrc_pktsnip_t *gnrc_ipv6_ext_frag_reass(gnrc_pktsnip_t *pkt);
|
||||
|
||||
/**
|
||||
* @name Reassembly buffer operations
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* @brief Get a reassembly buffer by the identifying parameters
|
||||
*
|
||||
* @internal
|
||||
* @see [RFC 8200, section 4.5](https://tools.ietf.org/html/rfc8200#section-4.5)
|
||||
*
|
||||
* @param[in] hdr IPv6 header to get source and destination address from.
|
||||
* @param[in] id The identification from the fragment header.
|
||||
*
|
||||
* @return A reassembly buffer matching @p id ipv6_hdr_t::src and ipv6_hdr::dst
|
||||
* of @p hdr or first free reassembly buffer. Will never be NULL, as
|
||||
* in the case of the reassembly buffer being full, the entry with the
|
||||
* lowest gnrc_ipv6_ext_frag_rbuf_t::arrival (serial-number-like) is
|
||||
* removed.
|
||||
*/
|
||||
gnrc_ipv6_ext_frag_rbuf_t *gnrc_ipv6_ext_frag_rbuf_get(ipv6_hdr_t *ipv6,
|
||||
uint32_t id);
|
||||
|
||||
/**
|
||||
* @brief Frees a reassembly buffer entry (but does not release its
|
||||
* gnrc_ipv6_ext_frag_rbuf_t::pkt)
|
||||
*
|
||||
* @param[in] rbuf A reassembly buffer entry.
|
||||
*/
|
||||
void gnrc_ipv6_ext_frag_rbuf_free(gnrc_ipv6_ext_frag_rbuf_t *rbuf);
|
||||
|
||||
/**
|
||||
* @brief Delete a reassembly buffer entry (and release its
|
||||
* gnrc_ipv6_ext_frag_rbuf_t::pkt)
|
||||
*
|
||||
* @note May be used by the IPv6 thread to remove a timed out reassembly
|
||||
* buffer entry.
|
||||
*
|
||||
* @param[in] rbuf A reassembly buffer entry.
|
||||
*/
|
||||
static inline void gnrc_ipv6_ext_frag_rbuf_del(gnrc_ipv6_ext_frag_rbuf_t *rbuf)
|
||||
{
|
||||
gnrc_pktbuf_release(rbuf->pkt);
|
||||
rbuf->pkt = NULL;
|
||||
gnrc_ipv6_ext_frag_rbuf_free(rbuf);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Garbage-collect reassembly buffer
|
||||
*
|
||||
* This calls @ref gnrc_ipv6_ext_frag_rbuf_del() for all reassembly buffer
|
||||
* entries for which * gnrc_ipv6_ext_frag_rbuf_t::arrival is
|
||||
* @ref GNRC_IPV6_EXT_FRAG_RBUF_TIMEOUT_US in the past.
|
||||
*/
|
||||
void gnrc_ipv6_ext_frag_rbuf_gc(void);
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* NET_GNRC_IPV6_EXT_FRAG_H */
|
||||
/** @} */
|
@ -13,6 +13,9 @@ endif
|
||||
ifneq (,$(filter gnrc_ipv6_ext,$(USEMODULE)))
|
||||
DIRS += network_layer/ipv6/ext
|
||||
endif
|
||||
ifneq (,$(filter gnrc_ipv6_ext_frag,$(USEMODULE)))
|
||||
DIRS += network_layer/ipv6/ext/frag
|
||||
endif
|
||||
ifneq (,$(filter gnrc_ipv6_ext_rh,$(USEMODULE)))
|
||||
DIRS += network_layer/ipv6/ext/rh
|
||||
endif
|
||||
|
3
sys/net/gnrc/network_layer/ipv6/ext/frag/Makefile
Normal file
3
sys/net/gnrc/network_layer/ipv6/ext/frag/Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
MODULE := gnrc_ipv6_ext_frag
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
401
sys/net/gnrc/network_layer/ipv6/ext/frag/gnrc_ipv6_ext_frag.c
Normal file
401
sys/net/gnrc/network_layer/ipv6/ext/frag/gnrc_ipv6_ext_frag.c
Normal file
@ -0,0 +1,401 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Freie Universität Berlin
|
||||
*
|
||||
* 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
|
||||
* @author Martine Lenders <m.lenders@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "byteorder.h"
|
||||
#include "net/ipv6/ext/frag.h"
|
||||
#include "net/ipv6/addr.h"
|
||||
#include "net/gnrc/ipv6/ext.h"
|
||||
#include "net/gnrc/pktbuf.h"
|
||||
#include "sched.h"
|
||||
#include "xtimer.h"
|
||||
|
||||
#include "net/gnrc/ipv6/ext/frag.h"
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
static gnrc_ipv6_ext_frag_rbuf_t _rbuf[GNRC_IPV6_EXT_FRAG_RBUF_SIZE];
|
||||
static gnrc_ipv6_ext_frag_limits_t _limits_pool[GNRC_IPV6_EXT_FRAG_LIMITS_POOL_SIZE];
|
||||
static clist_node_t _free_limits;
|
||||
static xtimer_t _gc_xtimer;
|
||||
static msg_t _gc_msg = { .type = GNRC_IPV6_EXT_FRAG_RBUF_GC };
|
||||
|
||||
typedef enum {
|
||||
FRAG_LIMITS_NEW = 0, /**< limits are not present and do not overlap */
|
||||
FRAG_LIMITS_DUPLICATE, /**< fragment limits are already present */
|
||||
FRAG_LIMITS_OVERLAP, /**< limits overlap */
|
||||
FRAG_LIMITS_FULL, /**< no free gnrc_ipv6_ext_frag_limits_t object */
|
||||
} _limits_res_t;
|
||||
|
||||
void gnrc_ipv6_ext_frag_init(void)
|
||||
{
|
||||
#ifdef TEST_SUITES
|
||||
memset(_rbuf, 0, sizeof(_rbuf));
|
||||
#endif
|
||||
for (unsigned i = 0; i < GNRC_IPV6_EXT_FRAG_LIMITS_POOL_SIZE; i++) {
|
||||
clist_rpush(&_free_limits, (clist_node_t *)&_limits_pool[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ===============
|
||||
* IPv6 reassembly
|
||||
* ===============
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Initializes a reassembly buffer entry
|
||||
*
|
||||
* @param[in] rbuf A reassembly buffer entry.
|
||||
* @param[in] ipv6 The IPv6 header for the reassembly buffer entry.
|
||||
* @param[in] id The identification from the fragment header.
|
||||
*/
|
||||
static inline void _init_rbuf(gnrc_ipv6_ext_frag_rbuf_t *rbuf, ipv6_hdr_t *ipv6,
|
||||
uint32_t id);
|
||||
|
||||
/**
|
||||
* @brief Checks if given fragment limits overlap with fragment limits already
|
||||
* in a given reassembly buffer entry
|
||||
*
|
||||
* If no overlap exists the new limits are added to @p rbuf.
|
||||
*
|
||||
* @param[in, out] rbuf A reassembly buffer entry.
|
||||
* @param[in] offset A fragment offset.
|
||||
* @param[in] pkt_len The length of the packet.
|
||||
*
|
||||
* @return see _limits_res_t.
|
||||
*/
|
||||
static _limits_res_t _overlaps(gnrc_ipv6_ext_frag_rbuf_t *rbuf,
|
||||
unsigned offset, unsigned pkt_len);
|
||||
|
||||
/**
|
||||
* @brief Sets the next header field of a header.
|
||||
*
|
||||
* @pre `hdr_snip->type` $\in$ {GNRC_NETTYPE_IPV6, GNRC_NETTYPE_IPV6_EXT}
|
||||
*
|
||||
* @param[in] hdr_snip A header
|
||||
* @param[in] nh A protocol number
|
||||
*/
|
||||
static inline void _set_nh(gnrc_pktsnip_t *hdr_snip, uint8_t nh);
|
||||
|
||||
/**
|
||||
* @brief Checks if a fragmented packet is completely reassembled.
|
||||
*
|
||||
* @param[in] rbuf A reassembly buffer entry.
|
||||
*
|
||||
* @return The reassembled packet on if it is completed.
|
||||
* @return NULL, if the packet is not completely reassembled yet
|
||||
*/
|
||||
static gnrc_pktsnip_t *_completed(gnrc_ipv6_ext_frag_rbuf_t *rbuf);
|
||||
|
||||
gnrc_pktsnip_t *gnrc_ipv6_ext_frag_reass(gnrc_pktsnip_t *pkt)
|
||||
{
|
||||
gnrc_ipv6_ext_frag_rbuf_t *rbuf;
|
||||
gnrc_pktsnip_t *fh_snip, *ipv6_snip;
|
||||
ipv6_hdr_t *ipv6;
|
||||
ipv6_ext_frag_t *fh;
|
||||
unsigned offset;
|
||||
uint8_t nh;
|
||||
|
||||
fh_snip = gnrc_pktbuf_mark(pkt, sizeof(ipv6_ext_frag_t),
|
||||
GNRC_NETTYPE_IPV6_EXT);
|
||||
if (fh_snip == NULL) {
|
||||
DEBUG("ipv6_ext_frag: unable to mark fragmentation header\n");
|
||||
goto error_release;
|
||||
}
|
||||
fh = fh_snip->data;
|
||||
/* search IPv6 header */
|
||||
ipv6_snip = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_IPV6);
|
||||
assert(ipv6_snip != NULL);
|
||||
ipv6 = ipv6_snip->data;
|
||||
rbuf = gnrc_ipv6_ext_frag_rbuf_get(ipv6, byteorder_ntohl(fh->id));
|
||||
if (rbuf == NULL) {
|
||||
DEBUG("ipv6_ext_frag: reassembly buffer full\n");
|
||||
goto error_release;
|
||||
}
|
||||
rbuf->arrival = xtimer_now_usec();
|
||||
xtimer_set_msg(&_gc_xtimer, GNRC_IPV6_EXT_FRAG_RBUF_TIMEOUT_US, &_gc_msg,
|
||||
sched_active_pid);
|
||||
nh = fh->nh;
|
||||
offset = ipv6_ext_frag_get_offset(fh);
|
||||
switch (_overlaps(rbuf, offset, pkt->size)) {
|
||||
case FRAG_LIMITS_NEW:
|
||||
break;
|
||||
case FRAG_LIMITS_DUPLICATE:
|
||||
gnrc_pktbuf_release(pkt);
|
||||
return NULL;
|
||||
case FRAG_LIMITS_OVERLAP:
|
||||
DEBUG("ipv6_ext_frag: fragment overlaps with existing fragments\n");
|
||||
/* intentionally falls through */
|
||||
case FRAG_LIMITS_FULL:
|
||||
default:
|
||||
DEBUG("ipv6_ext_frag: can't store fragment limits\n");
|
||||
goto error_exit;
|
||||
}
|
||||
if (offset > 0) {
|
||||
size_t size_until = offset + pkt->size;
|
||||
|
||||
/* use IPv6 header in reassembly buffer from here on */
|
||||
ipv6 = rbuf->ipv6;
|
||||
/* subsequent fragment */
|
||||
if (!ipv6_ext_frag_more(fh)) {
|
||||
/* last fragment; add to rbuf->pkt_len */
|
||||
rbuf->last++;
|
||||
rbuf->pkt_len += size_until;
|
||||
}
|
||||
/* not divisible by 8 */
|
||||
else if ((pkt->size & 0x7)) {
|
||||
DEBUG("ipv6_ext_frag: fragment length not divisible by 8");
|
||||
goto error_exit;
|
||||
}
|
||||
if (rbuf->pkt == NULL) {
|
||||
rbuf->pkt = gnrc_pktbuf_add(fh_snip->next, NULL, size_until,
|
||||
GNRC_NETTYPE_UNDEF);
|
||||
if (rbuf->pkt == NULL) {
|
||||
DEBUG("ipv6_ext_frag: unable to create space for reassembled "
|
||||
"packet\n");
|
||||
goto error_exit;
|
||||
}
|
||||
}
|
||||
else if (rbuf->pkt->size < size_until) {
|
||||
if (gnrc_pktbuf_realloc_data(rbuf->pkt, size_until) != 0) {
|
||||
DEBUG("ipv6_ext_frag: unable to allocate space for reassembled "
|
||||
"packet\n");
|
||||
goto error_exit;
|
||||
}
|
||||
}
|
||||
memcpy(((uint8_t *)rbuf->pkt->data) + offset, pkt->data, pkt->size);
|
||||
/* we don't need the rest anymore */
|
||||
gnrc_pktbuf_release(pkt);
|
||||
return _completed(rbuf);
|
||||
}
|
||||
else if (!ipv6_ext_frag_more(fh)) {
|
||||
/* first fragment but actually not fragmented */
|
||||
_set_nh(fh_snip->next, nh);
|
||||
gnrc_pktbuf_remove_snip(pkt, fh_snip);
|
||||
gnrc_ipv6_ext_frag_rbuf_del(rbuf);
|
||||
ipv6->len = byteorder_htons(byteorder_ntohs(ipv6->len) -
|
||||
sizeof(ipv6_ext_frag_t));
|
||||
return pkt;
|
||||
}
|
||||
else {
|
||||
/* first fragment */
|
||||
uint16_t ipv6_len = byteorder_ntohs(ipv6->len);
|
||||
|
||||
/* not divisible by 8*/
|
||||
if ((pkt->size & 0x7)) {
|
||||
DEBUG("ipv6_ext_frag: fragment length not divisible by 8");
|
||||
goto error_exit;
|
||||
}
|
||||
_set_nh(fh_snip->next, nh);
|
||||
gnrc_pktbuf_remove_snip(pkt, fh_snip);
|
||||
/* TODO: RFC 8200 says "- 8"; determine if `sizeof(ipv6_ext_frag_t)` is
|
||||
* really needed*/
|
||||
rbuf->pkt_len += ipv6_len - pkt->size - sizeof(ipv6_ext_frag_t);
|
||||
if (rbuf->pkt != NULL) {
|
||||
/* first fragment but not first arriving */
|
||||
memcpy(rbuf->pkt->data, pkt->data, pkt->size);
|
||||
rbuf->pkt->next = pkt->next;
|
||||
rbuf->pkt->type = pkt->type;
|
||||
/* payload was copied to reassembly buffer so remove it */
|
||||
gnrc_pktbuf_remove_snip(pkt, pkt);
|
||||
rbuf->ipv6 = ipv6;
|
||||
return _completed(rbuf);
|
||||
}
|
||||
else {
|
||||
/* first fragment but first arriving */
|
||||
rbuf->pkt = pkt;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
error_exit:
|
||||
gnrc_ipv6_ext_frag_rbuf_del(rbuf);
|
||||
error_release:
|
||||
gnrc_pktbuf_release(pkt);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gnrc_ipv6_ext_frag_rbuf_t *gnrc_ipv6_ext_frag_rbuf_get(ipv6_hdr_t *ipv6,
|
||||
uint32_t id)
|
||||
{
|
||||
gnrc_ipv6_ext_frag_rbuf_t *res = NULL, *oldest = NULL;
|
||||
for (unsigned i = 0; i < GNRC_IPV6_EXT_FRAG_RBUF_SIZE; i++) {
|
||||
gnrc_ipv6_ext_frag_rbuf_t *tmp = &_rbuf[i];
|
||||
if (tmp->ipv6 != NULL) {
|
||||
if ((tmp->id == id) &&
|
||||
ipv6_addr_equal(&tmp->ipv6->src, &ipv6->src) &&
|
||||
ipv6_addr_equal(&tmp->ipv6->dst, &ipv6->dst)) {
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
else if (res == NULL) {
|
||||
res = tmp;
|
||||
_init_rbuf(res, ipv6, id);
|
||||
}
|
||||
if ((oldest == NULL) ||
|
||||
/* xtimer_now_usec() overflows every ~1.2 hours */
|
||||
((tmp->arrival - oldest->arrival) < (UINT32_MAX / 2))) {
|
||||
oldest = tmp;
|
||||
}
|
||||
}
|
||||
if (res == NULL) {
|
||||
assert(oldest != NULL); /* reassembly buffer is full, so there needs
|
||||
* to be an oldest entry */
|
||||
DEBUG("ipv6_ext_frag: dropping oldest entry\n");
|
||||
gnrc_ipv6_ext_frag_rbuf_del(oldest);
|
||||
res = oldest;
|
||||
_init_rbuf(res, ipv6, id);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void gnrc_ipv6_ext_frag_rbuf_free(gnrc_ipv6_ext_frag_rbuf_t *rbuf)
|
||||
{
|
||||
rbuf->ipv6 = NULL;
|
||||
while (rbuf->limits.next != NULL) {
|
||||
clist_node_t *tmp = clist_lpop(&rbuf->limits);
|
||||
clist_rpush(&_free_limits, tmp);
|
||||
}
|
||||
}
|
||||
|
||||
void gnrc_ipv6_ext_frag_rbuf_gc(void)
|
||||
{
|
||||
uint32_t now = xtimer_now_usec();
|
||||
for (unsigned i = 0; i < GNRC_IPV6_EXT_FRAG_RBUF_SIZE; i++) {
|
||||
gnrc_ipv6_ext_frag_rbuf_t *rbuf = &_rbuf[i];
|
||||
if ((now - rbuf->arrival) > GNRC_IPV6_EXT_FRAG_RBUF_TIMEOUT_US) {
|
||||
gnrc_ipv6_ext_frag_rbuf_del(rbuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
uint16_t start;
|
||||
uint16_t end;
|
||||
} _check_limits_t;
|
||||
|
||||
static inline void _init_rbuf(gnrc_ipv6_ext_frag_rbuf_t *rbuf, ipv6_hdr_t *ipv6,
|
||||
uint32_t id)
|
||||
{
|
||||
rbuf->ipv6 = ipv6;
|
||||
rbuf->id = id;
|
||||
rbuf->pkt_len = 0;
|
||||
rbuf->last = 0;
|
||||
}
|
||||
|
||||
static int _check_overlap(clist_node_t *node, void *arg)
|
||||
{
|
||||
_check_limits_t *limits = arg;
|
||||
gnrc_ipv6_ext_frag_limits_t *cur = (gnrc_ipv6_ext_frag_limits_t *)node;
|
||||
|
||||
return ((cur->start < limits->end) && (limits->start < cur->end));
|
||||
}
|
||||
|
||||
static int _limits_cmp(clist_node_t *a, clist_node_t *b)
|
||||
{
|
||||
gnrc_ipv6_ext_frag_limits_t *al = (gnrc_ipv6_ext_frag_limits_t *)a;
|
||||
gnrc_ipv6_ext_frag_limits_t *bl = (gnrc_ipv6_ext_frag_limits_t *)b;
|
||||
|
||||
return (int)al->start - (int)bl->start;
|
||||
}
|
||||
|
||||
static _limits_res_t _overlaps(gnrc_ipv6_ext_frag_rbuf_t *rbuf,
|
||||
unsigned offset, unsigned pkt_len)
|
||||
{
|
||||
_check_limits_t limits = { .start = offset >> 3U,
|
||||
.end = (offset + pkt_len) >> 3U };
|
||||
gnrc_ipv6_ext_frag_limits_t *res;
|
||||
|
||||
if (limits.start == limits.end) {
|
||||
/* might happen with last fragment */
|
||||
limits.end++;
|
||||
}
|
||||
res = (gnrc_ipv6_ext_frag_limits_t *)clist_foreach(&rbuf->limits,
|
||||
_check_overlap,
|
||||
&limits);
|
||||
if (res == NULL) {
|
||||
res = (gnrc_ipv6_ext_frag_limits_t *)clist_lpop(&_free_limits);
|
||||
if (res != NULL) {
|
||||
res->start = limits.start;
|
||||
res->end = limits.end;
|
||||
clist_rpush(&rbuf->limits, (clist_node_t *)res);
|
||||
clist_sort(&rbuf->limits, _limits_cmp);
|
||||
return FRAG_LIMITS_NEW;
|
||||
}
|
||||
else {
|
||||
return FRAG_LIMITS_FULL;
|
||||
}
|
||||
}
|
||||
else if ((res->start == limits.start) && (res->end == limits.end)) {
|
||||
return FRAG_LIMITS_DUPLICATE;
|
||||
}
|
||||
else {
|
||||
return FRAG_LIMITS_NEW;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void _set_nh(gnrc_pktsnip_t *hdr_snip, uint8_t nh)
|
||||
{
|
||||
switch (hdr_snip->type) {
|
||||
case GNRC_NETTYPE_IPV6: {
|
||||
ipv6_hdr_t *hdr = hdr_snip->data;
|
||||
hdr->nh = nh;
|
||||
break;
|
||||
}
|
||||
case GNRC_NETTYPE_IPV6_EXT: {
|
||||
ipv6_ext_t *hdr = hdr_snip->data;
|
||||
hdr->nh = nh;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
/* should not happen */
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static gnrc_pktsnip_t *_completed(gnrc_ipv6_ext_frag_rbuf_t *rbuf)
|
||||
{
|
||||
assert(rbuf->limits.next != NULL); /* this function is only called when
|
||||
* at least one fragment was already
|
||||
* added */
|
||||
/* clist: first element is second element ;-) (from next of head) */
|
||||
gnrc_ipv6_ext_frag_limits_t *ptr =
|
||||
(gnrc_ipv6_ext_frag_limits_t *)rbuf->limits.next->next;
|
||||
if (rbuf->last && (ptr->start == 0)) {
|
||||
gnrc_pktsnip_t *res = NULL;
|
||||
|
||||
/* last and first fragment were received, so check if everything
|
||||
* in-between is there */
|
||||
do {
|
||||
gnrc_ipv6_ext_frag_limits_t *next = ptr->next;
|
||||
if (ptr->end < next->start) {
|
||||
return NULL;
|
||||
}
|
||||
ptr = next;
|
||||
} while (((clist_node_t *)ptr) != rbuf->limits.next);
|
||||
res = rbuf->pkt;
|
||||
/* rewrite length */
|
||||
rbuf->ipv6->len = byteorder_htons(rbuf->pkt_len);
|
||||
rbuf->pkt = NULL;
|
||||
gnrc_ipv6_ext_frag_rbuf_free(rbuf);
|
||||
return res;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** @} */
|
@ -32,6 +32,10 @@
|
||||
#include "net/gnrc/ipv6/whitelist.h"
|
||||
#include "net/gnrc/ipv6/blacklist.h"
|
||||
|
||||
#ifdef MODULE_GNRC_IPV6_EXT_FRAG
|
||||
#include "net/gnrc/ipv6/ext/frag.h"
|
||||
#endif
|
||||
|
||||
#include "net/gnrc/ipv6.h"
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
@ -171,6 +175,10 @@ static void *_event_loop(void *args)
|
||||
(void)args;
|
||||
msg_init_queue(msg_q, GNRC_IPV6_MSG_QUEUE_SIZE);
|
||||
|
||||
/* initialize fragmentation data-structures */
|
||||
#ifdef MODULE_GNRC_IPV6_EXT_FRAG
|
||||
gnrc_ipv6_ext_frag_init();
|
||||
#endif /* MODULE_GNRC_IPV6_EXT_FRAG */
|
||||
/* register interest in all IPv6 packets */
|
||||
gnrc_netreg_register(GNRC_NETTYPE_IPV6, &me_reg);
|
||||
|
||||
@ -200,6 +208,11 @@ static void *_event_loop(void *args)
|
||||
msg_reply(&msg, &reply);
|
||||
break;
|
||||
|
||||
#ifdef MODULE_GNRC_IPV6_EXT_FRAG
|
||||
case GNRC_IPV6_EXT_FRAG_RBUF_GC:
|
||||
gnrc_ipv6_ext_frag_rbuf_gc();
|
||||
break;
|
||||
#endif /* MODULE_GNRC_IPV6_EXT_FRAG */
|
||||
case GNRC_IPV6_NIB_SND_UC_NS:
|
||||
case GNRC_IPV6_NIB_SND_MC_NS:
|
||||
case GNRC_IPV6_NIB_SND_NA:
|
||||
|
Loading…
Reference in New Issue
Block a user