From 40138fdce8f644ad20e08083f40d71f6463b1f83 Mon Sep 17 00:00:00 2001 From: "Martine S. Lenders" Date: Mon, 28 Oct 2019 14:09:30 +0100 Subject: [PATCH] gnrc_sixlowpan_frag_rb: allow for deletion timer after completion This allows to set a timer between the completion of a datagram in the reassembly buffer and the deletion of the corresponding reassembly buffer entry. This allows to ignore potentially late incoming link-layer duplicates of fragments of the datagram that then will have the reassembly buffer entry be blocked. This was noted in this [discussion] for classic 6LoWPAN reassembly (and minimal fragment forwarding) and is recommended in the current [selective fragment recovery draft][SFR draft]. [discussion]: https://mailarchive.ietf.org/arch/msg/6lo/Ez0tzZDqawVn6AFhYzAFWUOtJns [SFR draft]: https://tools.ietf.org/html/draft-ietf-6lo-fragment-recovery-07#section-6 --- sys/include/net/gnrc/sixlowpan/config.h | 15 ++++++ .../frag/rb/gnrc_sixlowpan_frag_rb.c | 48 +++++++++++++++++-- 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/sys/include/net/gnrc/sixlowpan/config.h b/sys/include/net/gnrc/sixlowpan/config.h index ed1ed36ab0..4ee2c8e6b8 100644 --- a/sys/include/net/gnrc/sixlowpan/config.h +++ b/sys/include/net/gnrc/sixlowpan/config.h @@ -97,6 +97,21 @@ extern "C" { #define GNRC_SIXLOWPAN_FRAG_RBUF_AGGRESSIVE_OVERRIDE (1) #endif +/** + * @brief Deletion timer for reassembly buffer entries in microseconds + * + * @note Only applicable with + * [gnrc_sixlowpan_frag_rb](@ref net_gnrc_sixlowpan_frag_rb) module + * + * Time to pass between completion of a datagram and the deletion of its + * reassembly buffer entry. If this value is 0, the entry is dropped + * immediately. Use this value to prevent re-creation of a reassembly buffer + * entry on late arriving link-layer duplicates. + */ +#ifndef GNRC_SIXLOWPAN_FRAG_RBUF_DEL_TIMER +#define GNRC_SIXLOWPAN_FRAG_RBUF_DEL_TIMER (0U) +#endif + /** * @brief Registration lifetime in minutes for the address registration option * diff --git a/sys/net/gnrc/network_layer/sixlowpan/frag/rb/gnrc_sixlowpan_frag_rb.c b/sys/net/gnrc/network_layer/sixlowpan/frag/rb/gnrc_sixlowpan_frag_rb.c index c4faefa724..af44242c85 100644 --- a/sys/net/gnrc/network_layer/sixlowpan/frag/rb/gnrc_sixlowpan_frag_rb.c +++ b/sys/net/gnrc/network_layer/sixlowpan/frag/rb/gnrc_sixlowpan_frag_rb.c @@ -21,6 +21,7 @@ #include "net/ipv6/hdr.h" #include "net/gnrc.h" #include "net/gnrc/sixlowpan.h" +#include "net/gnrc/sixlowpan/config.h" #ifdef MODULE_GNRC_SIXLOWPAN_FRAG_VRB #include "net/gnrc/sixlowpan/frag/vrb.h" #endif /* MODULE_GNRC_SIXLOWPAN_FRAG_VRB */ @@ -360,6 +361,18 @@ static bool _rbuf_update_ints(gnrc_sixlowpan_frag_rb_base_t *entry, return true; } +static void _gc_pkt(gnrc_sixlowpan_frag_rb_t *rbuf) +{ +#if GNRC_SIXLOWPAN_FRAG_RBUF_DEL_TIMER > 0 + if (rbuf->super.current_size == 0) { + /* packet is scheduled for deletion, but was complete, i.e. pkt is + * already handed up to other layer, i.e. no need to release */ + return; + } +#endif + gnrc_pktbuf_release(rbuf->pkt); +} + void gnrc_sixlowpan_frag_rb_gc(void) { uint32_t now_usec = xtimer_now_usec(); @@ -380,7 +393,7 @@ void gnrc_sixlowpan_frag_rb_gc(void) l2addr_str), (unsigned)rbuf[i].super.datagram_size, rbuf[i].super.tag); - gnrc_pktbuf_release(rbuf[i].pkt); + _gc_pkt(&rbuf[i]); gnrc_sixlowpan_frag_rb_remove(&(rbuf[i])); } } @@ -419,6 +432,14 @@ static int _rbuf_get(const void *src, size_t src_len, rbuf[i].super.dst_len, l2addr_str), (unsigned)rbuf[i].super.datagram_size, rbuf[i].super.tag); +#if GNRC_SIXLOWPAN_FRAG_RBUF_DEL_TIMER > 0 + if (rbuf[i].super.current_size == 0) { + /* ensure that only empty reassembly buffer entries and entries + * scheduled for deletion have `current_size == 0` */ + DEBUG("6lo rfrag: scheduled for deletion, don't add fragment\n"); + return -1; + } +#endif rbuf[i].super.arrival = now_usec; _set_rbuf_timeout(); return i; @@ -514,7 +535,7 @@ void gnrc_sixlowpan_frag_rb_reset(void) for (unsigned int i = 0; i < GNRC_SIXLOWPAN_FRAG_RBUF_SIZE; i++) { if ((rbuf[i].pkt != NULL) && (rbuf[i].pkt->users > 0)) { - gnrc_pktbuf_release(rbuf[i].pkt); + _gc_pkt(&rbuf[i]); } } memset(rbuf, 0, sizeof(rbuf)); @@ -539,6 +560,25 @@ void gnrc_sixlowpan_frag_rb_base_rm(gnrc_sixlowpan_frag_rb_base_t *entry) entry->datagram_size = 0; } +static void _tmp_rm(gnrc_sixlowpan_frag_rb_t *rbuf) +{ +#if GNRC_SIXLOWPAN_FRAG_RBUF_DEL_TIMER > 0U + /* use garbage-collection to leave the entry for at least + * GNRC_SIXLOWPAN_FRAG_RBUF_DEL_TIMER in the reassembly buffer by + * setting the arrival time to + * (GNRC_SIXLOWPAN_FRAG_RBUF_TIMEOUT_US - GNRC_SIXLOWPAN_FRAG_RBUF_DEL_TIMER) + * microseconds in the past */ + rbuf->super.arrival = xtimer_now_usec() - + (GNRC_SIXLOWPAN_FRAG_RBUF_TIMEOUT_US - + GNRC_SIXLOWPAN_FRAG_RBUF_DEL_TIMER); + /* reset current size to prevent late duplicates to trigger another + * dispatch */ + rbuf->super.current_size = 0; +#else /* GNRC_SIXLOWPAN_FRAG_RBUF_DEL_TIMER == 0U */ + gnrc_sixlowpan_frag_rb_remove(rbuf); +#endif /* GNRC_SIXLOWPAN_FRAG_RBUF_DEL_TIMER */ +} + int gnrc_sixlowpan_frag_rb_dispatch_when_complete(gnrc_sixlowpan_frag_rb_t *rbuf, gnrc_netif_hdr_t *netif_hdr) { @@ -555,7 +595,7 @@ int gnrc_sixlowpan_frag_rb_dispatch_when_complete(gnrc_sixlowpan_frag_rb_t *rbuf if (netif == NULL) { DEBUG("6lo rbuf: error allocating netif header\n"); gnrc_pktbuf_release(rbuf->pkt); - gnrc_sixlowpan_frag_rb_remove(rbuf); + _tmp_rm(rbuf); return -1; } @@ -570,7 +610,7 @@ int gnrc_sixlowpan_frag_rb_dispatch_when_complete(gnrc_sixlowpan_frag_rb_t *rbuf new_netif_hdr->rssi = netif_hdr->rssi; LL_APPEND(rbuf->pkt, netif); gnrc_sixlowpan_dispatch_recv(rbuf->pkt, NULL, 0); - gnrc_sixlowpan_frag_rb_remove(rbuf); + _tmp_rm(rbuf); } return res; }