diff --git a/sys/include/net/gnrc/sixlowpan/config.h b/sys/include/net/gnrc/sixlowpan/config.h index 90cd46b559..ac0cd47bb8 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..7a08aee543 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 */ @@ -115,7 +116,7 @@ static int _check_fragments(gnrc_sixlowpan_frag_rb_base_t *entry, } /* End was already checked in overlap check */ if (ptr->start == offset) { - DEBUG("6lo rbuf: fragment already in reassembly buffer"); + DEBUG("6lo rbuf: fragment already in reassembly buffer\n"); return RBUF_ADD_DUPLICATE; } ptr = ptr->next; @@ -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; }