From 6c575821073812f96c46066ac1e7b7f08fd50429 Mon Sep 17 00:00:00 2001 From: BytesGalore Date: Wed, 5 Aug 2015 14:40:49 +0200 Subject: [PATCH] fib: initial source route support --- sys/include/net/fib.h | 265 +++- sys/include/net/fib/table.h | 55 +- sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c | 3 +- sys/net/network_layer/fib/fib.c | 1107 +++++++++++++++-- tests/unittests/tests-fib/tests-fib.c | 3 +- tests/unittests/tests-fib_sr/Makefile | 3 + tests/unittests/tests-fib_sr/Makefile.include | 3 + tests/unittests/tests-fib_sr/tests-fib_sr.c | 730 +++++++++++ tests/unittests/tests-fib_sr/tests-fib_sr.h | 43 + 9 files changed, 2111 insertions(+), 101 deletions(-) create mode 100644 tests/unittests/tests-fib_sr/Makefile create mode 100644 tests/unittests/tests-fib_sr/Makefile.include create mode 100644 tests/unittests/tests-fib_sr/tests-fib_sr.c create mode 100644 tests/unittests/tests-fib_sr/tests-fib_sr.h diff --git a/sys/include/net/fib.h b/sys/include/net/fib.h index 8ae1f707ea..abe714c0b4 100644 --- a/sys/include/net/fib.h +++ b/sys/include/net/fib.h @@ -15,6 +15,8 @@ * * @file * @brief Types and functions for FIB + * @note The used fib_table_t* used in all essential FIB functions + * MUST be valid. * * @author Martin Landsmann * @author Oliver Hahm @@ -41,7 +43,20 @@ typedef struct rp_address_msg_t { uint32_t address_flags; /**< The flags for the given address */ } rp_address_msg_t; -#define FIB_MSG_RP_SIGNAL (0x99) /**< message type for RP notifications */ +/** + * @brief message type for RP notification: unreachable destination + */ +#define FIB_MSG_RP_SIGNAL_UNREACHABLE_DESTINATION (0x99) + +/** + * @brief message type for RP notification: entry used + */ +#define FIB_MSG_RP_SIGNAL_DESTINATION_USED (0x98) + +/** + * @brief message type for RP notification: sr created + */ +#define FIB_MSG_RP_SIGNAL_SOURCE_ROUTE_CREATED (0x97) /** * @brief the size in bytes of a full address @@ -58,7 +73,7 @@ typedef struct fib_destination_set_entry_t { } fib_destination_set_entry_t; /** - * @brief indicator of a lifetime that does not expire (2^32 - 1) + * @brief indicator of a lifetime that does not expire (2^64 - 1) */ #define FIB_LIFETIME_NO_EXPIRE (0xFFFFFFFFffffffff) @@ -71,15 +86,13 @@ typedef struct fib_destination_set_entry_t { * @brief initializes all FIB entries with 0 * * @param[in] table the fib instance to initialize - * - * @pre @p table is not NULL and points to a pre-filled struct */ void fib_init(fib_table_t *table); /** - * @brief de-initializes the FIB entries + * @brief de-initializes the FIB entries and source route entries * - * @param[in] table the fib instance to de-initialize + * @param[in] table the fib instance to de-initialize */ void fib_deinit(fib_table_t *table); @@ -196,6 +209,238 @@ int fib_get_destination_set(fib_table_t *table, uint8_t *prefix, fib_destination_set_entry_t *dst_set, size_t *dst_set_size); +/** +* @brief creates a new source route +* +* @param[in, out] table the table the new source route belongs to +* @param[in, out] fib_sr pointer to store the new created source route pointer +* @param[in] sr_iface_id the interface ID used for the created source route +* @param[in] sr_flags the flags for the source route +* @param[in] sr_lifetime the lifetime in ms of the source route +* +* @return 0 on success +* -EFAULT on wrong parameters, i.e. fib_sr is NULL and/or sr_lifetime is 0 +* -ENOBUFS on insufficient memory, i.e. all source route fields are in use +*/ +int fib_sr_create(fib_table_t *table, fib_sr_t **fib_sr, kernel_pid_t sr_iface_id, + uint32_t sr_flags, uint32_t sr_lifetime); + +/** +* @brief reads the information from the sr head to the given locations +* +* @param[in] table the fib instance to access +* @param[in] fib_sr pointer to the sr to read from +* @param[in, out] sr_iface_id pointer to store the iface_id +* @param[in, out] sr_flags pointer to store the flags +* @param[in, out] sr_lifetime pointer to store the current left lifetime +* +* @return 0 on success +* -ENOENT on expired lifetime of the source route +* -EFAULT on fib_sr is NULL +*/ +int fib_sr_read_head(fib_table_t *table, fib_sr_t *fib_sr, kernel_pid_t *sr_iface_id, + uint32_t *sr_flags, uint32_t *sr_lifetime); + +/** +* @brief reads the destination address from the sr head to the given location +* +* @param[in] table the fib instance to access +* @param[in] fib_sr pointer to the sr to read from +* @param[in, out] dst pointer to the store destination address bytes +* @param[in, out] dst_size pointer to store the destination address size +* +* @return 0 on success +* -ENOENT on expired lifetime of the source route +* -EFAULT on one of the parameter pointers is NULL +* -ENOBUFS if the size to store the destination is insufficient low +* -EHOSTUNREACH on the destination address is not set +*/ +int fib_sr_read_destination(fib_table_t *table, fib_sr_t *fib_sr, + uint8_t *dst, size_t *dst_size); + +/** +* @brief sets the provided parameters in the given sr header if a given parameter +* is NULL its considered not to be changed +* +* @param[in] table the fib instance to access +* @param[in] fib_sr pointer to the sr to write to +* @param[in] sr_iface_id pointer to the new iface_id +* @param[in] sr_flags pointer to the new flags +* @param[in] sr_lifetime pointer to store the new lifetime +* +* @return 0 on success +* -ENOENT on expired lifetime of the source route +* -EFAULT on passed fib_sr is NULL +*/ +int fib_sr_set(fib_table_t *table, fib_sr_t *fib_sr, kernel_pid_t *sr_iface_id, + uint32_t *sr_flags, uint32_t *sr_lifetime); + +/** +* @brief deletes the sr +* +* @param[in, out] fib_sr pointer to the source route to be deleted +* +* @return 0 on success +* -EFAULT on fib_sr pointer is NULL +*/ +int fib_sr_delete(fib_table_t *table, fib_sr_t *fib_sr); + +/** +* @brief iterates to the next entry in the sr_path +* +* @param[in] table the fib instance to access +* @param[in] fib_sr pointer to the sr to iterate +* @param[in, out] sr_path_entry pointer to the entry for iterating to the next entry +* +* @return 0 on success +* 1 on success, end reached +* -ENOENT on expired lifetime of the source route +* -EFAULT on fib_sr and/or sr_path_entry is NULL +*/ +int fib_sr_next(fib_table_t *table, fib_sr_t *fib_sr, fib_sr_entry_t **sr_path_entry); + +/** +* @brief searches the entry containing the given address +* +* @param[in] table the fib instance to access +* @param[in] fib_sr pointer to the sr to search for a hop address +* @param[in] addr pointer to the searched address bytes +* @param[in] addr_size the size in bytes of the searched address type +* @param[out] sr_path_entry pointer to the found address entry +* +* @return 0 on success +* -EHOSTUNREACH when no address matches on the path +* -ENOENT on expired lifetime of the source route +* -EFAULT on one of the given parameter pointer is NULL +*/ +int fib_sr_search(fib_table_t *table, fib_sr_t *fib_sr, uint8_t *addr, size_t addr_size, + fib_sr_entry_t **sr_path_entry); + +/** +* @brief append a new entry at the end of the source route, i.e. a new destination +* +* @param[in] table the table with the source route to append the new entry +* @param[in] fib_sr pointer to the sr to append a hop address +* @param[in] dst pointer to the new destination address bytes +* @param[in] dst_size the size in bytes of the destination address type +* +* @return 0 on success +* -EINVAL on the given destination is already on the path in the source route +* -ENOENT on expired lifetime of the source route +* -EFAULT on fib_sr and/or dst is NULL +*/ +int fib_sr_entry_append(fib_table_t *table, fib_sr_t *fib_sr, + uint8_t *dst, size_t dst_size); + +/** +* @brief adds a new entry behind a given sr entry +* +* @param[in] table the table with the source route to add the new entry +* @param[in] fib_sr pointer to the sr to add a hop address +* @param[in] sr_path_entry pointer to the entry after which we add the new one +* @param[in] addr pointer to the new address bytes +* @param[in] addr_size the size in bytes of the address type +* @param[in] keep_remaining_route indicate if the remaining route after sr_path_entry +* should be kept and appended after the new entry +* +* @return 0 on success +* -EFAULT on fib_sr and/or sr_path_entry and/or addr is NULL +* -ENOENT on expired lifetime of the source route +* -EINVAL on the given address is already present in the path +*/ +int fib_sr_entry_add(fib_table_t *table, fib_sr_t *fib_sr, + fib_sr_entry_t *sr_path_entry, uint8_t *addr, size_t addr_size, + bool keep_remaining_route); + +/** +* @brief removes an entry from a source route +* +* @param[in] table the fib instance to access +* @param[in] fib_sr pointer to the sr to delete a hop address +* @param[in] addr pointer to the address bytes to delete +* @param[in] addr_size the size in bytes of the address type +* @param[in] keep_remaining_route indicate if the remaining route +* should be kept and appended after the predecessor of the removed entry +* +* @return 0 on success +* -EFAULT on one of the passed pointers is NULL +* -ENOENT on expired lifetime of the source route +*/ +int fib_sr_entry_delete(fib_table_t *table, fib_sr_t *fib_sr, uint8_t *addr, size_t addr_size, + bool keep_remaining_route); + +/** +* @brief overwrites the address of an entry with a new address +* +* @param[in] table the fib instance to access +* @param[in] fib_sr pointer to the sr to overwrite a hop address +* @param[in] addr_old pointer to the address bytes to overwrite +* @param[in] addr_old_size the size in bytes of the address type +* @param[in] addr_new pointer to the new address bytes +* @param[in] addr_new_size the size in bytes of the address type +* +* @return 0 on success +* -EINVAL on the given address is already present in the path +* -ENOMEM on no memory left to create a new address entry to overwrite the old one +* -EFAULT on one of the passed pointers is NULL +* -ENOENT on expired lifetime of the source route +*/ +int fib_sr_entry_overwrite(fib_table_t *table, fib_sr_t *fib_sr, + uint8_t *addr_old, size_t addr_old_size, + uint8_t *addr_new, size_t addr_new_size); + +/** +* @brief writes the address of an entry to the given out pointers +* +* @param[in] table the fib instance to access +* @param[in] fib_sr pointer to the sr to get a hop address +* @param[in] sr_path_entry pointer to the entry with the address +* @param[out] addr pointer to store the address bytes +* @param[in, out] addr_size the size in bytes of the address type +* +* @return 0 on success +* -ENOMEM on insufficient memory provided to store the address +* -EFAULT on one of the passed pointers is NULL +* -ENOENT on expired lifetime of the source route +*/ +int fib_sr_entry_get_address(fib_table_t *table, fib_sr_t *fib_sr, fib_sr_entry_t *sr_path_entry, + uint8_t *addr, size_t *addr_size); + + +/** +* @brief copies a source route to the given destination +* +* @param[in] table table to search for a source route +* @param[in] dst pointer to the destination address bytes +* @param[in] dst_size the size in bytes of the destination address type +* @param[out] sr_iface_id pointer to the store the iface_id for this route +* @param[in, out] sr_flags pointer to store the flags of this route +* @param[out] addr_list pointer to the location for storing the source route addresses +* @param[in, out] addr_list_elements the number of elements available in addr_list +* @param[in, out] element_size the provided size for one element in addr_list +* @param[in] reverse indicator if the hops should be stored in reverse order +* @param[in, out] fib_sr pointer for cosecutive receiving matching source routes. +* If NULL only the first matching source route is returned. +* If !NULL the pointer will be overwritten with the current returned fib_sr. +* The FIB skips all entries until the provided fib_sr+1. +* The fib_sr pointer is only overwritten when a further matching sr has been found. +* +* @note The actual needed size for an element and the number of elements +* is stored in addr_list_elements and element_size respectively +* when the return value is NOT -EFAULT or NOT -EHOSTUNREACH. +* However,the required size for may change in between calls. +* +* @return 0 on success, path to destination with equal flags +* 1 on success, path to destination with distinct flags +* -EFAULT on one of the provided parameter pointers is NULL +* -EHOSTUNREACH if no sr for the destination exists in the FIB +* -ENOBUFS if the size to store all hops is insufficient low +*/ +int fib_sr_get_route(fib_table_t *table, uint8_t *dst, size_t dst_size, kernel_pid_t *sr_iface_id, + uint32_t *sr_flags, + uint8_t *addr_list, size_t *addr_list_size, size_t *element_size, + bool reverse, fib_sr_t **fib_sr); + /** * @brief returns the actual number of used FIB entries * @@ -222,6 +467,14 @@ void fib_print_fib_table(fib_table_t *table); */ void fib_print_routes(fib_table_t *table); +/** +* @brief Prints the given FIB sourceroute +* +* @param[in] table the fib instance to print +* @param [in] sr the source route to print +*/ +void fib_print_sr(fib_table_t *table, fib_sr_t *sr); + #if FIB_DEVEL_HELPER /** * @brief get the point in time at which the entry for destination dst expires. diff --git a/sys/include/net/fib/table.h b/sys/include/net/fib/table.h index 0d590cd3f1..25992b5898 100644 --- a/sys/include/net/fib/table.h +++ b/sys/include/net/fib/table.h @@ -53,11 +53,64 @@ typedef struct fib_entry_t { struct universal_address_container_t *next_hop; } fib_entry_t; +/** +* @brief Container descriptor for a FIB source route entry +*/ +typedef struct fib_sr_entry_t { + /** Pointer to the shared generic address */ + struct universal_address_container_t *address; + /** Pointer to the next shared generic address on the source route */ + struct fib_sr_entry_t *next; +} fib_sr_entry_t; + +/** +* @brief Container descriptor for a FIB source route +*/ +typedef struct fib_sr_t { + /** interface ID */ + kernel_pid_t sr_iface_id; + /** Lifetime of this entry (an absolute time-point is stored by the FIB) */ + uint64_t sr_lifetime; + /** Flags for this source route */ + uint32_t sr_flags; + /** Pointer to the first hop on the source route */ + struct fib_sr_entry_t *sr_path; + /** Pointer to the destination of the source route */ + struct fib_sr_entry_t *sr_dest; +} fib_sr_t; + +/** +* @brief Container for one FIB source route table, +* combining source routes and an entry pool +*/ +typedef struct fib_sr_meta_t { + /** pointer to source route header array */ + fib_sr_t *headers; + /** pointer to entry pool array holding all hop entries for this table */ + fib_sr_entry_t *entry_pool; + /** the maximum number of elements in the entry pool */ + size_t entry_pool_size; +} fib_sr_meta_t; + +/** +* @breif FIB table type for single hop entries +*/ +#define FIB_TABLE_TYPE_SH (1) + +/** +* @breif FIB table type for source routes +*/ +#define FIB_TABLE_TYPE_SR (FIB_TABLE_TYPE_SH + 1) + /** * @brief Meta information about the FIB table */ typedef struct { - fib_entry_t *entries; /**< array holding the FIB entries */ + union{ + fib_entry_t *entries; /**< array holding the FIB entries */ + fib_sr_meta_t *source_routes; /**< array holding the FIB source routes */ + }data; + uint8_t table_type; /**< the table kind SR or single hop */ size_t size; /**< number of entries in this table */ mutex_t mtx_access; /** access mutex to control exclusive operations on calls */ size_t notify_rp_pos; /** registered RPs for notifications about unreachable destinations */ diff --git a/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c b/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c index bb4760100d..4297cfdd40 100644 --- a/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c +++ b/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c @@ -88,7 +88,8 @@ kernel_pid_t gnrc_ipv6_init(void) } #ifdef MODULE_FIB - gnrc_ipv6_fib_table.entries = _fib_entries; + gnrc_ipv6_fib_table.data.entries = _fib_entries; + gnrc_ipv6_fib_table.table_type = FIB_TABLE_TYPE_SH; gnrc_ipv6_fib_table.size = GNRC_IPV6_FIB_TABLE_SIZE; fib_init(&gnrc_ipv6_fib_table); #endif diff --git a/sys/net/network_layer/fib/fib.c b/sys/net/network_layer/fib/fib.c index bfad7d494d..d42253871c 100644 --- a/sys/net/network_layer/fib/fib.c +++ b/sys/net/network_layer/fib/fib.c @@ -28,6 +28,7 @@ #include "mutex.h" #include "msg.h" #include "xtimer.h" +#include "utlist.h" #define ENABLE_DEBUG (0) #include "debug.h" @@ -83,11 +84,11 @@ static int fib_find_entry(fib_table_t *table, uint8_t *dst, size_t dst_size, size_t count = 0; size_t prefix_size = 0; - size_t match_size = dst_size<<3; + size_t match_size = dst_size << 3; int ret = -EHOSTUNREACH; bool is_all_zeros_addr = true; - for(size_t i = 0; i < dst_size; ++i) { + for (size_t i = 0; i < dst_size; ++i) { if (dst[i] != 0) { is_all_zeros_addr = false; break; @@ -97,34 +98,34 @@ static int fib_find_entry(fib_table_t *table, uint8_t *dst, size_t dst_size, for (size_t i = 0; i < table->size; ++i) { /* autoinvalidate if the entry lifetime is not set to not expire */ - if (table->entries[i].lifetime != FIB_LIFETIME_NO_EXPIRE) { + if (table->data.entries[i].lifetime != FIB_LIFETIME_NO_EXPIRE) { /* check if the lifetime expired */ - if (table->entries[i].lifetime < now) { + if (table->data.entries[i].lifetime < now) { /* remove this entry if its lifetime expired */ - table->entries[i].lifetime = 0; - table->entries[i].global_flags = 0; - table->entries[i].next_hop_flags = 0; - table->entries[i].iface_id = KERNEL_PID_UNDEF; + table->data.entries[i].lifetime = 0; + table->data.entries[i].global_flags = 0; + table->data.entries[i].next_hop_flags = 0; + table->data.entries[i].iface_id = KERNEL_PID_UNDEF; - if (table->entries[i].global != NULL) { - universal_address_rem(table->entries[i].global); - table->entries[i].global = NULL; + if (table->data.entries[i].global != NULL) { + universal_address_rem(table->data.entries[i].global); + table->data.entries[i].global = NULL; } - if (table->entries[i].next_hop != NULL) { - universal_address_rem(table->entries[i].next_hop); - table->entries[i].next_hop = NULL; + if (table->data.entries[i].next_hop != NULL) { + universal_address_rem(table->data.entries[i].next_hop); + table->data.entries[i].next_hop = NULL; } } } - if ((prefix_size < (dst_size<<3)) && (table->entries[i].global != NULL)) { + if ((prefix_size < (dst_size<<3)) && (table->data.entries[i].global != NULL)) { - int ret_comp = universal_address_compare(table->entries[i].global, dst, &match_size); + int ret_comp = universal_address_compare(table->data.entries[i].global, dst, &match_size); /* If we found an exact match */ if (ret_comp == 0 || (is_all_zeros_addr && match_size == 0)) { - entry_arr[0] = &(table->entries[i]); + entry_arr[0] = &(table->data.entries[i]); *entry_arr_size = 1; /* we will not find a better one so we return */ return 1; @@ -132,12 +133,12 @@ static int fib_find_entry(fib_table_t *table, uint8_t *dst, size_t dst_size, else { /* we try to find the most fitting prefix */ if (ret_comp == 1) { - entry_arr[0] = &(table->entries[i]); + entry_arr[0] = &(table->data.entries[i]); /* we could find a better one so we move on */ ret = 0; prefix_size = match_size; - match_size = dst_size<<3; + match_size = dst_size << 3; count = 1; } } @@ -206,25 +207,25 @@ static int fib_create_entry(fib_table_t *table, kernel_pid_t iface_id, next_hop_flags, uint32_t lifetime) { for (size_t i = 0; i < table->size; ++i) { - if (table->entries[i].lifetime == 0) { + if (table->data.entries[i].lifetime == 0) { - table->entries[i].global = universal_address_add(dst, dst_size); + table->data.entries[i].global = universal_address_add(dst, dst_size); - if (table->entries[i].global != NULL) { - table->entries[i].global_flags = dst_flags; - table->entries[i].next_hop = universal_address_add(next_hop, next_hop_size); - table->entries[i].next_hop_flags = next_hop_flags; + if (table->data.entries[i].global != NULL) { + table->data.entries[i].global_flags = dst_flags; + table->data.entries[i].next_hop = universal_address_add(next_hop, next_hop_size); + table->data.entries[i].next_hop_flags = next_hop_flags; } - if (table->entries[i].next_hop != NULL) { + if (table->data.entries[i].next_hop != NULL) { /* everything worked fine */ - table->entries[i].iface_id = iface_id; + table->data.entries[i].iface_id = iface_id; if (lifetime != (uint32_t) FIB_LIFETIME_NO_EXPIRE) { - fib_lifetime_to_absolute(lifetime, &table->entries[i].lifetime); + fib_lifetime_to_absolute(lifetime, &table->data.entries[i].lifetime); } else { - table->entries[i].lifetime = FIB_LIFETIME_NO_EXPIRE; + table->data.entries[i].lifetime = FIB_LIFETIME_NO_EXPIRE; } return 0; @@ -266,27 +267,41 @@ static int fib_remove(fib_entry_t *entry) /** * @brief signals (sends a message to) all registered routing protocols * registered with a matching prefix (usually this should be only one). - * The message informs the recipient that no next-hop is available for the - * requested destination address. * 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 + * @param[in] table the fib instance to use + * @param[in] type the kind of signal + * @param[in] dat the data to send + * @param[in] dat_size the data size in bytes + * @param[in] dat_flags the data flags * * @return 0 on a new available entry, * -ENOENT if no suiting entry is provided. */ -static int fib_signal_rp(fib_table_t *table, uint8_t *dst, size_t dst_size, uint32_t dst_flags) +static int fib_signal_rp(fib_table_t *table, uint16_t type, uint8_t *dat, + size_t dat_size, uint32_t dat_flags) { msg_t msg, reply; - rp_address_msg_t content; - content.address = dst; - content.address_size = dst_size; - content.address_flags = dst_flags; + rp_address_msg_t rp_addr_msg; int ret = -ENOENT; + void *content = NULL; - msg.type = FIB_MSG_RP_SIGNAL; - msg.content.ptr = (void *)&content; + if (type != FIB_MSG_RP_SIGNAL_SOURCE_ROUTE_CREATED) { + /* the passed data is an address */ + rp_addr_msg.address = dat; + rp_addr_msg.address_size = dat_size; + rp_addr_msg.address_flags = dat_flags; + content = (void *)&rp_addr_msg; + } + else { + /* the passed data is a sr head + * dat_size and dat_flags are not used in this case + */ + content = (void *)dat; + } + + msg.type = type; + msg.content.ptr = content; for (size_t i = 0; i < FIB_MAX_REGISTERED_RP; ++i) { if (table->notify_rp[i] != KERNEL_PID_UNDEF) { @@ -294,14 +309,33 @@ static int fib_signal_rp(fib_table_t *table, uint8_t *dst, size_t dst_size, uint msg.content.ptr, (int)i, (int)(table->notify_rp[i])); /* do only signal a RP if its registered prefix matches */ - size_t dst_size_in_bits = dst_size<<3; - if (universal_address_compare(table->prefix_rp[i], dst, &dst_size_in_bits) == 1) { - /* the receiver, i.e. the RP, MUST copy the content value. - * using the provided pointer after replying this message - * will lead to errors - */ - msg_send_receive(&msg, &reply, table->notify_rp[i]); - DEBUG("[fib_signal_rp] got reply.\n"); + if (type != FIB_MSG_RP_SIGNAL_SOURCE_ROUTE_CREATED) { + size_t dat_size_in_bits = dat_size<<3; + if (universal_address_compare(table->prefix_rp[i], dat, + &dat_size_in_bits) == 1) { + /* the receiver, i.e. the RP, MUST copy the content value. + * using the provided pointer after replying this message + * will lead to errors + */ + msg_send_receive(&msg, &reply, table->notify_rp[i]); + DEBUG("[fib_signal_rp] got reply.\n"); + ret = 0; + } + } + else { + fib_sr_t *temp_sr = (fib_sr_t *)dat; + size_t dat_size_in_bits = temp_sr->sr_dest->address->address_size << 3; + if (universal_address_compare(table->prefix_rp[i], + temp_sr->sr_dest->address->address, + &dat_size_in_bits) == 1) { + /* the receiver, i.e. the RP, MUST copy the content value. + * using the provided pointer after replying this message + * will lead to errors + */ + msg_send_receive(&msg, &reply, table->notify_rp[i]); + DEBUG("[fib_signal_rp] got reply.\n"); + ret = 0; + } } } } @@ -420,7 +454,8 @@ int fib_get_next_hop(fib_table_t *table, kernel_pid_t *iface_id, int ret = fib_find_entry(table, dst, dst_size, &(entry[0]), &count); if (!(ret == 0 || ret == 1)) { /* notify all responsible RPs for unknown next-hop for the destination address */ - if (fib_signal_rp(table, dst, dst_size, dst_flags) == 0) { + if (fib_signal_rp(table, FIB_MSG_RP_SIGNAL_UNREACHABLE_DESTINATION, + dst, dst_size, dst_flags) == 0) { count = 1; /* now lets see if the RRPs have found a valid next-hop */ ret = fib_find_entry(table, dst, dst_size, &(entry[0]), &count); @@ -458,12 +493,12 @@ int fib_get_destination_set(fib_table_t *table, uint8_t *prefix, size_t found_entries = 0; for (size_t i = 0; i < table->size; ++i) { - if ((table->entries[i].global != NULL) && - (universal_address_compare_prefix(table->entries[i].global, prefix, prefix_size<<3) >= 0)) { + if ((table->data.entries[i].global != NULL) && + (universal_address_compare_prefix(table->data.entries[i].global, prefix, prefix_size<<3) >= 0)) { if( (dst_set != NULL) && (found_entries < *dst_set_size) ) { /* set the size to full byte usage */ dst_set[found_entries].dest_size = sizeof(dst_set[found_entries].dest); - universal_address_get_address(table->entries[i].global, + universal_address_get_address(table->data.entries[i].global, dst_set[found_entries].dest, &dst_set[found_entries].dest_size); } @@ -471,9 +506,10 @@ int fib_get_destination_set(fib_table_t *table, uint8_t *prefix, } } - if( found_entries > *dst_set_size ) { + if (found_entries > *dst_set_size) { ret = -ENOBUFS; - } else if( found_entries > 0 ) { + } + else if (found_entries > 0) { ret = 0; } @@ -497,8 +533,15 @@ void fib_init(fib_table_t *table) table->notify_rp_pos = 0; - memset(table->entries, 0, (table->size * sizeof(fib_entry_t))); - + if (table->table_type == FIB_TABLE_TYPE_SR) { + memset(table->data.source_routes->headers, 0, + sizeof(fib_sr_t) * table->size); + memset(table->data.source_routes->entry_pool, 0, + sizeof(fib_sr_entry_t) * table->data.source_routes->entry_pool_size); + } + else { + memset(table->data.entries, 0, (table->size * sizeof(fib_entry_t))); + } universal_address_init(); mutex_unlock(&(table->mtx_access)); } @@ -515,8 +558,15 @@ void fib_deinit(fib_table_t *table) table->notify_rp_pos = 0; - memset(table->entries, 0, (table->size * sizeof(fib_entry_t))); - + if (table->table_type == FIB_TABLE_TYPE_SR) { + memset(table->data.source_routes->headers, 0, + sizeof(fib_sr_t) * table->size); + memset(table->data.source_routes->entry_pool, 0, + sizeof(fib_sr_entry_t) * table->data.source_routes->entry_pool_size); + } + else { + memset(table->data.entries, 0, (table->size * sizeof(fib_entry_t))); + } universal_address_reset(); mutex_unlock(&(table->mtx_access)); } @@ -537,7 +587,8 @@ int fib_register_rp(fib_table_t *table, uint8_t *prefix, size_t prefix_addr_type if (table->notify_rp_pos < FIB_MAX_REGISTERED_RP) { table->notify_rp[table->notify_rp_pos] = sched_active_pid; - universal_address_container_t *container = universal_address_add(prefix, prefix_addr_type_size); + universal_address_container_t *container = universal_address_add(prefix, + prefix_addr_type_size); table->prefix_rp[table->notify_rp_pos] = container; table->notify_rp_pos++; } @@ -552,13 +603,811 @@ int fib_get_num_used_entries(fib_table_t *table) size_t used_entries = 0; for (size_t i = 0; i < table->size; ++i) { - used_entries += (size_t)(table->entries[i].global != NULL); + used_entries += (size_t)(table->data.entries[i].global != NULL); } mutex_unlock(&(table->mtx_access)); return used_entries; } +/* source route handling */ +int fib_sr_create(fib_table_t *table, fib_sr_t **fib_sr, kernel_pid_t sr_iface_id, + uint32_t sr_flags, uint32_t sr_lifetime) +{ + mutex_lock(&(table->mtx_access)); + if ((fib_sr == NULL) || (sr_lifetime == 0)) { + mutex_unlock(&(table->mtx_access)); + return -EFAULT; + } + + for (size_t i = 0; i < table->size; ++i) { + if (table->data.source_routes->headers[i].sr_lifetime == 0) { + table->data.source_routes->headers[i].sr_iface_id = sr_iface_id; + table->data.source_routes->headers[i].sr_flags = sr_flags; + table->data.source_routes->headers[i].sr_path = NULL; + table->data.source_routes->headers[i].sr_dest = NULL; + if (sr_lifetime < (uint32_t)FIB_LIFETIME_NO_EXPIRE) { + fib_lifetime_to_absolute(sr_lifetime, + &table->data.source_routes->headers[i].sr_lifetime); + } + else { + table->data.source_routes->headers[i].sr_lifetime = FIB_LIFETIME_NO_EXPIRE; + } + *fib_sr = &table->data.source_routes->headers[i]; + mutex_unlock(&(table->mtx_access)); + return 0; + } + } + + mutex_unlock(&(table->mtx_access)); + return -ENOBUFS; +} + +/** +* @brief Internal function: +* checks the lifetime and removes the entry in case it expired +*/ +static int fib_sr_check_lifetime(fib_sr_t *fib_sr, uint64_t *now) +{ + uint64_t tm = fib_sr->sr_lifetime - *now; + /* check if the lifetime expired */ + if ((int64_t)tm < 0) { + /* remove this sr if its lifetime expired */ + fib_sr->sr_lifetime = 0; + + if (fib_sr->sr_path != NULL) { + fib_sr_entry_t *elt; + LL_FOREACH(fib_sr->sr_path, elt) { + universal_address_rem(elt->address); + } + fib_sr->sr_path = NULL; + } + + /* and return an errorcode */ + return -ENOENT; + } + return 0; +} + +/** +* @brief Internal function: +* creates a new entry in the table entry pool for a hop in a source route +*/ +static int fib_sr_new_entry(fib_table_t *table, uint8_t *addr, size_t addr_size, + fib_sr_entry_t **new_entry) +{ + for (size_t i = 0; i < table->data.source_routes->entry_pool_size; ++i) { + if (table->data.source_routes->entry_pool[i].address == NULL) { + table->data.source_routes->entry_pool[i].address = universal_address_add(addr, addr_size); + if (table->data.source_routes->entry_pool[i].address == NULL) { + return -ENOMEM; + } + else { + (void)new_entry; + *new_entry = &table->data.source_routes->entry_pool[i]; + return 0; + } + } + } + return -ENOMEM; +} + +/** +* @brief Internal function: +* checks if the source route belongs to the given table +*/ +static int fib_is_sr_in_table(fib_table_t *table, fib_sr_t *fib_sr) +{ + for (size_t i = 0; i < table->size; ++i) { + if (&(table->data.source_routes->headers[i]) == fib_sr) { + return 0; + } + } + return -ENOENT; +} + +int fib_sr_read_head(fib_table_t *table, fib_sr_t *fib_sr, kernel_pid_t *iface_id, + uint32_t *sr_flags, uint32_t *sr_lifetime) +{ + uint64_t now = xtimer_now64(); + + mutex_lock(&(table->mtx_access)); + if ((fib_sr == NULL) || (iface_id == NULL) || (sr_flags == NULL) + || (sr_lifetime == NULL) || (fib_is_sr_in_table(table, fib_sr) == -ENOENT) ) { + mutex_unlock(&(table->mtx_access)); + return -EFAULT; + } + + if (fib_sr_check_lifetime(fib_sr, &now) == -ENOENT) { + mutex_unlock(&(table->mtx_access)); + return -ENOENT; + } + + *iface_id = fib_sr->sr_iface_id; + *sr_flags = fib_sr->sr_flags; + *sr_lifetime = fib_sr->sr_lifetime - now; + + mutex_unlock(&(table->mtx_access)); + return 0; +} + +int fib_sr_read_destination(fib_table_t *table, fib_sr_t *fib_sr, + uint8_t *dst, size_t *dst_size) +{ + uint64_t now = xtimer_now64(); + + mutex_lock(&(table->mtx_access)); + if ((fib_sr == NULL) || (dst == NULL) || (dst_size == NULL) + || (fib_is_sr_in_table(table, fib_sr) == -ENOENT)) { + mutex_unlock(&(table->mtx_access)); + return -EFAULT; + } + + if (fib_sr_check_lifetime(fib_sr, &now) == -ENOENT) { + mutex_unlock(&(table->mtx_access)); + return -ENOENT; + } + + if (fib_sr->sr_dest == NULL) { + mutex_unlock(&(table->mtx_access)); + return -EHOSTUNREACH; + } + + if (universal_address_get_address(fib_sr->sr_dest->address, dst, dst_size) == NULL) { + mutex_unlock(&(table->mtx_access)); + return -ENOBUFS; + } + + mutex_unlock(&(table->mtx_access)); + return 0; +} + +int fib_sr_set(fib_table_t *table, fib_sr_t *fib_sr, kernel_pid_t *sr_iface_id, + uint32_t *sr_flags, uint32_t *sr_lifetime) +{ + uint64_t now = xtimer_now64(); + + mutex_lock(&(table->mtx_access)); + if ((fib_sr == NULL) || (fib_is_sr_in_table(table, fib_sr) == -ENOENT)) { + mutex_unlock(&(table->mtx_access)); + return -EFAULT; + } + + if (fib_sr_check_lifetime(fib_sr, &now) == -ENOENT) { + mutex_unlock(&(table->mtx_access)); + return -ENOENT; + } + + if (sr_iface_id != NULL) { + fib_sr->sr_iface_id = *sr_iface_id; + } + + if (sr_flags != NULL) { + fib_sr->sr_flags = *sr_flags; + } + + if (sr_lifetime != NULL) { + fib_lifetime_to_absolute(*sr_lifetime, &(fib_sr->sr_lifetime)); + } + + mutex_unlock(&(table->mtx_access)); + return 0; +} + +int fib_sr_delete(fib_table_t *table, fib_sr_t *fib_sr) +{ + mutex_lock(&(table->mtx_access)); + if ((fib_sr == NULL) || (fib_is_sr_in_table(table, fib_sr) == -ENOENT)) { + mutex_unlock(&(table->mtx_access)); + return -EFAULT; + } + + fib_sr->sr_lifetime = 0; + + if (fib_sr->sr_path != NULL) { + fib_sr_entry_t *elt, *tmp; + LL_FOREACH_SAFE(fib_sr->sr_path, elt, tmp) { + universal_address_rem(elt->address); + elt->address = NULL; + LL_DELETE(fib_sr->sr_path, elt); + } + fib_sr->sr_path = NULL; + } + + mutex_unlock(&(table->mtx_access)); + return 0; +} + +int fib_sr_next(fib_table_t *table, fib_sr_t *fib_sr, fib_sr_entry_t **sr_path_entry) +{ + uint64_t now = xtimer_now64(); + + mutex_lock(&(table->mtx_access)); + if ((fib_sr == NULL) || (sr_path_entry == NULL) + || (fib_is_sr_in_table(table, fib_sr) == -ENOENT)) { + mutex_unlock(&(table->mtx_access)); + return -EFAULT; + } + + if (fib_sr->sr_path == NULL) { + mutex_unlock(&(table->mtx_access)); + return -EFAULT; + } + + if (fib_sr_check_lifetime(fib_sr, &now) == -ENOENT) { + mutex_unlock(&(table->mtx_access)); + return -ENOENT; + } + + /* if we reach the destination entry, i.e. the last entry we just return 1 */ + if (*sr_path_entry == fib_sr->sr_dest) { + mutex_unlock(&(table->mtx_access)); + return 1; + } + + /* when we start, we pass the first entry */ + if (*sr_path_entry == NULL) { + *sr_path_entry = fib_sr->sr_path; + } + else { + /* in any other case we just return the next entry */ + *sr_path_entry = (*sr_path_entry)->next; + } + + mutex_unlock(&(table->mtx_access)); + return 0; +} + +int fib_sr_search(fib_table_t *table, fib_sr_t *fib_sr, uint8_t *addr, size_t addr_size, + fib_sr_entry_t **sr_path_entry) +{ + uint64_t now = xtimer_now64(); + + mutex_lock(&(table->mtx_access)); + if ((fib_sr == NULL) || (addr == NULL) || (sr_path_entry == NULL) + || (fib_is_sr_in_table(table, fib_sr) == -ENOENT)) { + mutex_unlock(&(table->mtx_access)); + return -EFAULT; + } + + if (fib_sr_check_lifetime(fib_sr, &now) == -ENOENT) { + mutex_unlock(&(table->mtx_access)); + return -ENOENT; + } + + fib_sr_entry_t *elt; + LL_FOREACH(fib_sr->sr_path, elt) { + size_t addr_size_match = addr_size << 3; + if (universal_address_compare(elt->address, addr, &addr_size_match) == 0) { + + /* temporary workaround to calm compiler */ + (void)sr_path_entry; + + *sr_path_entry = elt; + mutex_unlock(&(table->mtx_access)); + return 0; + } + } + + mutex_unlock(&(table->mtx_access)); + return -EHOSTUNREACH; +} + +int fib_sr_entry_append(fib_table_t *table, fib_sr_t *fib_sr, + uint8_t *addr, size_t addr_size) +{ + uint64_t now = xtimer_now64(); + + mutex_lock(&(table->mtx_access)); + if ((fib_sr == NULL) || (addr == NULL) + || (fib_is_sr_in_table(table, fib_sr) == -ENOENT)) { + mutex_unlock(&(table->mtx_access)); + return -EFAULT; + } + + if (fib_sr_check_lifetime(fib_sr, &now) == -ENOENT) { + mutex_unlock(&(table->mtx_access)); + return -ENOENT; + } + + fib_sr_entry_t *elt; + LL_FOREACH(fib_sr->sr_path, elt) { + size_t addr_size_match = addr_size << 3; + if (universal_address_compare(elt->address, addr, &addr_size_match) == 0) { + mutex_unlock(&(table->mtx_access)); + return -EINVAL; + } + } + + fib_sr_entry_t *new_entry[1]; + int ret = fib_sr_new_entry(table, addr, addr_size, &new_entry[0]); + + if (ret == 0) { + fib_sr_entry_t *tmp = fib_sr->sr_dest; + if (tmp != NULL) { + /* we append the new entry behind the former destination */ + tmp->next = new_entry[0]; + } + else { + /* this is also our first entry */ + fib_sr->sr_path = new_entry[0]; + } + fib_sr->sr_dest = new_entry[0]; + } + + mutex_unlock(&(table->mtx_access)); + return ret; +} + +int fib_sr_entry_add(fib_table_t *table, fib_sr_t *fib_sr, + fib_sr_entry_t *sr_path_entry, uint8_t *addr, size_t addr_size, + bool keep_remaining_route) +{ + uint64_t now = xtimer_now64(); + + mutex_lock(&(table->mtx_access)); + if ((fib_sr == NULL) || (sr_path_entry == NULL) || (addr == NULL) + || (fib_is_sr_in_table(table, fib_sr) == -ENOENT)) { + mutex_unlock(&(table->mtx_access)); + return -EFAULT; + } + + if (fib_sr_check_lifetime(fib_sr, &now) == -ENOENT) { + mutex_unlock(&(table->mtx_access)); + return -ENOENT; + } + + bool found = false; + fib_sr_entry_t *elt; + LL_FOREACH(fib_sr->sr_path, elt) { + size_t addr_size_match = addr_size << 3; + if (universal_address_compare(elt->address, addr, &addr_size_match) == 0) { + mutex_unlock(&(table->mtx_access)); + return -EINVAL; + } + if (sr_path_entry == elt) { + found = true; + break; + } + } + + int ret = -ENOENT; + if (found) { + fib_sr_entry_t *new_entry[1]; + ret = fib_sr_new_entry(table, addr, addr_size, &new_entry[0]); + if (ret == 0) { + fib_sr_entry_t *remaining = sr_path_entry->next; + sr_path_entry->next = new_entry[0]; + if (keep_remaining_route) { + new_entry[0]->next = remaining; + } + else { + fib_sr_entry_t *elt, *tmp; + LL_FOREACH_SAFE(remaining, elt, tmp) { + universal_address_rem(elt->address); + elt->address = NULL; + LL_DELETE(remaining, elt); + } + new_entry[0]->next = NULL; + fib_sr->sr_dest = new_entry[0]; + } + } + } + + mutex_unlock(&(table->mtx_access)); + return ret; +} + +int fib_sr_entry_delete(fib_table_t *table, fib_sr_t *fib_sr, uint8_t *addr, size_t addr_size, + bool keep_remaining_route) +{ + uint64_t now = xtimer_now64(); + + mutex_lock(&(table->mtx_access)); + if ((fib_sr == NULL) || (fib_is_sr_in_table(table, fib_sr) == -ENOENT)) { + mutex_unlock(&(table->mtx_access)); + return -EFAULT; + } + + if (fib_sr_check_lifetime(fib_sr, &now) == -ENOENT) { + mutex_unlock(&(table->mtx_access)); + return -ENOENT; + } + + fib_sr_entry_t *elt, *tmp; + tmp = fib_sr->sr_path; + LL_FOREACH(fib_sr->sr_path, elt) { + size_t addr_size_match = addr_size << 3; + + if (universal_address_compare(elt->address, addr, &addr_size_match) == 0) { + universal_address_rem(elt->address); + if (keep_remaining_route) { + tmp->next = elt->next; + } + else { + fib_sr_entry_t *elt_del, *tmp_del; + LL_FOREACH_SAFE(tmp, elt_del, tmp_del) { + universal_address_rem(elt_del->address); + elt_del->address = NULL; + LL_DELETE(tmp, elt_del); + } + } + if (elt == fib_sr->sr_path) { + /* if we remove the first entry we must adjust the path start */ + fib_sr->sr_path = elt->next; + } + if (elt == fib_sr->sr_dest) { + /* if we remove the last entry we must adjust the destination */ + fib_sr->sr_dest = tmp; + } + mutex_unlock(&(table->mtx_access)); + return 0; + } + tmp = elt; + } + + return -ENOENT; +} + +int fib_sr_entry_overwrite(fib_table_t *table, fib_sr_t *fib_sr, + uint8_t *addr_old, size_t addr_old_size, + uint8_t *addr_new, size_t addr_new_size) +{ + uint64_t now = xtimer_now64(); + + mutex_lock(&(table->mtx_access)); + if ((fib_sr == NULL) || (addr_old == NULL) || (addr_new == NULL) + || (fib_is_sr_in_table(table, fib_sr) == -ENOENT)) { + mutex_unlock(&(table->mtx_access)); + return -EFAULT; + } + + if (fib_sr_check_lifetime(fib_sr, &now) == -ENOENT) { + mutex_unlock(&(table->mtx_access)); + return -ENOENT; + } + + fib_sr_entry_t *elt, *elt_repl; + elt_repl = NULL; + LL_FOREACH(fib_sr->sr_path, elt) { + size_t addr_old_size_match = addr_old_size << 3; + size_t addr_new_size_match = addr_old_size << 3; + if (universal_address_compare(elt->address, addr_old, &addr_old_size_match) == 0) { + elt_repl = elt; + } + + if (universal_address_compare(elt->address, addr_new, &addr_new_size_match) == 0) { + mutex_unlock(&(table->mtx_access)); + return -EINVAL; + } + } + + if (elt_repl != NULL) { + universal_address_rem(elt_repl->address); + universal_address_container_t *add = universal_address_add(addr_new, addr_new_size); + + if (add == NULL) { + /* if this happened we deleted one entry, i.e. decreased the usecount + * adding a new one was not possible since lack of memory + * so we add back the old entry, i.e. increasing the usecount + */ + universal_address_add(addr_old, addr_old_size); + mutex_unlock(&(table->mtx_access)); + return -ENOMEM; + } + elt_repl->address = add; + } + + mutex_unlock(&(table->mtx_access)); + return 0; +} + +int fib_sr_entry_get_address(fib_table_t *table, fib_sr_t *fib_sr, fib_sr_entry_t *sr_entry, + uint8_t *addr, size_t *addr_size) +{ + uint64_t now = xtimer_now64(); + + mutex_lock(&(table->mtx_access)); + if ((fib_sr == NULL) || (fib_is_sr_in_table(table, fib_sr) == -ENOENT)) { + mutex_unlock(&(table->mtx_access)); + return -EFAULT; + } + + if (fib_sr_check_lifetime(fib_sr, &now) == -ENOENT) { + mutex_unlock(&(table->mtx_access)); + return -ENOENT; + } + + fib_sr_entry_t *elt; + LL_FOREACH(fib_sr->sr_path, elt) { + if (elt == sr_entry) { + if (universal_address_get_address(elt->address, addr, addr_size) != NULL) { + mutex_unlock(&(table->mtx_access)); + return 0; + } + else { + mutex_unlock(&(table->mtx_access)); + return -ENOMEM; + } + } + } + mutex_unlock(&(table->mtx_access)); + return -ENOENT; +} + +/** + * @brief helper function to search a partial path to a given destination, + * and iff successful to create a new source route + * + * @param[in] table the fib table the entry should be added to + * @param[in] dst pointer to the destination address bytes + * @param[in] dst_size the size in bytes of the destination address type + * @param[in] check_free_entry position to start the search for a free entry + * @param[out] error the state of of this operation when finished + * + * @return pointer to the new source route on success + * NULL otherwise +*/ +static fib_sr_t* _fib_create_sr_from_partial(fib_table_t *table, uint8_t *dst, size_t dst_size, + int check_free_entry, int *error) { +fib_sr_t* hit = NULL; + + for (size_t i = 0; i < table->size; ++i) { + if (table->data.source_routes->headers[i].sr_lifetime != 0) { + + fib_sr_entry_t *elt; + LL_FOREACH(table->data.source_routes->headers[i].sr_path, elt) { + size_t addr_size_match = dst_size << 3; + if (universal_address_compare(elt->address, dst, &addr_size_match) == 0) { + /* we create a new sr */ + if (check_free_entry == -1) { + /* we have no room to create a new sr + * so we just retrun and NOT tell the RPs to find a route + * since we cannot save it + */ + *error = -ENOBUFS; + return NULL; + } + else { + /* we check if there is a free place for the new sr */ + fib_sr_t *new_sr = NULL; + for (size_t j = check_free_entry; j < table->size; ++j) { + if (table->data.source_routes->headers[j].sr_lifetime != 0) { + /* not this one, maybe the next one */ + continue; + } + else { + + /* there it is, so we copy the header */ + new_sr = &table->data.source_routes->headers[j]; + new_sr->sr_iface_id = table->data.source_routes->headers[i].sr_iface_id; + new_sr->sr_flags = table->data.source_routes->headers[i].sr_flags; + new_sr->sr_lifetime = table->data.source_routes->headers[i].sr_lifetime; + new_sr->sr_path = NULL; + + /* and the path until the searched destination */ + fib_sr_entry_t *elt_iter, *elt_add = NULL; + + LL_FOREACH(table->data.source_routes->headers[i].sr_path, elt_iter) { + fib_sr_entry_t *new_entry; + + if (fib_sr_new_entry(table, elt_iter->address->address, + elt_iter->address->address_size, + &new_entry) != 0) { + /* we could not create a new entry + * so we return to clean up the partial route + */ + *error = -ENOBUFS; + return new_sr; + } + + if (new_sr->sr_path == NULL) { + new_sr->sr_path = new_entry; + elt_add = new_sr->sr_path; + } + else { + elt_add->next = new_entry; + elt_add = elt_add->next; + } + + if (elt_iter == elt) { + /* we copied until the destination */ + new_sr->sr_dest = new_entry; + hit = new_sr; + + /* tell the RPs that a new sr has been created + * the size and the flags parameters are ignored + */ + if (fib_signal_rp(table, FIB_MSG_RP_SIGNAL_SOURCE_ROUTE_CREATED, + (uint8_t *)new_sr, 0, 0) != 0) { + /* if no RP can handle the source route + * then the host is not directly reachable + */ + *error = -EHOSTUNREACH; + } + + /* break from iterating for copy */ + break; + } + } + } + } + + /* break from iterating the found path */ + break; + } + } + } + if (hit != NULL) { + /* break iterating all sr since we have a path now */ + break; + } + } + } + return hit; +} + +int fib_sr_get_route(fib_table_t *table, uint8_t *dst, size_t dst_size, kernel_pid_t *sr_iface_id, + uint32_t *sr_flags, + uint8_t *addr_list, size_t *addr_list_elements, size_t *element_size, + bool reverse, fib_sr_t **fib_sr) +{ + uint64_t now = xtimer_now64(); + + mutex_lock(&(table->mtx_access)); + + if ((dst == NULL) || (sr_iface_id == NULL) || (sr_flags == NULL) + || (addr_list == NULL) || (addr_list_elements == NULL) || (element_size == NULL)) { + mutex_unlock(&(table->mtx_access)); + return -EFAULT; + } + + fib_sr_t *hit = NULL; + fib_sr_t *tmp_hit = NULL; + int check_free_entry = -1; + + bool skip = (fib_sr != NULL) && (*fib_sr != NULL)?true:false; + /* Case 1 - check if we know a direct route */ + for (size_t i = 0; i < table->size; ++i) { + + if (fib_sr_check_lifetime(&table->data.source_routes->headers[i], &now) == -ENOENT) { + /* expired, so skip this sr and remember its position */ + if (check_free_entry == -1) { + /* we want to fill up the source routes from the beginning */ + check_free_entry = i; + } + continue; + } + + if( skip ) { + if((*fib_sr == &table->data.source_routes->headers[i])) { + skip = false; + } + /* we skip all entries upon the consecutive one to start search */ + continue; + } + + size_t addr_size_match = dst_size << 3; + if (universal_address_compare(table->data.source_routes->headers[i].sr_dest->address, + dst, &addr_size_match) == 0) { + if (*sr_flags == table->data.source_routes->headers[i].sr_flags) { + /* found a perfect matching sr, no need to search further */ + hit = &table->data.source_routes->headers[i]; + tmp_hit = NULL; + if (check_free_entry == -1) { + check_free_entry = i; + } + break; + } + else { + /* found a sr to the destination but with different flags, + * maybe we find a better one. + */ + tmp_hit = &table->data.source_routes->headers[i]; + } + } + } + + if (hit == NULL) { + /* we didn't find a perfect sr, but one with distinct flags */ + hit = tmp_hit; + } + + /* Case 2 - if no hit is found check if there is a matching entry in one sr_path + * @note the first match wins, if we find one we will NOT continue searching, + * since this search is very expensive in terms of compare operations + */ + if (hit == NULL) { + int error = 0; + hit = _fib_create_sr_from_partial(table, dst, dst_size, check_free_entry, &error); + if ((error != 0) && (error != -EHOSTUNREACH)) { + /* something went wrong, so we clean up our mess + * + * @note we could handle -EHOSTUNREACH differently here, + * since it says that we have a partial source route but no RP + * to manage it. + * Thats why I let it pass for now. + */ + if (hit != NULL) { + hit->sr_lifetime = 0; + + if (hit->sr_path != NULL) { + fib_sr_entry_t *elt, *tmp; + LL_FOREACH_SAFE(hit->sr_path, elt, tmp) { + universal_address_rem(elt->address); + elt->address = NULL; + LL_DELETE(hit->sr_path, elt); + } + hit->sr_path = NULL; + } + } + mutex_unlock(&(table->mtx_access)); + return error; + } + } + + /* Final step - copy the list in the desired order */ + if (hit != NULL) { + + /* store the current hit to enable consecutive searches */ + if( fib_sr != NULL ) { + *fib_sr = hit; + } + + /* check the list size and if the sr entries will fit */ + int count; + fib_sr_entry_t *elt = NULL; + LL_COUNT(hit->sr_path, elt, count); + + if (((size_t)count > *addr_list_elements) + || (sizeof(hit->sr_path->address->address) > *element_size)) { + *addr_list_elements = count; + *element_size = sizeof(hit->sr_path->address->address); + mutex_unlock(&(table->mtx_access)); + return -ENOBUFS; + } + + /* start copy the individual entries in the desired order */ + uint8_t *next_entry = addr_list; + int one_address_size = *element_size; + + if (reverse) { + /* we move to the last list element */ + next_entry += (count - 1) * sizeof(hit->sr_path->address->address); + /* and set the storing direction during the iteration */ + one_address_size *= -1; + } + + elt = NULL; + LL_FOREACH(hit->sr_path, elt) { + size_t tmp_size = sizeof(hit->sr_path->address->address); + universal_address_get_address(elt->address, next_entry, &tmp_size); + next_entry += one_address_size; + } + *sr_iface_id = hit->sr_iface_id; + *sr_flags = hit->sr_flags; + *addr_list_elements = count; + *element_size = sizeof(hit->sr_path->address->address); + } + else { + + /* trigger RPs for route discovery */ + fib_signal_rp(table, FIB_MSG_RP_SIGNAL_UNREACHABLE_DESTINATION, dst, dst_size, *sr_flags); + + mutex_unlock(&(table->mtx_access)); + return -EHOSTUNREACH; + } + + mutex_unlock(&(table->mtx_access)); + + if (tmp_hit == NULL) { + return 0; + } + else { + return 1; + } +} + /* print functions */ void fib_print_notify_rp(fib_table_t *table) @@ -578,15 +1427,43 @@ void fib_print_fib_table(fib_table_t *table) for (size_t i = 0; i < table->size; ++i) { printf("[fib_print_table] %d) iface_id: %d, global: %p, next hop: %p, lifetime: %"PRIu32"\n", - (int)i, (int)table->entries[i].iface_id, - (void *)table->entries[i].global, - (void *)table->entries[i].next_hop, - (uint32_t)(table->entries[i].lifetime / 1000)); + (int)i, (int)table->data.entries[i].iface_id, + (void *)table->data.entries[i].global, + (void *)table->data.entries[i].next_hop, + (uint32_t)(table->data.entries[i].lifetime / 1000)); } mutex_unlock(&(table->mtx_access)); } +void fib_print_sr(fib_table_t *table, fib_sr_t *sr) +{ + /* does not adjust the lifetime */ + mutex_lock(&(table->mtx_access)); + if ((sr == NULL) || (fib_is_sr_in_table(table, sr) == -ENOENT)) { + mutex_unlock(&(table->mtx_access)); + return; + } + + printf("\n-= Source route (%p) =-\nIface: %d\nflags: %x\npath: %p\ndest: ", + (void *)sr, sr->sr_iface_id, (unsigned int)sr->sr_flags, (void *)sr->sr_path); + + if (sr->sr_dest != NULL) { + universal_address_print_entry(sr->sr_dest->address); + } else { + puts("Not set."); + } + + fib_sr_entry_t *nxt = sr->sr_path; + while (nxt) { + universal_address_print_entry(nxt->address); + nxt = nxt->next; + } + printf("-= END (%p) =-\n", (void *)sr); + + mutex_unlock(&(table->mtx_access)); +} + static void fib_print_address(universal_address_container_t *entry) { uint8_t address[UNIVERSAL_ADDRESS_SIZE]; @@ -621,38 +1498,69 @@ static void fib_print_address(universal_address_container_t *entry) void fib_print_routes(fib_table_t *table) { mutex_lock(&(table->mtx_access)); - printf("%-" FIB_ADDR_PRINT_LENS "s %-6s %-" FIB_ADDR_PRINT_LENS "s %-6s %-16s Interface\n" - , "Destination", "Flags", "Next Hop", "Flags", "Expires"); - uint64_t now = xtimer_now64(); + if (table->table_type == FIB_TABLE_TYPE_SH) { + printf("%-" FIB_ADDR_PRINT_LENS "s %-6s %-" FIB_ADDR_PRINT_LENS "s %-6s %-16s Interface\n" + , "Destination", "Flags", "Next Hop", "Flags", "Expires"); - for (size_t i = 0; i < table->size; ++i) { - if (table->entries[i].lifetime != 0) { - fib_print_address(table->entries[i].global); - printf(" 0x%04"PRIx32" ", table->entries[i].global_flags); - fib_print_address(table->entries[i].next_hop); - printf(" 0x%04"PRIx32" ", table->entries[i].next_hop_flags); + for (size_t i = 0; i < table->size; ++i) { + if (table->data.entries[i].lifetime != 0) { + fib_print_address(table->data.entries[i].global); + printf(" 0x%04"PRIx32" ", table->data.entries[i].global_flags); + fib_print_address(table->data.entries[i].next_hop); + printf(" 0x%04"PRIx32" ", table->data.entries[i].next_hop_flags); - if (table->entries[i].lifetime != FIB_LIFETIME_NO_EXPIRE) { + if (table->data.entries[i].lifetime != FIB_LIFETIME_NO_EXPIRE) { - uint64_t tm = table->entries[i].lifetime - now; + uint64_t tm = table->data.entries[i].lifetime - now; - /* we must interpret the values as signed */ - if ((int64_t)tm < 0 ) { - printf("%-16s ", "EXPIRED"); + /* we must interpret the values as signed */ + if ((int64_t)tm < 0 ) { + printf("%-16s ", "EXPIRED"); + } + else { + printf("%"PRIu32".%05"PRIu32, (uint32_t)(tm / 1000000), + (uint32_t)(tm % 1000000)); + } } else { - printf("%"PRIu32".%05"PRIu32, (uint32_t)(tm / 1000000), (uint32_t)(tm % 1000000)); + printf("%-16s ", "NEVER"); } - } - else { - printf("%-16s ", "NEVER"); - } - printf("%d\n", (int)table->entries[i].iface_id); + printf("%d\n", (int)table->data.entries[i].iface_id); + } } } + else if (table->table_type == FIB_TABLE_TYPE_SR) { + printf("%-" FIB_ADDR_PRINT_LENS "s %-" FIB_ADDR_PRINT_LENS "s %-6s %-16s Interface\n" + , "SR Destination", "SR First Hop", "SR Flags", "Expires"); + for (size_t i = 0; i < table->size; ++i) { + if (table->data.source_routes->headers[i].sr_lifetime != 0) { + fib_print_address(table->data.source_routes->headers[i].sr_dest->address); + fib_print_address(table->data.source_routes->headers[i].sr_path->address); + printf(" 0x%04"PRIx32" ", table->data.source_routes->headers[i].sr_flags); + if (table->data.source_routes->headers[i].sr_lifetime != FIB_LIFETIME_NO_EXPIRE) { + + uint64_t tm = table->data.source_routes->headers[i].sr_lifetime - now; + + /* we must interpret the values as signed */ + if ((int64_t)tm < 0 ) { + printf("%-16s ", "EXPIRED"); + } + else { + printf("%"PRIu32".%05"PRIu32, (uint32_t)(tm / 1000000), + (uint32_t)(tm % 1000000)); + } + } + else { + printf("%-16s ", "NEVER"); + } + + printf("%d\n", (int)table->data.source_routes->headers[i].sr_iface_id); + } + } + } mutex_unlock(&(table->mtx_access)); } @@ -660,15 +1568,30 @@ void fib_print_routes(fib_table_t *table) int fib_devel_get_lifetime(fib_table_t *table, uint64_t *lifetime, uint8_t *dst, size_t dst_size) { - size_t count = 1; - fib_entry_t *entry[count]; + if (table->table_type == FIB_TABLE_TYPE_SH) { + size_t count = 1; + fib_entry_t *entry[count]; - int ret = fib_find_entry(table, dst, dst_size, &(entry[0]), &count); - if (ret == 1 ) { - /* only return lifetime of exact matches */ - *lifetime = entry[0]->lifetime; - return 0; + int ret = fib_find_entry(table, dst, dst_size, &(entry[0]), &count); + if (ret == 1 ) { + /* only return lifetime of exact matches */ + *lifetime = entry[0]->lifetime; + return 0; + } + return -EHOSTUNREACH; } - return -EHOSTUNREACH; + else if (table->table_type == FIB_TABLE_TYPE_SR) { + size_t addr_size_match = dst_size << 3; + /* first hit wins here */ + for (size_t i = 0; i < table->size; ++i) { + if (universal_address_compare(table->data.source_routes->headers[i].sr_dest->address, + dst, &addr_size_match) == 0) { + *lifetime = table->data.source_routes->headers[i].sr_lifetime; + return 0; + } + } + return -EHOSTUNREACH; + } + return -EFAULT; } #endif diff --git a/tests/unittests/tests-fib/tests-fib.c b/tests/unittests/tests-fib/tests-fib.c index a3e804d39b..a9bd191de0 100644 --- a/tests/unittests/tests-fib/tests-fib.c +++ b/tests/unittests/tests-fib/tests-fib.c @@ -21,7 +21,8 @@ #define TEST_FIB_TABLE_SIZE (20) static fib_entry_t _entries[TEST_FIB_TABLE_SIZE]; -static fib_table_t test_fib_table = { .entries = _entries, +static fib_table_t test_fib_table = { .data.entries = _entries, + .table_type = FIB_TABLE_TYPE_SH, .size = TEST_FIB_TABLE_SIZE, .mtx_access = MUTEX_INIT, .notify_rp_pos = 0 }; diff --git a/tests/unittests/tests-fib_sr/Makefile b/tests/unittests/tests-fib_sr/Makefile new file mode 100644 index 0000000000..843e1d955d --- /dev/null +++ b/tests/unittests/tests-fib_sr/Makefile @@ -0,0 +1,3 @@ +MODULE = tests-fib_sr + +include $(RIOTBASE)/Makefile.base diff --git a/tests/unittests/tests-fib_sr/Makefile.include b/tests/unittests/tests-fib_sr/Makefile.include new file mode 100644 index 0000000000..75e2535a55 --- /dev/null +++ b/tests/unittests/tests-fib_sr/Makefile.include @@ -0,0 +1,3 @@ +CFLAGS += -DFIB_DEVEL_HELPER -DUNIVERSAL_ADDRESS_SIZE=16 -DUNIVERSAL_ADDRESS_MAX_ENTRIES=40 + +USEMODULE += fib diff --git a/tests/unittests/tests-fib_sr/tests-fib_sr.c b/tests/unittests/tests-fib_sr/tests-fib_sr.c new file mode 100644 index 0000000000..155f4dcd02 --- /dev/null +++ b/tests/unittests/tests-fib_sr/tests-fib_sr.c @@ -0,0 +1,730 @@ +/* +* 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. +*/ + +#include /**< required for snprintf() */ +#include +#include +#include "embUnit.h" +#include "tests-fib_sr.h" + +#include "thread.h" +#include "net/fib.h" +#include "universal_address.h" + +/** + * @brief maximum number of source routes + */ +#define TEST_MAX_FIB_SR (20) + +/** + * @brief maximum number of entries in all source routes + */ +#define TEST_MAX_FIB_SR_ENTRIES (TEST_MAX_FIB_SR * 20) + +/** + * @brief the FIB source route headers + */ +static fib_sr_t _sr_headers[TEST_MAX_FIB_SR]; + +/** + * @brief the FIB source route table entries pool + */ +static fib_sr_entry_t _sr_datapool[TEST_MAX_FIB_SR_ENTRIES]; + +/** + * @brief the FIB source route table "glue container" + */ +static fib_sr_meta_t _entries_sr = { .headers = _sr_headers, + .entry_pool = _sr_datapool, + .entry_pool_size = TEST_MAX_FIB_SR_ENTRIES}; + +/** + * @brief the FIB source route table + * @note is initialized in the entry function of this unittest + */ +static fib_table_t test_fib_sr_table; + +/* +* @brief create a new empty source route and check the parameters +* It is expected to have a new source route with the given parameters +*/ +static void test_fib_sr_01_create_empty_sr(void) +{ + fib_sr_t *local_sourceroutes[1]; + + TEST_ASSERT_EQUAL_INT(0, fib_sr_create(&test_fib_sr_table, &local_sourceroutes[0], + 42, 0x0, 10000)); + + kernel_pid_t sr_iface_id; + uint32_t sr_flags; + uint32_t sr_lifetime; + TEST_ASSERT_EQUAL_INT(0, fib_sr_read_head(&test_fib_sr_table, local_sourceroutes[0], + &sr_iface_id, &sr_flags, + &sr_lifetime) + ); + + TEST_ASSERT_EQUAL_INT(42, sr_iface_id); + TEST_ASSERT_EQUAL_INT(0x0, sr_flags); + + /* the lifetime should decrease + * Since the FIB uses timex under the hood which has a finer grained resolution + * testing the lifetime requires to active wait a bit, say 5 ms. + * Uncomment the following two lines if you want to test the temporal behaviour + * @note this may fail since unittests are currently not thread-friendly + */ + //xtimer_usleep(5 * MS_IN_USEC); + //TEST_ASSERT(sr_lifetime<10000); + + fib_deinit(&test_fib_sr_table); +} + +/* +* @brief create a new empty source route, change its parameters +* and check the change +* It is expected to have a new source route with the given adjusted parameters +*/ +static void test_fib_sr_02_change_sr_parameters(void) +{ + fib_sr_t *local_sourceroutes[1]; + + TEST_ASSERT_EQUAL_INT(0, fib_sr_create(&test_fib_sr_table, &local_sourceroutes[0], + 42, 0x0, 10000)); + + /* Case 01 - re-set all parameters */ + kernel_pid_t sr_iface_id = 43; + uint32_t sr_flags = 0x99; + uint32_t sr_lifetime = 20000; + TEST_ASSERT_EQUAL_INT(0, fib_sr_set(&test_fib_sr_table, local_sourceroutes[0], + &sr_iface_id, &sr_flags, &sr_lifetime)); + TEST_ASSERT_EQUAL_INT(0, fib_sr_read_head(&test_fib_sr_table, local_sourceroutes[0], + &sr_iface_id, &sr_flags, + &sr_lifetime) + ); + + TEST_ASSERT_EQUAL_INT(43, sr_iface_id); + TEST_ASSERT_EQUAL_INT(0x99, sr_flags); + + /* the lifetime should be slightly below 20000 + * Since the FIB uses timex under the hood which has a finer grained resolution + * testing the lifetime requires to active wait a bit, say 5 ms. + * Uncomment the following three lines if you want to test the temporal behaviour + * @note this may fail since unittests are currently not thread-friendly + */ + //xtimer_usleep(5 * MS_IN_USEC); + //TEST_ASSERT(sr_lifetime>10000); + //TEST_ASSERT(sr_lifetime<20000); + + fib_deinit(&test_fib_sr_table); +} + +/* +* @brief check errors on reading sr parameters and trying to access an expired sr +*/ +static void test_fib_sr_03_read_sr_parameters(void) +{ + fib_sr_t *local_sourceroutes[1]; + + TEST_ASSERT_EQUAL_INT(0, fib_sr_create(&test_fib_sr_table, &local_sourceroutes[0], + 42, 0x0, 10000)); + + kernel_pid_t sr_iface_id; + uint32_t sr_flags; + uint32_t sr_lifetime; + TEST_ASSERT_EQUAL_INT(-EFAULT, fib_sr_read_head(&test_fib_sr_table, NULL, + &sr_iface_id, &sr_flags, + &sr_lifetime) + ); + TEST_ASSERT_EQUAL_INT(-EFAULT, fib_sr_read_head(&test_fib_sr_table, local_sourceroutes[0], + NULL, &sr_flags, + &sr_lifetime) + ); + /* we set the lifetime to 1 ms and expect it to expire */ + sr_lifetime = 1; + TEST_ASSERT_EQUAL_INT(0, fib_sr_set(&test_fib_sr_table, local_sourceroutes[0], + &sr_iface_id, &sr_flags, &sr_lifetime) + ); + /* + * Since the FIB uses timex under the hood which has a finer grained resolution + * testing the lifetime requires to active wait a bit, say 1 ms. + * Uncomment the following lines if you want to test the temporal behaviour + * @note this may fail since unittests are currently not thread-friendly + */ + /* + xtimer_usleep(1 * MS_IN_USEC); + TEST_ASSERT_EQUAL_INT(-ENOENT, fib_sr_read_head(&test_fib_sr_table, local_sourceroutes[0], + &sr_iface_id, &sr_flags, + &sr_lifetime) + ); + */ + + fib_deinit(&test_fib_sr_table); +} + +/* +* @brief create a new empty source route and delete it +* It is expected to have no source route +*/ +static void test_fib_sr_04_create_and_delete_sr(void) +{ + fib_sr_t *local_sourceroutes[1]; + + TEST_ASSERT_EQUAL_INT(0, fib_sr_create(&test_fib_sr_table, &local_sourceroutes[0], + 42, 0x0, 10000)); + + /* test wrong parameter */ + TEST_ASSERT_EQUAL_INT(-EFAULT, fib_sr_delete(&test_fib_sr_table, NULL)); + + + TEST_ASSERT_EQUAL_INT(0, fib_sr_delete(&test_fib_sr_table, local_sourceroutes[0])); + + kernel_pid_t sr_iface_id; + uint32_t sr_flags; + uint32_t sr_lifetime; + TEST_ASSERT_EQUAL_INT(-ENOENT, fib_sr_read_head(&test_fib_sr_table, local_sourceroutes[0], + &sr_iface_id, &sr_flags, + &sr_lifetime) + ); + + fib_deinit(&test_fib_sr_table); +} + +/* +* @brief create a new empty source route and append two hops on its path +*/ +static void test_fib_sr_05_create_sr_and_append_hop(void) +{ + fib_sr_t *local_sourceroutes[1]; + size_t add_buf_size = 16; + char addr_nxt[add_buf_size]; + snprintf(addr_nxt, add_buf_size, "Some address X1"); + + TEST_ASSERT_EQUAL_INT(0, fib_sr_create(&test_fib_sr_table, &local_sourceroutes[0], + 42, 0x0, 10000)); + + /* append a first hop */ + TEST_ASSERT_EQUAL_INT(0, fib_sr_entry_append(&test_fib_sr_table, local_sourceroutes[0], + (uint8_t *)&addr_nxt, + add_buf_size) + ); + + snprintf(addr_nxt, add_buf_size, "Some address X2"); + /* append a hop behind the first hop */ + TEST_ASSERT_EQUAL_INT(0, fib_sr_entry_append(&test_fib_sr_table, local_sourceroutes[0], + (uint8_t *)&addr_nxt, + add_buf_size) + ); + + /* the destination should be now X2 */ + char addr_out[add_buf_size]; + TEST_ASSERT_EQUAL_INT(0, fib_sr_read_destination(&test_fib_sr_table, local_sourceroutes[0], + (uint8_t *)&addr_out, + &add_buf_size) + ); + + TEST_ASSERT_EQUAL_INT(0, memcmp(addr_nxt, addr_out, add_buf_size)); + + /* try to append the address twice */ + TEST_ASSERT_EQUAL_INT(-EINVAL, fib_sr_entry_append(&test_fib_sr_table, local_sourceroutes[0], + (uint8_t *)&addr_nxt, + add_buf_size) + ); + + /* remove the source route */ + TEST_ASSERT_EQUAL_INT(0, fib_sr_delete(&test_fib_sr_table, local_sourceroutes[0])); + + fib_deinit(&test_fib_sr_table); +} + +/* +* @brief helper function to create source routes. +* The enrties are constructed with the given prefix and numbers +* counted from the given count borders (from and to). +* @param[in] pre prefix string to use +* @param[in] from starting index to count from +* @param[in] to last index to count to +* @param[in] sr the source route to append the created entries +* @param[in] buf_size the size in bytes for each address to be created +*/ +static void _create_sr(const char* pre, size_t from, size_t to, fib_sr_t* sr, size_t buf_size) +{ + char addr_nxt[buf_size]; + strncpy(addr_nxt, pre, strlen(pre)); + + for (size_t i = from; i < to; ++i) { + snprintf(&(addr_nxt[strlen(pre)]), buf_size, "%d", (int)i); + /* append a hop */ + TEST_ASSERT_EQUAL_INT(0, fib_sr_entry_append(&test_fib_sr_table, sr, + (uint8_t *)&addr_nxt, + buf_size) + ); + } +} + +/* +* @brief create a new source route with a number of hops on its path +* Search entry and add a new entry after the found one +*/ +static void test_fib_sr_06_create_sr_with_hops(void) +{ + fib_sr_t *local_sourceroutes[1]; + size_t add_buf_size = 16; + char addr_nxt[add_buf_size]; + + TEST_ASSERT_EQUAL_INT(0, fib_sr_create(&test_fib_sr_table, &local_sourceroutes[0], + 42, 0x0, 10000)); + + _create_sr("Some address X", 0, 10, local_sourceroutes[0], 16); + + /* now we search for a specific entry, e.g. X6 */ + fib_sr_entry_t *sr_path_entry[1]; + snprintf(addr_nxt, add_buf_size, "Some address X6"); + + TEST_ASSERT_EQUAL_INT(0, fib_sr_search(&test_fib_sr_table, local_sourceroutes[0], + (uint8_t *)&addr_nxt, add_buf_size, + &sr_path_entry[0]) + ); + + /* and move on to the next entry (just because we can) */ + TEST_ASSERT_EQUAL_INT(0, fib_sr_next(&test_fib_sr_table, local_sourceroutes[0], + &sr_path_entry[0])); + + /* now we add a new entry behind sr_path_entry[0] and drop the remaining route */ + snprintf(addr_nxt, add_buf_size, "Some address XY"); + TEST_ASSERT_EQUAL_INT(0, fib_sr_entry_add(&test_fib_sr_table, local_sourceroutes[0], + sr_path_entry[0], + (uint8_t *)&addr_nxt, + add_buf_size, false) + ); + + /* so entries X8 and X9 should be gone */ + snprintf(addr_nxt, add_buf_size, "Some address X8"); + TEST_ASSERT_EQUAL_INT(-EHOSTUNREACH, fib_sr_search(&test_fib_sr_table, local_sourceroutes[0], + (uint8_t *)&addr_nxt, add_buf_size, + &sr_path_entry[0]) + ); + snprintf(addr_nxt, add_buf_size, "Some address X9"); + TEST_ASSERT_EQUAL_INT(-EHOSTUNREACH, fib_sr_search(&test_fib_sr_table, local_sourceroutes[0], + (uint8_t *)&addr_nxt, add_buf_size, + &sr_path_entry[0]) + ); + + /* now we go to a earlier entry and add a new entry not dropping the remaining ones */ + snprintf(addr_nxt, add_buf_size, "Some address X2"); + + TEST_ASSERT_EQUAL_INT(0, fib_sr_search(&test_fib_sr_table, local_sourceroutes[0], + (uint8_t *)&addr_nxt, add_buf_size, + &sr_path_entry[0]) + ); + + snprintf(addr_nxt, add_buf_size, "Some address XZ"); + TEST_ASSERT_EQUAL_INT(0, fib_sr_entry_add(&test_fib_sr_table, local_sourceroutes[0], + sr_path_entry[0], + (uint8_t *)&addr_nxt, + add_buf_size, true) + ); + + /* + * @note uncomment the following line to print the source route. + * expected X0, .., X2, XZ, X3, .., X7, XY + */ + /* fib_print_sr( local_sourceroutes[0]); */ + + /* remove the source route */ + TEST_ASSERT_EQUAL_INT(0, fib_sr_delete(&test_fib_sr_table, local_sourceroutes[0])); + + fib_deinit(&test_fib_sr_table); +} + + +/* +* @brief create a new source route with a number of hops on its path, +* and delete several entries +*/ +static void test_fib_sr_07_create_sr_with_hops_and_delete_one(void) +{ + fib_sr_t *local_sourceroutes[1]; + size_t add_buf_size = 16; + char addr_nxt[add_buf_size]; + + TEST_ASSERT_EQUAL_INT(0, fib_sr_create(&test_fib_sr_table, &local_sourceroutes[0], + 42, 0x0, 10000)); + + _create_sr("Some address X", 0, 10, local_sourceroutes[0], 16); + + snprintf(addr_nxt, add_buf_size, "Some address X6"); + /* we delete X6 keeping the remaining entries */ + TEST_ASSERT_EQUAL_INT(0, fib_sr_entry_delete(&test_fib_sr_table, local_sourceroutes[0], + (uint8_t *)&addr_nxt, add_buf_size, + true) + ); + + snprintf(addr_nxt, add_buf_size, "Some address X9"); + /* now the destination */ + TEST_ASSERT_EQUAL_INT(0, fib_sr_entry_delete(&test_fib_sr_table, local_sourceroutes[0], + (uint8_t *)&addr_nxt, add_buf_size, + true) + ); + + snprintf(addr_nxt, add_buf_size, "Some address X0"); + /* and now the first hop on the path */ + TEST_ASSERT_EQUAL_INT(0, fib_sr_entry_delete(&test_fib_sr_table, local_sourceroutes[0], + (uint8_t *)&addr_nxt, add_buf_size, + true) + ); + + /* + * @note uncomment the following line to print the source route. + * expected X1, .., X5, X7, .., X8 + */ + /* fib_print_sr( local_sourceroutes[0]); */ + + /* remove the source route */ + TEST_ASSERT_EQUAL_INT(0, fib_sr_delete(&test_fib_sr_table, local_sourceroutes[0])); + + fib_deinit(&test_fib_sr_table); +} + +/* +* @brief create a new source route with a number of hops on its path, +* and overwrite the address of one entry +*/ +static void test_fib_sr_08_create_sr_with_hops_and_overwrite_one(void) +{ + fib_sr_t *local_sourceroutes[1]; + size_t add_buf_size = 16; + char addr_nxt[add_buf_size]; + char addr_exc[add_buf_size]; + + TEST_ASSERT_EQUAL_INT(0, fib_sr_create(&test_fib_sr_table, &local_sourceroutes[0], + 42, 0x0, 10000)); + + _create_sr("Some address X", 0, 10, local_sourceroutes[0], 16); + + /* now we search for a specific entry, e.g. X6 */ + snprintf(addr_nxt, add_buf_size, "Some address X6"); + snprintf(addr_exc, add_buf_size, "Some address XY"); + + TEST_ASSERT_EQUAL_INT(0, fib_sr_entry_overwrite(&test_fib_sr_table, local_sourceroutes[0], + (uint8_t *)&addr_nxt, add_buf_size, + (uint8_t *)&addr_exc, add_buf_size) + ); + /* + * @note uncomment the following line to print the source route. + * expected X0, .., X5, XY, X7, .., X9 + */ + /* fib_print_sr( local_sourceroutes[0]); */ + + /* remove the source route */ + TEST_ASSERT_EQUAL_INT(0, fib_sr_delete(&test_fib_sr_table, local_sourceroutes[0])); + + fib_deinit(&test_fib_sr_table); +} + +/* +* @brief create a new source route with a number of hops on its path, +* search an entry and copy the address to the caller +*/ +static void test_fib_sr_09_create_sr_with_hops_and_get_one(void) +{ + fib_sr_t *local_sourceroutes[1]; + size_t add_buf_size = 16; + char addr_nxt[add_buf_size]; + char addr_out[add_buf_size]; + + TEST_ASSERT_EQUAL_INT(0, fib_sr_create(&test_fib_sr_table, &local_sourceroutes[0], + 42, 0x0, 10000)); + + _create_sr("Some address X", 0, 10, local_sourceroutes[0], 16); + + /* first we have an empty iterator */ + fib_sr_entry_t *sr_path_entry[1]; + sr_path_entry[0] = NULL; + + /* now we move on to a specific hop, i.e. the 6th one (X5) */ + for (size_t i = 0; i < 6; ++i) { + TEST_ASSERT_EQUAL_INT(0, fib_sr_next(&test_fib_sr_table, local_sourceroutes[0], + &sr_path_entry[0])); + } + + TEST_ASSERT_EQUAL_INT(0, fib_sr_entry_get_address(&test_fib_sr_table, local_sourceroutes[0], + sr_path_entry[0], + (uint8_t *)&addr_out, &add_buf_size) + ); + + /* we ecpect the X5 address */ + snprintf(addr_nxt, add_buf_size, "Some address X5"); + TEST_ASSERT_EQUAL_INT(0, memcmp(addr_nxt, addr_out, add_buf_size)); + + /* + * @note uncomment the following line to print the source route. + */ + /* fib_print_sr( local_sourceroutes[0]); */ + + /* remove the source route */ + TEST_ASSERT_EQUAL_INT(0, fib_sr_delete(&test_fib_sr_table, local_sourceroutes[0])); + + fib_deinit(&test_fib_sr_table); +} + +/* +* @brief create a new source route with a number of hops on its path, +* and get it (also in reverse order) +*/ +static void test_fib_sr_10_create_sr_with_hops_and_get_a_route(void) +{ + fib_sr_t *local_sourceroutes[1]; + size_t add_buf_size = 16; + char addr_nxt[add_buf_size]; + + TEST_ASSERT_EQUAL_INT(0, fib_sr_create(&test_fib_sr_table, &local_sourceroutes[0], + 42, 0x0, 10000)); + + _create_sr("Some address X", 0, 10, local_sourceroutes[0], 16); + + size_t addr_list_elements = 10; + size_t element_size = 16; + uint8_t addr_list[ addr_list_elements * element_size ]; + kernel_pid_t sr_iface_id; + uint32_t sr_flags = 0x0; + snprintf(addr_nxt, add_buf_size, "Some address X9"); + + TEST_ASSERT_EQUAL_INT(0, fib_sr_get_route(&test_fib_sr_table, (uint8_t *)&addr_nxt, + add_buf_size, &sr_iface_id, &sr_flags, + addr_list, &addr_list_elements, &element_size, + false, NULL) + ); + + /* + * @note uncomment the following lines to print the source route and the result. + * expected X0, .., X9 + */ + /* + fib_print_sr( local_sourceroutes[0]); + + for (size_t i = 0; i < addr_list_elements; ++i) { + printf("["); + for (size_t j = 0; j < element_size; j++) { + printf("%c", addr_list[(i*element_size)+j]); + }printf("] "); + }puts(""); + */ + + /* now we get the addresses in reverse order */ + TEST_ASSERT_EQUAL_INT(0, fib_sr_get_route(&test_fib_sr_table, (uint8_t *)&addr_nxt, + add_buf_size, &sr_iface_id, &sr_flags, + addr_list, &addr_list_elements, &element_size, + true, NULL) + ); + + /* + * @note uncomment the following lines to print the source route and the result. + * expected X0, .., X9 + */ + /* + fib_print_sr( local_sourceroutes[0]); + + for (size_t i = 0; i < addr_list_elements; ++i) { + printf("["); + for (size_t j = 0; j < element_size; j++) { + printf("%c", addr_list[(i*element_size)+j]); + }printf("] "); + }puts(""); + */ + + /* remove the source route */ + TEST_ASSERT_EQUAL_INT(0, fib_sr_delete(&test_fib_sr_table, local_sourceroutes[0])); + + fib_deinit(&test_fib_sr_table); +} + +/* +* @brief create a new source route with a number of hops on its path, +* and get a partial route (also in reverse order) +*/ +static void test_fib_sr_11_create_sr_with_hops_and_get_a_partial_route(void) +{ + fib_sr_t *local_sourceroutes[1]; + size_t add_buf_size = 16; + char addr_nxt[add_buf_size]; + + TEST_ASSERT_EQUAL_INT(0, fib_sr_create(&test_fib_sr_table, &local_sourceroutes[0], + 42, 0x0, 10000)); + + _create_sr("Some address X", 0, 10, local_sourceroutes[0], 16); + + size_t addr_list_elements = 10; + size_t element_size = 16; + uint8_t addr_list[ addr_list_elements * element_size ]; + kernel_pid_t sr_iface_id; + uint32_t sr_flags = 0x0; + snprintf(addr_nxt, add_buf_size, "Some address X7"); + + TEST_ASSERT_EQUAL_INT(0, fib_sr_get_route(&test_fib_sr_table, (uint8_t *)&addr_nxt, + add_buf_size, &sr_iface_id, &sr_flags, + addr_list, &addr_list_elements, &element_size, + false, NULL) + ); + + /* + * @note uncomment the following lines to print the resulting source route. + * expected X0..X9 where X0..X7 has usecount of 2 + */ + /* + fib_print_sr( local_sourceroutes[0]); + + for (size_t i = 0; i < addr_list_elements; ++i) { + printf("["); + for (size_t j = 0; j < element_size; j++) { + printf("%c", addr_list[(i*element_size)+j]); + }printf("] "); + }puts(""); + */ + + /* now we get the addresses in reverse order */ + TEST_ASSERT_EQUAL_INT(0, fib_sr_get_route(&test_fib_sr_table, (uint8_t *)&addr_nxt, + add_buf_size, &sr_iface_id, &sr_flags, + addr_list, &addr_list_elements, &element_size, + true, NULL) + ); + + /* + * @note uncomment the following lines to print the resulting source route. + */ + /* + for (size_t i = 0; i < addr_list_elements; ++i) { + printf("["); + for (size_t j = 0; j < element_size; j++) { + printf("%c", addr_list[(i*element_size)+j]); + }printf("] "); + }puts(""); + */ + + /* remove the source route */ + TEST_ASSERT_EQUAL_INT(0, fib_sr_delete(&test_fib_sr_table, local_sourceroutes[0])); + + fib_deinit(&test_fib_sr_table); +} + +/* +* @brief create new source routes and get all routes succesively +*/ +static void test_fib_sr_12_get_consecutive_sr(void) +{ + fib_sr_t *local_sourceroutes[3]; + size_t add_buf_size = 16; + char addr_nxt[add_buf_size]; + + /* Create SR1 X0,.., X9,XX */ + TEST_ASSERT_EQUAL_INT(0, fib_sr_create(&test_fib_sr_table, &local_sourceroutes[0], + 42, 0x0, 10000)); + + _create_sr("Some address X", 0, 10, local_sourceroutes[0], 16); + + snprintf(addr_nxt, add_buf_size, "Some address XX"); + + /* append a the last hop, i.e. the destination */ + TEST_ASSERT_EQUAL_INT(0, fib_sr_entry_append(&test_fib_sr_table, local_sourceroutes[0], + (uint8_t *)&addr_nxt, + add_buf_size) + ); + + /* Create SR1 Y1,.., Y7,XX */ + TEST_ASSERT_EQUAL_INT(0, fib_sr_create(&test_fib_sr_table, &local_sourceroutes[1], + 42, 0x0, 10000)); + + _create_sr("Some address Y", 1, 8, local_sourceroutes[1], 16); + + snprintf(addr_nxt, add_buf_size, "Some address XX"); + /* append a the last hop, i.e. the destination */ + TEST_ASSERT_EQUAL_INT(0, fib_sr_entry_append(&test_fib_sr_table, local_sourceroutes[1], + (uint8_t *)&addr_nxt, + add_buf_size) + ); + + + /* Create SR1 Z5,.., Z7,XX */ + TEST_ASSERT_EQUAL_INT(0, fib_sr_create(&test_fib_sr_table, &local_sourceroutes[2], + 42, 0x0, 10000)); + + _create_sr("Some address Z", 5, 8, local_sourceroutes[2], 16); + + snprintf(addr_nxt, add_buf_size, "Some address XX"); + /* append a the last hop, i.e. the destination */ + TEST_ASSERT_EQUAL_INT(0, fib_sr_entry_append(&test_fib_sr_table, local_sourceroutes[2], + (uint8_t *)&addr_nxt, + add_buf_size) + ); + + fib_sr_t *iter[1]; + iter[0] = NULL; + size_t addr_list_elements = 11; + size_t element_size = 16; + uint8_t addr_list[ addr_list_elements * element_size ]; + kernel_pid_t sr_iface_id; + uint32_t sr_flags = 0x0; + snprintf(addr_nxt, add_buf_size, "Some address XX"); + + TEST_ASSERT_EQUAL_INT(0, fib_sr_get_route(&test_fib_sr_table, (uint8_t *)&addr_nxt, + add_buf_size, &sr_iface_id, &sr_flags, + addr_list, &addr_list_elements, &element_size, + false, iter) + ); + + TEST_ASSERT_EQUAL_INT(11, addr_list_elements); + + TEST_ASSERT_EQUAL_INT(0, fib_sr_get_route(&test_fib_sr_table, (uint8_t *)&addr_nxt, + add_buf_size, &sr_iface_id, &sr_flags, + addr_list, &addr_list_elements, &element_size, + false, iter) + ); + + TEST_ASSERT_EQUAL_INT(8, addr_list_elements); + + TEST_ASSERT_EQUAL_INT(0, fib_sr_get_route(&test_fib_sr_table, (uint8_t *)&addr_nxt, + add_buf_size, &sr_iface_id, &sr_flags, + addr_list, &addr_list_elements, &element_size, + false, iter) + ); + TEST_ASSERT_EQUAL_INT(4, addr_list_elements); + + fib_deinit(&test_fib_sr_table); +} + +Test *tests_fib_sr_tests(void) +{ + test_fib_sr_table.data.source_routes = &_entries_sr; + test_fib_sr_table.table_type = FIB_TABLE_TYPE_SR; + test_fib_sr_table.size = TEST_MAX_FIB_SR; + mutex_init(&(test_fib_sr_table.mtx_access)); + test_fib_sr_table.notify_rp_pos = 0; + + fib_init(&test_fib_sr_table); + EMB_UNIT_TESTFIXTURES(fixtures) { + new_TestFixture(test_fib_sr_01_create_empty_sr), + new_TestFixture(test_fib_sr_02_change_sr_parameters), + new_TestFixture(test_fib_sr_03_read_sr_parameters), + new_TestFixture(test_fib_sr_04_create_and_delete_sr), + new_TestFixture(test_fib_sr_05_create_sr_and_append_hop), + new_TestFixture(test_fib_sr_06_create_sr_with_hops), + new_TestFixture(test_fib_sr_07_create_sr_with_hops_and_delete_one), + new_TestFixture(test_fib_sr_08_create_sr_with_hops_and_overwrite_one), + new_TestFixture(test_fib_sr_09_create_sr_with_hops_and_get_one), + new_TestFixture(test_fib_sr_10_create_sr_with_hops_and_get_a_route), + new_TestFixture(test_fib_sr_11_create_sr_with_hops_and_get_a_partial_route), + new_TestFixture(test_fib_sr_12_get_consecutive_sr), + }; + + EMB_UNIT_TESTCALLER(fib_sr_tests, NULL, NULL, fixtures); + + return (Test *)&fib_sr_tests; +} + +void tests_fib_sr(void) +{ + TESTS_RUN(tests_fib_sr_tests()); +} diff --git a/tests/unittests/tests-fib_sr/tests-fib_sr.h b/tests/unittests/tests-fib_sr/tests-fib_sr.h new file mode 100644 index 0000000000..6acdb9776b --- /dev/null +++ b/tests/unittests/tests-fib_sr/tests-fib_sr.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 + * @brief Unittests for the ``fib`` module (source routes) + * + * @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_sr(void); + +/** + * @brief Generates tests for FIB source routing + * + * @return embUnit tests if successful, NULL if not. + */ +Test *tests_fib_sr_tests(void); + +#ifdef __cplusplus +} +#endif + +#endif /* TESTS_FIB_H_ */ +/** @} */