diff --git a/Makefile.dep b/Makefile.dep index 8a90097714f6..0d8e61e00c43 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -150,3 +150,9 @@ ifneq (,$(filter nhdp,$(USEMODULE))) USEMODULE += oonf_common USEMODULE += oonf_rfc5444 endif + +ifneq (,$(filter fib,$(USEMODULE))) + USEMODULE += timex + USEMODULE += vtimer + USEMODULE += net_help +endif diff --git a/sys/Makefile b/sys/Makefile index abf8bc9480cb..42d8c99c6568 100644 --- a/sys/Makefile +++ b/sys/Makefile @@ -101,6 +101,9 @@ endif ifneq (,$(filter ng_pktdump,$(USEMODULE))) DIRS += net/crosslayer/ng_pktdump endif +ifneq (,$(filter fib,$(USEMODULE))) + DIRS += net/network_layer/fib +endif DIRS += $(dir $(wildcard $(addsuffix /Makefile, ${USEMODULE}))) diff --git a/sys/Makefile.include b/sys/Makefile.include index de8b66de5293..ee40a88f8259 100644 --- a/sys/Makefile.include +++ b/sys/Makefile.include @@ -74,6 +74,10 @@ ifneq (,$(filter oneway_malloc,$(USEMODULE))) USEMODULE_INCLUDES += $(RIOTBASE)/sys/oneway-malloc/include endif +ifneq (,$(filter fib,$(USEMODULE))) + USEMODULE_INCLUDES += $(RIOTBASE)/sys/include/net +endif + ifneq (,$(filter embunit,$(USEMODULE))) ifeq ($(OUTPUT),XML) CFLAGS += -DOUTPUT=OUTPUT_XML diff --git a/sys/include/net/ng_fib.h b/sys/include/net/ng_fib.h new file mode 100644 index 000000000000..dbe6ae2cdc12 --- /dev/null +++ b/sys/include/net/ng_fib.h @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2014 Martin Landsmann + * + * 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_fib Forwarding Information Base (FIB) + * @ingroup net + * @brief FIB implementation + * + * @{ + * + * @file + * @brief Types and functions for FIB + * @author Martin Landsmann + */ + +#ifndef FIB_H_ +#define FIB_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Reactive Routing Protocol (RRP) message content to request/reply route discovery + */ +typedef struct rrp_address_msg_t { + uint8_t *address; /**< The pointer to the address */ + uint8_t address_size; /**< The address size */ + uint32_t address_flags; /**< The flags for the given address */ +} rrp_address_msg_t; + +#define FIB_MSG_RRP_SIGNAL (0x99) /**< message type for RRP notifications */ + +/** + * @brief indicator of a lifetime that does not expire (2^32 - 1) + */ +#define FIB_LIFETIME_NO_EXPIRE (0xFFFFFFFF) + +/** + * @brief initializes all FIB entries with 0 + */ +void fib_init(void); + +/** + * @brief de-initializes the FIB entries + */ +void fib_deinit(void); + +/** + * @brief Registration of reactive routing protocol handler function + */ +void fib_register_rrp(void); + +/** + * @brief Adds a new entry in the corresponding FIB table for global destination and next hop + * + * @param[in] iface_id the interface ID + * @param[in] dst the destination address + * @param[in] dst_size the destination address size + * @param[in] dst_flags the destination address flags + * @param[in] next_hop the next hop address to be updated + * @param[in] next_hop_size the next hop address size + * @param[in] next_hop_flags the next-hop address flags + * @param[in] lifetime the lifetime in ms to be updates + * + * @return 0 on success + * -ENOMEM if the entry cannot be created due to unsufficient RAM + */ +int fib_add_entry(kernel_pid_t iface_id, uint8_t *dst, size_t dst_size, uint32_t dst_flags, + uint8_t *next_hop, size_t next_hop_size, uint32_t next_hop_flags, + uint32_t lifetime); + +/** + * @brief Updates an entry in the FIB table with next hop and lifetime + * + * @param[in] dst the destination address + * @param[in] dst_size the destination address size + * @param[in] next_hop the next hop address to be updated + * @param[in] next_hop_size the next hop address size + * @param[in] next_hop_flags the next-hop address flags + * @param[in] lifetime the lifetime in ms to be updates + * + * @return 0 on success + * -ENOMEM if the entry cannot be updated due to unsufficient RAM + */ +int fib_update_entry(uint8_t *dst, size_t dst_size, + uint8_t *next_hop, size_t next_hop_size, uint32_t next_hop_flags, + uint32_t lifetime); + +/** + * @brief removes an entry from the corresponding FIB table + * + * @param[in] dst the destination address + * @param[in] dst_size the destination address size + */ +void fib_remove_entry(uint8_t *dst, size_t dst_size); + +/** + * @brief provides a next hop for a given destination + * + * @param[in, out] iface_id pointer to store the interface ID for the next hop + * @param[out] next_hop pointer where the next hop address should be stored + * @param[in, out] next_hop_size the next hop address size. The value is + * overwritten with the actual next hop size + * @param[out] next_hop_flags the next-hop address flags, e.g. compression type + * @param[in] dst the destination address + * @param[in] dst_size the destination address size + * @param[in] dst_flags the destination address flags + * + * @return 0 on success + * -EHOSTUNREACH if no next hop is available in any FIB table + * all RRPs are notified before the return + * -ENOBUFS if the size for the next hop address is insufficient low + */ +int fib_get_next_hop(kernel_pid_t *iface_id, + uint8_t *next_hop, size_t *next_hop_size, uint32_t* next_hop_flags, + uint8_t *dst, size_t dst_size, uint32_t dst_flags); + +/** + * @brief returns the actual number of used FIB entries + */ +int fib_get_num_used_entries(void); + +/** + * @brief Prints the kernel_pid_t for all registered RRPs + */ +void fib_print_notify_rrp(void); + +/** + * @brief Prints the FIB content (does not print the entries) + */ +void fib_print_fib_table(void); + +/** + * @brief Prints the FIB content + */ +void fib_print_routes(void); + +#ifdef __cplusplus +} +#endif + +#endif /* FIB_H_ */ +/** @} */ diff --git a/sys/include/net/ng_fib/ng_fib_table.h b/sys/include/net/ng_fib/ng_fib_table.h new file mode 100644 index 000000000000..4dae8e9bd98c --- /dev/null +++ b/sys/include/net/ng_fib/ng_fib_table.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2014 Martin Landsmann + * + * 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. + */ + +/** + * @brief forwarding table structs + * @ingroup net_fib + * + * @{ + * + * @file + * @brief Types and functions for operating fib tables + * @author Martin Landsmann + */ + +#ifndef FIB_TABLE_H_ +#define FIB_TABLE_H_ + +#include +#include "vtimer.h" +#include "ng_universal_address.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** +* @brief Container descriptor for a FIB entry +*/ +typedef struct fib_entry_t { + /** interface ID */ + kernel_pid_t iface_id; + /** Lifetime of this entry */ + timex_t lifetime; + /** Unique identifier for the type of the global address */ + uint32_t global_flags; + /** Pointer to the shared generic address */ + struct universal_address_container_t *global; + /** Unique identifier for the type of the next hop address */ + uint32_t next_hop_flags; + /** Pointer to the shared generic address */ + struct universal_address_container_t *next_hop; +} fib_entry_t; + +#ifdef __cplusplus +} +#endif + +#endif /* FIB_TABLE_H_ */ +/** @} */ diff --git a/sys/include/net/ng_fib/ng_universal_address.h b/sys/include/net/ng_fib/ng_universal_address.h new file mode 100644 index 000000000000..30fecaadeb11 --- /dev/null +++ b/sys/include/net/ng_fib/ng_universal_address.h @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2014 Martin Landsmann + * + * 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_universal_address Universal Address Container + * @ingroup net + * @brief universal address container + * + * @{ + * + * @file + * @brief Types and functions for operating universal addresses + * @author Martin Landsmann + */ + +#ifndef UNIVERSAL_ADDRESS_H_ +#define UNIVERSAL_ADDRESS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define UNIVERSAL_ADDRESS_SIZE (16) /**< size of the used addresses in bytes */ + +/** + * @brief The container descriptor used to identify a universal address entry + */ +typedef struct universal_address_container_t { + uint8_t use_count; /**< The number of entries link here */ + uint8_t address_size; /**< Size in bytes of the used genereic address */ + uint8_t address[UNIVERSAL_ADDRESS_SIZE]; /**< the genereic address data */ +} universal_address_container_t; + +/** + * @brief initializes the datastructure for the entries + */ +void universal_address_init(void); + +/** + * @brief resets the usecoumt for all entries + */ +void universal_address_reset(void); + +/** + * @brief Adds a given address to the universal address entries + * if the entry already exists, the use_count will be increased. + * + * @param[in] addr pointer to the address + * @param[in] addr_size the number of bytes required for the address entry + * + * @return pointer to the universal_address_container_t containing the address on success + * NULL if the address could not be inserted + */ +universal_address_container_t *universal_address_add(uint8_t *addr, size_t addr_size); + +/** + * @brief Adds a given container from the universal address entries + * if the entry exists, the use_count will be decreased. + * + * @param[in] entry pointer to the universal_address_container_t to be removed + */ +void universal_address_rem(universal_address_container_t *entry); + +/** + * @brief copies the address from the given container to the provided pointer + * + * @param[in] entry pointer to the universal_address_container_t + * @param[out] addr pointer to store the address entry + * @param[in, out] addr_size pointer providing the size of available memory on addr + * this value is overwritten with the actual size required + * + * @return addr if the address is copied to the addr destination + * NULL if the size is unsufficient for copy + */ +uint8_t* universal_address_get_address(universal_address_container_t *entry, + uint8_t *addr, size_t *addr_size); + +/** + * @brief determines if the entry equals the provided address + * + * @param[in] entry pointer to the universal_address_container_t for compare + * @param[in] addr pointer to the address for compare + * @param[in, out] addr_size the number of bytes used for the address entry + * on sucessfull return this value is overwritten + * with the number of matching bytes till the + * first of trailing `0`s indicating a prefix + * + * @return 0 if the entries are equal or comperable + * -ENOENT if the given adresses do not match + */ +int universal_address_compare(universal_address_container_t *entry, + uint8_t *addr, size_t *addr_size); + +/** + * @brief Prints the content of the given entry + * + * @param[in] entry pointer to the universal_address_container_t to be printed + */ +void universal_address_print_entry(universal_address_container_t *entry); + +/** + * @brief returns the number of used entries + */ +int universal_address_get_num_used_entries(void); + +/** + * @brief Prints the content of the genereic address table up to the used element + */ +void universal_address_print_table(void); + +#ifdef __cplusplus +} +#endif + +#endif /* UNIVERSAL_ADDRESS_H_ */ +/** @} */ diff --git a/sys/net/network_layer/fib/Makefile b/sys/net/network_layer/fib/Makefile new file mode 100644 index 000000000000..48422e909a47 --- /dev/null +++ b/sys/net/network_layer/fib/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/sys/net/network_layer/fib/fib.c b/sys/net/network_layer/fib/fib.c new file mode 100644 index 000000000000..00bc21858753 --- /dev/null +++ b/sys/net/network_layer/fib/fib.c @@ -0,0 +1,584 @@ +/** + * FIB implementation + * + * Copyright (C) 2014 Martin Landsmann + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + * + * @ingroup fib + * @{ + * @file fib.c + * @brief Functions to manage FIB entries + * @author Martin Landsmann + * @} + */ + +#include +#include +#include +#include +#include "thread.h" +#include "mutex.h" +#include "msg.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#include "ng_fib.h" +#include "ng_fib/ng_fib_table.h" + +/** + * @brief access mutex to control exclusive operations on calls + */ +static mutex_t mtx_access = MUTEX_INIT; + +/** + * @brief maximum number of handled reactice routing protocols (RRP) + * used to notify the saved kernel_pid_t + */ +#define FIB_MAX_RRP (5) + +/** + * @brief registered RRPs for notification un unreachable destinations + */ +static size_t notify_rrp_pos = 0; + +/** + * @brief the kernel_pid_t for notifying the RRPs + */ +static kernel_pid_t notify_rrp[FIB_MAX_RRP]; + +/** + * @brief maximum number of FIB tables entries handled + */ +#define FIB_MAX_FIB_TABLE_ENTRIES (20) + +/** + * @brief array of the FIB tables + */ +static fib_entry_t fib_table[FIB_MAX_FIB_TABLE_ENTRIES]; + +/** + * @brief convert given ms to a timepoint from now on in the future + * @param[in] ms the miliseconds to be converted + * @param[out] timex the converted timepoint + */ +static void fib_ms_to_timex(uint32_t ms, timex_t *timex) +{ + vtimer_now(timex); + timex->seconds += ms / 1000; + timex->microseconds += (ms - timex->seconds * 1000) * 1000; +} + +/** + * @brief returns pointer to the entry for the given destination address + * + * @param[in] dst the destination address + * @param[in] dst_size the destination address size + * @param[out] entry_arr the array to scribe the found match + * @param[in, out] entry_arr_size the number of entries provided by entry_arr (should be always 1) + * this value is overwritten with the actual found number + * + * @return 0 if we found a next-hop prefix + * 1 if we found the exact address next-hop + * -EHOSTUNREACH if no fitting next-hop is available + */ +static int fib_find_entry(uint8_t *dst, size_t dst_size, + fib_entry_t **entry_arr, size_t *entry_arr_size) +{ + timex_t now; + vtimer_now(&now); + + size_t count = 0; + size_t prefix_size = 0; + size_t match_size = dst_size; + int ret = -EHOSTUNREACH; + + for (size_t i = 0; i < FIB_MAX_FIB_TABLE_ENTRIES; ++i) { + + /* autoinvalidate if the entry lifetime is not set to not expire */ + if ((fib_table[i].lifetime.seconds != FIB_LIFETIME_NO_EXPIRE) + || (fib_table[i].lifetime.microseconds != FIB_LIFETIME_NO_EXPIRE)) { + + /* check if the lifetime expired */ + if (timex_cmp(now, fib_table[i].lifetime) > -1) { + /* remove this entry if its lifetime expired */ + fib_table[i].lifetime.seconds = 0; + fib_table[i].lifetime.microseconds = 0; + + if (fib_table[i].global != NULL) { + universal_address_rem(fib_table[i].global); + fib_table[i].global = NULL; + } + + if (fib_table[i].next_hop != NULL) { + universal_address_rem(fib_table[i].next_hop); + fib_table[i].next_hop = NULL; + } + } + } + + if ((prefix_size < dst_size) && + (fib_table[i].global != NULL) && + (universal_address_compare(fib_table[i].global, dst, &match_size) == 0)) { + + /* If we found an exact match */ + if (match_size == dst_size) { + entry_arr[0] = &(fib_table[i]); + *entry_arr_size = 1; + /* we will not find a better one so we return */ + return 1; + } + else { + /* we try to find the most fitting prefix */ + if (match_size > prefix_size) { + entry_arr[0] = &(fib_table[i]); + /* we could find a better one so we move on */ + ret = 0; + } + } + + prefix_size = match_size; + match_size = dst_size; + count = 1; + } + } + + *entry_arr_size = count; + return ret; +} + +/** + * @brief updates the next hop the lifetime and the interface id for a given entry + * + * @param[in] entry the entry to be updated + * @param[in] next_hop the next hop address to be updated + * @param[in] next_hop_size the next hop address size + * @param[in] next_hop_flags the next-hop address flags + * @param[in] lifetime the lifetime in ms + * + * @return 0 if the entry has been updated + * -ENOMEM if the entry cannot be updated due to unsufficient RAM + */ +static int fib_upd_entry(fib_entry_t *entry, + uint8_t *next_hop, size_t next_hop_size, uint32_t next_hop_flags, + uint32_t lifetime) +{ + universal_address_container_t *container = universal_address_add(next_hop, next_hop_size); + + if (container == NULL) { + return -ENOMEM; + } + + universal_address_rem(entry->next_hop); + entry->next_hop = container; + entry->next_hop_flags = next_hop_flags; + + if (lifetime < FIB_LIFETIME_NO_EXPIRE) { + fib_ms_to_timex(lifetime, &entry->lifetime); + } + else { + entry->lifetime.seconds = FIB_LIFETIME_NO_EXPIRE; + entry->lifetime.microseconds = FIB_LIFETIME_NO_EXPIRE; + } + + return 0; +} + +/** + * @brief creates a new FIB entry with the provided parameters + * + * @param[in] iface_id the interface ID + * @param[in] dst the destination address + * @param[in] dst_size the destination address size + * @param[in] dst_flags the destination address flags + * @param[in] next_hop the next hop address + * @param[in] next_hop_size the next hop address size + * @param[in] next_hop_flags the next-hop address flags + * @param[in] lifetime the lifetime in ms + * + * @return 0 on success + * -ENOMEM if no new entry can be created + */ +static int fib_create_entry(kernel_pid_t iface_id, + uint8_t *dst, size_t dst_size, uint32_t dst_flags, + uint8_t *next_hop, size_t next_hop_size, uint32_t next_hop_flags, + uint32_t lifetime) +{ + for (size_t i = 0; i < FIB_MAX_FIB_TABLE_ENTRIES; ++i) { + if (fib_table[i].lifetime.seconds == 0 && fib_table[i].lifetime.microseconds == 0) { + + fib_table[i].global = universal_address_add(dst, dst_size); + + if (fib_table[i].global != NULL) { + fib_table[i].global_flags = dst_flags; + fib_table[i].next_hop = universal_address_add(next_hop, next_hop_size); + fib_table[i].next_hop_flags = next_hop_flags; + } + + if (fib_table[i].next_hop != NULL) { + /* everything worked fine */ + fib_table[i].iface_id = iface_id; + + if (lifetime < FIB_LIFETIME_NO_EXPIRE) { + fib_ms_to_timex(lifetime, &fib_table[i].lifetime); + } + else { + fib_table[i].lifetime.seconds = FIB_LIFETIME_NO_EXPIRE; + fib_table[i].lifetime.microseconds = FIB_LIFETIME_NO_EXPIRE; + } + + return 0; + } + } + } + + return -ENOMEM; +} + +/** + * @brief removes the given entry + * + * @param[in] entry the entry to be removed + * + * @return 0 on success + */ +static int fib_remove(fib_entry_t *entry) +{ + if (entry->global != NULL) { + universal_address_rem(entry->global); + } + + if (entry->next_hop) { + universal_address_rem(entry->next_hop); + } + + entry->global = NULL; + entry->global_flags = 0; + entry->next_hop = NULL; + entry->next_hop_flags = 0; + + entry->iface_id = KERNEL_PID_UNDEF; + entry->lifetime.seconds = 0; + entry->lifetime.microseconds = 0; + + return 0; +} + +/** + * @brief signalls (sends a message to) all registered RRPs + * to start a route discovery for the provided destination. + * The receiver MUST copy the content, i.e. the address before reply. + * + * @param[in] dst the destination address + * @param[in] dst_size the destination address size + * + * @return 0 on a new available entry, + * -ENOENT if no suiting entry is provided. + */ +static int fib_signal_rrp(uint8_t *dst, size_t dst_size, uint32_t dst_flags) +{ + msg_t msg, reply; + rrp_address_msg_t content; + content.address = dst; + content.address_size = dst_size; + content.address_flags = dst_flags; + int ret = -ENOENT; + + msg.type = FIB_MSG_RRP_SIGNAL; + msg.content.ptr = (void *)&content; + + for (size_t i = 0; i < FIB_MAX_RRP; ++i) { + if (notify_rrp[i] != KERNEL_PID_UNDEF) { + DEBUG("[fib_signal_rrp] send msg@: %p to pid[%d]: %d\n", \ + msg.content.ptr, (int)i, (int)notify_rrp[i]); + + /* the receiver, i.e. the RRP, MUST copy the content value. + * using the provided pointer after replying this message + * will lead to errors + */ + msg_send_receive(&msg, &reply, notify_rrp[i]); + DEBUG("[fib_signal_rrp] got reply."); + } + } + + return ret; +} + +int fib_add_entry(kernel_pid_t iface_id, uint8_t *dst, size_t dst_size, uint32_t dst_flags, + uint8_t *next_hop, size_t next_hop_size, uint32_t next_hop_flags, + uint32_t lifetime) +{ + mutex_lock(&mtx_access); + DEBUG("[fib_add_entry]"); + size_t count = 1; + fib_entry_t *entry[count]; + + int ret = fib_find_entry(dst, dst_size, &(entry[0]), &count); + + if (ret == 1) { + /* we must take the according entry and update the values */ + ret = fib_upd_entry(entry[0], next_hop, next_hop_size, next_hop_flags, lifetime); + } + else { + ret = fib_create_entry(iface_id, dst, dst_size, dst_flags, + next_hop, next_hop_size, next_hop_flags, lifetime); + } + + mutex_unlock(&mtx_access); + return ret; +} + +int fib_update_entry(uint8_t *dst, size_t dst_size, + uint8_t *next_hop, size_t next_hop_size, uint32_t next_hop_flags, + uint32_t lifetime) +{ + mutex_lock(&mtx_access); + DEBUG("[fib_update_entry]"); + size_t count = 1; + fib_entry_t *entry[count]; + int ret = -ENOMEM; + + if (fib_find_entry(dst, dst_size, &(entry[0]), &count) == 1) { + DEBUG("[fib_update_entry] found entry: %p\n", (void *)(entry[0])); + /* we must take the according entry and update the values */ + ret = fib_upd_entry(entry[0], next_hop, next_hop_size, next_hop_flags, lifetime); + } + else { + /* we have ambigious entries, i.e. count > 1 + * this should never happen + */ + DEBUG("[fib_update_entry] ambigious entries detected!!!"); + } + + mutex_unlock(&mtx_access); + return ret; +} + +void fib_remove_entry(uint8_t *dst, size_t dst_size) +{ + mutex_lock(&mtx_access); + DEBUG("[fib_remove_entry]"); + size_t count = 1; + fib_entry_t *entry[count]; + + int ret = fib_find_entry(dst, dst_size, &(entry[0]), &count); + + if (ret == 1) { + /* we must take the according entry and update the values */ + fib_remove(entry[0]); + } + else { + /* we have ambigious entries, i.e. count > 1 + * this should never happen + */ + DEBUG("[fib_update_entry] ambigious entries detected!!!"); + } + + mutex_unlock(&mtx_access); +} + +int fib_get_next_hop(kernel_pid_t *iface_id, + uint8_t *next_hop, size_t *next_hop_size, uint32_t *next_hop_flags, + uint8_t *dst, size_t dst_size, uint32_t dst_flags) +{ + mutex_lock(&mtx_access); + DEBUG("[fib_get_next_hop]"); + size_t count = 1; + fib_entry_t *entry[count]; + + int ret = fib_find_entry(dst, dst_size, &(entry[0]), &count); + + if (!(ret == 0 || ret == 1)) { + /* notify all RRPs for route discovery if available */ + if (fib_signal_rrp(dst, dst_size, dst_flags) == 0) { + count = 1; + /* now lets see if the RRPs have found a valid next-hop */ + ret = fib_find_entry(dst, dst_size, &(entry[0]), &count); + } + } + + if (ret == 0 || ret == 1) { + + uint8_t *address_ret = universal_address_get_address(entry[0]->next_hop, + next_hop, next_hop_size); + + if (address_ret == NULL) { + mutex_unlock(&mtx_access); + return -ENOBUFS; + } + } + else { + mutex_unlock(&mtx_access); + return -EHOSTUNREACH; + } + + *iface_id = entry[0]->iface_id; + *next_hop_flags = entry[0]->next_hop_flags; + mutex_unlock(&mtx_access); + return 0; +} + +void fib_init(void) +{ + DEBUG("[fib_init] hello. Initializing some stuff."); + mutex_lock(&mtx_access); + + for (size_t i = 0; i < FIB_MAX_RRP; ++i) { + notify_rrp[i] = KERNEL_PID_UNDEF; + } + + for (size_t i = 0; i < FIB_MAX_FIB_TABLE_ENTRIES; ++i) { + fib_table[i].iface_id = 0; + fib_table[i].lifetime.seconds = 0; + fib_table[i].lifetime.microseconds = 0; + fib_table[i].global_flags = 0; + fib_table[i].global = NULL; + fib_table[i].next_hop_flags = 0; + fib_table[i].next_hop = NULL; + } + + universal_address_init(); + mutex_unlock(&mtx_access); +} + +void fib_deinit(void) +{ + DEBUG("[fib_deinit] hello. De-Initializing stuff."); + mutex_lock(&mtx_access); + + for (size_t i = 0; i < FIB_MAX_RRP; ++i) { + notify_rrp[i] = KERNEL_PID_UNDEF; + } + + notify_rrp_pos = 0; + + for (size_t i = 0; i < FIB_MAX_FIB_TABLE_ENTRIES; ++i) { + fib_table[i].iface_id = 0; + fib_table[i].lifetime.seconds = 0; + fib_table[i].lifetime.microseconds = 0; + fib_table[i].global_flags = 0; + fib_table[i].global = NULL; + fib_table[i].next_hop_flags = 0; + fib_table[i].next_hop = NULL; + } + + universal_address_reset(); + mutex_unlock(&mtx_access); +} + +void fib_register_rrp(void) +{ + mutex_lock(&mtx_access); + + if (notify_rrp_pos < FIB_MAX_RRP) { + notify_rrp[notify_rrp_pos] = sched_active_pid; + notify_rrp_pos++; + } + + mutex_unlock(&mtx_access); +} + +int fib_get_num_used_entries(void) +{ + mutex_lock(&mtx_access); + size_t used_entries = 0; + + for (size_t i = 0; i < FIB_MAX_FIB_TABLE_ENTRIES; ++i) { + used_entries += (size_t)(fib_table[i].global != NULL); + } + + mutex_unlock(&mtx_access); + return used_entries; +} + +/* print functions */ + +void fib_print_notify_rrp(void) +{ + mutex_lock(&mtx_access); + + for (size_t i = 0; i < FIB_MAX_RRP; ++i) { + printf("[fib_print_notify_rrp] pid[%d]: %d\n", (int)i, (int)notify_rrp[i]); + } + + mutex_unlock(&mtx_access); +} + +void fib_print_fib_table(void) +{ + mutex_lock(&mtx_access); + + for (size_t i = 0; i < FIB_MAX_FIB_TABLE_ENTRIES; ++i) { + printf("[fib_print_fib_table] %d) iface_id: %d, global: %p, next hop: %p, lifetime: %d.%d\n", \ + (int)i, \ + (int)fib_table[i].iface_id, \ + (void *)fib_table[i].global, \ + (void *)fib_table[i].next_hop, \ + (int)fib_table[i].lifetime.seconds, \ + (int)fib_table[i].lifetime.microseconds); + } + + mutex_unlock(&mtx_access); +} + +static void fib_print_adress(universal_address_container_t *entry) +{ + uint8_t address[UNIVERSAL_ADDRESS_SIZE]; + size_t addr_size = UNIVERSAL_ADDRESS_SIZE; + uint8_t *ret = universal_address_get_address(entry, address, &addr_size); + + if (ret == address) { + for (size_t i = 0; i < UNIVERSAL_ADDRESS_SIZE; ++i) { + if (i <= addr_size) { + printf("%02x", address[i]); + } + else { + printf(" "); + } + } + } +} + +void fib_print_routes(void) +{ + mutex_lock(&mtx_access); + printf("%-32s %-6s %-32s %-6s %-16s Interface\n" + , "Destination", "Flags", "Next Hop", "Flags", "Expires"); + + timex_t now; + vtimer_now(&now); + + for (size_t i = 0; i < FIB_MAX_FIB_TABLE_ENTRIES; ++i) { + if (fib_table[i].lifetime.seconds != 0 || fib_table[i].lifetime.microseconds != 0) { + fib_print_adress(fib_table[i].global); + printf(" 0x%04x ", fib_table[i].global_flags); + fib_print_adress(fib_table[i].next_hop); + printf(" 0x%04x ", fib_table[i].next_hop_flags); + + if ((fib_table[i].lifetime.seconds != FIB_LIFETIME_NO_EXPIRE) + || (fib_table[i].lifetime.microseconds != FIB_LIFETIME_NO_EXPIRE)) { + + timex_t tm = timex_sub(fib_table[i].lifetime, now); + + /* we must interpret the values as signed */ + if ((int32_t)tm.seconds < 0 + || (tm.seconds == 0 && (int32_t)tm.microseconds < 0)) { + printf("%-16s ", "EXPIRED"); + } + else { + printf("%"PRIu32".%05"PRIu32, tm.seconds, tm.microseconds); + } + } + else { + printf("%-16s ", "NEVER"); + } + + printf("%d\n", (int)fib_table[i].iface_id); + } + } + + mutex_unlock(&mtx_access); +} diff --git a/sys/net/network_layer/fib/universal_address.c b/sys/net/network_layer/fib/universal_address.c new file mode 100644 index 000000000000..d1d817479e25 --- /dev/null +++ b/sys/net/network_layer/fib/universal_address.c @@ -0,0 +1,261 @@ +/** + * universal address container implementation + * + * Copyright (C) 2014 Martin Landsmann + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + * + * @ingroup fib + * @{ + * @file universal_address.c + * @brief Functions to manage universal address container + * @author Martin Landsmann + * @} + */ + +#include +#include +#include +#include +#include "mutex.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" +#include "ng_fib/ng_universal_address.h" + +/** + * @brief Maximum number of entries handled + */ +#define UNIVERSAL_ADDRESS_MAX_ENTRIES (40) + +/** + * @brief counter indicating the number of entries allocated + */ +static size_t universal_address_table_filled = 0; + +/** + * @brief The array of universal_address containers + */ +static universal_address_container_t universal_address_table[UNIVERSAL_ADDRESS_MAX_ENTRIES]; + +/** + * @brief access mutex to control exclusive operations on calls + */ +static mutex_t mtx_access = MUTEX_INIT; + +/** + * @brief finds the universal address container for the given address + * + * @param[in] addr pointer to the address + * @param[in] addr_size the number of bytes required for the address entry + * + * @return pointer to the universal_address_container_t containing the address on success + * NULL if the address could not be inserted + */ +static universal_address_container_t *universal_address_find_entry(uint8_t *addr, size_t addr_size) +{ + for (size_t i = 0; i < UNIVERSAL_ADDRESS_MAX_ENTRIES; ++i) { + if ((universal_address_table[i].address_size == addr_size) && (universal_address_table[i].address != NULL)) { + if (memcmp((universal_address_table[i].address), addr, addr_size) == 0) { + return &(universal_address_table[i]); + } + } + } + + return NULL; +} + +/** + * @brief finds the next empty or unused universal address containers + * + * @return pointer to the next free/unused universal_address_container_t + * or NULL if no memory is left in universal_address_table + */ +static universal_address_container_t *universal_address_get_next_unused_entry(void) +{ + if (universal_address_table_filled < UNIVERSAL_ADDRESS_MAX_ENTRIES) { + for (size_t i = 0; i < UNIVERSAL_ADDRESS_MAX_ENTRIES; ++i) { + if ((universal_address_table[i].use_count == 0)) { + return &(universal_address_table[i]); + } + } + } + + return NULL; +} + +universal_address_container_t *universal_address_add(uint8_t *addr, size_t addr_size) +{ + mutex_lock(&mtx_access); + universal_address_container_t *pEntry = universal_address_find_entry(addr, addr_size); + + if (pEntry == NULL) { + /* look for a free entry */ + pEntry = universal_address_get_next_unused_entry(); + + if (pEntry == NULL) { + mutex_unlock(&mtx_access); + /* no free room */ + return NULL; + } + + /* look if the former memory has distinct size */ + if (pEntry->address_size != addr_size) { + /* clean the address */ + memset(pEntry->address, 0, UNIVERSAL_ADDRESS_SIZE); + + /* set the used bytes */ + pEntry->address_size = addr_size; + pEntry->use_count = 0; + } + + /* copy the address */ + memcpy((pEntry->address), addr, addr_size); + } + + pEntry->use_count++; + + if (pEntry->use_count == 1) { + DEBUG("[universal_address_add] universal_address_table_filled: %d\n", \ + (int)universal_address_table_filled); + universal_address_table_filled++; + } + + mutex_unlock(&mtx_access); + return pEntry; +} + +void universal_address_rem(universal_address_container_t *entry) +{ + mutex_lock(&mtx_access); + DEBUG("[universal_address_rem] entry: %p\n", (void *)entry); + + /* we do not delete anything on remove */ + if (entry != NULL) { + if (entry->use_count != 0) { + entry->use_count--; + + if (entry->use_count == 0) { + universal_address_table_filled--; + } + } + else { + DEBUG("[universal_address_rem] universal_address_table_filled: %d\n", \ + (int)universal_address_table_filled); + } + } + + mutex_unlock(&mtx_access); +} + +uint8_t* universal_address_get_address(universal_address_container_t *entry, + uint8_t *addr, size_t *addr_size) +{ + mutex_lock(&mtx_access); + + if (*addr_size >= entry->address_size) { + memcpy(addr, entry->address, entry->address_size); + *addr_size = entry->address_size; + mutex_unlock(&mtx_access); + return addr; + } + + *addr_size = entry->address_size; + mutex_unlock(&mtx_access); + return NULL; +} + +int universal_address_compare(universal_address_container_t *entry, + uint8_t *addr, size_t *addr_size) +{ + mutex_lock(&mtx_access); + + int ret = -ENOENT; + + /* If we have distinct sizes, the addresses are probably not comperable */ + if (entry->address_size != *addr_size) { + mutex_unlock(&mtx_access); + return ret; + } + + /* Get the index of the first trailing `0` (indicates a network prefix) */ + int i = 0; + for( i = entry->address_size-1; i >= 0; --i) { + if( entry->address[i] != 0 ) { + break; + } + } + + if( memcmp(entry->address, addr, i+1) == 0 ) { + ret = 0; + *addr_size = i+1; + } + + mutex_unlock(&mtx_access); + return ret; +} + +void universal_address_init(void) +{ + mutex_lock(&mtx_access); + + for (size_t i = 0; i < UNIVERSAL_ADDRESS_MAX_ENTRIES; ++i) { + universal_address_table[i].use_count = 0; + universal_address_table[i].address_size = 0; + memset(universal_address_table[i].address, 0, UNIVERSAL_ADDRESS_SIZE); + } + + mutex_unlock(&mtx_access); +} + +void universal_address_reset(void) +{ + mutex_lock(&mtx_access); + + for (size_t i = 0; i < UNIVERSAL_ADDRESS_MAX_ENTRIES; ++i) { + universal_address_table[i].use_count = 0; + } + + universal_address_table_filled = 0; + mutex_unlock(&mtx_access); +} + +void universal_address_print_entry(universal_address_container_t *entry) +{ + mutex_lock(&mtx_access); + + if (entry != NULL) { + printf("[universal_address_print_entry] entry@: %p, use_count: %d, \ +address_size: %d, content: ", \ + (void *)entry, (int)entry->use_count, (int)entry->address_size); + + for (size_t i = 0; i < entry->address_size; ++i) { + /* printf("%02x ", (char)entry->address[i]); */ + printf("%c", (char)entry->address[i]); + } + + puts(""); + } + + mutex_unlock(&mtx_access); +} + +int universal_address_get_num_used_entries(void) +{ + mutex_lock(&mtx_access); + size_t ret = universal_address_table_filled; + mutex_unlock(&mtx_access); + return ret; +} + +void universal_address_print_table(void) +{ + printf("[universal_address_print_table] universal_address_table_filled: %d\n", \ + (int)universal_address_table_filled); + + for (size_t i = 0; i < UNIVERSAL_ADDRESS_MAX_ENTRIES; ++i) { + universal_address_print_entry((&universal_address_table[i])); + } +} diff --git a/sys/shell/commands/Makefile b/sys/shell/commands/Makefile index 68ca2308b4cd..a86ce8bc800b 100644 --- a/sys/shell/commands/Makefile +++ b/sys/shell/commands/Makefile @@ -58,6 +58,9 @@ endif ifneq (,$(filter ng_netif,$(USEMODULE))) SRC += sc_netif.c endif +ifneq (,$(filter fib,$(USEMODULE))) + SRC += sc_fib.c +endif # TODO # Conditional building not possible at the moment due to diff --git a/sys/shell/commands/sc_fib.c b/sys/shell/commands/sc_fib.c new file mode 100644 index 000000000000..c68e0aaaa0ef --- /dev/null +++ b/sys/shell/commands/sc_fib.c @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2015 Martin Landsmann + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup sys_shell_commands + * @{ + * + * @file + * @brief Provides shell commands to manage and show FIB Entries + * + * @author 2015 Martin Landsmann + * + * @} + */ + +#include +#include +#include +#include +#include "thread.h" +#include "inet_pton.h" +#include "ng_fib.h" + +#define INFO1_TXT "fibroute add via dev " +#define INFO2_TXT " [lifetime ]" +#define INFO3_TXT " - the destination address\n" \ + " - the address of the next-hop towards the \n" \ + " - the device id of the Interface to use\n" +#define INFO4_TXT " - optional lifetime in ms when the entry automatically invalidates\n" +#define INFO5_TXT "fibroute del \n" \ + " - the destination address of the entry to be deleted\n" + +static unsigned char tmp_ipv4_dst[INADDRSZ]; /**< buffer for ipv4 address conversion */ +static unsigned char tmp_ipv4_nxt[INADDRSZ]; /**< buffer for ipv4 address conversion */ +static unsigned char tmp_ipv6_dst[IN6ADDRSZ]; /**< buffer for ipv6 address conversion */ +static unsigned char tmp_ipv6_nxt[IN6ADDRSZ]; /**< buffer for ipv6 address conversion */ + +static void _fib_usage(int info) +{ + switch (info) { + case 1: { + puts("\nbrief: adds a new entry to the FIB.\nusage: " + INFO1_TXT "\n" INFO3_TXT); + break; + } + + case 2: { + puts("\nbrief: adds a new entry to the FIB.\nusage: " + INFO1_TXT INFO2_TXT "\n" INFO3_TXT INFO4_TXT); + break; + } + + case 3: { + puts("\nbrief: deletes an entry from the FIB.\nusage: " + INFO5_TXT); + break; + } + + default: + break; + }; +} + +void _fib_route_handler(int argc, char **argv) +{ + /* e.g. fibroute right now dont care about the adress/protocol family */ + if (argc == 1) { + fib_print_routes(); + return; + } + + /* e.g. firoute [add|del] */ + if (argc == 2) { + if ((strcmp("add", argv[1]) == 0)) { + _fib_usage(2); + } + else if ((strcmp("del", argv[1]) == 0)) { + _fib_usage(3); + } + else { + puts("\nunrecognized parameter1.\nPlease enter fibroute [add|del] for more information."); + } + + return; + } + + if (argc > 2 && !((strcmp("add", argv[1]) == 0) || (strcmp("del", argv[1]) == 0))) { + puts("\nunrecognized parameter2.\nPlease enter fibroute [add|del] for more information."); + return; + } + + /* e.g. fibroute del */ + if (argc == 3) { + if (inet_pton(AF_INET6, argv[2], tmp_ipv6_dst)) { + fib_remove_entry(tmp_ipv6_dst, IN6ADDRSZ); + } + else if (inet_pton(AF_INET, argv[2], tmp_ipv4_dst)) { + fib_remove_entry(tmp_ipv4_dst, INADDRSZ); + } + else { + fib_remove_entry((uint8_t *)argv[2], (strlen(argv[2]))); + } + + return; + } + + /* e.g. fibroute add via dev */ + if (argc == 7) { + if ((strcmp("add", argv[1]) == 0) && (strcmp("via", argv[3]) == 0) + && (strcmp("dev", argv[5]) == 0)) { + + unsigned char *dst = (unsigned char *)argv[2]; + size_t dst_size = (strlen(argv[2])); + uint32_t dst_flags = 0xffff; + unsigned char *nxt = (unsigned char *)argv[4]; + size_t nxt_size = (strlen(argv[4])); + uint32_t nxt_flags = 0xffff; + + /* determine destination address */ + if (inet_pton(AF_INET6, argv[2], tmp_ipv6_dst)) { + dst = tmp_ipv6_dst; + dst_size = IN6ADDRSZ; + dst_flags = AF_INET6; + } + else if (inet_pton(AF_INET, argv[2], tmp_ipv4_dst)) { + dst = tmp_ipv4_dst; + dst_size = INADDRSZ; + dst_flags = AF_INET; + } + + /* determine next-hop address */ + if (inet_pton(AF_INET6, argv[4], tmp_ipv6_nxt)) { + nxt = tmp_ipv6_nxt; + nxt_size = IN6ADDRSZ; + nxt_flags = AF_INET6; + } + else if (inet_pton(AF_INET, argv[4], tmp_ipv4_nxt)) { + nxt = tmp_ipv4_nxt; + nxt_size = INADDRSZ; + nxt_flags = AF_INET; + } + + fib_add_entry((kernel_pid_t) atoi(argv[6]), + dst, dst_size, dst_flags, + nxt, nxt_size, nxt_flags, + FIB_LIFETIME_NO_EXPIRE); + } + else { + _fib_usage(1); + } + + return; + } + + /* e.g. fibroute add via dev lifetime */ + if (argc == 9) { + if ((strcmp("add", argv[1]) == 0) && (strcmp("via", argv[3]) == 0) + && (strcmp("dev", argv[5]) == 0) + && (strcmp("lifetime", argv[7]) == 0)) { + + unsigned char *dst = (unsigned char *)argv[2]; + size_t dst_size = (strlen(argv[2])); + uint32_t dst_flags = 0xffff; + unsigned char *nxt = (unsigned char *)argv[4]; + size_t nxt_size = (strlen(argv[4])); + uint32_t nxt_flags = 0xffff; + + /* determine destination address */ + if (inet_pton(AF_INET6, argv[2], tmp_ipv6_dst)) { + dst = tmp_ipv6_dst; + dst_size = IN6ADDRSZ; + dst_flags = AF_INET6; + } + else if (inet_pton(AF_INET, argv[2], tmp_ipv4_dst)) { + dst = tmp_ipv4_dst; + dst_size = INADDRSZ; + dst_flags = AF_INET; + } + + /* determine next-hop address */ + if (inet_pton(AF_INET6, argv[4], tmp_ipv6_nxt)) { + nxt = tmp_ipv6_nxt; + nxt_size = IN6ADDRSZ; + nxt_flags = AF_INET6; + } + else if (inet_pton(AF_INET, argv[4], tmp_ipv4_nxt)) { + nxt = tmp_ipv4_nxt; + nxt_size = INADDRSZ; + nxt_flags = AF_INET; + } + + fib_add_entry((kernel_pid_t) atoi(argv[6]), + dst, dst_size, dst_flags, + nxt, nxt_size, nxt_flags, + (uint32_t)atoi(argv[8])); + } + else { + _fib_usage(2); + } + + return; + } + + puts("\nunrecognized parameters.\nPlease enter fibroute [add|del] for more information."); +} diff --git a/sys/shell/commands/shell_commands.c b/sys/shell/commands/shell_commands.c index dd87a9aa32c3..0cc4c779b531 100644 --- a/sys/shell/commands/shell_commands.c +++ b/sys/shell/commands/shell_commands.c @@ -159,6 +159,10 @@ extern int _netif_send(int argc, char **argv); #endif #endif +#ifdef MODULE_FIB +extern void _fib_route_handler( int argc, char **argv ); +#endif + const shell_command_t _shell_command_list[] = { {"reboot", "Reboot the node", _reboot_handler}, #ifdef MODULE_CONFIG @@ -259,6 +263,13 @@ const shell_command_t _shell_command_list[] = { #ifndef MODULE_TRANSCEIVER {"txtsnd", "send raw data", _netif_send }, #endif +#endif +#ifdef MODULE_FIB + { + "fibroute", + "Manipulate the FIB (info: 'fibroute [add|del]')", + _fib_route_handler + }, #endif {NULL, NULL, NULL} }; diff --git a/tests/unittests/tests-fib/Makefile b/tests/unittests/tests-fib/Makefile new file mode 100644 index 000000000000..cc35beb96756 --- /dev/null +++ b/tests/unittests/tests-fib/Makefile @@ -0,0 +1,3 @@ +MODULE = tests-fib + +include $(RIOTBASE)/Makefile.base diff --git a/tests/unittests/tests-fib/Makefile.include b/tests/unittests/tests-fib/Makefile.include new file mode 100644 index 000000000000..7b57b02e44a1 --- /dev/null +++ b/tests/unittests/tests-fib/Makefile.include @@ -0,0 +1 @@ +USEMODULE += fib diff --git a/tests/unittests/tests-fib/tests-fib.c b/tests/unittests/tests-fib/tests-fib.c new file mode 100644 index 000000000000..407adaa0d062 --- /dev/null +++ b/tests/unittests/tests-fib/tests-fib.c @@ -0,0 +1,560 @@ +/* +* Copyright (C) 2015 Martin Landsmann +* +* 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. +*/ + +#define TEST_FIB_SHOW_OUTPUT (0) /**< set */ + +#include /**< required for snprintf() */ +#include +#include +#include "embUnit.h" +#include "tests-fib.h" + +#include "thread.h" +#include "ng_fib.h" +#include "ng_fib/ng_universal_address.h" + +/* +* @brief helper to fill FIB with unique entries +*/ +static void _fill_FIB_unique(size_t entries) +{ + size_t add_buf_size = 16; + char addr_dst[add_buf_size]; + char addr_nxt[add_buf_size]; + uint32_t addr_dst_flags = 0x77777777; + uint32_t addr_nxt_flags = 0x77777777; + + for (size_t i = 0; i < entries; ++i) { + /* construct "addresses" for the FIB */ + snprintf(addr_dst, add_buf_size, "Test address %02d", (int)i); + snprintf(addr_nxt, add_buf_size, "Test address %02d", entries + i); + /* the terminating \0 is unnecessary here */ + fib_add_entry(42, (uint8_t *)addr_dst, add_buf_size - 1, addr_dst_flags, + (uint8_t *)addr_nxt, add_buf_size - 1, addr_nxt_flags, 10000); + } +} + +/* +* @brief helper to fill FIB with multiple used entries +* The modulus adjusts the number of reused addresses +*/ +static void _fill_FIB_multiple(size_t entries, size_t modulus) +{ + size_t add_buf_size = 16; + char addr_dst[add_buf_size]; + char addr_nxt[add_buf_size]; + uint32_t addr_dst_flags = 0x33333333; + uint32_t addr_nxt_flags = 0x33333333; + + for (size_t i = 0; i < entries; ++i) { + /* construct "addresses" for the FIB */ + snprintf(addr_dst, add_buf_size, "Test address %02d", (int)i); + snprintf(addr_nxt, add_buf_size, "Test address %02d", i % modulus); + fib_add_entry(42, (uint8_t *)addr_dst, add_buf_size - 1, addr_dst_flags, + (uint8_t *)addr_nxt, add_buf_size - 1, addr_nxt_flags, 10000); + } +} + +/* +* @brief filling the FIB with entries +* It is expected to have 20 FIB entries and 40 used universal address entries +*/ +static void test_fib_01_fill_unique_entries(void) +{ + _fill_FIB_unique(20); +#if (TEST_FIB_SHOW_OUTPUT == 1) + fib_print_fib_table(); + puts(""); + universal_address_print_table(); + puts(""); +#endif + TEST_ASSERT_EQUAL_INT(20, fib_get_num_used_entries()); + TEST_ASSERT_EQUAL_INT(40, universal_address_get_num_used_entries()); + fib_deinit(); +} + +/* + * @brief filling the FIB with reusable entries + * It is expected to have 20 FIB entries and 20 universal address entries + */ +static void test_fib_02_fill_multiple_entries(void) +{ + size_t entries = 20; + _fill_FIB_multiple(entries, 11); + +#if (TEST_FIB_SHOW_OUTPUT == 1) + fib_print_fib_table(); + puts(""); + universal_address_print_table(); + puts(""); +#endif + TEST_ASSERT_EQUAL_INT(20, fib_get_num_used_entries()); + TEST_ASSERT_EQUAL_INT(20, universal_address_get_num_used_entries()); + fib_deinit(); +} + +/* +* @brief filling the FIB with entries and removing all entries +* It is expected to have 0 FIB entries and 0 universal address entries after remove +*/ +static void test_fib_03_removing_all_entries(void) +{ + size_t add_buf_size = 16; + char addr_dst[add_buf_size]; + + size_t entries = 20; + _fill_FIB_unique(entries); + + TEST_ASSERT_EQUAL_INT(20, fib_get_num_used_entries()); + TEST_ASSERT_EQUAL_INT(40, universal_address_get_num_used_entries()); + + for (size_t i = 0; i < entries; ++i) { + /* construct "addresses" to remove */ + snprintf(addr_dst, add_buf_size, "Test address %02d", (int)i); + fib_remove_entry((uint8_t *)addr_dst, add_buf_size - 1); + } + + TEST_ASSERT_EQUAL_INT(0, fib_get_num_used_entries()); + TEST_ASSERT_EQUAL_INT(0, universal_address_get_num_used_entries()); + +#if (TEST_FIB_SHOW_OUTPUT == 1) + fib_print_fib_table(); + puts(""); + universal_address_print_table(); + puts(""); +#endif + + fib_deinit(); +} + +/* +* @brief filling the FIB with entries and removing the lower 1/2 entries (0..9) +* It is expected to have 10 FIB entries and 19 used universal address entries after remove +*/ +static void test_fib_04_remove_lower_half(void) +{ + size_t add_buf_size = 16; + char addr_dst[add_buf_size]; + + size_t entries = 20; + _fill_FIB_multiple(entries, 11); + + TEST_ASSERT_EQUAL_INT(20, fib_get_num_used_entries()); + TEST_ASSERT_EQUAL_INT(20, universal_address_get_num_used_entries()); + + for (size_t i = 0; i < entries / 2; ++i) { + /* construct "addresses" to remove */ + snprintf(addr_dst, add_buf_size, "Test address %02d", (int)i); + fib_remove_entry((uint8_t *)addr_dst, add_buf_size - 1); + } + +#if (TEST_FIB_SHOW_OUTPUT == 1) + fib_print_fib_table(); + puts(""); + universal_address_print_table(); + puts(""); +#endif + TEST_ASSERT_EQUAL_INT(10, fib_get_num_used_entries()); + TEST_ASSERT_EQUAL_INT(19, universal_address_get_num_used_entries()); + fib_deinit(); +} + +/* +* @brief filling the FIB with entries and removing the upper 1/2 entries (10..19) +* It is expected to have 10 FIB entries and 10 universal address entries after remove +*/ +static void test_fib_05_remove_upper_half(void) +{ + size_t add_buf_size = 16; + char addr_dst[add_buf_size]; + + size_t entries = 20; + _fill_FIB_multiple(entries, 11); + + TEST_ASSERT_EQUAL_INT(20, fib_get_num_used_entries()); + TEST_ASSERT_EQUAL_INT(20, universal_address_get_num_used_entries()); + + for (size_t i = 0; i < entries / 2; ++i) { + /* construct "addresses" to remove */ + snprintf(addr_dst, add_buf_size, "Test address %02d", ((entries / 2) + i)); + fib_remove_entry((uint8_t *)addr_dst, add_buf_size - 1); + } + +#if (TEST_FIB_SHOW_OUTPUT == 1) + fib_print_fib_table(); + puts(""); + universal_address_print_table(); + puts(""); +#endif + TEST_ASSERT_EQUAL_INT(10, fib_get_num_used_entries()); + TEST_ASSERT_EQUAL_INT(10, universal_address_get_num_used_entries()); + fib_deinit(); +} + +/* +* @brief filling the FIB with entries and removing one entry +* It is expected to have 19 FIB entries and still 20 universal address entries +* after removing 02 +* (the use count for 02 is reduced to 1 after remove) +*/ +static void test_fib_06_remove_one_entry(void) +{ + size_t add_buf_size = 16; + char addr_dst[] = "Test address 02"; + + size_t entries = 20; + _fill_FIB_multiple(entries, 11); + + TEST_ASSERT_EQUAL_INT(20, fib_get_num_used_entries()); + TEST_ASSERT_EQUAL_INT(20, universal_address_get_num_used_entries()); + + fib_remove_entry((uint8_t *)addr_dst, add_buf_size - 1); + +#if (TEST_FIB_SHOW_OUTPUT == 1) + fib_print_fib_table(); + puts(""); + universal_address_print_table(); + puts(""); +#endif + TEST_ASSERT_EQUAL_INT(19, fib_get_num_used_entries()); + TEST_ASSERT_EQUAL_INT(20, universal_address_get_num_used_entries()); + fib_deinit(); +} + +/* +* @brief filling the FIB with entries and removing one entry several times +* It is expected to have 19 FIB entries and 19 universal address entries +* after removing 13 +*/ +static void test_fib_07_remove_one_entry_multiple_times(void) +{ + size_t add_buf_size = 16; /* includes space for terminating \0 */ + char addr_dst[] = "Test address 13"; + + size_t entries = 20; + _fill_FIB_multiple(entries, 11); + + TEST_ASSERT_EQUAL_INT(20, fib_get_num_used_entries()); + TEST_ASSERT_EQUAL_INT(20, universal_address_get_num_used_entries()); + + fib_remove_entry((uint8_t *)addr_dst, add_buf_size - 1); + fib_remove_entry((uint8_t *)addr_dst, add_buf_size - 1); + fib_remove_entry((uint8_t *)addr_dst, add_buf_size - 1); + fib_remove_entry((uint8_t *)addr_dst, add_buf_size - 1); + +#if (TEST_FIB_SHOW_OUTPUT == 1) + fib_print_fib_table(); + puts(""); + universal_address_print_table(); + puts(""); +#endif + TEST_ASSERT_EQUAL_INT(19, fib_get_num_used_entries()); + TEST_ASSERT_EQUAL_INT(19, universal_address_get_num_used_entries()); + fib_deinit(); +} + +/* +* @brief filling the FIB with entries and removing an unknown entry +* It is expected to have 20 FIB entries and 20 universal address entries after removing +*/ +static void test_fib_08_remove_unknown(void) +{ + size_t add_buf_size = 16; /* includes space for terminating \0 */ + char addr_dst[] = "Test address 99"; + + size_t entries = 20; + _fill_FIB_multiple(entries, 11); + + TEST_ASSERT_EQUAL_INT(20, fib_get_num_used_entries()); + TEST_ASSERT_EQUAL_INT(20, universal_address_get_num_used_entries()); + + fib_remove_entry((uint8_t *)addr_dst, add_buf_size - 1); + fib_remove_entry((uint8_t *)addr_dst, add_buf_size - 1); + fib_remove_entry((uint8_t *)addr_dst, add_buf_size - 1); + +#if (TEST_FIB_SHOW_OUTPUT == 1) + fib_print_fib_table(); + puts(""); + universal_address_print_table(); + puts(""); +#endif + TEST_ASSERT_EQUAL_INT(20, fib_get_num_used_entries()); + TEST_ASSERT_EQUAL_INT(20, universal_address_get_num_used_entries()); + fib_deinit(); +} + +/* +* @brief filling the FIB with entries and update an entry +* It is expected to have FIB entry 13 with updated lifetime of 9999 +* and entry 7 with updated iface ID of 7, lifetime of 7777 and next hop "Test address 77" +*/ +static void test_fib_09_update_entry(void) +{ + size_t add_buf_size = 16; /* includes space for terminating \0 */ + char addr_dst13[] = "Test address 13"; + char addr_dst07[] = "Test address 07"; + char addr_nxt2[] = "Test address 99"; + char addr_nxt77[] = "Test address 77"; + + size_t entries = 20; + _fill_FIB_multiple(entries, 11); + + TEST_ASSERT_EQUAL_INT(20, fib_get_num_used_entries()); + TEST_ASSERT_EQUAL_INT(20, universal_address_get_num_used_entries()); + + fib_update_entry((uint8_t *)addr_dst13, add_buf_size - 1, + (uint8_t *)addr_nxt2, add_buf_size - 1, 0x99, 9999); + fib_update_entry((uint8_t *)addr_dst07, add_buf_size - 1, + (uint8_t *)addr_nxt77, add_buf_size - 1, 0x77, 7777); + +#if (TEST_FIB_SHOW_OUTPUT == 1) + fib_print_fib_table(); + puts(""); + universal_address_print_table(); + puts(""); +#endif + fib_deinit(); +} + +/* +* @brief filling the FIB with entries and adding an additional one (not fitting) +* It is expected to have 20 FIB entries and to receive FPC_ERROR on adding an additional one +*/ +static void test_fib_10_add_exceed(void) +{ + size_t add_buf_size = 16; /* includes space for terminating \0 */ + char addr_dst[] = "Test address 98"; + char addr_nxt[] = "Test address 99"; + + size_t entries = 20; + _fill_FIB_unique(entries); + + TEST_ASSERT_EQUAL_INT(20, fib_get_num_used_entries()); + TEST_ASSERT_EQUAL_INT(40, universal_address_get_num_used_entries()); + + int ret = fib_add_entry(42, (uint8_t *)addr_dst, add_buf_size - 1, 0x98, + (uint8_t *)addr_nxt, add_buf_size - 1, 0x99, 9999); + + TEST_ASSERT_EQUAL_INT(-ENOMEM, ret); + TEST_ASSERT_EQUAL_INT(20, fib_get_num_used_entries()); + TEST_ASSERT_EQUAL_INT(40, universal_address_get_num_used_entries()); + +#if (TEST_FIB_SHOW_OUTPUT == 1) + fib_print_fib_table(); + puts(""); + universal_address_print_table(); + puts(""); +#endif + fib_deinit(); +} + +/* +* @brief get next hop for known destination +* It is expected to get the next hop 02 and receive 0 +*/ +static void test_fib_11_get_next_hop_success(void) +{ + size_t add_buf_size = 16; /* includes space for terminating \0 */ + char addr_dst[] = "Test address 13"; + char addr_expect[] = "Test address 02"; + kernel_pid_t iface_id = KERNEL_PID_UNDEF; + uint32_t next_hop_flags = 0; + char addr_nxt[add_buf_size]; + + size_t entries = 20; + _fill_FIB_multiple(entries, 11); + + TEST_ASSERT_EQUAL_INT(20, fib_get_num_used_entries()); + TEST_ASSERT_EQUAL_INT(20, universal_address_get_num_used_entries()); + int ret = fib_get_next_hop(&iface_id, + (uint8_t *)addr_nxt, &add_buf_size, &next_hop_flags, + (uint8_t *)addr_dst, add_buf_size - 1, 0x13); + + TEST_ASSERT_EQUAL_INT(0, ret); + TEST_ASSERT_EQUAL_INT(20, fib_get_num_used_entries()); + TEST_ASSERT_EQUAL_INT(20, universal_address_get_num_used_entries()); + + ret = strncmp(addr_expect, addr_nxt, add_buf_size); + TEST_ASSERT_EQUAL_INT(0, ret); + +#if (TEST_FIB_SHOW_OUTPUT == 1) + fib_print_fib_table(); + puts(""); + universal_address_print_table(); + puts(""); +#endif + fib_deinit(); +} + +/* +* @brief get next hop for unknown destination +* It is expected to get no next hop and receive -EHOSTUNREACH +*/ +static void test_fib_12_get_next_hop_fail(void) +{ + size_t add_buf_size = 16; /* includes space for terminating \0 */ + char addr_dst[] = "Test address 99"; + kernel_pid_t iface_id = KERNEL_PID_UNDEF; + uint32_t next_hop_flags = 0; + char addr_nxt[add_buf_size]; + + size_t entries = 20; + _fill_FIB_multiple(entries, 11); + TEST_ASSERT_EQUAL_INT(20, fib_get_num_used_entries()); + TEST_ASSERT_EQUAL_INT(20, universal_address_get_num_used_entries()); + int ret = fib_get_next_hop(&iface_id, + (uint8_t *)addr_nxt, &add_buf_size, &next_hop_flags, + (uint8_t *)addr_dst, add_buf_size - 1, 0x99); + + TEST_ASSERT_EQUAL_INT(-EHOSTUNREACH, ret); + +#if (TEST_FIB_SHOW_OUTPUT == 1) + fib_print_fib_table(); + puts(""); + universal_address_print_table(); + puts(""); +#endif + fib_deinit(); +} + +/* +* @brief get next hop for known destination but unsufficient size for the output +* It is expected to get no next hop and receive -ENOBUFS +*/ +static void test_fib_13_get_next_hop_fail_on_buffer_size(void) +{ + size_t add_buf_size = 16; /* includes space for terminating \0 */ + char addr_dst[] = "Test address 13"; + kernel_pid_t iface_id = KERNEL_PID_UNDEF; + uint32_t next_hop_flags = 0; + size_t add_buf_size_nxt = 12; + char addr_nxt[add_buf_size]; + + size_t entries = 20; + _fill_FIB_multiple(entries, 11); + TEST_ASSERT_EQUAL_INT(20, fib_get_num_used_entries()); + TEST_ASSERT_EQUAL_INT(20, universal_address_get_num_used_entries()); + + int ret = fib_get_next_hop(&iface_id, + (uint8_t *)addr_nxt, &add_buf_size_nxt, &next_hop_flags, + (uint8_t *)addr_dst, add_buf_size - 1, 0x13); + + TEST_ASSERT_EQUAL_INT(-ENOBUFS, ret); + TEST_ASSERT_EQUAL_INT(add_buf_size_nxt, add_buf_size - 1); + +#if (TEST_FIB_SHOW_OUTPUT == 1) + fib_print_fib_table(); + puts(""); + universal_address_print_table(); + puts(""); +#endif + fib_deinit(); +} + +/* +* @brief testing prefix and exact match +* It is expected receive 23 for addr123 as exact match and +* 12 for addr124 +*/ +static void test_fib_14_exact_and_prefix_match(void) +{ + size_t add_buf_size = 16; + char addr_dst[add_buf_size]; + char addr_nxt[add_buf_size]; + kernel_pid_t iface_id = KERNEL_PID_UNDEF; + uint32_t next_hop_flags = 0; + char addr_lookup[add_buf_size]; + memset(addr_dst, 0, add_buf_size); + memset(addr_nxt, 0, add_buf_size); + + snprintf(addr_dst, add_buf_size, "Test addr12"); + snprintf(addr_nxt, add_buf_size, "Test address %02d", 12); + fib_add_entry(42, (uint8_t *)addr_dst, add_buf_size - 1, 0x12, + (uint8_t *)addr_nxt, add_buf_size - 1, 0x12, 100000); + + snprintf(addr_dst, add_buf_size, "Test addr123"); + snprintf(addr_nxt, add_buf_size, "Test address %02d", 23); + fib_add_entry(42, (uint8_t *)addr_dst, add_buf_size - 1, 0x123, + (uint8_t *)addr_nxt, add_buf_size - 1, 0x23, 100000); + + snprintf(addr_dst, add_buf_size, "Test addr1234"); + snprintf(addr_nxt, add_buf_size, "Test address %02d", 34); + fib_add_entry(42, (uint8_t *)addr_dst, add_buf_size - 1, 0x1234, + (uint8_t *)addr_nxt, add_buf_size - 1, 0x34, 100000); + + /* exact match */ + snprintf(addr_lookup, add_buf_size, "Test addr123"); + int ret = fib_get_next_hop(&iface_id, + (uint8_t *)addr_nxt, &add_buf_size, &next_hop_flags, + (uint8_t *)addr_lookup, add_buf_size - 1, 0x123); + + TEST_ASSERT_EQUAL_INT(0, ret); + + char addr_expect_01[] = "Test address 23"; + ret = strncmp(addr_expect_01, addr_nxt, add_buf_size - 1); + TEST_ASSERT_EQUAL_INT(0, ret); + + /* prefix match */ + add_buf_size = 16; + memset(addr_nxt, 0, add_buf_size); + memset(addr_lookup, 0, add_buf_size); + + /* cppcheck: addr_lookup is only passed but not required to be read, + * since we test prefix matching + */ + /* cppcheck-suppress redundantCopy */ + snprintf(addr_lookup, add_buf_size, "Test addr124"); + ret = fib_get_next_hop(&iface_id, + (uint8_t *)addr_nxt, &add_buf_size, &next_hop_flags, + (uint8_t *)addr_lookup, add_buf_size - 1, 0x124); + + TEST_ASSERT_EQUAL_INT(0, ret); + + add_buf_size = 16; + char addr_expect_02[] = "Test address 12"; + ret = strncmp(addr_expect_02, addr_nxt, add_buf_size); + TEST_ASSERT_EQUAL_INT(0, ret); + +#if (TEST_FIB_SHOW_OUTPUT == 1) + fib_print_fib_table(); + puts(""); + universal_address_print_table(); + puts(""); +#endif + fib_deinit(); +} + +Test *tests_fib_tests(void) +{ + fib_init(); + EMB_UNIT_TESTFIXTURES(fixtures) { + new_TestFixture(test_fib_01_fill_unique_entries), + new_TestFixture(test_fib_02_fill_multiple_entries), + new_TestFixture(test_fib_03_removing_all_entries), + new_TestFixture(test_fib_04_remove_lower_half), + new_TestFixture(test_fib_05_remove_upper_half), + new_TestFixture(test_fib_06_remove_one_entry), + new_TestFixture(test_fib_07_remove_one_entry_multiple_times), + new_TestFixture(test_fib_08_remove_unknown), + new_TestFixture(test_fib_09_update_entry), + new_TestFixture(test_fib_10_add_exceed), + new_TestFixture(test_fib_11_get_next_hop_success), + new_TestFixture(test_fib_12_get_next_hop_fail), + new_TestFixture(test_fib_13_get_next_hop_fail_on_buffer_size), + new_TestFixture(test_fib_14_exact_and_prefix_match), + }; + + EMB_UNIT_TESTCALLER(fib_tests, NULL, NULL, fixtures); + + return (Test *)&fib_tests; +} + +void tests_fib(void) +{ + TESTS_RUN(tests_fib_tests()); +} diff --git a/tests/unittests/tests-fib/tests-fib.h b/tests/unittests/tests-fib/tests-fib.h new file mode 100644 index 000000000000..59f906d31a71 --- /dev/null +++ b/tests/unittests/tests-fib/tests-fib.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2015 Martin Landsmann + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @addtogroup unittests + * @{ + * + * @file tests-fib.h + * @brief Unittests for the ``fib`` module + * + * @author Martin Landsmann + */ +#ifndef TESTS_FIB_H_ +#define TESTS_FIB_H_ +#include "embUnit/embUnit.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** +* @brief The entry point of this test suite. +*/ +void tests_fib(void); + +/** + * @brief Generates tests for FIB + * + * @return embUnit tests if successful, NULL if not. + */ +Test *tests_fib_tests(void); + +#ifdef __cplusplus +} +#endif + +#endif /* TESTS_FIB_H_ */ +/** @} */