From 27f4939211a03292ee1bb7646d6ef58d8fe3f9f3 Mon Sep 17 00:00:00 2001
From: "Martine S. Lenders"
Date: Mon, 28 Oct 2019 18:57:44 +0100
Subject: [PATCH 01/10] gnrc_sixlowpan_frag_vrb: add reverse look-up
To label switch ACKs for fragments back to the originator, a reverse
look-up in the VRB is required.
---
sys/include/net/gnrc/sixlowpan/frag/vrb.h | 17 ++++++++++++++
.../frag/vrb/gnrc_sixlowpan_frag_vrb.c | 23 +++++++++++++++++++
2 files changed, 40 insertions(+)
diff --git a/sys/include/net/gnrc/sixlowpan/frag/vrb.h b/sys/include/net/gnrc/sixlowpan/frag/vrb.h
index 3bb859195a7c..462a82e971a6 100644
--- a/sys/include/net/gnrc/sixlowpan/frag/vrb.h
+++ b/sys/include/net/gnrc/sixlowpan/frag/vrb.h
@@ -117,6 +117,23 @@ void gnrc_sixlowpan_frag_vrb_gc(void);
gnrc_sixlowpan_frag_vrb_t *gnrc_sixlowpan_frag_vrb_get(
const uint8_t *src, size_t src_len, unsigned src_tag);
+/**
+ * @brief Reverse VRB lookup
+ *
+ * @param[in] netif Network interface the reverse label-switched packet
+ * came over
+ * @param[in] src Link-layer source address of reverse label-switched
+ * packet.
+ * @param[in] src_len Length of @p src.
+ * @param[in] tag Tag of the reverse label-switched packet.
+ *
+ * @return The VRB entry with `vrb->super.dst == src` and `vrb->out_tag == tag`.
+ * @return NULL, if there is no entry in the VRB that has these values.
+ */
+gnrc_sixlowpan_frag_vrb_t *gnrc_sixlowpan_frag_vrb_reverse(
+ const gnrc_netif_t *netif, const uint8_t *src, size_t src_len,
+ unsigned tag);
+
/**
* @brief Removes an entry from the VRB
*
diff --git a/sys/net/gnrc/network_layer/sixlowpan/frag/vrb/gnrc_sixlowpan_frag_vrb.c b/sys/net/gnrc/network_layer/sixlowpan/frag/vrb/gnrc_sixlowpan_frag_vrb.c
index 00159c3228c8..1e6c7797fb05 100644
--- a/sys/net/gnrc/network_layer/sixlowpan/frag/vrb/gnrc_sixlowpan_frag_vrb.c
+++ b/sys/net/gnrc/network_layer/sixlowpan/frag/vrb/gnrc_sixlowpan_frag_vrb.c
@@ -184,6 +184,29 @@ gnrc_sixlowpan_frag_vrb_t *gnrc_sixlowpan_frag_vrb_get(
return NULL;
}
+gnrc_sixlowpan_frag_vrb_t *gnrc_sixlowpan_frag_vrb_reverse(
+ const gnrc_netif_t *netif, const uint8_t *src, size_t src_len,
+ unsigned tag)
+{
+ DEBUG("6lo vrb: trying to get entry for reverse label switching (%s, %u)\n",
+ gnrc_netif_addr_to_str(src, src_len, addr_str), tag);
+ for (unsigned i = 0; i < CONFIG_GNRC_SIXLOWPAN_FRAG_VRB_SIZE; i++) {
+ gnrc_sixlowpan_frag_vrb_t *vrbe = &_vrb[i];
+
+ if ((vrbe->out_tag == tag) && (vrbe->out_netif == netif) &&
+ (memcmp(vrbe->super.dst, src, src_len) == 0)) {
+ DEBUG("6lo vrb: got VRB entry from (%s, %u)\n",
+ gnrc_netif_addr_to_str(vrbe->super.src,
+ vrbe->super.src_len,
+ addr_str), vrbe->super.tag);
+ return vrbe;
+ }
+ }
+ DEBUG("6lo vrb: no entry found\n");
+ return NULL;
+
+}
+
void gnrc_sixlowpan_frag_vrb_gc(void)
{
uint32_t now_usec = xtimer_now_usec();
From 0af8a1600ad43cc17e7fc8c3a4acd16b71edd5f8 Mon Sep 17 00:00:00 2001
From: "Martine S. Lenders"
Date: Mon, 28 Oct 2019 13:47:47 +0100
Subject: [PATCH 02/10] sixlowpan|gnrc_sixlowpan: change SFR draft to RFC in
doc
---
sys/include/net/gnrc/sixlowpan/config.h | 4 ++--
sys/include/net/gnrc/sixlowpan/frag/rb.h | 8 ++++----
sys/include/net/sixlowpan.h | 9 +++------
sys/include/net/sixlowpan/sfr.h | 9 +++------
4 files changed, 12 insertions(+), 18 deletions(-)
diff --git a/sys/include/net/gnrc/sixlowpan/config.h b/sys/include/net/gnrc/sixlowpan/config.h
index e4017146a3c5..f3e0a580880c 100644
--- a/sys/include/net/gnrc/sixlowpan/config.h
+++ b/sys/include/net/gnrc/sixlowpan/config.h
@@ -163,8 +163,8 @@ extern "C" {
/**
* @name Selective fragment recovery configuration
- * @see [draft-ietf-6lo-fragment-recovery-07, section 7.1]
- * (https://tools.ietf.org/html/draft-ietf-6lo-fragment-recovery-07#section-7.1)
+ * @see [RFC 8931, section 7.1]
+ * (https://tools.ietf.org/html/rfc8931#section-7.1)
* @note Only applicable with gnrc_sixlowpan_frag_sfr module
* @{
*/
diff --git a/sys/include/net/gnrc/sixlowpan/frag/rb.h b/sys/include/net/gnrc/sixlowpan/frag/rb.h
index 3909edad30b0..ff5dfb825996 100644
--- a/sys/include/net/gnrc/sixlowpan/frag/rb.h
+++ b/sys/include/net/gnrc/sixlowpan/frag/rb.h
@@ -130,8 +130,8 @@ gnrc_sixlowpan_frag_rb_t *gnrc_sixlowpan_frag_rb_add(gnrc_netif_hdr_t *netif_hdr
*
* @note datagram_size is not a search parameter as the primary use case
* for this function is [Selective Fragment Recovery]
- * (https://tools.ietf.org/html/draft-ietf-6lo-fragment-recovery-05)
- * where this information only exists in the first fragment.
+ * (https://tools.ietf.org/html/rfc8931) where this information only
+ * exists in the first fragment.
*
* @return true, if an entry with the given tuple exist.
* @return false, if no entry with the given tuple exist.
@@ -151,8 +151,8 @@ bool gnrc_sixlowpan_frag_rb_exists(const gnrc_netif_hdr_t *netif_hdr,
*
* @note datagram_size is not a search parameter as the primary use case
* for this function is [Selective Fragment Recovery]
- * (https://tools.ietf.org/html/draft-ietf-6lo-fragment-recovery-05)
- * where this information only exists in the first fragment.
+ * (https://tools.ietf.org/html/rfc8931) where this information only
+ * exists in the first fragment.
*/
void gnrc_sixlowpan_frag_rb_rm_by_datagram(const gnrc_netif_hdr_t *netif_hdr,
uint16_t tag);
diff --git a/sys/include/net/sixlowpan.h b/sys/include/net/sixlowpan.h
index 7e4dcd27df7d..773b60048cfb 100644
--- a/sys/include/net/sixlowpan.h
+++ b/sys/include/net/sixlowpan.h
@@ -63,22 +63,19 @@ extern "C" {
/**
* @brief Dispatch mask for 6LoWPAN selective fragment recovery
- * @see [draft-ietf-6lo-fragment-recovery-05,
- * section 5](https://tools.ietf.org/html/draft-ietf-6lo-fragment-recovery-05#section-5)
+ * @see [RFC 8931, section 5](https://tools.ietf.org/html/rfc8931#section-5)
*/
#define SIXLOWPAN_SFR_DISP_MASK (0xfe)
/**
* @brief Dispatch for 6LoWPAN recoverable fragment
- * @see [draft-ietf-6lo-fragment-recovery-05, section
- * 5.1](https://tools.ietf.org/html/draft-ietf-6lo-fragment-recovery-05#section-5.1)
+ * @see [RFC 8931, section 5.1](https://tools.ietf.org/html/rfc8931#section-5.1)
*/
#define SIXLOWPAN_SFR_RFRAG_DISP (0xe8)
/**
* @brief Dispatch for 6LoWPAN recoverable fragment acknowledgment
- * @see [draft-ietf-6lo-fragment-recovery-05, section
- * 5.2](https://tools.ietf.org/html/draft-ietf-6lo-fragment-recovery-05#section-5.2)
+ * @see [RFC 8931, section 5.2](https://tools.ietf.org/html/rfc8931#section-5.2)
*/
#define SIXLOWPAN_SFR_ACK_DISP (0xea)
diff --git a/sys/include/net/sixlowpan/sfr.h b/sys/include/net/sixlowpan/sfr.h
index b2a2fe21b24b..492da358d9b4 100644
--- a/sys/include/net/sixlowpan/sfr.h
+++ b/sys/include/net/sixlowpan/sfr.h
@@ -57,8 +57,7 @@ extern "C" {
/**
* @brief Generic type for selective fragment recovery headers
*
- * @see [draft-ietf-6lo-fragment-recovery-05, section
- * 5](https://tools.ietf.org/html/draft-ietf-6lo-fragment-recovery-05#section-5)
+ * @see [RFC 8931, section 5](https://tools.ietf.org/html/rfc8931#section-5)
*/
typedef struct __attribute__((packed)) {
/**
@@ -92,8 +91,7 @@ typedef struct __attribute__((packed)) {
/**
* @brief Recoverable fragment header
*
- * @see [draft-ietf-6lo-fragment-recovery-05, section
- * 5.1](https://tools.ietf.org/html/draft-ietf-6lo-fragment-recovery-05#section-5.1)
+ * @see [RFC 8931, section 5.1](https://tools.ietf.org/html/rfc8931#section-5.1)
*/
typedef struct __attribute__((packed)) {
sixlowpan_sfr_t base; /**< generic part */
@@ -137,8 +135,7 @@ typedef struct __attribute__((packed)) {
/**
* @brief Recoverable fragment (RFRAG) acknowledgment header
*
- * @see [draft-ietf-6lo-fragment-recovery-05, section
- * 5.2](https://tools.ietf.org/html/draft-ietf-6lo-fragment-recovery-05#section-5.2)
+ * @see [RFC 8931, section 5.2](https://tools.ietf.org/html/rfc8931#section-5.2)
*/
typedef struct __attribute__((packed)) {
sixlowpan_sfr_t base; /**< generic part */
From 82c4d263e8b32bed854ce2b90f0c15e46ebf6d38 Mon Sep 17 00:00:00 2001
From: "Martine S. Lenders"
Date: Thu, 29 Oct 2020 22:15:29 +0100
Subject: [PATCH 03/10] gnrc_sixlowpan_config: rename SRF parameters for
Kconfig
---
sys/include/net/gnrc/sixlowpan/config.h | 79 ++++++++++++++-----------
1 file changed, 45 insertions(+), 34 deletions(-)
diff --git a/sys/include/net/gnrc/sixlowpan/config.h b/sys/include/net/gnrc/sixlowpan/config.h
index f3e0a580880c..ce344608ca0e 100644
--- a/sys/include/net/gnrc/sixlowpan/config.h
+++ b/sys/include/net/gnrc/sixlowpan/config.h
@@ -171,8 +171,8 @@ extern "C" {
/**
* @brief Default minimum value for fragment size (MinFragmentSize)
*/
-#ifndef GNRC_SIXLOWPAN_SFR_MIN_FRAG_SIZE
-#define GNRC_SIXLOWPAN_SFR_MIN_FRAG_SIZE (96U)
+#ifndef CONFIG_GNRC_SIXLOWPAN_SFR_MIN_FRAG_SIZE
+#define CONFIG_GNRC_SIXLOWPAN_SFR_MIN_FRAG_SIZE 96U
#endif
/**
@@ -182,32 +182,37 @@ extern "C" {
* the chances of buffer bloat and transmission loss. The value must be less
* than 512 if the unit is defined for the PHY layer is the octet.
*/
-#ifndef GNRC_SIXLOWPAN_SFR_MAX_FRAG_SIZE
-#define GNRC_SIXLOWPAN_SFR_MAX_FRAG_SIZE (112U)
+#ifndef CONFIG_GNRC_SIXLOWPAN_SFR_MAX_FRAG_SIZE
+#define CONFIG_GNRC_SIXLOWPAN_SFR_MAX_FRAG_SIZE 112U
#endif
/**
* @brief Default value for fragment size that the sender should use to start
* with (OptFragmentSize)
+ *
+ * @pre Must be inclusively between
+ * @ref CONFIG_GNRC_SIXLOWPAN_SFR_MIN_FRAG_SIZE and
+ * @ref CONFIG_GNRC_SIXLOWPAN_SFR_MAX_FRAG_SIZE
*/
-#ifndef GNRC_SIXLOWPAN_SFR_OPT_FRAG_SIZE
-#define GNRC_SIXLOWPAN_SFR_OPT_FRAG_SIZE (GNRC_SIXLOWPAN_SFR_MAX_FRAG_SIZE)
+#ifndef CONFIG_GNRC_SIXLOWPAN_SFR_OPT_FRAG_SIZE
+#define CONFIG_GNRC_SIXLOWPAN_SFR_OPT_FRAG_SIZE CONFIG_GNRC_SIXLOWPAN_SFR_MAX_FRAG_SIZE
#endif
/**
* @brief Indicates whether the sender should react to ECN (UseECN)
*
- * When the sender reacts to ECN its window size will vary between @ref
- * GNRC_SIXLOWPAN_SFR_MIN_WIN_SIZE and @ref GNRC_SIXLOWPAN_SFR_MAX_WIN_SIZE.
+ * When the sender reacts to Explicit Congestion Notification (ECN) its window
+ * size will vary between @ref CONFIG_GNRC_SIXLOWPAN_SFR_MIN_WIN_SIZE and @ref
+ * CONFIG_GNRC_SIXLOWPAN_SFR_MAX_WIN_SIZE.
*/
-#define GNRC_SIXLOWPAN_SFR_USE_ECN (0U)
+#define CONFIG_GNRC_SIXLOWPAN_SFR_USE_ECN 0U
/**
* @brief Default minimum value of window size that the sender can use
* (MinWindowSize)
*/
-#ifndef GNRC_SIXLOWPAN_SFR_MIN_WIN_SIZE
-#define GNRC_SIXLOWPAN_SFR_MIN_WIN_SIZE (1U)
+#ifndef CONFIG_GNRC_SIXLOWPAN_SFR_MIN_WIN_SIZE
+#define CONFIG_GNRC_SIXLOWPAN_SFR_MIN_WIN_SIZE 1U
#endif
/**
@@ -216,16 +221,20 @@ extern "C" {
*
* @warning **Must** be lesser than 32.
*/
-#ifndef GNRC_SIXLOWPAN_SFR_MAX_WIN_SIZE
-#define GNRC_SIXLOWPAN_SFR_MAX_WIN_SIZE (16U)
+#ifndef CONFIG_GNRC_SIXLOWPAN_SFR_MAX_WIN_SIZE
+#define CONFIG_GNRC_SIXLOWPAN_SFR_MAX_WIN_SIZE 16U
#endif
/**
* @brief Default value of window size that the sender should start with
* (OptWindowSize)
+ *
+ * @pre Must be inclusively between
+ * @ref CONFIG_GNRC_SIXLOWPAN_SFR_MIN_WIN_SIZE and
+ * @ref CONFIG_GNRC_SIXLOWPAN_SFR_MAX_WIN_SIZE
*/
-#ifndef GNRC_SIXLOWPAN_SFR_OPT_WIN_SIZE
-#define GNRC_SIXLOWPAN_SFR_OPT_WIN_SIZE (16U)
+#ifndef CONFIG_GNRC_SIXLOWPAN_SFR_OPT_WIN_SIZE
+#define CONFIG_GNRC_SIXLOWPAN_SFR_OPT_WIN_SIZE 16U
#endif
/**
@@ -240,51 +249,53 @@ extern "C" {
* ratio of air and memory in intermediate nodes that a particular datagram will
* use.
*/
-#ifndef GNRC_SIXLOWPAN_SFR_INTER_FRAME_GAP_US
-#define GNRC_SIXLOWPAN_SFR_INTER_FRAME_GAP_US (100U)
+#ifndef CONFIG_GNRC_SIXLOWPAN_SFR_INTER_FRAME_GAP_US
+#define CONFIG_GNRC_SIXLOWPAN_SFR_INTER_FRAME_GAP_US 100U
#endif
/**
- * @brief Default minimum amount of time in milliseconds a node should wait
- * for an RFRAG Acknowledgment before it takes a next action
+ * @brief Minimum RFRAG-ACK timeout in msec before a node takes a next action
* (MinARQTimeOut)
*/
-#ifndef GNRC_SIXLOWPAN_SFR_MIN_ARQ_TIMEOUT_MS
-#define GNRC_SIXLOWPAN_SFR_MIN_ARQ_TIMEOUT_MS (350U)
+#ifndef CONFIG_GNRC_SIXLOWPAN_SFR_MIN_ARQ_TIMEOUT_MS
+#define CONFIG_GNRC_SIXLOWPAN_SFR_MIN_ARQ_TIMEOUT_MS 350U
#endif
/**
- * @brief Default maximum amount of time in milliseconds a node should wait
- * for an RFRAG Acknowledgment before it takes a next action
+ * @brief Maximum RFRAG-ACK timeout in msec before a node takes a next action
* (MaxARQTimeOut)
*/
-#ifndef GNRC_SIXLOWPAN_SFR_MAX_ARQ_TIMEOUT_MS
-#define GNRC_SIXLOWPAN_SFR_MAX_ARQ_TIMEOUT_MS (700U)
+#ifndef CONFIG_GNRC_SIXLOWPAN_SFR_MAX_ARQ_TIMEOUT_MS
+#define CONFIG_GNRC_SIXLOWPAN_SFR_MAX_ARQ_TIMEOUT_MS 700U
#endif
/**
- * @brief Default starting point of the value of the amount of time in
- * milliseconds that a sender should wait for an RFRAG Acknowledgment
- * before it takes a next action (OptARQTimeOut)
+ * @brief Default RFRAG-ACK timeout in msec before a node takes a next action
+ * (OptARQTimeOut)
+ *
+ * @pre Must be inclusively between
+ * @ref CONFIG_GNRC_SIXLOWPAN_SFR_MIN_ARQ_TIMEOUT_MS and
+ * @ref CONFIG_GNRC_SIXLOWPAN_SFR_MAX_ARQ_TIMEOUT_MS
*/
-#ifndef GNRC_SIXLOWPAN_SFR_OPT_ARQ_TIMEOUT_MS
-#define GNRC_SIXLOWPAN_SFR_OPT_ARQ_TIMEOUT_MS (GNRC_SIXLOWPAN_SFR_MAX_ARQ_TIMEOUT_MS)
+#ifndef CONFIG_GNRC_SIXLOWPAN_SFR_OPT_ARQ_TIMEOUT_MS
+#define CONFIG_GNRC_SIXLOWPAN_SFR_OPT_ARQ_TIMEOUT_MS \
+ CONFIG_GNRC_SIXLOWPAN_SFR_MAX_ARQ_TIMEOUT_MS
#endif
/**
* @brief The maximum number of retries for a particular fragment
* (MaxFragRetries)
*/
-#ifndef GNRC_SIXLOWPAN_SFR_FRAG_RETRIES
-#define GNRC_SIXLOWPAN_SFR_FRAG_RETRIES (2U)
+#ifndef CONFIG_GNRC_SIXLOWPAN_SFR_FRAG_RETRIES
+#define CONFIG_GNRC_SIXLOWPAN_SFR_FRAG_RETRIES 2U
#endif
/**
* @brief The maximum number of retries from scratch for a particular
* datagram (MaxDatagramRetries)
*/
-#ifndef GNRC_SIXLOWPAN_SFR_DG_RETRIES
-#define GNRC_SIXLOWPAN_SFR_DG_RETRIES (0U)
+#ifndef CONFIG_GNRC_SIXLOWPAN_SFR_DG_RETRIES
+#define CONFIG_GNRC_SIXLOWPAN_SFR_DG_RETRIES 0U
#endif
/** @} */
From 80ff517eba8ef701b56752e87210d974b1afb16a Mon Sep 17 00:00:00 2001
From: "Martine S. Lenders"
Date: Fri, 27 Sep 2019 12:10:47 +0200
Subject: [PATCH 04/10] gnrc_netif: 6lo.h: add flags to indicate SRF capability
---
sys/include/net/gnrc/netif/6lo.h | 31 +++++++++++++++++++++++++++++++
1 file changed, 31 insertions(+)
diff --git a/sys/include/net/gnrc/netif/6lo.h b/sys/include/net/gnrc/netif/6lo.h
index 2c8b57b00989..766d1b5ac3d1 100644
--- a/sys/include/net/gnrc/netif/6lo.h
+++ b/sys/include/net/gnrc/netif/6lo.h
@@ -18,12 +18,34 @@
#ifndef NET_GNRC_NETIF_6LO_H
#define NET_GNRC_NETIF_6LO_H
+#include
#include
#ifdef __cplusplus
extern "C" {
#endif
+/**
+ * @name Local 6LoWPAN capability flags
+ * @anchor net_gnrc_netif_6lo_local_flags
+ * @see gnrc_netif_6lo_t::local_flags
+ *
+ * Like the the capability flags in the [6LoWPAN Capability Indication Option
+ * (6CIO)](https://tools.ietf.org/html/rfc7400#section-3.3) are less about
+ * hardware capabilities than about the implementation status within the
+ * network. For the flags in this group it is currently undefined how to
+ * exchange the capabilities between nodes, but they might be added to the 6CIO
+ * at a later point. Once the 6CIO is implemented in GNRC and the flag is
+ * supported by it, the corresponding flag in these local flags can be removed.
+ * @{
+ */
+/**
+ * @brief Selective Fragment Recovery enabled
+ * @see [RFC 8931](https://tools.ietf.org/html/rfc8931)
+ */
+#define GNRC_NETIF_6LO_LOCAL_FLAGS_SFR (0x01)
+/** @} */
+
/**
* @brief 6Lo component of @ref gnrc_netif_t
*/
@@ -35,6 +57,15 @@ typedef struct {
* @ref net_gnrc_sixlowpan_frag "gnrc_sixlowpan_frag".
*/
uint16_t max_frag_size;
+ /**
+ * @brief 6LoWPAN capability flags beyond the ones advertised in
+ * [6LoWPAN Capability Indication Option
+ * (6CIO)](https://tools.ietf.org/html/rfc7400#section-3.3)
+ *
+ * @see [Local 6LoWPAN capability flags](@ref
+ * net_gnrc_netif_6lo_local_flags)
+ */
+ uint8_t local_flags;
} gnrc_netif_6lo_t;
#ifdef __cplusplus
From bd300a3cc01cd710529db61de360020e99392eb3 Mon Sep 17 00:00:00 2001
From: "Martine S. Lenders"
Date: Mon, 2 Dec 2019 11:58:17 +0100
Subject: [PATCH 05/10] gnrc_sixlowpan_frag_rb: add check function for empty
interval pool
---
sys/include/net/gnrc/sixlowpan/frag/rb.h | 21 +++++++++++++++++++
.../frag/rb/gnrc_sixlowpan_frag_rb.c | 12 +++++++++++
2 files changed, 33 insertions(+)
diff --git a/sys/include/net/gnrc/sixlowpan/frag/rb.h b/sys/include/net/gnrc/sixlowpan/frag/rb.h
index ff5dfb825996..8bfa78c84de5 100644
--- a/sys/include/net/gnrc/sixlowpan/frag/rb.h
+++ b/sys/include/net/gnrc/sixlowpan/frag/rb.h
@@ -251,6 +251,27 @@ static inline void gnrc_sixlowpan_frag_rb_remove(gnrc_sixlowpan_frag_rb_t *rbuf)
}
#endif
+#if defined(TEST_SUITES) || defined(DOXYGEN)
+/**
+ * @brief Check if pool of fragment intervals is empty
+ *
+ * @see @ref gnrc_sixlowpan_frag_rb_int_t
+ * @note Returns only non-true values if @ref TEST_SUITES is defined.
+ *
+ * @return true, if pool of fragment intervals is empty
+ * @return false, if pool of fragment intervals is not empty
+ */
+bool gnrc_sixlowpan_frag_rb_ints_empty(void);
+#else /* defined(TEST_SUITES) || defined(DOXYGEN) */
+/* always true without TEST_SUITES defined to optimize out when not testing,
+ * as checking the status of the fragment interval pool is unnecessary in
+ * production */
+static inline bool gnrc_sixlowpan_frag_rb_ints_empty(void)
+{
+ return true;
+}
+#endif /* defined(TEST_SUITES) || defined(DOXYGEN) */
+
#ifdef __cplusplus
}
#endif
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 fee17cc9e35a..dedd6cec6a12 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
@@ -423,6 +423,18 @@ static gnrc_sixlowpan_frag_rb_int_t *_rbuf_int_get_free(void)
return NULL;
}
+#ifdef TEST_SUITES
+bool gnrc_sixlowpan_frag_rb_ints_empty(void)
+{
+ for (unsigned int i = 0; i < RBUF_INT_SIZE; i++) {
+ if (rbuf_int[i].end > 0) {
+ return false;
+ }
+ }
+ return true;
+}
+#endif /* TEST_SUITES */
+
static bool _rbuf_update_ints(gnrc_sixlowpan_frag_rb_base_t *entry,
uint16_t offset, size_t frag_size)
{
From d393008b9f2f8e5ccdfa0621b9e0c8771303c553 Mon Sep 17 00:00:00 2001
From: "Martine S. Lenders"
Date: Fri, 30 Oct 2020 13:51:37 +0100
Subject: [PATCH 06/10] gnrc_sixlowpan_frag_rb: externalize get_by_tag function
---
sys/include/net/gnrc/sixlowpan/frag/rb.h | 23 +++++++++++++++++++
.../frag/rb/gnrc_sixlowpan_frag_rb.c | 7 +++++-
tests/gnrc_sixlowpan_frag/main.c | 20 ++++++++++++++++
3 files changed, 49 insertions(+), 1 deletion(-)
diff --git a/sys/include/net/gnrc/sixlowpan/frag/rb.h b/sys/include/net/gnrc/sixlowpan/frag/rb.h
index 8bfa78c84de5..1463b81421f4 100644
--- a/sys/include/net/gnrc/sixlowpan/frag/rb.h
+++ b/sys/include/net/gnrc/sixlowpan/frag/rb.h
@@ -118,6 +118,29 @@ gnrc_sixlowpan_frag_rb_t *gnrc_sixlowpan_frag_rb_add(gnrc_netif_hdr_t *netif_hdr
gnrc_pktsnip_t *frag,
size_t offset, unsigned page);
+/**
+ * @brief Gets a reassembly buffer entry with a given link-layer address
+ * pair and tag.
+ *
+ * @pre `netif_hdr != NULL`
+ *
+ * @param[in] netif_hdr An interface header to provide the (source, destination)
+ * link-layer address pair. Must not be NULL.
+ * @param[in] tag Tag to search for.
+ *
+ * @note datagram_size is not a search parameter as the primary use case
+ * for this function is [Selective Fragment Recovery]
+ * (https://tools.ietf.org/html/rfc8931) where this information only
+ * exists in the first fragment.
+ *
+ * @return The reassembly buffer entry identified by the source and destination
+ * address in the @p netif_hdr and @p tag, if any such entry exist.
+ * @return NULL, if no entry with the given identifying tuple exist.
+ */
+gnrc_sixlowpan_frag_rb_t *gnrc_sixlowpan_frag_rb_get_by_datagram(
+ const gnrc_netif_hdr_t *netif_hdr,
+ uint16_t tag);
+
/**
* @brief Checks if a reassembly buffer entry with a given link-layer address
* pair and tag exists
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 dedd6cec6a12..4a98dcee8fea 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
@@ -153,7 +153,12 @@ gnrc_sixlowpan_frag_rb_t *gnrc_sixlowpan_frag_rb_add(gnrc_netif_hdr_t *netif_hdr
return (res < 0) ? NULL : &rbuf[res];
}
-
+gnrc_sixlowpan_frag_rb_t *gnrc_sixlowpan_frag_rb_get_by_datagram(
+ const gnrc_netif_hdr_t *netif_hdr,
+ uint16_t tag)
+{
+ return _rbuf_get_by_tag(netif_hdr, tag);
+}
bool gnrc_sixlowpan_frag_rb_exists(const gnrc_netif_hdr_t *netif_hdr,
uint16_t tag)
diff --git a/tests/gnrc_sixlowpan_frag/main.c b/tests/gnrc_sixlowpan_frag/main.c
index b1e0c91edce7..e79aaae20c22 100644
--- a/tests/gnrc_sixlowpan_frag/main.c
+++ b/tests/gnrc_sixlowpan_frag/main.c
@@ -536,6 +536,25 @@ static void test_rbuf_add__overlap_rhs(void)
_check_pktbuf(NULL);
}
+static void test_rbuf_get_by_dg(void)
+{
+ const gnrc_sixlowpan_frag_rb_t *entry;
+
+ TEST_ASSERT_NULL(
+ gnrc_sixlowpan_frag_rb_get_by_datagram(&_test_netif_hdr.hdr, TEST_TAG)
+ );
+ /* add a fragment */
+ _rbuf_create_first_fragment();
+ TEST_ASSERT_NOT_NULL(
+ gnrc_sixlowpan_frag_rb_get_by_datagram(&_test_netif_hdr.hdr, TEST_TAG)
+ );
+ /* get entry to release entry->pkt it in `_check_pktbuf()` */
+ entry = _first_non_empty_rbuf();
+ /* entry is however not properly removed yet */
+ TEST_ASSERT_NOT_NULL(entry);
+ _check_pktbuf(entry);
+}
+
static void test_rbuf_exists(void)
{
const gnrc_sixlowpan_frag_rb_t *entry;
@@ -631,6 +650,7 @@ static void run_unittests(void)
new_TestFixture(test_rbuf_add__too_big_fragment),
new_TestFixture(test_rbuf_add__overlap_lhs),
new_TestFixture(test_rbuf_add__overlap_rhs),
+ new_TestFixture(test_rbuf_get_by_dg),
new_TestFixture(test_rbuf_exists),
new_TestFixture(test_rbuf_rm_by_dg),
new_TestFixture(test_rbuf_rm),
From ec436e99d9e1841453502a2a68630a21d990b155 Mon Sep 17 00:00:00 2001
From: "Martine S. Lenders"
Date: Thu, 17 Oct 2019 15:30:10 +0200
Subject: [PATCH 07/10] gnrc_sixlowpan_frag_rb: add handling for RFRAG packets
---
.../frag/rb/gnrc_sixlowpan_frag_rb.c | 66 ++++++++++++++++---
1 file changed, 57 insertions(+), 9 deletions(-)
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 4a98dcee8fea..a84b060b02e5 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
@@ -29,6 +29,7 @@
#include "net/gnrc/sixlowpan/frag/minfwd.h"
#include "net/gnrc/sixlowpan/frag/vrb.h"
#include "net/sixlowpan.h"
+#include "net/sixlowpan/sfr.h"
#include "thread.h"
#include "xtimer.h"
#include "utlist.h"
@@ -205,9 +206,18 @@ static gnrc_sixlowpan_frag_rb_t *_rbuf_get_by_tag(const gnrc_netif_hdr_t *netif_
#ifndef NDEBUG
static bool _valid_offset(gnrc_pktsnip_t *pkt, size_t offset)
{
- return (sixlowpan_frag_1_is(pkt->data) && (offset == 0)) ||
- (sixlowpan_frag_n_is(pkt->data) &&
- (offset == sixlowpan_frag_offset(pkt->data)));
+ return (
+ IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG) &&
+ ((sixlowpan_frag_1_is(pkt->data) && (offset == 0)) ||
+ (sixlowpan_frag_n_is(pkt->data) &&
+ (offset == sixlowpan_frag_offset(pkt->data))))
+ ) || (
+ IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_SFR) &&
+ /* offset == 0 is an abort condition that should not be handed to the
+ * reassembly buffer */
+ sixlowpan_sfr_rfrag_is(pkt->data) &&
+ (sixlowpan_sfr_rfrag_get_offset(pkt->data) != 0)
+ );
}
#endif
@@ -239,6 +249,25 @@ static size_t _6lo_frag_size(gnrc_pktsnip_t *pkt, size_t offset, uint8_t *data)
return frag_size;
}
+static uint16_t _6lo_sfr_datagram_size(gnrc_pktsnip_t *pkt, size_t offset)
+{
+ /* offset doubles as datagram size in RFRAG header when sequence number is 0
+ * see https://tools.ietf.org/html/draft-ietf-6lo-fragment-recovery-08#section-5.1 */
+ return (offset == 0) ? sixlowpan_sfr_rfrag_get_offset(pkt->data) : 0;
+}
+
+static uint8_t *_6lo_sfr_payload(gnrc_pktsnip_t *pkt)
+{
+ return ((uint8_t *)pkt->data) + sizeof(sixlowpan_sfr_rfrag_t);
+}
+
+static size_t _6lo_sfr_frag_size(gnrc_pktsnip_t *pkt)
+{
+ /* TODO: if necessary check MAC layer here,
+ * see https://tools.ietf.org/html/draft-ietf-6lo-fragment-recovery-08#section-5.1 */
+ return sixlowpan_sfr_rfrag_get_frag_size(pkt->data);
+}
+
static int _rbuf_add(gnrc_netif_hdr_t *netif_hdr, gnrc_pktsnip_t *pkt,
size_t offset, unsigned page)
{
@@ -249,18 +278,37 @@ static int _rbuf_add(gnrc_netif_hdr_t *netif_hdr, gnrc_pktsnip_t *pkt,
} entry;
const uint8_t *src = gnrc_netif_hdr_get_src_addr(netif_hdr);
const uint8_t *dst = gnrc_netif_hdr_get_dst_addr(netif_hdr);
- uint8_t *data;
- size_t frag_size;
+ uint8_t *data = NULL;
+ size_t frag_size = 0; /* assign 0, otherwise cppcheck complains ;-) */
int res;
uint16_t datagram_size;
uint16_t datagram_tag;
/* check if provided offset is the same as in fragment */
assert(_valid_offset(pkt, offset));
- data = _6lo_frag_payload(pkt);
- frag_size = _6lo_frag_size(pkt, offset, data);
- datagram_size = sixlowpan_frag_datagram_size(pkt->data);
- datagram_tag = sixlowpan_frag_datagram_tag(pkt->data);
+ if (IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG) && sixlowpan_frag_is(pkt->data)) {
+ data = _6lo_frag_payload(pkt);
+ frag_size = _6lo_frag_size(pkt, offset, data);
+ datagram_size = sixlowpan_frag_datagram_size(pkt->data);
+ datagram_tag = sixlowpan_frag_datagram_tag(pkt->data);
+ }
+ else if (IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_SFR) &&
+ sixlowpan_sfr_rfrag_is(pkt->data)) {
+ sixlowpan_sfr_rfrag_t *rfrag = pkt->data;;
+
+ data = _6lo_sfr_payload(pkt);
+ frag_size = _6lo_sfr_frag_size(pkt);
+ /* offset doubles as datagram size in RFRAG header when sequence number
+ * is 0 */
+ datagram_size = _6lo_sfr_datagram_size(pkt, offset);
+ datagram_tag = rfrag->base.tag;
+ }
+ else {
+ /* either one of the if branches above was taken */
+ assert(data != NULL);
+ gnrc_pktbuf_release(pkt);
+ return RBUF_ADD_ERROR;
+ }
gnrc_sixlowpan_frag_rb_gc();
/* only check VRB for subsequent frags, first frags create and not get VRB
From 1cd1716280667168bf273345f57e2e5e15623771 Mon Sep 17 00:00:00 2001
From: "Martine S. Lenders"
Date: Fri, 27 Sep 2019 15:34:35 +0200
Subject: [PATCH 08/10] gnrc_sixlowpan_frag: initial import of Selective
Fragment Recovery
---
sys/Makefile.dep | 21 +-
sys/include/net/gnrc/sixlowpan/config.h | 4 +
sys/include/net/gnrc/sixlowpan/frag/fb.h | 15 +-
sys/include/net/gnrc/sixlowpan/frag/rb.h | 14 +
sys/include/net/gnrc/sixlowpan/frag/sfr.h | 171 ++
.../net/gnrc/sixlowpan/frag/sfr_types.h | 65 +
sys/include/net/gnrc/sixlowpan/frag/vrb.h | 7 +
sys/net/gnrc/Makefile | 3 +
sys/net/gnrc/netif/gnrc_netif.c | 6 +
.../gnrc/network_layer/sixlowpan/frag/Kconfig | 10 +-
.../network_layer/sixlowpan/frag/fb/Kconfig | 1 +
.../frag/rb/gnrc_sixlowpan_frag_rb.c | 65 +-
.../network_layer/sixlowpan/frag/sfr/Kconfig | 101 ++
.../network_layer/sixlowpan/frag/sfr/Makefile | 3 +
.../frag/sfr/gnrc_sixlowpan_frag_sfr.c | 1604 +++++++++++++++++
.../network_layer/sixlowpan/gnrc_sixlowpan.c | 81 +-
.../sixlowpan/iphc/gnrc_sixlowpan_iphc.c | 72 +-
17 files changed, 2207 insertions(+), 36 deletions(-)
create mode 100644 sys/include/net/gnrc/sixlowpan/frag/sfr.h
create mode 100644 sys/include/net/gnrc/sixlowpan/frag/sfr_types.h
create mode 100644 sys/net/gnrc/network_layer/sixlowpan/frag/sfr/Kconfig
create mode 100644 sys/net/gnrc/network_layer/sixlowpan/frag/sfr/Makefile
create mode 100644 sys/net/gnrc/network_layer/sixlowpan/frag/sfr/gnrc_sixlowpan_frag_sfr.c
diff --git a/sys/Makefile.dep b/sys/Makefile.dep
index 0e11d736e3cd..989e4b618fef 100644
--- a/sys/Makefile.dep
+++ b/sys/Makefile.dep
@@ -280,20 +280,26 @@ endif
ifneq (,$(filter gnrc_sixlowpan_default,$(USEMODULE)))
USEMODULE += gnrc_ipv6_nib_6ln
USEMODULE += gnrc_sixlowpan
- USEMODULE += gnrc_sixlowpan_frag
+ ifeq (,$(filter gnrc_sixlowpan_frag_sfr,$(USEMODULE)))
+ USEMODULE += gnrc_sixlowpan_frag
+ endif
USEMODULE += gnrc_sixlowpan_iphc
endif
ifneq (,$(filter gnrc_sixlowpan_router_default,$(USEMODULE)))
USEMODULE += gnrc_ipv6_nib_6lr
- USEMODULE += gnrc_sixlowpan_frag
+ ifeq (,$(filter gnrc_sixlowpan_frag_sfr,$(USEMODULE)))
+ USEMODULE += gnrc_sixlowpan_frag
+ endif
USEMODULE += gnrc_sixlowpan_iphc
endif
ifneq (,$(filter gnrc_sixlowpan_border_router_default,$(USEMODULE)))
USEMODULE += gnrc_ipv6_nib_6lbr
USEMODULE += gnrc_ipv6_router_default
- USEMODULE += gnrc_sixlowpan_frag
+ ifeq (,$(filter gnrc_sixlowpan_frag_sfr,$(USEMODULE)))
+ USEMODULE += gnrc_sixlowpan_frag
+ endif
USEMODULE += gnrc_sixlowpan_iphc
endif
@@ -318,6 +324,15 @@ ifneq (,$(filter gnrc_sixlowpan_frag_rb,$(USEMODULE)))
USEMODULE += xtimer
endif
+ifneq (,$(filter gnrc_sixlowpan_frag_sfr,$(USEMODULE)))
+ USEMODULE += gnrc_sixlowpan
+ USEMODULE += gnrc_sixlowpan_frag_fb
+ USEMODULE += gnrc_sixlowpan_frag_vrb
+ USEMODULE += gnrc_sixlowpan_frag_rb
+ USEMODULE += evtimer
+ USEMODULE += xtimer
+endif
+
ifneq (,$(filter gnrc_sixlowpan_frag_vrb,$(USEMODULE)))
USEMODULE += xtimer
USEMODULE += gnrc_sixlowpan_frag_fb
diff --git a/sys/include/net/gnrc/sixlowpan/config.h b/sys/include/net/gnrc/sixlowpan/config.h
index ce344608ca0e..343d17bb6c75 100644
--- a/sys/include/net/gnrc/sixlowpan/config.h
+++ b/sys/include/net/gnrc/sixlowpan/config.h
@@ -63,7 +63,11 @@ extern "C" {
* [gnrc_sixlowpan_frag_fb](@ref net_gnrc_sixlowpan_frag_fb) module
*/
#ifndef CONFIG_GNRC_SIXLOWPAN_FRAG_FB_SIZE
+#if IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_SFR)
+#define CONFIG_GNRC_SIXLOWPAN_FRAG_FB_SIZE (4U)
+#else /* defined(MODULE_GNRC_SIXLOWPAN_FRAG_SFR) */
#define CONFIG_GNRC_SIXLOWPAN_FRAG_FB_SIZE (1U)
+#endif /* defined(MODULE_GNRC_SIXLOWPAN_FRAG_SFR) */
#endif
/**
diff --git a/sys/include/net/gnrc/sixlowpan/frag/fb.h b/sys/include/net/gnrc/sixlowpan/frag/fb.h
index d24aed929815..07f15a207290 100644
--- a/sys/include/net/gnrc/sixlowpan/frag/fb.h
+++ b/sys/include/net/gnrc/sixlowpan/frag/fb.h
@@ -28,6 +28,7 @@
#ifdef MODULE_GNRC_SIXLOWPAN_FRAG_HINT
#include "net/gnrc/sixlowpan/frag/hint.h"
#endif /* MODULE_GNRC_SIXLOWPAN_FRAG_HINT */
+#include "net/gnrc/sixlowpan/frag/sfr_types.h"
#ifdef __cplusplus
extern "C" {
@@ -43,10 +44,22 @@ extern "C" {
*/
typedef struct {
gnrc_pktsnip_t *pkt; /**< Pointer to the IPv6 packet to be fragmented */
- uint16_t datagram_size; /**< Length of just the (uncompressed) IPv6 packet to be fragmented */
+ /**
+ * @brief Length of just the (uncompressed) IPv6 packet to be fragmented
+ *
+ * @note With @ref net_gnrc_sixlowpan_frag_sfr this denotes the
+ * _compressed form_ of the datagram
+ */
+ uint16_t datagram_size;
uint16_t tag; /**< Tag used for the fragment */
uint16_t offset; /**< Offset of the Nth fragment from the beginning of the
* payload datagram */
+#if IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_SFR)
+ /**
+ * @brief Extension for selective fragment recovery.
+ */
+ gnrc_sixlowpan_frag_sfr_fb_t sfr;
+#endif /* IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_SFR) */
#ifdef MODULE_GNRC_SIXLOWPAN_FRAG_HINT
/**
* @brief Hint for the size (smaller than link-layer PDU) for the next
diff --git a/sys/include/net/gnrc/sixlowpan/frag/rb.h b/sys/include/net/gnrc/sixlowpan/frag/rb.h
index 1463b81421f4..c990ada64083 100644
--- a/sys/include/net/gnrc/sixlowpan/frag/rb.h
+++ b/sys/include/net/gnrc/sixlowpan/frag/rb.h
@@ -25,6 +25,9 @@
#include "net/gnrc/netif/hdr.h"
#include "net/gnrc/pkt.h"
+#ifdef MODULE_GNRC_SIXLOWPAN_FRAG_SFR
+#include "net/sixlowpan/sfr.h"
+#endif /* MODULE_GNRC_SIXLOWPAN_FRAG_SFR */
#include "net/gnrc/sixlowpan/config.h"
@@ -96,6 +99,17 @@ typedef struct {
* @brief The reassembled packet in the packet buffer
*/
gnrc_pktsnip_t *pkt;
+#if IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_SFR)
+ /**
+ * @brief Bitmap for received fragments
+ *
+ * @note Only available with module `gnrc_sixlowpan_frag_sfr` compiled
+ * in.
+ */
+ BITFIELD(received, SIXLOWPAN_SFR_ACK_BITMAP_SIZE);
+ int8_t offset_diff; /**< offset change due to
+ * recompression */
+#endif /* IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_SFR) */
} gnrc_sixlowpan_frag_rb_t;
/**
diff --git a/sys/include/net/gnrc/sixlowpan/frag/sfr.h b/sys/include/net/gnrc/sixlowpan/frag/sfr.h
new file mode 100644
index 000000000000..3d5b9a5fcfbc
--- /dev/null
+++ b/sys/include/net/gnrc/sixlowpan/frag/sfr.h
@@ -0,0 +1,171 @@
+/*
+ * 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_sixlowpan_frag_sfr 6LoWPAN selective fragment recovery
+ * @ingroup net_gnrc_sixlowpan
+ * @brief 6LoWPAN selective fragment recovery implementation for GNRC
+ *
+ * 6LoWPAN selective fragment recovery is an alternative fragmentation
+ * specification to [classic 6LoWPAN fragmentation](@ref
+ * net_gnrc_sixlowpan_frag). It can be run in parallel to classic fragmentation,
+ * but is incompatible with its message formats.
+ *
+ * How nodes can exchange that they are able to communicate using selective
+ * fragment recovery is currently not specified, so this feature should only be
+ * used if the operator of a network can ensure that all 6LoWPAN nodes within
+ * that network can communicate using selective fragment recovery.
+ *
+ * @see [RFC 8931](https://tools.ietf.org/html/rfc8931)
+ * @{
+ *
+ * @file
+ * @brief 6LoWPAN selective fragment recovery definitions for GNRC
+ *
+ * @author Martine Lenders
+ */
+#ifndef NET_GNRC_SIXLOWPAN_FRAG_SFR_H
+#define NET_GNRC_SIXLOWPAN_FRAG_SFR_H
+
+#include "assert.h"
+#include "bitfield.h"
+#include "net/gnrc/pkt.h"
+#include "net/gnrc/netif.h"
+#include "net/gnrc/sixlowpan/config.h"
+#include "net/gnrc/sixlowpan/frag/fb.h"
+#include "net/gnrc/sixlowpan/frag/vrb.h"
+#include "net/gnrc/sixlowpan/frag/sfr_types.h"
+#include "net/sixlowpan/sfr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Message type to signal an acknowledgement request timeout.
+ */
+#define GNRC_SIXLOWPAN_FRAG_SFR_ARQ_TIMEOUT_MSG (0x0227)
+
+/**
+ * @brief Message type to signal the sending of the next frame.
+ */
+#define GNRC_SIXLOWPAN_FRAG_SFR_INTER_FRAG_GAP_MSG (0x0228)
+
+/**
+ * @brief Initialize selective fragment recovery
+ */
+void gnrc_sixlowpan_frag_sfr_init(void);
+
+/**
+ * @brief Initialize a network interface for selective fragment recovery
+ *
+ * @note This is a NOP without module `gnrc_sixlowpan_frag_sfr`
+ *
+ * @param[in] netif A network interface
+ */
+static inline void gnrc_sixlowpan_frag_sfr_init_iface(gnrc_netif_t *netif)
+{
+ if (IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_SFR) &&
+ gnrc_netif_is_6lo(netif)) {
+#if IS_USED(MODULE_GNRC_NETIF_6LO)
+ netif->sixlo.local_flags |= GNRC_NETIF_6LO_LOCAL_FLAGS_SFR;
+ netif->sixlo.max_frag_size =
+ (netif->sixlo.max_frag_size > CONFIG_GNRC_SIXLOWPAN_SFR_OPT_FRAG_SIZE)
+ ? CONFIG_GNRC_SIXLOWPAN_SFR_OPT_FRAG_SIZE
+ : netif->sixlo.max_frag_size;
+ assert(netif->sixlo.max_frag_size >= CONFIG_GNRC_SIXLOWPAN_SFR_MIN_FRAG_SIZE);
+#endif
+ }
+}
+
+/**
+ * @brief Checks if a network interface is configured for selective fragment
+ * recovery
+ *
+ * @param[in] netif A network interface.
+ *
+ * @return true, if @p netif supports selective fragment recovery and has it
+ * enabled.
+ * @return false, if @p netif does not support selective fragment recovery or
+ * does not have it enabled.
+ */
+static inline bool gnrc_sixlowpan_frag_sfr_netif(gnrc_netif_t *netif)
+{
+#if IS_USED(MODULE_GNRC_NETIF_6LO)
+ return IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_SFR) &&
+ gnrc_netif_is_6lo(netif) &&
+ (netif->sixlo.local_flags & GNRC_NETIF_6LO_LOCAL_FLAGS_SFR);
+#else
+ (void)netif;
+ return false;
+#endif
+}
+
+/**
+ * @brief Sends a packet via selective fragment recovery
+ *
+ * @pre `ctx != NULL`
+ * @pre gnrc_sixlowpan_frag_fb_t::pkt of @p ctx is equal to @p pkt or
+ * `pkt == NULL`.
+ *
+ * @param[in] pkt A packet. May be NULL.
+ * @param[in] ctx Fragmentation buffer entry of. Expected to be of type
+ * @ref gnrc_sixlowpan_frag_fb_t, with gnrc_sixlowpan_frag_fb_t
+ * set to @p pkt. Must not be NULL.
+ * @param[in] page Current 6Lo dispatch parsing page.
+ */
+void gnrc_sixlowpan_frag_sfr_send(gnrc_pktsnip_t *pkt, void *ctx,
+ unsigned page);
+
+/**
+ * @brief Handles a packet containing a selective fragment recovery header
+ *
+ * @param[in] pkt The packet to handle.
+ * @param[in] ctx Context for the packet. May be NULL.
+ * @param[in] page Current 6Lo dispatch parsing page.
+ */
+void gnrc_sixlowpan_frag_sfr_recv(gnrc_pktsnip_t *pkt, void *ctx, unsigned page);
+
+/**
+ * @brief Forward a fragment via selective fragment recovery
+ *
+ * @param[in] pkt The fragment to forward (without RFRAG header).
+ * Is consumed by this function.
+ * @param[in] rfrag The originally received RFRAG header.
+ * @param[in] vrbe Virtual reassembly buffer containing the forwarding
+ * information.
+ * @param[in] page Current 6Lo dispatch parsing page.
+ *
+ * @return 0, on success.
+ * @return -ENOMEM, when packet buffer is too full to prepare packet for
+ * forwarding. @p pkt is released in that case.
+ */
+int gnrc_sixlowpan_frag_sfr_forward(gnrc_pktsnip_t *pkt,
+ sixlowpan_sfr_rfrag_t *rfrag,
+ gnrc_sixlowpan_frag_vrb_t *vrbe,
+ unsigned page);
+
+/**
+ * @brief Handles an Acknowledgment request timeout
+ *
+ * @param[in] fbuf The fragmentation buffer representing the datagram for which
+ * fragments the Acknowledgment request timed out.
+ */
+void gnrc_sixlowpan_frag_sfr_arq_timeout(gnrc_sixlowpan_frag_fb_t *fbuf);
+
+/**
+ * @brief Handles inter frame gap
+ */
+void gnrc_sixlowpan_frag_sfr_inter_frame_gap(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NET_GNRC_SIXLOWPAN_FRAG_SFR_H */
+/** @} */
diff --git a/sys/include/net/gnrc/sixlowpan/frag/sfr_types.h b/sys/include/net/gnrc/sixlowpan/frag/sfr_types.h
new file mode 100644
index 000000000000..1894c0120848
--- /dev/null
+++ b/sys/include/net/gnrc/sixlowpan/frag/sfr_types.h
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+/**
+ * @addtogroup net_gnrc_sixlowpan_frag_sfr
+ * @{
+ *
+ * @file
+ * @brief 6LoWPAN selective fragment recovery type definitions for GNRC
+ *
+ * @author Martine Lenders
+ */
+#ifndef NET_GNRC_SIXLOWPAN_FRAG_SFR_TYPES_H
+#define NET_GNRC_SIXLOWPAN_FRAG_SFR_TYPES_H
+
+#include
+
+#include "bitfield.h"
+#include "clist.h"
+#include "evtimer_msg.h"
+#include "msg.h"
+#include "xtimer.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Bitmap type to simplify comparisons
+ */
+typedef union {
+ uint32_t u32; /**< numerical version of bitmap */
+ BITFIELD(bf, 32U); /**< bitfield version of bitmap */
+} gnrc_sixlowpan_frag_sfr_bitmap_t;
+
+/**
+ * @brief Extension for @ref net_gnrc_sixlowpan_frag_fb for selective
+ * fragment recovery
+ */
+typedef struct gnrc_sixlowpan_frag_sfr_fb {
+ /**
+ * @brief Acknowledgment request timeout event
+ */
+ evtimer_msg_event_t arq_timeout_event;
+ uint32_t arq_timeout; /**< Time in microseconds the sender should
+ * wait for an RFRAG Acknowledgment */
+ uint8_t cur_seq; /**< Sequence number for next fragment */
+ uint8_t frags_sent; /**< Number of fragments sent */
+ uint8_t window_size; /**< Current window size in number of
+ * fragments */
+ uint8_t retrans; /**< Datagram retransmissions */
+ clist_node_t window; /**< Sent fragments of the current window */
+} gnrc_sixlowpan_frag_sfr_fb_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NET_GNRC_SIXLOWPAN_FRAG_SFR_TYPES_H */
+/** @} */
diff --git a/sys/include/net/gnrc/sixlowpan/frag/vrb.h b/sys/include/net/gnrc/sixlowpan/frag/vrb.h
index 462a82e971a6..58fe7838af7a 100644
--- a/sys/include/net/gnrc/sixlowpan/frag/vrb.h
+++ b/sys/include/net/gnrc/sixlowpan/frag/vrb.h
@@ -54,6 +54,13 @@ typedef struct {
* @brief Outgoing tag to gnrc_sixlowpan_frag_rb_base_t::dst
*/
uint16_t out_tag;
+#if IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_SFR)
+ int16_t offset_diff; /**< offset change due to recompression */
+ /**
+ * @brief Incoming interface to gnrc_sixlowpan_frag_rb_base_t::src
+ */
+ gnrc_netif_t *in_netif;
+#endif /* IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_SFR) */
} gnrc_sixlowpan_frag_vrb_t;
/**
diff --git a/sys/net/gnrc/Makefile b/sys/net/gnrc/Makefile
index dc63650d80d3..8f8da293d16b 100644
--- a/sys/net/gnrc/Makefile
+++ b/sys/net/gnrc/Makefile
@@ -106,6 +106,9 @@ endif
ifneq (,$(filter gnrc_sixlowpan_frag_rb,$(USEMODULE)))
DIRS += network_layer/sixlowpan/frag/rb
endif
+ifneq (,$(filter gnrc_sixlowpan_frag_sfr,$(USEMODULE)))
+ DIRS += network_layer/sixlowpan/frag/sfr
+endif
ifneq (,$(filter gnrc_sixlowpan_frag_stats,$(USEMODULE)))
DIRS += network_layer/sixlowpan/frag/stats
endif
diff --git a/sys/net/gnrc/netif/gnrc_netif.c b/sys/net/gnrc/netif/gnrc_netif.c
index 6e33d5b19930..94dc8dc212cc 100644
--- a/sys/net/gnrc/netif/gnrc_netif.c
+++ b/sys/net/gnrc/netif/gnrc_netif.c
@@ -32,6 +32,9 @@
#if IS_USED(MODULE_GNRC_NETIF_PKTQ)
#include "net/gnrc/netif/pktq.h"
#endif /* IS_USED(MODULE_GNRC_NETIF_PKTQ) */
+#if IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_SFR)
+#include "net/gnrc/sixlowpan/frag/sfr.h"
+#endif /* IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_SFR) */
#if IS_USED(MODULE_NETSTATS)
#include "net/netstats.h"
#endif /* IS_USED(MODULE_NETSTATS) */
@@ -1389,6 +1392,9 @@ void gnrc_netif_default_init(gnrc_netif_t *netif)
_init_from_device(netif);
#ifdef DEVELHELP
_test_options(netif);
+#endif
+#if IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_SFR)
+ gnrc_sixlowpan_frag_sfr_init_iface(netif);
#endif
netif->cur_hl = CONFIG_GNRC_NETIF_DEFAULT_HL;
#ifdef MODULE_GNRC_IPV6_NIB
diff --git a/sys/net/gnrc/network_layer/sixlowpan/frag/Kconfig b/sys/net/gnrc/network_layer/sixlowpan/frag/Kconfig
index 71df2bee96c9..09667f2d676e 100644
--- a/sys/net/gnrc/network_layer/sixlowpan/frag/Kconfig
+++ b/sys/net/gnrc/network_layer/sixlowpan/frag/Kconfig
@@ -5,10 +5,16 @@
# directory for more details.
#
-if USEMODULE_GNRC_SIXLOWPAN_FRAG
+if USEMODULE_GNRC_SIXLOWPAN_FRAG || USEMODULE_GNRC_SIXLOWPAN_FRAG_SFR
rsource "fb/Kconfig"
rsource "rb/Kconfig"
rsource "vrb/Kconfig"
-endif # USEMODULE_GNRC_SIXLOWPAN_FRAG
+endif # USEMODULE_GNRC_SIXLOWPAN_FRAG || USEMODULE_GNRC_SIXLOWPAN_FRAG_SFR
+
+if USEMODULE_GNRC_SIXLOWPAN_FRAG_SFR
+
+rsource "sfr/Kconfig"
+
+endif # USEMODULE_GNRC_SIXLOWPAN_FRAG_SFR
diff --git a/sys/net/gnrc/network_layer/sixlowpan/frag/fb/Kconfig b/sys/net/gnrc/network_layer/sixlowpan/frag/fb/Kconfig
index ec333cd722fa..745485b9dab2 100644
--- a/sys/net/gnrc/network_layer/sixlowpan/frag/fb/Kconfig
+++ b/sys/net/gnrc/network_layer/sixlowpan/frag/fb/Kconfig
@@ -14,6 +14,7 @@ if KCONFIG_USEMODULE_GNRC_SIXLOWPAN_FRAG_FB
config GNRC_SIXLOWPAN_FRAG_FB_SIZE
int "Number of datagrams that can be fragmented simultaneously"
+ default 4 if MODULE_GNRC_SIXLOWPAN_FRAG_SFR
default 1
help
This determines the number of @ref gnrc_sixlowpan_frag_fb_t instances
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 a84b060b02e5..97fa3cd2fdc1 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
@@ -252,7 +252,7 @@ static size_t _6lo_frag_size(gnrc_pktsnip_t *pkt, size_t offset, uint8_t *data)
static uint16_t _6lo_sfr_datagram_size(gnrc_pktsnip_t *pkt, size_t offset)
{
/* offset doubles as datagram size in RFRAG header when sequence number is 0
- * see https://tools.ietf.org/html/draft-ietf-6lo-fragment-recovery-08#section-5.1 */
+ * see https://tools.ietf.org/html/rfc8931#section-5.1 */
return (offset == 0) ? sixlowpan_sfr_rfrag_get_offset(pkt->data) : 0;
}
@@ -264,10 +264,27 @@ static uint8_t *_6lo_sfr_payload(gnrc_pktsnip_t *pkt)
static size_t _6lo_sfr_frag_size(gnrc_pktsnip_t *pkt)
{
/* TODO: if necessary check MAC layer here,
- * see https://tools.ietf.org/html/draft-ietf-6lo-fragment-recovery-08#section-5.1 */
+ * see https://tools.ietf.org/html/rfc8931#section-5.1 */
return sixlowpan_sfr_rfrag_get_frag_size(pkt->data);
}
+static gnrc_pktsnip_t *_mark_frag_hdr(gnrc_pktsnip_t *pkt)
+{
+ if (IS_USED(MODULE_GNRC_SIXLOWPAN_IPHC)) {
+ if (IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_SFR) &&
+ sixlowpan_sfr_rfrag_is(pkt->data)) {
+ return gnrc_pktbuf_mark(pkt, sizeof(sixlowpan_sfr_rfrag_t),
+ GNRC_NETTYPE_SIXLOWPAN);
+ }
+ else if (IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG) &&
+ sixlowpan_frag_is(pkt->data)) {
+ return gnrc_pktbuf_mark(pkt, sizeof(sixlowpan_frag_t),
+ GNRC_NETTYPE_SIXLOWPAN);
+ }
+ }
+ return NULL;
+}
+
static int _rbuf_add(gnrc_netif_hdr_t *netif_hdr, gnrc_pktsnip_t *pkt,
size_t offset, unsigned page)
{
@@ -294,7 +311,7 @@ static int _rbuf_add(gnrc_netif_hdr_t *netif_hdr, gnrc_pktsnip_t *pkt,
}
else if (IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_SFR) &&
sixlowpan_sfr_rfrag_is(pkt->data)) {
- sixlowpan_sfr_rfrag_t *rfrag = pkt->data;;
+ sixlowpan_sfr_rfrag_t *rfrag = pkt->data;
data = _6lo_sfr_payload(pkt);
frag_size = _6lo_sfr_frag_size(pkt);
@@ -354,6 +371,9 @@ static int _rbuf_add(gnrc_netif_hdr_t *netif_hdr, gnrc_pktsnip_t *pkt,
return RBUF_ADD_ERROR;
}
entry.rbuf = &rbuf[res];
+#if IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_SFR)
+ offset += entry.rbuf->offset_diff;
+#endif /* IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_SFR) */
if ((offset + frag_size) > entry.super->datagram_size) {
DEBUG("6lo rfrag: fragment too big for resulting datagram, discarding datagram\n");
gnrc_pktbuf_release(entry.rbuf->pkt);
@@ -379,11 +399,11 @@ static int _rbuf_add(gnrc_netif_hdr_t *netif_hdr, gnrc_pktsnip_t *pkt,
DEBUG("6lo rbuf: add fragment data\n");
entry.super->current_size += (uint16_t)frag_size;
if (offset == 0) {
-#ifdef MODULE_GNRC_SIXLOWPAN_IPHC
- if (sixlowpan_iphc_is(data)) {
+ if (IS_USED(MODULE_GNRC_SIXLOWPAN_IPHC) &&
+ sixlowpan_iphc_is(data)) {
DEBUG("6lo rbuf: detected IPHC header.\n");
- gnrc_pktsnip_t *frag_hdr = gnrc_pktbuf_mark(pkt,
- sizeof(sixlowpan_frag_t), GNRC_NETTYPE_SIXLOWPAN);
+ gnrc_pktsnip_t *frag_hdr = _mark_frag_hdr(pkt);
+
if (frag_hdr == NULL) {
DEBUG("6lo rbuf: unable to mark fragment header. "
"aborting reassembly.\n");
@@ -403,9 +423,7 @@ static int _rbuf_add(gnrc_netif_hdr_t *netif_hdr, gnrc_pktsnip_t *pkt,
return res;
}
}
- else
-#endif
- if (data[0] == SIXLOWPAN_UNCOMP) {
+ else if (data[0] == SIXLOWPAN_UNCOMP) {
DEBUG("6lo rbuf: detected uncompressed datagram\n");
data++;
if (IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_MINFWD) &&
@@ -429,12 +447,17 @@ static int _rbuf_add(gnrc_netif_hdr_t *netif_hdr, gnrc_pktsnip_t *pkt,
return _forward_uncomp(pkt, rbuf, vrbe, page);
}
}
+ else if (IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_SFR) &&
+ sixlowpan_sfr_rfrag_is(pkt->data)) {
+ entry.super->datagram_size--;
+ }
}
}
- if (IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_MINFWD)) {
- /* all cases to try forwarding with minfwd above failed so just do
- * normal reassembly. For the `minfwd` case however, we need to
- * resize `entry.rbuf->pkt`, since we kept the packet allocation
+ if (IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_MINFWD) ||
+ IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_SFR)) {
+ /* all cases to try forwarding with minfwd or SFR above failed so
+ * just do normal reassembly. For the `minfwd` case however, we need
+ * to resize `entry.rbuf->pkt`, since we kept the packet allocation
* with fragment forwarding as minimal as possible in
* `_rbuf_get()` */
res = _rbuf_resize_for_reassembly(entry.rbuf);
@@ -575,8 +598,14 @@ static int _rbuf_get(const void *src, size_t src_len,
for (unsigned int i = 0; i < CONFIG_GNRC_SIXLOWPAN_FRAG_RBUF_SIZE; i++) {
/* check first if entry already available */
- if ((rbuf[i].pkt != NULL) && (rbuf[i].super.datagram_size == size) &&
- (rbuf[i].super.tag == tag) && (rbuf[i].super.src_len == src_len) &&
+ if ((rbuf[i].pkt != NULL) && (rbuf[i].super.tag == tag) &&
+ ((IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_SFR) &&
+ /* not all SFR fragments carry the datagram size, so make 0 a
+ * legal value to not compare datagram size */
+ ((size == 0) || (rbuf[i].super.datagram_size == size))) ||
+ (!IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_SFR) &&
+ (rbuf[i].super.datagram_size == size))) &&
+ (rbuf[i].super.src_len == src_len) &&
(rbuf[i].super.dst_len == dst_len) &&
(memcmp(rbuf[i].super.src, src, src_len) == 0) &&
(memcmp(rbuf[i].super.dst, dst, dst_len) == 0)) {
@@ -689,6 +718,10 @@ static int _rbuf_get(const void *src, size_t src_len,
res->super.dst_len = dst_len;
res->super.tag = tag;
res->super.current_size = 0;
+#if IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_SFR)
+ res->offset_diff = 0U;
+ memset(res->received, 0U, sizeof(res->received));
+#endif /* IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_SFR) */
DEBUG("6lo rfrag: entry %p (%s, ", (void *)res,
gnrc_netif_addr_to_str(res->super.src, res->super.src_len,
diff --git a/sys/net/gnrc/network_layer/sixlowpan/frag/sfr/Kconfig b/sys/net/gnrc/network_layer/sixlowpan/frag/sfr/Kconfig
new file mode 100644
index 000000000000..3db6a9e11a20
--- /dev/null
+++ b/sys/net/gnrc/network_layer/sixlowpan/frag/sfr/Kconfig
@@ -0,0 +1,101 @@
+# Copyright (c) 2020 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.
+#
+menuconfig KCONFIG_USEMODULE_GNRC_SIXLOWPAN_FRAG_SFR
+ bool "Configure GNRC 6LoWPAN Selective Fragment Recovery"
+ depends on USEMODULE_GNRC_SIXLOWPAN_FRAG_SFR
+ help
+ Configure GNRC 6LoWPAN Selective Fragement Recovery using Kconfig.
+
+if KCONFIG_USEMODULE_GNRC_SIXLOWPAN_FRAG_SFR
+
+config GNRC_SIXLOWPAN_SFR_MIN_FRAG_SIZE
+ int "Default minimum value for fragment size (MinFragmentSize)"
+ default 96
+
+config GNRC_SIXLOWPAN_SFR_MAX_FRAG_SIZE
+ int "Default maximum value for fragment size (MaxFragmentSize)"
+ default 112
+ help
+ It must be lower than the minimum MTU along the path. A large value
+ augments the chances of buffer bloat and transmission loss. The value
+ must be less than 512 if the unit is defined for the PHY layer is the
+ octet.
+
+config GNRC_SIXLOWPAN_SFR_OPT_FRAG_SIZE
+ int "Default value for fragment size that the sender should use to start with (OptFragmentSize)"
+ default 112
+ help
+ Must be inclusively between @ref GNRC_SIXLOWPAN_SFR_MIN_FRAG_SIZE and
+ @ref GNRC_SIXLOWPAN_SFR_MAX_FRAG_SIZE
+
+config GNRC_SIXLOWPAN_SFR_USE_ECN
+ bool "Indicates whether the sender should react to ECN (UseECN)"
+ default false
+ help
+ When the sender reacts to Explicit Congestion Notification (ECN) its
+ window size will vary between @ref
+ CONFIG_GNRC_SIXLOWPAN_SFR_MIN_WIN_SIZE and @ref
+ CONFIG_GNRC_SIXLOWPAN_SFR_MAX_WIN_SIZE.
+
+if GNRC_SIXLOWPAN_SFR_USE_ECN
+comment "Warning: Reaction of sender to ECN is not implemented yet"
+endif
+
+config GNRC_SIXLOWPAN_SFR_MIN_WIN_SIZE
+ int "Default minimum value of window size that the sender can use (MinWindowSize)"
+ default 1
+ range 1 32
+
+config GNRC_SIXLOWPAN_SFR_MAX_WIN_SIZE
+ int "Default maximum value of window size that the sender can use (MaxWindowSize)"
+ default 16
+ range 1 32
+
+config GNRC_SIXLOWPAN_SFR_OPT_WIN_SIZE
+ int "Default value of window size that the sender should start with (OptWindowSize)"
+ default 16
+ range 1 32
+ help
+ Must be inclusively between @ref GNRC_SIXLOWPAN_SFR_MIN_WIN_SIZE and
+ @ref GNRC_SIXLOWPAN_SFR_MAX_WIN_SIZE
+
+config GNRC_SIXLOWPAN_SFR_INTER_FRAME_GAP_US
+ int "Default minimum amount of time between transmissions in microseconds (InterFrameGap)"
+ default 100
+ help
+ All packets to a same destination, and in particular fragments, may be
+ subject to receive while transmitting and hidden terminal collisions
+ with the next or the previous transmission as the fragments progress
+ along a same path. The InterFrameGap protects the propagation of to one
+ transmission before the next one is triggered and creates a duty cycle
+ that controls the ratio of air and memory in intermediate nodes that a
+ particular datagram will use.
+
+config GNRC_SIXLOWPAN_SFR_MIN_ARQ_TIMEOUT_MS
+ int "Minimum RFRAG-ACK timeout in msec before a node takes a next action (MinARQTimeOut)"
+ default 350
+
+config GNRC_SIXLOWPAN_SFR_MAX_ARQ_TIMEOUT_MS
+ int "Maximum RFRAG-ACK timeout in msec before a node takes a next action (MaxARQTimeOut)"
+ default 700
+
+config GNRC_SIXLOWPAN_SFR_OPT_ARQ_TIMEOUT_MS
+ int "Default RFRAG-ACK timeout in msec before a node takes a next action (OptARQTimeOut)"
+ default 700
+ help
+ Must be inclusively between @ref GNRC_SIXLOWPAN_SFR_MIN_ARQ_TIMEOUT_MS
+ and @ref GNRC_SIXLOWPAN_SFR_MAX_ARQ_TIMEOUT_MS
+
+config GNRC_SIXLOWPAN_SFR_FRAG_RETRIES
+ int "The maximum number of retries for a particular fragment (MaxFragRetries)"
+ default 2
+
+config GNRC_SIXLOWPAN_SFR_DG_RETRIES
+ int "The maximum number of retries from scratch for a particular datagram (MaxDatagramRetries)"
+ default 0
+
+endif
diff --git a/sys/net/gnrc/network_layer/sixlowpan/frag/sfr/Makefile b/sys/net/gnrc/network_layer/sixlowpan/frag/sfr/Makefile
new file mode 100644
index 000000000000..70cae6bbeb49
--- /dev/null
+++ b/sys/net/gnrc/network_layer/sixlowpan/frag/sfr/Makefile
@@ -0,0 +1,3 @@
+MODULE := gnrc_sixlowpan_frag_sfr
+
+include $(RIOTBASE)/Makefile.base
diff --git a/sys/net/gnrc/network_layer/sixlowpan/frag/sfr/gnrc_sixlowpan_frag_sfr.c b/sys/net/gnrc/network_layer/sixlowpan/frag/sfr/gnrc_sixlowpan_frag_sfr.c
new file mode 100644
index 000000000000..9b2119c8c448
--- /dev/null
+++ b/sys/net/gnrc/network_layer/sixlowpan/frag/sfr/gnrc_sixlowpan_frag_sfr.c
@@ -0,0 +1,1604 @@
+/*
+ * 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
+ */
+
+#include "irq.h"
+#include "kernel_defines.h"
+#ifdef MODULE_GNRC_IPV6_NIB
+#include "net/ipv6/addr.h"
+#endif
+#ifdef MODULE_GNRC_IPV6
+#include "net/ipv6/hdr.h"
+#endif
+#include "net/gnrc/neterr.h"
+#include "net/gnrc/netif/internal.h"
+#include "net/gnrc/pkt.h"
+#include "net/gnrc/sixlowpan.h"
+#include "net/gnrc/sixlowpan/config.h"
+#include "net/gnrc/sixlowpan/frag/fb.h"
+#include "net/gnrc/sixlowpan/frag/rb.h"
+#include "net/gnrc/sixlowpan/frag/vrb.h"
+#include "net/sixlowpan/sfr.h"
+#include "thread.h"
+#include "unaligned.h"
+#include "xtimer.h"
+
+#include "net/gnrc/sixlowpan/frag/sfr.h"
+
+#define ENABLE_DEBUG 0
+#include "debug.h"
+
+#define FRAG_DESCS_POOL_SIZE (CONFIG_GNRC_SIXLOWPAN_FRAG_FB_SIZE * \
+ CONFIG_GNRC_SIXLOWPAN_SFR_MAX_WIN_SIZE)
+#define FRAME_QUEUE_POOL_SIZE (FRAG_DESCS_POOL_SIZE + \
+ CONFIG_GNRC_SIXLOWPAN_FRAG_VRB_SIZE)
+
+typedef struct {
+ clist_node_t super; /**< list parent instance */
+ uint32_t last_sent; /**< last time sent in microseconds */
+ /**
+ * @brief Acknowledgment request flag, sequence number, and fragment size
+ */
+ uint16_t ar_seq_fs;
+ uint16_t offset; /**< offset of the fragment */
+ uint8_t retries; /**< how often the fragment was retried */
+} _frag_desc_t;
+
+typedef struct {
+ clist_node_t super; /**< list parent instance */
+ gnrc_pktsnip_t *frame; /**< frame in the queue */
+ uint8_t datagram_tag; /**< tag for identification */
+ uint8_t page; /**< parsing page context for the frame */
+} _frame_queue_t;
+
+typedef struct {
+ enum {
+ _UNDEF = 0,
+ _RB,
+ _VRB,
+ } type;
+ union {
+ gnrc_sixlowpan_frag_rb_base_t *base;
+ gnrc_sixlowpan_frag_rb_t *rb;
+ gnrc_sixlowpan_frag_vrb_t *vrb;
+ } entry;
+} _generic_rb_entry_t;
+
+#ifdef MODULE_GNRC_IPV6_NIB
+static char addr_str[IPV6_ADDR_MAX_STR_LEN];
+#else /* MODULE_GNRC_IPV6_NIB */
+static char addr_str[GNRC_NETIF_HDR_L2ADDR_PRINT_LEN];
+#endif /* MODULE_GNRC_IPV6_NIB */
+
+static evtimer_msg_t _arq_timer;
+static xtimer_t _if_gap_timer = { .offset = 0, .long_offset = 0 };
+static msg_t _if_gap_msg = { .type = GNRC_SIXLOWPAN_FRAG_SFR_INTER_FRAG_GAP_MSG };
+static uint32_t _last_frame_sent = 0U;
+
+static _frag_desc_t _frag_descs_pool[FRAG_DESCS_POOL_SIZE];
+static _frame_queue_t _frame_queue_pool[FRAME_QUEUE_POOL_SIZE];
+
+static clist_node_t _frag_descs_free;
+static clist_node_t _frame_queue_free;
+static clist_node_t _frame_queue;
+
+static const gnrc_sixlowpan_frag_sfr_bitmap_t _full_bitmap = { .u32 = UINT32_MAX };
+static const gnrc_sixlowpan_frag_sfr_bitmap_t _null_bitmap = { .u32 = 0U };
+
+/**
+ * @brief Converts a @ref sys_bitmap based bitmap to a
+ * gnrc_sixlowpan_frag_sfr_bitmap_t
+ *
+ * @param[in] bitmap A @ref sys_bitmap
+ *
+ * @return A gnrc_sixlowpan_frag_sfr_bitmap_t.
+ */
+static inline gnrc_sixlowpan_frag_sfr_bitmap_t *_to_bitmap(uint8_t *bitmap);
+
+/**
+ * @brief Checks if fragment represented by a fragment descriptor requested an
+ * ACK
+ */
+static inline bool _frag_ack_req(_frag_desc_t *frag);
+/**
+ * @brief Returns sequence number for fragment represented by fragment
+ * descriptor
+ */
+static inline uint8_t _frag_seq(_frag_desc_t *frag);
+
+/**
+ * @brief Returns fragment size for fragment represented by fragment
+ * descriptor
+ */
+static inline uint16_t _frag_size(_frag_desc_t *frag);
+
+/**
+ * @brief Cleans up a fragmentation buffer entry and all state related to its
+ * datagram.
+ *
+ * @param[in] fbuf A fragmentation buffer entry
+ * @param[in] error An errno to provide to an upper layer as the reason for why
+ * gnrc_sixlowpan_frag_fb_t::pkt of @p fbuf was released.
+ */
+static void _clean_up_fbuf(gnrc_sixlowpan_frag_fb_t *fbuf, int error);
+
+/**
+ * @brief Send first fragment.
+ *
+ * @param[in] netif Network interface to send fragment over
+ * @param[in] fbuf Fragmentation buffer for the datagram to fragment
+ * @param[in] page Current 6Lo dispatch parsing page.
+ *
+ * @return Size of the fragment
+ */
+static uint16_t _send_1st_fragment(gnrc_netif_t *netif,
+ gnrc_sixlowpan_frag_fb_t *fbuf,
+ unsigned page);
+
+/**
+ * @brief Send subsequent fragment.
+ *
+ * @param[in] netif Network interface to send fragment over
+ * @param[in] fbuf Fragmentation buffer for the datagram to fragment
+ * @param[in] page Current 6Lo dispatch parsing page.
+ *
+ * @return Size of the fragment
+ */
+static uint16_t _send_nth_fragment(gnrc_netif_t *netif,
+ gnrc_sixlowpan_frag_fb_t *fbuf,
+ unsigned page);
+
+/**
+ * @brief Send a abort pseudo fragment for datagram identified by @p tag
+ *
+ * @param[in] pkt Datagram that is to be aborted.
+ * @param[in] tag Tag for @p pkt.
+ * @param[in] req_ack Request ACK for pseudo fragment from receive
+ * @param[in] page Current 6Lo dispatch parsing page.
+ *
+ * @return true, if abort pseudo fragment was sent.
+ * @return false, if abort pseudo fragment was unable to be sent.
+ */
+static bool _send_abort_frag(gnrc_pktsnip_t *pkt, uint8_t tag, bool req_ack,
+ unsigned page);
+
+/**
+ * @brief Re-send a fragment
+ *
+ * @param[in] node The fragment descriptor for the fragment to be
+ * resend
+ * @param[in] fbuf_ptr Fragmentation buffer for the datagram to fragment
+ *
+ * Used as a `clist_foreach()` iterator function
+ *
+ * return true when fragment was resent
+ * return false on error
+ */
+static int _resend_frag(clist_node_t *node, void *fbuf_ptr);
+
+/**
+ * @brief Retry to send the complete datagram
+ *
+ * @param[in] fbuf Fragmentation buffer for the datagram
+ */
+static void _retry_datagram(gnrc_sixlowpan_frag_fb_t *fbuf);
+
+/**
+ * @brief Cleans up state for the causing RFRAG and optionally also sends an
+ * abort ACK (NULL-bitmap ACK).
+ *
+ * @param[in] pkt The packet causing the abort. Will be released
+ * by this function. gnrc_pktsnip_t::data of @p pkt is
+ * expected to point to an RFRAG packet.
+ * @param[in] entry (Virtual) reassembly buffer entry to abort.
+ * @param[in] netif_hdr NETIF header of @p pkt.
+ * @param[in] send_ack Send an abort ACK.
+ */
+static void _abort_rb(gnrc_pktsnip_t *pkt, _generic_rb_entry_t *entry,
+ gnrc_netif_hdr_t *netif_hdr, bool send_ack);
+
+/**
+ * @brief Sends an RFRAG-ACK
+ *
+ * @param[in] netif Network interface to send ACK over
+ * @param[in] dst Destination address of ACK.
+ * @param[in] dst_len Length of @p dst.
+ * @param[in] rfrag The RFRAG to ACK
+ * @param[in] bitmap The bitmap for the ACK.
+ */
+static void _send_ack(gnrc_netif_t *netif, const uint8_t *dst, uint8_t dst_len,
+ const sixlowpan_sfr_t *rfrag, const uint8_t *bitmap);
+
+/**
+ * @brief Schedule next frame (RFRAG or RFRAG-ACK) with
+ * @ref GNRC_SIXLOWPAN_FRAG_SFR_INTER_FRAG_GAP_MSG
+ */
+static void _sched_next_frame(void);
+
+/**
+ * @brief Schedule ARQ timeout
+ *
+ * @param[in] fbuf A fragmentation buffer holding the state of the datagram
+ * and recoverable fragments.
+ * @param[in] offset Offset for the ARQ timeout in milliseconds.
+ */
+static void _sched_arq_timeout(gnrc_sixlowpan_frag_fb_t *fbuf, uint32_t offset);
+
+/**
+ * @brief Schedule ARQ timeout for an abort fragment pseudo fragment
+ *
+ * @param[in,out] fbuf A fragmentation buffer. All state information will be
+ * cleared when called, except for identifying the ACK.
+ */
+static void _sched_abort_timeout(gnrc_sixlowpan_frag_fb_t *fbuf);
+
+/**
+ * @brief Handle a received RFRAG packet
+ *
+ * @param[in] netif_hdr NETIF header of @p pkt
+ * @param[in] pkt An RFRAG packet
+ * @param[in] page Current 6Lo dispatch parsing page.
+ */
+static void _handle_rfrag(gnrc_netif_hdr_t *netif_hdr,
+ gnrc_pktsnip_t *pkt, unsigned page);
+
+/**
+ * @brief Handle a received RFRAG-ACK
+ *
+ * @param[in] netif_hdr NETIF header of @p pkt
+ * @param[in] pkt An RFRAG-ACK
+ * @param[in] page Current 6Lo dispatch parsing page.
+ */
+static void _handle_ack(gnrc_netif_hdr_t *netif_hdr,
+ gnrc_pktsnip_t *pkt, unsigned page);
+
+/**
+ * @brief Forward a RFRAG
+ *
+ * @pre `entry->type == _VRB`
+ *
+ * @param[in] pkt The RFRAG to forward (without NETIF header)
+ * @param[in] entry The VRB entry to determine the route
+ * @param[in] offset Offset (from the incoming RFRAG's field) of the RFRAG.
+ * for offset > 0 this will be adapted for the offset
+ * difference from the first fragment due to recompression
+ * @param[in] page Current 6Lo dispatch parsing page.
+ *
+ * @return 0 on success,
+ * @return -ENOMEM, when packet buffer is too full to prepare packet for
+ * forwarding. @p pkt is released in that case.
+ */
+static int _forward_rfrag(gnrc_pktsnip_t *pkt, _generic_rb_entry_t *entry,
+ uint16_t offset, unsigned page);
+
+/* ====== PUBLIC FUNCTION DEFINITIONS ====== */
+void gnrc_sixlowpan_frag_sfr_init(void)
+{
+ if (CONFIG_GNRC_SIXLOWPAN_SFR_INTER_FRAME_GAP_US > 0) {
+ for (unsigned i = 0; i < FRAME_QUEUE_POOL_SIZE; i++) {
+ clist_rpush(&_frame_queue_free, &_frame_queue_pool[i].super);
+ }
+ }
+ for (unsigned i = 0; i < FRAG_DESCS_POOL_SIZE; i++) {
+ clist_rpush(&_frag_descs_free, &_frag_descs_pool[i].super);
+ }
+}
+
+void gnrc_sixlowpan_frag_sfr_send(gnrc_pktsnip_t *pkt, void *ctx,
+ unsigned page)
+{
+ gnrc_sixlowpan_frag_fb_t *fbuf = ctx;
+ gnrc_netif_t *netif;
+ int error_no = GNRC_NETERR_SUCCESS;
+ uint16_t res;
+
+ assert((fbuf != NULL) && ((fbuf->pkt == pkt) || (pkt == NULL)));
+ DEBUG("6lo sfr: (re-)sending fragmented datagram %u\n", fbuf->tag);
+ pkt = fbuf->pkt;
+ assert(pkt->type == GNRC_NETTYPE_NETIF);
+ netif = gnrc_netif_hdr_get_netif(pkt->data);
+ assert(netif != NULL);
+
+ if (fbuf->offset == 0) {
+ DEBUG("6lo sfr: sending first fragment\n");
+ res = _send_1st_fragment(netif, fbuf, page);
+ if (res == 0) {
+ DEBUG("6lo sfr: error sending first fragment\n");
+ /* _send_1st_fragment only returns 0 if there is a memory problem */
+ error_no = ENOMEM;
+ goto error;
+ }
+ }
+ else if (fbuf->sfr.frags_sent >= fbuf->sfr.window_size) {
+ DEBUG("6lo sfr: frags_sent >= fbuf->sfr.window_size: don't send more\n");
+ return;
+ }
+ else if (fbuf->offset < fbuf->datagram_size) {
+ DEBUG("6lo sfr: sending subsequent fragment\n");
+ res = _send_nth_fragment(netif, fbuf, page);
+ if (res == 0) {
+ DEBUG("6lo sfr: error sending subsequent fragment (offset = %u)\n",
+ fbuf->offset);
+ /* _send_nth_fragment only returns 0 if there is a memory problem */
+ error_no = ENOMEM;
+ goto error;
+ }
+ }
+ else {
+ /* offset is greater or equal to datagram size
+ * => we are done sending fragments (not an error, but we can release
+ * the fragmentation buffer now) */
+ goto error;
+ }
+ fbuf->offset += res;
+
+ if ((fbuf->sfr.frags_sent < fbuf->sfr.window_size) &&
+ (fbuf->offset < fbuf->datagram_size) &&
+ !gnrc_sixlowpan_frag_fb_send(fbuf)) {
+ /* the queue of the 6LoWPAN thread is full */
+ error_no = ENOMEM;
+ /* go back offset to not send abort on first fragment */
+ fbuf->offset -= res;
+ goto error;
+ }
+ /* check if last fragment sent requested an ACK */
+ _frag_desc_t *frag_desc = (_frag_desc_t *)clist_rpeek(&fbuf->sfr.window);
+ DEBUG("6lo sfr: last sent fragment (tag: %u, X: %i, seq: %u, "
+ "frag_size: %u, offset: %u)\n",
+ (uint8_t)fbuf->tag, _frag_ack_req(frag_desc),
+ _frag_seq(frag_desc), _frag_size(frag_desc),
+ frag_desc->offset);
+ if (_frag_ack_req(frag_desc)) {
+ /* initialize _arq_timer if not yet done */
+ if (_arq_timer.callback == NULL) {
+ evtimer_init_msg(&_arq_timer);
+ }
+ _sched_arq_timeout(fbuf, fbuf->sfr.arq_timeout);
+ }
+ thread_yield();
+ return;
+error:
+ /* don't send abort for first fragment, the network does not know about
+ * the datagram */
+ if ((fbuf->offset > 0) &&
+ _send_abort_frag(fbuf->pkt, (uint8_t)fbuf->tag, true, 0)) {
+ /* wait for ACK before fbuf is deleted */
+ _sched_abort_timeout(fbuf);
+ }
+ else {
+ _clean_up_fbuf(fbuf, error_no);
+ }
+}
+
+void gnrc_sixlowpan_frag_sfr_recv(gnrc_pktsnip_t *pkt, void *ctx,
+ unsigned page)
+{
+ sixlowpan_sfr_t *hdr;
+ gnrc_netif_hdr_t *netif_hdr;
+
+ (void)ctx;
+ DEBUG("6lo sfr: received selective fragment forwarding message\n");
+ assert(pkt != NULL);
+ hdr = pkt->data;
+ assert(pkt->next != NULL);
+ netif_hdr = pkt->next->data;
+ assert(netif_hdr != NULL);
+ if (page != 0) {
+ DEBUG("6lo sfr: Invalid page %u\n", page);
+ gnrc_pktbuf_release(pkt);
+ }
+ else if (sixlowpan_sfr_rfrag_is(hdr)) {
+ _handle_rfrag(netif_hdr, pkt, page);
+ }
+ else if (sixlowpan_sfr_ack_is(hdr)) {
+ _handle_ack(netif_hdr, pkt, page);
+ }
+ else {
+ DEBUG("6lo sfr: Unknown dispatch: %02x\n",
+ hdr->disp_ecn & SIXLOWPAN_SFR_DISP_MASK);
+ gnrc_pktbuf_release(pkt);
+ }
+}
+
+int gnrc_sixlowpan_frag_sfr_forward(gnrc_pktsnip_t *pkt,
+ sixlowpan_sfr_rfrag_t *rfrag,
+ gnrc_sixlowpan_frag_vrb_t *vrbe,
+ unsigned page)
+{
+ _generic_rb_entry_t entry = { .type = _VRB, .entry = { .vrb = vrbe } };
+ gnrc_pktsnip_t *hdrsnip = gnrc_pktbuf_add(pkt, rfrag, sizeof(*rfrag),
+ GNRC_NETTYPE_SIXLOWPAN);
+
+ /* free all intervals associated to the VRB entry, as we don't need them
+ * with SFR, so throw them out, to save this resource */
+ while (vrbe->super.ints) {
+ vrbe->super.ints->end = 0U;
+ vrbe->super.ints = vrbe->super.ints->next;
+ }
+ if (hdrsnip == NULL) {
+ DEBUG("6lo sfr: Unable to allocate new rfrag header\n");
+ gnrc_pktbuf_release(pkt);
+ return -ENOMEM;
+ }
+ DEBUG("6lo sfr: adapting old fragment size (%u) for forwarding to %u\n",
+ sixlowpan_sfr_rfrag_get_frag_size(hdrsnip->data),
+ (unsigned)gnrc_pkt_len(pkt));
+ /* due to compression, packet length of the original fragment might have
+ * changed */
+ sixlowpan_sfr_rfrag_set_frag_size(hdrsnip->data, gnrc_pkt_len(pkt));
+ /* offset is adapted in `_forward_rfrag()` */
+ return _forward_rfrag(hdrsnip, &entry, sixlowpan_sfr_rfrag_get_offset(rfrag),
+ page);
+}
+
+void gnrc_sixlowpan_frag_sfr_arq_timeout(gnrc_sixlowpan_frag_fb_t *fbuf)
+{
+ uint32_t now = xtimer_now_usec();
+ _frag_desc_t * const head = (_frag_desc_t *)fbuf->sfr.window.next;
+ _frag_desc_t *frag_desc = head;
+ uint32_t next_arq_offset = fbuf->sfr.arq_timeout;
+ bool reschedule_arq_timeout = false;
+ int error_no = ETIMEDOUT; /* assume time out for fbuf->pkt */
+
+ DEBUG("6lo sfr: ARQ timeout for datagram %u\n", fbuf->tag);
+ fbuf->sfr.arq_timeout_event.msg.content.ptr = NULL;
+ /* copying clist_foreach because we can't work just in function context */
+ if (frag_desc) {
+ do {
+ uint32_t diff;
+
+ frag_desc = (_frag_desc_t *)frag_desc->super.next;
+ diff = now - frag_desc->last_sent;
+ if (diff < (fbuf->sfr.arq_timeout * US_PER_MS)) {
+ /* this fragment's last was last sent < fbuf->sfr.arq_timeout
+ * ago */
+ uint32_t offset = fbuf->sfr.arq_timeout - (diff / US_PER_MS);
+
+ DEBUG("6lo sfr: wait for fragment %u in next reschedule\n",
+ _frag_seq(frag_desc));
+ if (offset < next_arq_offset) {
+ /* wait for this fragments ACK next */
+ next_arq_offset = offset;
+ DEBUG(" (next ARQ timeout in %lu)\n",
+ (long unsigned)next_arq_offset);
+ }
+ /* this fragment is still waiting for its ACK,
+ * reschedule the next ACK timeout to the difference
+ * of the ACK timeout and the time of its last send */
+ reschedule_arq_timeout = true;
+ }
+ else if (_frag_ack_req(frag_desc)) {
+ /* for this fragment we requested an ACK which was not received
+ * yet. Try to resend it */
+ if ((frag_desc->retries++) < CONFIG_GNRC_SIXLOWPAN_SFR_FRAG_RETRIES) {
+ /* we have retries left for this fragment */
+ DEBUG("6lo sfr: %u retries left for fragment (tag: %u, "
+ "X: %i, seq: %u, frag_size: %u, offset: %u)\n",
+ CONFIG_GNRC_SIXLOWPAN_SFR_FRAG_RETRIES -
+ (frag_desc->retries - 1), (uint8_t)fbuf->tag,
+ _frag_ack_req(frag_desc), _frag_seq(frag_desc),
+ _frag_size(frag_desc), frag_desc->offset);
+ if (_resend_frag(&frag_desc->super, fbuf) != 0) {
+ /* _resend_frag failed due to a memory resource
+ * problem */
+ error_no = ENOMEM;
+ goto error;
+ }
+ /* fragment was resent successfully, schedule next ACK
+ * timeout */
+ reschedule_arq_timeout = true;
+ }
+ else {
+ /* out of retries */
+ DEBUG("6lo sfr: no retries left for fragment "
+ "(tag: %u, X: %i, seq: %u, frag_size: %u, "
+ "offset: %u)\n",
+ (uint8_t)fbuf->tag, _frag_ack_req(frag_desc),
+ _frag_seq(frag_desc), _frag_size(frag_desc),
+ frag_desc->offset);
+ /* we are out of retries on the fragment level, but we
+ * might be able to retry the datagram if retries for the
+ * datagram are configured. */
+ _retry_datagram(fbuf);
+ return;
+ }
+ }
+ else {
+ /* Do not resend fragments that were not explicitly asking for
+ * an ACK from the reassembling endpoint on ACK timeout.
+ * If this is true for all fragments remaining in the fragment
+ * buffer, the datagram is to be considered timed out, so
+ * error_no should remain ETIMEDOUT */
+ DEBUG("6lo sfr: nothing to do for fragment %u\n",
+ _frag_seq(frag_desc));
+ }
+ } while (frag_desc != head);
+ }
+ else {
+ /* No fragments to resend, we can assume the packet was delivered
+ * successfully */
+ error_no = GNRC_NETERR_SUCCESS;
+ }
+ assert(fbuf->sfr.frags_sent == clist_count(&fbuf->sfr.window));
+ if (reschedule_arq_timeout) {
+ _sched_arq_timeout(fbuf, next_arq_offset);
+ return;
+ }
+error:
+ /* don't check return value, as we don't want to wait for an ACK again ;-) */
+ _send_abort_frag(fbuf->pkt, (uint8_t)fbuf->tag, false, 0);
+ _clean_up_fbuf(fbuf, error_no);
+}
+
+void gnrc_sixlowpan_frag_sfr_inter_frame_gap(void)
+{
+ if (CONFIG_GNRC_SIXLOWPAN_SFR_INTER_FRAME_GAP_US > 0) {
+ _frame_queue_t *node = (_frame_queue_t *)clist_lpop(&_frame_queue);
+
+ if (node != NULL) {
+ _last_frame_sent = xtimer_now_usec();
+ gnrc_sixlowpan_dispatch_send(node->frame, NULL, node->page);
+ /* unset packet just to be safe */
+ node->frame = NULL;
+ clist_rpush(&_frame_queue_free, &node->super);
+ }
+ if (clist_lpeek(&_frame_queue) != NULL) {
+ _sched_next_frame();
+ }
+ }
+}
+
+/* ====== INTERNAL FUNCTION DEFINITIONS ====== */
+static inline uint16_t _min(uint16_t a, size_t b)
+{
+ return (a < b) ? a : (uint16_t)b;
+}
+
+static inline kernel_pid_t _getpid(void)
+{
+ /* in production, only the 6LoWPAN thread is supposed to call the API
+ * functions, so just get the current thread's PID for sending messages.
+ * When testing, those functions might however be called by the testing
+ * thread (usually the main thread), so indirect over the 6LoWPAN thread in
+ * that case */
+ return IS_ACTIVE(TEST_SUITES) ? gnrc_sixlowpan_get_pid() : thread_getpid();
+}
+
+/*
+ * @brief Returns the datagram in @p fbuf to its original state
+ *
+ * This function can be both used to clean up the fragmentation buffer on
+ * failure without releasing @p fbuf's gnrc_sixlowpan_frag_fb_t::pkt and to
+ * reset a datagram for a datagram retry.
+ *
+ * @param[in] fbuf The fragmentation buffer entry to clean up
+ */
+static void _clean_slate_datagram(gnrc_sixlowpan_frag_fb_t *fbuf)
+{
+ clist_node_t new_queue = { .next = NULL };
+
+ fbuf->sfr.arq_timeout_event.msg.content.ptr = NULL;
+ /* remove potentially scheduled timers for this datagram */
+ evtimer_del((evtimer_t *)(&_arq_timer),
+ &fbuf->sfr.arq_timeout_event.event);
+ fbuf->sfr.arq_timeout_event.event.next = NULL;
+ if (CONFIG_GNRC_SIXLOWPAN_SFR_INTER_FRAME_GAP_US > 0) {
+ for (clist_node_t *node = clist_lpop(&_frame_queue);
+ node != NULL; node = clist_lpop(&_frame_queue)) {
+ _frame_queue_t *entry = (_frame_queue_t *)node;
+ /* remove frames of this datagram from frame queue */
+ if (entry->datagram_tag == fbuf->tag) {
+ gnrc_pktbuf_release(entry->frame);
+ /* unset packet just to be safe */
+ entry->frame = NULL;
+ clist_rpush(&_frag_descs_free, node);
+ }
+ else {
+ clist_rpush(&new_queue, node);
+ }
+ }
+ /* reset frame queue with remaining frames */
+ _frame_queue = new_queue;
+ }
+ fbuf->offset = 0U;
+ fbuf->sfr.cur_seq = 0U;
+ fbuf->sfr.frags_sent = 0U;
+ fbuf->sfr.window_size = CONFIG_GNRC_SIXLOWPAN_SFR_OPT_WIN_SIZE;
+ for (clist_node_t *node = clist_lpop(&fbuf->sfr.window);
+ node != NULL; node = clist_lpop(&fbuf->sfr.window)) {
+ clist_rpush(&_frag_descs_free, node);
+ }
+}
+
+static gnrc_pktsnip_t *_build_rfrag(uint8_t tag, bool ack_req, uint16_t size,
+ uint8_t seq)
+{
+ sixlowpan_sfr_rfrag_t *hdr;
+ gnrc_pktsnip_t *frag = gnrc_pktbuf_add(NULL, NULL,
+ sizeof(sixlowpan_sfr_rfrag_t) +
+ size, GNRC_NETTYPE_SIXLOWPAN);
+
+ if (frag == NULL) {
+ return NULL;
+ }
+ sixlowpan_sfr_rfrag_set_disp(frag->data);
+
+ hdr = frag->data;
+ hdr->base.tag = tag;
+ if (ack_req) {
+ sixlowpan_sfr_rfrag_set_ack_req(hdr);
+ }
+ else {
+ sixlowpan_sfr_rfrag_clear_ack_req(hdr);
+ }
+ sixlowpan_sfr_rfrag_set_frag_size(hdr, size);
+ sixlowpan_sfr_rfrag_set_seq(hdr, seq);
+ /* set offset / datagram_size in callers */
+ return frag;
+}
+
+static gnrc_pktsnip_t *_build_frag_pkt(gnrc_netif_hdr_t *old_netif_hdr,
+ uint8_t tag, bool ack_req, uint16_t size,
+ uint8_t seq)
+{
+ gnrc_netif_hdr_t *new_netif_hdr;
+ gnrc_pktsnip_t *netif, *res;
+
+ DEBUG("6lo sfr: building fragment (tag: %u, X: %i, seq: %u, frag_size: %u)\n",
+ tag, ack_req, seq, size);
+ netif = gnrc_netif_hdr_build(gnrc_netif_hdr_get_src_addr(old_netif_hdr),
+ old_netif_hdr->src_l2addr_len,
+ gnrc_netif_hdr_get_dst_addr(old_netif_hdr),
+ old_netif_hdr->dst_l2addr_len);
+ if (netif == NULL) {
+ return NULL;
+ }
+
+ new_netif_hdr = netif->data;
+ *new_netif_hdr = *old_netif_hdr;
+ res = _build_rfrag(tag, ack_req, size, seq);
+ if (res == NULL) {
+ gnrc_pktbuf_release(netif);
+ return NULL;
+ }
+ return gnrc_pkt_prepend(res, netif);
+}
+
+static gnrc_pktsnip_t *_build_frag_from_fbuf(gnrc_pktsnip_t *pkt,
+ gnrc_sixlowpan_frag_fb_t *fbuf,
+ uint16_t frag_size)
+{
+ return _build_frag_pkt(pkt->data, (uint8_t)fbuf->tag,
+ ((frag_size + fbuf->offset) >= fbuf->datagram_size) ||
+ ((fbuf->sfr.frags_sent + 1) >= fbuf->sfr.window_size),
+ frag_size, fbuf->sfr.cur_seq);
+}
+
+static uint16_t _copy_pkt_to_frag(uint8_t *data, const gnrc_pktsnip_t *pkt,
+ uint16_t frag_size, uint16_t init_offset)
+{
+ uint16_t offset = init_offset;
+
+ while ((pkt != NULL) && (offset < frag_size)) {
+ uint16_t len = _min(frag_size - offset, pkt->size);
+
+ memcpy(data + offset, pkt->data, len);
+
+ offset += len;
+ pkt = pkt->next;
+ }
+ return offset;
+}
+
+static uint16_t _find_offset_and_copy_rest(uint8_t *data, gnrc_pktsnip_t **pkt,
+ uint16_t frag_size,
+ uint16_t offset)
+{
+ uint16_t offset_count = 0, cur_frag_size = 0;
+ while ((*pkt != NULL) && (offset_count != offset)) { /* go to offset */
+ uint16_t pkt_size = (uint16_t)(*pkt)->size;
+ offset_count += pkt_size;
+
+ if (offset_count > offset) { /* we overshot */
+ /* => copy rest of partly send packet snip */
+ uint16_t pkt_offset = offset - (offset_count - pkt_size);
+ size_t clen = _min(frag_size, pkt_size - pkt_offset);
+
+ memcpy(data, ((uint8_t *)(*pkt)->data) + pkt_offset, clen);
+ cur_frag_size = clen;
+ *pkt = (*pkt)->next;
+ break;
+ }
+ *pkt = (*pkt)->next;
+ }
+ return cur_frag_size;
+}
+
+static bool _send_frame(gnrc_pktsnip_t *frame, void *ctx, unsigned page)
+{
+ uint32_t now = xtimer_now_usec();
+
+ if ((CONFIG_GNRC_SIXLOWPAN_SFR_INTER_FRAME_GAP_US == 0) ||
+ ((now - _last_frame_sent) > CONFIG_GNRC_SIXLOWPAN_SFR_INTER_FRAME_GAP_US)) {
+ DEBUG("6lo sfr: dispatch frame to network interface\n");
+ _last_frame_sent = now;
+ gnrc_sixlowpan_dispatch_send(frame, ctx, page);
+ return true;
+ }
+ else {
+ _frame_queue_t *node = (_frame_queue_t *)clist_lpop(&_frame_queue_free);
+
+ if (node != NULL) {
+ sixlowpan_sfr_t *hdr = frame->next->data;
+
+ assert(sixlowpan_sfr_is(hdr));
+ node->frame = frame;
+ node->datagram_tag = hdr->tag;
+ node->page = page;
+ clist_rpush(&_frame_queue, &node->super);
+ _sched_next_frame();
+ }
+ return (node != NULL);
+ }
+}
+
+static bool _send_fragment(gnrc_pktsnip_t *frag, gnrc_sixlowpan_frag_fb_t *fbuf,
+ unsigned page, uint16_t offset)
+{
+ sixlowpan_sfr_rfrag_t *hdr = frag->next->data;
+ _frag_desc_t *frag_desc = (_frag_desc_t *)clist_lpop(&_frag_descs_free);
+ bool res;
+
+ if (frag_desc == NULL) {
+ DEBUG("6lo sfr: could not remember fragment to send\n");
+ gnrc_pktbuf_release(frag);
+ return false;
+ }
+ frag_desc->ar_seq_fs = byteorder_ntohs(hdr->ar_seq_fs);
+ frag_desc->offset = offset;
+ frag_desc->retries = 0;
+ clist_rpush(&fbuf->sfr.window, &frag_desc->super);
+ if ((res = _send_frame(frag, NULL, page))) {
+ frag_desc->last_sent = _last_frame_sent;
+ fbuf->sfr.cur_seq++;
+ fbuf->sfr.frags_sent++;
+ }
+ return res;
+}
+
+static gnrc_pktsnip_t *_build_ack(gnrc_netif_t *netif,
+ const uint8_t *dst, uint8_t dst_len,
+ const sixlowpan_sfr_t *hdr,
+ const uint8_t *bitmap)
+{
+ gnrc_pktsnip_t *ack_snip, *ack_netif;
+ sixlowpan_sfr_ack_t *ack;
+
+ ack_netif = gnrc_netif_hdr_build(NULL, 0, dst, dst_len);
+ if (ack_netif == NULL) {
+ DEBUG("6lo sfr: can't allocate netif header for ACK for (%s, %02x).\n",
+ gnrc_netif_addr_to_str(dst, dst_len, addr_str), hdr->tag);
+ return NULL;
+ }
+ gnrc_netif_hdr_set_netif(ack_netif->data, netif);
+
+ ack_snip = gnrc_pktbuf_add(NULL, NULL, sizeof(sixlowpan_sfr_ack_t),
+ GNRC_NETTYPE_SIXLOWPAN);
+
+ if (ack_snip == NULL) {
+ DEBUG("6lo sfr: can't allocate ACK for (%s, %02x).\n",
+ gnrc_netif_addr_to_str(dst, dst_len, addr_str), hdr->tag);
+ gnrc_pktbuf_release(ack_netif);
+ return NULL;
+ }
+ ack = ack_snip->data;
+ /* https://tools.ietf.org/html/rfc8931#section-6:
+ * The Datagram_Tag in the RFRAG_ACK is unique to the reassembling endpoint
+ * and is enough information for an intermediate hop to locate the VRB that
+ * contains the Datagram_Tag used by the previous hop and the Layer-2
+ * information associated with it (interface and Link-Layer address)..
+ * [...] The reassembling endpoint of a fragment with the 'E' (ECN) flag set
+ * MUST echo that information at most once by setting the 'E' (ECN) flag in
+ * the next RFRAG_ACK.
+ *
+ * => base except dispatch are the same as ack'd RFRAG.
+ */
+ ack->base = *hdr;
+ sixlowpan_sfr_ack_set_disp(&ack->base);
+ memcpy(ack->bitmap, bitmap, sizeof(ack->bitmap));
+ ack_netif->next = ack_snip;
+ return ack_netif;
+}
+
+static void _clean_up_rb_entry(_generic_rb_entry_t *entry)
+{
+ if (entry != NULL) {
+ switch (entry->type) {
+ case _RB:
+ gnrc_pktbuf_release(entry->entry.rb->pkt);
+ gnrc_sixlowpan_frag_rb_remove(entry->entry.rb);
+ break;
+ case _VRB:
+ gnrc_sixlowpan_frag_vrb_rm(entry->entry.vrb);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static void _try_reassembly(gnrc_netif_hdr_t *netif_hdr,
+ gnrc_pktsnip_t *rfrag, unsigned offset,
+ _generic_rb_entry_t *entry,
+ unsigned page)
+{
+ gnrc_sixlowpan_frag_vrb_t *vrbe;
+ const gnrc_sixlowpan_frag_sfr_bitmap_t *bitmap;
+ sixlowpan_sfr_rfrag_t *hdr = rfrag->data;
+ gnrc_pktsnip_t *netif_snip = rfrag->next;
+ /* copy base for ACK */
+ sixlowpan_sfr_t base = hdr->base;
+ int8_t ack_req = sixlowpan_sfr_rfrag_ack_req(hdr);
+ uint8_t seq = sixlowpan_sfr_rfrag_get_seq(hdr);
+
+ assert(netif_snip->data == netif_hdr);
+ gnrc_pktbuf_hold(netif_snip, 1); /* hold netif header to use it with
+ * dispatch_when_complete()
+ * (rb_add() releases `pkt`) */
+ entry->entry.rb = gnrc_sixlowpan_frag_rb_add(netif_hdr, rfrag,
+ offset, page);
+ /* check if VRB entry was created */
+ vrbe = gnrc_sixlowpan_frag_vrb_get(gnrc_netif_hdr_get_src_addr(netif_hdr),
+ netif_hdr->src_l2addr_len, base.tag);
+ if ((entry->entry.rb == NULL) && (vrbe == NULL)) {
+ DEBUG("6lo sfr: can't allocate reassembly buffer or forward compressed "
+ "fragment\n");
+ /* send abort */
+ bitmap = &_null_bitmap;
+ }
+ else if (vrbe != NULL) {
+ DEBUG("6lo sfr: packet was forwarded\n");
+ goto end;
+ }
+ else {
+ int res;
+
+ DEBUG("6lo sfr: reassembling datagram (%s, %u)\n",
+ gnrc_netif_addr_to_str(gnrc_netif_hdr_get_src_addr(netif_hdr),
+ netif_hdr->src_l2addr_len, addr_str),
+ base.tag);
+ entry->type = _RB;
+ bf_set(entry->entry.rb->received, seq);
+ if ((res = gnrc_sixlowpan_frag_rb_dispatch_when_complete(entry->entry.rb,
+ netif_hdr)) < 0) {
+ DEBUG("6lo sfr: can not dispatch datagram to upper layer\n");
+ _clean_up_rb_entry(entry);
+ /* send abort */
+ bitmap = &_null_bitmap;
+ }
+ else {
+ if (res) {
+ DEBUG("6lo sfr: dispatched datagram to upper layer\n");
+ bitmap = &_full_bitmap;
+ }
+ else if (ack_req) {
+ DEBUG("6lo sfr: ACKing received fragments %02X%02X%02X%02X "
+ "(%u of %u bytes received)\n",
+ entry->entry.rb->received[0],
+ entry->entry.rb->received[1],
+ entry->entry.rb->received[2],
+ entry->entry.rb->received[3],
+ entry->entry.base->current_size,
+ entry->entry.base->datagram_size);
+ bitmap = _to_bitmap(entry->entry.rb->received);
+ }
+ else {
+ /* no ACK was requested and no error was causing an abort ACK*/
+ DEBUG("6lo sfr: no ACK requested by received fragment %u "
+ "(bitmap so far: %02X%02X%02X%02X)\n", seq,
+ entry->entry.rb->received[0],
+ entry->entry.rb->received[1],
+ entry->entry.rb->received[2],
+ entry->entry.rb->received[3]);
+ goto end;
+ }
+ }
+ }
+ _send_ack(gnrc_netif_hdr_get_netif(netif_hdr),
+ gnrc_netif_hdr_get_src_addr(netif_hdr),
+ netif_hdr->src_l2addr_len, &base, bitmap->bf);
+end:
+ gnrc_pktbuf_release(netif_snip); /* release hold */
+}
+
+static void _forward_uncomp(gnrc_netif_hdr_t *netif_hdr, gnrc_pktsnip_t *pkt,
+ unsigned page, _generic_rb_entry_t *entry,
+ void *payload)
+{
+ sixlowpan_sfr_rfrag_t *hdr = pkt->data;
+ gnrc_sixlowpan_frag_rb_base_t vrb_base = {
+ .src_len = netif_hdr->src_l2addr_len,
+ .tag = hdr->base.tag,
+ .datagram_size = sixlowpan_sfr_rfrag_get_offset(hdr),
+ };
+ gnrc_pktsnip_t tmp = {
+ .data = payload,
+ .size = pkt->size - sizeof(sixlowpan_sfr_rfrag_t) - 1,
+ .users = 1,
+ };
+
+ switch (page) {
+#if defined(MODULE_GNRC_IPV6)
+ case 0: {
+ ipv6_hdr_t *ipv6_hdr = tmp.data;
+
+ if (ipv6_hdr->hl <= 1) {
+ DEBUG("6lo sfr: minimal hop-limit reached\n");
+ /* try to reassemble to hand to IPv6 module for error
+ * handling */
+ _try_reassembly(netif_hdr, pkt, 0, entry, page);
+ return;
+ }
+ tmp.type = GNRC_NETTYPE_IPV6;
+ break;
+ }
+#endif
+ default:
+ tmp.type = GNRC_NETTYPE_UNDEF;
+ break;
+ }
+ vrb_base.arrival = xtimer_now_usec();
+ memcpy(vrb_base.src, gnrc_netif_hdr_get_src_addr(netif_hdr),
+ vrb_base.src_len);
+ entry->entry.vrb = gnrc_sixlowpan_frag_vrb_from_route(&vrb_base,
+ NULL, &tmp);
+ if (entry->entry.vrb == NULL) {
+ DEBUG("6lo sfr: no route found or no VRB space left, "
+ "trying reassembly\n");
+ _try_reassembly(netif_hdr, pkt, 0, entry, page);
+ return;
+ }
+ /* only decrement hop-limit after check in case we reassemble */
+ switch (page) {
+#if defined(MODULE_GNRC_IPV6)
+ case 0: {
+ ipv6_hdr_t *ipv6_hdr = tmp.data;
+
+ ipv6_hdr->hl--;
+ break;
+ }
+#endif
+ default:
+ break;
+ }
+ entry->type = _VRB;
+ entry->entry.vrb->in_netif = gnrc_netif_hdr_get_netif(netif_hdr);
+ entry->entry.vrb->offset_diff = 0; /* packet is uncompressed so offset
+ * does not change */
+ _forward_rfrag(pkt, entry, sixlowpan_sfr_rfrag_get_offset(hdr), page);
+}
+
+static void _handle_1st_rfrag(gnrc_netif_hdr_t *netif_hdr, gnrc_pktsnip_t *pkt,
+ unsigned page, _generic_rb_entry_t *entry)
+{
+ sixlowpan_sfr_rfrag_t *hdr = pkt->data;
+ /* https://tools.ietf.org/html/rfc8931#section-5.1:
+ * + For a first fragment (i.e. with a Sequence of 0), this field
+ * indicates the datagram_size of the compressed datagram, [...] */
+ uint16_t datagram_size = sixlowpan_sfr_rfrag_get_offset(hdr);
+ uint8_t fragment_size = sixlowpan_sfr_rfrag_get_frag_size(hdr);
+ uint8_t *payload;
+
+ if ((datagram_size == 0) && (fragment_size == 0)) {
+ /* the received fragment is a pseudo-fragment that signals an abort
+ * condition by the fragmenting end-point, release state on the
+ * datagram */
+ bool release_pkt = true;
+
+ DEBUG("6lo sfr: Abort for datagram (%s, %u) received\n",
+ gnrc_netif_addr_to_str(gnrc_netif_hdr_get_src_addr(netif_hdr),
+ netif_hdr->src_l2addr_len, addr_str),
+ hdr->base.tag);
+ if ((entry->entry.vrb = gnrc_sixlowpan_frag_vrb_get(
+ gnrc_netif_hdr_get_src_addr(netif_hdr),
+ netif_hdr->src_l2addr_len, hdr->base.tag)) != NULL) {
+ /* we have a VRB on the aborted datagram. Release it */
+ entry->type = _VRB;
+ _forward_rfrag(pkt, entry, 0, page);
+ gnrc_sixlowpan_frag_vrb_rm(entry->entry.vrb);
+ release_pkt = false;
+ }
+ if ((entry->entry.rb = gnrc_sixlowpan_frag_rb_get_by_datagram(
+ netif_hdr, hdr->base.tag)) != NULL) {
+ /* we have a reassembly buffer entry on the aborted datagram.
+ * Release it */
+ entry->type = _RB;
+ _abort_rb(pkt, entry, netif_hdr, sixlowpan_sfr_rfrag_ack_req(hdr));
+ release_pkt = false;
+ }
+ if (release_pkt) {
+ DEBUG("6lo sfr: received abort for unknown datagram\n");
+ /* neither VRB or RB exists so we don't have any state on the
+ * aborted datagram left. Just release the abort pseudo fragment */
+ gnrc_pktbuf_release(pkt);
+ }
+ return;
+ }
+ DEBUG("6lo sfr: First fragment (%s, %u) received\n",
+ gnrc_netif_addr_to_str(gnrc_netif_hdr_get_src_addr(netif_hdr),
+ netif_hdr->src_l2addr_len, addr_str),
+ hdr->base.tag);
+
+ payload = (uint8_t *)(hdr + 1);
+ if (payload[0] == SIXLOWPAN_UNCOMP) {
+ _forward_uncomp(netif_hdr, pkt, page, entry, payload + 1);
+ }
+ else {
+ if (IS_USED(MODULE_GNRC_SIXLOWPAN_IPHC) &&
+ sixlowpan_iphc_is(payload)) {
+ _try_reassembly(netif_hdr, pkt, 0, entry, page);
+ return;
+ }
+ DEBUG("6lo sfr: unable to parse next dispatch for forwarding "
+ "information. Abort\n");
+ _abort_rb(pkt, entry, netif_hdr, false);
+ return;
+ }
+}
+
+static void _handle_nth_rfrag(gnrc_netif_hdr_t *netif_hdr, gnrc_pktsnip_t *pkt,
+ unsigned page, _generic_rb_entry_t *entry)
+{
+ sixlowpan_sfr_rfrag_t *hdr = pkt->data;
+ uint16_t offset = sixlowpan_sfr_rfrag_get_offset(hdr);
+
+ DEBUG("6lo sfr: Subsequent fragment (%s, %u) received\n",
+ gnrc_netif_addr_to_str(gnrc_netif_hdr_get_src_addr(netif_hdr),
+ netif_hdr->src_l2addr_len, addr_str),
+ hdr->base.tag);
+ if (gnrc_sixlowpan_frag_rb_exists(netif_hdr, hdr->base.tag)) {
+ DEBUG("6lo sfr: I am destination endpoint => adding to reassembly "
+ "buffer\n");
+ _try_reassembly(netif_hdr, pkt, offset, entry, page);
+ }
+ else if ((entry->entry.vrb = gnrc_sixlowpan_frag_vrb_get(
+ gnrc_netif_hdr_get_src_addr(netif_hdr),
+ netif_hdr->src_l2addr_len, hdr->base.tag)) != NULL) {
+ entry->type = _VRB;
+ entry->entry.base->arrival = xtimer_now_usec();
+ _forward_rfrag(pkt, entry, offset, page);
+ }
+ else {
+ DEBUG("6lo sfr: neither VRB nor RB found\n");
+ /* always send abort ACK:
+ * https://tools.ietf.org/html/rfc8931#section-6.1.2 */
+ _abort_rb(pkt, entry, netif_hdr, true);
+ }
+}
+
+static void _check_failed_frags(sixlowpan_sfr_ack_t *ack,
+ gnrc_sixlowpan_frag_fb_t *fbuf)
+{
+ _frag_desc_t *frag_desc;
+ clist_node_t not_received = { .next = NULL };
+
+ DEBUG("6lo sfr: checking which fragments to resend for datagram %u\n",
+ fbuf->tag);
+ for (frag_desc = (_frag_desc_t *)clist_lpop(&fbuf->sfr.window);
+ frag_desc != NULL;
+ frag_desc = (_frag_desc_t *)clist_lpop(&fbuf->sfr.window)) {
+ uint8_t seq;
+
+ seq = _frag_seq(frag_desc);
+ if (bf_isset(ack->bitmap, seq)) {
+ DEBUG("6lo sfr: fragment %u (offset: %u, frag_size: %u) "
+ "for datagram %u was received\n", seq,
+ frag_desc->offset, _frag_size(frag_desc), fbuf->tag);
+ fbuf->sfr.frags_sent--;
+ clist_rpush(&_frag_descs_free, &frag_desc->super);
+ }
+ else {
+ DEBUG("6lo sfr: fragment %u (offset: %u, frag_size: %u) "
+ "for datagram %u was not received\n", seq,
+ frag_desc->offset, _frag_size(frag_desc), fbuf->tag);
+ if ((frag_desc->retries++) < CONFIG_GNRC_SIXLOWPAN_SFR_FRAG_RETRIES) {
+ DEBUG("6lo sfr: %u retries left\n",
+ CONFIG_GNRC_SIXLOWPAN_SFR_FRAG_RETRIES -
+ (frag_desc->retries - 1));
+ /* put fragment in "not received" list */
+ clist_rpush(¬_received, &frag_desc->super);
+ frag_desc->ar_seq_fs &= ~(SIXLOWPAN_SFR_ACK_REQ << 8U);
+ }
+ else {
+ DEBUG("6lo sfr: no more retries for fragment %u\n", seq);
+ clist_rpush(&_frag_descs_free, &frag_desc->super);
+ /* retry to resend whole datagram */
+ _retry_datagram(fbuf);
+ return;
+ }
+ }
+ }
+ /* all fragments were received of the current window were received and
+ * the datagram was transmitted completely */
+ if ((clist_lpeek(¬_received) == NULL) &&
+ (fbuf->offset == fbuf->datagram_size)) {
+ /* release fragmentation buffer */
+ _clean_up_fbuf(fbuf, GNRC_NETERR_SUCCESS);
+ }
+ /* at least one fragment was not received */
+ else {
+ fbuf->sfr.window = not_received;
+ assert(fbuf->sfr.frags_sent == clist_count(&fbuf->sfr.window));
+ /* use _resend_frag here instead of loop above, so _resend_frag
+ * can know if the fragment is the last in the window by using
+ * clist_rpeek() on fbuf->sfr.window */
+ if (clist_foreach(&fbuf->sfr.window, _resend_frag, fbuf) != NULL) {
+ /* XXX: it is unlikely that allocating an abort RFRAG will be
+ * successful since the resources missing to cause the abort are
+ * still in use, but we should at least try */
+ if (_send_abort_frag(fbuf->pkt, (uint8_t)fbuf->tag, true, 0)) {
+ /* wait for ACK before fbuf is deleted */
+ _sched_abort_timeout(fbuf);
+ }
+ else {
+ /* we have no memory resources left to send neither the
+ * resent fragment nor the abort ACK to signalize that fact to
+ * the reassembling endpoint */
+ _clean_up_fbuf(fbuf, ENOMEM);
+ }
+ }
+ if ((fbuf->sfr.frags_sent < fbuf->sfr.window_size) &&
+ (fbuf->offset < fbuf->datagram_size)) {
+ DEBUG("6lo sfr: trigger send of further fragments of datagram %u\n",
+ fbuf->tag);
+ gnrc_sixlowpan_frag_fb_send(fbuf);
+ }
+ }
+}
+
+/* ====== INTERNAL FUNCTIONS USED BY PUBLIC FUNCTIONS ======
+ * ====== AND TO MANIPULATE INTERNAL DATA STRUCTURES ====== */
+static inline gnrc_sixlowpan_frag_sfr_bitmap_t *_to_bitmap(uint8_t *bitmap)
+{
+ return (gnrc_sixlowpan_frag_sfr_bitmap_t *)bitmap;
+}
+
+static inline bool _frag_ack_req(_frag_desc_t *frag)
+{
+
+ return (frag->ar_seq_fs & (SIXLOWPAN_SFR_ACK_REQ << 8U));
+}
+
+static inline uint8_t _frag_seq(_frag_desc_t *frag)
+{
+ return (frag->ar_seq_fs & (SIXLOWPAN_SFR_SEQ_MASK << 8U)) >>
+ (SIXLOWPAN_SFR_SEQ_POS + 8U);
+}
+
+static inline uint16_t _frag_size(_frag_desc_t *frag)
+{
+ return (frag->ar_seq_fs & SIXLOWPAN_SFR_FRAG_SIZE_MASK);
+}
+
+static void _clean_up_fbuf(gnrc_sixlowpan_frag_fb_t *fbuf, int error)
+{
+ DEBUG("6lo sfr: removing fragmentation buffer entry for datagram %u\n",
+ fbuf->tag);
+ _clean_slate_datagram(fbuf);
+ gnrc_pktbuf_release_error(fbuf->pkt, error);
+ fbuf->pkt = NULL;
+}
+
+static uint16_t _send_1st_fragment(gnrc_netif_t *netif,
+ gnrc_sixlowpan_frag_fb_t *fbuf,
+ unsigned page)
+{
+ gnrc_pktsnip_t *frag, *pkt = fbuf->pkt;
+ sixlowpan_sfr_rfrag_t *hdr;
+ uint8_t *data;
+ size_t comp_form_size = gnrc_pkt_len(pkt->next);
+ uint16_t frag_size = (uint16_t)netif->sixlo.max_frag_size -
+ sizeof(sixlowpan_sfr_rfrag_t);
+
+ assert((fbuf->sfr.cur_seq == 0) && (fbuf->sfr.frags_sent == 0));
+ assert(fbuf->sfr.window.next == NULL);
+ assert(comp_form_size <= UINT16_MAX);
+ /* restrict tag to value space of SFR, so that later RFRAG ACK can find
+ * it in reverse look-up */
+ fbuf->tag &= UINT8_MAX;
+ DEBUG("6lo sfr: determined frag_size = %u\n", frag_size);
+
+ /* packet was compressed */
+ if (fbuf->datagram_size > comp_form_size) {
+ /* add slack to first fragment */
+ frag_size -= (fbuf->datagram_size - comp_form_size);
+ /* use compressed form */
+ fbuf->datagram_size = (uint16_t)gnrc_pkt_len(pkt->next);
+ }
+ else {
+ /* Add uncompressed datagram dispatch to "compressed form"
+ * datagram_size */
+ fbuf->datagram_size++;
+ }
+ fbuf->sfr.arq_timeout = CONFIG_GNRC_SIXLOWPAN_SFR_OPT_ARQ_TIMEOUT_MS;
+ fbuf->sfr.window_size = CONFIG_GNRC_SIXLOWPAN_SFR_OPT_WIN_SIZE;
+
+ frag = _build_frag_from_fbuf(pkt, fbuf, frag_size);
+ if (frag == NULL) {
+ DEBUG("6lo sfr: error allocating first fragment\n");
+ return 0;
+ }
+ hdr = frag->next->data;
+ data = (uint8_t *)(hdr + 1);
+ sixlowpan_sfr_rfrag_set_offset(hdr, fbuf->datagram_size);
+ /* don't copy netif header of pkt => pkt->next */
+ frag_size = _copy_pkt_to_frag(data, pkt->next, frag_size, 0);
+
+ DEBUG("6lo sfr: send first fragment (tag: %u, X: %i, seq: %u, "
+ "frag_size: %u, datagram_size: %u)\n",
+ hdr->base.tag, sixlowpan_sfr_rfrag_ack_req(hdr),
+ sixlowpan_sfr_rfrag_get_seq(hdr),
+ sixlowpan_sfr_rfrag_get_frag_size(hdr),
+ sixlowpan_sfr_rfrag_get_offset(hdr));
+ if (!_send_fragment(frag, fbuf, page, 0)) {
+ frag_size = 0;
+ }
+ return frag_size;
+}
+
+static uint16_t _send_nth_fragment(gnrc_netif_t *netif,
+ gnrc_sixlowpan_frag_fb_t *fbuf,
+ unsigned page)
+{
+ gnrc_pktsnip_t *frag, *pkt = fbuf->pkt;
+ sixlowpan_sfr_rfrag_t *hdr;
+ uint8_t *data;
+ uint16_t frag_size = (uint16_t)netif->sixlo.max_frag_size -
+ sizeof(sixlowpan_sfr_rfrag_t);
+ uint16_t local_offset;
+
+ assert((fbuf->sfr.cur_seq > 0) &&
+ (fbuf->sfr.cur_seq <= SIXLOWPAN_SFR_SEQ_MAX));
+ assert((fbuf->sfr.frags_sent == 0) || (fbuf->sfr.window.next != NULL));
+ assert(fbuf->tag <= UINT8_MAX);
+
+ DEBUG("6lo sfr: determined frag_size = %u\n", frag_size);
+ frag = _build_frag_from_fbuf(pkt, fbuf,
+ _min(frag_size,
+ fbuf->datagram_size - fbuf->offset));
+ if (frag == NULL) {
+ DEBUG("6lo sfr: error allocating subsequent fragment\n");
+ return 0;
+ }
+ hdr = frag->next->data;
+ data = (uint8_t *)(hdr + 1);
+ sixlowpan_sfr_rfrag_set_offset(hdr, fbuf->offset);
+ pkt = pkt->next; /* don't copy netif header */
+ local_offset = _find_offset_and_copy_rest(data, &pkt, frag_size,
+ fbuf->offset);
+ /* copy remaining packet snips */
+ local_offset = _copy_pkt_to_frag(data, pkt, frag_size, local_offset);
+ DEBUG("6lo sfr: send subsequent fragment (tag: %u, X: %i, seq: %u, "
+ "frag_size: %u, offset: %u)\n",
+ hdr->base.tag, sixlowpan_sfr_rfrag_ack_req(hdr),
+ sixlowpan_sfr_rfrag_get_seq(hdr),
+ sixlowpan_sfr_rfrag_get_frag_size(hdr),
+ sixlowpan_sfr_rfrag_get_offset(hdr));
+ if (!_send_fragment(frag, fbuf, page, fbuf->offset)) {
+ local_offset = 0;
+ }
+ return local_offset;
+}
+
+static bool _send_abort_frag(gnrc_pktsnip_t *pkt, uint8_t tag, bool req_ack,
+ unsigned page)
+{
+ gnrc_pktsnip_t *frag;
+
+ frag = _build_frag_pkt(pkt->data, tag, req_ack, 0, 0);
+ if (frag != NULL) {
+ sixlowpan_sfr_rfrag_set_offset(frag->next->data, 0);
+ _send_frame(frag, NULL, page);
+ return true;
+ }
+ return false;
+}
+
+static int _resend_frag(clist_node_t *node, void *fbuf_ptr)
+{
+ _frag_desc_t *frag_desc = (_frag_desc_t *)node;
+ gnrc_sixlowpan_frag_fb_t *fbuf = fbuf_ptr;
+ gnrc_pktsnip_t *frag, *pkt = fbuf->pkt;
+ sixlowpan_sfr_rfrag_t *hdr;
+ uint8_t *data;
+ uint16_t frag_size = _frag_size(frag_desc), cur_frag_size;
+
+ frag = _build_frag_pkt(pkt->data, (uint8_t)fbuf->tag, false,
+ frag_size, 0);
+ if (frag == NULL) {
+ DEBUG("6lo sfr: error allocating fragment to resend\n");
+ return 1;
+ }
+ hdr = frag->next->data;
+ /* is last fragment in window */
+ if (((fbuf->sfr.frags_sent >= fbuf->sfr.window_size) ||
+ (fbuf->offset >= fbuf->datagram_size)) &&
+ (clist_node_t *)frag_desc == clist_rpeek(&fbuf->sfr.window)) {
+ frag_desc->ar_seq_fs |= (SIXLOWPAN_SFR_ACK_REQ << 8U);
+ _sched_arq_timeout(fbuf, fbuf->sfr.arq_timeout);
+ }
+ hdr->ar_seq_fs = byteorder_htons(frag_desc->ar_seq_fs);
+ if (frag_desc->offset > 0) {
+ sixlowpan_sfr_rfrag_set_offset(hdr, frag_desc->offset);
+ }
+ else {
+ sixlowpan_sfr_rfrag_set_offset(hdr, fbuf->datagram_size);
+ }
+
+ data = (uint8_t *)(hdr + 1);
+ pkt = pkt->next; /* don't copy netif header */
+ cur_frag_size = _find_offset_and_copy_rest(data, &pkt, frag_size,
+ frag_desc->offset);
+ /* copy remaining packet snips */
+ cur_frag_size = _copy_pkt_to_frag(data, pkt, frag_size, cur_frag_size);
+ DEBUG("6lo sfr: resending fragment (retry: %u, tag: %u, X: %i, seq: %u, "
+ "frag_size: %u, %s: %u)\n", frag_desc->retries,
+ hdr->base.tag, sixlowpan_sfr_rfrag_ack_req(hdr),
+ sixlowpan_sfr_rfrag_get_seq(hdr),
+ sixlowpan_sfr_rfrag_get_frag_size(hdr),
+ (sixlowpan_sfr_rfrag_get_seq(hdr)) ? "offset" : "datagram_size",
+ sixlowpan_sfr_rfrag_get_offset(hdr));
+ if (_send_frame(frag, NULL, 0)) {
+ frag_desc->last_sent = _last_frame_sent;
+ return 0;
+ }
+ else {
+ return 1;
+ }
+}
+
+static void _retry_datagram(gnrc_sixlowpan_frag_fb_t *fbuf)
+{
+ if ((CONFIG_GNRC_SIXLOWPAN_SFR_DG_RETRIES == 0) ||
+ (fbuf->sfr.retrans == 0)) {
+ DEBUG("6lo sfr: giving up to send datagram %u\n",
+ fbuf->tag);
+ _clean_up_fbuf(fbuf, ETIMEDOUT);
+ }
+ else {
+ DEBUG("6lo sfr: Retrying to send datagram %u completely\n", fbuf->tag);
+ fbuf->sfr.retrans--;
+ /* return fragmentation buffer to its original state to resend the whole
+ * datagram again */
+ _clean_slate_datagram(fbuf);
+ gnrc_sixlowpan_frag_sfr_send(fbuf->pkt, fbuf, 0);
+ }
+}
+
+static void _abort_rb(gnrc_pktsnip_t *pkt, _generic_rb_entry_t *entry,
+ gnrc_netif_hdr_t *netif_hdr, bool send_ack)
+{
+ sixlowpan_sfr_rfrag_t *hdr = pkt->data;
+
+ DEBUG("6lo sfr: Aborting datagram (%s, %02x)\n",
+ gnrc_netif_addr_to_str(gnrc_netif_hdr_get_src_addr(netif_hdr),
+ netif_hdr->src_l2addr_len, addr_str),
+ hdr->base.tag);
+ if (send_ack) {
+ _send_ack(gnrc_netif_hdr_get_netif(netif_hdr),
+ gnrc_netif_hdr_get_src_addr(netif_hdr),
+ netif_hdr->src_l2addr_len,
+ &hdr->base, _null_bitmap.bf);
+ }
+ _clean_up_rb_entry(entry);
+ gnrc_pktbuf_release(pkt);
+}
+
+static void _send_ack(gnrc_netif_t *netif, const uint8_t *dst, uint8_t dst_len,
+ const sixlowpan_sfr_t *hdr, const uint8_t *bitmap)
+{
+ gnrc_pktsnip_t *ack = _build_ack(netif, dst, dst_len, hdr, bitmap);
+
+ DEBUG("6lo sfr: Sending ACK for (%s, %02x): %02X%02X%02X%02X\n",
+ gnrc_netif_addr_to_str(dst, dst_len, addr_str),
+ hdr->tag, bitmap[0], bitmap[1], bitmap[2], bitmap[3]);
+ if (ack != NULL) {
+ _send_frame(ack, NULL, 0);
+ }
+ else {
+ DEBUG("6lo sfr: unable to build ACK for sending\n");
+ }
+}
+
+static void _sched_next_frame(void)
+{
+ if (CONFIG_GNRC_SIXLOWPAN_SFR_INTER_FRAME_GAP_US > 0) {
+ int state = irq_disable(); /* make timer check atomic */
+ bool already_set = (_if_gap_timer.offset ||
+ _if_gap_timer.long_offset);
+
+ irq_restore(state);
+ if (!already_set) {
+ uint32_t last_sent_since = (_last_frame_sent - xtimer_now_usec());
+
+ if (last_sent_since <= CONFIG_GNRC_SIXLOWPAN_SFR_INTER_FRAME_GAP_US) {
+ uint32_t offset = CONFIG_GNRC_SIXLOWPAN_SFR_INTER_FRAME_GAP_US -
+ last_sent_since;
+ DEBUG("6lo sfr: arming inter-frame timer in %" PRIu32 " us\n",
+ last_sent_since);
+ xtimer_set_msg(&_if_gap_timer, offset, &_if_gap_msg, _getpid());
+ }
+ else {
+ DEBUG("6lo sfr: send frame immediately\n");
+ gnrc_sixlowpan_frag_sfr_inter_frame_gap();
+ }
+ }
+ else {
+ DEBUG("6lo sfr: inter-frame timer was already set\n");
+ }
+ }
+}
+
+static void _sched_arq_timeout(gnrc_sixlowpan_frag_fb_t *fbuf, uint32_t offset)
+{
+ if (fbuf->sfr.arq_timeout_event.msg.content.ptr != NULL) {
+ DEBUG("6lo sfr: ARQ timeout for datagram %u already scheduled\n",
+ (uint8_t)fbuf->tag);
+ return;
+ }
+ DEBUG("6lo sfr: arming ACK timeout in %lums for datagram %u\n",
+ (long unsigned)offset, fbuf->tag);
+ fbuf->sfr.arq_timeout_event.event.offset = offset;
+ fbuf->sfr.arq_timeout_event.msg.content.ptr = fbuf;
+ fbuf->sfr.arq_timeout_event.msg.type = GNRC_SIXLOWPAN_FRAG_SFR_ARQ_TIMEOUT_MSG;
+ evtimer_add_msg(&_arq_timer, &fbuf->sfr.arq_timeout_event,
+ _getpid());
+}
+
+static void _sched_abort_timeout(gnrc_sixlowpan_frag_fb_t *fbuf)
+{
+ /* no fragments to wait for anymore as we aborted fragmentation and just
+ * wait for an ACK by the reassembling end point that they know. As such,
+ * clean-out the fragmentation buffer. */
+ _clean_slate_datagram(fbuf);
+ fbuf->sfr.retrans = 0;
+ _sched_arq_timeout(fbuf, fbuf->sfr.arq_timeout);
+}
+
+static void _handle_rfrag(gnrc_netif_hdr_t *netif_hdr, gnrc_pktsnip_t *pkt,
+ unsigned page)
+{
+ _generic_rb_entry_t entry = { .type = _UNDEF };
+
+ if (sixlowpan_sfr_rfrag_get_seq(pkt->data) == 0U) {
+ _handle_1st_rfrag(netif_hdr, pkt, page, &entry);
+ }
+ else {
+ _handle_nth_rfrag(netif_hdr, pkt, page, &entry);
+ }
+}
+
+static void _handle_ack(gnrc_netif_hdr_t *netif_hdr, gnrc_pktsnip_t *pkt,
+ unsigned page)
+{
+ gnrc_sixlowpan_frag_vrb_t *vrbe;
+ sixlowpan_sfr_ack_t *hdr = pkt->data;
+
+ (void)page;
+ DEBUG("6lo sfr: received ACK for datagram (%s, %02x): %02X%02X%02X%02X\n",
+ gnrc_netif_addr_to_str(gnrc_netif_hdr_get_src_addr(netif_hdr),
+ netif_hdr->src_l2addr_len,
+ addr_str), hdr->base.tag,
+ hdr->bitmap[0], hdr->bitmap[1], hdr->bitmap[2], hdr->bitmap[3]);
+ if ((vrbe = gnrc_sixlowpan_frag_vrb_reverse(
+ gnrc_netif_hdr_get_netif(netif_hdr),
+ gnrc_netif_hdr_get_src_addr(netif_hdr),
+ netif_hdr->src_l2addr_len, hdr->base.tag)) != NULL) {
+ /* we found a VRB entry by reverse lookup, forward ACK further down. */
+ sixlowpan_sfr_t mock_base = { .disp_ecn = hdr->base.disp_ecn,
+ .tag = vrbe->super.tag };
+ DEBUG("6lo sfr: forward ACK to (%s, %02x)\n",
+ gnrc_netif_addr_to_str(vrbe->super.src, vrbe->super.src_len,
+ addr_str), vrbe->super.tag);
+ _send_ack(vrbe->in_netif, vrbe->super.src, vrbe->super.src_len,
+ &mock_base, hdr->bitmap);
+ if ((unaligned_get_u32(hdr->bitmap) == _full_bitmap.u32) ||
+ (unaligned_get_u32(hdr->bitmap) == _null_bitmap.u32)) {
+ if (CONFIG_GNRC_SIXLOWPAN_FRAG_RBUF_DEL_TIMER > 0) {
+ /* garbage-collect entry after CONFIG_GNRC_SIXLOWPAN_FRAG_RBUF_DEL_TIMER
+ * microseconds */
+ vrbe->super.arrival = xtimer_now_usec() -
+ (CONFIG_GNRC_SIXLOWPAN_FRAG_VRB_TIMEOUT_US -
+ CONFIG_GNRC_SIXLOWPAN_FRAG_RBUF_DEL_TIMER);
+ }
+ else {
+ gnrc_sixlowpan_frag_vrb_rm(vrbe);
+ }
+ }
+ else {
+ vrbe->super.arrival = xtimer_now_usec();
+ }
+ }
+ else {
+ gnrc_sixlowpan_frag_fb_t *fbuf;
+
+ if ((fbuf = gnrc_sixlowpan_frag_fb_get_by_tag(hdr->base.tag)) != NULL) {
+ /* ACK for pending ACK timeout received. removing ACK timeout */
+ DEBUG("6lo sfr: cancelling ARQ timeout\n");
+ evtimer_del((evtimer_t *)(&_arq_timer),
+ &fbuf->sfr.arq_timeout_event.event);
+ fbuf->sfr.arq_timeout_event.msg.content.ptr = NULL;
+ if ((unaligned_get_u32(hdr->bitmap) == _null_bitmap.u32)) {
+ /* ACK indicates the reassembling endpoint canceled reassembly
+ */
+ DEBUG("6lo sfr: fragmentation canceled\n");
+ /* Retry to send whole datagram if configured, otherwise
+ * cancel fragmentation */
+ _retry_datagram(fbuf);
+ }
+ else {
+ /* Check and resent failed fragments within the current window
+ */
+ _check_failed_frags(hdr, fbuf);
+ }
+ }
+ else {
+ DEBUG("6lo sfr: no VRB or fragmentation buffer found\n");
+ }
+ }
+ gnrc_pktbuf_release(pkt);
+}
+
+static int _forward_rfrag(gnrc_pktsnip_t *pkt, _generic_rb_entry_t *entry,
+ uint16_t offset, unsigned page)
+{
+ gnrc_pktsnip_t *old, *new = gnrc_netif_hdr_build(
+ NULL, 0,
+ entry->entry.base->dst, entry->entry.base->dst_len
+ );
+ sixlowpan_sfr_rfrag_t *hdr;
+
+ assert(entry->type == _VRB);
+ /* restrict out_tag to value space of SFR, so that later RFRAG ACK can find
+ * it in reverse look-up */
+ entry->entry.vrb->out_tag &= UINT8_MAX;
+ DEBUG("6lo sfr: Forwarding to (%s, %u)\n",
+ gnrc_netif_addr_to_str(entry->entry.base->dst,
+ entry->entry.base->dst_len, addr_str),
+ entry->entry.vrb->out_tag);
+ if (new == NULL) {
+ DEBUG("6lo sfr: Unable to forward fragment, "
+ "packet buffer full\n");
+ gnrc_pktbuf_release(pkt);
+ return -ENOMEM;
+ }
+
+ hdr = pkt->data;
+ old = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_NETIF);
+ if (old != NULL) {
+ /* remove original netif header */
+ gnrc_pktbuf_remove_snip(pkt, old);
+ }
+ if (offset > 0) {
+ offset += entry->entry.vrb->offset_diff;
+ }
+ sixlowpan_sfr_rfrag_set_offset(hdr, offset);
+ hdr->base.tag = entry->entry.vrb->out_tag;
+ gnrc_netif_hdr_set_netif(new->data, entry->entry.vrb->out_netif);
+ new->next = pkt;
+ _send_frame(new, NULL, page);
+ return 0;
+}
+
+/** @} */
diff --git a/sys/net/gnrc/network_layer/sixlowpan/gnrc_sixlowpan.c b/sys/net/gnrc/network_layer/sixlowpan/gnrc_sixlowpan.c
index 7b2e69b18b42..1592bf9a4c42 100644
--- a/sys/net/gnrc/network_layer/sixlowpan/gnrc_sixlowpan.c
+++ b/sys/net/gnrc/network_layer/sixlowpan/gnrc_sixlowpan.c
@@ -23,6 +23,9 @@
#include "net/gnrc/sixlowpan.h"
#include "net/gnrc/sixlowpan/frag.h"
#include "net/gnrc/sixlowpan/frag/rb.h"
+#ifdef MODULE_GNRC_SIXLOWPAN_FRAG_SFR
+#include "net/gnrc/sixlowpan/frag/sfr.h"
+#endif /* MODULE_GNRC_SIXLOWPAN_FRAG_SFR */
#include "net/gnrc/sixlowpan/iphc.h"
#include "net/gnrc/netif.h"
#include "net/sixlowpan.h"
@@ -114,11 +117,16 @@ void gnrc_sixlowpan_multiplex_by_size(gnrc_pktsnip_t *pkt,
DEBUG("6lo: Dispatch for sending\n");
gnrc_sixlowpan_dispatch_send(pkt, NULL, page);
}
-#ifdef MODULE_GNRC_SIXLOWPAN_FRAG
+#if defined(MODULE_GNRC_SIXLOWPAN_FRAG) || defined(MODULE_GNRC_SIXLOWPAN_FRAG_SFR)
else if (orig_datagram_size <= SIXLOWPAN_FRAG_MAX_LEN) {
DEBUG("6lo: Send fragmented (%u > %u)\n",
(unsigned int)datagram_size, netif->sixlo.max_frag_size);
gnrc_sixlowpan_frag_fb_t *fbuf;
+#ifdef MODULE_GNRC_SIXLOWPAN_FRAG_SFR
+ bool sfr = gnrc_sixlowpan_frag_sfr_netif(netif);
+#else /* MODULE_GNRC_SIXLOWPAN_FRAG_SFR */
+ bool sfr = false;
+#endif /* MODULE_GNRC_SIXLOWPAN_FRAG_SFR */
fbuf = gnrc_sixlowpan_frag_fb_get();
if (fbuf == NULL) {
@@ -136,9 +144,20 @@ void gnrc_sixlowpan_multiplex_by_size(gnrc_pktsnip_t *pkt,
fbuf->hint.fragsz = 0;
#endif
- gnrc_sixlowpan_frag_send(pkt, fbuf, page);
- }
+ if (!sfr) {
+#ifdef MODULE_GNRC_SIXLOWPAN_FRAG
+ gnrc_sixlowpan_frag_send(pkt, fbuf, page);
#endif
+ }
+#ifdef MODULE_GNRC_SIXLOWPAN_FRAG_SFR
+ else {
+ fbuf->sfr.cur_seq = 0U;
+ fbuf->sfr.frags_sent = 0U;
+ gnrc_sixlowpan_frag_sfr_send(pkt, fbuf, page);
+ }
+#endif /* MODULE_GNRC_SIXLOWPAN_FRAG_SFR */
+ }
+#endif /* defined(MODULE_GNRC_SIXLOWPAN_FRAG) || defined(MODULE_GNRC_SIXLOWPAN_FRAG_SFR) */
else {
(void)orig_datagram_size;
DEBUG("6lo: packet too big (%u > %u)\n",
@@ -216,6 +235,13 @@ static void _receive(gnrc_pktsnip_t *pkt)
return;
}
#endif
+#ifdef MODULE_GNRC_SIXLOWPAN_FRAG_SFR
+ else if (sixlowpan_sfr_is((sixlowpan_sfr_t *)dispatch)) {
+ DEBUG("6lo: received 6LoWPAN recoverable fragment\n");
+ gnrc_sixlowpan_frag_sfr_recv(pkt, NULL, 0);
+ return;
+ }
+#endif /* MODULE_GNRC_SIXLOWPAN_FRAG_SFR */
#ifdef MODULE_GNRC_SIXLOWPAN_IPHC
else if (sixlowpan_iphc_is(dispatch)) {
DEBUG("6lo: received 6LoWPAN IPHC compressed datagram\n");
@@ -331,6 +357,32 @@ static void _send(gnrc_pktsnip_t *pkt)
gnrc_sixlowpan_multiplex_by_size(pkt, datagram_size, netif, 0);
}
+#ifdef MODULE_GNRC_SIXLOWPAN_FRAG_FB
+static void _continue_fragmenting(gnrc_sixlowpan_frag_fb_t *fbuf)
+{
+#ifdef MODULE_GNRC_SIXLOWPAN_FRAG_SFR
+ if (fbuf->pkt == NULL) {
+ /* In case the timer fired before the entry was removed */
+ return;
+ }
+
+ gnrc_netif_t *netif = gnrc_netif_hdr_get_netif(fbuf->pkt->data);
+ assert(netif != NULL);
+ if (gnrc_sixlowpan_frag_sfr_netif(netif)) {
+ gnrc_sixlowpan_frag_sfr_send(NULL, fbuf, 0);
+ return;
+ }
+#endif /* MODULE_GNRC_SIXLOWPAN_FRAG_SFR */
+#ifdef MODULE_GNRC_SIXLOWPAN_FRAG
+ gnrc_sixlowpan_frag_send(NULL, fbuf, 0);
+#else /* MODULE_GNRC_SIXLOWPAN_FRAG */
+ (void)fbuf;
+ DEBUG("6lo: No fragmentation implementation available to sent\n");
+ assert(false);
+#endif /* MODULE_GNRC_SIXLOWPAN_FRAG */
+}
+#endif /* MODULE_GNRC_SIXLOWPAN_FRAG_FB */
+
static void *_event_loop(void *args)
{
msg_t msg, reply, msg_q[GNRC_SIXLOWPAN_MSG_QUEUE_SIZE];
@@ -346,6 +398,10 @@ static void *_event_loop(void *args)
/* preinitialize ACK */
reply.type = GNRC_NETAPI_MSG_TYPE_ACK;
+#ifdef MODULE_GNRC_SIXLOWPAN_FRAG_SFR
+ gnrc_sixlowpan_frag_sfr_init();
+#endif
+
/* start event loop */
while (1) {
DEBUG("6lo: waiting for incoming message.\n");
@@ -371,20 +427,25 @@ static void *_event_loop(void *args)
#ifdef MODULE_GNRC_SIXLOWPAN_FRAG_FB
case GNRC_SIXLOWPAN_FRAG_FB_SND_MSG:
DEBUG("6lo: send fragmented event received\n");
-#ifdef MODULE_GNRC_SIXLOWPAN_FRAG
- gnrc_sixlowpan_frag_send(NULL, msg.content.ptr, 0);
-#else /* MODULE_GNRC_SIXLOWPAN_FRAG_FB */
- DEBUG("6lo: No fragmentation implementation available to sent\n");
- assert(false);
-#endif /* MODULE_GNRC_SIXLOWPAN_FRAG_FB */
+ _continue_fragmenting(msg.content.ptr);
break;
-#endif
+#endif /* MODULE_GNRC_SIXLOWPAN_FRAG_FB */
#ifdef MODULE_GNRC_SIXLOWPAN_FRAG_RB
case GNRC_SIXLOWPAN_FRAG_RB_GC_MSG:
DEBUG("6lo: garbage collect reassembly buffer event received\n");
gnrc_sixlowpan_frag_rb_gc();
break;
#endif
+#ifdef MODULE_GNRC_SIXLOWPAN_FRAG_SFR
+ case GNRC_SIXLOWPAN_FRAG_SFR_ARQ_TIMEOUT_MSG:
+ DEBUG("6lo sfr: ARQ timeout received\n");
+ gnrc_sixlowpan_frag_sfr_arq_timeout(msg.content.ptr);
+ break;
+ case GNRC_SIXLOWPAN_FRAG_SFR_INTER_FRAG_GAP_MSG:
+ DEBUG("6lo sfr: sending next scheduled frame\n");
+ gnrc_sixlowpan_frag_sfr_inter_frame_gap();
+ break;
+#endif
default:
DEBUG("6lo: operation not supported\n");
diff --git a/sys/net/gnrc/network_layer/sixlowpan/iphc/gnrc_sixlowpan_iphc.c b/sys/net/gnrc/network_layer/sixlowpan/iphc/gnrc_sixlowpan_iphc.c
index 159007e58cee..6545217ab5e9 100644
--- a/sys/net/gnrc/network_layer/sixlowpan/iphc/gnrc_sixlowpan_iphc.c
+++ b/sys/net/gnrc/network_layer/sixlowpan/iphc/gnrc_sixlowpan_iphc.c
@@ -27,6 +27,9 @@
#include "net/gnrc/sixlowpan/ctx.h"
#include "net/gnrc/sixlowpan/frag/rb.h"
#include "net/gnrc/sixlowpan/frag/minfwd.h"
+#ifdef MODULE_GNRC_SIXLOWPAN_FRAG_SFR
+#include "net/gnrc/sixlowpan/frag/sfr.h"
+#endif /* MODULE_GNRC_SIXLOWPAN_FRAG_SFR */
#ifdef MODULE_GNRC_SIXLOWPAN_FRAG_VRB
#include "net/gnrc/sixlowpan/frag/vrb.h"
#endif /* MODULE_GNRC_SIXLOWPAN_FRAG_VRB */
@@ -116,6 +119,18 @@
static char addr_str[IPV6_ADDR_MAX_STR_LEN];
#endif /* MODULE_GNRC_SIXLOWPAN_FRAG_VRB */
+static inline bool _is_rfrag(gnrc_pktsnip_t *sixlo)
+{
+#ifdef MODULE_GNRC_SIXLOWPAN_FRAG_SFR
+ assert((sixlo->next != NULL) &&
+ (sixlo->next->type == GNRC_NETTYPE_SIXLOWPAN));
+ return sixlowpan_sfr_rfrag_is(sixlo->next->data);
+#else /* MODULE_GNRC_SIXLOWPAN_FRAG_SFR */
+ (void)sixlo;
+ return false;
+#endif /* MODULE_GNRC_SIXLOWPAN_FRAG_SFR */
+}
+
static inline bool _context_overlaps_iid(gnrc_sixlowpan_ctx_t *ctx,
ipv6_addr_t *addr,
eui64_t *iid)
@@ -562,8 +577,14 @@ static size_t _iphc_nhc_ipv6_decode(gnrc_pktsnip_t *sixlo, size_t offset,
/* might be needed to be overwritten by IPv6 reassembly after the IPv6
* packet was reassembled to get complete length */
if (rbuf != NULL) {
- payload_len = rbuf->super.datagram_size - *uncomp_hdr_len-
- sizeof(ipv6_hdr_t);
+ if (_is_rfrag(sixlo)) {
+ payload_len = (rbuf->super.datagram_size + *uncomp_hdr_len) -
+ (sizeof(ipv6_hdr_t) - offset);
+ }
+ else {
+ payload_len = rbuf->super.datagram_size - *uncomp_hdr_len -
+ sizeof(ipv6_hdr_t);
+ }
}
else {
payload_len = (sixlo->size + *uncomp_hdr_len) -
@@ -669,7 +690,13 @@ static size_t _iphc_nhc_udp_decode(gnrc_pktsnip_t *sixlo, size_t offset,
/* might be needed to be overwritten by IPv6 reassembly after the IPv6
* packet was reassembled to get complete length */
if (rbuf != NULL) {
- payload_len = rbuf->super.datagram_size - *uncomp_hdr_len;
+ if (_is_rfrag(sixlo)) {
+ payload_len = rbuf->super.datagram_size + sizeof(udp_hdr_t) -
+ offset;
+ }
+ else {
+ payload_len = rbuf->super.datagram_size - *uncomp_hdr_len;
+ }
}
else {
payload_len = sixlo->size + sizeof(udp_hdr_t) - offset;
@@ -781,7 +808,29 @@ void gnrc_sixlowpan_iphc_recv(gnrc_pktsnip_t *sixlo, void *rbuf_ptr,
uint16_t payload_len;
if (rbuf != NULL) {
/* for a fragmented datagram we know the overall length already */
- payload_len = (uint16_t)(rbuf->super.datagram_size - sizeof(ipv6_hdr_t));
+ if (_is_rfrag(sixlo)) {
+ DEBUG("6lo iphc: calculating payload length for SFR\n");
+ DEBUG(" - rbuf->super.datagram_size: %u\n",
+ rbuf->super.datagram_size);
+ DEBUG(" - payload_offset: %u\n", (unsigned)payload_offset);
+ DEBUG(" - uncomp_hdr_len: %u\n", (unsigned)uncomp_hdr_len);
+ /* set IPv6 header payload length field to the length of whatever is
+ * left after removing the 6LoWPAN header and adding uncompressed
+ * headers */
+ payload_len = (rbuf->super.datagram_size - payload_offset) +
+ (uncomp_hdr_len - sizeof(ipv6_hdr_t));
+ DEBUG(" => %u\n", payload_len);
+ /* adapt datagram size for uncompressed datagram */
+#ifdef MODULE_GNRC_SIXLOWPAN_FRAG_SFR
+ /* guard required because SFR-specific field of vrbe is accessed */
+ rbuf->offset_diff += (uncomp_hdr_len - payload_offset);
+ rbuf->super.datagram_size += rbuf->offset_diff;
+#endif /* MODULE_GNRC_SIXLOWPAN_FRAG_VRB */
+ }
+ else {
+ /* for a fragmented datagram we know the overall length already */
+ payload_len = (uint16_t)(rbuf->super.datagram_size - sizeof(ipv6_hdr_t));
+ }
#ifdef MODULE_GNRC_SIXLOWPAN_FRAG_VRB
DEBUG("6lo iphc: VRB present, trying to create entry for dst %s\n",
ipv6_addr_to_str(addr_str, &ipv6_hdr->dst, sizeof(addr_str)));
@@ -845,6 +894,16 @@ void gnrc_sixlowpan_iphc_recv(gnrc_pktsnip_t *sixlo, void *rbuf_ptr,
ipv6_hdr->hl--;
vrbe->super.current_size = rbuf->super.current_size;
if ((ipv6 = _encode_frag_for_forwarding(ipv6, vrbe))) {
+#ifdef MODULE_GNRC_SIXLOWPAN_FRAG_SFR
+ /* guard required because SFR-specific field of vrbe is
+ * accessed */
+ if (_is_rfrag(sixlo)) {
+ vrbe->in_netif = iface;
+ /* calculate offset difference due to compression */
+ vrbe->offset_diff = ((int)gnrc_pkt_len(ipv6->next)) -
+ sixlo->size;
+ }
+#endif /* MODULE_GNRC_SIXLOWPAN_FRAG_SFR */
if ((res = _forward_frag(ipv6, sixlo->next, vrbe, page)) == 0) {
DEBUG("6lo iphc: successfully recompressed and forwarded "
"1st fragment\n");
@@ -930,6 +989,11 @@ static int _forward_frag(gnrc_pktsnip_t *pkt, gnrc_pktsnip_t *frag_hdr,
}
/* the following is just debug output for testing without any forwarding
* scheme */
+#ifdef MODULE_GNRC_SIXLOWPAN_FRAG_SFR
+ if (sixlowpan_sfr_rfrag_is(frag_hdr->data)) {
+ return gnrc_sixlowpan_frag_sfr_forward(pkt, frag_hdr->data, vrbe, page);
+ }
+#endif /* MODULE_GNRC_SIXLOWPAN_FRAG_SFR */
DEBUG("6lo iphc: Do not know how to forward fragment from (%s, %u) ",
gnrc_netif_addr_to_str(vrbe->super.src, vrbe->super.src_len,
addr_str), vrbe->super.tag);
From ed4ac70887f9e6db570db573f4cb1299bc59f5f0 Mon Sep 17 00:00:00 2001
From: "Martine S. Lenders"
Date: Fri, 15 Nov 2019 18:12:58 +0100
Subject: [PATCH 09/10] tests: provide tests for gnrc_sixlowpan_frag_sfr
---
tests/gnrc_sixlowpan_frag_sfr/Makefile | 36 +
tests/gnrc_sixlowpan_frag_sfr/Makefile.ci | 42 +
tests/gnrc_sixlowpan_frag_sfr/app.config | 10 +
tests/gnrc_sixlowpan_frag_sfr/common.h | 50 +
tests/gnrc_sixlowpan_frag_sfr/main.c | 2836 +++++++++++++++++
tests/gnrc_sixlowpan_frag_sfr/mockup_netif.c | 123 +
tests/gnrc_sixlowpan_frag_sfr/tests/01-run.py | 19 +
7 files changed, 3116 insertions(+)
create mode 100644 tests/gnrc_sixlowpan_frag_sfr/Makefile
create mode 100644 tests/gnrc_sixlowpan_frag_sfr/Makefile.ci
create mode 100644 tests/gnrc_sixlowpan_frag_sfr/app.config
create mode 100644 tests/gnrc_sixlowpan_frag_sfr/common.h
create mode 100644 tests/gnrc_sixlowpan_frag_sfr/main.c
create mode 100644 tests/gnrc_sixlowpan_frag_sfr/mockup_netif.c
create mode 100755 tests/gnrc_sixlowpan_frag_sfr/tests/01-run.py
diff --git a/tests/gnrc_sixlowpan_frag_sfr/Makefile b/tests/gnrc_sixlowpan_frag_sfr/Makefile
new file mode 100644
index 000000000000..7302622270de
--- /dev/null
+++ b/tests/gnrc_sixlowpan_frag_sfr/Makefile
@@ -0,0 +1,36 @@
+include ../Makefile.tests_common
+
+USEMODULE += gnrc_ipv6_router_default
+USEMODULE += gnrc_sixlowpan_frag_sfr
+USEMODULE += gnrc_sixlowpan_iphc
+USEMODULE += gnrc_ipv6_nib
+USEMODULE += gnrc_netif
+USEMODULE += embunit
+USEMODULE += netdev_ieee802154
+USEMODULE += netdev_test
+
+CFLAGS += -DTEST_SUITES
+
+include $(RIOTBASE)/Makefile.include
+
+ifndef CONFIG_GNRC_IPV6_NIB_NO_RTR_SOL
+ # disable router solicitations so they don't interfere with the tests
+ CFLAGS += -DCONFIG_GNRC_IPV6_NIB_NO_RTR_SOL=1
+endif
+# SFR parameters
+ifndef CONFIG_GNRC_SIXLOWPAN_SFR_OPT_WIN_SIZE
+ # fix window size
+ CFLAGS += -DCONFIG_GNRC_SIXLOWPAN_SFR_OPT_WIN_SIZE=3U
+endif
+ifndef CONFIG_GNRC_SIXLOWPAN_SFR_INTER_FRAME_GAP_US
+ # decrease inter frame gap
+ CFLAGS += -DCONFIG_GNRC_SIXLOWPAN_SFR_INTER_FRAME_GAP_US=5U
+endif
+ifndef CONFIG_GNRC_SIXLOWPAN_SFR_MIN_ARQ_TIMEOUT_MS
+ # decrease minimal ARQ timeout
+ CFLAGS += -DCONFIG_GNRC_SIXLOWPAN_SFR_MIN_ARQ_TIMEOUT_MS=100U
+endif
+ifndef CONFIG_GNRC_SIXLOWPAN_SFR_OPT_ARQ_TIMEOUT_MS
+ # decrease initial ARQ timeout
+ CFLAGS += -DCONFIG_GNRC_SIXLOWPAN_SFR_OPT_ARQ_TIMEOUT_MS=100U
+endif
diff --git a/tests/gnrc_sixlowpan_frag_sfr/Makefile.ci b/tests/gnrc_sixlowpan_frag_sfr/Makefile.ci
new file mode 100644
index 000000000000..dad20b146896
--- /dev/null
+++ b/tests/gnrc_sixlowpan_frag_sfr/Makefile.ci
@@ -0,0 +1,42 @@
+BOARD_INSUFFICIENT_MEMORY := \
+ arduino-duemilanove \
+ arduino-leonardo \
+ arduino-mega2560 \
+ arduino-nano \
+ arduino-uno \
+ atmega1284p \
+ atmega328p \
+ blackpill \
+ bluepill \
+ derfmega128 \
+ hifive1 \
+ hifive1b \
+ i-nucleo-lrwan1 \
+ im880b \
+ mega-xplained \
+ microduino-corerf \
+ msb-430 \
+ msb-430h \
+ nucleo-f030r8 \
+ nucleo-f031k6 \
+ nucleo-f042k6 \
+ nucleo-f070rb \
+ nucleo-f072rb \
+ nucleo-f302r8 \
+ nucleo-f303k8 \
+ nucleo-f334r8 \
+ nucleo-l011k4 \
+ nucleo-l031k6 \
+ nucleo-l053r8 \
+ samd10-xmini \
+ saml10-xpro \
+ saml11-xpro \
+ stk3200 \
+ stm32f030f4-demo \
+ stm32f0discovery \
+ stm32l0538-disco \
+ stm32mp157c-dk2 \
+ telosb \
+ waspmote-pro \
+ z1 \
+ #
diff --git a/tests/gnrc_sixlowpan_frag_sfr/app.config b/tests/gnrc_sixlowpan_frag_sfr/app.config
new file mode 100644
index 000000000000..1075ffab37d5
--- /dev/null
+++ b/tests/gnrc_sixlowpan_frag_sfr/app.config
@@ -0,0 +1,10 @@
+CONFIG_KCONFIG_USEMODULE_GNRC_IPV6_NIB=y
+CONFIG_KCONFIG_USEMODULE_GNRC_SIXLOWPAN=y
+CONFIG_KCONFIG_USEMODULE_GNRC_SIXLOWPAN_FRAG_SFR=y
+# disable router solicitations so they don't interfere with the tests
+CONFIG_GNRC_IPV6_NIB_NO_RTR_SOL=y
+# preconfigure SFR for tests
+CONFIG_GNRC_SIXLOWPAN_SFR_OPT_WIN_SIZE=3
+CONFIG_GNRC_SIXLOWPAN_SFR_INTER_FRAME_GAP_US=5
+CONFIG_GNRC_SIXLOWPAN_SFR_MIN_ARQ_TIMEOUT_MS=100
+CONFIG_GNRC_SIXLOWPAN_SFR_OPT_ARQ_TIMEOUT_MS=100
diff --git a/tests/gnrc_sixlowpan_frag_sfr/common.h b/tests/gnrc_sixlowpan_frag_sfr/common.h
new file mode 100644
index 000000000000..1c6bd3031bb7
--- /dev/null
+++ b/tests/gnrc_sixlowpan_frag_sfr/common.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2017 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 tests_gnrc_ipv6_nib Common header for GNRC's NIB tests
+ * @ingroup tests
+ * @brief Common definitions for GNRC's NIB tests
+ * @{
+ *
+ * @file
+ *
+ * @author Martine Lenders
+ */
+#ifndef COMMON_H
+#define COMMON_H
+
+#include
+
+#include "net/gnrc.h"
+#include "net/gnrc/netif.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define _LL0 (0xb8)
+#define _LL1 (0x8c)
+#define _LL2 (0xcc)
+#define _LL3 (0xba)
+#define _LL4 (0xef)
+#define _LL5 (0x9a)
+#define _LL6 (0x67)
+#define _LL7 (0x42)
+
+extern gnrc_netif_t *_mock_netif;
+
+void _tests_init(void);
+void _common_set_up(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* COMMON_H */
+/** @} */
diff --git a/tests/gnrc_sixlowpan_frag_sfr/main.c b/tests/gnrc_sixlowpan_frag_sfr/main.c
new file mode 100644
index 000000000000..8694fab11dde
--- /dev/null
+++ b/tests/gnrc_sixlowpan_frag_sfr/main.c
@@ -0,0 +1,2836 @@
+/*
+ * 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.
+ */
+
+/**
+ * @ingroup tests
+ * @{
+ *
+ * @file
+ * @brief Tests 6LoWPAN minimal forwarding
+ *
+ * @author Martine Lenders
+ *
+ * @}
+ */
+
+#include
+#include
+
+#include "cib.h"
+#include "common.h"
+#include "embUnit.h"
+#include "embUnit/embUnit.h"
+#include "mutex.h"
+#include "net/ipv6.h"
+#include "net/gnrc.h"
+#include "net/gnrc/ipv6/nib.h"
+#include "net/gnrc/ipv6/nib/nc.h"
+#include "net/gnrc/ipv6/nib/ft.h"
+#include "net/gnrc/sixlowpan/frag.h"
+#include "net/gnrc/sixlowpan/frag/rb.h"
+#include "net/gnrc/sixlowpan/frag/sfr.h"
+#include "net/gnrc/sixlowpan/iphc.h"
+#include "net/netdev_test.h"
+#ifdef MODULE_OD
+/* for debugging _target_buf */
+#include "od.h"
+#endif
+#include "utlist.h"
+#include "xtimer.h"
+
+#define SEND_PACKET_TIMEOUT (500U)
+
+#define LOC_L2 { _LL0, _LL1, _LL2, _LL3, _LL4, _LL5, _LL6, _LL7 }
+#define LOC_LL { 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ _LL0 ^ 0x2, _LL1, _LL2, _LL3, _LL4, _LL5, _LL6, _LL7 \
+ }
+#define LOC_GB { 0x20, 0x01, 0x0d, 0xb8, 0xd3, 0x35, 0x91, 0x7e, \
+ _LL0 ^ 0x2, _LL1, _LL2, _LL3, _LL4, _LL5, _LL6, _LL7 \
+ }
+#define REM_L2 { _LL0, _LL1, _LL2, _LL3, _LL4, _LL5, _LL6, _LL7 + 1 }
+#define REM_LL { 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ _LL0 ^ 0x2, _LL1, _LL2, _LL3, _LL4, _LL5, _LL6, _LL7 + 1 \
+ }
+#define REM_GB { 0x20, 0x01, 0x0d, 0xb8, 0xd3, 0x35, 0x91, 0x7e, \
+ _LL0 ^ 0x2, _LL1, _LL2, _LL3, _LL4, _LL5, _LL6, _LL7 + 1 \
+ }
+
+#define LOC_GB_PFX_LEN (64U)
+#define REM_GB_PFX_LEN (64U)
+#define TEST_1ST_FRAG_UNCOMP_PAYLOAD_POS (6U)
+#define TEST_1ST_FRAG_UNCOMP_IPV6_HDR_POS (7U)
+#define TEST_1ST_FRAG_UNCOMP_IPV6_PAYLOAD_SIZE (40U)
+#define TEST_1ST_FRAG_UNCOMP_IPV6_PAYLOAD_POS (47U)
+#define TEST_1ST_FRAG_UNCOMP_UDP_PAYLOAD_SIZE (32U)
+#define TEST_1ST_FRAG_UNCOMP_UDP_PAYLOAD_POS (55U)
+#define TEST_1ST_FRAG_COMP_FRAG_SIZE (80U)
+#define TEST_1ST_FRAG_COMP_ONLY_IPHC_FRAG_SIZE (48U)
+#define TEST_1ST_FRAG_COMP_SIZE (38U)
+#define TEST_1ST_FRAG_COMP_PAYLOAD_POS (6U)
+#define TEST_1ST_FRAG_COMP_UDP_PAYLOAD_POS (44U)
+#define TEST_1ST_FRAG_COMP_PREV_HOP_UDP_PAYLOAD_POS (45U)
+#define TEST_NTH_FRAG_SIZE (32U)
+#define TEST_NTH_FRAG_PAYLOAD_POS (6U)
+#define TEST_FRAG_TAG (0xADU)
+#define TEST_SEND_COMP_DATAGRAM_SIZE (193U)
+#define TEST_SEND_DATAGRAM_TAG (0x25U)
+#define TEST_SEND_FRAG1_PAYLOAD_POS (6U)
+#define TEST_SEND_FRAG1_ICMPV6_PAYLOAD_POS (41U)
+#define TEST_SEND_FRAG1_PAYLOAD_SIZE (35U)
+#define TEST_OFFSET_DIFF (6U)
+
+static const uint8_t _test_1st_frag_uncomp[] = {
+ 0xe8, /* RFRAG | no ECN */
+ TEST_FRAG_TAG, /* tag: TEST_FRAG_TAG */
+ 0x00, 0x51, /* no ACK REQ | sequence: 0 | fragment_size: 81 */
+ 0x04, 0xd1, /* compressed datagram size: 1233 */
+ 0x41, /* uncompressed IPv6 */
+ /* IPv6 header: payload length = 1192,
+ * next header = UDP (17), hop limit = 65 */
+ 0x60, 0x00, 0x00, 0x00, 0x04, 0xa8, 0x11, 0x41,
+ /* Source: 2001:db8:d6c3:acf:dc71:2b85:82f:75fb */
+ 0x20, 0x01, 0x0d, 0xb8, 0xd6, 0xc3, 0x0a, 0xcf,
+ 0xdc, 0x71, 0x2b, 0x85, 0x08, 0x2f, 0x75, 0xfb,
+ /* Destination: REM_GB */
+ 0x20, 0x01, 0x0d, 0xb8, 0xd3, 0x35, 0x91, 0x7e,
+ _LL0 ^ 0x2, _LL1, _LL2, _LL3, _LL4, _LL5, _LL6, _LL7 + 1,
+ /* UDP source: 0xf0b4, UDP destination: 0xf0ba,
+ * length: 1192, (random) checksum: 0x47b8 */
+ 0xf0, 0xb4, 0xf0, 0xba, 0x04, 0xa8, 0x47, 0xb8,
+ /* (random) payload of length 32 */
+ 0xba, 0xb3, 0x6e, 0x4f, 0xd8, 0x23, 0x40, 0xf3,
+ 0xfb, 0xb9, 0x05, 0xbf, 0xbe, 0x19, 0xf6, 0xa2,
+ 0xc7, 0x6e, 0x09, 0xf9, 0xba, 0x70, 0x3a, 0x38,
+ 0xd5, 0x2f, 0x08, 0x85, 0xb8, 0xc1, 0x1a, 0x31,
+ };
+static const uint8_t _test_1st_frag_comp[] = {
+ 0xe8, /* RFRAG | no ECN */
+ TEST_FRAG_TAG, /* tag: TEST_FRAG_TAG */
+ 0x00, 0x46, /* no ACK REQ | sequence: 0 | fragment_size: 70 */
+ 0x04, 0xc6, /* compressed datagram size: 1222 */
+ /* IPHC: TF: 0b11, NH: 0b1 (NHC), HLIM: 0b10 (64), CID: 0b0,
+ * Source: uncompressed (SAC: 0b0, SAM: 0b00),
+ * Destination: uncompressed (M:0, DAC: 0b0, DAM: 0b00) */
+ 0x7e, 0x00,
+ /* (uncompressed) Source: 2001:db8:d6c3:acf:dc71:2b85:82f:75fb */
+ 0x20, 0x01, 0x0d, 0xb8, 0xd6, 0xc3, 0x0a, 0xcf,
+ 0xdc, 0x71, 0x2b, 0x85, 0x08, 0x2f, 0x75, 0xfb,
+ /* (uncompressed) Destination: REM_GB */
+ 0x20, 0x01, 0x0d, 0xb8, 0xd3, 0x35, 0x91, 0x7e,
+ _LL0 ^ 0x2, _LL1, _LL2, _LL3, _LL4, _LL5, _LL6, _LL7 + 1,
+ /* NHC UDP: ports: 0b11 (12 bytes elided), (random) Checksum inline */
+ 0xf3, 0x4a, 0x47, 0xb8,
+ /* (random) payload of length 32 */
+ 0xba, 0xb3, 0x6e, 0x4f, 0xd8, 0x23, 0x40, 0xf3,
+ 0xfb, 0xb9, 0x05, 0xbf, 0xbe, 0x19, 0xf6, 0xa2,
+ 0xc7, 0x6e, 0x09, 0xf9, 0xba, 0x70, 0x3a, 0x38,
+ 0xd5, 0x2f, 0x08, 0x85, 0xb8, 0xc1, 0x1a, 0x31,
+ };
+static const uint8_t _test_1st_frag_comp_prev_hop[] = {
+ 0xe8, /* RFRAG | no ECN */
+ TEST_FRAG_TAG, /* tag: TEST_FRAG_TAG */
+ 0x00, 0x47, /* no ACK REQ | sequence: 0 | fragment_size: 71 */
+ 0x04, 0xc7, /* compressed datagram size: 1223 */
+ /* IPHC: TF: 0b11, NH: 0b1 (NHC), HLIM: 0b00 (inline), CID: 0b0,
+ * Source: uncompressed (SAC: 0b0, SAM: 0b00),
+ * Destination: uncompressed (M:0, DAC: 0b0, DAM: 0b00) */
+ 0x7c, 0x00,
+ /* Hop Limit: 65 */
+ 0x41,
+ /* (uncompressed) Source: 2001:db8:d6c3:acf:dc71:2b85:82f:75fb */
+ 0x20, 0x01, 0x0d, 0xb8, 0xd6, 0xc3, 0x0a, 0xcf,
+ 0xdc, 0x71, 0x2b, 0x85, 0x08, 0x2f, 0x75, 0xfb,
+ /* (uncompressed) Destination: REM_GB */
+ 0x20, 0x01, 0x0d, 0xb8, 0xd3, 0x35, 0x91, 0x7e,
+ _LL0 ^ 0x2, _LL1, _LL2, _LL3, _LL4, _LL5, _LL6, _LL7 + 1,
+ /* NHC UDP: ports: 0b11 (12 bytes elided), (random) Checksum inline */
+ 0xf3, 0x4a, 0x47, 0xb8,
+ /* (random) payload of length 32 */
+ 0xba, 0xb3, 0x6e, 0x4f, 0xd8, 0x23, 0x40, 0xf3,
+ 0xfb, 0xb9, 0x05, 0xbf, 0xbe, 0x19, 0xf6, 0xa2,
+ 0xc7, 0x6e, 0x09, 0xf9, 0xba, 0x70, 0x3a, 0x38,
+ 0xd5, 0x2f, 0x08, 0x85, 0xb8, 0xc1, 0x1a, 0x31,
+ };
+static const uint8_t _test_abort_frag[] = {
+ 0xe8, /* RFRAG | no ECN */
+ TEST_FRAG_TAG, /* tag: TEST_FRAG_TAG */
+ 0x00, 0x00, /* no ACK REQ | sequence: 0 | fragment_size: 0 */
+ 0x00, 0x00, /* offset: 0 */
+ };
+static const uint8_t _test_nth_frag[] = {
+ 0xe8, /* RFRAG | no ECN */
+ TEST_FRAG_TAG, /* tag: TEST_FRAG_TAG */
+ 0x06, 0x20, /* no ACK REQ | sequence: 6 | fragment_size: 32 */
+ 0x04, 0xb0, /* offset: 1200 */
+ /* payload of length 32 */
+ 0x54, 0x26, 0x63, 0xab, 0x31, 0x0b, 0xa4, 0x4e,
+ 0x6e, 0xa9, 0x09, 0x02, 0x15, 0xbb, 0x24, 0xa9,
+ 0x56, 0x44, 0x4a, 0x84, 0xd1, 0x83, 0xb9, 0xdb,
+ 0x0e, 0x0d, 0xd6, 0x6a, 0x83, 0x31, 0x1d, 0x94,
+ };
+static const uint8_t _test_ack[] = {
+ 0xea, /* RFRAG-ACK | no ECN */
+ 0xf1, /* tag: 0xf1 */
+ /* randomly set bitmap */
+ 0xbb, 0x6d, 0x5d, 0x94
+ };
+static const uint8_t _test_send_ipv6[] = {
+ /* IPv6 header: payload length = 158,
+ * next header = ICMPv6 (58), hop limit = 64 */
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x3a, 0x40,
+ /* Source: LOC_GB */
+ 0x20, 0x01, 0x0d, 0xb8, 0xd3, 0x35, 0x91, 0x7e,
+ _LL0 ^ 0x2, _LL1, _LL2, _LL3, _LL4, _LL5, _LL6, _LL7,
+ /* Destination: REM_GB */
+ 0x20, 0x01, 0x0d, 0xb8, 0xd3, 0x35, 0x91, 0x7e,
+ _LL0 ^ 0x2, _LL1, _LL2, _LL3, _LL4, _LL5, _LL6, _LL7 + 1,
+ };
+static const uint8_t _test_send_icmpv6[] = {
+ /* ICMPv6 Echo request (128), Code 0, (Random) checksum: 0x7269,
+ * random identifier: 0x59be, random sequence number: 15804 */
+ 0x80, 0x00, 0x72, 0x69, 0x59, 0xbe, 0x3d, 0xbc,
+ /* random payload */
+ 0x49, 0x19, 0xe8, 0x0b, 0x25, 0xbb, 0x00, 0x13,
+ 0x45, 0x85, 0xbd, 0x4a, 0xbb, 0xf1, 0x3d, 0xe3,
+ 0x36, 0xff, 0x52, 0xea, 0xe8, 0xec, 0xec, 0x82,
+ 0x94, 0x5f, 0xa4, 0x30, 0x1f, 0x46, 0x28, 0xc7,
+ 0x41, 0xff, 0x50, 0x84, 0x00, 0x41, 0xc7, 0x8d,
+ 0xb0, 0xdc, 0x18, 0xff, 0xcd, 0xfa, 0xa7, 0x72,
+ 0x4b, 0xcf, 0x7c, 0xf7, 0x7c, 0x8b, 0x65, 0x78,
+ 0xb0, 0xa8, 0xe7, 0x8f, 0xbc, 0x1e, 0xba, 0x4a,
+ 0x92, 0x13, 0x81, 0x5e, 0x23, 0xd1, 0xde, 0x09,
+ 0x84, 0x8a, 0xd0, 0xe2, 0xdd, 0x01, 0xc8, 0xd7,
+ 0x08, 0x4c, 0xd8, 0xc2, 0x21, 0x5c, 0x21, 0xb9,
+ 0x43, 0xea, 0x52, 0xbd, 0x6a, 0x9a, 0xac, 0x48,
+ 0x94, 0x98, 0xd1, 0x95, 0x6a, 0x0e, 0x10, 0xf9,
+ 0x2d, 0xe3, 0x53, 0xe4, 0x84, 0xb0, 0x8a, 0x92,
+ 0xaa, 0xe0, 0x5a, 0x63, 0x8b, 0x7d, 0x17, 0x51,
+ 0x22, 0x58, 0xa5, 0x6e, 0x87, 0x18, 0x32, 0x46,
+ 0x91, 0xd0, 0x59, 0xda, 0xc4, 0x9b, 0xa9, 0xde,
+ 0x20, 0xf4, 0xc8, 0xc4, 0xef, 0x1d, 0x9e, 0x13,
+ 0x6c, 0x28, 0x16, 0x59, 0xcc, 0x06
+ };
+static const uint8_t _test_send_frag1_prev_hop[] = {
+ 0xe8, /* RFRAG | no ECN */
+ TEST_SEND_DATAGRAM_TAG, /* tag: TEST_SEND_DATAGRAM_TAG */
+ 0x00, 0x24, /* no ACK REQ | sequence: 0 | fragment_size: 36 */
+ /* compressed datagram size: 144 */
+ 0x00, TEST_SEND_COMP_DATAGRAM_SIZE + 1,
+ /* IPHC: TF: 0b11, NH: 0b0 (inline), HLIM: 0b00 (inline), CID: 0b0,
+ * Source: uncompressed (SAC: 0b0, SAM: 0b00),
+ * Destination: uncompressed (M:0, DAC: 0b0, DAM: 0b00) */
+ 0x78, 0x00,
+ /* Next header: ICMPv6 (58), Hop Limit: 65 */
+ 0x3a, 0x41,
+ /* (uncompressed) Source: LOC_GB */
+ 0x20, 0x01, 0x0d, 0xb8, 0xd3, 0x35, 0x91, 0x7e,
+ _LL0 ^ 0x2, _LL1, _LL2, _LL3, _LL4, _LL5, _LL6, _LL7,
+ /* (uncompressed) Destination: REM_GB */
+ 0x20, 0x01, 0x0d, 0xb8, 0xd3, 0x35, 0x91, 0x7e,
+ _LL0 ^ 0x2, _LL1, _LL2, _LL3, _LL4, _LL5, _LL6, _LL7 + 1,
+ };
+static const uint8_t _test_send_frag1[] = {
+ 0xe8, /* RFRAG | no ECN */
+ TEST_SEND_DATAGRAM_TAG, /* tag: TEST_SEND_DATAGRAM_TAG */
+ 0x00, 0x5b, /* no ACK REQ | sequence: 0 | fragment_size: 91 */
+ /* compressed datagram size: 143 */
+ 0x00, TEST_SEND_COMP_DATAGRAM_SIZE,
+ /* IPHC: TF: 0b11, NH: 0b0 (inline), HLIM: 0b10 (64), CID: 0b0,
+ * Source: uncompressed (SAC: 0b0, SAM: 0b00),
+ * Destination: uncompressed (M:0, DAC: 0b0, DAM: 0b00) */
+ 0x7a, 0x00,
+ /* Next header: ICMPv6 (58) */
+ 0x3a,
+ /* (uncompressed) Source: LOC_GB */
+ 0x20, 0x01, 0x0d, 0xb8, 0xd3, 0x35, 0x91, 0x7e,
+ _LL0 ^ 0x2, _LL1, _LL2, _LL3, _LL4, _LL5, _LL6, _LL7,
+ /* (uncompressed) Destination: REM_GB */
+ 0x20, 0x01, 0x0d, 0xb8, 0xd3, 0x35, 0x91, 0x7e,
+ _LL0 ^ 0x2, _LL1, _LL2, _LL3, _LL4, _LL5, _LL6, _LL7 + 1,
+ 0x80, 0x00, 0x72, 0x69, 0x59, 0xbe, 0x3d, 0xbc,
+ 0x49, 0x19, 0xe8, 0x0b, 0x25, 0xbb, 0x00, 0x13,
+ 0x45, 0x85, 0xbd, 0x4a, 0xbb, 0xf1, 0x3d, 0xe3,
+ 0x36, 0xff, 0x52, 0xea, 0xe8, 0xec, 0xec, 0x82,
+ 0x94, 0x5f, 0xa4, 0x30, 0x1f, 0x46, 0x28, 0xc7,
+ 0x41, 0xff, 0x50, 0x84, 0x00, 0x41, 0xc7, 0x8d,
+ 0xb0, 0xdc, 0x18, 0xff, 0xcd, 0xfa, 0xa7, 0x72,
+ };
+static const uint8_t _test_send_frag2[] = {
+ 0xe8, /* RFRAG | no ECN */
+ TEST_SEND_DATAGRAM_TAG, /* tag: TEST_SEND_DATAGRAM_TAG */
+ 0x04, 0x60, /* no ACK REQ | sequence: 1 | fragment_size: 96 */
+ 0x00, 0x5b, /* offset: 91 */
+ 0x4b, 0xcf, 0x7c, 0xf7, 0x7c, 0x8b, 0x65, 0x78,
+ 0xb0, 0xa8, 0xe7, 0x8f, 0xbc, 0x1e, 0xba, 0x4a,
+ 0x92, 0x13, 0x81, 0x5e, 0x23, 0xd1, 0xde, 0x09,
+ 0x84, 0x8a, 0xd0, 0xe2, 0xdd, 0x01, 0xc8, 0xd7,
+ 0x08, 0x4c, 0xd8, 0xc2, 0x21, 0x5c, 0x21, 0xb9,
+ 0x43, 0xea, 0x52, 0xbd, 0x6a, 0x9a, 0xac, 0x48,
+ 0x94, 0x98, 0xd1, 0x95, 0x6a, 0x0e, 0x10, 0xf9,
+ 0x2d, 0xe3, 0x53, 0xe4, 0x84, 0xb0, 0x8a, 0x92,
+ 0xaa, 0xe0, 0x5a, 0x63, 0x8b, 0x7d, 0x17, 0x51,
+ 0x22, 0x58, 0xa5, 0x6e, 0x87, 0x18, 0x32, 0x46,
+ 0x91, 0xd0, 0x59, 0xda, 0xc4, 0x9b, 0xa9, 0xde,
+ 0x20, 0xf4, 0xc8, 0xc4, 0xef, 0x1d, 0x9e, 0x13,
+ };
+static const uint8_t _test_send_frag3[] = {
+ 0xe8, /* RFRAG | no ECN */
+ TEST_SEND_DATAGRAM_TAG, /* tag: TEST_SEND_DATAGRAM_TAG */
+ 0x08, 0x06, /* no ACK REQ | sequence: 2 | fragment_size: 6 */
+ 0x00, 0xbb, /* offset: 187 */
+ 0x6c, 0x28, 0x16, 0x59, 0xcc, 0x06
+ };
+static const uint8_t _loc_l2[] = LOC_L2;
+static const ipv6_addr_t _rem_ll = { .u8 = REM_LL };
+static const ipv6_addr_t _rem_gb = { .u8 = REM_GB };
+static const uint8_t _rem_l2[] = REM_L2;
+static const gnrc_sixlowpan_frag_rb_base_t _vrbe_base = {
+ .src = { 0xde, 0x71, 0x2b, 0x85, 0x08, 0x2f, 0x75, 0xfb },
+ .src_len = IEEE802154_LONG_ADDRESS_LEN,
+ .dst = LOC_L2,
+ .dst_len = sizeof(_loc_l2),
+ .tag = TEST_FRAG_TAG,
+ .datagram_size = 1232U,
+ .current_size = 0U,
+ };
+static uint8_t _target_buf[128U];
+static uint8_t _target_buf_len;
+/* to protect _target_buf and _target_buf_len */
+/* to wait for new data in _target_buf */
+static mutex_t _target_buf_filled = MUTEX_INIT_LOCKED;
+static mutex_t _target_buf_barrier = MUTEX_INIT;
+uint32_t _last_sent_frame;
+
+static gnrc_pktsnip_t *_create_recv_frag(const void *frag_data,
+ size_t frag_size);
+static gnrc_pktsnip_t *_create_recv_ack(const void *ack_data,
+ size_t ack_size);
+static int _set_route_and_nce(const ipv6_addr_t *route, unsigned pfx_len);
+static int _add_dst(const ipv6_addr_t *dst, unsigned pfx_len);
+static gnrc_pktsnip_t *_create_send_datagram(bool compressed, bool payload);
+static size_t _wait_for_packet(size_t exp_size);
+static inline void _wait_arq_timeout(gnrc_sixlowpan_frag_fb_t *fb);
+static void _check_vrbe_values(gnrc_sixlowpan_frag_vrb_t *vrbe,
+ size_t mhr_len, bool check_offset_diff,
+ int16_t exp_offset_diff);
+static void _check_ack(size_t mhr_len, uint8_t exp_tag,
+ const uint8_t *exp_bitmap);
+static void _check_abort_ack(size_t mhr_len, uint8_t exp_tag);
+static void _check_1st_frag_uncomp(size_t mhr_len, uint8_t exp_hl_diff);
+static void _check_send_frag1(size_t mhr_len, bool ack_req);
+static void _check_send_frag2(size_t mhr_len, bool ack_req);
+static void _check_send_frag3(size_t mhr_len, bool ack_req);
+static const gnrc_sixlowpan_frag_rb_t *_first_non_empty_rbuf(void);
+static int _mock_netdev_send(netdev_t *dev, const iolist_t *iolist);
+
+static void _set_up(void)
+{
+ /* reset data-structures */
+ _last_sent_frame = xtimer_now_usec() - CONFIG_GNRC_SIXLOWPAN_SFR_INTER_FRAME_GAP_US;
+ gnrc_sixlowpan_frag_rb_reset();
+ gnrc_sixlowpan_frag_vrb_reset();
+ gnrc_pktbuf_init();
+ memset(_mock_netif->ipv6.addrs, 0, sizeof(_mock_netif->ipv6.addrs));
+ memset(_mock_netif->ipv6.addrs_flags, 0,
+ sizeof(_mock_netif->ipv6.addrs_flags));
+ gnrc_ipv6_nib_init();
+ gnrc_ipv6_nib_init_iface(_mock_netif);
+ /* re-init for syncing */
+ mutex_init(&_target_buf_filled);
+ mutex_lock(&_target_buf_filled);
+ mutex_init(&_target_buf_barrier);
+}
+
+static void _tear_down(void)
+{
+ netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev, NULL);
+ mutex_unlock(&_target_buf_barrier);
+ /* wait in case mutex in _mock_netdev_send was already entered */
+ mutex_lock(&_target_buf_barrier);
+ memset(_target_buf, 0, sizeof(_target_buf));
+ _target_buf_len = 0;
+ mutex_unlock(&_target_buf_barrier);
+}
+
+static void test_sfr_forward__success__1st_frag_sixlo(void)
+{
+ gnrc_sixlowpan_frag_vrb_t *vrbe = gnrc_sixlowpan_frag_vrb_add(
+ &_vrbe_base, _mock_netif, _rem_l2, sizeof(_rem_l2)
+ );
+ gnrc_pktsnip_t *pkt, *frag;
+ size_t mhr_len;
+
+ vrbe->in_netif = _mock_netif;
+ TEST_ASSERT_NOT_NULL((pkt = gnrc_pktbuf_add(NULL, _test_1st_frag_uncomp,
+ sizeof(_test_1st_frag_uncomp),
+ GNRC_NETTYPE_SIXLOWPAN)));
+ /* separate fragment header from payload */
+ TEST_ASSERT_NOT_NULL((frag = gnrc_pktbuf_mark(pkt, sizeof(sixlowpan_sfr_rfrag_t),
+ GNRC_NETTYPE_SIXLOWPAN)));
+ LL_DELETE(pkt, frag);
+ netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev,
+ _mock_netdev_send);
+ TEST_ASSERT_EQUAL_INT(0, gnrc_sixlowpan_frag_sfr_forward(pkt,
+ frag->data,
+ vrbe,
+ 0));
+ gnrc_pktbuf_release(frag); /* delete separated fragment header */
+ TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_1st_frag_uncomp))));
+ TEST_ASSERT(gnrc_pktbuf_is_sane());
+ TEST_ASSERT(gnrc_pktbuf_is_empty());
+ _check_vrbe_values(vrbe, mhr_len, false, 0);
+ _check_1st_frag_uncomp(mhr_len, 0U);
+ /* VRB entry should not have been removed */
+ TEST_ASSERT_NOT_NULL(gnrc_sixlowpan_frag_vrb_get(_vrbe_base.src,
+ _vrbe_base.src_len,
+ _vrbe_base.tag));
+}
+
+static void test_sfr_forward__success__1st_frag_iphc(void)
+{
+ gnrc_sixlowpan_frag_vrb_t *vrbe = gnrc_sixlowpan_frag_vrb_add(
+ &_vrbe_base, _mock_netif, _rem_l2, sizeof(_rem_l2)
+ );
+ gnrc_pktsnip_t *pkt, *frag;
+ size_t mhr_len;
+
+ vrbe->in_netif = _mock_netif;
+ TEST_ASSERT_NOT_NULL((pkt = gnrc_pktbuf_add(NULL, _test_1st_frag_comp,
+ sizeof(_test_1st_frag_comp),
+ GNRC_NETTYPE_SIXLOWPAN)));
+ /* separate fragment header from payload */
+ TEST_ASSERT_NOT_NULL((frag = gnrc_pktbuf_mark(pkt, sizeof(sixlowpan_sfr_rfrag_t),
+ GNRC_NETTYPE_SIXLOWPAN)));
+ LL_DELETE(pkt, frag);
+ netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev,
+ _mock_netdev_send);
+ TEST_ASSERT_EQUAL_INT(0, gnrc_sixlowpan_frag_sfr_forward(pkt,
+ frag->data,
+ vrbe,
+ 0));
+ gnrc_pktbuf_release(frag); /* delete separated fragment header */
+ /* first wait and check IPHC part (we put some slack in the first fragment) */
+ TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_1st_frag_comp))));
+ _check_vrbe_values(vrbe, mhr_len, false, 0);
+ TEST_ASSERT_MESSAGE(
+ memcmp(&_test_1st_frag_comp[TEST_1ST_FRAG_COMP_PAYLOAD_POS],
+ &_target_buf[mhr_len + sizeof(sixlowpan_sfr_rfrag_t)],
+ sizeof(_test_1st_frag_comp) - sizeof(sixlowpan_sfr_rfrag_t)) == 0,
+ "unexpected payload"
+ );
+ /* VRB entry should not have been removed */
+ TEST_ASSERT_NOT_NULL(gnrc_sixlowpan_frag_vrb_get(_vrbe_base.src,
+ _vrbe_base.src_len,
+ _vrbe_base.tag));
+}
+
+static void test_sfr_forward__success__nth_frag_incomplete(void)
+{
+ gnrc_sixlowpan_frag_vrb_t *vrbe = gnrc_sixlowpan_frag_vrb_add(
+ &_vrbe_base, _mock_netif, _rem_l2, sizeof(_rem_l2)
+ );
+ gnrc_pktsnip_t *pkt, *frag;
+ size_t mhr_len;
+
+ vrbe->in_netif = _mock_netif;
+ TEST_ASSERT_NOT_NULL((pkt = gnrc_pktbuf_add(NULL, _test_nth_frag,
+ sizeof(_test_nth_frag),
+ GNRC_NETTYPE_SIXLOWPAN)));
+ /* separate fragment header from payload */
+ TEST_ASSERT_NOT_NULL((frag = gnrc_pktbuf_mark(pkt,
+ sizeof(sixlowpan_sfr_rfrag_t),
+ GNRC_NETTYPE_SIXLOWPAN)));
+ LL_DELETE(pkt, frag);
+ netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev,
+ _mock_netdev_send);
+ TEST_ASSERT_EQUAL_INT(0, gnrc_sixlowpan_frag_sfr_forward(pkt,
+ frag->data,
+ vrbe,
+ 0));
+ gnrc_pktbuf_release(frag); /* delete separated fragment header */
+ TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_nth_frag))));
+ TEST_ASSERT(gnrc_pktbuf_is_sane());
+ TEST_ASSERT(gnrc_pktbuf_is_empty());
+ _check_vrbe_values(vrbe, mhr_len, false, 0);
+ TEST_ASSERT_MESSAGE(
+ memcmp(&_test_nth_frag[TEST_NTH_FRAG_PAYLOAD_POS],
+ &_target_buf[mhr_len + sizeof(sixlowpan_sfr_rfrag_t)],
+ TEST_NTH_FRAG_SIZE) == 0,
+ "unexpected forwarded packet payload"
+ );
+ /* VRB entry should not have been removed */
+ TEST_ASSERT_NOT_NULL(gnrc_sixlowpan_frag_vrb_get(_vrbe_base.src,
+ _vrbe_base.src_len,
+ _vrbe_base.tag));
+}
+
+static void test_sfr_forward__success__nth_frag_complete(void)
+{
+ gnrc_sixlowpan_frag_vrb_t *vrbe = gnrc_sixlowpan_frag_vrb_add(
+ &_vrbe_base, _mock_netif, _rem_l2, sizeof(_rem_l2)
+ );
+ gnrc_pktsnip_t *pkt, *frag;
+
+ TEST_ASSERT_NOT_NULL((pkt = gnrc_pktbuf_add(NULL, _test_nth_frag,
+ sizeof(_test_nth_frag),
+ GNRC_NETTYPE_SIXLOWPAN)));
+ /* separate fragment header from payload */
+ TEST_ASSERT_NOT_NULL((frag = gnrc_pktbuf_mark(pkt,
+ sizeof(sixlowpan_sfr_rfrag_t),
+ GNRC_NETTYPE_SIXLOWPAN)));
+ LL_DELETE(pkt, frag);
+ /* simulate current_size only missing the created fragment */
+ vrbe->super.current_size = _vrbe_base.datagram_size;
+ netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev,
+ _mock_netdev_send);
+ TEST_ASSERT_EQUAL_INT(0, gnrc_sixlowpan_frag_sfr_forward(pkt,
+ frag->data,
+ vrbe,
+ 0));
+ gnrc_pktbuf_release(frag); /* delete separated fragment header */
+ TEST_ASSERT(_wait_for_packet(sizeof(_test_nth_frag)));
+ TEST_ASSERT(gnrc_pktbuf_is_sane());
+ TEST_ASSERT(gnrc_pktbuf_is_empty());
+ /* VRB entry still exists, as with SFR it is removed by ACK or timeout */
+ TEST_ASSERT_NOT_NULL(gnrc_sixlowpan_frag_vrb_get(_vrbe_base.src,
+ _vrbe_base.src_len,
+ _vrbe_base.tag));
+}
+
+static void test_sfr_forward__ENOMEM__netif_hdr_build_fail(void)
+{
+ gnrc_sixlowpan_frag_vrb_t *vrbe = gnrc_sixlowpan_frag_vrb_add(
+ &_vrbe_base, _mock_netif, _rem_l2, sizeof(_rem_l2)
+ );
+ gnrc_pktsnip_t *pkt, *frag, *filled_space;
+
+ TEST_ASSERT_NOT_NULL((filled_space = gnrc_pktbuf_add(
+ NULL, NULL,
+ /* 115U == 2 * sizeof(gnrc_pktsnip_t) + movement due to mark */
+ CONFIG_GNRC_PKTBUF_SIZE - sizeof(_test_nth_frag) - 115U,
+ GNRC_NETTYPE_UNDEF
+ )));
+ TEST_ASSERT_NOT_NULL((pkt = gnrc_pktbuf_add(NULL, _test_nth_frag,
+ sizeof(_test_nth_frag),
+ GNRC_NETTYPE_SIXLOWPAN)));
+ /* separate fragment header from payload */
+ TEST_ASSERT_NOT_NULL((frag = gnrc_pktbuf_mark(pkt,
+ sizeof(sixlowpan_sfr_rfrag_t),
+ GNRC_NETTYPE_SIXLOWPAN)));
+ LL_DELETE(pkt, frag);
+
+ netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev,
+ _mock_netdev_send);
+ TEST_ASSERT_EQUAL_INT(-ENOMEM, gnrc_sixlowpan_frag_sfr_forward(pkt,
+ frag->data,
+ vrbe,
+ 0));
+ gnrc_pktbuf_release(frag); /* delete separated fragment header */
+ gnrc_pktbuf_release(filled_space);
+ TEST_ASSERT(gnrc_pktbuf_is_sane());
+ TEST_ASSERT(gnrc_pktbuf_is_empty());
+}
+
+static void test_sixlo_recv_fwd__1st_frag_uncomp(void)
+{
+ gnrc_sixlowpan_frag_vrb_t *vrbe;
+ gnrc_pktsnip_t *frag;
+ size_t mhr_len;
+
+ TEST_ASSERT_NOT_NULL(
+ (frag = _create_recv_frag(_test_1st_frag_uncomp,
+ sizeof(_test_1st_frag_uncomp)))
+ );
+ /* configure route to destination of IP header in frag */
+ TEST_ASSERT_EQUAL_INT(0, _set_route_and_nce(&_rem_gb, REM_GB_PFX_LEN));
+ netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev,
+ _mock_netdev_send);
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ frag));
+ TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_1st_frag_uncomp))));
+ TEST_ASSERT(gnrc_pktbuf_is_sane());
+ TEST_ASSERT(gnrc_pktbuf_is_empty());
+ /* reassembly buffer remains empty */
+ TEST_ASSERT_NULL(_first_non_empty_rbuf());
+ /* but there was a VRB entry created */
+ TEST_ASSERT_NOT_NULL((vrbe = gnrc_sixlowpan_frag_vrb_get(
+ _vrbe_base.src, _vrbe_base.src_len, _vrbe_base.tag
+ )));
+ _check_vrbe_values(vrbe, mhr_len, true, 0);
+ _check_1st_frag_uncomp(mhr_len, 1U);
+}
+
+static void test_sixlo_recv_fwd__1st_frag_uncomp__req_ack(void)
+{
+ gnrc_sixlowpan_frag_vrb_t *vrbe;
+ gnrc_pktsnip_t *frag;
+ size_t mhr_len;
+
+ TEST_ASSERT_NOT_NULL(
+ (frag = _create_recv_frag(_test_1st_frag_uncomp,
+ sizeof(_test_1st_frag_uncomp)))
+ );
+ /* set ACK Req flag in RFRAG header */
+ sixlowpan_sfr_rfrag_set_ack_req(frag->data);
+ /* configure route to destination of IP header in frag */
+ TEST_ASSERT_EQUAL_INT(0, _set_route_and_nce(&_rem_gb, REM_GB_PFX_LEN));
+ netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev,
+ _mock_netdev_send);
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ frag));
+ TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_1st_frag_uncomp))));
+ /* reassembly buffer remains empty */
+ TEST_ASSERT_NULL(_first_non_empty_rbuf());
+ /* but there was a VRB entry created */
+ TEST_ASSERT_NOT_NULL((vrbe = gnrc_sixlowpan_frag_vrb_get(
+ _vrbe_base.src, _vrbe_base.src_len, _vrbe_base.tag
+ )));
+ _check_vrbe_values(vrbe, mhr_len, true, 0);
+ _check_1st_frag_uncomp(mhr_len, 1U);
+ /* should time out as only the reassembling endpoint is supposed to send
+ * an abort ACK or if no VRB exists on a forwarding node. See
+ * - https://tools.ietf.org/html/rfc8931#section-6.1.2
+ * - https://tools.ietf.org/html/rfc8931#section-6.3
+ */
+ TEST_ASSERT_EQUAL_INT(0, _wait_for_packet(sizeof(_test_1st_frag_comp)));
+ TEST_ASSERT(gnrc_pktbuf_is_sane());
+ TEST_ASSERT(gnrc_pktbuf_is_empty());
+}
+
+static void test_sixlo_recv_fwd__1st_frag_uncomp__no_route(void)
+{
+ const gnrc_sixlowpan_frag_rb_t *rbuf;
+ gnrc_pktsnip_t *frag;
+
+ TEST_ASSERT_NOT_NULL(
+ (frag = _create_recv_frag(_test_1st_frag_uncomp,
+ sizeof(_test_1st_frag_uncomp)))
+ );
+ netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev,
+ _mock_netdev_send);
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ frag));
+ /* should time out */
+ TEST_ASSERT_EQUAL_INT(0, _wait_for_packet(sizeof(_test_1st_frag_uncomp)));
+ /* normal reassembly should have started */
+ /* reassembly buffer entry should have been created */
+ TEST_ASSERT_NOT_NULL((rbuf = _first_non_empty_rbuf()));
+ /* and VRB remains empty */
+ TEST_ASSERT_NULL(gnrc_sixlowpan_frag_vrb_get(
+ _vrbe_base.src, _vrbe_base.src_len, _vrbe_base.tag
+ ));
+ TEST_ASSERT_EQUAL_INT(_vrbe_base.datagram_size, rbuf->pkt->size);
+ gnrc_pktbuf_release(rbuf->pkt);
+ TEST_ASSERT(gnrc_pktbuf_is_sane());
+ TEST_ASSERT(gnrc_pktbuf_is_empty());
+}
+
+static void test_sixlo_recv_fwd__1st_frag_uncomp__after_nth_frag(void)
+{
+ gnrc_sixlowpan_frag_vrb_t *vrbe;
+ gnrc_pktsnip_t *frag;
+ size_t mhr_len;
+
+ TEST_ASSERT_NOT_NULL(
+ (frag = _create_recv_frag(_test_nth_frag, sizeof(_test_nth_frag)))
+ );
+ /* configure route to destination of IP header in frag */
+ TEST_ASSERT_EQUAL_INT(0, _set_route_and_nce(&_rem_gb, REM_GB_PFX_LEN));
+ netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev,
+ _mock_netdev_send);
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ frag));
+
+ /* expect abort ACK, see
+ * https://tools.ietf.org/html/rfc8931#section-6.1.2 */
+ TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_nth_frag))));
+ /* no reassembly buffer entry */
+ TEST_ASSERT_NULL(_first_non_empty_rbuf());
+ /* and VRB remains empty */
+ TEST_ASSERT_NULL(gnrc_sixlowpan_frag_vrb_get(
+ _vrbe_base.src, _vrbe_base.src_len, _vrbe_base.tag
+ ));
+ _check_abort_ack(mhr_len, _vrbe_base.tag);
+ TEST_ASSERT_NOT_NULL(
+ (frag = _create_recv_frag(_test_1st_frag_uncomp,
+ sizeof(_test_1st_frag_uncomp)))
+ );
+ _target_buf_len = 0;
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ frag));
+ TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_1st_frag_uncomp))));
+ TEST_ASSERT(gnrc_pktbuf_is_sane());
+ TEST_ASSERT(gnrc_pktbuf_is_empty());
+ /* reassembly buffer remains empty */
+ TEST_ASSERT_NULL(_first_non_empty_rbuf());
+ /* but there was a VRB entry created */
+ TEST_ASSERT_NOT_NULL((vrbe = gnrc_sixlowpan_frag_vrb_get(
+ _vrbe_base.src, _vrbe_base.src_len, _vrbe_base.tag
+ )));
+ _check_vrbe_values(vrbe, mhr_len, true, 0);
+ _check_1st_frag_uncomp(mhr_len, 1U);
+}
+
+static void test_sixlo_recv_fwd__1st_frag_comp(void)
+{
+ gnrc_sixlowpan_frag_vrb_t *vrbe;
+ gnrc_pktsnip_t *frag;
+ size_t mhr_len;
+
+ TEST_ASSERT_NOT_NULL(
+ (frag = _create_recv_frag(_test_1st_frag_comp_prev_hop,
+ sizeof(_test_1st_frag_comp_prev_hop)))
+ );
+ /* configure route to destination of IP header in frag */
+ TEST_ASSERT_EQUAL_INT(0, _set_route_and_nce(&_rem_gb, REM_GB_PFX_LEN));
+ netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev,
+ _mock_netdev_send);
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ frag));
+ /* wait for recompressed fragment (hop-limit is now compressed) */
+ TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_1st_frag_comp))));
+ TEST_ASSERT(gnrc_pktbuf_is_sane());
+ TEST_ASSERT(gnrc_pktbuf_is_empty());
+ /* reassembly buffer remains empty */
+ TEST_ASSERT_NULL(_first_non_empty_rbuf());
+ /* but there was a VRB entry created */
+ TEST_ASSERT_NOT_NULL((vrbe = gnrc_sixlowpan_frag_vrb_get(
+ _vrbe_base.src, _vrbe_base.src_len, _vrbe_base.tag
+ )));
+ _check_vrbe_values(vrbe, mhr_len, true,
+ (int16_t)sizeof(_test_1st_frag_comp) -
+ (int16_t)sizeof(_test_1st_frag_comp_prev_hop));
+ TEST_ASSERT_EQUAL_INT(TEST_1ST_FRAG_COMP_FRAG_SIZE,
+ vrbe->super.current_size);
+ /* frag_size changed to the size of the expected fragment */
+ TEST_ASSERT_EQUAL_INT(
+ sixlowpan_sfr_rfrag_get_frag_size(
+ (sixlowpan_sfr_rfrag_t *)_test_1st_frag_comp
+ ),
+ sixlowpan_sfr_rfrag_get_frag_size(
+ (sixlowpan_sfr_rfrag_t *)&_target_buf[mhr_len]
+ )
+ );
+ TEST_ASSERT_MESSAGE(
+ memcmp(&_test_1st_frag_comp[TEST_1ST_FRAG_COMP_PAYLOAD_POS],
+ &_target_buf[mhr_len + sizeof(sixlowpan_sfr_rfrag_t)],
+ TEST_1ST_FRAG_COMP_SIZE) == 0,
+ "unexpected IPHC header"
+ );
+ TEST_ASSERT_MESSAGE(
+ memcmp(&_test_1st_frag_comp_prev_hop[TEST_1ST_FRAG_COMP_PREV_HOP_UDP_PAYLOAD_POS],
+ &_target_buf[mhr_len + TEST_1ST_FRAG_COMP_UDP_PAYLOAD_POS],
+ TEST_1ST_FRAG_UNCOMP_UDP_PAYLOAD_SIZE) == 0,
+ "unexpected forwarded packet payload"
+ );
+}
+
+/* a regression test to check for the bugs fixed in
+ * https://github.com/RIOT-OS/RIOT/pull/12848 */
+static void test_sixlo_recv_fwd__1st_frag_comp__resend(void)
+{
+ gnrc_sixlowpan_frag_vrb_t *vrbe;
+ gnrc_pktsnip_t *frag;
+ size_t mhr_len;
+
+ TEST_ASSERT_NOT_NULL(
+ (frag = _create_recv_frag(_test_1st_frag_comp_prev_hop,
+ sizeof(_test_1st_frag_comp_prev_hop)))
+ );
+ /* configure route to destination of IP header in frag */
+ TEST_ASSERT_EQUAL_INT(0, _set_route_and_nce(&_rem_gb, REM_GB_PFX_LEN));
+ netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev,
+ _mock_netdev_send);
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ frag));
+ /* wait for recompressed fragment (hop-limit is now compressed) */
+ TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_1st_frag_comp))));
+ TEST_ASSERT(gnrc_pktbuf_is_sane());
+ TEST_ASSERT(gnrc_pktbuf_is_empty());
+ /* reassembly buffer remains empty */
+ TEST_ASSERT_NULL(_first_non_empty_rbuf());
+ /* but there was a VRB entry created */
+ TEST_ASSERT_NOT_NULL(gnrc_sixlowpan_frag_vrb_get(_vrbe_base.src,
+ _vrbe_base.src_len,
+ _vrbe_base.tag));
+ /* receive fragment for a second time (e.g. resent) */
+ TEST_ASSERT_NOT_NULL(
+ (frag = _create_recv_frag(_test_1st_frag_comp_prev_hop,
+ sizeof(_test_1st_frag_comp_prev_hop)))
+ );
+ _target_buf_len = 0;
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ frag));
+ /* wait for recompressed fragment (hop-limit is now compressed) */
+ TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_1st_frag_comp))));
+ TEST_ASSERT(gnrc_pktbuf_is_sane());
+ gnrc_pktbuf_stats();
+ TEST_ASSERT(gnrc_pktbuf_is_empty());
+ /* reassembly buffer remains empty */
+ TEST_ASSERT_NULL(_first_non_empty_rbuf());
+ /* and VRB entry still exists */
+ TEST_ASSERT_NOT_NULL((vrbe = gnrc_sixlowpan_frag_vrb_get(
+ _vrbe_base.src, _vrbe_base.src_len, _vrbe_base.tag
+ )));
+ /* and if removed ... */
+ gnrc_sixlowpan_frag_vrb_rm(vrbe);
+ /* the fragment interval pool is empty again */
+ TEST_ASSERT(gnrc_sixlowpan_frag_rb_ints_empty());
+}
+
+static void test_sixlo_recv_fwd__1st_frag_comp__only_iphc(void)
+{
+ gnrc_sixlowpan_frag_vrb_t *vrbe;
+ gnrc_pktsnip_t *frag;
+ size_t mhr_len;
+
+ TEST_ASSERT_NOT_NULL(
+ (frag = _create_recv_frag(_test_1st_frag_comp_prev_hop,
+ TEST_1ST_FRAG_COMP_PREV_HOP_UDP_PAYLOAD_POS))
+ );
+ sixlowpan_sfr_rfrag_set_frag_size(frag->data,
+ TEST_1ST_FRAG_COMP_PREV_HOP_UDP_PAYLOAD_POS -
+ sizeof(sixlowpan_sfr_rfrag_t));
+ /* configure route to destination of IP header in frag */
+ TEST_ASSERT_EQUAL_INT(0, _set_route_and_nce(&_rem_gb, REM_GB_PFX_LEN));
+ netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev,
+ _mock_netdev_send);
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ frag));
+ /* wait for recompressed fragment (hop-limit is now compressed) */
+ TEST_ASSERT((mhr_len = _wait_for_packet(TEST_1ST_FRAG_COMP_UDP_PAYLOAD_POS)));
+ /* reassembly buffer remains empty */
+ TEST_ASSERT_NULL(_first_non_empty_rbuf());
+ /* but there was a VRB entry created */
+ TEST_ASSERT_NOT_NULL((vrbe = gnrc_sixlowpan_frag_vrb_get(
+ _vrbe_base.src, _vrbe_base.src_len, _vrbe_base.tag
+ )));
+ _check_vrbe_values(vrbe, mhr_len, true,
+ (int16_t)TEST_1ST_FRAG_COMP_UDP_PAYLOAD_POS -
+ (int16_t)TEST_1ST_FRAG_COMP_PREV_HOP_UDP_PAYLOAD_POS);
+ TEST_ASSERT_EQUAL_INT(TEST_1ST_FRAG_COMP_ONLY_IPHC_FRAG_SIZE,
+ vrbe->super.current_size);
+ TEST_ASSERT_MESSAGE(
+ memcmp(&_test_1st_frag_comp[TEST_1ST_FRAG_COMP_PAYLOAD_POS],
+ &_target_buf[mhr_len + sizeof(sixlowpan_sfr_rfrag_t)],
+ TEST_1ST_FRAG_COMP_SIZE) == 0,
+ "unexpected IPHC header"
+ );
+ TEST_ASSERT(gnrc_pktbuf_is_sane());
+ TEST_ASSERT(gnrc_pktbuf_is_empty());
+}
+
+static void test_sixlo_recv_fwd__1st_frag_comp__only_iphc_no_nhc(void)
+{
+ gnrc_sixlowpan_frag_vrb_t *vrbe;
+ gnrc_pktsnip_t *frag;
+ size_t mhr_len;
+
+ TEST_ASSERT_NOT_NULL(
+ (frag = _create_recv_frag(_test_send_frag1_prev_hop,
+ sizeof(_test_send_frag1_prev_hop)))
+ );
+ /* configure route to destination of IP header in frag */
+ TEST_ASSERT_EQUAL_INT(0, _set_route_and_nce(&_rem_gb, REM_GB_PFX_LEN));
+ netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev,
+ _mock_netdev_send);
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ frag));
+ /* wait for recompressed fragment (hop-limit is now compressed) */
+ TEST_ASSERT((mhr_len = _wait_for_packet(TEST_SEND_FRAG1_ICMPV6_PAYLOAD_POS)));
+ /* reassembly buffer remains empty */
+ TEST_ASSERT_NULL(_first_non_empty_rbuf());
+ /* but there was a VRB entry created */
+ TEST_ASSERT_NOT_NULL((vrbe = gnrc_sixlowpan_frag_vrb_get(
+ _vrbe_base.src, _vrbe_base.src_len, TEST_SEND_DATAGRAM_TAG
+ )));
+ _check_vrbe_values(vrbe, mhr_len, true,
+ (int16_t)TEST_SEND_FRAG1_ICMPV6_PAYLOAD_POS -
+ (int16_t)sizeof(_test_send_frag1_prev_hop));
+ TEST_ASSERT_MESSAGE(
+ memcmp(&_test_send_frag1[TEST_SEND_FRAG1_PAYLOAD_POS],
+ &_target_buf[mhr_len + sizeof(sixlowpan_sfr_rfrag_t)],
+ TEST_SEND_FRAG1_PAYLOAD_SIZE) == 0,
+ "unexpected IPHC header"
+ );
+ TEST_ASSERT(gnrc_pktbuf_is_sane());
+ TEST_ASSERT(gnrc_pktbuf_is_empty());
+}
+
+static void test_sixlo_recv_fwd__1st_frag_comp__no_route(void)
+{
+ const gnrc_sixlowpan_frag_rb_t *rbuf;
+ gnrc_pktsnip_t *frag;
+
+ TEST_ASSERT_NOT_NULL(
+ (frag = _create_recv_frag(_test_1st_frag_comp_prev_hop,
+ sizeof(_test_1st_frag_comp_prev_hop)))
+ );
+ netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev,
+ _mock_netdev_send);
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ frag));
+ /* should time out */
+ TEST_ASSERT_EQUAL_INT(0, _wait_for_packet(sizeof(_test_1st_frag_comp)));
+ /* normal reassembly should have started */
+ /* reassembly buffer entry should have been created */
+ TEST_ASSERT_NOT_NULL((rbuf = _first_non_empty_rbuf()));
+ /* and VRB remains empty */
+ TEST_ASSERT_NULL(gnrc_sixlowpan_frag_vrb_get(
+ _vrbe_base.src, _vrbe_base.src_len, _vrbe_base.tag
+ ));
+ TEST_ASSERT_EQUAL_INT(_vrbe_base.datagram_size, rbuf->pkt->size);
+ gnrc_pktbuf_release(rbuf->pkt);
+ TEST_ASSERT(gnrc_pktbuf_is_sane());
+ TEST_ASSERT(gnrc_pktbuf_is_empty());
+}
+
+static void test_sixlo_recv_fwd__1st_frag_comp__no_route_only_iphc(void)
+{
+ const gnrc_sixlowpan_frag_rb_t *rbuf;
+ gnrc_pktsnip_t *frag;
+
+ TEST_ASSERT_NOT_NULL(
+ (frag = _create_recv_frag(_test_1st_frag_comp_prev_hop,
+ TEST_1ST_FRAG_COMP_PREV_HOP_UDP_PAYLOAD_POS))
+ );
+ netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev,
+ _mock_netdev_send);
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ frag));
+ /* should time out */
+ TEST_ASSERT_EQUAL_INT(0, _wait_for_packet(sizeof(_test_1st_frag_comp)));
+ /* normal reassembly should have started */
+ /* reassembly buffer entry should have been created */
+ TEST_ASSERT_NOT_NULL((rbuf = _first_non_empty_rbuf()));
+ /* and VRB remains empty */
+ TEST_ASSERT_NULL(gnrc_sixlowpan_frag_vrb_get(
+ _vrbe_base.src, _vrbe_base.src_len, _vrbe_base.tag
+ ));
+ TEST_ASSERT_EQUAL_INT(_vrbe_base.datagram_size, rbuf->pkt->size);
+ gnrc_pktbuf_release(rbuf->pkt);
+ TEST_ASSERT(gnrc_pktbuf_is_sane());
+ TEST_ASSERT(gnrc_pktbuf_is_empty());
+}
+
+static void test_sixlo_recv_fwd__1st_frag_comp__no_refrag(void)
+{
+ gnrc_sixlowpan_frag_fb_t *reserved[CONFIG_GNRC_SIXLOWPAN_FRAG_FB_SIZE];
+ const gnrc_sixlowpan_frag_rb_t *rbuf;
+ gnrc_pktsnip_t *frag;
+
+ TEST_ASSERT_NOT_NULL(
+ (frag = _create_recv_frag(_test_1st_frag_comp_prev_hop,
+ sizeof(_test_1st_frag_comp_prev_hop)))
+ );
+ /* consume all available gnrc_sixlowpan_frag_fb_t instances so creating
+ * a fragment with extra slack is not possible */
+ for (unsigned i = 0; i < CONFIG_GNRC_SIXLOWPAN_FRAG_FB_SIZE; i++) {
+ reserved[i] = gnrc_sixlowpan_frag_fb_get();
+ reserved[i]->pkt = frag;
+ }
+ netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev,
+ _mock_netdev_send);
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ frag));
+ /* should time out */
+ TEST_ASSERT_EQUAL_INT(0, _wait_for_packet(sizeof(_test_1st_frag_comp)));
+ /* normal reassembly should have started */
+ /* reassembly buffer entry should have been created */
+ TEST_ASSERT_NOT_NULL((rbuf = _first_non_empty_rbuf()));
+ /* and VRB remains empty */
+ TEST_ASSERT_NULL(gnrc_sixlowpan_frag_vrb_get(
+ _vrbe_base.src, _vrbe_base.src_len, _vrbe_base.tag
+ ));
+ TEST_ASSERT_EQUAL_INT(_vrbe_base.datagram_size, rbuf->pkt->size);
+ gnrc_pktbuf_release(rbuf->pkt);
+ /* release all gnrc_sixlowpan_frag_fb_t instances again */
+ for (unsigned i = 0; i < CONFIG_GNRC_SIXLOWPAN_FRAG_FB_SIZE; i++) {
+ reserved[i]->pkt = NULL;
+ }
+ TEST_ASSERT(gnrc_pktbuf_is_sane());
+ TEST_ASSERT(gnrc_pktbuf_is_empty());
+ /* This is normal reassembly so the rest should have be tested in the
+ * test for normal reassembly ;-) */
+}
+
+static void test_sixlo_recv_fwd__1st_frag_comp__after_nth_frag(void)
+{
+ gnrc_sixlowpan_frag_vrb_t *vrbe;
+ gnrc_pktsnip_t *frag;
+ size_t mhr_len;
+
+ TEST_ASSERT_NOT_NULL(
+ (frag = _create_recv_frag(_test_nth_frag,
+ sizeof(_test_nth_frag)))
+ );
+ /* configure route to destination of IP header in frag */
+ TEST_ASSERT_EQUAL_INT(0, _set_route_and_nce(&_rem_gb, REM_GB_PFX_LEN));
+ netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev,
+ _mock_netdev_send);
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ frag));
+
+ /* expect abort ACK, see
+ * https://tools.ietf.org/html/rfc8931#section-6.1.2 */
+ TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_nth_frag))));
+ /* no reassembly buffer entry */
+ TEST_ASSERT_NULL(_first_non_empty_rbuf());
+ /* and VRB remains empty */
+ TEST_ASSERT_NULL(gnrc_sixlowpan_frag_vrb_get(
+ _vrbe_base.src, _vrbe_base.src_len, _vrbe_base.tag
+ ));
+ _check_abort_ack(mhr_len, _vrbe_base.tag);
+ TEST_ASSERT_NOT_NULL(
+ (frag = _create_recv_frag(_test_1st_frag_comp_prev_hop,
+ sizeof(_test_1st_frag_comp_prev_hop)))
+ );
+ _target_buf_len = 0;
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ frag));
+ /* wait for recompressed fragment (hop-limit is now compressed) */
+ TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_1st_frag_comp))));
+ TEST_ASSERT(gnrc_pktbuf_is_sane());
+ TEST_ASSERT(gnrc_pktbuf_is_empty());
+ /* reassembly buffer remains empty */
+ TEST_ASSERT_NULL(_first_non_empty_rbuf());
+ /* but there was a VRB entry created */
+ TEST_ASSERT_NOT_NULL((vrbe = gnrc_sixlowpan_frag_vrb_get(
+ _vrbe_base.src, _vrbe_base.src_len, _vrbe_base.tag
+ )));
+ _check_vrbe_values(vrbe, mhr_len, true,
+ (int16_t)sizeof(_test_1st_frag_comp) -
+ (int16_t)sizeof(_test_1st_frag_comp_prev_hop));
+ TEST_ASSERT_EQUAL_INT(TEST_1ST_FRAG_COMP_FRAG_SIZE,
+ vrbe->super.current_size);
+ TEST_ASSERT_MESSAGE(
+ memcmp(&_test_1st_frag_comp[TEST_1ST_FRAG_COMP_PAYLOAD_POS],
+ &_target_buf[mhr_len + sizeof(sixlowpan_sfr_rfrag_t)],
+ TEST_1ST_FRAG_COMP_SIZE) == 0,
+ "unexpected IPHC header"
+ );
+ TEST_ASSERT_MESSAGE(
+ memcmp(&_test_1st_frag_comp_prev_hop[TEST_1ST_FRAG_COMP_PREV_HOP_UDP_PAYLOAD_POS],
+ &_target_buf[mhr_len + TEST_1ST_FRAG_COMP_UDP_PAYLOAD_POS],
+ TEST_1ST_FRAG_UNCOMP_UDP_PAYLOAD_SIZE) == 0,
+ "unexpected forwarded packet payload"
+ );
+}
+
+static void test_sixlo_recv_fwd__1st_frag_abort(void)
+{
+ uint8_t target_buf_dst[IEEE802154_LONG_ADDRESS_LEN];
+ gnrc_sixlowpan_frag_vrb_t *vrbe;
+ gnrc_pktsnip_t *frag;
+ sixlowpan_sfr_rfrag_t *rfrag_hdr;
+ size_t mhr_len;
+ le_uint16_t tmp;
+ uint8_t exp_tag;
+
+ TEST_ASSERT_NOT_NULL(
+ (frag = _create_recv_frag(_test_abort_frag,
+ sizeof(_test_abort_frag)))
+ );
+ TEST_ASSERT_NOT_NULL(
+ (vrbe = gnrc_sixlowpan_frag_vrb_add(&_vrbe_base, _mock_netif,
+ _rem_l2, sizeof(_rem_l2)))
+ );
+ vrbe->in_netif = _mock_netif;
+ exp_tag = vrbe->out_tag & 0xff;
+ netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev,
+ _mock_netdev_send);
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ frag));
+ /* should be forwarded */
+ TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_abort_frag))));
+ /* reassembly buffer remains empty */
+ TEST_ASSERT_NULL(_first_non_empty_rbuf());
+ /* and VRB entry was deleted */
+ TEST_ASSERT_NULL(gnrc_sixlowpan_frag_vrb_get(
+ _vrbe_base.src, _vrbe_base.src_len, _vrbe_base.tag
+ ));
+ TEST_ASSERT_EQUAL_INT(sizeof(_rem_l2), ieee802154_get_dst(_target_buf,
+ target_buf_dst,
+ &tmp));
+ TEST_ASSERT_MESSAGE(memcmp(_rem_l2, target_buf_dst, sizeof(_rem_l2)) == 0,
+ "Unexpected destination address");
+ rfrag_hdr = (sixlowpan_sfr_rfrag_t *)&_target_buf[mhr_len];
+ TEST_ASSERT(sixlowpan_sfr_rfrag_is(&rfrag_hdr->base));
+ TEST_ASSERT_EQUAL_INT(exp_tag, rfrag_hdr->base.tag);
+ TEST_ASSERT(!sixlowpan_sfr_rfrag_ack_req(rfrag_hdr));
+ TEST_ASSERT_EQUAL_INT(0, sixlowpan_sfr_rfrag_get_seq(rfrag_hdr));
+ TEST_ASSERT_EQUAL_INT(0, sixlowpan_sfr_rfrag_get_frag_size(rfrag_hdr));
+ TEST_ASSERT_EQUAL_INT(0, sixlowpan_sfr_rfrag_get_offset(rfrag_hdr));
+ TEST_ASSERT(gnrc_pktbuf_is_sane());
+ TEST_ASSERT(gnrc_pktbuf_is_empty());
+}
+
+static void test_sixlo_recv_fwd__1st_frag_nalp(void)
+{
+ gnrc_pktsnip_t *frag;
+ uint8_t *data;
+
+ TEST_ASSERT_NOT_NULL(
+ (frag = _create_recv_frag(_test_1st_frag_comp,
+ sizeof(sixlowpan_sfr_rfrag_t) + 1))
+ );
+ data = frag->data;
+ /* mark dispatch after RFRAG header a non-6LoWPAN frame */
+ data[sizeof(sixlowpan_sfr_rfrag_t)] &= ~(0xc0);
+ /* configure route to destination of IP header in frag */
+ TEST_ASSERT_EQUAL_INT(0, _set_route_and_nce(&_rem_gb, REM_GB_PFX_LEN));
+ netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev,
+ _mock_netdev_send);
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ frag));
+ /* should time out */
+ TEST_ASSERT_EQUAL_INT(0, _wait_for_packet(sizeof(_test_1st_frag_comp)));
+ /* reassembly buffer remains empty */
+ TEST_ASSERT_NULL(_first_non_empty_rbuf());
+ /* and VRB entry was deleted */
+ TEST_ASSERT_NULL(gnrc_sixlowpan_frag_vrb_get(
+ _vrbe_base.src, _vrbe_base.src_len, _vrbe_base.tag
+ ));
+ TEST_ASSERT(gnrc_pktbuf_is_sane());
+ TEST_ASSERT(gnrc_pktbuf_is_empty());
+}
+
+static void test_sixlo_recv_fwd__1st_frag_nalp__req_ack(void)
+{
+ gnrc_pktsnip_t *frag;
+ uint8_t *data;
+
+ TEST_ASSERT_NOT_NULL(
+ (frag = _create_recv_frag(_test_1st_frag_comp,
+ sizeof(sixlowpan_sfr_rfrag_t) + 1))
+ );
+ data = frag->data;
+ /* mark dispatch after RFRAG header a non-6LoWPAN frame */
+ data[sizeof(sixlowpan_sfr_rfrag_t)] &= ~(0xc0);
+ /* set ACK request flag */
+ sixlowpan_sfr_rfrag_set_ack_req(frag->data);
+ /* configure route to destination of IP header in frag */
+ TEST_ASSERT_EQUAL_INT(0, _set_route_and_nce(&_rem_gb, REM_GB_PFX_LEN));
+ netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev,
+ _mock_netdev_send);
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ frag));
+ /* should time out as only the reassembling endpoint is supposed to send
+ * an abort ACK or if no VRB exists on a forwarding node. See
+ * - https://tools.ietf.org/html/rfc8931#section-6.1.2
+ * - https://tools.ietf.org/html/rfc8931#section-6.3
+ */
+ TEST_ASSERT_EQUAL_INT(0, _wait_for_packet(sizeof(_test_1st_frag_comp)));
+ /* reassembly buffer remains empty */
+ TEST_ASSERT_NULL(_first_non_empty_rbuf());
+ /* and VRB entry was deleted */
+ TEST_ASSERT_NULL(gnrc_sixlowpan_frag_vrb_get(
+ _vrbe_base.src, _vrbe_base.src_len, _vrbe_base.tag
+ ));
+ TEST_ASSERT(gnrc_pktbuf_is_sane());
+ TEST_ASSERT(gnrc_pktbuf_is_empty());
+}
+
+static void test_sixlo_recv_fwd__1st_frag_nalp__no_vrb(void)
+{
+ gnrc_pktsnip_t *frag;
+ uint8_t *data;
+
+ TEST_ASSERT_NOT_NULL(
+ (frag = _create_recv_frag(_test_1st_frag_comp,
+ sizeof(sixlowpan_sfr_rfrag_t) + 1))
+ );
+ data = frag->data;
+ /* mark dispatch after RFRAG header a non-6LoWPAN frame */
+ data[sizeof(sixlowpan_sfr_rfrag_t)] &= ~(0xc0);
+ netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev,
+ _mock_netdev_send);
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ frag));
+ /* should time out */
+ TEST_ASSERT_EQUAL_INT(0, _wait_for_packet(sizeof(_test_1st_frag_comp)));
+ /* reassembly buffer remains empty */
+ TEST_ASSERT_NULL(_first_non_empty_rbuf());
+ /* and VRB remains entry */
+ TEST_ASSERT_NULL(gnrc_sixlowpan_frag_vrb_get(
+ _vrbe_base.src, _vrbe_base.src_len, _vrbe_base.tag
+ ));
+ TEST_ASSERT(gnrc_pktbuf_is_sane());
+ TEST_ASSERT(gnrc_pktbuf_is_empty());
+}
+
+static void test_sixlo_recv_fwd__nth_frag(void)
+{
+ gnrc_sixlowpan_frag_vrb_t *vrbe;
+ gnrc_pktsnip_t *frag;
+ size_t mhr_len;
+
+ TEST_ASSERT_NOT_NULL(
+ (frag = _create_recv_frag(_test_nth_frag, sizeof(_test_nth_frag)))
+ );
+ TEST_ASSERT_NOT_NULL(
+ (vrbe = gnrc_sixlowpan_frag_vrb_add(&_vrbe_base, _mock_netif,
+ _rem_l2, sizeof(_rem_l2)))
+ );
+ vrbe->in_netif = _mock_netif;
+ /* set offset_diff to test it in the outgoing RFRAG */
+ vrbe->offset_diff = TEST_OFFSET_DIFF;
+ netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev,
+ _mock_netdev_send);
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ frag));
+ TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_nth_frag))));
+ /* reassembly buffer remains empty */
+ TEST_ASSERT_NULL(_first_non_empty_rbuf());
+ /* and VRB entry still exist */
+ TEST_ASSERT_NOT_NULL((vrbe = gnrc_sixlowpan_frag_vrb_get(
+ _vrbe_base.src, _vrbe_base.src_len, _vrbe_base.tag
+ )));
+ TEST_ASSERT(gnrc_pktbuf_is_sane());
+ TEST_ASSERT(gnrc_pktbuf_is_empty());
+ _check_vrbe_values(vrbe, mhr_len, false, 0);
+ /* offset changed according to offset_diff */
+ TEST_ASSERT_EQUAL_INT(
+ sixlowpan_sfr_rfrag_get_offset((sixlowpan_sfr_rfrag_t *)&_test_nth_frag) +
+ TEST_OFFSET_DIFF,
+ sixlowpan_sfr_rfrag_get_offset((sixlowpan_sfr_rfrag_t *)&_target_buf[mhr_len])
+ );
+ TEST_ASSERT_MESSAGE(
+ memcmp(&_test_nth_frag[TEST_NTH_FRAG_PAYLOAD_POS],
+ &_target_buf[mhr_len + sizeof(sixlowpan_sfr_rfrag_t)],
+ TEST_NTH_FRAG_SIZE) == 0,
+ "unexpected forwarded packet payload"
+ );
+}
+
+static void test_sixlo_recv_fwd__nth_frag__no_vrbe(void)
+{
+ gnrc_pktsnip_t *frag;
+ size_t mhr_len;
+
+ TEST_ASSERT_NOT_NULL(
+ (frag = _create_recv_frag(_test_nth_frag, sizeof(_test_nth_frag)))
+ );
+ netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev,
+ _mock_netdev_send);
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ frag));
+ /* expect abort ACK, see
+ * https://tools.ietf.org/html/rfc8931#section-6.1.2 */
+ TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_nth_frag))));
+ /* reassembly buffer remains empty */
+ TEST_ASSERT_NULL(_first_non_empty_rbuf());
+ /* and VRB too */
+ TEST_ASSERT_NULL(gnrc_sixlowpan_frag_vrb_get(
+ _vrbe_base.src, _vrbe_base.src_len, _vrbe_base.tag
+ ));
+ _check_abort_ack(mhr_len, _vrbe_base.tag);
+ TEST_ASSERT(gnrc_pktbuf_is_sane());
+ TEST_ASSERT(gnrc_pktbuf_is_empty());
+
+}
+
+static void test_sixlo_recv_fwd__nth_frag__duplicate(void)
+{
+ gnrc_sixlowpan_frag_vrb_t *vrbe;
+ gnrc_pktsnip_t *frag;
+
+ TEST_ASSERT_NOT_NULL(
+ (frag = _create_recv_frag(_test_nth_frag, sizeof(_test_nth_frag)))
+ );
+ TEST_ASSERT_NOT_NULL(
+ (vrbe = gnrc_sixlowpan_frag_vrb_add(&_vrbe_base, _mock_netif,
+ _rem_l2, sizeof(_rem_l2)))
+ );
+ netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev,
+ _mock_netdev_send);
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ frag));
+ TEST_ASSERT(_wait_for_packet(sizeof(_test_nth_frag)));
+ /* reassembly buffer remains empty */
+ TEST_ASSERT_NULL(_first_non_empty_rbuf());
+ /* VRB entry should not have been removed */
+ TEST_ASSERT_NOT_NULL(gnrc_sixlowpan_frag_vrb_get(_vrbe_base.src,
+ _vrbe_base.src_len,
+ _vrbe_base.tag));
+
+ /* generate and receive duplicate */
+ TEST_ASSERT_NOT_NULL(
+ (frag = _create_recv_frag(_test_nth_frag, sizeof(_test_nth_frag)))
+ );
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ frag));
+ /* with SFR the forwarder does not care about duplicates and just forwards
+ * them as well */
+ _target_buf_len = 0;
+ TEST_ASSERT(_wait_for_packet(sizeof(_test_nth_frag)));
+ /* reassembly buffer remains empty */
+ TEST_ASSERT_NULL(_first_non_empty_rbuf());
+ /* VRB entry should not have been removed */
+ TEST_ASSERT_NOT_NULL(gnrc_sixlowpan_frag_vrb_get(_vrbe_base.src,
+ _vrbe_base.src_len,
+ _vrbe_base.tag));
+}
+
+static void test_sixlo_recv_fwd__nth_frag__overlap(void)
+{
+ gnrc_sixlowpan_frag_vrb_t *vrbe;
+ gnrc_pktsnip_t *frag;
+ sixlowpan_sfr_rfrag_t *frag_hdr;
+
+ TEST_ASSERT_NOT_NULL(
+ (frag = _create_recv_frag(_test_nth_frag, sizeof(_test_nth_frag)))
+ );
+ TEST_ASSERT_NOT_NULL(
+ (vrbe = gnrc_sixlowpan_frag_vrb_add(&_vrbe_base, _mock_netif,
+ _rem_l2, sizeof(_rem_l2)))
+ );
+ netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev,
+ _mock_netdev_send);
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ frag));
+ TEST_ASSERT(_wait_for_packet(sizeof(_test_nth_frag)));
+ /* reassembly buffer remains empty */
+ TEST_ASSERT_NULL(_first_non_empty_rbuf());
+ /* rest was already tested */
+
+ /* generate and receive overlapping fragment */
+ TEST_ASSERT_NOT_NULL(
+ (frag = _create_recv_frag(_test_nth_frag, sizeof(_test_nth_frag)))
+ );
+ frag_hdr = frag->data;
+ /* move offset to simulate overlap*/
+ sixlowpan_sfr_rfrag_set_offset(frag_hdr,
+ sixlowpan_sfr_rfrag_get_offset(frag_hdr) - 1);
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ frag));
+ _target_buf_len = 0;
+ /* should time out */
+ TEST_ASSERT(_wait_for_packet(sizeof(_test_nth_frag)));
+ /* with SFR the forwarder does not care about overlapping fragments and just
+ * forwards them as well */
+ TEST_ASSERT(gnrc_pktbuf_is_sane());
+ TEST_ASSERT(gnrc_pktbuf_is_empty());
+ /* reassembly buffer remains empty */
+ TEST_ASSERT_NULL(_first_non_empty_rbuf());
+ /* VRB entry should not have been removed */
+ TEST_ASSERT_NOT_NULL(gnrc_sixlowpan_frag_vrb_get(_vrbe_base.src,
+ _vrbe_base.src_len,
+ _vrbe_base.tag));
+ /* rest was already tested */
+}
+
+static void test_sixlo_recv_fwd__frag__too_short(void)
+{
+ gnrc_pktsnip_t *frag;
+
+ TEST_ASSERT_NOT_NULL(
+ (frag = _create_recv_frag(_test_1st_frag_comp,
+ sizeof(sixlowpan_sfr_rfrag_t) - 1))
+ );
+ netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev,
+ _mock_netdev_send);
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ frag));
+ /* should be forwarded */
+ TEST_ASSERT_EQUAL_INT(0, _wait_for_packet(sizeof(_test_abort_frag)));
+ /* reassembly buffer remains empty */
+ TEST_ASSERT_NULL(_first_non_empty_rbuf());
+ /* and VRB remains empty */
+ TEST_ASSERT_NULL(gnrc_sixlowpan_frag_vrb_get(
+ _vrbe_base.src, _vrbe_base.src_len, _vrbe_base.tag
+ ));
+ TEST_ASSERT(gnrc_pktbuf_is_sane());
+ TEST_ASSERT(gnrc_pktbuf_is_empty());
+}
+
+static void test_sixlo_recv_fwd__ack(void)
+{
+ gnrc_sixlowpan_frag_vrb_t *vrbe;
+ gnrc_pktsnip_t *ack;
+ sixlowpan_sfr_ack_t *ack_hdr;
+ size_t mhr_len;
+
+ TEST_ASSERT_NOT_NULL(
+ (ack = _create_recv_ack(_test_ack, sizeof(_test_ack)))
+ );
+ TEST_ASSERT_NOT_NULL(
+ (vrbe = gnrc_sixlowpan_frag_vrb_add(&_vrbe_base, _mock_netif,
+ _rem_l2, sizeof(_rem_l2)))
+ );
+ vrbe->in_netif = _mock_netif;
+ ack_hdr = ack->data;
+ ack_hdr->base.tag = vrbe->out_tag;
+ netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev,
+ _mock_netdev_send);
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ ack));
+ TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_ack))));
+ /* reassembly buffer remains empty */
+ TEST_ASSERT_NULL(_first_non_empty_rbuf());
+ /* and VRB entry still exist */
+ TEST_ASSERT_NOT_NULL(gnrc_sixlowpan_frag_vrb_get(
+ _vrbe_base.src, _vrbe_base.src_len, _vrbe_base.tag
+ ));
+ _check_ack(mhr_len, _vrbe_base.tag, &_test_ack[2]);
+ TEST_ASSERT(gnrc_pktbuf_is_sane());
+ TEST_ASSERT(gnrc_pktbuf_is_empty());
+}
+
+static void test_sixlo_recv_fwd__NULL_ack(void)
+{
+ gnrc_sixlowpan_frag_vrb_t *vrbe;
+ gnrc_pktsnip_t *ack;
+ sixlowpan_sfr_ack_t *ack_hdr;
+ size_t mhr_len;
+
+ TEST_ASSERT_NOT_NULL(
+ (ack = _create_recv_ack(_test_ack, sizeof(_test_ack)))
+ );
+ TEST_ASSERT_NOT_NULL(
+ (vrbe = gnrc_sixlowpan_frag_vrb_add(&_vrbe_base, _mock_netif,
+ _rem_l2, sizeof(_rem_l2)))
+ );
+ vrbe->in_netif = _mock_netif;
+ ack_hdr = ack->data;
+ ack_hdr->base.tag = vrbe->out_tag;
+ /* set ACK bitmap to NULL */
+ memset(ack_hdr->bitmap, 0, sizeof(ack_hdr->bitmap));
+ netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev,
+ _mock_netdev_send);
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ ack));
+ TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_ack))));
+ /* reassembly buffer remains empty */
+ TEST_ASSERT_NULL(_first_non_empty_rbuf());
+ /* and VRB entry was removed */
+ TEST_ASSERT_NULL(gnrc_sixlowpan_frag_vrb_get(
+ _vrbe_base.src, _vrbe_base.src_len, _vrbe_base.tag
+ ));
+ /* NULL bitmap == abort ACK */
+ _check_abort_ack(mhr_len, _vrbe_base.tag);
+ TEST_ASSERT(gnrc_pktbuf_is_sane());
+ TEST_ASSERT(gnrc_pktbuf_is_empty());
+}
+
+static void test_sixlo_recv_fwd__FULL_ack(void)
+{
+ const uint8_t full_bitmap[] = { 0xff, 0xff, 0xff, 0xff };
+ gnrc_sixlowpan_frag_vrb_t *vrbe;
+ gnrc_pktsnip_t *ack;
+ sixlowpan_sfr_ack_t *ack_hdr;
+ size_t mhr_len;
+
+ TEST_ASSERT_NOT_NULL(
+ (ack = _create_recv_ack(_test_ack, sizeof(_test_ack)))
+ );
+ TEST_ASSERT_NOT_NULL(
+ (vrbe = gnrc_sixlowpan_frag_vrb_add(&_vrbe_base, _mock_netif,
+ _rem_l2, sizeof(_rem_l2)))
+ );
+ vrbe->in_netif = _mock_netif;
+ ack_hdr = ack->data;
+ ack_hdr->base.tag = vrbe->out_tag;
+ /* set ACK bitmap to NULL */
+ memcpy(ack_hdr->bitmap, full_bitmap, sizeof(ack_hdr->bitmap));
+ netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev,
+ _mock_netdev_send);
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ ack));
+ TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_ack))));
+ /* reassembly buffer remains empty */
+ TEST_ASSERT_NULL(_first_non_empty_rbuf());
+ /* and VRB entry was removed */
+ TEST_ASSERT_NULL(gnrc_sixlowpan_frag_vrb_get(
+ _vrbe_base.src, _vrbe_base.src_len, _vrbe_base.tag
+ ));
+ _check_ack(mhr_len, _vrbe_base.tag, full_bitmap);
+ TEST_ASSERT(gnrc_pktbuf_is_sane());
+ TEST_ASSERT(gnrc_pktbuf_is_empty());
+}
+
+static void test_sixlo_recv_fwd__ack__no_vrbe(void)
+{
+ gnrc_pktsnip_t *ack;
+
+ TEST_ASSERT_NOT_NULL(
+ (ack = _create_recv_ack(_test_ack, sizeof(_test_ack)))
+ );
+ netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev,
+ _mock_netdev_send);
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ ack));
+ /*
+ * https://tools.ietf.org/html/rfc8931#section-6.2
+ * > If the Reverse LSP is not found, the router MUST silently drop the
+ * > RFRAG-ACK message.
+ */
+ TEST_ASSERT_EQUAL_INT(0, _wait_for_packet(sizeof(_test_ack)));
+ /* reassembly buffer remains empty */
+ TEST_ASSERT_NULL(_first_non_empty_rbuf());
+ /* and VRB remains empty */
+ TEST_ASSERT_NULL(gnrc_sixlowpan_frag_vrb_get(
+ _vrbe_base.src, _vrbe_base.src_len, _vrbe_base.tag
+ ));
+ TEST_ASSERT(gnrc_pktbuf_is_sane());
+ TEST_ASSERT(gnrc_pktbuf_is_empty());
+}
+
+static void test_sixlo_recv_fwd__ack__too_short(void)
+{
+ gnrc_pktsnip_t *frag;
+
+ TEST_ASSERT_NOT_NULL(
+ (frag = _create_recv_frag(_test_ack,
+ sizeof(sixlowpan_sfr_ack_t) - 1))
+ );
+ netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev,
+ _mock_netdev_send);
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ frag));
+ /* should be forwarded */
+ TEST_ASSERT_EQUAL_INT(0, _wait_for_packet(sizeof(_test_abort_frag)));
+ /* reassembly buffer remains empty */
+ TEST_ASSERT_NULL(_first_non_empty_rbuf());
+ /* and VRB remains empty */
+ TEST_ASSERT_NULL(gnrc_sixlowpan_frag_vrb_get(
+ _vrbe_base.src, _vrbe_base.src_len, _vrbe_base.tag
+ ));
+ TEST_ASSERT(gnrc_pktbuf_is_sane());
+ TEST_ASSERT(gnrc_pktbuf_is_empty());
+}
+
+static void test_sixlo_recv_ep__1st_frag_uncomp(void)
+{
+ const gnrc_sixlowpan_frag_rb_t *rbuf;
+ gnrc_pktsnip_t *frag;
+
+ TEST_ASSERT_NOT_NULL(
+ (frag = _create_recv_frag(_test_1st_frag_uncomp,
+ sizeof(_test_1st_frag_uncomp)))
+ );
+ /* configure interface to be endpoint of route */
+ TEST_ASSERT_EQUAL_INT(sizeof(ipv6_addr_t),
+ _add_dst(&_rem_gb, REM_GB_PFX_LEN));
+ netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev,
+ _mock_netdev_send);
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ frag));
+ /* shouldn't trigger any sending */
+ TEST_ASSERT_EQUAL_INT(0, _wait_for_packet(sizeof(_test_1st_frag_uncomp)));
+ /* normal reassembly should have started */
+ /* reassembly buffer entry should have been created */
+ TEST_ASSERT_NOT_NULL((rbuf = _first_non_empty_rbuf()));
+ /* and VRB remains empty */
+ TEST_ASSERT_NULL(gnrc_sixlowpan_frag_vrb_get(
+ _vrbe_base.src, _vrbe_base.src_len, _vrbe_base.tag
+ ));
+ TEST_ASSERT_EQUAL_INT(_vrbe_base.datagram_size, rbuf->pkt->size);
+ /* check if IPv6 packet is started in reassembly buffer */
+ TEST_ASSERT_EQUAL_INT(0x60, ((uint8_t *)rbuf->pkt->data)[0] & 0xf0);
+ /* check `received` bitmap (used for ACKs) */
+ TEST_ASSERT_EQUAL_INT(0x80, rbuf->received[0]);
+ TEST_ASSERT_EQUAL_INT(0x00, rbuf->received[1]);
+ TEST_ASSERT_EQUAL_INT(0x00, rbuf->received[2]);
+ TEST_ASSERT_EQUAL_INT(0x00, rbuf->received[3]);
+ gnrc_pktbuf_release(rbuf->pkt);
+ TEST_ASSERT(gnrc_pktbuf_is_sane());
+ TEST_ASSERT(gnrc_pktbuf_is_empty());
+}
+
+static void test_sixlo_recv_ep__1st_frag_uncomp__req_ack(void)
+{
+ const gnrc_sixlowpan_frag_rb_t *rbuf;
+ gnrc_pktsnip_t *frag;
+ size_t mhr_len;
+ BITFIELD(exp_bitmap, 32U) = { 0 };
+
+ TEST_ASSERT_NOT_NULL(
+ (frag = _create_recv_frag(_test_1st_frag_uncomp,
+ sizeof(_test_1st_frag_uncomp)))
+ );
+ /* set ACK Req flag in RFRAG header */
+ sixlowpan_sfr_rfrag_set_ack_req(frag->data);
+ /* configure interface to be endpoint of route */
+ TEST_ASSERT_EQUAL_INT(sizeof(ipv6_addr_t),
+ _add_dst(&_rem_gb, REM_GB_PFX_LEN));
+ netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev,
+ _mock_netdev_send);
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ frag));
+ /* expect RFRAG-ACK */
+ TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(sixlowpan_sfr_ack_t))));
+ /* only sequence number 0 (first fragment) should be ACK'd */
+ bf_set(exp_bitmap, 0);
+ _check_ack(mhr_len, _vrbe_base.tag, exp_bitmap);
+ /* normal reassembly should have started */
+ /* reassembly buffer entry should have been created */
+ TEST_ASSERT_NOT_NULL((rbuf = _first_non_empty_rbuf()));
+ /* and VRB remains empty */
+ TEST_ASSERT_NULL(gnrc_sixlowpan_frag_vrb_get(
+ _vrbe_base.src, _vrbe_base.src_len, _vrbe_base.tag
+ ));
+ TEST_ASSERT_EQUAL_INT(_vrbe_base.datagram_size, rbuf->pkt->size);
+ /* check if IPv6 packet is started in reassembly buffer */
+ TEST_ASSERT_EQUAL_INT(0x60, ((uint8_t *)rbuf->pkt->data)[0] & 0xf0);
+ /* check `received` bitmap (used for ACKs) */
+ TEST_ASSERT_EQUAL_INT(0x80, rbuf->received[0]);
+ TEST_ASSERT_EQUAL_INT(0x00, rbuf->received[1]);
+ TEST_ASSERT_EQUAL_INT(0x00, rbuf->received[2]);
+ TEST_ASSERT_EQUAL_INT(0x00, rbuf->received[3]);
+ gnrc_pktbuf_release(rbuf->pkt);
+ TEST_ASSERT(gnrc_pktbuf_is_sane());
+ TEST_ASSERT(gnrc_pktbuf_is_empty());
+}
+
+static void test_sixlo_recv_ep__1st_frag_uncomp__after_nth_frag(void)
+{
+ const gnrc_sixlowpan_frag_rb_t *rbuf;
+ gnrc_pktsnip_t *frag;
+ size_t mhr_len;
+
+ TEST_ASSERT_NOT_NULL(
+ (frag = _create_recv_frag(_test_nth_frag, sizeof(_test_nth_frag)))
+ );
+ /* configure interface to be endpoint of route */
+ TEST_ASSERT_EQUAL_INT(sizeof(ipv6_addr_t),
+ _add_dst(&_rem_gb, REM_GB_PFX_LEN));
+ netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev,
+ _mock_netdev_send);
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ frag));
+
+ /* expect abort ACK, see
+ * https://tools.ietf.org/html/rfc8931#section-6.1.2 */
+ TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_nth_frag))));
+ /* no reassembly buffer entry */
+ TEST_ASSERT_NULL(_first_non_empty_rbuf());
+ /* and VRB remains empty */
+ TEST_ASSERT_NULL(gnrc_sixlowpan_frag_vrb_get(
+ _vrbe_base.src, _vrbe_base.src_len, _vrbe_base.tag
+ ));
+ _check_abort_ack(mhr_len, _vrbe_base.tag);
+ TEST_ASSERT_NOT_NULL(
+ (frag = _create_recv_frag(_test_1st_frag_uncomp,
+ sizeof(_test_1st_frag_uncomp)))
+ );
+ _target_buf_len = 0;
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ frag));
+ /* shouldn't trigger any sending */
+ TEST_ASSERT_EQUAL_INT(0, _wait_for_packet(sizeof(_test_1st_frag_uncomp)));
+ /* normal reassembly should have started */
+ /* reassembly buffer entry should have been created */
+ TEST_ASSERT_NOT_NULL((rbuf = _first_non_empty_rbuf()));
+ /* and VRB remains empty */
+ TEST_ASSERT_NULL(gnrc_sixlowpan_frag_vrb_get(
+ _vrbe_base.src, _vrbe_base.src_len, _vrbe_base.tag
+ ));
+ TEST_ASSERT_EQUAL_INT(_vrbe_base.datagram_size, rbuf->pkt->size);
+ /* check if IPv6 packet is started in reassembly buffer */
+ TEST_ASSERT_EQUAL_INT(0x60, ((uint8_t *)rbuf->pkt->data)[0] & 0xf0);
+ /* check `received` bitmap (used for ACKs) */
+ TEST_ASSERT_EQUAL_INT(0x80, rbuf->received[0]);
+ TEST_ASSERT_EQUAL_INT(0x00, rbuf->received[1]);
+ TEST_ASSERT_EQUAL_INT(0x00, rbuf->received[2]);
+ TEST_ASSERT_EQUAL_INT(0x00, rbuf->received[3]);
+ gnrc_pktbuf_release(rbuf->pkt);
+ TEST_ASSERT(gnrc_pktbuf_is_sane());
+ TEST_ASSERT(gnrc_pktbuf_is_empty());
+}
+
+static void test_sixlo_recv_ep__1st_frag_comp(void)
+{
+ const gnrc_sixlowpan_frag_rb_t *rbuf;
+ gnrc_pktsnip_t *frag;
+
+ TEST_ASSERT_NOT_NULL(
+ (frag = _create_recv_frag(_test_1st_frag_comp,
+ sizeof(_test_1st_frag_comp)))
+ );
+ /* configure interface to be endpoint of route */
+ TEST_ASSERT_EQUAL_INT(sizeof(ipv6_addr_t),
+ _add_dst(&_rem_gb, REM_GB_PFX_LEN));
+ netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev,
+ _mock_netdev_send);
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ frag));
+ /* shouldn't trigger any sending */
+ TEST_ASSERT_EQUAL_INT(0, _wait_for_packet(sizeof(_test_1st_frag_comp)));
+ /* normal reassembly should have started */
+ /* reassembly buffer entry should have been created */
+ TEST_ASSERT_NOT_NULL((rbuf = _first_non_empty_rbuf()));
+ /* and VRB remains empty */
+ TEST_ASSERT_NULL(gnrc_sixlowpan_frag_vrb_get(
+ _vrbe_base.src, _vrbe_base.src_len, _vrbe_base.tag
+ ));
+ TEST_ASSERT_EQUAL_INT(_vrbe_base.datagram_size, rbuf->pkt->size);
+ /* check if IPv6 packet is started in reassembly buffer */
+ TEST_ASSERT_EQUAL_INT(0x60, ((uint8_t *)rbuf->pkt->data)[0] & 0xf0);
+ /* check `received` bitmap (used for ACKs) */
+ TEST_ASSERT_EQUAL_INT(0x80, rbuf->received[0]);
+ TEST_ASSERT_EQUAL_INT(0x00, rbuf->received[1]);
+ TEST_ASSERT_EQUAL_INT(0x00, rbuf->received[2]);
+ TEST_ASSERT_EQUAL_INT(0x00, rbuf->received[3]);
+ gnrc_pktbuf_release(rbuf->pkt);
+ TEST_ASSERT(gnrc_pktbuf_is_sane());
+ TEST_ASSERT(gnrc_pktbuf_is_empty());
+}
+
+static void test_sixlo_recv_ep__1st_frag_comp__after_nth_frag(void)
+{
+ const gnrc_sixlowpan_frag_rb_t *rbuf;
+ gnrc_pktsnip_t *frag;
+ size_t mhr_len;
+
+ TEST_ASSERT_NOT_NULL(
+ (frag = _create_recv_frag(_test_nth_frag, sizeof(_test_nth_frag)))
+ );
+ /* configure interface to be endpoint of route */
+ TEST_ASSERT_EQUAL_INT(sizeof(ipv6_addr_t),
+ _add_dst(&_rem_gb, REM_GB_PFX_LEN));
+ netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev,
+ _mock_netdev_send);
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ frag));
+
+ /* expect abort ACK, see
+ * https://tools.ietf.org/html/rfc8931#section-6.1.2 */
+ TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_nth_frag))));
+ /* no reassembly buffer entry */
+ TEST_ASSERT_NULL(_first_non_empty_rbuf());
+ /* and VRB remains empty */
+ TEST_ASSERT_NULL(gnrc_sixlowpan_frag_vrb_get(
+ _vrbe_base.src, _vrbe_base.src_len, _vrbe_base.tag
+ ));
+ _check_abort_ack(mhr_len, _vrbe_base.tag);
+ TEST_ASSERT_NOT_NULL(
+ (frag = _create_recv_frag(_test_1st_frag_comp,
+ sizeof(_test_1st_frag_comp)))
+ );
+ _target_buf_len = 0;
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ frag));
+ /* shouldn't trigger any sending */
+ TEST_ASSERT_EQUAL_INT(0, _wait_for_packet(sizeof(_test_1st_frag_comp)));
+ /* normal reassembly should have started */
+ /* reassembly buffer entry should have been created */
+ TEST_ASSERT_NOT_NULL((rbuf = _first_non_empty_rbuf()));
+ /* and VRB remains empty */
+ TEST_ASSERT_NULL(gnrc_sixlowpan_frag_vrb_get(
+ _vrbe_base.src, _vrbe_base.src_len, _vrbe_base.tag
+ ));
+ TEST_ASSERT_EQUAL_INT(_vrbe_base.datagram_size, rbuf->pkt->size);
+ /* check if IPv6 packet is started in reassembly buffer */
+ TEST_ASSERT_EQUAL_INT(0x60, ((uint8_t *)rbuf->pkt->data)[0] & 0xf0);
+ /* check `received` bitmap (used for ACKs) */
+ TEST_ASSERT_EQUAL_INT(0x80, rbuf->received[0]);
+ TEST_ASSERT_EQUAL_INT(0x00, rbuf->received[1]);
+ TEST_ASSERT_EQUAL_INT(0x00, rbuf->received[2]);
+ TEST_ASSERT_EQUAL_INT(0x00, rbuf->received[3]);
+ gnrc_pktbuf_release(rbuf->pkt);
+ TEST_ASSERT(gnrc_pktbuf_is_sane());
+ TEST_ASSERT(gnrc_pktbuf_is_empty());
+}
+
+static void test_sixlo_recv_ep__1st_frag_abort(void)
+{
+ const gnrc_sixlowpan_frag_rb_t *rbuf;
+ gnrc_pktsnip_t *frag;
+
+ TEST_ASSERT_NOT_NULL(
+ (frag = _create_recv_frag(_test_1st_frag_comp,
+ sizeof(_test_1st_frag_comp)))
+ );
+ /* configure interface to be endpoint of route */
+ TEST_ASSERT_EQUAL_INT(sizeof(ipv6_addr_t),
+ _add_dst(&_rem_gb, REM_GB_PFX_LEN));
+ netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev,
+ _mock_netdev_send);
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ frag));
+ /* shouldn't trigger any sending */
+ TEST_ASSERT_EQUAL_INT(0, _wait_for_packet(sizeof(_test_1st_frag_comp)));
+ /* normal reassembly should have started */
+ /* reassembly buffer entry should have been created */
+ TEST_ASSERT_NOT_NULL((rbuf = _first_non_empty_rbuf()));
+ /* and VRB remains empty */
+ TEST_ASSERT_NULL(gnrc_sixlowpan_frag_vrb_get(
+ _vrbe_base.src, _vrbe_base.src_len, _vrbe_base.tag
+ ));
+ TEST_ASSERT_NOT_NULL(
+ (frag = _create_recv_frag(_test_abort_frag,
+ sizeof(_test_abort_frag)))
+ );
+ /* expect abort ACK, see
+ * https://tools.ietf.org/html/rfc8931#section-6.3 */
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ frag));
+ _target_buf_len = 0;
+ TEST_ASSERT_EQUAL_INT(0, _wait_for_packet(sizeof(sixlowpan_sfr_ack_t)));
+ /* reassembly buffer entry was deleted */
+ TEST_ASSERT_NULL(_first_non_empty_rbuf());
+ /* and VRB remains empty */
+ TEST_ASSERT_NULL(gnrc_sixlowpan_frag_vrb_get(
+ _vrbe_base.src, _vrbe_base.src_len, _vrbe_base.tag
+ ));
+ TEST_ASSERT(gnrc_pktbuf_is_sane());
+ TEST_ASSERT(gnrc_pktbuf_is_empty());
+}
+
+static void test_sixlo_recv_ep__1st_frag_abort__req_ack(void)
+{
+ const gnrc_sixlowpan_frag_rb_t *rbuf;
+ gnrc_pktsnip_t *frag;
+ size_t mhr_len;
+
+ TEST_ASSERT_NOT_NULL(
+ (frag = _create_recv_frag(_test_1st_frag_comp,
+ sizeof(_test_1st_frag_comp)))
+ );
+ /* configure interface to be endpoint of route */
+ TEST_ASSERT_EQUAL_INT(sizeof(ipv6_addr_t),
+ _add_dst(&_rem_gb, REM_GB_PFX_LEN));
+ netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev,
+ _mock_netdev_send);
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ frag));
+ /* shouldn't trigger any sending */
+ TEST_ASSERT_EQUAL_INT(0, _wait_for_packet(sizeof(_test_1st_frag_comp)));
+ /* normal reassembly should have started */
+ /* reassembly buffer entry should have been created */
+ TEST_ASSERT_NOT_NULL((rbuf = _first_non_empty_rbuf()));
+ /* and VRB remains empty */
+ TEST_ASSERT_NULL(gnrc_sixlowpan_frag_vrb_get(
+ _vrbe_base.src, _vrbe_base.src_len, _vrbe_base.tag
+ ));
+ TEST_ASSERT_NOT_NULL(
+ (frag = _create_recv_frag(_test_abort_frag,
+ sizeof(_test_abort_frag)))
+ );
+ /* set ACK request flag */
+ sixlowpan_sfr_rfrag_set_ack_req(frag->data);
+ /* expect abort ACK, see
+ * https://tools.ietf.org/html/rfc8931#section-6.3 */
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ frag));
+ TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(sixlowpan_sfr_ack_t))));
+ /* reassembly buffer entry was deleted */
+ TEST_ASSERT_NULL(_first_non_empty_rbuf());
+ /* and VRB remains empty */
+ TEST_ASSERT_NULL(gnrc_sixlowpan_frag_vrb_get(
+ _vrbe_base.src, _vrbe_base.src_len, _vrbe_base.tag
+ ));
+ _check_abort_ack(mhr_len, _vrbe_base.tag);
+ TEST_ASSERT(gnrc_pktbuf_is_sane());
+ TEST_ASSERT(gnrc_pktbuf_is_empty());
+}
+
+static void test_sixlo_recv_ep__complete_datagram(void)
+{
+ const gnrc_sixlowpan_frag_rb_t *rbuf;
+ gnrc_netreg_entry_t netreg = GNRC_NETREG_ENTRY_INIT_PID(
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ thread_getpid()
+ );
+ gnrc_pktsnip_t *frag;
+ size_t mhr_len;
+ BITFIELD(exp_bitmap, 32U) = { 0 };
+ uint8_t exp_seq;
+
+ /* configure interface to be endpoint of route */
+ TEST_ASSERT_EQUAL_INT(sizeof(ipv6_addr_t),
+ _add_dst(&_rem_gb, REM_GB_PFX_LEN));
+ netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev,
+ _mock_netdev_send);
+ /* ==== FIRST FRAGMENT ==== */
+ TEST_ASSERT_NOT_NULL(
+ (frag = _create_recv_frag(_test_send_frag1,
+ sizeof(_test_send_frag1)))
+ );
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ frag));
+ /* shouldn't trigger any sending */
+ TEST_ASSERT_EQUAL_INT(0, _wait_for_packet(sizeof(_test_send_frag1)));
+ /* normal reassembly should have started */
+ /* reassembly buffer entry should have been created */
+ TEST_ASSERT_NOT_NULL((rbuf = _first_non_empty_rbuf()));
+ /* and VRB remains empty */
+ TEST_ASSERT_NULL(gnrc_sixlowpan_frag_vrb_get(
+ _vrbe_base.src, _vrbe_base.src_len, TEST_SEND_DATAGRAM_TAG
+ ));
+ TEST_ASSERT_EQUAL_INT(sizeof(_test_send_ipv6) + sizeof(_test_send_icmpv6),
+ rbuf->pkt->size);
+ /* check if IPv6 packet is started in reassembly buffer */
+ TEST_ASSERT_EQUAL_INT(0x60, ((uint8_t *)rbuf->pkt->data)[0] & 0xf0);
+ /* check `received` bitmap (used for ACKs) */
+ TEST_ASSERT_EQUAL_INT(0x80, rbuf->received[0]);
+ TEST_ASSERT_EQUAL_INT(0x00, rbuf->received[1]);
+ TEST_ASSERT_EQUAL_INT(0x00, rbuf->received[2]);
+ TEST_ASSERT_EQUAL_INT(0x00, rbuf->received[3]);
+
+ /* ==== SECOND FRAGMENT ==== */
+ TEST_ASSERT_NOT_NULL(
+ (frag = _create_recv_frag(_test_send_frag2,
+ sizeof(_test_send_frag2)))
+ );
+ /* set ACK Req flag in RFRAG header */
+ sixlowpan_sfr_rfrag_set_ack_req(frag->data);
+ exp_seq = sixlowpan_sfr_rfrag_get_seq(frag->data);
+ _target_buf_len = 0;
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ frag));
+ /* expect RFRAG-ACK */
+ TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_send_frag2))));
+ bf_set(exp_bitmap, 0); /* first fragment was received */
+ bf_set(exp_bitmap, exp_seq); /* and second fragment */
+ _check_ack(mhr_len, TEST_SEND_DATAGRAM_TAG, exp_bitmap);
+ /* reassembly buffer entry should still exist */
+ TEST_ASSERT_NOT_NULL((rbuf = _first_non_empty_rbuf()));
+ /* check `received` bitmap (used for ACKs) */
+ TEST_ASSERT_EQUAL_INT(0xc0, rbuf->received[0]);
+ TEST_ASSERT_EQUAL_INT(0x00, rbuf->received[1]);
+ TEST_ASSERT_EQUAL_INT(0x00, rbuf->received[2]);
+ TEST_ASSERT_EQUAL_INT(0x00, rbuf->received[3]);
+
+ gnrc_netreg_register(GNRC_NETTYPE_IPV6, &netreg);
+ /* ==== THIRD FRAGMENT ==== */
+ TEST_ASSERT_NOT_NULL(
+ (frag = _create_recv_frag(_test_send_frag3,
+ sizeof(_test_send_frag3)))
+ );
+ _target_buf_len = 0;
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ frag));
+ /* expect RFRAG-ACK */
+ TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_send_frag3))));
+ /* reassembly buffer should have been removed */
+ TEST_ASSERT_NULL(_first_non_empty_rbuf());
+ /* expect FULL bitmap, as the datagram is now complete */
+ memset(exp_bitmap, 0xff, sizeof(exp_bitmap));
+ _check_ack(mhr_len, TEST_SEND_DATAGRAM_TAG, exp_bitmap);
+
+ /* retry 5 times to receive then give up (IPv6 thread might send NDP
+ * messages to itself) */
+ for (unsigned i = 0; i < 5U; i++) {
+ msg_t msg;
+
+ if (xtimer_msg_receive_timeout(&msg, 1000) < 0) {
+ continue;
+ }
+ if (msg.type == GNRC_NETAPI_MSG_TYPE_RCV) {
+ gnrc_pktsnip_t *pkt;
+ uint8_t *data;
+
+ gnrc_netreg_unregister(GNRC_NETTYPE_IPV6, &netreg);
+ pkt = msg.content.ptr;
+ data = pkt->data;
+ TEST_ASSERT_EQUAL_INT(sizeof(_test_send_ipv6) +
+ sizeof(_test_send_icmpv6), pkt->size);
+ TEST_ASSERT_MESSAGE(
+ memcmp(data, _test_send_ipv6, sizeof(_test_send_ipv6)) == 0,
+ "Unexpected IPv6 header in reassembly"
+ );
+ TEST_ASSERT_MESSAGE(
+ memcmp(&data[sizeof(_test_send_ipv6)], _test_send_icmpv6,
+ sizeof(_test_send_icmpv6)) == 0,
+ "Unexpected ICMPv6 packet in reassembly"
+ );
+ gnrc_pktbuf_release(pkt);
+ break;
+ }
+ else if (msg.type == GNRC_NETAPI_MSG_TYPE_SND) {
+ /* release packet sent by network stack */
+ gnrc_pktbuf_release(msg.content.ptr);
+ }
+ }
+ xtimer_usleep(1000); /* give GNRC time to clean up */
+ TEST_ASSERT(gnrc_pktbuf_is_sane());
+ TEST_ASSERT(gnrc_pktbuf_is_empty());
+}
+
+static void test_sixlo_send(void)
+{
+ gnrc_sixlowpan_frag_fb_t *fbuf;
+ gnrc_pktsnip_t *pkt;
+ sixlowpan_sfr_rfrag_t *hdr;
+ size_t mhr_len;
+ uint8_t tag;
+
+ /* window is large enough to send all fragments at once */
+ TEST_ASSERT(3 <= CONFIG_GNRC_SIXLOWPAN_SFR_OPT_WIN_SIZE);
+ TEST_ASSERT_NOT_NULL((pkt = _create_send_datagram(false, true)));
+
+ netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev,
+ _mock_netdev_send);
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_send(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ pkt));
+ TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_send_frag1))));
+ /* tags are generated by the stack so don't check */
+ _check_send_frag1(mhr_len, false);
+ hdr = (sixlowpan_sfr_rfrag_t *)&_target_buf[mhr_len];
+ tag = hdr->base.tag;
+ _target_buf_len = 0;
+ TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_send_frag2))));
+ _check_send_frag2(mhr_len, false);
+ hdr = (sixlowpan_sfr_rfrag_t *)&_target_buf[mhr_len];
+ TEST_ASSERT_EQUAL_INT(tag, hdr->base.tag);
+ _target_buf_len = 0;
+ TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_send_frag3))));
+ /* last fragment should request an ACK */
+ _check_send_frag3(mhr_len, true);
+ hdr = (sixlowpan_sfr_rfrag_t *)&_target_buf[mhr_len];
+ TEST_ASSERT_EQUAL_INT(tag, hdr->base.tag);
+ for (unsigned i = 0; i < CONFIG_GNRC_SIXLOWPAN_SFR_FRAG_RETRIES; i++) {
+ /* fragmentation buffer still exists for re-sending */
+ TEST_ASSERT_NOT_NULL((fbuf = gnrc_sixlowpan_frag_fb_get_by_tag(tag)));
+ /* three fragments wait for potential re-sending */
+ TEST_ASSERT_EQUAL_INT(3U, clist_count(&fbuf->sfr.window));
+ _target_buf_len = 0;
+ _wait_arq_timeout(fbuf);
+ /* resend of fragment 3 */
+ TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_send_frag3))));
+ /* last fragment should request an ACK */
+ _check_send_frag3(mhr_len, true);
+ hdr = (sixlowpan_sfr_rfrag_t *)&_target_buf[mhr_len];
+ TEST_ASSERT_EQUAL_INT(tag, hdr->base.tag);
+ }
+ _target_buf_len = 0;
+ _wait_arq_timeout(fbuf);
+ /* should time out */
+ TEST_ASSERT_EQUAL_INT(0, _wait_for_packet(sizeof(_test_send_frag3)));
+ /* fragmentation buffer should have been deleted after
+ * CONFIG_GNRC_SIXLOWPAN_SFR_FRAG_RETRIES retries */
+ TEST_ASSERT_NULL(gnrc_sixlowpan_frag_fb_get_by_tag(tag));
+ TEST_ASSERT(gnrc_pktbuf_is_sane());
+ TEST_ASSERT(gnrc_pktbuf_is_empty());
+}
+
+static void test_sixlo_send__first_ackd(void)
+{
+ gnrc_sixlowpan_frag_fb_t *fbuf;
+ gnrc_pktsnip_t *pkt;
+ sixlowpan_sfr_rfrag_t *hdr;
+ sixlowpan_sfr_ack_t *ack_hdr;
+ size_t mhr_len;
+ uint8_t tag;
+ BITFIELD(ack_bitmap, 32U) = { 0 };
+
+ /* window is large enough to send all fragments at once */
+ TEST_ASSERT(3 <= CONFIG_GNRC_SIXLOWPAN_SFR_OPT_WIN_SIZE);
+ TEST_ASSERT_NOT_NULL((pkt = _create_send_datagram(false, true)));
+
+ netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev,
+ _mock_netdev_send);
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_send(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ pkt));
+ TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_send_frag1))));
+ /* tags are generated by the stack so don't check */
+ _check_send_frag1(mhr_len, false);
+ hdr = (sixlowpan_sfr_rfrag_t *)&_target_buf[mhr_len];
+ tag = hdr->base.tag;
+ /* simulate successful reception for this fragment */
+ bf_set(ack_bitmap, sixlowpan_sfr_rfrag_get_seq(hdr));
+ _target_buf_len = 0;
+ TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_send_frag2))));
+ _check_send_frag2(mhr_len, false);
+ hdr = (sixlowpan_sfr_rfrag_t *)&_target_buf[mhr_len];
+ TEST_ASSERT_EQUAL_INT(tag, hdr->base.tag);
+ _target_buf_len = 0;
+ TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_send_frag3))));
+ /* last fragment should request an ACK */
+ _check_send_frag3(mhr_len, true);
+ hdr = (sixlowpan_sfr_rfrag_t *)&_target_buf[mhr_len];
+ TEST_ASSERT_EQUAL_INT(tag, hdr->base.tag);
+ /* fragmentation buffer still exists for re-sending */
+ TEST_ASSERT_NOT_NULL((fbuf = gnrc_sixlowpan_frag_fb_get_by_tag(tag)));
+ /* three fragments wait for potential re-sending */
+ TEST_ASSERT_EQUAL_INT(3U, clist_count(&fbuf->sfr.window));
+ TEST_ASSERT_NOT_NULL(
+ (pkt = _create_recv_ack(_test_ack, sizeof(_test_ack)))
+ );
+ ack_hdr = pkt->data;
+ ack_hdr->base.tag = tag;
+ memcpy(ack_hdr->bitmap, ack_bitmap, sizeof(ack_hdr->bitmap));
+ _target_buf_len = 0;
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ pkt));
+ /* fragmentation buffer still exists for re-sending */
+ TEST_ASSERT_NOT_NULL((fbuf = gnrc_sixlowpan_frag_fb_get_by_tag(tag)));
+ /* two fragments wait for potential re-sending */
+ TEST_ASSERT_EQUAL_INT(2U, clist_count(&fbuf->sfr.window));
+ /* resend of fragment 2 (fragment 1 is not resent) */
+ TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_send_frag2))));
+ _check_send_frag2(mhr_len, false);
+ hdr = (sixlowpan_sfr_rfrag_t *)&_target_buf[mhr_len];
+ TEST_ASSERT_EQUAL_INT(tag, hdr->base.tag);
+ for (unsigned i = 0; i < CONFIG_GNRC_SIXLOWPAN_SFR_FRAG_RETRIES; i++) {
+ /* fragmentation buffer still exists for re-sending */
+ TEST_ASSERT_NOT_NULL((fbuf = gnrc_sixlowpan_frag_fb_get_by_tag(tag)));
+ /* two fragments wait for potential re-sending */
+ TEST_ASSERT_EQUAL_INT(2U, clist_count(&fbuf->sfr.window));
+ /* resend of fragment 3 */
+ TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_send_frag3))));
+ /* last fragment should request an ACK */
+ _check_send_frag3(mhr_len, true);
+ hdr = (sixlowpan_sfr_rfrag_t *)&_target_buf[mhr_len];
+ TEST_ASSERT_EQUAL_INT(tag, hdr->base.tag);
+ _target_buf_len = 0;
+ _wait_arq_timeout(fbuf);
+ }
+ /* should time out */
+ TEST_ASSERT_EQUAL_INT(0, _wait_for_packet(sizeof(_test_send_frag3)));
+ /* fragmentation buffer should have been deleted after
+ * CONFIG_GNRC_SIXLOWPAN_SFR_FRAG_RETRIES retries */
+ TEST_ASSERT_NULL(gnrc_sixlowpan_frag_fb_get_by_tag(tag));
+ TEST_ASSERT(gnrc_pktbuf_is_sane());
+ TEST_ASSERT(gnrc_pktbuf_is_empty());
+}
+
+static void test_sixlo_send__middle_ackd(void)
+{
+ gnrc_sixlowpan_frag_fb_t *fbuf;
+ gnrc_pktsnip_t *pkt;
+ sixlowpan_sfr_rfrag_t *hdr;
+ sixlowpan_sfr_ack_t *ack_hdr;
+ size_t mhr_len;
+ uint8_t tag;
+ BITFIELD(ack_bitmap, 32U) = { 0 };
+
+ /* window is large enough to send all fragments at once */
+ TEST_ASSERT(3 <= CONFIG_GNRC_SIXLOWPAN_SFR_OPT_WIN_SIZE);
+ TEST_ASSERT_NOT_NULL((pkt = _create_send_datagram(false, true)));
+
+ netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev,
+ _mock_netdev_send);
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_send(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ pkt));
+ TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_send_frag1))));
+ /* tags are generated by the stack so don't check */
+ _check_send_frag1(mhr_len, false);
+ hdr = (sixlowpan_sfr_rfrag_t *)&_target_buf[mhr_len];
+ tag = hdr->base.tag;
+ _target_buf_len = 0;
+ TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_send_frag2))));
+ _check_send_frag2(mhr_len, false);
+ hdr = (sixlowpan_sfr_rfrag_t *)&_target_buf[mhr_len];
+ TEST_ASSERT_EQUAL_INT(tag, hdr->base.tag);
+ /* simulate successful reception for this fragment */
+ bf_set(ack_bitmap, sixlowpan_sfr_rfrag_get_seq(hdr));
+ _target_buf_len = 0;
+ TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_send_frag3))));
+ /* last fragment should request an ACK */
+ _check_send_frag3(mhr_len, true);
+ hdr = (sixlowpan_sfr_rfrag_t *)&_target_buf[mhr_len];
+ TEST_ASSERT_EQUAL_INT(tag, hdr->base.tag);
+ /* fragmentation buffer still exists for re-sending */
+ TEST_ASSERT_NOT_NULL((fbuf = gnrc_sixlowpan_frag_fb_get_by_tag(tag)));
+ /* three fragments wait for potential re-sending */
+ TEST_ASSERT_EQUAL_INT(3U, clist_count(&fbuf->sfr.window));
+ TEST_ASSERT_NOT_NULL(
+ (pkt = _create_recv_ack(_test_ack, sizeof(_test_ack)))
+ );
+ ack_hdr = pkt->data;
+ ack_hdr->base.tag = tag;
+ memcpy(ack_hdr->bitmap, ack_bitmap, sizeof(ack_hdr->bitmap));
+ _target_buf_len = 0;
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ pkt));
+ /* fragmentation buffer still exists for re-sending */
+ TEST_ASSERT_NOT_NULL((fbuf = gnrc_sixlowpan_frag_fb_get_by_tag(tag)));
+ /* two fragments wait for potential re-sending */
+ TEST_ASSERT_EQUAL_INT(2U, clist_count(&fbuf->sfr.window));
+ /* resend of fragment 1 */
+ TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_send_frag1))));
+ _check_send_frag1(mhr_len, false);
+ hdr = (sixlowpan_sfr_rfrag_t *)&_target_buf[mhr_len];
+ TEST_ASSERT_EQUAL_INT(tag, hdr->base.tag);
+ /* should time out since fragment 2 is not resent */
+ TEST_ASSERT_EQUAL_INT(0, _wait_for_packet(sizeof(_test_send_frag2)));
+ for (unsigned i = 0; i < CONFIG_GNRC_SIXLOWPAN_SFR_FRAG_RETRIES; i++) {
+ /* fragmentation buffer still exists for re-sending */
+ TEST_ASSERT_NOT_NULL((fbuf = gnrc_sixlowpan_frag_fb_get_by_tag(tag)));
+ /* two fragments wait for potential re-sending */
+ TEST_ASSERT_EQUAL_INT(2U, clist_count(&fbuf->sfr.window));
+ /* resend of fragment 3 */
+ TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_send_frag3))));
+ /* last fragment should request an ACK */
+ _check_send_frag3(mhr_len, true);
+ hdr = (sixlowpan_sfr_rfrag_t *)&_target_buf[mhr_len];
+ TEST_ASSERT_EQUAL_INT(tag, hdr->base.tag);
+ _target_buf_len = 0;
+ _wait_arq_timeout(fbuf);
+ }
+ /* should time out */
+ TEST_ASSERT_EQUAL_INT(0, _wait_for_packet(sizeof(_test_send_frag3)));
+ /* fragmentation buffer should have been deleted after
+ * CONFIG_GNRC_SIXLOWPAN_SFR_FRAG_RETRIES retries */
+ TEST_ASSERT_NULL(gnrc_sixlowpan_frag_fb_get_by_tag(tag));
+ TEST_ASSERT(gnrc_pktbuf_is_sane());
+ TEST_ASSERT(gnrc_pktbuf_is_empty());
+}
+
+static void test_sixlo_send__last_ackd(void)
+{
+ gnrc_sixlowpan_frag_fb_t *fbuf;
+ gnrc_pktsnip_t *pkt;
+ sixlowpan_sfr_rfrag_t *hdr;
+ sixlowpan_sfr_ack_t *ack_hdr;
+ size_t mhr_len;
+ uint8_t tag;
+ BITFIELD(ack_bitmap, 32U) = { 0 };
+
+ /* window is large enough to send all fragments at once */
+ TEST_ASSERT(3 <= CONFIG_GNRC_SIXLOWPAN_SFR_OPT_WIN_SIZE);
+ TEST_ASSERT_NOT_NULL((pkt = _create_send_datagram(false, true)));
+
+ netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev,
+ _mock_netdev_send);
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_send(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ pkt));
+ TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_send_frag1))));
+ /* tags are generated by the stack so don't check */
+ _check_send_frag1(mhr_len, false);
+ hdr = (sixlowpan_sfr_rfrag_t *)&_target_buf[mhr_len];
+ tag = hdr->base.tag;
+ _target_buf_len = 0;
+ TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_send_frag2))));
+ _check_send_frag2(mhr_len, false);
+ hdr = (sixlowpan_sfr_rfrag_t *)&_target_buf[mhr_len];
+ TEST_ASSERT_EQUAL_INT(tag, hdr->base.tag);
+ _target_buf_len = 0;
+ TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_send_frag3))));
+ /* last fragment should request an ACK */
+ _check_send_frag3(mhr_len, true);
+ hdr = (sixlowpan_sfr_rfrag_t *)&_target_buf[mhr_len];
+ TEST_ASSERT_EQUAL_INT(tag, hdr->base.tag);
+ /* fragmentation buffer still exists for re-sending */
+ TEST_ASSERT_NOT_NULL((fbuf = gnrc_sixlowpan_frag_fb_get_by_tag(tag)));
+ /* three fragments wait for potential re-sending */
+ TEST_ASSERT_EQUAL_INT(3U, clist_count(&fbuf->sfr.window));
+ /* simulate successful reception for this fragment */
+ bf_set(ack_bitmap, sixlowpan_sfr_rfrag_get_seq(hdr));
+ TEST_ASSERT_NOT_NULL(
+ (pkt = _create_recv_ack(_test_ack, sizeof(_test_ack)))
+ );
+ ack_hdr = pkt->data;
+ ack_hdr->base.tag = tag;
+ memcpy(ack_hdr->bitmap, ack_bitmap, sizeof(ack_hdr->bitmap));
+ _target_buf_len = 0;
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ pkt));
+ /* fragmentation buffer still exists for re-sending */
+ TEST_ASSERT_NOT_NULL((fbuf = gnrc_sixlowpan_frag_fb_get_by_tag(tag)));
+ /* two fragments wait for potential re-sending */
+ TEST_ASSERT_EQUAL_INT(2U, clist_count(&fbuf->sfr.window));
+ /* resend of fragment 1 */
+ TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_send_frag1))));
+ _check_send_frag1(mhr_len, false);
+ hdr = (sixlowpan_sfr_rfrag_t *)&_target_buf[mhr_len];
+ TEST_ASSERT_EQUAL_INT(tag, hdr->base.tag);
+ for (unsigned i = 0; i < CONFIG_GNRC_SIXLOWPAN_SFR_FRAG_RETRIES; i++) {
+ /* fragmentation buffer still exists for re-sending */
+ TEST_ASSERT_NOT_NULL((fbuf = gnrc_sixlowpan_frag_fb_get_by_tag(tag)));
+ /* two fragments wait for potential re-sending */
+ TEST_ASSERT_EQUAL_INT(2U, clist_count(&fbuf->sfr.window));
+ /* resend of fragment 2 (fragment 3 was ACK'd so it does not need
+ * to be resent) */
+ TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_send_frag2))));
+ /* second fragment should now request the ACK */
+ _check_send_frag2(mhr_len, true);
+ hdr = (sixlowpan_sfr_rfrag_t *)&_target_buf[mhr_len];
+ TEST_ASSERT_EQUAL_INT(tag, hdr->base.tag);
+ TEST_ASSERT(sixlowpan_sfr_rfrag_ack_req(hdr));
+ _target_buf_len = 0;
+ _wait_arq_timeout(fbuf);
+ }
+ /* should time out */
+ TEST_ASSERT_EQUAL_INT(0, _wait_for_packet(sizeof(_test_send_frag3)));
+ /* fragmentation buffer should have been deleted after
+ * CONFIG_GNRC_SIXLOWPAN_SFR_FRAG_RETRIES retries */
+ TEST_ASSERT_NULL(gnrc_sixlowpan_frag_fb_get_by_tag(tag));
+ TEST_ASSERT(gnrc_pktbuf_is_sane());
+ TEST_ASSERT(gnrc_pktbuf_is_empty());
+}
+
+static void test_sixlo_send__all_ackd(void)
+{
+ gnrc_sixlowpan_frag_fb_t *fbuf;
+ gnrc_pktsnip_t *pkt;
+ sixlowpan_sfr_rfrag_t *hdr;
+ sixlowpan_sfr_ack_t *ack_hdr;
+ size_t mhr_len;
+ uint8_t tag;
+ BITFIELD(ack_bitmap, 32U) = { 0 };
+
+ /* window is large enough to send all fragments at once */
+ TEST_ASSERT(3 <= CONFIG_GNRC_SIXLOWPAN_SFR_OPT_WIN_SIZE);
+ TEST_ASSERT_NOT_NULL((pkt = _create_send_datagram(false, true)));
+
+ netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev,
+ _mock_netdev_send);
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_send(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ pkt));
+ TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_send_frag1))));
+ /* tags are generated by the stack so don't check */
+ _check_send_frag1(mhr_len, false);
+ hdr = (sixlowpan_sfr_rfrag_t *)&_target_buf[mhr_len];
+ tag = hdr->base.tag;
+ /* simulate successful reception for this fragment */
+ bf_set(ack_bitmap, sixlowpan_sfr_rfrag_get_seq(hdr));
+ _target_buf_len = 0;
+ TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_send_frag2))));
+ _check_send_frag2(mhr_len, false);
+ hdr = (sixlowpan_sfr_rfrag_t *)&_target_buf[mhr_len];
+ TEST_ASSERT_EQUAL_INT(tag, hdr->base.tag);
+ /* simulate successful reception for this fragment */
+ bf_set(ack_bitmap, sixlowpan_sfr_rfrag_get_seq(hdr));
+ _target_buf_len = 0;
+ TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_send_frag3))));
+ /* last fragment should request an ACK */
+ _check_send_frag3(mhr_len, true);
+ hdr = (sixlowpan_sfr_rfrag_t *)&_target_buf[mhr_len];
+ TEST_ASSERT_EQUAL_INT(tag, hdr->base.tag);
+ /* simulate successful reception for this fragment */
+ bf_set(ack_bitmap, sixlowpan_sfr_rfrag_get_seq(hdr));
+ /* fragmentation buffer still exists for re-sending */
+ TEST_ASSERT_NOT_NULL((fbuf = gnrc_sixlowpan_frag_fb_get_by_tag(tag)));
+ /* three fragments wait for potential re-sending */
+ TEST_ASSERT_EQUAL_INT(3U, clist_count(&fbuf->sfr.window));
+ TEST_ASSERT_NOT_NULL(
+ (pkt = _create_recv_ack(_test_ack, sizeof(_test_ack)))
+ );
+ ack_hdr = pkt->data;
+ ack_hdr->base.tag = tag;
+ memcpy(ack_hdr->bitmap, ack_bitmap, sizeof(ack_hdr->bitmap));
+ _target_buf_len = 0;
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ pkt));
+ /* should time out */
+ TEST_ASSERT_EQUAL_INT(0, _wait_for_packet(sizeof(_test_send_frag3)));
+ TEST_ASSERT(gnrc_pktbuf_is_sane());
+ TEST_ASSERT(gnrc_pktbuf_is_empty());
+}
+
+static void test_sixlo_send__FULL_ack_recv(void)
+{
+ gnrc_sixlowpan_frag_fb_t *fbuf;
+ gnrc_pktsnip_t *pkt;
+ sixlowpan_sfr_rfrag_t *hdr;
+ sixlowpan_sfr_ack_t *ack_hdr;
+ size_t mhr_len;
+ uint8_t tag;
+
+ /* window is large enough to send all fragments at once */
+ TEST_ASSERT(3 <= CONFIG_GNRC_SIXLOWPAN_SFR_OPT_WIN_SIZE);
+ TEST_ASSERT_NOT_NULL((pkt = _create_send_datagram(false, true)));
+
+ netdev_test_set_send_cb((netdev_test_t *)_mock_netif->dev,
+ _mock_netdev_send);
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_send(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ pkt));
+ TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_send_frag1))));
+ /* tags are generated by the stack so don't check */
+ _check_send_frag1(mhr_len, false);
+ hdr = (sixlowpan_sfr_rfrag_t *)&_target_buf[mhr_len];
+ tag = hdr->base.tag;
+ /* simulate successful reception for this fragment */
+ _target_buf_len = 0;
+ TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_send_frag2))));
+ _check_send_frag2(mhr_len, false);
+ hdr = (sixlowpan_sfr_rfrag_t *)&_target_buf[mhr_len];
+ TEST_ASSERT_EQUAL_INT(tag, hdr->base.tag);
+ /* simulate successful reception for this fragment */
+ _target_buf_len = 0;
+ TEST_ASSERT((mhr_len = _wait_for_packet(sizeof(_test_send_frag3))));
+ /* last fragment should request an ACK */
+ _check_send_frag3(mhr_len, true);
+ hdr = (sixlowpan_sfr_rfrag_t *)&_target_buf[mhr_len];
+ TEST_ASSERT_EQUAL_INT(tag, hdr->base.tag);
+ /* fragmentation buffer still exists for re-sending */
+ TEST_ASSERT_NOT_NULL((fbuf = gnrc_sixlowpan_frag_fb_get_by_tag(tag)));
+ /* three fragments wait for potential re-sending */
+ TEST_ASSERT_EQUAL_INT(3U, clist_count(&fbuf->sfr.window));
+ /* simulate successful reception for this fragment */
+ TEST_ASSERT_NOT_NULL(
+ (pkt = _create_recv_ack(_test_ack, sizeof(_test_ack)))
+ );
+ ack_hdr = pkt->data;
+ ack_hdr->base.tag = tag;
+ /* FULL ACK == all ones */
+ memset(ack_hdr->bitmap, 0xff, sizeof(ack_hdr->bitmap));
+ _target_buf_len = 0;
+ TEST_ASSERT(0 < gnrc_netapi_dispatch_receive(GNRC_NETTYPE_SIXLOWPAN,
+ GNRC_NETREG_DEMUX_CTX_ALL,
+ pkt));
+ /* should time out */
+ TEST_ASSERT_EQUAL_INT(0, _wait_for_packet(sizeof(_test_send_frag3)));
+ TEST_ASSERT(gnrc_pktbuf_is_sane());
+ TEST_ASSERT(gnrc_pktbuf_is_empty());
+}
+
+static Test *tests_gnrc_sixlowpan_frag_sfr_api(void)
+{
+ EMB_UNIT_TESTFIXTURES(fixtures) {
+ new_TestFixture(test_sfr_forward__success__1st_frag_sixlo),
+ new_TestFixture(test_sfr_forward__success__1st_frag_iphc),
+ new_TestFixture(test_sfr_forward__success__nth_frag_incomplete),
+ new_TestFixture(test_sfr_forward__success__nth_frag_complete),
+ new_TestFixture(test_sfr_forward__ENOMEM__netif_hdr_build_fail),
+ };
+
+ EMB_UNIT_TESTCALLER(tests, _set_up, _tear_down, fixtures);
+
+ return (Test *)&tests;
+}
+
+static Test *tests_gnrc_sixlowpan_frag_sfr_integration(void)
+{
+ EMB_UNIT_TESTFIXTURES(fixtures) {
+ new_TestFixture(test_sixlo_recv_fwd__1st_frag_uncomp),
+ new_TestFixture(test_sixlo_recv_fwd__1st_frag_uncomp__req_ack),
+ new_TestFixture(test_sixlo_recv_fwd__1st_frag_uncomp__no_route),
+ new_TestFixture(test_sixlo_recv_fwd__1st_frag_uncomp__after_nth_frag),
+ new_TestFixture(test_sixlo_recv_fwd__1st_frag_comp),
+ new_TestFixture(test_sixlo_recv_fwd__1st_frag_comp__resend),
+ new_TestFixture(test_sixlo_recv_fwd__1st_frag_comp__only_iphc),
+ new_TestFixture(test_sixlo_recv_fwd__1st_frag_comp__only_iphc_no_nhc),
+ new_TestFixture(test_sixlo_recv_fwd__1st_frag_comp__no_route),
+ new_TestFixture(test_sixlo_recv_fwd__1st_frag_comp__no_route_only_iphc),
+ new_TestFixture(test_sixlo_recv_fwd__1st_frag_comp__no_refrag),
+ new_TestFixture(test_sixlo_recv_fwd__1st_frag_comp__after_nth_frag),
+ new_TestFixture(test_sixlo_recv_fwd__1st_frag_abort),
+ new_TestFixture(test_sixlo_recv_fwd__1st_frag_nalp),
+ new_TestFixture(test_sixlo_recv_fwd__1st_frag_nalp__req_ack),
+ new_TestFixture(test_sixlo_recv_fwd__1st_frag_nalp__no_vrb),
+ new_TestFixture(test_sixlo_recv_fwd__nth_frag),
+ new_TestFixture(test_sixlo_recv_fwd__nth_frag__no_vrbe),
+ new_TestFixture(test_sixlo_recv_fwd__nth_frag__duplicate),
+ new_TestFixture(test_sixlo_recv_fwd__nth_frag__overlap),
+ new_TestFixture(test_sixlo_recv_fwd__frag__too_short),
+ new_TestFixture(test_sixlo_recv_fwd__ack),
+ new_TestFixture(test_sixlo_recv_fwd__NULL_ack),
+ new_TestFixture(test_sixlo_recv_fwd__FULL_ack),
+ new_TestFixture(test_sixlo_recv_fwd__ack__no_vrbe),
+ new_TestFixture(test_sixlo_recv_fwd__ack__too_short),
+ new_TestFixture(test_sixlo_recv_ep__1st_frag_uncomp),
+ new_TestFixture(test_sixlo_recv_ep__1st_frag_uncomp__req_ack),
+ new_TestFixture(test_sixlo_recv_ep__1st_frag_uncomp__after_nth_frag),
+ new_TestFixture(test_sixlo_recv_ep__1st_frag_comp),
+ new_TestFixture(test_sixlo_recv_ep__1st_frag_comp__after_nth_frag),
+ new_TestFixture(test_sixlo_recv_ep__1st_frag_abort),
+ new_TestFixture(test_sixlo_recv_ep__1st_frag_abort__req_ack),
+ new_TestFixture(test_sixlo_recv_ep__complete_datagram),
+ new_TestFixture(test_sixlo_send),
+ new_TestFixture(test_sixlo_send__first_ackd),
+ new_TestFixture(test_sixlo_send__middle_ackd),
+ new_TestFixture(test_sixlo_send__last_ackd),
+ new_TestFixture(test_sixlo_send__all_ackd),
+ new_TestFixture(test_sixlo_send__FULL_ack_recv),
+ };
+
+ EMB_UNIT_TESTCALLER(tests, _set_up, _tear_down, fixtures);
+
+ return (Test *)&tests;
+}
+
+int main(void)
+{
+ _tests_init();
+
+ TESTS_START();
+ TESTS_RUN(tests_gnrc_sixlowpan_frag_sfr_api());
+ TESTS_RUN(tests_gnrc_sixlowpan_frag_sfr_integration());
+ TESTS_END();
+ return 0;
+}
+
+static gnrc_pktsnip_t *_create_recv_frag(const void *frag_data,
+ size_t frag_size)
+{
+ gnrc_pktsnip_t *netif;
+ gnrc_netif_hdr_t *netif_hdr;
+
+ netif = gnrc_netif_hdr_build(_vrbe_base.src, _vrbe_base.src_len,
+ _vrbe_base.dst, _vrbe_base.dst_len);
+ if (netif == NULL) {
+ return NULL;
+ }
+ netif_hdr = netif->data;
+ gnrc_netif_hdr_set_netif(netif_hdr, _mock_netif);
+ netif_hdr->flags = GNRC_NETIF_HDR_FLAGS_MORE_DATA;
+ return gnrc_pktbuf_add(netif, frag_data, frag_size,
+ GNRC_NETTYPE_SIXLOWPAN);
+}
+
+static gnrc_pktsnip_t *_create_recv_ack(const void *ack_data,
+ size_t ack_size)
+{
+ gnrc_pktsnip_t *netif;
+ gnrc_netif_hdr_t *netif_hdr;
+
+ netif = gnrc_netif_hdr_build(_rem_l2, sizeof(_rem_l2),
+ _vrbe_base.dst, _vrbe_base.dst_len);
+ if (netif == NULL) {
+ return NULL;
+ }
+ netif_hdr = netif->data;
+ gnrc_netif_hdr_set_netif(netif_hdr, _mock_netif);
+ return gnrc_pktbuf_add(netif, ack_data, ack_size,
+ GNRC_NETTYPE_SIXLOWPAN);
+}
+
+static int _set_route_and_nce(const ipv6_addr_t *route, unsigned pfx_len)
+{
+ /* add neighbor cache entry */
+ if (gnrc_ipv6_nib_nc_set(&_rem_ll, _mock_netif->pid,
+ _rem_l2, sizeof(_rem_l2)) < 0) {
+ return -1;
+ }
+ /* and route to neighbor */
+ if (gnrc_ipv6_nib_ft_add(route, pfx_len, &_rem_ll, _mock_netif->pid,
+ 0) < 0) {
+ return -1;
+ }
+ return 0;
+}
+
+static int _add_dst(const ipv6_addr_t *dst, unsigned pfx_len)
+{
+ /* discarding const qualifier should be safe */
+ return gnrc_netif_ipv6_addr_add(_mock_netif, (ipv6_addr_t *)dst, pfx_len,
+ GNRC_NETIF_IPV6_ADDRS_FLAGS_STATE_VALID);
+}
+
+static gnrc_pktsnip_t *_create_send_datagram(bool compressed, bool payload)
+{
+ gnrc_pktsnip_t *pkt1 = NULL, *pkt2;
+ gnrc_netif_hdr_t *netif_hdr;
+
+ if (payload) {
+ pkt1 = gnrc_pktbuf_add(NULL, _test_send_icmpv6, sizeof(_test_send_icmpv6),
+ GNRC_NETTYPE_ICMPV6);
+ if (pkt1 == NULL) {
+ return NULL;
+ }
+ }
+ if (compressed) {
+ /* Use IPHC header from expected data */
+ pkt2 = gnrc_pktbuf_add(pkt1,
+ &_test_send_frag1[TEST_SEND_FRAG1_PAYLOAD_POS],
+ TEST_SEND_FRAG1_PAYLOAD_SIZE,
+ GNRC_NETTYPE_SIXLOWPAN);
+ }
+ else {
+ pkt2 = gnrc_pktbuf_add(pkt1, _test_send_ipv6, sizeof(_test_send_ipv6),
+ GNRC_NETTYPE_IPV6);
+ }
+ if (pkt2 == NULL) {
+ return NULL;
+ }
+ pkt1 = gnrc_netif_hdr_build(_vrbe_base.src, _vrbe_base.src_len,
+ _vrbe_base.dst, _vrbe_base.dst_len);
+ if (pkt1 == NULL) {
+ return NULL;
+ }
+ netif_hdr = pkt1->data;
+ if (netif_hdr == NULL) {
+ return NULL;
+ }
+ gnrc_netif_hdr_set_netif(netif_hdr, _mock_netif);
+ LL_PREPEND(pkt2, pkt1);
+ return pkt2;
+}
+
+static size_t _wait_for_packet(size_t exp_size)
+{
+ size_t mhr_len;
+ uint32_t now = 0U;
+
+ xtimer_mutex_lock_timeout(&_target_buf_filled,
+ SEND_PACKET_TIMEOUT);
+ while ((mhr_len = ieee802154_get_frame_hdr_len(_target_buf))) {
+ now = xtimer_now_usec();
+ size_t size = _target_buf_len - mhr_len;
+#ifdef MODULE_OD
+ if (_target_buf_len > 0) {
+ puts("Sent packet: ");
+ od_hex_dump(_target_buf, _target_buf_len, OD_WIDTH_DEFAULT);
+ }
+#endif /* MODULE_OD */
+ if ((sizeof(sixlowpan_sfr_ack_t) == size) &&
+ (sixlowpan_sfr_ack_is((sixlowpan_sfr_t *)&_target_buf[mhr_len]))) {
+ /* found ACK */
+ break;
+ }
+ if (exp_size == size) {
+ /* found expected packet */
+ break;
+ }
+ /* let packets in again at the device */
+ mutex_unlock(&_target_buf_barrier);
+ /* wait for next packet */
+ if (xtimer_mutex_lock_timeout(&_target_buf_filled,
+ SEND_PACKET_TIMEOUT) < 0) {
+ return 0;
+ }
+ }
+ if (mhr_len > 0) {
+ if ((CONFIG_GNRC_SIXLOWPAN_SFR_INTER_FRAME_GAP_US > 0U) &&
+ (now - _last_sent_frame) < CONFIG_GNRC_SIXLOWPAN_SFR_INTER_FRAME_GAP_US) {
+ puts("(now - _last_sent_frame) < CONFIG_GNRC_SIXLOWPAN_SFR_INTER_FRAME_GAP_US");
+ return 0;
+ }
+ _last_sent_frame = now;
+ }
+ return mhr_len;
+}
+
+static inline void _wait_arq_timeout(gnrc_sixlowpan_frag_fb_t *fbuf)
+{
+ /* wait 1.25 * arq_timeout of datagram fbuf->tag */
+ xtimer_usleep((fbuf->sfr.arq_timeout +
+ (fbuf->sfr.arq_timeout >> 2)) * US_PER_MS);
+}
+
+static void _check_vrbe_values(gnrc_sixlowpan_frag_vrb_t *vrbe,
+ size_t mhr_len, bool check_offset_diff,
+ int16_t exp_offset_diff)
+{
+ uint8_t target_buf_dst[IEEE802154_LONG_ADDRESS_LEN];
+ sixlowpan_sfr_rfrag_t *frag_hdr = (sixlowpan_sfr_rfrag_t *)&_target_buf[mhr_len];
+ le_uint16_t tmp;
+
+ TEST_ASSERT(sixlowpan_sfr_rfrag_is(&frag_hdr->base));
+ TEST_ASSERT_EQUAL_INT(vrbe->super.dst_len,
+ ieee802154_get_dst(_target_buf,
+ target_buf_dst,
+ &tmp));
+ TEST_ASSERT_MESSAGE(memcmp(vrbe->super.dst, target_buf_dst,
+ vrbe->super.dst_len) == 0,
+ "vrbe->out_dst != target_buf_dst");
+
+ TEST_ASSERT_EQUAL_INT(vrbe->out_tag,
+ frag_hdr->base.tag);
+ TEST_ASSERT_NOT_NULL(vrbe->in_netif);
+ if (check_offset_diff) {
+ TEST_ASSERT_EQUAL_INT(exp_offset_diff, vrbe->offset_diff);
+ }
+}
+
+static void _check_ack(size_t mhr_len, uint8_t exp_tag,
+ const uint8_t *exp_bitmap)
+{
+ sixlowpan_sfr_ack_t *ack = (sixlowpan_sfr_ack_t *)&_target_buf[mhr_len];
+
+ TEST_ASSERT(sixlowpan_sfr_ack_is(&ack->base));
+ TEST_ASSERT_EQUAL_INT(exp_tag, ack->base.tag);
+ TEST_ASSERT_MESSAGE(memcmp(ack->bitmap, exp_bitmap,
+ sizeof(ack->bitmap)) == 0,
+ "Unexpected RFRAG-ACK bitmap");
+}
+
+static void _check_abort_ack(size_t mhr_len, uint8_t exp_tag)
+{
+ static const uint8_t _null_bitmap[] = { 0, 0, 0, 0 };
+
+ _check_ack(mhr_len, exp_tag, _null_bitmap);
+}
+
+static void _check_1st_frag_uncomp(size_t mhr_len, uint8_t exp_hl_diff)
+{
+ static const ipv6_hdr_t *exp_ipv6_hdr = (ipv6_hdr_t *)&_test_1st_frag_uncomp[
+ TEST_1ST_FRAG_UNCOMP_IPV6_HDR_POS
+ ];
+ ipv6_hdr_t *ipv6_hdr;
+
+ TEST_ASSERT_EQUAL_INT(
+ SIXLOWPAN_UNCOMP,
+ _target_buf[mhr_len + TEST_1ST_FRAG_UNCOMP_PAYLOAD_POS]
+ );
+ ipv6_hdr = (ipv6_hdr_t *)&_target_buf[
+ mhr_len + TEST_1ST_FRAG_UNCOMP_IPV6_HDR_POS
+ ];
+ TEST_ASSERT_EQUAL_INT(exp_ipv6_hdr->v_tc_fl.u32, ipv6_hdr->v_tc_fl.u32);
+ TEST_ASSERT_EQUAL_INT(exp_ipv6_hdr->len.u16, ipv6_hdr->len.u16);
+ TEST_ASSERT_EQUAL_INT(exp_ipv6_hdr->nh, ipv6_hdr->nh);
+ /* hop-limit shall be decremented by 1 */
+ TEST_ASSERT_EQUAL_INT(exp_ipv6_hdr->hl - exp_hl_diff, ipv6_hdr->hl);
+ TEST_ASSERT(ipv6_addr_equal(&exp_ipv6_hdr->src, &ipv6_hdr->src));
+ TEST_ASSERT(ipv6_addr_equal(&exp_ipv6_hdr->dst, &ipv6_hdr->dst));
+ TEST_ASSERT_MESSAGE(
+ memcmp(&_test_1st_frag_uncomp[TEST_1ST_FRAG_UNCOMP_IPV6_PAYLOAD_POS],
+ ipv6_hdr + 1, TEST_1ST_FRAG_UNCOMP_IPV6_PAYLOAD_SIZE) == 0,
+ "unexpected forwarded packet payload"
+ );
+}
+
+static void _check_send_frag_datagram_fields(size_t mhr_len)
+{
+ sixlowpan_sfr_rfrag_t *frag_hdr = (sixlowpan_sfr_rfrag_t *)&_target_buf[mhr_len];
+
+ if (sixlowpan_sfr_rfrag_get_seq(frag_hdr) == 0) {
+ TEST_ASSERT_EQUAL_INT(TEST_SEND_COMP_DATAGRAM_SIZE,
+ sixlowpan_sfr_rfrag_get_offset(frag_hdr));
+ }
+}
+
+static void _check_send_frag1(size_t mhr_len, bool ack_req)
+{
+ sixlowpan_sfr_rfrag_t *frag_hdr;
+
+ frag_hdr = (sixlowpan_sfr_rfrag_t *)&_target_buf[mhr_len];
+ TEST_ASSERT(sixlowpan_sfr_rfrag_is(&frag_hdr->base));
+ _check_send_frag_datagram_fields(mhr_len);
+ TEST_ASSERT_EQUAL_INT(
+ sizeof(_test_send_frag1) - sizeof(sixlowpan_sfr_rfrag_t),
+ sixlowpan_sfr_rfrag_get_frag_size(frag_hdr)
+ );
+ TEST_ASSERT_EQUAL_INT(0, sixlowpan_sfr_rfrag_get_seq(frag_hdr));
+ TEST_ASSERT_EQUAL_INT(ack_req, sixlowpan_sfr_rfrag_ack_req(frag_hdr));
+ TEST_ASSERT_MESSAGE(
+ memcmp(&_test_send_frag1[TEST_SEND_FRAG1_PAYLOAD_POS],
+ &_target_buf[TEST_SEND_FRAG1_PAYLOAD_POS + mhr_len],
+ sixlowpan_sfr_rfrag_get_frag_size(frag_hdr)) == 0,
+ "unexpected IPHC header"
+ );
+}
+
+static void _check_send_frag2(size_t mhr_len, bool ack_req)
+{
+ sixlowpan_sfr_rfrag_t *frag_hdr;
+
+ frag_hdr = (sixlowpan_sfr_rfrag_t *)&_target_buf[mhr_len];
+ TEST_ASSERT(sixlowpan_sfr_rfrag_is(&frag_hdr->base));
+ _check_send_frag_datagram_fields(mhr_len);
+ TEST_ASSERT_EQUAL_INT(
+ sizeof(_test_send_frag2) - sizeof(sixlowpan_sfr_rfrag_t),
+ sixlowpan_sfr_rfrag_get_frag_size(frag_hdr)
+ );
+ TEST_ASSERT_EQUAL_INT(
+ sixlowpan_sfr_rfrag_get_offset((sixlowpan_sfr_rfrag_t *)&_test_send_frag2),
+ sixlowpan_sfr_rfrag_get_offset(frag_hdr)
+ );
+ TEST_ASSERT(sixlowpan_sfr_rfrag_get_seq(frag_hdr) > 0);
+ TEST_ASSERT_EQUAL_INT(ack_req, sixlowpan_sfr_rfrag_ack_req(frag_hdr));
+ TEST_ASSERT_MESSAGE(
+ memcmp(&_test_send_frag2[sizeof(sixlowpan_sfr_rfrag_t)],
+ &_target_buf[sizeof(sixlowpan_sfr_rfrag_t) + mhr_len],
+ sixlowpan_sfr_rfrag_get_frag_size(frag_hdr)) == 0,
+ "unexpected send packet payload"
+ );
+}
+
+static void _check_send_frag3(size_t mhr_len, bool ack_req)
+{
+ sixlowpan_sfr_rfrag_t *frag_hdr;
+
+ frag_hdr = (sixlowpan_sfr_rfrag_t *)&_target_buf[mhr_len];
+ TEST_ASSERT(sixlowpan_sfr_rfrag_is(&frag_hdr->base));
+ _check_send_frag_datagram_fields(mhr_len);
+ TEST_ASSERT_EQUAL_INT(
+ sizeof(_test_send_frag3) - sizeof(sixlowpan_sfr_rfrag_t),
+ sixlowpan_sfr_rfrag_get_frag_size(frag_hdr)
+ );
+ TEST_ASSERT_EQUAL_INT(
+ sixlowpan_sfr_rfrag_get_offset((sixlowpan_sfr_rfrag_t *)&_test_send_frag3),
+ sixlowpan_sfr_rfrag_get_offset(frag_hdr)
+ );
+ TEST_ASSERT(sixlowpan_sfr_rfrag_get_seq(frag_hdr) > 0);
+ TEST_ASSERT_EQUAL_INT(ack_req, sixlowpan_sfr_rfrag_ack_req(frag_hdr));
+ TEST_ASSERT_MESSAGE(
+ memcmp(&_test_send_frag3[sizeof(sixlowpan_sfr_rfrag_t)],
+ &_target_buf[sizeof(sixlowpan_sfr_rfrag_t) + mhr_len],
+ sixlowpan_sfr_rfrag_get_frag_size(frag_hdr)) == 0,
+ "unexpected send packet payload"
+ );
+}
+
+static const gnrc_sixlowpan_frag_rb_t *_first_non_empty_rbuf(void)
+{
+ const gnrc_sixlowpan_frag_rb_t *rbuf = gnrc_sixlowpan_frag_rb_array();
+
+ for (unsigned i = 0; i < CONFIG_GNRC_SIXLOWPAN_FRAG_RBUF_SIZE; i++) {
+ if (!gnrc_sixlowpan_frag_rb_entry_empty(&rbuf[i])) {
+ return rbuf;
+ }
+ }
+ return NULL;
+}
+
+static int _mock_netdev_send(netdev_t *dev, const iolist_t *iolist)
+{
+ (void)dev;
+ mutex_lock(&_target_buf_barrier);
+ _target_buf_len = 0;
+ for (const iolist_t *ptr = iolist; ptr != NULL; ptr = ptr->iol_next) {
+ if ((_target_buf_len + iolist->iol_len) > sizeof(_target_buf)) {
+ return -ENOBUFS;
+ }
+ memcpy(&_target_buf[_target_buf_len], ptr->iol_base, ptr->iol_len);
+ _target_buf_len += ptr->iol_len;
+ }
+ /* wake-up test thread */
+ mutex_unlock(&_target_buf_filled);
+ return _target_buf_len;
+}
diff --git a/tests/gnrc_sixlowpan_frag_sfr/mockup_netif.c b/tests/gnrc_sixlowpan_frag_sfr/mockup_netif.c
new file mode 100644
index 000000000000..2924ce08617d
--- /dev/null
+++ b/tests/gnrc_sixlowpan_frag_sfr/mockup_netif.c
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2017 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
+ */
+
+#include "common.h"
+#include "msg.h"
+#include "net/gnrc.h"
+#include "net/ethernet.h"
+#include "net/gnrc/ipv6/nib.h"
+#include "net/gnrc/netif/ieee802154.h"
+#include "net/gnrc/netif/internal.h"
+#include "net/netdev_test.h"
+#include "sched.h"
+#include "thread.h"
+
+#define _MSG_QUEUE_SIZE (2)
+
+gnrc_netif_t *_mock_netif = NULL;
+
+static netdev_test_t _mock_netdev;
+static char _mock_netif_stack[THREAD_STACKSIZE_DEFAULT];
+static msg_t _main_msg_queue[_MSG_QUEUE_SIZE];
+static gnrc_netif_t _netif;
+
+void _common_set_up(void)
+{
+ assert(_mock_netif != NULL);
+ gnrc_ipv6_nib_init();
+ gnrc_netif_acquire(_mock_netif);
+ gnrc_ipv6_nib_init_iface(_mock_netif);
+ gnrc_netif_release(_mock_netif);
+}
+
+int _get_device_type(netdev_t *dev, void *value, size_t max_len)
+{
+ (void)dev;
+ assert(max_len == sizeof(uint16_t));
+ *((uint16_t *)value) = NETDEV_TYPE_IEEE802154;
+ return sizeof(uint16_t);
+}
+
+static int _get_netdev_proto(netdev_t *netdev, void *value, size_t max_len)
+{
+ assert(max_len == sizeof(gnrc_nettype_t));
+ (void)netdev;
+
+ *((gnrc_nettype_t *)value) = GNRC_NETTYPE_SIXLOWPAN;
+ return sizeof(gnrc_nettype_t);
+}
+
+int _get_max_packet_size(netdev_t *dev, void *value, size_t max_len)
+{
+ (void)dev;
+ assert(max_len == sizeof(uint16_t));
+ *((uint16_t *)value) = 102U;
+ return sizeof(uint16_t);
+}
+
+int _get_src_len(netdev_t *dev, void *value, size_t max_len)
+{
+ (void)dev;
+ assert(max_len == sizeof(uint16_t));
+ *((uint16_t *)value) = IEEE802154_LONG_ADDRESS_LEN;
+ return sizeof(uint16_t);
+}
+
+int _get_address_long(netdev_t *dev, void *value, size_t max_len)
+{
+ static const uint8_t addr[] = { _LL0, _LL1, _LL2, _LL3,
+ _LL4, _LL5, _LL6, _LL7 };
+
+ (void)dev;
+ assert(max_len >= sizeof(addr));
+ memcpy(value, addr, sizeof(addr));
+ return sizeof(addr);
+}
+
+int _get_proto(netdev_t *dev, void *value, size_t max_len)
+{
+ (void)dev;
+ assert(max_len == sizeof(gnrc_nettype_t));
+ *((gnrc_nettype_t *)value) = GNRC_NETTYPE_SIXLOWPAN;
+ return sizeof(gnrc_nettype_t);
+}
+
+void _tests_init(void)
+{
+ int res;
+
+ msg_init_queue(_main_msg_queue, _MSG_QUEUE_SIZE);
+ netdev_test_setup(&_mock_netdev, 0);
+ netdev_test_set_get_cb(&_mock_netdev, NETOPT_DEVICE_TYPE,
+ _get_device_type);
+ netdev_test_set_get_cb(&_mock_netdev, NETOPT_PROTO,
+ _get_netdev_proto);
+ netdev_test_set_get_cb(&_mock_netdev, NETOPT_MAX_PACKET_SIZE,
+ _get_max_packet_size);
+ netdev_test_set_get_cb(&_mock_netdev, NETOPT_SRC_LEN,
+ _get_src_len);
+ netdev_test_set_get_cb(&_mock_netdev, NETOPT_ADDRESS_LONG,
+ _get_address_long);
+ netdev_test_set_get_cb(&_mock_netdev, NETOPT_PROTO,
+ _get_proto);
+ res = gnrc_netif_ieee802154_create(
+ &_netif, _mock_netif_stack, THREAD_STACKSIZE_DEFAULT,
+ GNRC_NETIF_PRIO, "mockup_wpan", &_mock_netdev.netdev.netdev
+ );
+ assert(res == 0);
+ _mock_netif = &_netif;
+}
+
+/** @} */
diff --git a/tests/gnrc_sixlowpan_frag_sfr/tests/01-run.py b/tests/gnrc_sixlowpan_frag_sfr/tests/01-run.py
new file mode 100755
index 000000000000..6d8b056e2d59
--- /dev/null
+++ b/tests/gnrc_sixlowpan_frag_sfr/tests/01-run.py
@@ -0,0 +1,19 @@
+#!/usr/bin/env python3
+
+# Copyright (C) 2016 Kaspar Schleiser
+# Copyright (C) 2016 Takuo Yonezawa
+#
+# 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.
+
+import sys
+from testrunner import run
+
+
+def testfunc(child):
+ child.expect(r"OK \(\d+ tests\)")
+
+
+if __name__ == "__main__":
+ sys.exit(run(testfunc))
From e980405cbce777ef7ff3832f4ed725891915d955 Mon Sep 17 00:00:00 2001
From: "Martine S. Lenders"
Date: Tue, 3 Dec 2019 15:56:32 +0100
Subject: [PATCH 10/10] gnrc_sixlowpan_frag_sfr: provide statistics sub-module
---
makefiles/pseudomodules.inc.mk | 1 +
sys/Makefile.dep | 4 ++
sys/include/net/gnrc/sixlowpan/frag/sfr.h | 31 +++++++++
.../frag/sfr/gnrc_sixlowpan_frag_sfr.c | 63 +++++++++++++++++--
sys/shell/commands/sc_gnrc_6lo_frag_stats.c | 21 +++++++
5 files changed, 116 insertions(+), 4 deletions(-)
diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk
index ee617d964410..1ef1e6804a4e 100644
--- a/makefiles/pseudomodules.inc.mk
+++ b/makefiles/pseudomodules.inc.mk
@@ -56,6 +56,7 @@ PSEUDOMODULES += gnrc_sixloenc
PSEUDOMODULES += gnrc_sixlowpan_border_router_default
PSEUDOMODULES += gnrc_sixlowpan_default
PSEUDOMODULES += gnrc_sixlowpan_frag_hint
+PSEUDOMODULES += gnrc_sixlowpan_frag_sfr_stats
PSEUDOMODULES += gnrc_sixlowpan_iphc_nhc
PSEUDOMODULES += gnrc_sixlowpan_nd_border_router
PSEUDOMODULES += gnrc_sixlowpan_router_default
diff --git a/sys/Makefile.dep b/sys/Makefile.dep
index 989e4b618fef..66ca09d64830 100644
--- a/sys/Makefile.dep
+++ b/sys/Makefile.dep
@@ -333,6 +333,10 @@ ifneq (,$(filter gnrc_sixlowpan_frag_sfr,$(USEMODULE)))
USEMODULE += xtimer
endif
+ifneq (,$(filter gnrc_sixlowpan_frag_sfr_stats,$(USEMODULE)))
+ USEMODULE += gnrc_sixlowpan_frag_sfr
+endif
+
ifneq (,$(filter gnrc_sixlowpan_frag_vrb,$(USEMODULE)))
USEMODULE += xtimer
USEMODULE += gnrc_sixlowpan_frag_fb
diff --git a/sys/include/net/gnrc/sixlowpan/frag/sfr.h b/sys/include/net/gnrc/sixlowpan/frag/sfr.h
index 3d5b9a5fcfbc..3a3a32e26053 100644
--- a/sys/include/net/gnrc/sixlowpan/frag/sfr.h
+++ b/sys/include/net/gnrc/sixlowpan/frag/sfr.h
@@ -56,6 +56,28 @@ extern "C" {
*/
#define GNRC_SIXLOWPAN_FRAG_SFR_INTER_FRAG_GAP_MSG (0x0228)
+/**
+ * @brief Stats on selective fragment recovery
+ */
+typedef struct {
+ uint32_t datagram_resends; /**< datagrams resent */
+ struct {
+ uint32_t usual; /**< non-abort fragments sent */
+ uint32_t aborts; /**< abort pseudo-fragments sent */
+ uint32_t forwarded; /**< forwarded fragments */
+ } fragments_sent; /**< RFRAG packets sent */
+ struct {
+ uint32_t by_nack; /**< fragments resent due to a 0 in ACK's bitmap */
+ uint32_t by_timeout; /**< fragments resent due to an ARQ timeout */
+ } fragment_resends; /**< fragments resent */
+ struct {
+ uint32_t full; /**< full RFRAGs ACKs sent */
+ uint32_t partly; /**< partly ACKing RFRAGs sent */
+ uint32_t aborts; /**< abort RFRAG ACKs sent */
+ uint32_t forwarded; /**< forwarded ACKs */
+ } acks; /**< ACKs stats */
+} gnrc_sixlowpan_frag_sfr_stats_t;
+
/**
* @brief Initialize selective fragment recovery
*/
@@ -163,6 +185,15 @@ void gnrc_sixlowpan_frag_sfr_arq_timeout(gnrc_sixlowpan_frag_fb_t *fbuf);
*/
void gnrc_sixlowpan_frag_sfr_inter_frame_gap(void);
+#if IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_SFR_STATS)
+/**
+ * @brief Fetch current stats for selective fragment recovery
+ *
+ * @param[out] stats The current stats. Must not be NULL.
+ */
+void gnrc_sixlowpan_frag_sfr_stats_get(gnrc_sixlowpan_frag_sfr_stats_t *stats);
+#endif /* IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_SFR_STATS) */
+
#ifdef __cplusplus
}
#endif
diff --git a/sys/net/gnrc/network_layer/sixlowpan/frag/sfr/gnrc_sixlowpan_frag_sfr.c b/sys/net/gnrc/network_layer/sixlowpan/frag/sfr/gnrc_sixlowpan_frag_sfr.c
index 9b2119c8c448..8058a5b545c6 100644
--- a/sys/net/gnrc/network_layer/sixlowpan/frag/sfr/gnrc_sixlowpan_frag_sfr.c
+++ b/sys/net/gnrc/network_layer/sixlowpan/frag/sfr/gnrc_sixlowpan_frag_sfr.c
@@ -96,6 +96,8 @@ static clist_node_t _frame_queue;
static const gnrc_sixlowpan_frag_sfr_bitmap_t _full_bitmap = { .u32 = UINT32_MAX };
static const gnrc_sixlowpan_frag_sfr_bitmap_t _null_bitmap = { .u32 = 0U };
+static gnrc_sixlowpan_frag_sfr_stats_t _stats;
+
/**
* @brief Converts a @ref sys_bitmap based bitmap to a
* gnrc_sixlowpan_frag_sfr_bitmap_t
@@ -495,6 +497,11 @@ void gnrc_sixlowpan_frag_sfr_arq_timeout(gnrc_sixlowpan_frag_fb_t *fbuf)
error_no = ENOMEM;
goto error;
}
+ else if (IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_SFR_STATS)) {
+ /* fragment was resent successfully, note this done in
+ * the statistics */
+ _stats.fragment_resends.by_timeout++;
+ }
/* fragment was resent successfully, schedule next ACK
* timeout */
reschedule_arq_timeout = true;
@@ -559,6 +566,13 @@ void gnrc_sixlowpan_frag_sfr_inter_frame_gap(void)
}
}
+void gnrc_sixlowpan_frag_sfr_stats_get(gnrc_sixlowpan_frag_sfr_stats_t *stats)
+{
+ if (IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_SFR_STATS)) {
+ *stats = _stats;
+ }
+}
+
/* ====== INTERNAL FUNCTION DEFINITIONS ====== */
static inline uint16_t _min(uint16_t a, size_t b)
{
@@ -770,6 +784,9 @@ static bool _send_fragment(gnrc_pktsnip_t *frag, gnrc_sixlowpan_frag_fb_t *fbuf,
frag_desc->retries = 0;
clist_rpush(&fbuf->sfr.window, &frag_desc->super);
if ((res = _send_frame(frag, NULL, page))) {
+ if (IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_SFR_STATS)) {
+ _stats.fragments_sent.usual++;
+ }
frag_desc->last_sent = _last_frame_sent;
fbuf->sfr.cur_seq++;
fbuf->sfr.frags_sent++;
@@ -866,6 +883,9 @@ static void _try_reassembly(gnrc_netif_hdr_t *netif_hdr,
"fragment\n");
/* send abort */
bitmap = &_null_bitmap;
+ if (IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_SFR_STATS)) {
+ _stats.acks.aborts++;
+ }
}
else if (vrbe != NULL) {
DEBUG("6lo sfr: packet was forwarded\n");
@@ -886,11 +906,17 @@ static void _try_reassembly(gnrc_netif_hdr_t *netif_hdr,
_clean_up_rb_entry(entry);
/* send abort */
bitmap = &_null_bitmap;
+ if (IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_SFR_STATS)) {
+ _stats.acks.aborts++;
+ }
}
else {
if (res) {
DEBUG("6lo sfr: dispatched datagram to upper layer\n");
bitmap = &_full_bitmap;
+ if (IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_SFR_STATS)) {
+ _stats.acks.full++;
+ }
}
else if (ack_req) {
DEBUG("6lo sfr: ACKing received fragments %02X%02X%02X%02X "
@@ -902,6 +928,9 @@ static void _try_reassembly(gnrc_netif_hdr_t *netif_hdr,
entry->entry.base->current_size,
entry->entry.base->datagram_size);
bitmap = _to_bitmap(entry->entry.rb->received);
+ if (IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_SFR_STATS)) {
+ _stats.acks.partly++;
+ }
}
else {
/* no ACK was requested and no error was causing an abort ACK*/
@@ -1087,6 +1116,17 @@ static void _handle_nth_rfrag(gnrc_netif_hdr_t *netif_hdr, gnrc_pktsnip_t *pkt,
}
}
+static int _resend_failed_frag(clist_node_t *node, void *fbuf_ptr)
+{
+ int res;
+
+ if (((res = _resend_frag(node, fbuf_ptr)) == 0) &&
+ IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_SFR_STATS)) {
+ _stats.fragment_resends.by_nack++;
+ }
+ return res;
+}
+
static void _check_failed_frags(sixlowpan_sfr_ack_t *ack,
gnrc_sixlowpan_frag_fb_t *fbuf)
{
@@ -1140,10 +1180,10 @@ static void _check_failed_frags(sixlowpan_sfr_ack_t *ack,
else {
fbuf->sfr.window = not_received;
assert(fbuf->sfr.frags_sent == clist_count(&fbuf->sfr.window));
- /* use _resend_frag here instead of loop above, so _resend_frag
- * can know if the fragment is the last in the window by using
- * clist_rpeek() on fbuf->sfr.window */
- if (clist_foreach(&fbuf->sfr.window, _resend_frag, fbuf) != NULL) {
+ /* use _resend_failed_frag here instead of loop above, so
+ * _resend_frag can know if the fragment is the last in the window by
+ * using clist_rpeek() on fbuf->sfr.window */
+ if (clist_foreach(&fbuf->sfr.window, _resend_failed_frag, fbuf) != NULL) {
/* XXX: it is unlikely that allocating an abort RFRAG will be
* successful since the resources missing to cause the abort are
* still in use, but we should at least try */
@@ -1310,6 +1350,9 @@ static bool _send_abort_frag(gnrc_pktsnip_t *pkt, uint8_t tag, bool req_ack,
if (frag != NULL) {
sixlowpan_sfr_rfrag_set_offset(frag->next->data, 0);
_send_frame(frag, NULL, page);
+ if (IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_SFR_STATS)) {
+ _stats.fragments_sent.aborts++;
+ }
return true;
}
return false;
@@ -1378,6 +1421,9 @@ static void _retry_datagram(gnrc_sixlowpan_frag_fb_t *fbuf)
}
else {
DEBUG("6lo sfr: Retrying to send datagram %u completely\n", fbuf->tag);
+ if (IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_SFR_STATS)) {
+ _stats.datagram_resends++;
+ }
fbuf->sfr.retrans--;
/* return fragmentation buffer to its original state to resend the whole
* datagram again */
@@ -1396,6 +1442,9 @@ static void _abort_rb(gnrc_pktsnip_t *pkt, _generic_rb_entry_t *entry,
netif_hdr->src_l2addr_len, addr_str),
hdr->base.tag);
if (send_ack) {
+ if (IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_SFR_STATS)) {
+ _stats.acks.aborts++;
+ }
_send_ack(gnrc_netif_hdr_get_netif(netif_hdr),
gnrc_netif_hdr_get_src_addr(netif_hdr),
netif_hdr->src_l2addr_len,
@@ -1513,6 +1562,9 @@ static void _handle_ack(gnrc_netif_hdr_t *netif_hdr, gnrc_pktsnip_t *pkt,
addr_str), vrbe->super.tag);
_send_ack(vrbe->in_netif, vrbe->super.src, vrbe->super.src_len,
&mock_base, hdr->bitmap);
+ if (IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_SFR_STATS)) {
+ _stats.acks.forwarded++;
+ }
if ((unaligned_get_u32(hdr->bitmap) == _full_bitmap.u32) ||
(unaligned_get_u32(hdr->bitmap) == _null_bitmap.u32)) {
if (CONFIG_GNRC_SIXLOWPAN_FRAG_RBUF_DEL_TIMER > 0) {
@@ -1598,6 +1650,9 @@ static int _forward_rfrag(gnrc_pktsnip_t *pkt, _generic_rb_entry_t *entry,
gnrc_netif_hdr_set_netif(new->data, entry->entry.vrb->out_netif);
new->next = pkt;
_send_frame(new, NULL, page);
+ if (IS_USED(MODULE_GNRC_SIXLOWPAN_FRAG_SFR_STATS)) {
+ _stats.fragments_sent.forwarded++;
+ }
return 0;
}
diff --git a/sys/shell/commands/sc_gnrc_6lo_frag_stats.c b/sys/shell/commands/sc_gnrc_6lo_frag_stats.c
index 53beec138447..6e256fc7cf45 100644
--- a/sys/shell/commands/sc_gnrc_6lo_frag_stats.c
+++ b/sys/shell/commands/sc_gnrc_6lo_frag_stats.c
@@ -16,6 +16,9 @@
#include
#include "net/gnrc/sixlowpan/frag/stats.h"
+#ifdef MODULE_GNRC_SIXLOWPAN_FRAG_SFR_STATS
+#include "net/gnrc/sixlowpan/frag/sfr.h"
+#endif
int _gnrc_6lo_frag_stats(int argc, char **argv)
{
@@ -28,6 +31,24 @@ int _gnrc_6lo_frag_stats(int argc, char **argv)
#ifdef MODULE_GNRC_SIXLOWPAN_FRAG_VRB
printf("VRB full: %u\n", stats->vrb_full);
#endif
+#ifdef MODULE_GNRC_SIXLOWPAN_FRAG_SFR_STATS
+ gnrc_sixlowpan_frag_sfr_stats_t sfr;
+
+ gnrc_sixlowpan_frag_sfr_stats_get(&sfr);
+ printf("DG resends: %lu\n", (long unsigned)sfr.datagram_resends);
+ printf("frags sent: usual: %lu, aborts: %lu, forwarded: %lu\n",
+ (long unsigned)sfr.fragments_sent.usual,
+ (long unsigned)sfr.fragments_sent.aborts,
+ (long unsigned)sfr.fragments_sent.forwarded);
+ printf("frag resends: NACK: %lu, timeout: %lu\n",
+ (long unsigned)sfr.fragment_resends.by_nack,
+ (long unsigned)sfr.fragment_resends.by_timeout);
+ printf("ACKs: full: %lu, partly: %lu, aborts: %lu, forwarded: %lu\n",
+ (long unsigned)sfr.acks.full,
+ (long unsigned)sfr.acks.partly,
+ (long unsigned)sfr.acks.aborts,
+ (long unsigned)sfr.acks.forwarded);
+#endif /* MODULE_GNRC_SIXLOWPAN_FRAG_SFR_STATS */
printf("frags complete: %u\n", stats->fragments);
printf("dgs complete: %u\n", stats->datagrams);
return 0;