diff --git a/sys/include/net/gnrc/ipv6/nib.h b/sys/include/net/gnrc/ipv6/nib.h index 56a34b1b46..00796e7fd4 100644 --- a/sys/include/net/gnrc/ipv6/nib.h +++ b/sys/include/net/gnrc/ipv6/nib.h @@ -23,6 +23,7 @@ #define NET_GNRC_IPV6_NIB_H #include "net/gnrc/ipv6/nib/nc.h" +#include "net/gnrc/ipv6/nib/pl.h" #ifdef __cplusplus extern "C" { diff --git a/sys/include/net/gnrc/ipv6/nib/pl.h b/sys/include/net/gnrc/ipv6/nib/pl.h new file mode 100644 index 0000000000..ca84fe2e3f --- /dev/null +++ b/sys/include/net/gnrc/ipv6/nib/pl.h @@ -0,0 +1,137 @@ +/* + * 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 net_gnrc_ipv6_nib_pl Prefix list + * @ingroup net_gnrc_ipv6_nib + * @brief Prefix list component of neighbor information base + * @{ + * + * @file + * @brief Prefix list defintions + * + * @author Martine Lenders + */ +#ifndef NET_GNRC_IPV6_NIB_PL_H +#define NET_GNRC_IPV6_NIB_PL_H + +#include + +#include "net/ipv6/addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Prefix list entry view on NIB + */ +typedef struct { + ipv6_addr_t pfx; /**< prefix */ + uint8_t pfx_len; /**< length of gnrc_ipv6_nib_pl_t::pfx in bits */ + uint16_t iface; /**< interface gnrc_ipv6_nib_pl_t::pfx is assigned + * to */ + uint32_t valid_until; /**< timestamp (in ms) until which the prefix is + valid */ + uint32_t pref_until; /**< timestamp (in ms) until which the prefix is + preferred */ +} gnrc_ipv6_nib_pl_t; + +/** + * @brief Adds (or updates) prefix to NIB + * + * @pre `(pfx != NULL)` + * + * @param[in] iface Interface @p pfx is valid on. + * @param[in] pfx The prefix. May not be a link-local prefix or a + * multicast address and its first @p pfx_len bits + * may not be 0. + * @param[in] pfx_len Length of @p pfx in bits. + * Condition @p pfx_len > 0 must hold. + * @param[in] valid_ltime Lifetime (in ms) until prefix expires from now. + * UINT32_MAX for infinite lifetime. Addresses with + * expired prefixes are removed from @p iface. + * @param[in] pref_ltime Lifetime (in ms) until prefix deprecates from now. + * UINT32_MAX for infinite lifetime. Addresses with + * deprecated prefixes should not be used for new + * communication. Only applications with difficulty + * changing to another address without service + * disruption should use deprecated addresses. May not + * be greater then @p valid_ltime. + * + * @return 0, on success. + * @return -EINVAL, if @p pfx was fe80::` or multicast, + * @p pfx_len was == 0, the first @p pfx_len bits of @ pfx were 0, + * or if pref_ltime > valid_ltime. + * @return -ENOMEM, if no space was left in the prefix list. + */ +int gnrc_ipv6_nib_pl_set(unsigned iface, + const ipv6_addr_t *pfx, unsigned pfx_len, + uint32_t valid_ltime, uint32_t pref_ltime); + +/** + * @brief Deletes prefix from NIB + * + * @pre `pfx != NULL` + * + * @param[in] iface The interface @p pfx is expected to be on (0 for any). + * @param[in] pfx The prefix to be removed. + * @param[in] pfx_len Length of @p pfx in bits. + */ +void gnrc_ipv6_nib_pl_del(unsigned iface, + const ipv6_addr_t *pfx, unsigned pfx_len); + +/** + * @brief Iterates over all prefix list entries in the NIB. + * + * @pre `(state != NULL) && (ple != NULL)` + * + * @param[in] iface Restrict iteration to entries on this interface. + * 0 for any interface. + * @param[in,out] state Iteration state of the prefix list. Must point to NULL + * pointer to start iteration + * @param[out] ple The next prefix list entry. + * + * Usage example: + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c} + * #include "net/gnrc/ipv6/nib/pl.h" + * + * int main(void) { + * void *state = NULL; + * gnrc_ipv6_nib_pl_t ple; + * + * puts("My prefixes:"); + * while (gnrc_ipv6_nib_pl_iter(0, &state, &ple)) { + * gnrc_ipv6_nib_pl_print(&ple); + * } + * return 0; + * } + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * @return true, if iteration can be continued. + * @return false, if @p ple is the last prefix list ple in the NIB. + */ +bool gnrc_ipv6_nib_pl_iter(unsigned iface, void **state, + gnrc_ipv6_nib_pl_t *ple); + +/** + * @brief Prints a prefix list entry + * + * @pre `ple != NULL` + * + * @param[in] ple A prefix list entry + */ +void gnrc_ipv6_nib_pl_print(gnrc_ipv6_nib_pl_t *ple); + +#ifdef __cplusplus +} +#endif + +#endif /* NET_GNRC_IPV6_NIB_PL_H */ +/** @} */ diff --git a/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.c b/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.c index 383fddfcfb..1593f94057 100644 --- a/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.c +++ b/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.c @@ -483,6 +483,41 @@ _nib_offl_entry_t *_nib_offl_iter(const _nib_offl_entry_t *last) return NULL; } +_nib_offl_entry_t *_nib_pl_add(unsigned iface, + const ipv6_addr_t *pfx, + unsigned pfx_len, + uint32_t valid_ltime, + uint32_t pref_ltime) +{ + _nib_offl_entry_t *dst = _nib_offl_add(NULL, iface, pfx, pfx_len, _PL); + + if (dst == NULL) { + return NULL; + } + assert(valid_ltime >= pref_ltime); + if ((valid_ltime != UINT32_MAX) || (pref_ltime != UINT32_MAX)) { + uint32_t now = (xtimer_now_usec64() / US_PER_MS) & UINT32_MAX; + if (pref_ltime != UINT32_MAX) { + _evtimer_add(dst, GNRC_IPV6_NIB_PFX_TIMEOUT, &dst->pfx_timeout, + pref_ltime); + if (((pref_ltime + now) == UINT32_MAX) && (now != 0)) { + pref_ltime++; + } + pref_ltime += now; + } + if (valid_ltime != UINT32_MAX) { + /* prevent valid_ltime from becoming UINT32_MAX */ + if ((valid_ltime + now) == UINT32_MAX) { + valid_ltime++; + } + valid_ltime += now; + } + } + dst->valid_until = valid_ltime; + dst->pref_until = pref_ltime; + return dst; +} + _nib_iface_t *_nib_iface_get(unsigned iface) { _nib_iface_t *ni = NULL; diff --git a/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.h b/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.h index 491633b769..0b96040b14 100644 --- a/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.h +++ b/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.h @@ -165,8 +165,16 @@ typedef struct { ipv6_addr_t pfx; /**< prefix to the destination */ unsigned pfx_len; /**< prefix-length in bits of * _nib_onl_entry_t::pfx */ + /** + * @brief Event for @ref GNRC_IPV6_NIB_PFX_TIMEOUT + */ + evtimer_msg_event_t pfx_timeout; uint8_t mode; /**< [mode](@ref net_gnrc_ipv6_nib_mode) of the * off-link entry */ + uint32_t valid_until; /**< timestamp (in ms) until which the prefix + valid (UINT32_MAX means forever) */ + uint32_t pref_until; /**< timestamp (in ms) until which the prefix + preferred (UINT32_MAX means forever) */ } _nib_offl_entry_t; /** @@ -565,6 +573,7 @@ static inline void _nib_dc_remove(_nib_offl_entry_t *nib_offl) * * @pre `(next_hop != NULL)` * @pre `(pfx != NULL) && (pfx != "::") && (pfx_len != 0) && (pfx_len <= 128)` + * @pre `(pref_ltime <= valid_ltime)` * * @param[in] iface The interface to the prefix is added to. * @param[in] pfx The IPv6 prefix or address of the destination. @@ -576,12 +585,11 @@ static inline void _nib_dc_remove(_nib_offl_entry_t *nib_offl) * @p pfx. * @return NULL, if no space is left. */ -static inline _nib_offl_entry_t *_nib_pl_add(unsigned iface, - const ipv6_addr_t *pfx, - unsigned pfx_len) -{ - return _nib_offl_add(NULL, iface, pfx, pfx_len, _PL); -} +_nib_offl_entry_t *_nib_pl_add(unsigned iface, + const ipv6_addr_t *pfx, + unsigned pfx_len, + uint32_t valid_ltime, + uint32_t pref_ltime); /** * @brief Removes a prefix list entry @@ -592,6 +600,7 @@ static inline _nib_offl_entry_t *_nib_pl_add(unsigned iface, */ static inline void _nib_pl_remove(_nib_offl_entry_t *nib_offl) { + evtimer_del(&_nib_evtimer, &nib_offl->pfx_timeout.event); _nib_offl_remove(nib_offl, _PL); } diff --git a/sys/net/gnrc/network_layer/ipv6/nib/nib_pl.c b/sys/net/gnrc/network_layer/ipv6/nib/nib_pl.c new file mode 100644 index 0000000000..635d9dec0d --- /dev/null +++ b/sys/net/gnrc/network_layer/ipv6/nib/nib_pl.c @@ -0,0 +1,120 @@ +/* + * 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 +#include +#include + +#include "net/gnrc/ipv6/nib/pl.h" +#include "timex.h" +#include "xtimer.h" + +#include "_nib-internal.h" + +int gnrc_ipv6_nib_pl_set(unsigned iface, + const ipv6_addr_t *pfx, unsigned pfx_len, + uint32_t valid_ltime, uint32_t pref_ltime) +{ + int res = 0; + _nib_offl_entry_t *dst; + ipv6_addr_t tmp = IPV6_ADDR_UNSPECIFIED; + + assert((pfx != NULL)); + if (pfx_len > IPV6_ADDR_BIT_LEN) { + pfx_len = IPV6_ADDR_BIT_LEN; + } + ipv6_addr_init_prefix(&tmp, pfx, pfx_len); + /* pfx_len == 0 implicitly checked, since this leaves tmp unspecified */ + if (ipv6_addr_is_unspecified(&tmp) || ipv6_addr_is_link_local(pfx) || + ipv6_addr_is_multicast(pfx) || (pref_ltime > valid_ltime)) { + return -EINVAL; + } + mutex_lock(&_nib_mutex); + dst = _nib_pl_add(iface, pfx, pfx_len, valid_ltime, + pref_ltime); + if (dst == NULL) { + res = -ENOMEM; + } + mutex_unlock(&_nib_mutex); + /* TODO: send RA with PIO, if iface is allowed to send RAs */ + return res; +} + +void gnrc_ipv6_nib_pl_del(unsigned iface, + const ipv6_addr_t *pfx, unsigned pfx_len) +{ + _nib_offl_entry_t *dst = NULL; + + assert(pfx != NULL); + mutex_lock(&_nib_mutex); + while ((dst = _nib_offl_iter(dst)) != NULL) { + assert(dst->next_hop != NULL); + if ((pfx_len == dst->pfx_len) && + ((iface == 0) || (iface == _nib_onl_get_if(dst->next_hop))) && + (ipv6_addr_match_prefix(pfx, &dst->pfx) >= pfx_len)) { + _nib_pl_remove(dst); + mutex_unlock(&_nib_mutex); + /* TODO: send RA with PIO, if iface is allowed to send RAs */ + return; + } + } + mutex_unlock(&_nib_mutex); +} + +bool gnrc_ipv6_nib_pl_iter(unsigned iface, void **state, + gnrc_ipv6_nib_pl_t *entry) +{ + _nib_offl_entry_t *dst = *state; + + mutex_lock(&_nib_mutex); + while ((dst = _nib_offl_iter(dst)) != NULL) { + const _nib_onl_entry_t *node = dst->next_hop; + assert(node != NULL); + if ((dst->mode & _PL) && + ((iface == 0) || (_nib_onl_get_if(node) == iface))) { + entry->pfx_len = dst->pfx_len; + ipv6_addr_set_unspecified(&entry->pfx); + ipv6_addr_init_prefix(&entry->pfx, &dst->pfx, dst->pfx_len); + entry->iface = _nib_onl_get_if(node); + entry->valid_until = dst->valid_until; + entry->pref_until = dst->pref_until; + break; + } + } + mutex_unlock(&_nib_mutex); + *state = dst; + return (*state != NULL); +} + +void gnrc_ipv6_nib_pl_print(gnrc_ipv6_nib_pl_t *entry) +{ + char addr_str[IPV6_ADDR_MAX_STR_LEN]; + ipv6_addr_t pfx = IPV6_ADDR_UNSPECIFIED; + uint32_t now = ((xtimer_now_usec64() / US_PER_MS)) & UINT32_MAX; + + ipv6_addr_init_prefix(&pfx, &entry->pfx, entry->pfx_len); + printf("%s/%u ", ipv6_addr_to_str(addr_str, &pfx, sizeof(addr_str)), + entry->pfx_len); + printf("dev #%u ", entry->iface); + if (entry->valid_until < UINT32_MAX) { + printf(" expires %" PRIu32 "sec", (entry->valid_until - now) / MS_PER_SEC); + } + if (entry->pref_until < UINT32_MAX) { + printf(" deprecates %" PRIu32 "sec", (entry->pref_until - now) / MS_PER_SEC); + } + puts(""); +} + +/** @} */ diff --git a/sys/shell/commands/sc_gnrc_ipv6_nib.c b/sys/shell/commands/sc_gnrc_ipv6_nib.c index 975fa2802d..0ec222a70a 100644 --- a/sys/shell/commands/sc_gnrc_ipv6_nib.c +++ b/sys/shell/commands/sc_gnrc_ipv6_nib.c @@ -21,6 +21,7 @@ static void _usage(char **argv); static int _nib_neigh(int argc, char **argv); +static int _nib_prefix(int argc, char **argv); int _gnrc_ipv6_nib(int argc, char **argv) { @@ -33,6 +34,9 @@ int _gnrc_ipv6_nib(int argc, char **argv) else if (strcmp(argv[1], "neigh") == 0) { res = _nib_neigh(argc, argv); } + else if (strcmp(argv[1], "prefix") == 0) { + res = _nib_prefix(argc, argv); + } else { _usage(argv); } @@ -41,7 +45,7 @@ int _gnrc_ipv6_nib(int argc, char **argv) static void _usage(char **argv) { - printf("usage: %s {neigh|help} ...\n", argv[0]); + printf("usage: %s {neigh|prefix|help} ...\n", argv[0]); } static void _usage_nib_neigh(char **argv) @@ -52,6 +56,15 @@ static void _usage_nib_neigh(char **argv) printf(" %s %s show \n", argv[0], argv[1]); } +static void _usage_nib_prefix(char **argv) +{ + printf("usage: %s %s [show|add|del|help]\n", argv[0], argv[1]); + printf(" %s %s add [/] [] []\n", + argv[0], argv[1]); + printf(" %s %s del [/]\n", argv[0], argv[1]); + printf(" %s %s show \n", argv[0], argv[1]); +} + static int _nib_neigh(int argc, char **argv) { if ((argc == 2) || (strcmp(argv[2], "show") == 0)) { @@ -103,4 +116,57 @@ static int _nib_neigh(int argc, char **argv) return 0; } +static int _nib_prefix(int argc, char **argv) +{ + if ((argc == 2) || (strcmp(argv[2], "show") == 0)) { + gnrc_ipv6_nib_pl_t entry; + void *state = NULL; + unsigned iface = 0U; + + if (argc > 3) { + iface = atoi(argv[3]); + } + while (gnrc_ipv6_nib_pl_iter(iface, &state, &entry)) { + gnrc_ipv6_nib_pl_print(&entry); + } + } + else if ((argc > 2) && (strcmp(argv[2], "help") == 0)) { + _usage_nib_prefix(argv); + } + else if ((argc > 4) && (strcmp(argv[2], "add") == 0)) { + ipv6_addr_t pfx; + unsigned iface = atoi(argv[3]); + unsigned pfx_len = ipv6_addr_split_prefix(argv[4]); + unsigned valid_ltime = UINT32_MAX, pref_ltime = UINT32_MAX; + + if (ipv6_addr_from_str(&pfx, argv[4]) == NULL) { + _usage_nib_prefix(argv); + return 1; + } + if (argc > 5) { + valid_ltime = atoi(argv[5]); + } + if (argc > 6) { + pref_ltime = atoi(argv[6]); + } + gnrc_ipv6_nib_pl_set(iface, &pfx, pfx_len, valid_ltime, pref_ltime); + } + else if ((argc > 4) && (strcmp(argv[2], "del") == 0)) { + ipv6_addr_t pfx; + unsigned iface = atoi(argv[3]); + unsigned pfx_len = ipv6_addr_split_prefix(argv[4]); + + if (ipv6_addr_from_str(&pfx, argv[4]) == NULL) { + _usage_nib_prefix(argv); + return 1; + } + gnrc_ipv6_nib_pl_del(iface, &pfx, pfx_len); + } + else { + _usage_nib_prefix(argv); + return 1; + } + return 0; +} + /** @} */ 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 9ec6f07160..abf93e3e22 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 @@ -1647,13 +1647,16 @@ static void test_nib_pl_add__success(void) _nib_offl_entry_t *dst; static const ipv6_addr_t pfx = { .u64 = { { .u8 = GLOBAL_PREFIX } } }; - TEST_ASSERT_NOT_NULL((dst = _nib_pl_add(IFACE, &pfx, GLOBAL_PREFIX_LEN))); + TEST_ASSERT_NOT_NULL((dst = _nib_pl_add(IFACE, &pfx, GLOBAL_PREFIX_LEN, + UINT32_MAX, UINT32_MAX))); TEST_ASSERT(dst->mode & _PL); TEST_ASSERT_EQUAL_INT(GLOBAL_PREFIX_LEN, dst->pfx_len); TEST_ASSERT(GLOBAL_PREFIX_LEN <= ipv6_addr_match_prefix(&pfx, &dst->pfx)); TEST_ASSERT_NOT_NULL(dst->next_hop); TEST_ASSERT_EQUAL_INT(_DST, dst->next_hop->mode); TEST_ASSERT_EQUAL_INT(IFACE, _nib_onl_get_if(dst->next_hop)); + TEST_ASSERT_EQUAL_INT(UINT32_MAX, dst->valid_until); + TEST_ASSERT_EQUAL_INT(UINT32_MAX, dst->pref_until); } /* @@ -1666,7 +1669,8 @@ static void test_nib_pl_remove(void) _nib_offl_entry_t *dst; static const ipv6_addr_t pfx = { .u64 = { { .u8 = GLOBAL_PREFIX } } }; - TEST_ASSERT_NOT_NULL((dst = _nib_pl_add(IFACE, &pfx, GLOBAL_PREFIX_LEN))); + TEST_ASSERT_NOT_NULL((dst = _nib_pl_add(IFACE, &pfx, GLOBAL_PREFIX_LEN, + UINT32_MAX, UINT32_MAX))); _nib_pl_remove(dst); TEST_ASSERT_NULL(_nib_offl_iter(NULL)); } diff --git a/tests/unittests/tests-gnrc_ipv6_nib/tests-gnrc_ipv6_nib-pl.c b/tests/unittests/tests-gnrc_ipv6_nib/tests-gnrc_ipv6_nib-pl.c new file mode 100644 index 0000000000..5fe350d90b --- /dev/null +++ b/tests/unittests/tests-gnrc_ipv6_nib/tests-gnrc_ipv6_nib-pl.c @@ -0,0 +1,407 @@ +/* + * Copyright (C) 2016 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 + +#include "net/ipv6/addr.h" +#include "net/gnrc/ipv6/nib.h" +#include "net/gnrc/ipv6/nib/pl.h" + +#include "_nib-internal.h" + +#include "unittests-constants.h" + +#include "tests-gnrc_ipv6_nib.h" + +#define LINK_LOCAL_PREFIX { 0xfe, 0x08, 0, 0, 0, 0, 0, 0 } +#define GLOBAL_PREFIX { 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0 } +#define L2ADDR { 0x90, 0xd5, 0x8e, 0x8c, 0x92, 0x43, 0x73, 0x5c } +#define GLOBAL_PREFIX_LEN (30) +#define IFACE (6) + +static void set_up(void) +{ + /* TODO: add if #6988 gets merged */ + /* evtimer_event_t *tmp; */ + + /* for (evtimer_event_t *ptr = _nib_evtimer.events; */ + /* (ptr != NULL) && (tmp = (ptr->next), 1); */ + /* ptr = tmp) { */ + /* evtimer_del((evtimer_t *)(&_nib_evtimer), ptr); */ + /* } */ + _nib_init(); +} + +/* + * Tries to create a prefix list entry with the unspecified address (::) as + * prefix. + * Expected result: gnrc_ipv6_nib_pl_set() returns -EINVAL + */ +static void test_nib_pl_set__EINVAL_unspec_addr(void) +{ + TEST_ASSERT_EQUAL_INT(-EINVAL, gnrc_ipv6_nib_pl_set(IFACE, + &ipv6_addr_unspecified, + GLOBAL_PREFIX_LEN, + UINT32_MAX, UINT32_MAX)); +} + +/* + * Tries to create a prefix list entry with a link-local prefix (fe80::) as + * prefix. + * Expected result: gnrc_ipv6_nib_pl_set() returns -EINVAL + */ +static void test_nib_pl_set__EINVAL_link_local(void) +{ + TEST_ASSERT_EQUAL_INT(-EINVAL, gnrc_ipv6_nib_pl_set(IFACE, + &ipv6_addr_link_local_prefix, + GLOBAL_PREFIX_LEN, + UINT32_MAX, + UINT32_MAX)); +} + +/* + * Tries to create a prefix list entry with the multicast address as + * prefix. + * Expected result: gnrc_ipv6_nib_pl_set() returns -EINVAL + */ +static void test_nib_pl_set__EINVAL_mc_addr(void) +{ + TEST_ASSERT_EQUAL_INT(-EINVAL, gnrc_ipv6_nib_pl_set(IFACE, + &ipv6_addr_all_nodes_link_local, + GLOBAL_PREFIX_LEN, + UINT32_MAX, + UINT32_MAX)); +} + +/* + * Tries to create multiple prefix list entries with prefix length 0 and > 128. + * Expected result: gnrc_ipv6_nib_pl_set() always returns -EINVAL + */ +static void test_nib_pl_set__EINVAL_pfx_len(void) +{ + static const ipv6_addr_t pfx = { .u64 = { { .u8 = GLOBAL_PREFIX } } }; + TEST_ASSERT_EQUAL_INT(-EINVAL, gnrc_ipv6_nib_pl_set(IFACE, &pfx, 0, + UINT32_MAX, + UINT32_MAX)); +} + +#if GNRC_IPV6_NIB_NUMOF < GNRC_IPV6_NIB_OFFL_NUMOF +#define MAX_NUMOF (GNRC_IPV6_NIB_NUMOF) +#else /* GNRC_IPV6_NIB_NUMOF < GNRC_IPV6_NIB_OFFL_NUMOF */ +#define MAX_NUMOF (GNRC_IPV6_NIB_OFFL_NUMOF) +#endif + +/* + * Creates MAX_NUMOF prefix list entries with different interfaces and then + * tries to create another one + * Expected result: gnrc_ipv6_nib_pl_set() returns -ENOMEM + */ +static void test_nib_pl_set__ENOMEM_diff_iface(void) +{ + static const ipv6_addr_t pfx = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + unsigned iface = IFACE; + + for (unsigned i = 0; i < MAX_NUMOF; i++) { + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_pl_set(iface, &pfx, + GLOBAL_PREFIX_LEN, + UINT32_MAX, UINT32_MAX)); + iface++; + } + TEST_ASSERT_EQUAL_INT(-ENOMEM, gnrc_ipv6_nib_pl_set(iface, &pfx, + GLOBAL_PREFIX_LEN, + UINT32_MAX, + UINT32_MAX)); +} + +/* + * Creates GNRC_IPV6_NIB_OFFL_NUMOF prefix list entries with different prefix of + * the same length and then tries to create another one + * Expected result: gnrc_ipv6_nib_pl_set() returns -ENOMEM + */ +static void test_nib_pl_set__ENOMEM_diff_pfx(void) +{ + ipv6_addr_t pfx = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + + for (unsigned i = 0; i < GNRC_IPV6_NIB_OFFL_NUMOF; i++) { + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_pl_set(IFACE, &pfx, + GLOBAL_PREFIX_LEN, + UINT32_MAX, UINT32_MAX)); + pfx.u16[0].u16++; + } + TEST_ASSERT_EQUAL_INT(-ENOMEM, gnrc_ipv6_nib_pl_set(IFACE, &pfx, + GLOBAL_PREFIX_LEN, + UINT32_MAX, + UINT32_MAX)); +} + +/* + * Creates GNRC_IPV6_NIB_OFFL_NUMOF prefix list entries with different prefix of + * the same length and different interfaces and then tries to create another one + * Expected result: gnrc_ipv6_nib_pl_set() returns -ENOMEM + */ +static void test_nib_pl_set__ENOMEM_diff_iface_pfx(void) +{ + ipv6_addr_t pfx = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + unsigned iface = IFACE; + + for (unsigned i = 0; i < MAX_NUMOF; i++) { + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_pl_set(iface, &pfx, + GLOBAL_PREFIX_LEN, + UINT32_MAX, UINT32_MAX)); + iface++; + pfx.u16[0].u16++; + } + TEST_ASSERT_EQUAL_INT(-ENOMEM, gnrc_ipv6_nib_pl_set(iface, &pfx, + GLOBAL_PREFIX_LEN, + UINT32_MAX, + UINT32_MAX)); +} + +/* + * Creates GNRC_IPV6_NIB_OFFL_NUMOF prefix list entries with prefixes of + * different length and then tries to create another one + * Expected result: gnrc_ipv6_nib_pl_set() returns -ENOMEM + */ +static void test_nib_pl_set__ENOMEM_diff_pfx_len(void) +{ + static const ipv6_addr_t pfx = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + unsigned pfx_len = GLOBAL_PREFIX_LEN; + + for (unsigned i = 0; i < GNRC_IPV6_NIB_OFFL_NUMOF; i++) { + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_pl_set(IFACE, &pfx, pfx_len, + UINT32_MAX, UINT32_MAX)); + pfx_len--; + } + TEST_ASSERT_EQUAL_INT(-ENOMEM, gnrc_ipv6_nib_pl_set(IFACE, &pfx, pfx_len, + UINT32_MAX, + UINT32_MAX)); +} + +/* + * Creates MAX_NUMOF prefix list entries with prefixes of different length and + * different interfaces and then tries to create another one + * Expected result: gnrc_ipv6_nib_pl_set() returns -ENOMEM + */ +static void test_nib_pl_set__ENOMEM_diff_iface_pfx_len(void) +{ + static const ipv6_addr_t pfx = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + unsigned pfx_len = GLOBAL_PREFIX_LEN, iface = IFACE; + + for (unsigned i = 0; i < MAX_NUMOF; i++) { + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_pl_set(iface, &pfx, pfx_len, + UINT32_MAX, UINT32_MAX)); + pfx_len--; + iface++; + } + TEST_ASSERT_EQUAL_INT(-ENOMEM, gnrc_ipv6_nib_pl_set(iface, &pfx, pfx_len, + UINT32_MAX, + UINT32_MAX)); +} + +/* + * Creates GNRC_IPV6_NIB_OFFL_NUMOF prefix list entries with different prefixes + * and then tries to create another one + * Expected result: gnrc_ipv6_nib_pl_set() returns -ENOMEM + */ +static void test_nib_pl_set__ENOMEM_diff_pfx_pfx_len(void) +{ + ipv6_addr_t pfx = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + unsigned pfx_len = GLOBAL_PREFIX_LEN; + + for (unsigned i = 0; i < GNRC_IPV6_NIB_OFFL_NUMOF; i++) { + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_pl_set(IFACE, &pfx, pfx_len, + UINT32_MAX, UINT32_MAX)); + pfx_len--; + pfx.u16[0].u16++; + } + TEST_ASSERT_EQUAL_INT(-ENOMEM, gnrc_ipv6_nib_pl_set(IFACE, &pfx, pfx_len, + UINT32_MAX, + UINT32_MAX)); +} + +/* + * Creates MAX_NUMOF prefix list entries with different prefixes and different + * interfaces then tries to create another one + * Expected result: gnrc_ipv6_nib_pl_set() returns -ENOMEM + */ +static void test_nib_pl_set__ENOMEM_diff_iface_pfx_pfx_len(void) +{ + ipv6_addr_t pfx = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + unsigned pfx_len = GLOBAL_PREFIX_LEN, iface = IFACE; + + for (unsigned i = 0; i < MAX_NUMOF; i++) { + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_pl_set(iface, &pfx, pfx_len, + UINT32_MAX, UINT32_MAX)); + pfx_len--; + pfx.u16[0].u16++; + iface++; + } + TEST_ASSERT_EQUAL_INT(-ENOMEM, gnrc_ipv6_nib_pl_set(iface, &pfx, pfx_len, + UINT32_MAX, + UINT32_MAX)); +} + +/* + * Creates MAX_NUMOF prefix list entries with different prefixes and different + * interfaces and then tries to add another equal to the last. + * Expected result: should return not NULL (the last) + */ +static void test_nib_pl_set__success_duplicate(void) +{ + ipv6_addr_t pfx = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + unsigned pfx_len = GLOBAL_PREFIX_LEN, iface = IFACE; + + for (unsigned i = 0; i < MAX_NUMOF; i++) { + pfx_len--; + pfx.u16[0].u16++; + iface++; + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_pl_set(iface, &pfx, pfx_len, + UINT32_MAX, UINT32_MAX)); + } + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_pl_set(iface, &pfx, pfx_len, + UINT32_MAX, UINT32_MAX)); +} + +/* + * Creates a prefix list entry then creates it again with a different valid and + * preferred lifetime. + * Expected result: should be able to be created and the lifetimes should have + * been changed + */ +static void test_nib_pl_set__success_change(void) +{ + gnrc_ipv6_nib_pl_t ple; + void *iter_state = NULL; + static const ipv6_addr_t pfx = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_pl_set(IFACE, &pfx, + GLOBAL_PREFIX_LEN, + UINT32_MAX, UINT32_MAX)); + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_pl_set(IFACE, &pfx, + GLOBAL_PREFIX_LEN, + TEST_UINT32, + TEST_UINT32 - TEST_UINT8)); + TEST_ASSERT(gnrc_ipv6_nib_pl_iter(0, &iter_state, &ple)); + TEST_ASSERT(ple.valid_until != UINT32_MAX); + TEST_ASSERT(ple.pref_until != UINT32_MAX); + TEST_ASSERT(!gnrc_ipv6_nib_pl_iter(0, &iter_state, &ple)); +} + +/* + * Creates a prefix list entry then creates it again with a different valid and + * preferred lifetime. + * Expected result: a new entry should exist and contain the given prefix, + * interface, and lifetimes + */ +static void test_nib_pl_set__success(void) +{ + gnrc_ipv6_nib_pl_t ple; + void *iter_state = NULL; + static const ipv6_addr_t pfx = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_pl_set(IFACE, &pfx, + GLOBAL_PREFIX_LEN, + UINT32_MAX, UINT32_MAX)); + TEST_ASSERT(gnrc_ipv6_nib_pl_iter(0, &iter_state, &ple)); + TEST_ASSERT(ipv6_addr_match_prefix(&ple.pfx, &pfx) >= GLOBAL_PREFIX_LEN); + TEST_ASSERT_EQUAL_INT(GLOBAL_PREFIX_LEN, ple.pfx_len); + TEST_ASSERT_EQUAL_INT(IFACE, ple.iface); + TEST_ASSERT_EQUAL_INT(UINT32_MAX, ple.valid_until); + TEST_ASSERT_EQUAL_INT(UINT32_MAX, ple.pref_until); + TEST_ASSERT(!gnrc_ipv6_nib_pl_iter(0, &iter_state, &ple)); +} + +/* + * Creates MAX_NUMOF prefix list entries with different prefix and interfaces + * and then tries to delete one with yet another prefix and interface. + * Expected result: There should be still GNRC_IPV6_NIB_NUMOF entries in the + * neigbor cache + */ +static void test_nib_pl_del__unknown(void) +{ + void *iter_state = NULL; + ipv6_addr_t pfx = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + gnrc_ipv6_nib_pl_t ple; + unsigned pfx_len = GLOBAL_PREFIX_LEN, iface = IFACE, count = 0; + + for (unsigned i = 0; i < MAX_NUMOF; i++) { + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_pl_set(iface, &pfx, pfx_len, + UINT32_MAX, UINT32_MAX)); + pfx_len--; + pfx.u16[0].u16++; + iface++; + } + gnrc_ipv6_nib_pl_del(iface, &pfx, pfx_len); + while (gnrc_ipv6_nib_pl_iter(0, &iter_state, &ple)) { + count++; + } + TEST_ASSERT_EQUAL_INT(MAX_NUMOF, count); +} + +/* + * Creates a prefix entry and removes it. + * Expected result: prefix entry should be empty + */ +static void test_nib_pl_del__success(void) +{ + void *iter_state = NULL; + ipv6_addr_t pfx = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + gnrc_ipv6_nib_pl_t ple; + + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_pl_set(IFACE, &pfx, GLOBAL_PREFIX_LEN, + UINT32_MAX, UINT32_MAX)); + gnrc_ipv6_nib_pl_del(IFACE, &pfx, GLOBAL_PREFIX_LEN); + TEST_ASSERT(!gnrc_ipv6_nib_pl_iter(0, &iter_state, &ple)); +} + +Test *tests_gnrc_ipv6_nib_pl_tests(void) +{ + EMB_UNIT_TESTFIXTURES(fixtures) { + new_TestFixture(test_nib_pl_set__EINVAL_unspec_addr), + new_TestFixture(test_nib_pl_set__EINVAL_link_local), + new_TestFixture(test_nib_pl_set__EINVAL_mc_addr), + new_TestFixture(test_nib_pl_set__EINVAL_pfx_len), + new_TestFixture(test_nib_pl_set__ENOMEM_diff_pfx), + new_TestFixture(test_nib_pl_set__ENOMEM_diff_iface), + new_TestFixture(test_nib_pl_set__ENOMEM_diff_iface_pfx), + new_TestFixture(test_nib_pl_set__ENOMEM_diff_pfx_len), + new_TestFixture(test_nib_pl_set__ENOMEM_diff_iface_pfx_len), + new_TestFixture(test_nib_pl_set__ENOMEM_diff_pfx_pfx_len), + new_TestFixture(test_nib_pl_set__ENOMEM_diff_iface_pfx_pfx_len), + new_TestFixture(test_nib_pl_set__success_duplicate), + new_TestFixture(test_nib_pl_set__success_change), + new_TestFixture(test_nib_pl_set__success), + new_TestFixture(test_nib_pl_del__unknown), + new_TestFixture(test_nib_pl_del__success), + /* gnrc_ipv6_nib_pl_iter() is tested during all the tests above */ + }; + + EMB_UNIT_TESTCALLER(tests, set_up, NULL, + fixtures); + + return (Test *)&tests; +} diff --git a/tests/unittests/tests-gnrc_ipv6_nib/tests-gnrc_ipv6_nib.c b/tests/unittests/tests-gnrc_ipv6_nib/tests-gnrc_ipv6_nib.c index 7682229e50..93258f94f9 100644 --- a/tests/unittests/tests-gnrc_ipv6_nib/tests-gnrc_ipv6_nib.c +++ b/tests/unittests/tests-gnrc_ipv6_nib/tests-gnrc_ipv6_nib.c @@ -19,4 +19,5 @@ void tests_gnrc_ipv6_nib(void) { TESTS_RUN(tests_gnrc_ipv6_nib_internal_tests()); TESTS_RUN(tests_gnrc_ipv6_nib_nc_tests()); + TESTS_RUN(tests_gnrc_ipv6_nib_pl_tests()); } diff --git a/tests/unittests/tests-gnrc_ipv6_nib/tests-gnrc_ipv6_nib.h b/tests/unittests/tests-gnrc_ipv6_nib/tests-gnrc_ipv6_nib.h index cc58551781..a7763061e0 100644 --- a/tests/unittests/tests-gnrc_ipv6_nib/tests-gnrc_ipv6_nib.h +++ b/tests/unittests/tests-gnrc_ipv6_nib/tests-gnrc_ipv6_nib.h @@ -43,6 +43,13 @@ Test *tests_gnrc_ipv6_nib_internal_tests(void); */ Test *tests_gnrc_ipv6_nib_nc_tests(void); +/** + * @brief Generates tests for prefix list view + * + * @return embUnit tests if successful, NULL if not. + */ +Test *tests_gnrc_ipv6_nib_pl_tests(void); + #ifdef __cplusplus } #endif