From d8acdc9d2d8422318abe0dbb8540f210e5ce62d2 Mon Sep 17 00:00:00 2001 From: Martine Lenders Date: Fri, 23 Jun 2017 19:33:25 +0200 Subject: [PATCH 1/3] gnrc_ipv6_nib: add forwarding table component --- sys/include/net/gnrc/ipv6/nib.h | 1 + sys/include/net/gnrc/ipv6/nib/ft.h | 148 ++++++++++++++++++ .../network_layer/ipv6/nib/_nib-internal.c | 93 +++++++++++ .../network_layer/ipv6/nib/_nib-internal.h | 50 ++++++ sys/net/gnrc/network_layer/ipv6/nib/nib_ft.c | 146 +++++++++++++++++ 5 files changed, 438 insertions(+) create mode 100644 sys/include/net/gnrc/ipv6/nib/ft.h create mode 100644 sys/net/gnrc/network_layer/ipv6/nib/nib_ft.c diff --git a/sys/include/net/gnrc/ipv6/nib.h b/sys/include/net/gnrc/ipv6/nib.h index 3a4db2763f..cd0631241f 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/abr.h" +#include "net/gnrc/ipv6/nib/ft.h" #include "net/gnrc/ipv6/nib/nc.h" #include "net/gnrc/ipv6/nib/pl.h" diff --git a/sys/include/net/gnrc/ipv6/nib/ft.h b/sys/include/net/gnrc/ipv6/nib/ft.h new file mode 100644 index 0000000000..2b7306f167 --- /dev/null +++ b/sys/include/net/gnrc/ipv6/nib/ft.h @@ -0,0 +1,148 @@ +/* + * 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_ft Forwarding table + * @ingroup net_gnrc_ipv6_nib + * @brief + * @{ + * + * @file + * @brief Forwarding table definitions + * + * @author Martine Lenders + */ +#ifndef NET_GNRC_IPV6_NIB_FT_H +#define NET_GNRC_IPV6_NIB_FT_H + +#include + +#include "net/gnrc/pkt.h" +#include "net/ipv6/addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Forwarding table entry view on NIB + */ +typedef struct { + ipv6_addr_t dst; /**< destination or prefix */ + ipv6_addr_t next_hop; /**< next hop to gnrc_ipv6_nib_ft_t::dst */ + uint8_t dst_len; /**< prefix-length in bits of + * gnrc_ipv6_nib_ft_t::dst */ + uint8_t primary; /**< != 0 if gnrc_ipv6_nib_ft_t::dst is preferred + * default route */ + uint16_t iface; /**< interface to gnrc_ipv6_nib_ft_t::next_hop */ +} gnrc_ipv6_nib_ft_t; + +/** + * @brief Gets the best matching forwarding table entry to a destination + * + * @pre `(dst != NULL) && (fte != NULL)` + * + * @param[in] dst The destination. + * @param[in] pkt Packet that is supposed to go to that destination + * (is handed over to a reactive routing protocol if one exists + * on the interface found and no route is found) + * @param[out] fte The resulting forwarding table entry. + * + * @return 0, on success. + * @return -ENETUNREACH, if no route was found. + */ +int gnrc_ipv6_nib_ft_get(const ipv6_addr_t *dst, gnrc_pktsnip_t *pkt, + gnrc_ipv6_nib_ft_t *fte); + +/** + * @brief Adds a new route to the forwarding table + * + * @param[in] dst The destination to the route. May be NULL or `::` for + * default route. + * @param[in] dst_len The prefix length of @p dst in bits. May be 0 for + * default route. + * @param[in] next_hop The next hop to @p dst/@p dst_len. May be NULL, if + * @p dst/@p dst_len is no the default route. + * @param[in] iface The interface to @p next_hop. May not be 0. + * + * @return 0, on success. + * @return -EINVAL, if a parameter was of invalid value. + * @return -ENOMEM, if there was no space left in forwarding table. + */ +int gnrc_ipv6_nib_ft_add(const ipv6_addr_t *dst, unsigned dst_len, + const ipv6_addr_t *next_hop, unsigned iface); + +/** + * @brief Deletes a route from forwarding table. + * + * @param[in] dst The destination of the route. May be NULL or `::` for + * default route. + * @param[in] dst_len The prefix length of @p dst in bits. May be 0 for + * default route. + */ +void gnrc_ipv6_nib_ft_del(const ipv6_addr_t *dst, unsigned dst_len); + +/** + * @brief Iterates over all forwarding table entries in the NIB + * + * @pre `(state != NULL) && (fte != NULL)` + * + * @param[in] next_hop Restrict iteration to entries to this next hop. NULL + * for any next hop. Can be used to build a source + * routing tree. + * @param[in] iface Restrict iteration to entries on this interface. + * 0 for any interface. + * @param[in,out] state Iteration state of the forwarding table. Must point + * to a NULL pointer to start iteration. + * @param[out] fte The next forwarding table entry. + * + * The iteration over all forwarding table entries in the NIB includes all + * entries added via @p gnrc_ipv6_nib_ft_add() and entries that are currently + * in the Destination Cache, in the Prefix List, and in the Default Router List. + * + * Usage example: + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c} + * #include "net/gnrc/ipv6/nib/ft.h" + * + * int main(void) { + * void *state = NULL; + * gnrc_ipv6_nib_ft_t fte; + * + * puts("My neighbors:"); + * while (gnrc_ipv6_nib_ft_iter(NULL, 0, &state, &fte)) { + * gnrc_ipv6_nib_ft_print(&fte); + * } + * return 0; + * } + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * @note The list may change during iteration. + * + * @return true, if iteration can be continued. + * @return false, if @p fte is the last neighbor cache entry in the NIB. + */ +bool gnrc_ipv6_nib_ft_iter(const ipv6_addr_t *next_hop, unsigned iface, + void **state, gnrc_ipv6_nib_ft_t *fte); + + +/** + * @brief Prints a forwarding table entry + * + * @pre `fce != NULL` + * + * @param[in] fte A forwarding table entry. + */ +void gnrc_ipv6_nib_ft_print(const gnrc_ipv6_nib_ft_t *fte); + +#ifdef __cplusplus +} +#endif + +#endif /* NET_GNRC_IPV6_NIB_FT_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 87049f245d..129c60b617 100644 --- a/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.c +++ b/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.c @@ -401,6 +401,18 @@ _nib_dr_entry_t *_nib_drl_get_dr(void) return _prime_def_router; } +void _nib_drl_ft_get(const _nib_dr_entry_t *drl, gnrc_ipv6_nib_ft_t *fte) +{ + assert((drl != NULL) && (drl->next_hop != NULL) && (fte != NULL)); + ipv6_addr_set_unspecified(&fte->dst); + fte->dst_len = 0; + fte->primary = ((drl == _prime_def_router) && + !((_prime_def_router == NULL) || + (_node_unreachable(_prime_def_router->next_hop)))); + memcpy(&fte->next_hop, &drl->next_hop->ipv6, sizeof(fte->next_hop)); + fte->iface = _nib_onl_get_if(drl->next_hop); +} + _nib_offl_entry_t *_nib_offl_alloc(const ipv6_addr_t *next_hop, unsigned iface, const ipv6_addr_t *pfx, unsigned pfx_len) { @@ -497,6 +509,87 @@ _nib_offl_entry_t *_nib_offl_iter(const _nib_offl_entry_t *last) return NULL; } +bool _nib_offl_is_entry(const _nib_offl_entry_t *entry) +{ + return (entry >= _dsts) && _in_dsts(entry); +} + +static _nib_offl_entry_t *_nib_offl_get_match(const ipv6_addr_t *dst) +{ + _nib_offl_entry_t *res = NULL; + uint8_t best_match = 0; + + DEBUG("nib: get match for destination %s from NIB\n", + ipv6_addr_to_str(addr_str, dst, sizeof(addr_str))); + for (_nib_offl_entry_t *entry = _dsts; _in_dsts(entry); entry++) { + if (entry->mode != _EMPTY) { + uint8_t match = ipv6_addr_match_prefix(&entry->pfx, dst); + + DEBUG("nib: %s/%u => ", + ipv6_addr_to_str(addr_str, &entry->pfx, sizeof(addr_str)), + entry->pfx_len); + DEBUG("%s%%%u matches with %u bits\n", + (entry->mode == _PL) ? "(nil)" : + ipv6_addr_to_str(addr_str, &entry->next_hop->ipv6, + sizeof(addr_str)), + _nib_onl_get_if(entry->next_hop), match); + if ((match > best_match) && (match >= entry->pfx_len)) { + DEBUG("nib: best match (%u bits)\n", match); + res = entry; + best_match = match; + } + } + } + return res; +} + +void _nib_ft_get(const _nib_offl_entry_t *dst, gnrc_ipv6_nib_ft_t *fte) +{ + assert((dst != NULL) && (dst->next_hop != NULL) && (fte != NULL)); + memcpy(&fte->dst, &dst->pfx, sizeof(dst->pfx)); + fte->dst_len = dst->pfx_len; + fte->primary = 0; + fte->iface = _nib_onl_get_if(dst->next_hop); + if (dst->mode == _PL) { /* entry is only in prefix list */ + ipv6_addr_set_unspecified(&fte->next_hop); + } + else { + memcpy(&fte->next_hop, &dst->next_hop->ipv6, sizeof(dst->next_hop->ipv6)); + } +} + +int _nib_get_route(const ipv6_addr_t *dst, gnrc_pktsnip_t *pkt, + gnrc_ipv6_nib_ft_t *fte) +{ + assert((dst != NULL) && (fte != NULL)); + DEBUG("nib: get route %s for packet %p\n", + ipv6_addr_to_str(addr_str, dst, sizeof(addr_str)), + (void *)pkt); + _nib_offl_entry_t *offl = _nib_offl_get_match(dst); + + assert((dst != NULL) && (fte != NULL)); + if ((offl == NULL) || (offl->mode == _PL)) { + /* give default router precedence over PLE */ + _nib_dr_entry_t *router = _nib_drl_get_dr(); + + if ((router == NULL) && (offl == NULL)) { + (void)pkt; + /* TODO: ask RRP to search for route (using pkt) */ + return -ENETUNREACH; + } + else if (router != NULL) { + DEBUG("nib: prefer default router %s%%%u over prefix list entry\n", + ipv6_addr_to_str(addr_str, &router->next_hop->ipv6, + sizeof(addr_str)), + _nib_onl_get_if(router->next_hop)); + _nib_drl_ft_get(router, fte); + return 0; + } + } + _nib_ft_get(offl, fte); + return 0; +} + void _nib_pl_remove(_nib_offl_entry_t *nib_offl) { _nib_offl_remove(nib_offl, _PL); 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 3dd34e6b2f..bd6eb009f1 100644 --- a/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.h +++ b/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.h @@ -32,6 +32,7 @@ #ifdef MODULE_GNRC_IPV6 #include "net/gnrc/ipv6.h" #endif +#include "net/gnrc/ipv6/nib/ft.h" #include "net/gnrc/ipv6/nib/nc.h" #include "net/gnrc/ipv6/nib/conf.h" #include "net/gnrc/pktqueue.h" @@ -463,6 +464,17 @@ _nib_dr_entry_t *_nib_drl_iter(const _nib_dr_entry_t *last); */ _nib_dr_entry_t *_nib_drl_get(const ipv6_addr_t *router_addr, unsigned iface); +/** + * @brief Gets external forwarding table entry representation from default + * router entry + * + * @pre `(drl != NULL) && (drl->next_hop != NULL) && (fte != NULL)` + * + * @param[in] drl Default router entry. + * @param[out] fte External representation of the forwarding table entry. + */ +void _nib_drl_ft_get(const _nib_dr_entry_t *drl, gnrc_ipv6_nib_ft_t *fte); + /** * @brief Gets *the* default router * @@ -509,6 +521,16 @@ void _nib_offl_clear(_nib_offl_entry_t *dst); */ _nib_offl_entry_t *_nib_offl_iter(const _nib_offl_entry_t *last); +/** + * @brief Checks if @p entry was allocated using _nib_offl_alloc() + * + * @param[in] entry An entry. + * + * @return true, if @p entry was allocated using @ref _nib_offl_alloc() + * @return false, if @p entry was not allocated using @ref _nib_offl_alloc() + */ +bool _nib_offl_is_entry(const _nib_offl_entry_t *entry); + /** * @brief Helper function for view-level add-functions below * @@ -726,6 +748,34 @@ _nib_offl_entry_t *_nib_abr_iter_pfx(const _nib_abr_entry_t *abr, _nib_abr_entry_t *_nib_abr_iter(const _nib_abr_entry_t *last); #endif +/** + * @brief Gets external forwarding table entry representation from off-link + * entry + * + * @pre `(dst != NULL) && (dst->next_hop != NULL) && (fte != NULL)` + * + * @param[in] dst Off-link entry. + * @param[out] fte External representation of the forwarding table entry. + */ +void _nib_ft_get(const _nib_offl_entry_t *dst, gnrc_ipv6_nib_ft_t *fte); + +/** + * @brief Gets best match to @p dst from all off-link entries and default + * route. + * + * @pre `(dst != NULL) && (fte != NULL)` + * + * @param[in] dst Destination address to get the off-link entry for. + * @param[in] pkt Packet causing the route look-up (provided to allow reactive + * routing protocols to queue it if needed). May be NULL. + * @param[out] fte Resulting forwarding table entry. + * + * @return 0, on success. + * @return -ENETUNREACH, when no route was found. + */ +int _nib_get_route(const ipv6_addr_t *dst, gnrc_pktsnip_t *ctx, + gnrc_ipv6_nib_ft_t *entry); + /** * @brief Gets (or creates if it not exists) interface information for * neighbor discovery diff --git a/sys/net/gnrc/network_layer/ipv6/nib/nib_ft.c b/sys/net/gnrc/network_layer/ipv6/nib/nib_ft.c new file mode 100644 index 0000000000..4b4a965bd7 --- /dev/null +++ b/sys/net/gnrc/network_layer/ipv6/nib/nib_ft.c @@ -0,0 +1,146 @@ +/* + * 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 "_nib-internal.h" + +#include "net/gnrc/ipv6/nib/ft.h" + +int gnrc_ipv6_nib_ft_get(const ipv6_addr_t *dst, gnrc_pktsnip_t *pkt, + gnrc_ipv6_nib_ft_t *fte) +{ + int res; + + assert((dst != NULL) && (fte != NULL)); + mutex_lock(&_nib_mutex); + res = _nib_get_route(dst, pkt, fte); + mutex_unlock(&_nib_mutex); + return res; +} + +int gnrc_ipv6_nib_ft_add(const ipv6_addr_t *dst, unsigned dst_len, + const ipv6_addr_t *next_hop, unsigned iface) +{ + void *ptr = NULL; + int res = 0; + bool is_default_route = ((dst == NULL) || (dst_len == 0) || + ipv6_addr_is_unspecified(dst)); + + if ((iface == 0) || ((is_default_route) && (next_hop == NULL))) { + return -EINVAL; + } + mutex_lock(&_nib_mutex); + if (is_default_route) { + ptr = _nib_drl_add(next_hop, iface); + } +#if GNRC_IPV6_NIB_CONF_ROUTER + else { + dst_len = (dst_len > 128) ? 128 : dst_len; + ptr = _nib_ft_add(next_hop, iface, dst, dst_len); + } +#else /* GNRC_IPV6_NIB_CONF_ROUTER */ + else { + res = -ENOTSUP; + } +#endif + mutex_unlock(&_nib_mutex); + if (ptr == NULL) { + res = -ENOMEM; + } + return res; +} + +void gnrc_ipv6_nib_ft_del(const ipv6_addr_t *dst, unsigned dst_len) +{ + mutex_lock(&_nib_mutex); + if ((dst == NULL) || (dst_len == 0) || ipv6_addr_is_unspecified(dst)) { + _nib_dr_entry_t *entry = _nib_drl_get_dr(); + + if (entry != NULL) { + _nib_drl_remove(entry); + } + } +#if GNRC_IPV6_NIB_CONF_ROUTER + else { + _nib_offl_entry_t *entry = NULL; + + while ((entry = _nib_offl_iter(entry))) { + if ((entry->pfx_len == dst_len) && + (ipv6_addr_match_prefix(&entry->pfx, dst) >= dst_len)) { + _nib_ft_remove(entry); + break; + } + } + } +#endif + mutex_unlock(&_nib_mutex); +} + +bool gnrc_ipv6_nib_ft_iter(const ipv6_addr_t *next_hop, unsigned iface, + void **state, gnrc_ipv6_nib_ft_t *fte) +{ + _nib_dr_entry_t *entry; + assert((state != NULL) && (fte != NULL)); + + if ((*state == NULL) || _nib_offl_is_entry(*state)) { + _nib_offl_entry_t *offl = *state; + + while ((offl = _nib_offl_iter(offl))) { + assert((offl->mode != 0) || (offl->next_hop != NULL)); + if (((iface == 0) || (iface == _nib_onl_get_if(offl->next_hop))) && + ((next_hop == NULL) || ipv6_addr_equal(&offl->next_hop->ipv6, + next_hop))) { + _nib_ft_get(offl, fte); + *state = offl; + return true; + } + } + *state = NULL; + } + entry = *state; + while ((entry = _nib_drl_iter(entry))) { + assert((entry->next_hop != NULL)); + if (((iface == 0) || (iface == _nib_onl_get_if(entry->next_hop))) && + ((next_hop == NULL) || ipv6_addr_equal(&entry->next_hop->ipv6, + next_hop))) { + _nib_drl_ft_get(entry, fte); + break; + } + } + *state = entry; + return (*state != NULL); +} + +void gnrc_ipv6_nib_ft_print(const gnrc_ipv6_nib_ft_t *fte) +{ + char addr_str[IPV6_ADDR_MAX_STR_LEN]; + + if ((fte->dst_len == 0) || ipv6_addr_is_unspecified(&fte->dst)) { + printf("default%s ", (fte->primary ? "*" : "")); + } + else { + printf("%s/%u ", ipv6_addr_to_str(addr_str, &fte->dst, sizeof(addr_str)), + fte->dst_len); + } + if (!ipv6_addr_is_unspecified(&fte->next_hop)) { + printf("via %s ", ipv6_addr_to_str(addr_str, &fte->next_hop, + sizeof(addr_str))); + } + printf("dev #%u\n", fte->iface); +} + +/**i @} */ From e1a37a01ac716264b013cec5440c34c4497d15aa Mon Sep 17 00:00:00 2001 From: Martine Lenders Date: Fri, 23 Jun 2017 19:39:02 +0200 Subject: [PATCH 2/3] shell: commands: extend nib shell command for forwarding table --- sys/shell/commands/sc_gnrc_ipv6_nib.c | 68 ++++++++++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/sys/shell/commands/sc_gnrc_ipv6_nib.c b/sys/shell/commands/sc_gnrc_ipv6_nib.c index 0ec222a70a..4c7c2f34b7 100644 --- a/sys/shell/commands/sc_gnrc_ipv6_nib.c +++ b/sys/shell/commands/sc_gnrc_ipv6_nib.c @@ -22,6 +22,7 @@ static void _usage(char **argv); static int _nib_neigh(int argc, char **argv); static int _nib_prefix(int argc, char **argv); +static int _nib_route(int argc, char **argv); int _gnrc_ipv6_nib(int argc, char **argv) { @@ -37,6 +38,9 @@ int _gnrc_ipv6_nib(int argc, char **argv) else if (strcmp(argv[1], "prefix") == 0) { res = _nib_prefix(argc, argv); } + else if (strcmp(argv[1], "route") == 0) { + res = _nib_route(argc, argv); + } else { _usage(argv); } @@ -45,7 +49,7 @@ int _gnrc_ipv6_nib(int argc, char **argv) static void _usage(char **argv) { - printf("usage: %s {neigh|prefix|help} ...\n", argv[0]); + printf("usage: %s {neigh|prefix|route|help} ...\n", argv[0]); } static void _usage_nib_neigh(char **argv) @@ -65,6 +69,15 @@ static void _usage_nib_prefix(char **argv) printf(" %s %s show \n", argv[0], argv[1]); } +static void _usage_nib_route(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)) { @@ -169,4 +182,57 @@ static int _nib_prefix(int argc, char **argv) return 0; } +static int _nib_route(int argc, char **argv) +{ + if ((argc == 2) || (strcmp(argv[2], "show") == 0)) { + gnrc_ipv6_nib_ft_t entry; + void *state = NULL; + unsigned iface = 0U; + + if (argc > 3) { + iface = atoi(argv[3]); + } + while (gnrc_ipv6_nib_ft_iter(NULL, iface, &state, &entry)) { + gnrc_ipv6_nib_ft_print(&entry); + } + } + else if ((argc > 2) && (strcmp(argv[2], "help") == 0)) { + _usage_nib_route(argv); + } + else if ((argc > 5) && (strcmp(argv[2], "add") == 0)) { + ipv6_addr_t pfx = IPV6_ADDR_UNSPECIFIED, next_hop; + unsigned iface = atoi(argv[3]); + unsigned pfx_len = ipv6_addr_split_prefix(argv[4]); + + if (ipv6_addr_from_str(&pfx, argv[4]) == NULL) { + /* check if string equals "default" + * => keep pfx as unspecified address == default route */ + if (strcmp(argv[4], "default") != 0) { + _usage_nib_route(argv); + return 1; + } + } + if (ipv6_addr_from_str(&next_hop, argv[5]) == NULL) { + _usage_nib_route(argv); + return 1; + } + gnrc_ipv6_nib_ft_add(&pfx, pfx_len, &next_hop, iface); + } + else if ((argc > 4) && (strcmp(argv[2], "del") == 0)) { + ipv6_addr_t pfx; + unsigned pfx_len = ipv6_addr_split_prefix(argv[4]); + + if (ipv6_addr_from_str(&pfx, argv[4]) == NULL) { + _usage_nib_route(argv); + return 1; + } + gnrc_ipv6_nib_ft_del(&pfx, pfx_len); + } + else { + _usage_nib_route(argv); + return 1; + } + return 0; +} + /** @} */ From c60caccf43f3dbc68f37a0a7e2e1fe0c26981fc3 Mon Sep 17 00:00:00 2001 From: Martine Lenders Date: Mon, 26 Jun 2017 17:29:42 +0200 Subject: [PATCH 3/3] tests: extend gnrc_ipv6_nib unittests for FT component --- .../tests-gnrc_ipv6_nib-ft.c | 656 ++++++++++++++++++ .../tests-gnrc_ipv6_nib/tests-gnrc_ipv6_nib.c | 1 + .../tests-gnrc_ipv6_nib/tests-gnrc_ipv6_nib.h | 7 + 3 files changed, 664 insertions(+) create mode 100644 tests/unittests/tests-gnrc_ipv6_nib/tests-gnrc_ipv6_nib-ft.c diff --git a/tests/unittests/tests-gnrc_ipv6_nib/tests-gnrc_ipv6_nib-ft.c b/tests/unittests/tests-gnrc_ipv6_nib/tests-gnrc_ipv6_nib-ft.c new file mode 100644 index 0000000000..34607d90f5 --- /dev/null +++ b/tests/unittests/tests-gnrc_ipv6_nib/tests-gnrc_ipv6_nib-ft.c @@ -0,0 +1,656 @@ +/* + * 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 "bitfield.h" +#include "net/ipv6/addr.h" +#include "net/gnrc/ipv6/nib.h" +#include "net/gnrc/ipv6/nib/ft.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) +{ + 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 get a route from an empty forwarding table. + * Expected result: gnrc_ipv6_nib_ft_get() returns -ENETUNREACH + */ +static void test_nib_ft_get__ENETUNREACH_empty(void) +{ + gnrc_ipv6_nib_ft_t fte; + static const ipv6_addr_t dst = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + + TEST_ASSERT_EQUAL_INT(-ENETUNREACH, gnrc_ipv6_nib_ft_get(&dst, NULL, &fte)); +} + +/* + * Adds a route (not the default route) to the forwarding table, then tries to + * get another route. + * Expected result: gnrc_ipv6_nib_ft_get() returns -ENETUNREACH + */ +static void test_nib_ft_get__ENETUNREACH_no_def_route(void) +{ + gnrc_ipv6_nib_ft_t fte; + static const ipv6_addr_t next_hop = { .u64 = { { .u8 = LINK_LOCAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + ipv6_addr_t dst = { .u64 = { { .u8 = GLOBAL_PREFIX } } }; + + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_ft_add(&dst, GLOBAL_PREFIX_LEN, + &next_hop, IFACE)); + dst.u16[0].u16--; + TEST_ASSERT_EQUAL_INT(-ENETUNREACH, gnrc_ipv6_nib_ft_get(&dst, NULL, &fte)); +} + +/* + * Adds the default route to the forwarding table, then tries to get an + * arbitrary route. + * Expected result: gnrc_ipv6_nib_ft_get() returns the configured default route + */ +static void test_nib_ft_get__success1(void) +{ + gnrc_ipv6_nib_ft_t fte; + static const ipv6_addr_t dst = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + static const ipv6_addr_t next_hop = { .u64 = { { .u8 = LINK_LOCAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_ft_add(NULL, 0, &next_hop, IFACE)); + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_ft_get(&dst, NULL, &fte)); + TEST_ASSERT(ipv6_addr_is_unspecified(&fte.dst)); + TEST_ASSERT(ipv6_addr_equal(&next_hop, &fte.next_hop)); + TEST_ASSERT_EQUAL_INT(0, fte.dst_len); + /* we can't make any sure assumption on fte.primary */ + TEST_ASSERT_EQUAL_INT(IFACE, fte.iface); +} + +/* + * Adds an arbitrary route to the forwarding table, then tries to get an address + * with the same prefix. + * Expected result: gnrc_ipv6_nib_ft_get() returns the configured route + */ +static void test_nib_ft_get__success2(void) +{ + gnrc_ipv6_nib_ft_t fte; + static const ipv6_addr_t dst = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + static const ipv6_addr_t next_hop = { .u64 = { { .u8 = LINK_LOCAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_ft_add(&dst, GLOBAL_PREFIX_LEN, + &next_hop, IFACE)); + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_ft_get(&dst, NULL, &fte)); + TEST_ASSERT(ipv6_addr_match_prefix(&dst, &fte.dst) >= GLOBAL_PREFIX_LEN); + TEST_ASSERT(ipv6_addr_equal(&next_hop, &fte.next_hop)); + TEST_ASSERT_EQUAL_INT(GLOBAL_PREFIX_LEN, fte.dst_len); + /* we can't make any sure assumption on fte.primary */ + TEST_ASSERT_EQUAL_INT(IFACE, fte.iface); +} + +/* + * Adds two routes to the forwarding table that differ in their last byte + * (prefixes have same length), then tries to get an address with the same + * prefix as the first route. + * Expected result: gnrc_ipv6_nib_ft_get() returns the first configured route + */ +static void test_nib_ft_get__success3(void) +{ + gnrc_ipv6_nib_ft_t fte; + static const ipv6_addr_t dst1 = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + static const ipv6_addr_t next_hop1 = { .u64 = { { .u8 = LINK_LOCAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + static const ipv6_addr_t next_hop2 = { .u64 = { { .u8 = LINK_LOCAL_PREFIX }, + { .u64 = TEST_UINT64 + 1 } } }; + ipv6_addr_t dst2 = { .u64 = { { .u8 = GLOBAL_PREFIX } } }; + + bf_toggle(dst2.u8, GLOBAL_PREFIX_LEN); + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_ft_add(&dst1, GLOBAL_PREFIX_LEN, + &next_hop1, IFACE)); + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_ft_add(&dst2, GLOBAL_PREFIX_LEN, + &next_hop2, IFACE)); + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_ft_get(&dst1, NULL, &fte)); + TEST_ASSERT(ipv6_addr_match_prefix(&dst1, &fte.dst) >= GLOBAL_PREFIX_LEN); + TEST_ASSERT(ipv6_addr_equal(&next_hop1, &fte.next_hop)); + TEST_ASSERT_EQUAL_INT(GLOBAL_PREFIX_LEN, fte.dst_len); + /* we can't make any sure assumption on fte.primary */ + TEST_ASSERT_EQUAL_INT(IFACE, fte.iface); +} + +/* + * Adds two routes to the forwarding table that only differ in their prefix + * length by one bit me length, then tries to get an address with the same + * prefix as the route with the longer prefix. + * Expected result: gnrc_ipv6_nib_ft_get() returns route with the longer prefix + */ +static void test_nib_ft_get__success4(void) +{ + gnrc_ipv6_nib_ft_t fte; + static const ipv6_addr_t dst = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + static const ipv6_addr_t next_hop1 = { .u64 = { { .u8 = LINK_LOCAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + static const ipv6_addr_t next_hop2 = { .u64 = { { .u8 = LINK_LOCAL_PREFIX }, + { .u64 = TEST_UINT64 + 1 } } }; + + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_ft_add(&dst, GLOBAL_PREFIX_LEN, + &next_hop1, IFACE)); + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_ft_add(&dst, GLOBAL_PREFIX_LEN - 1, + &next_hop2, IFACE)); + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_ft_get(&dst, NULL, &fte)); + TEST_ASSERT(ipv6_addr_match_prefix(&dst, &fte.dst) >= GLOBAL_PREFIX_LEN); + TEST_ASSERT(ipv6_addr_equal(&next_hop1, &fte.next_hop)); + TEST_ASSERT_EQUAL_INT(GLOBAL_PREFIX_LEN, fte.dst_len); + /* we can't make any sure assumption on fte.primary */ + TEST_ASSERT_EQUAL_INT(IFACE, fte.iface); +} + +/* + * Tries to create a forwarding table entry for the default route (::) with + * NULL as next hop. + * Expected result: gnrc_ipv6_nib_ft_add() returns -EINVAL + */ +static void test_nib_ft_add__EINVAL_def_route_next_hop_NULL(void) +{ + static const ipv6_addr_t dst = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + + TEST_ASSERT_EQUAL_INT(-EINVAL, gnrc_ipv6_nib_ft_add(&ipv6_addr_unspecified, + GLOBAL_PREFIX_LEN, + NULL, IFACE)); + TEST_ASSERT_EQUAL_INT(-EINVAL, gnrc_ipv6_nib_ft_add(NULL, GLOBAL_PREFIX_LEN, + NULL, IFACE)); + TEST_ASSERT_EQUAL_INT(-EINVAL, gnrc_ipv6_nib_ft_add(&dst, 0, NULL, IFACE)); + TEST_ASSERT_EQUAL_INT(-EINVAL, gnrc_ipv6_nib_ft_add(NULL, 0, NULL, IFACE)); +} + +/* + * Tries to create a route via interface 0. + * Expected result: gnrc_ipv6_nib_ft_add() returns -EINVAL + */ +static void test_nib_ft_add__EINVAL_iface0(void) +{ + static const ipv6_addr_t dst = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + static const ipv6_addr_t next_hop = { .u64 = { { .u8 = LINK_LOCAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + + TEST_ASSERT_EQUAL_INT(-EINVAL, gnrc_ipv6_nib_ft_add(&dst, GLOBAL_PREFIX_LEN, + &next_hop, 0)); +} + +#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 GNRC_IPV6_NIB_DEFAULT_ROUTER_NUMOF default route entries and then + * tries to create another one + * Expected result: gnrc_ipv6_nib_ft_add() returns -ENOMEM + */ +static void test_nib_ft_add__ENOMEM_diff_def_router(void) +{ + ipv6_addr_t next_hop = { .u64 = { { .u8 = LINK_LOCAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + + for (unsigned i = 0; i < GNRC_IPV6_NIB_DEFAULT_ROUTER_NUMOF; i++) { + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_ft_add(NULL, 0, &next_hop, + IFACE)); + next_hop.u64[1].u64++; + } + TEST_ASSERT_EQUAL_INT(-ENOMEM, gnrc_ipv6_nib_ft_add(NULL, GLOBAL_PREFIX_LEN, + &next_hop, IFACE)); +} + +/* + * Creates GNRC_IPV6_NIB_OFFL_NUMOF routes with different destinations of same + * prefix lengths to the same next hop and then tries to create another one + * Expected result: gnrc_ipv6_nib_ft_add() returns -ENOMEM + */ +static void test_nib_ft_add__ENOMEM_diff_dst(void) +{ + ipv6_addr_t dst = { .u64 = { { .u8 = GLOBAL_PREFIX } } }; + + for (unsigned i = 0; i < GNRC_IPV6_NIB_OFFL_NUMOF; i++) { + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_ft_add(&dst, GLOBAL_PREFIX_LEN, + NULL, IFACE)); + dst.u16[0].u16--; + } + TEST_ASSERT_EQUAL_INT(-ENOMEM, gnrc_ipv6_nib_ft_add(&dst, GLOBAL_PREFIX_LEN, + NULL, IFACE)); +} + +/* + * Creates GNRC_IPV6_NIB_OFFL_NUMOF routes with destinations of different + * prefix lengths to the same next hop and then tries to create another one + * Expected result: gnrc_ipv6_nib_ft_add() returns -ENOMEM + */ +static void test_nib_ft_add__ENOMEM_diff_dst_len(void) +{ + static const ipv6_addr_t dst = { .u64 = { { .u8 = GLOBAL_PREFIX } } }; + unsigned dst_len = GLOBAL_PREFIX_LEN; + + for (unsigned i = 0; i < GNRC_IPV6_NIB_OFFL_NUMOF; i++) { + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_ft_add(&dst, dst_len, + NULL, IFACE)); + dst_len--; + } + TEST_ASSERT_EQUAL_INT(-ENOMEM, gnrc_ipv6_nib_ft_add(&dst, dst_len, + NULL, IFACE)); +} + +/* + * Creates GNRC_IPV6_NIB_OFFL_NUMOF routes with different destination of + * different prefix lengths to the same next hop and then tries to create + * another one + * Expected result: gnrc_ipv6_nib_ft_add() returns -ENOMEM + */ +static void test_nib_ft_add__ENOMEM_diff_dst_dst_len(void) +{ + ipv6_addr_t dst = { .u64 = { { .u8 = GLOBAL_PREFIX } } }; + unsigned dst_len = GLOBAL_PREFIX_LEN; + + for (unsigned i = 0; i < GNRC_IPV6_NIB_OFFL_NUMOF; i++) { + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_ft_add(&dst, dst_len, + NULL, IFACE)); + dst.u16[0].u16--; + dst_len--; + } + TEST_ASSERT_EQUAL_INT(-ENOMEM, gnrc_ipv6_nib_ft_add(&dst, dst_len, + NULL, IFACE)); +} + +/* + * Creates MAX_NUMOF routes with the same destination different next hops and + * then tries to create another one + * Expected result: gnrc_ipv6_nib_ft_add() returns -ENOMEM + */ +static void test_nib_ft_add__ENOMEM_diff_next_hop(void) +{ + static const ipv6_addr_t dst = { .u64 = { { .u8 = GLOBAL_PREFIX } } }; + ipv6_addr_t next_hop = { .u64 = { { .u8 = LINK_LOCAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + + for (unsigned i = 0; i < MAX_NUMOF; i++) { + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_ft_add(&dst, GLOBAL_PREFIX_LEN, + &next_hop, IFACE)); + next_hop.u64[1].u64++; + } + TEST_ASSERT_EQUAL_INT(-ENOMEM, gnrc_ipv6_nib_ft_add(&dst, GLOBAL_PREFIX_LEN, + &next_hop, IFACE)); +} + +/* + * Creates MAX_NUMOF routes with different destinations of same prefix lengths + * to the different next hops and then tries to create another one + * Expected result: gnrc_ipv6_nib_ft_add() returns -ENOMEM + */ +static void test_nib_ft_add__ENOMEM_diff_dst_next_hop(void) +{ + ipv6_addr_t dst = { .u64 = { { .u8 = GLOBAL_PREFIX } } }; + ipv6_addr_t next_hop = { .u64 = { { .u8 = LINK_LOCAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + + for (unsigned i = 0; i < MAX_NUMOF; i++) { + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_ft_add(&dst, GLOBAL_PREFIX_LEN, + &next_hop, IFACE)); + dst.u16[0].u16--; + next_hop.u64[1].u64++; + } + TEST_ASSERT_EQUAL_INT(-ENOMEM, gnrc_ipv6_nib_ft_add(&dst, GLOBAL_PREFIX_LEN, + &next_hop, IFACE)); +} + +/* + * Creates MAX_NUMOF routes with different destinations of same prefix lengths + * to different next hops and then tries to create another one + * Expected result: gnrc_ipv6_nib_ft_add() returns -ENOMEM + */ +static void test_nib_ft_add__ENOMEM_diff_dst_dst_len_next_hop(void) +{ + ipv6_addr_t dst = { .u64 = { { .u8 = GLOBAL_PREFIX } } }; + ipv6_addr_t next_hop = { .u64 = { { .u8 = LINK_LOCAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + unsigned dst_len = GLOBAL_PREFIX_LEN; + + for (unsigned i = 0; i < MAX_NUMOF; i++) { + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_ft_add(&dst, dst_len, &next_hop, + IFACE)); + dst.u16[0].u16--; + dst_len--; + next_hop.u64[1].u64++; + } + TEST_ASSERT_EQUAL_INT(-ENOMEM, gnrc_ipv6_nib_ft_add(&dst, dst_len, + &next_hop, IFACE)); +} + +/* + * Creates MAX_NUMOF routes with different destinations of same prefix lengths + * to the same next hop but on different interfaces and then tries to create + * another one + * Expected result: gnrc_ipv6_nib_ft_add() returns -ENOMEM + */ +static void test_nib_ft_add__ENOMEM_diff_dst_iface(void) +{ + ipv6_addr_t dst = { .u64 = { { .u8 = GLOBAL_PREFIX } } }; + unsigned iface = IFACE; + + for (unsigned i = 0; i < MAX_NUMOF; i++) { + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_ft_add(&dst, GLOBAL_PREFIX_LEN, + NULL, iface)); + dst.u16[0].u16--; + iface++; + } + TEST_ASSERT_EQUAL_INT(-ENOMEM, gnrc_ipv6_nib_ft_add(&dst, GLOBAL_PREFIX_LEN, + NULL, iface)); +} + +/* + * Creates MAX_NUMOF routes with destinations of different prefix lengths to the + * same next hop but on different interfaces and then tries to create another + * one + * Expected result: gnrc_ipv6_nib_ft_add() returns -ENOMEM + */ +static void test_nib_ft_add__ENOMEM_diff_dst_len_iface(void) +{ + static const ipv6_addr_t dst = { .u64 = { { .u8 = GLOBAL_PREFIX } } }; + unsigned dst_len = GLOBAL_PREFIX_LEN; + unsigned iface = IFACE; + + for (unsigned i = 0; i < MAX_NUMOF; i++) { + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_ft_add(&dst, dst_len, + NULL, iface)); + dst_len--; + iface++; + } + TEST_ASSERT_EQUAL_INT(-ENOMEM, gnrc_ipv6_nib_ft_add(&dst, dst_len, + NULL, iface)); +} + +/* + * Creates MAX_NUMOF routes with different destination of different prefix + * lengths to the same next hop but on different interfaces and then tries to + * create another one + * Expected result: gnrc_ipv6_nib_ft_add() returns -ENOMEM + */ +static void test_nib_ft_add__ENOMEM_diff_dst_dst_len_iface(void) +{ + ipv6_addr_t dst = { .u64 = { { .u8 = GLOBAL_PREFIX } } }; + unsigned dst_len = GLOBAL_PREFIX_LEN; + unsigned iface = IFACE; + + for (unsigned i = 0; i < MAX_NUMOF; i++) { + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_ft_add(&dst, dst_len, + NULL, iface)); + dst.u16[0].u16--; + dst_len--; + iface++; + } + TEST_ASSERT_EQUAL_INT(-ENOMEM, gnrc_ipv6_nib_ft_add(&dst, dst_len, + NULL, iface)); +} + +/* + * Creates MAX_NUMOF routes with the same destination to different next hops and + * interfaces and then tries to create another one + * Expected result: gnrc_ipv6_nib_ft_add() returns -ENOMEM + */ +static void test_nib_ft_add__ENOMEM_diff_next_hop_iface(void) +{ + static const ipv6_addr_t dst = { .u64 = { { .u8 = GLOBAL_PREFIX } } }; + ipv6_addr_t next_hop = { .u64 = { { .u8 = LINK_LOCAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + unsigned iface = IFACE; + + for (unsigned i = 0; i < MAX_NUMOF; i++) { + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_ft_add(&dst, GLOBAL_PREFIX_LEN, + &next_hop, iface)); + next_hop.u64[1].u64++; + iface++; + } + TEST_ASSERT_EQUAL_INT(-ENOMEM, gnrc_ipv6_nib_ft_add(&dst, GLOBAL_PREFIX_LEN, + &next_hop, iface)); +} + +/* + * Creates MAX_NUMOF routes with different destinations of same prefix lengths + * to different next hops and interfaces and then tries to create another one + * Expected result: gnrc_ipv6_nib_ft_add() returns -ENOMEM + */ +static void test_nib_ft_add__ENOMEM_diff_dst_next_hop_iface(void) +{ + ipv6_addr_t dst = { .u64 = { { .u8 = GLOBAL_PREFIX } } }; + ipv6_addr_t next_hop = { .u64 = { { .u8 = LINK_LOCAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + unsigned iface = IFACE; + + for (unsigned i = 0; i < MAX_NUMOF; i++) { + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_ft_add(&dst, GLOBAL_PREFIX_LEN, + &next_hop, iface)); + dst.u16[0].u16--; + next_hop.u64[1].u64++; + iface++; + } + TEST_ASSERT_EQUAL_INT(-ENOMEM, gnrc_ipv6_nib_ft_add(&dst, GLOBAL_PREFIX_LEN, + &next_hop, iface)); +} + +/* + * Creates MAX_NUMOF routes with different destinations of same prefix lengths + * to the different next hops and interfaces and then tries to create another + * one + * Expected result: gnrc_ipv6_nib_ft_add() returns -ENOMEM + */ +static void test_nib_ft_add__ENOMEM_diff_dst_dst_len_next_hop_iface(void) +{ + ipv6_addr_t dst = { .u64 = { { .u8 = GLOBAL_PREFIX } } }; + ipv6_addr_t next_hop = { .u64 = { { .u8 = LINK_LOCAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + unsigned dst_len = GLOBAL_PREFIX_LEN; + unsigned iface = IFACE; + + for (unsigned i = 0; i < MAX_NUMOF; i++) { + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_ft_add(&dst, dst_len, &next_hop, + iface)); + dst.u16[0].u16--; + dst_len--; + next_hop.u64[1].u64++; + iface++; + } + TEST_ASSERT_EQUAL_INT(-ENOMEM, gnrc_ipv6_nib_ft_add(&dst, dst_len, + &next_hop, iface)); +} + +/* + * Creates MAX_NUMOF routes with different destinations to different next hops + * and interfaces and then tries add another equal to the last. + * Expected result: should return not NULL (the last) + */ +static void test_nib_ft_add__success_duplicate(void) +{ + ipv6_addr_t dst = { .u64 = { { .u8 = GLOBAL_PREFIX } } }; + ipv6_addr_t next_hop = { .u64 = { { .u8 = LINK_LOCAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + unsigned dst_len = GLOBAL_PREFIX_LEN; + unsigned iface = IFACE; + + for (unsigned i = 0; i < MAX_NUMOF; i++) { + dst.u16[0].u16--; + dst_len--; + next_hop.u64[1].u64++; + iface++; + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_ft_add(&dst, dst_len, &next_hop, + iface)); + } + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_ft_add(&dst, dst_len, + &next_hop, iface)); +} + +/* + * Creates a route with no next hop address then adds another with equal prefix + * and interface to the last, but with a next hop address + * Expected result: there should only be one route (with the configuration of + * the second) + */ +static void test_nib_ft_add__success_overwrite_unspecified(void) +{ + gnrc_ipv6_nib_ft_t fte; + void *iter_state = NULL; + static const ipv6_addr_t dst = { .u64 = { { .u8 = GLOBAL_PREFIX } } }; + static const ipv6_addr_t next_hop = { .u64 = { { .u8 = LINK_LOCAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_ft_add(&dst, GLOBAL_PREFIX_LEN, NULL, + IFACE)); + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_ft_add(&dst, GLOBAL_PREFIX_LEN, + &next_hop, IFACE)); + TEST_ASSERT(gnrc_ipv6_nib_ft_iter(NULL, 0, &iter_state, &fte)); + TEST_ASSERT(ipv6_addr_equal(&fte.next_hop, &next_hop)); + TEST_ASSERT(!gnrc_ipv6_nib_ft_iter(NULL, 0, &iter_state, &fte)); +} + +/* + * Creates a route + * Expected result: a new entry should exist and contain the given prefix, + * interface, and lifetimes + */ +static void test_nib_ft_add__success(void) +{ + gnrc_ipv6_nib_ft_t fte; + void *iter_state = NULL; + static const ipv6_addr_t dst = { .u64 = { { .u8 = GLOBAL_PREFIX } } }; + static const ipv6_addr_t next_hop = { .u64 = { { .u8 = LINK_LOCAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_ft_add(&dst, GLOBAL_PREFIX_LEN, + &next_hop, IFACE)); + TEST_ASSERT(gnrc_ipv6_nib_ft_iter(NULL, 0, &iter_state, &fte)); + TEST_ASSERT(ipv6_addr_match_prefix(&fte.dst, &dst) >= GLOBAL_PREFIX_LEN); + TEST_ASSERT_EQUAL_INT(GLOBAL_PREFIX_LEN, fte.dst_len); + TEST_ASSERT(ipv6_addr_equal(&fte.next_hop, &next_hop)); + TEST_ASSERT_EQUAL_INT(IFACE, fte.iface); + TEST_ASSERT_EQUAL_INT(0, fte.primary); + TEST_ASSERT(!gnrc_ipv6_nib_ft_iter(NULL, 0, &iter_state, &fte)); +} + +/* + * Creates MAX_NUMOF routes with different destinations of to the different + * next hops and interfaces and then tries to delete one with yet another + * destination, next hop and interface. + * Expected result: There should be still MAX_NUMOF entries in the forwarding + * table + */ +static void test_nib_ft_del__unknown(void) +{ + gnrc_ipv6_nib_ft_t fte; + void *iter_state = NULL; + ipv6_addr_t dst = { .u64 = { { .u8 = GLOBAL_PREFIX } } }; + ipv6_addr_t next_hop = { .u64 = { { .u8 = LINK_LOCAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + unsigned dst_len = GLOBAL_PREFIX_LEN, iface = IFACE, count = 0; + + for (unsigned i = 0; i < MAX_NUMOF; i++) { + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_ft_add(&dst, dst_len, &next_hop, + iface)); + dst.u16[0].u16--; + dst_len--; + next_hop.u64[1].u64++; + iface++; + } + gnrc_ipv6_nib_ft_del(&dst, dst_len); + while(gnrc_ipv6_nib_ft_iter(NULL, 0, &iter_state, &fte)) { + count++; + } + TEST_ASSERT_EQUAL_INT(MAX_NUMOF, count); +} + +/* + * Creates a route and removes it. + * Expected result: forwarding table should be empty + */ +static void test_nib_ft_del__success(void) +{ + void *iter_state = NULL; + static const ipv6_addr_t dst = { .u64 = { { .u8 = GLOBAL_PREFIX } } }; + static const ipv6_addr_t next_hop = { .u64 = { { .u8 = LINK_LOCAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + gnrc_ipv6_nib_ft_t fte; + + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_ft_add(&dst, GLOBAL_PREFIX_LEN, + &next_hop, IFACE)); + gnrc_ipv6_nib_ft_del(&dst, GLOBAL_PREFIX_LEN); + TEST_ASSERT(!gnrc_ipv6_nib_ft_iter(NULL ,0, &iter_state, &fte)); +} + +Test *tests_gnrc_ipv6_nib_ft_tests(void) +{ + EMB_UNIT_TESTFIXTURES(fixtures) { + new_TestFixture(test_nib_ft_get__ENETUNREACH_empty), + new_TestFixture(test_nib_ft_get__ENETUNREACH_no_def_route), + new_TestFixture(test_nib_ft_get__success1), + new_TestFixture(test_nib_ft_get__success2), + new_TestFixture(test_nib_ft_get__success3), + new_TestFixture(test_nib_ft_get__success4), + new_TestFixture(test_nib_ft_add__EINVAL_def_route_next_hop_NULL), + new_TestFixture(test_nib_ft_add__EINVAL_iface0), + new_TestFixture(test_nib_ft_add__ENOMEM_diff_def_router), + new_TestFixture(test_nib_ft_add__ENOMEM_diff_dst), + new_TestFixture(test_nib_ft_add__ENOMEM_diff_dst_len), + new_TestFixture(test_nib_ft_add__ENOMEM_diff_dst_dst_len), + new_TestFixture(test_nib_ft_add__ENOMEM_diff_next_hop), + new_TestFixture(test_nib_ft_add__ENOMEM_diff_dst_next_hop), + new_TestFixture(test_nib_ft_add__ENOMEM_diff_dst_dst_len_next_hop), + new_TestFixture(test_nib_ft_add__ENOMEM_diff_dst_iface), + new_TestFixture(test_nib_ft_add__ENOMEM_diff_dst_len_iface), + new_TestFixture(test_nib_ft_add__ENOMEM_diff_dst_dst_len_iface), + new_TestFixture(test_nib_ft_add__ENOMEM_diff_next_hop_iface), + new_TestFixture(test_nib_ft_add__ENOMEM_diff_dst_next_hop_iface), + new_TestFixture(test_nib_ft_add__ENOMEM_diff_dst_dst_len_next_hop_iface), + new_TestFixture(test_nib_ft_add__success_duplicate), + new_TestFixture(test_nib_ft_add__success_overwrite_unspecified), + new_TestFixture(test_nib_ft_add__success), + new_TestFixture(test_nib_ft_del__unknown), + new_TestFixture(test_nib_ft_del__success), + /* gnrc_ipv6_nib_ft_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 9ac03846fd..8ea9147cd7 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,6 +19,7 @@ void tests_gnrc_ipv6_nib(void) { TESTS_RUN(tests_gnrc_ipv6_nib_internal_tests()); TESTS_RUN(tests_gnrc_ipv6_nib_abr_tests()); + TESTS_RUN(tests_gnrc_ipv6_nib_ft_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 6e210d0b75..5dc3f65750 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_abr_tests(void); +/** + * @brief Generates tests for forwarding table view + * + * @return embUnit tests if successful, NULL if not. + */ +Test *tests_gnrc_ipv6_nib_ft_tests(void); + /** * @brief Generates tests for neighbor cache view *