diff --git a/sys/net/gnrc/network_layer/ipv6/nib/_nib-arsm.c b/sys/net/gnrc/network_layer/ipv6/nib/_nib-arsm.c index b8dc68d0bbadef36cda13c834e7366169686fc27..c4ecf556bc4388a5e092c22ba4718df160e0e660 100644 --- a/sys/net/gnrc/network_layer/ipv6/nib/_nib-arsm.c +++ b/sys/net/gnrc/network_layer/ipv6/nib/_nib-arsm.c @@ -460,17 +460,10 @@ bool _is_reachable(_nib_onl_entry_t *entry) static inline uint32_t _exp_backoff_retrans_timer(uint8_t ns_sent, uint32_t retrans_timer) { - uint32_t tmp = random_uint32_range(NDP_MIN_RANDOM_FACTOR, - NDP_MAX_RANDOM_FACTOR); - - /* backoff according to https://tools.ietf.org/html/rfc7048 with - * BACKOFF_MULTIPLE == 2 */ - tmp = ((1 << ns_sent) * retrans_timer * tmp) / US_PER_MS; - /* random factors were statically multiplied with 1000 ^ */ - if (tmp > NDP_MAX_RETRANS_TIMER_MS) { - tmp = NDP_MAX_RETRANS_TIMER_MS; - } - return tmp; + uint32_t factor = random_uint32_range(NDP_MIN_RANDOM_FACTOR, + NDP_MAX_RANDOM_FACTOR); + + return _exp_backoff_retrans_timer_factor(ns_sent, retrans_timer, factor); } #if GNRC_IPV6_NIB_CONF_REDIRECT diff --git a/sys/net/gnrc/network_layer/ipv6/nib/_nib-arsm.h b/sys/net/gnrc/network_layer/ipv6/nib/_nib-arsm.h index abfdb8f263cb8a0374b3a6e944b6c95d74ca26fe..aa7210992b2516dbf0ff690d32ee413525130322 100644 --- a/sys/net/gnrc/network_layer/ipv6/nib/_nib-arsm.h +++ b/sys/net/gnrc/network_layer/ipv6/nib/_nib-arsm.h @@ -81,6 +81,43 @@ void _snd_uc_ns(_nib_onl_entry_t *nbr, bool reset); void _handle_sl2ao(gnrc_netif_t *netif, const ipv6_hdr_t *ipv6, const icmpv6_hdr_t *icmpv6, const ndp_opt_t *sl2ao); +/** + * @brief Calculates truncated exponential back-off for retransmission timer + * for neighbor solicitations based on a randomized factor + * + * The truncation is at @ref NDP_MAX_RETRANS_TIMER_MS. as suggested in + * [RFC 7048, section 3](https://tools.ietf.org/html/rfc7048#section-3). + * + * @param[in] ns_sent Neighbor solicitations sent up until now. Must be + * lesser than or equal to @ref NDP_MAX_NS_NUMOF. + * @param[in] retrans_timer Currently configured retransmission timer in ms. + * @param[in] factor An equally distributed factor between + * @ref NDP_MIN_RANDOM_FACTOR and (exclusive) + * @ref NDP_MAX_RANDOM_FACTOR. + * + * @pre (NDP_MIN_RANDOM_FACTOR <= factor < NDP_MAX_RANDOM_FACTOR) + * @pre (ns_sent <= NDP_MAX_NS_NUMOF) + * + * @return exponential back-off of the retransmission timer + */ +static inline uint32_t _exp_backoff_retrans_timer_factor(uint8_t ns_sent, + uint32_t retrans_timer, + uint32_t factor) +{ + assert(NDP_MIN_RANDOM_FACTOR <= factor); + assert(factor < NDP_MAX_RANDOM_FACTOR); + assert(ns_sent <= NDP_MAX_NS_NUMOF); + /* backoff according to https://tools.ietf.org/html/rfc7048 with + * BACKOFF_MULTIPLE == 2 */ + uint32_t res = (uint32_t)(((uint64_t)(((uint32_t) 1) << ns_sent) * + retrans_timer * factor) / US_PER_MS); + /* random factors were statically multiplied with 1000 */ + if (res > NDP_MAX_RETRANS_TIMER_MS) { + res = NDP_MAX_RETRANS_TIMER_MS; + } + return res; +} + #if GNRC_IPV6_NIB_CONF_ARSM || defined(DOXYGEN) /** * @brief Handler for @ref GNRC_IPV6_NIB_SND_UC_NS and diff --git a/tests/unittests/tests-gnrc_ipv6_nib/tests-gnrc_ipv6_nib-internal.c b/tests/unittests/tests-gnrc_ipv6_nib/tests-gnrc_ipv6_nib-internal.c index be6e75d6836e93e59dccd54230cbf138f3a45d1d..a171d3c2a173ba3eb5afd42436c5912af0f342d5 100644 --- a/tests/unittests/tests-gnrc_ipv6_nib/tests-gnrc_ipv6_nib-internal.c +++ b/tests/unittests/tests-gnrc_ipv6_nib/tests-gnrc_ipv6_nib-internal.c @@ -21,6 +21,7 @@ #include "net/gnrc/ipv6/nib.h" #include "_nib-internal.h" +#include "_nib-arsm.h" #include "unittests-constants.h" @@ -1947,6 +1948,58 @@ static void test_nib_abr_iter__three_elem_middle_removed(void) } #endif +static void test_retrans_exp_backoff(void) +{ + TEST_ASSERT_EQUAL_INT(0, + _exp_backoff_retrans_timer_factor(0, 0, NDP_MIN_RANDOM_FACTOR)); + /* factor 1000 means multiplied by 1 */ + TEST_ASSERT_EQUAL_INT(NDP_RETRANS_TIMER_MS, + _exp_backoff_retrans_timer_factor(0, NDP_RETRANS_TIMER_MS, 1000)); + TEST_ASSERT_EQUAL_INT(2 * NDP_RETRANS_TIMER_MS, /* 2^1 = 2 */ + _exp_backoff_retrans_timer_factor(1, NDP_RETRANS_TIMER_MS, 1000)); + TEST_ASSERT_EQUAL_INT(4 * NDP_RETRANS_TIMER_MS, /* 2^2 = 4 */ + _exp_backoff_retrans_timer_factor(2, NDP_RETRANS_TIMER_MS, 1000)); + TEST_ASSERT_EQUAL_INT(8 * NDP_RETRANS_TIMER_MS, /* 2^3 = 8 */ + _exp_backoff_retrans_timer_factor(3, NDP_RETRANS_TIMER_MS, 1000)); + TEST_ASSERT_EQUAL_INT(16 * NDP_RETRANS_TIMER_MS, /* 2^4 = 16 */ + _exp_backoff_retrans_timer_factor(4, NDP_RETRANS_TIMER_MS, 1000)); + TEST_ASSERT_EQUAL_INT(32 * NDP_RETRANS_TIMER_MS, /* 2^5 = 32 */ + _exp_backoff_retrans_timer_factor(5, NDP_RETRANS_TIMER_MS, 1000)); + TEST_ASSERT_EQUAL_INT(NDP_MAX_RETRANS_TIMER_MS, + _exp_backoff_retrans_timer_factor(6, NDP_RETRANS_TIMER_MS, 1000)); + TEST_ASSERT_EQUAL_INT(NDP_MAX_RETRANS_TIMER_MS, + _exp_backoff_retrans_timer_factor(0, UINT32_MAX, + NDP_MIN_RANDOM_FACTOR)); + TEST_ASSERT_EQUAL_INT(NDP_MAX_RETRANS_TIMER_MS, + _exp_backoff_retrans_timer_factor(0, UINT32_MAX, + NDP_MAX_RANDOM_FACTOR - 1)); + TEST_ASSERT_EQUAL_INT(NDP_MAX_RETRANS_TIMER_MS, + _exp_backoff_retrans_timer_factor(NDP_MAX_NS_NUMOF, UINT32_MAX, + NDP_MAX_RANDOM_FACTOR - 1)); + TEST_ASSERT_EQUAL_INT(NDP_MAX_RETRANS_TIMER_MS, + _exp_backoff_retrans_timer_factor(NDP_MAX_NS_NUMOF, + NDP_RETRANS_TIMER_MS, + NDP_MAX_RANDOM_FACTOR - 1)); + TEST_ASSERT_EQUAL_INT(NDP_MAX_RETRANS_TIMER_MS, + _exp_backoff_retrans_timer_factor(NDP_MAX_NS_NUMOF, + NDP_RETRANS_TIMER_MS, + NDP_MIN_RANDOM_FACTOR)); + TEST_ASSERT_EQUAL_INT(NDP_MAX_RETRANS_TIMER_MS, + _exp_backoff_retrans_timer_factor(NDP_MAX_NS_NUMOF, + NDP_MAX_RETRANS_TIMER_MS, + NDP_MAX_RANDOM_FACTOR - 1)); + TEST_ASSERT_EQUAL_INT(32768, + _exp_backoff_retrans_timer_factor(NDP_MAX_NS_NUMOF - 1, 1, + NDP_MIN_RANDOM_FACTOR)); + TEST_ASSERT_EQUAL_INT(47653, + _exp_backoff_retrans_timer_factor(5U, 1118U, 1332)); + TEST_ASSERT_EQUAL_INT(47653, + _exp_backoff_retrans_timer_factor(5U, 1118U, 1332)); + /* test 64-bit overfrow */ + TEST_ASSERT_EQUAL_INT(NDP_MAX_RETRANS_TIMER_MS, + _exp_backoff_retrans_timer_factor(NDP_MAX_NS_NUMOF, 32768, 1024)); +} + Test *tests_gnrc_ipv6_nib_internal_tests(void) { EMB_UNIT_TESTFIXTURES(fixtures) { @@ -2046,6 +2099,7 @@ Test *tests_gnrc_ipv6_nib_internal_tests(void) new_TestFixture(test_nib_abr_iter__three_elem), new_TestFixture(test_nib_abr_iter__three_elem_middle_removed), #endif + new_TestFixture(test_retrans_exp_backoff), }; EMB_UNIT_TESTCALLER(tests, set_up, NULL,