diff --git a/Makefile.dep b/Makefile.dep index a2a31ff59c..f2889527f1 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -316,6 +316,7 @@ ifneq (,$(filter gnrc_ipv6_nc,$(USEMODULE))) endif ifneq (,$(filter gnrc_ipv6_nib,$(USEMODULE))) + USEMODULE += evtimer USEMODULE += ipv6_addr USEMODULE += random endif diff --git a/sys/include/net/gnrc/ipv6.h b/sys/include/net/gnrc/ipv6.h index 11841393b2..ef600fb7e8 100644 --- a/sys/include/net/gnrc/ipv6.h +++ b/sys/include/net/gnrc/ipv6.h @@ -36,7 +36,9 @@ #include "net/ipv6.h" #include "net/gnrc/ipv6/ext.h" #include "net/gnrc/ipv6/hdr.h" +#ifndef MODULE_GNRC_IPV6_NIB #include "net/gnrc/ipv6/nc.h" +#endif #include "net/gnrc/ipv6/netif.h" #ifdef MODULE_FIB diff --git a/sys/include/net/gnrc/ipv6/nib.h b/sys/include/net/gnrc/ipv6/nib.h index f132517a4f..56a34b1b46 100644 --- a/sys/include/net/gnrc/ipv6/nib.h +++ b/sys/include/net/gnrc/ipv6/nib.h @@ -22,6 +22,8 @@ #ifndef NET_GNRC_IPV6_NIB_H #define NET_GNRC_IPV6_NIB_H +#include "net/gnrc/ipv6/nib/nc.h" + #ifdef __cplusplus extern "C" { #endif diff --git a/sys/include/net/gnrc/ipv6/nib/nc.h b/sys/include/net/gnrc/ipv6/nib/nc.h index 917e5d1edf..e6cff1a381 100644 --- a/sys/include/net/gnrc/ipv6/nib/nc.h +++ b/sys/include/net/gnrc/ipv6/nib/nc.h @@ -47,22 +47,22 @@ extern "C" { #define GNRC_IPV6_NIB_NC_INFO_NUD_STATE_MASK (0x0007) /** - * @brief not managed by NUD + * @brief Not managed by NUD */ #define GNRC_IPV6_NIB_NC_INFO_NUD_STATE_UNMANAGED (0x0000) /** - * @brief entry is not reachable + * @brief Entry is not reachable */ #define GNRC_IPV6_NIB_NC_INFO_NUD_STATE_UNREACHABLE (0x0001) /** - * @brief address resolution is currently performed + * @brief Address resolution is currently performed */ #define GNRC_IPV6_NIB_NC_INFO_NUD_STATE_INCOMPLETE (0x0002) /** - * @brief address might not be reachable + * @brief Address might not be reachable */ #define GNRC_IPV6_NIB_NC_INFO_NUD_STATE_STALE (0x0003) @@ -77,7 +77,7 @@ extern "C" { #define GNRC_IPV6_NIB_NC_INFO_NUD_STATE_PROBE (0x0005) /** - * @brief entry is reachable + * @brief Entry is reachable */ #define GNRC_IPV6_NIB_NC_INFO_NUD_STATE_REACHABLE (0x0006) @@ -111,21 +111,201 @@ extern "C" { #define GNRC_IPV6_NIB_NC_INFO_AR_STATE_MASK (0x0600) /** - * @brief not managed by 6Lo-AR (address can be removed when memory is low + * @brief Shift position of address registration states + */ +#define GNRC_IPV6_NIB_NC_INFO_AR_STATE_POS (9) + +/** + * @brief Not managed by 6Lo-AR (address can be removed when memory is low */ #define GNRC_IPV6_NIB_NC_INFO_AR_STATE_GC (0x0000) /** - * @brief address registration still pending at upstream router + * @brief Address registration still pending at upstream router */ #define GNRC_IPV6_NIB_NC_INFO_AR_STATE_TENTATIVE (0x0200) /** - * @brief address is registered + * @brief Address is registered */ -#define GNRC_IPV6_NIB_NC_INFO_AR_STATE_REGISTERED (0x0600) +#define GNRC_IPV6_NIB_NC_INFO_AR_STATE_REGISTERED (0x0400) + +/** + * @brief Address was added manually + */ +#define GNRC_IPV6_NIB_NC_INFO_AR_STATE_MANUAL (0x0600) /** @} */ +/** + * @brief Neighbor cache entry view on NIB + */ +typedef struct { + ipv6_addr_t ipv6; /**< Neighbor's IPv6 address */ + /** + * @brief Neighbor's link-layer address + */ + uint8_t l2addr[GNRC_IPV6_NIB_L2ADDR_MAX_LEN]; + /** + * @brief Neighbor information as defined in + * @ref net_gnrc_ipv6_nib_nc_info "info values" + */ + uint16_t info; + uint8_t l2addr_len; /**< Length of gnrc_ipv6_nib_nc_t::l2addr in bytes */ +} gnrc_ipv6_nib_nc_t; + +/** + * @brief Gets neighbor unreachability state from entry + * + * @param[in] entry A neighbor cache entry. + * + * @return The neighbor unreachability state of @p entry. + */ +static inline unsigned gnrc_ipv6_nib_nc_get_nud_state(const gnrc_ipv6_nib_nc_t *entry) +{ + return (entry->info & GNRC_IPV6_NIB_NC_INFO_NUD_STATE_MASK); +} + +/** + * @brief Gets router flag of a neighbor. + * + * @param[in] entry A neighbor cache entry. + * + * @return true, if @p entry is a router. + * @return false, if @p entry is not a router. + */ +static inline bool gnrc_ipv6_nib_nc_is_router(const gnrc_ipv6_nib_nc_t *entry) +{ + return (entry->info & GNRC_IPV6_NIB_NC_INFO_IS_ROUTER); +} + +/** + * @brief Gets interface from entry + * + * @param[in] entry A neighbor cache entry + * + * @return The interface identifier of @p entry. + * @return 0 if no interface is identified for @p entry. + */ +static inline unsigned gnrc_ipv6_nib_nc_get_iface(const gnrc_ipv6_nib_nc_t *entry) +{ + return (entry->info & GNRC_IPV6_NIB_NC_INFO_IFACE_MASK) >> + GNRC_IPV6_NIB_NC_INFO_IFACE_POS; +} + +/** + * @brief Gets address registration state of an entry + * + * @param[in] entry A neighbor cache entry + * + * @return The address registration state of @p entry. + */ +static inline unsigned gnrc_ipv6_nib_nc_get_ar_state(const gnrc_ipv6_nib_nc_t *entry) +{ + return (entry->info & GNRC_IPV6_NIB_NC_INFO_AR_STATE_MASK); +} + +/** + * @brief Adds an unmanaged neighbor entry to NIB + * + * @pre `(ipv6 != NULL) && (l2addr != NULL)` + * @pre `l2addr_len <= GNRC_IPV6_NIB_L2ADDR_MAX_LEN` + * @pre `(iface > KERNEL_PID_UNDEF) && (iface <= KERNEL_PID_LAST)` + * + * @param[in] ipv6 The neighbor's IPv6 address. + * @param[in] iface The interface to the neighbor. + * @param[in] l2addr The neighbor's L2 address. + * @param[in] l2addr_len Length of @p l2addr. + * + * A neighbor cache entry created this way is marked as persistent. + * Also, a non-persistent neighbor or destination cache entry already in the + * NIB might be removed to make room for the new entry. + * If an entry pointing to the same IPv6 address as @p ipv6 exists already it + * will be overwritten and marked as unmanaged. + * + * If @ref GNRC_IPV6_NIB_CONF_ARSM != 0 @p l2addr and @p l2addr_len won't be set. + * + * @return 0 on success. + * @return -ENOMEM, if no space is left in neighbor cache. + */ +int gnrc_ipv6_nib_nc_set(const ipv6_addr_t *ipv6, unsigned iface, + const uint8_t *l2addr, size_t l2addr_len); + +/** + * @brief Deletes neighbor with address @p ipv6 from NIB + * + * @pre `ipv6 != NULL` + * + * @param[in] ipv6 The neighbor's IPv6 address. + * + * If the @p ipv6 can't be found for a neighbor in the NIB nothing happens. + */ +void gnrc_ipv6_nib_nc_del(const ipv6_addr_t *ipv6); + +/** + * @brief Mark neighbor with address @p ipv6 as reachable + * + * @pre `ipv6 != NULL` + * + * @param[in] ipv6 A neighbor's IPv6 address. May not be NULL. + * + * This function shall be called if an upper layer gets reachability + * confirmation via its own means (e.g. a TCP connection build-up or + * confirmation). Unmanaged neighbor cache entries (i.e. entries created using + * @ref gnrc_ipv6_nib_nc_set()) or entries whose next-hop are not yet in the + * neighbor cache are ignored. + * + * Entries in state @ref GNRC_IPV6_NIB_NC_INFO_NUD_STATE_UNMANAGED are not + * affected by this, since they are assumed to always be reachable and kept out + * of the NUD state-machine + */ +void gnrc_ipv6_nib_nc_mark_reachable(const ipv6_addr_t *ipv6); + +/** + * @brief Iterates over all neighbor cache entries in the NIB + * + * @pre `(state != NULL) && (nce != NULL)` + * + * @param[in] iface Restrict iteration to entries on this interface. + * 0 for any interface. + * @param[in,out] state Iteration state of the neighbor cache. Must point to + * a NULL pointer to start iteration. + * @param[out] nce The next neighbor cache entry. + * + * Usage example: + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c} + * #include "net/gnrc/ipv6/nib/nc.h" + * + * int main(void) { + * void *state = NULL; + * gnrc_ipv6_nib_nc_t nce; + * + * puts("My neighbors:"); + * while (gnrc_ipv6_nib_nc_iter(0, &state, &nce)) { + * gnrc_ipv6_nib_nc_print(&nce); + * } + * return 0; + * } + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * @note The list may change during iteration, but no duplicate of already + * traversed entries must be returned. + * + * @return true, if iteration can be continued. + * @return false, if @p nce is the last neighbor cache entry in the NIB. + */ +bool gnrc_ipv6_nib_nc_iter(unsigned iface, void **state, + gnrc_ipv6_nib_nc_t *nce); + +/** + * @brief Prints a neighbor cache entry + * + * @pre `nce != NULL` + * + * @param[in] nce A neighbor cache entry. + */ +void gnrc_ipv6_nib_nc_print(gnrc_ipv6_nib_nc_t *nce); + #ifdef __cplusplus } #endif 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 f366d5ec7c..15349b3af9 100644 --- a/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.c +++ b/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.c @@ -41,6 +41,7 @@ static char addr_str[IPV6_ADDR_MAX_STR_LEN]; #endif mutex_t _nib_mutex = MUTEX_INIT; +evtimer_msg_t _nib_evtimer; static void _override_node(const ipv6_addr_t *addr, unsigned iface, _nib_onl_entry_t *node); @@ -55,6 +56,7 @@ void _nib_init(void) memset(_def_routers, 0, sizeof(_def_routers)); memset(_nis, 0, sizeof(_nis)); #endif + evtimer_init_msg(&_nib_evtimer); /* TODO: load ABR information from persistent memory */ } @@ -120,8 +122,9 @@ static inline _nib_onl_entry_t *_cache_out_onl_entry(const ipv6_addr_t *addr, DEBUG("for (addr = %s, iface = %u)\n", ipv6_addr_to_str(addr_str, addr, sizeof(addr_str)), iface); + /* call _nib_nc_remove to remove timers from _evtimer */ + _nib_nc_remove(tmp); res = tmp; - res->mode = _EMPTY; _override_node(addr, iface, res); /* cstate masked in _nib_nc_add() already */ res->info |= cstate; @@ -204,6 +207,7 @@ _nib_onl_entry_t *_nib_onl_get(const ipv6_addr_t *addr, unsigned iface) void _nib_nc_set_reachable(_nib_onl_entry_t *node) { +#if GNRC_IPV6_NIB_CONF_ARSM _nib_iface_t *iface = _nib_iface_get(_nib_onl_get_if(node)); DEBUG("nib: set %s%%%u reachable (reachable time = %u)\n", @@ -211,8 +215,11 @@ void _nib_nc_set_reachable(_nib_onl_entry_t *node) _nib_onl_get_if(node), iface->reach_time); node->info &= ~GNRC_IPV6_NIB_NC_INFO_NUD_STATE_MASK; node->info |= GNRC_IPV6_NIB_NC_INFO_NUD_STATE_REACHABLE; - /* TODO add event for state change to STALE to event timer*/ - (void)iface; + _evtimer_add(node, GNRC_IPV6_NIB_REACH_TIMEOUT, &node->nud_timeout, + iface->reach_time); +#else + (void)node; +#endif } void _nib_nc_remove(_nib_onl_entry_t *node) @@ -221,10 +228,46 @@ void _nib_nc_remove(_nib_onl_entry_t *node) ipv6_addr_to_str(addr_str, &node->ipv6, sizeof(addr_str)), _nib_onl_get_if(node)); node->mode &= ~(_NC); - /* TODO: remove NC related timers */ +#if GNRC_IPV6_NIB_CONF_ARSM + evtimer_del((evtimer_t *)&_nib_evtimer, &node->nud_timeout.event); +#endif _nib_onl_clear(node); } +static inline void _get_l2addr_from_ipv6(uint8_t *l2addr, + const ipv6_addr_t *ipv6) +{ + memcpy(l2addr, &ipv6->u64[1], sizeof(uint64_t)); + l2addr[0] ^= 0x02; +} + +void _nib_nc_get(const _nib_onl_entry_t *node, gnrc_ipv6_nib_nc_t *nce) +{ + assert((node != NULL) && (nce != NULL)); + memcpy(&nce->ipv6, &node->ipv6, sizeof(nce->ipv6)); + nce->info = node->info; +#if GNRC_IPV6_NIB_CONF_ARSM +#if GNRC_IPV6_NIB_CONF_6LN + if (ipv6_addr_is_link_local(&nce->ipv6)) { + gnrc_ipv6_netif_t *netif = gnrc_ipv6_netif_get(_nib_onl_get_if(node)); + assert(netif != NULL); + if ((netif->flags & GNRC_IPV6_NETIF_FLAGS_SIXLOWPAN) && + !(netif->flags & GNRC_IPV6_NETIF_FLAGS_ROUTER)) { + _get_l2addr_from_ipv6(nce->l2addr, &node->ipv6); + nce->l2addr_len = sizeof(uint64_t); + return; + } + } +#endif + nce->l2addr_len = node->l2addr_len; + memcpy(&nce->l2addr, &node->l2addr, node->l2addr_len); +#else + assert(ipv6_addr_is_link_local(&nce->ipv6)); + _get_l2addr_from_ipv6(nce->l2addr, &node->ipv6); + nce->l2addr_len = sizeof(uint64_t); +#endif +} + _nib_dr_entry_t *_nib_drl_add(const ipv6_addr_t *router_addr, unsigned iface) { _nib_dr_entry_t *def_router = NULL; @@ -312,7 +355,7 @@ _nib_dr_entry_t *_nib_drl_get_dr(void) /* if there is already a default router selected or * its reachability is not suspect */ if (!((_prime_def_router == NULL) || - (_node_unreachable(_prime_def_router->next_hop)))) { + (_node_unreachable(_prime_def_router->next_hop)))) { /* take it */ return _prime_def_router; } @@ -383,4 +426,21 @@ static inline bool _node_unreachable(_nib_onl_entry_t *node) } } +uint32_t _evtimer_lookup(const void *ctx, uint16_t type) +{ + evtimer_msg_event_t *event = (evtimer_msg_event_t *)_nib_evtimer.events; + uint32_t offset = 0; + + DEBUG("nib: lookup ctx = %p, type = %u\n", (void *)ctx, type); + while (event != NULL) { + offset += event->event.offset; + if ((event->msg.type == type) && + ((ctx == NULL) || (event->msg.content.ptr == ctx))) { + return offset; + } + event = (evtimer_msg_event_t *)event->event.next; + } + return UINT32_MAX; +} + /** @} */ 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 980565cc14..a1a9a711db 100644 --- a/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.h +++ b/sys/net/gnrc/network_layer/ipv6/nib/_nib-internal.h @@ -22,10 +22,14 @@ #include #include +#include "evtimer_msg.h" #include "kernel_types.h" #include "mutex.h" #include "net/eui64.h" #include "net/ipv6/addr.h" +#ifdef MODULE_GNRC_IPV6 +#include "net/gnrc/ipv6.h" +#endif #include "net/gnrc/ipv6/nib/nc.h" #include "net/gnrc/ipv6/nib/conf.h" #include "net/gnrc/pktqueue.h" @@ -98,6 +102,16 @@ typedef struct _nib_onl_entry { * @note Only available if @ref GNRC_IPV6_NIB_CONF_ARSM != 0. */ uint8_t l2addr[GNRC_IPV6_NIB_L2ADDR_MAX_LEN]; + /** + * @brief Event for @ref GNRC_IPV6_NIB_REACH_TIMEOUT and + * @ref GNRC_IPV6_NIB_DELAY_TIMEOUT + * + * @note Events of these types can't be in the event queue at the same + * time (since they only have one NUD state at a time). Because of + * this we can use one event for both of them (but need the + * different types, since the events are handled differently) + */ + evtimer_msg_event_t nud_timeout; #endif /** @@ -199,6 +213,11 @@ typedef struct { */ extern mutex_t _nib_mutex; +/** + * @brief Event timer for the NIB. + */ +extern evtimer_msg_t _nib_evtimer; + /** * @brief Initializes NIB internally */ @@ -312,6 +331,16 @@ _nib_onl_entry_t *_nib_nc_add(const ipv6_addr_t *addr, unsigned iface, */ void _nib_nc_remove(_nib_onl_entry_t *node); +/** + * @brief Gets external neighbor cache entry representation from on-link entry + * + * @pre `(node != NULL) && (nce != NULL)` + * + * @param[in] node On-link entry. + * @param[out] nce External representation of the neighbor cache entry. + */ +void _nib_nc_get(const _nib_onl_entry_t *node, gnrc_ipv6_nib_nc_t *nce); + /** * @brief Sets a NUD-managed neighbor cache entry to reachable and sets the * respective event in @ref _nib_evtimer "event timer" @@ -625,6 +654,41 @@ static inline void _nib_ft_remove(_nib_offl_entry_t *nib_offl) */ _nib_iface_t *_nib_iface_get(unsigned iface); +/** + * @brief Looks up if an event is queued in the event timer + * + * @param[in] ctx Context of the event. May be NULL for any event context. + * @param[in] type [Type of the event](@ref net_gnrc_ipv6_nib_msg). + * + * @return Milliseconds to the event, if event in queue. + * @return UINT32_MAX, event is not in queue. + */ +uint32_t _evtimer_lookup(const void *ctx, uint16_t type); + +/** + * @brief Adds an event to the event timer + * + * @param[in] ctx The context of the event + * @param[in] type [Type of the event](@ref net_gnrc_ipv6_nib_msg). + * @param[in,out] event Representation of the event. + * @param[in] offset Offset in milliseconds to the event. + */ +static inline void _evtimer_add(void *ctx, int16_t type, + evtimer_msg_event_t *event, uint32_t offset) +{ +#ifdef MODULE_GNRC_IPV6 + kernel_pid_t target_pid = gnrc_ipv6_pid; +#else + kernel_pid_t target_pid = KERNEL_PID_LAST; /* just for testing */ +#endif + evtimer_del((evtimer_t *)(&_nib_evtimer), (evtimer_event_t *)event); + event->event.next = NULL; + event->event.offset = offset; + event->msg.type = type; + event->msg.content.ptr = ctx; + evtimer_add_msg(&_nib_evtimer, event, target_pid); +} + #ifdef __cplusplus } #endif diff --git a/sys/net/gnrc/network_layer/ipv6/nib/nib_nc.c b/sys/net/gnrc/network_layer/ipv6/nib/nib_nc.c new file mode 100644 index 0000000000..4cd920a535 --- /dev/null +++ b/sys/net/gnrc/network_layer/ipv6/nib/nib_nc.c @@ -0,0 +1,151 @@ +/* + * 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.h" +#include "net/gnrc/netif.h" + +#include "net/gnrc/ipv6/nib/nc.h" + +#include "_nib-internal.h" + +int gnrc_ipv6_nib_nc_set(const ipv6_addr_t *ipv6, unsigned iface, + const uint8_t *l2addr, size_t l2addr_len) +{ + _nib_onl_entry_t *node; + + assert((ipv6 != NULL) && (l2addr != NULL)); + assert(l2addr_len <= GNRC_IPV6_NIB_L2ADDR_MAX_LEN); + assert((iface > KERNEL_PID_UNDEF) && (iface <= KERNEL_PID_LAST)); + mutex_lock(&_nib_mutex); + node = _nib_nc_add(ipv6, iface, GNRC_IPV6_NIB_NC_INFO_NUD_STATE_UNMANAGED); + if (node == NULL) { + mutex_unlock(&_nib_mutex); + return -ENOMEM; + } +#if GNRC_IPV6_NIB_CONF_ARSM + memcpy(node->l2addr, l2addr, l2addr_len); + node->l2addr_len = l2addr_len; +#else + (void)l2addr; + (void)l2addr_len; +#endif + node->info &= ~(GNRC_IPV6_NIB_NC_INFO_AR_STATE_MASK | + GNRC_IPV6_NIB_NC_INFO_NUD_STATE_MASK); + node->info |= (GNRC_IPV6_NIB_NC_INFO_AR_STATE_MANUAL | + GNRC_IPV6_NIB_NC_INFO_NUD_STATE_UNMANAGED); + mutex_unlock(&_nib_mutex); + return 0; +} + +void gnrc_ipv6_nib_nc_del(const ipv6_addr_t *ipv6) +{ + _nib_onl_entry_t *node = NULL; + + mutex_lock(&_nib_mutex); + while ((node = _nib_onl_iter(node)) != NULL) { + if (ipv6_addr_equal(ipv6, &node->ipv6)) { + _nib_nc_remove(node); + break; + } + } + mutex_unlock(&_nib_mutex); +} + +void gnrc_ipv6_nib_nc_mark_reachable(const ipv6_addr_t *ipv6) +{ + _nib_onl_entry_t *node = NULL; + + mutex_lock(&_nib_mutex); + while ((node = _nib_onl_iter(node)) != NULL) { + if ((node->mode & _NC) && ipv6_addr_equal(ipv6, &node->ipv6)) { + /* only set reachable if not unmanaged */ + if ((node->info & GNRC_IPV6_NIB_NC_INFO_NUD_STATE_MASK)) { + _nib_nc_set_reachable(node); + } + break; + } + } + mutex_unlock(&_nib_mutex); +} + +bool gnrc_ipv6_nib_nc_iter(unsigned iface, void **state, + gnrc_ipv6_nib_nc_t *entry) +{ + _nib_onl_entry_t *node = *state; + + mutex_lock(&_nib_mutex); + while ((node = _nib_onl_iter(node)) != NULL) { + if ((node->mode & _NC) && + ((iface == 0) || (_nib_onl_get_if(node) == iface))) { + _nib_nc_get(node, entry); + break; + } + } + *state = node; + mutex_unlock(&_nib_mutex); + return (*state != NULL); +} + +#if GNRC_IPV6_NIB_CONF_ARSM +static const char *_nud_str[] = { + [GNRC_IPV6_NIB_NC_INFO_NUD_STATE_UNMANAGED] = "-", + [GNRC_IPV6_NIB_NC_INFO_NUD_STATE_UNREACHABLE] = "UNREACHABLE", + [GNRC_IPV6_NIB_NC_INFO_NUD_STATE_INCOMPLETE] = "INCOMPLETE", + [GNRC_IPV6_NIB_NC_INFO_NUD_STATE_STALE] = "STALE", + [GNRC_IPV6_NIB_NC_INFO_NUD_STATE_DELAY] = "DELAY", + [GNRC_IPV6_NIB_NC_INFO_NUD_STATE_PROBE] = "PROBE", + [GNRC_IPV6_NIB_NC_INFO_NUD_STATE_REACHABLE] = "REACHABLE", +}; +#endif + +#if GNRC_IPV6_NIB_CONF_6LR +#define _AR_STR_IDX(state) ((state) >> GNRC_IPV6_NIB_NC_INFO_AR_STATE_POS) + +static const char *_ar_str[] = { + [_AR_STR_IDX(GNRC_IPV6_NIB_NC_INFO_AR_STATE_GC)] = "GC", + [_AR_STR_IDX(GNRC_IPV6_NIB_NC_INFO_AR_STATE_TENTATIVE)] = "TENTATIVE", + [_AR_STR_IDX(GNRC_IPV6_NIB_NC_INFO_AR_STATE_REGISTERED)] = "REGISTERED", + [_AR_STR_IDX(GNRC_IPV6_NIB_NC_INFO_AR_STATE_MANUAL)] = "MANUAL", +}; +#endif + +void gnrc_ipv6_nib_nc_print(gnrc_ipv6_nib_nc_t *entry) +{ + char addr_str[IPV6_ADDR_MAX_STR_LEN]; + + printf("%s ", ipv6_addr_to_str(addr_str, &entry->ipv6, sizeof(addr_str))); + if (gnrc_ipv6_nib_nc_get_iface(entry) != KERNEL_PID_UNDEF) { + printf("dev #%u ", gnrc_ipv6_nib_nc_get_iface(entry)); + } + printf("lladdr %s ", gnrc_netif_addr_to_str(addr_str, sizeof(addr_str), + entry->l2addr, + entry->l2addr_len)); + if (gnrc_ipv6_nib_nc_is_router(entry)) { + printf("router"); + } +#if GNRC_IPV6_NIB_CONF_ARSM + printf(" %s", _nud_str[gnrc_ipv6_nib_nc_get_nud_state(entry)]); +#endif +#if GNRC_IPV6_NIB_CONF_6LR + printf(" %s",_ar_str[_AR_STR_IDX(gnrc_ipv6_nib_nc_get_ar_state(entry))]); +#endif + puts(""); +} + +/** @} */ diff --git a/sys/shell/commands/Makefile b/sys/shell/commands/Makefile index 7d77144d8e..c9733ce05d 100644 --- a/sys/shell/commands/Makefile +++ b/sys/shell/commands/Makefile @@ -32,6 +32,9 @@ endif ifneq (,$(filter gnrc_ipv6_nc,$(USEMODULE))) SRC += sc_ipv6_nc.c endif +ifneq (,$(filter gnrc_ipv6_nib,$(USEMODULE))) + SRC += sc_gnrc_ipv6_nib.c +endif ifneq (,$(filter gnrc_ipv6_whitelist,$(USEMODULE))) SRC += sc_whitelist.c endif diff --git a/sys/shell/commands/sc_gnrc_ipv6_nib.c b/sys/shell/commands/sc_gnrc_ipv6_nib.c new file mode 100644 index 0000000000..975fa2802d --- /dev/null +++ b/sys/shell/commands/sc_gnrc_ipv6_nib.c @@ -0,0 +1,106 @@ +/* + * 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 "net/gnrc/ipv6/nib.h" +#include "net/gnrc/netif.h" +#include "net/ipv6/addr.h" + +static void _usage(char **argv); +static int _nib_neigh(int argc, char **argv); + +int _gnrc_ipv6_nib(int argc, char **argv) +{ + int res = 1; + + if ((argc < 2) || (strcmp(argv[1], "help") == 0)) { + _usage(argv); + res = 0; + } + else if (strcmp(argv[1], "neigh") == 0) { + res = _nib_neigh(argc, argv); + } + else { + _usage(argv); + } + return res; +} + +static void _usage(char **argv) +{ + printf("usage: %s {neigh|help} ...\n", argv[0]); +} + +static void _usage_nib_neigh(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)) { + gnrc_ipv6_nib_nc_t entry; + void *state = NULL; + unsigned iface = 0U; + + if (argc > 3) { + iface = atoi(argv[3]); + } + while (gnrc_ipv6_nib_nc_iter(iface, &state, &entry)) { + gnrc_ipv6_nib_nc_print(&entry); + } + } + else if ((argc > 2) && (strcmp(argv[2], "help") == 0)) { + _usage_nib_neigh(argv); + } + else if ((argc > 4) && (strcmp(argv[2], "add") == 0)) { + ipv6_addr_t ipv6_addr; + uint8_t l2addr[GNRC_IPV6_NIB_L2ADDR_MAX_LEN]; + size_t l2addr_len = 0; + unsigned iface = atoi(argv[3]); + + if (ipv6_addr_from_str(&ipv6_addr, argv[4]) == NULL) { + _usage_nib_neigh(argv); + return 1; + } + if ((argc > 5) && /* TODO also check if interface supports link-layers or not */ + (l2addr_len = gnrc_netif_addr_from_str(l2addr, sizeof(l2addr), + argv[5])) == 0) { + _usage_nib_neigh(argv); + return 1; + } + gnrc_ipv6_nib_nc_set(&ipv6_addr, iface, l2addr, l2addr_len); + } + else if ((argc > 3) && (strcmp(argv[2], "del") == 0)) { + ipv6_addr_t ipv6_addr; + + if (ipv6_addr_from_str(&ipv6_addr, argv[4]) == NULL) { + _usage_nib_neigh(argv); + return 1; + } + gnrc_ipv6_nib_nc_del(&ipv6_addr); + } + else { + _usage_nib_neigh(argv); + return 1; + } + return 0; +} + +/** @} */ diff --git a/sys/shell/commands/shell_commands.c b/sys/shell/commands/shell_commands.c index 8b4882f5a0..eaa95cec2b 100644 --- a/sys/shell/commands/shell_commands.c +++ b/sys/shell/commands/shell_commands.c @@ -84,6 +84,10 @@ extern int _random_init(int argc, char **argv); extern int _random_get(int argc, char **argv); #endif +#ifdef MODULE_GNRC_IPV6_NIB +extern int _gnrc_ipv6_nib(int argc, char **argv); +#endif + #ifdef MODULE_GNRC_NETIF extern int _netif_config(int argc, char **argv); extern int _netif_send(int argc, char **argv); @@ -182,6 +186,9 @@ const shell_command_t _shell_command_list[] = { #ifdef CPU_X86 {"lspci", "Lists PCI devices", _x86_lspci}, #endif +#ifdef MODULE_GNRC_IPV6_NIB + {"nib", "Configure neighbor information base", _gnrc_ipv6_nib}, +#endif #ifdef MODULE_GNRC_NETIF {"ifconfig", "Configure network interfaces", _netif_config}, #ifdef MODULE_GNRC_TXTSND diff --git a/tests/unittests/tests-gnrc_ipv6_nib/Makefile.include b/tests/unittests/tests-gnrc_ipv6_nib/Makefile.include index ccae089522..2370879fe5 100644 --- a/tests/unittests/tests-gnrc_ipv6_nib/Makefile.include +++ b/tests/unittests/tests-gnrc_ipv6_nib/Makefile.include @@ -1,4 +1,5 @@ USEMODULE += gnrc_ipv6_nib +USEMODULE += gnrc_ipv6_netif CFLAGS += -DGNRC_IPV6_NIB_NUMOF=16 CFLAGS += -DGNRC_IPV6_NIB_DEFAULT_ROUTER_NUMOF=4 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 2b9c7ebb7f..5cd7b8e5f3 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 @@ -431,7 +431,7 @@ static void test_nib_nc_add__success_duplicate(void) node->info |= GNRC_IPV6_NIB_NC_INFO_AR_STATE_REGISTERED; } TEST_ASSERT(node == _nib_nc_add(&addr, iface, - GNRC_IPV6_NIB_NC_INFO_NUD_STATE_UNREACHABLE)); + GNRC_IPV6_NIB_NC_INFO_NUD_STATE_UNREACHABLE)); } /* diff --git a/tests/unittests/tests-gnrc_ipv6_nib/tests-gnrc_ipv6_nib-nc.c b/tests/unittests/tests-gnrc_ipv6_nib/tests-gnrc_ipv6_nib-nc.c new file mode 100644 index 0000000000..f700008389 --- /dev/null +++ b/tests/unittests/tests-gnrc_ipv6_nib/tests-gnrc_ipv6_nib-nc.c @@ -0,0 +1,346 @@ +/* + * 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 "net/gnrc/ipv6/nib.h" +#include "net/gnrc/ipv6/nib/nc.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 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(); +} + +/* + * Creates GNRC_IPV6_NIB_NUMOF neighbor cache entries with different addresses + * and then tries to create another one + * Expected result: gnrc_ipv6_nib_nc_set() returns -ENOMEM + */ +static void test_nib_nc_set__ENOMEM_diff_addr(void) +{ + ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + uint8_t l2addr[] = L2ADDR; + + for (unsigned i = 0; i < GNRC_IPV6_NIB_NUMOF; i++) { + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_nc_set(&addr, IFACE, l2addr, + sizeof(l2addr))); + addr.u64[1].u64++; + l2addr[7]++; + } + TEST_ASSERT_EQUAL_INT(-ENOMEM, gnrc_ipv6_nib_nc_set(&addr, IFACE, l2addr, + sizeof(l2addr))); +} + +/* + * Creates GNRC_IPV6_NIB_NUMOF neighbor cache entries with different interfaces + * and then tries to create another one + * Expected result: gnrc_ipv6_nib_nc_set() returns -ENOMEM + */ +static void test_nib_nc_set__ENOMEM_diff_iface(void) +{ + static const ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + static const uint8_t l2addr[] = L2ADDR; + unsigned iface = IFACE; + + for (unsigned i = 0; i < GNRC_IPV6_NIB_NUMOF; i++) { + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_nc_set(&addr, iface, l2addr, + sizeof(l2addr))); + iface++; + } + TEST_ASSERT_EQUAL_INT(-ENOMEM, gnrc_ipv6_nib_nc_set(&addr, iface, l2addr, + sizeof(l2addr))); +} + +/* + * Creates GNRC_IPV6_NIB_NUMOF neighbor cache entries with different addresses + * and interfaces and then tries to create another one + * Expected result: gnrc_ipv6_nib_nc_set() returns -ENOMEM + */ +static void test_nib_nc_set__ENOMEM_diff_addr_iface(void) +{ + ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + uint8_t l2addr[] = L2ADDR; + unsigned iface = IFACE; + + for (unsigned i = 0; i < GNRC_IPV6_NIB_NUMOF; i++) { + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_nc_set(&addr, iface, l2addr, + sizeof(l2addr))); + addr.u64[1].u64++; + iface++; + l2addr[7]++; + } + TEST_ASSERT_EQUAL_INT(-ENOMEM, gnrc_ipv6_nib_nc_set(&addr, iface, l2addr, + sizeof(l2addr))); +} + +/* + * Creates a neighbor cache entry. + * Expected result: a new entry should exist and contain the given address + * and interface + */ +static void test_nib_nc_set__success(void) +{ + void *iter_state = NULL; + ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + uint8_t l2addr[] = L2ADDR; + gnrc_ipv6_nib_nc_t nce; + + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_nc_set(&addr, IFACE, l2addr, + sizeof(l2addr))); + TEST_ASSERT(gnrc_ipv6_nib_nc_iter(0, &iter_state, &nce)); + TEST_ASSERT_EQUAL_INT(0, memcmp(&nce.ipv6, &addr, sizeof(addr))); + TEST_ASSERT_EQUAL_INT(0, memcmp(&nce.l2addr, &l2addr, sizeof(l2addr))); + TEST_ASSERT_EQUAL_INT(sizeof(l2addr), nce.l2addr_len); + TEST_ASSERT_EQUAL_INT(IFACE, gnrc_ipv6_nib_nc_get_iface(&nce)); + TEST_ASSERT_EQUAL_INT(GNRC_IPV6_NIB_NC_INFO_NUD_STATE_UNMANAGED, + gnrc_ipv6_nib_nc_get_nud_state(&nce)); + TEST_ASSERT_EQUAL_INT(GNRC_IPV6_NIB_NC_INFO_AR_STATE_MANUAL, + gnrc_ipv6_nib_nc_get_ar_state(&nce)); + TEST_ASSERT(!gnrc_ipv6_nib_nc_iter(0, &iter_state, &nce)); +} + +/* + * Creates GNRC_IPV6_NIB_NUMOF neighbor cache entries with different IP + * addresses and interface identifiers and then tries to add another that is + * equal to the last. + * Expected result: gnrc_ipv6_nib_nc_set() returns 0 + */ +static void test_nib_nc_set__success_duplicate(void) +{ + ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + uint8_t l2addr[] = L2ADDR; + unsigned iface = 0; + + for (unsigned i = 0; i < GNRC_IPV6_NIB_NUMOF; i++) { + addr.u64[1].u64++; + iface++; + l2addr[7]++; + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_nc_set(&addr, iface, l2addr, + sizeof(l2addr))); + } + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_nc_set(&addr, iface, l2addr, + sizeof(l2addr))); +} + +/* + * Creates GNRC_IPV6_NIB_NUMOF neighbor cache entries with different addresses + * and interfaces and then tries to delete one with yet another address. + * Expected result: There should be still GNRC_IPV6_NIB_NUMOF entries in the + * neigbor cache + */ +static void test_nib_nc_del__unknown(void) +{ + void *iter_state = NULL; + ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + uint8_t l2addr[] = L2ADDR; + gnrc_ipv6_nib_nc_t nce; + unsigned iface = IFACE, count = 0; + + for (unsigned i = 0; i < GNRC_IPV6_NIB_NUMOF; i++) { + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_nc_set(&addr, iface, l2addr, + sizeof(l2addr))); + addr.u64[1].u64++; + iface++; + l2addr[7]++; + } + gnrc_ipv6_nib_nc_del(&addr); + while (gnrc_ipv6_nib_nc_iter(0, &iter_state, &nce)) { + count++; + } + TEST_ASSERT_EQUAL_INT(GNRC_IPV6_NIB_NUMOF, count); +} + +/* + * Creates a neighbor cache entry and removes it. + * Expected result: neighbor cache should be empty + */ +static void test_nib_nc_del__success(void) +{ + void *iter_state = NULL; + static const ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + uint8_t l2addr[] = L2ADDR; + gnrc_ipv6_nib_nc_t nce; + + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_nc_set(&addr, IFACE, l2addr, + sizeof(l2addr))); + gnrc_ipv6_nib_nc_del(&addr); + TEST_ASSERT(!gnrc_ipv6_nib_nc_iter(0, &iter_state, &nce)); +} + +/* + * Creates a non-manual neighbor cache entry (as the NIB would create it on an + * incoming NDP packet), sets it to UNREACHABLE and then calls + * gnrc_ipv6_nib_mark_reachable(). + * Expected result: The created entry should be the only entry and its state + * should be still unreachable. The event timer is still empty. + */ +static void test_nib_nc_mark_reachable__not_in_neighbor_cache(void) +{ + void *iter_state = NULL; + ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + gnrc_ipv6_nib_nc_t nce; + + TEST_ASSERT_NOT_NULL(_nib_nc_add(&addr, IFACE, + GNRC_IPV6_NIB_NC_INFO_NUD_STATE_UNREACHABLE)); + + /* check pre-state */ + TEST_ASSERT(gnrc_ipv6_nib_nc_iter(0, &iter_state, &nce)); + TEST_ASSERT_EQUAL_INT(GNRC_IPV6_NIB_NC_INFO_NUD_STATE_UNREACHABLE, + gnrc_ipv6_nib_nc_get_nud_state(&nce)); + TEST_ASSERT(!gnrc_ipv6_nib_nc_iter(0, &iter_state, &nce)); + TEST_ASSERT_NULL(_nib_evtimer.events); + + addr.u64[1].u64++; + gnrc_ipv6_nib_nc_mark_reachable(&addr); + /* check if entry is still unmanaged */ + TEST_ASSERT_EQUAL_INT(GNRC_IPV6_NIB_NC_INFO_NUD_STATE_UNREACHABLE, + gnrc_ipv6_nib_nc_get_nud_state(&nce)); + /* check if there are still no events */ + TEST_ASSERT_NULL(_nib_evtimer.events); + /* check if still the only entry */ + iter_state = NULL; + TEST_ASSERT(gnrc_ipv6_nib_nc_iter(0, &iter_state, &nce)); + TEST_ASSERT(!gnrc_ipv6_nib_nc_iter(0, &iter_state, &nce)); +} + +/* + * Creates a manual neighbor cache entry , sets it to UNREACHABLE and then calls + * gnrc_ipv6_nib_mark_reachable(). + * Expected result: The created entry should be the only entry and its state + * should be still unreachable. The event timer is still empty. + */ +static void test_nib_nc_mark_reachable__unmanaged(void) +{ + void *iter_state = NULL; + static const ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + gnrc_ipv6_nib_nc_t nce; + + TEST_ASSERT_EQUAL_INT(0, gnrc_ipv6_nib_nc_set(&addr, IFACE, NULL, 0)); + /* check pre-state */ + TEST_ASSERT(gnrc_ipv6_nib_nc_iter(0, &iter_state, &nce)); + TEST_ASSERT_EQUAL_INT(GNRC_IPV6_NIB_NC_INFO_NUD_STATE_UNMANAGED, + gnrc_ipv6_nib_nc_get_nud_state(&nce)); + TEST_ASSERT(!gnrc_ipv6_nib_nc_iter(0, &iter_state, &nce)); + TEST_ASSERT_NULL(_nib_evtimer.events); + gnrc_ipv6_nib_nc_mark_reachable(&addr); + /* check if entry is still unmanaged */ + TEST_ASSERT_EQUAL_INT(GNRC_IPV6_NIB_NC_INFO_NUD_STATE_UNMANAGED, + gnrc_ipv6_nib_nc_get_nud_state(&nce)); + /* check if there are still no events */ + TEST_ASSERT_NULL(_nib_evtimer.events); + /* check if still the only entry */ + iter_state = NULL; + TEST_ASSERT(gnrc_ipv6_nib_nc_iter(0, &iter_state, &nce)); + TEST_ASSERT(!gnrc_ipv6_nib_nc_iter(0, &iter_state, &nce)); +} + +/* + * Creates a non-manual neighbor cache entry (as the NIB would create it on an + * incoming NDP packet), sets it to a state unequal to REACHABLE and then calls + * gnrc_ipv6_nib_mark_reachable() + * Expected result: the neighbor cache entry should be in state REACHABLE and a + * reachability timeout event for that entry is in the event timer. + */ +static void test_nib_nc_mark_reachable__success(void) +{ + void *iter_state = NULL; + _nib_onl_entry_t *node; +#if GNRC_IPV6_NIB_CONF_ARSM + evtimer_msg_event_t *event; +#endif + _nib_iface_t *iface; + static const ipv6_addr_t addr = { .u64 = { { .u8 = GLOBAL_PREFIX }, + { .u64 = TEST_UINT64 } } }; + gnrc_ipv6_nib_nc_t nce; + + TEST_ASSERT_NOT_NULL((node = _nib_nc_add(&addr, IFACE, + GNRC_IPV6_NIB_NC_INFO_NUD_STATE_UNREACHABLE))); + /* set an "infinite" reachability time */ + iface = _nib_iface_get(_nib_onl_get_if(node)); + iface->reach_time = UINT32_MAX; + + /* check pre-state */ + TEST_ASSERT(gnrc_ipv6_nib_nc_iter(0, &iter_state, &nce)); + TEST_ASSERT_EQUAL_INT(GNRC_IPV6_NIB_NC_INFO_NUD_STATE_UNREACHABLE, + gnrc_ipv6_nib_nc_get_nud_state(&nce)); + TEST_ASSERT(!gnrc_ipv6_nib_nc_iter(0, &iter_state, &nce)); + TEST_ASSERT_NULL(_nib_evtimer.events); + + gnrc_ipv6_nib_nc_mark_reachable(&addr); + + iter_state = NULL; + TEST_ASSERT(gnrc_ipv6_nib_nc_iter(0, &iter_state, &nce)); +#if GNRC_IPV6_NIB_CONF_ARSM + /* check if entry is reachable */ + TEST_ASSERT_EQUAL_INT(GNRC_IPV6_NIB_NC_INFO_NUD_STATE_REACHABLE, + gnrc_ipv6_nib_nc_get_nud_state(&nce)); + /* check if there now is an event for reachability timeout of node */ + TEST_ASSERT_NOT_NULL((event = (evtimer_msg_event_t *)_nib_evtimer.events)); + TEST_ASSERT_EQUAL_INT(GNRC_IPV6_NIB_REACH_TIMEOUT, event->msg.type); + TEST_ASSERT_MESSAGE(node == event->msg.content.ptr, + "event's context is not node"); +#endif + /* check if still the only entry */ + TEST_ASSERT(!gnrc_ipv6_nib_nc_iter(0, &iter_state, &nce)); +} + +Test *tests_gnrc_ipv6_nib_nc_tests(void) +{ + EMB_UNIT_TESTFIXTURES(fixtures) { + new_TestFixture(test_nib_nc_set__ENOMEM_diff_addr), + new_TestFixture(test_nib_nc_set__ENOMEM_diff_iface), + new_TestFixture(test_nib_nc_set__ENOMEM_diff_addr_iface), + new_TestFixture(test_nib_nc_set__success), + new_TestFixture(test_nib_nc_set__success_duplicate), + new_TestFixture(test_nib_nc_del__unknown), + new_TestFixture(test_nib_nc_del__success), + new_TestFixture(test_nib_nc_mark_reachable__not_in_neighbor_cache), + new_TestFixture(test_nib_nc_mark_reachable__unmanaged), + new_TestFixture(test_nib_nc_mark_reachable__success), + /* gnrc_ipv6_nib_nc_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 c1c4f83815..7682229e50 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 @@ -18,4 +18,5 @@ void tests_gnrc_ipv6_nib(void) { TESTS_RUN(tests_gnrc_ipv6_nib_internal_tests()); + TESTS_RUN(tests_gnrc_ipv6_nib_nc_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 1e3952b6a0..cc58551781 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 @@ -36,6 +36,13 @@ void tests_gnrc_ipv6_nib(void); */ Test *tests_gnrc_ipv6_nib_internal_tests(void); +/** + * @brief Generates tests for neighbor cache view + * + * @return embUnit tests if successful, NULL if not. + */ +Test *tests_gnrc_ipv6_nib_nc_tests(void); + #ifdef __cplusplus } #endif