From 9079e30da0e385b4f5ab1eadbb2802a914ff507e Mon Sep 17 00:00:00 2001 From: BytesGalore Date: Tue, 14 Apr 2015 18:30:19 +0200 Subject: [PATCH] sys/net/fib: added function to request a set of destination addresses --- sys/include/net/ng_fib.h | 35 +++++ sys/include/net/ng_fib/ng_universal_address.h | 36 ++++- sys/net/network_layer/fib/fib.c | 55 +++++-- sys/net/network_layer/fib/universal_address.c | 83 +++++++++- tests/unittests/Makefile | 2 +- tests/unittests/tests-fib/tests-fib.c | 146 ++++++++++++++++++ 6 files changed, 332 insertions(+), 25 deletions(-) diff --git a/sys/include/net/ng_fib.h b/sys/include/net/ng_fib.h index 28289b8e866a..5fe8b7c0e909 100644 --- a/sys/include/net/ng_fib.h +++ b/sys/include/net/ng_fib.h @@ -38,6 +38,20 @@ typedef struct rp_address_msg_t { #define FIB_MSG_RP_SIGNAL (0x99) /**< message type for RP notifications */ +/** + * @brief the size in bytes of a full address + * TODO: replace with UNIVERSAL_ADDRESS_SIZE (#3022) +*/ +#define FIB_DESTINATION_SIZE_SUBSTITUTE (16) + +/** + * @brief entry used to collect available destinations + */ +typedef struct fib_destination_set_entry_t { + uint8_t dest[FIB_DESTINATION_SIZE_SUBSTITUTE]; /**< The destination address */ + size_t dest_size; /**< The destination address size */ +} fib_destination_set_entry_t; + /** * @brief indicator of a lifetime that does not expire (2^32 - 1) */ @@ -130,6 +144,27 @@ 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 provides a set of destination addresses matching the given prefix +* If the out buffer is insufficient low or passed as NULL, +* the function will continue to count the number of matching entries +* and provide the number to the caller. +* +* @param[in] prefix the destination address +* @param[in] prefix_size the destination address size +* @param[out] dst_set the destination addresses matching the prefix +* @param[in, out] dst_size the number of entries available on in and used on out +* +* @return 0 on success +* -EHOSTUNREACH if no entry matches the type in the FIB +* -ENOBUFS if the size for the found entries is insufficient low +* The actual needed size is stored then in dst_set_size, +* however the required size may change in between calls. +*/ +int fib_get_destination_set(uint8_t *prefix, size_t prefix_size, + fib_destination_set_entry_t *dst_set, size_t* dst_set_size); + + /** * @brief returns the actual number of used FIB entries */ diff --git a/sys/include/net/ng_fib/ng_universal_address.h b/sys/include/net/ng_fib/ng_universal_address.h index 30fecaadeb11..91e55d74ac30 100644 --- a/sys/include/net/ng_fib/ng_universal_address.h +++ b/sys/include/net/ng_fib/ng_universal_address.h @@ -82,19 +82,43 @@ uint8_t* universal_address_get_address(universal_address_container_t *entry, /** * @brief determines if the entry equals the provided address + * This function requires to be provided with the full size of the used + * address type behind *addr to be compareable with the address stored in *entry. * * @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 + * @param[in, out] addr_size_in_bits the number of bits used for the address entry + * on sucessfull return this value is overwritten + * with the number of matching bits till the + * first of trailing `0`s * - * @return 0 if the entries are equal or comperable + * @return 0 if the entries are equal + * 1 if the entry match to a certain prefix (trailing '0's in *entry) * -ENOENT if the given adresses do not match */ int universal_address_compare(universal_address_container_t *entry, - uint8_t *addr, size_t *addr_size); + uint8_t *addr, size_t *addr_size_in_bits); + + +/** +* @brief determines if the entry equals the provided prefix +* This function requires to be provided with the full size of the used +* address type behind *prefix to be compareable with the address stored in *entry. +* +* +* @param[in] entry pointer to the universal_address_container_t for compare +* @param[in] prefix pointer to the address for compare +* @param[in] prefix_size_in_bits the number of bits used for the prefix entry. +* This size MUST be the full address size including trailing '0's, +* e.g. for an ng_ipv6_addr_t it would be sizeof(ng_ipv6_addr_t) +* regardless if the stored prefix is < ::/128 +* +* @return 0 if the entries are equal +* 1 if the entry match to a certain prefix (trailing '0's in *prefix) +* -ENOENT if the given adresses do not match +*/ +int universal_address_compare_prefix(universal_address_container_t *entry, + uint8_t *prefix, size_t prefix_size_in_bits); /** * @brief Prints the content of the given entry diff --git a/sys/net/network_layer/fib/fib.c b/sys/net/network_layer/fib/fib.c index f9c8e8377d74..7ff50a3b971a 100644 --- a/sys/net/network_layer/fib/fib.c +++ b/sys/net/network_layer/fib/fib.c @@ -98,7 +98,7 @@ static int fib_find_entry(uint8_t *dst, size_t dst_size, size_t count = 0; size_t prefix_size = 0; - size_t match_size = dst_size; + size_t match_size = dst_size<<3; int ret = -EHOSTUNREACH; bool is_all_zeros_addr = true; @@ -136,13 +136,11 @@ static int fib_find_entry(uint8_t *dst, size_t dst_size, } } - if ((prefix_size < dst_size) && - (fib_table[i].global != NULL) && - (universal_address_compare(fib_table[i].global, dst, &match_size) == 0)) { + if ((prefix_size < (dst_size<<3)) && (fib_table[i].global != NULL)) { + int ret_comp = universal_address_compare(fib_table[i].global, dst, &match_size); /* If we found an exact match */ - if (match_size == dst_size - || (is_all_zeros_addr && match_size == 0)) { + if (ret_comp == 0 || (is_all_zeros_addr && match_size == 0)) { entry_arr[0] = &(fib_table[i]); *entry_arr_size = 1; /* we will not find a better one so we return */ @@ -150,16 +148,16 @@ static int fib_find_entry(uint8_t *dst, size_t dst_size, } else { /* we try to find the most fitting prefix */ - if (match_size > prefix_size) { + if (ret_comp == 1) { 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<<3; + count = 1; } } - - prefix_size = match_size; - match_size = dst_size; - count = 1; } } @@ -412,7 +410,6 @@ int fib_get_next_hop(kernel_pid_t *iface_id, fib_entry_t *entry[count]; int ret = fib_find_entry(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(dst, dst_size, dst_flags) == 0) { @@ -443,6 +440,40 @@ int fib_get_next_hop(kernel_pid_t *iface_id, return 0; } +int fib_get_destination_set(uint8_t *prefix, size_t prefix_size, + fib_destination_set_entry_t *dst_set, size_t* dst_set_size) +{ + mutex_lock(&mtx_access); + int ret = -EHOSTUNREACH; + size_t found_entries = 0; + + for (size_t i = 0; i < FIB_MAX_FIB_TABLE_ENTRIES; ++i) { + if ((fib_table[i].global != NULL) && + (universal_address_compare_prefix(fib_table[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(fib_table[i].global, + dst_set[found_entries].dest, + &dst_set[found_entries].dest_size); + } + found_entries++; + } + } + + if( found_entries > *dst_set_size ) { + ret = -ENOBUFS; + } else if( found_entries > 0 ) { + ret = 0; + } + + *dst_set_size = found_entries; + + mutex_unlock(&mtx_access); + + return ret; +} + void fib_init(void) { DEBUG("[fib_init] hello. Initializing some stuff."); diff --git a/sys/net/network_layer/fib/universal_address.c b/sys/net/network_layer/fib/universal_address.c index 1f12ad068c8c..373108a620fb 100644 --- a/sys/net/network_layer/fib/universal_address.c +++ b/sys/net/network_layer/fib/universal_address.c @@ -168,19 +168,19 @@ uint8_t* universal_address_get_address(universal_address_container_t *entry, } int universal_address_compare(universal_address_container_t *entry, - uint8_t *addr, size_t *addr_size) + uint8_t *addr, size_t *addr_size_in_bits) { mutex_lock(&mtx_access); int ret = -ENOENT; /* If we have distinct sizes, the addresses are probably not comperable */ - if (entry->address_size != *addr_size) { + if ((size_t)(entry->address_size<<3) != *addr_size_in_bits) { mutex_unlock(&mtx_access); return ret; } - /* Get the index of the first trailing `0` (indicates a network prefix) */ + /* Get the index of the first trailing `0` (indicates a prefix) */ int i = 0; for( i = entry->address_size-1; i >= 0; --i) { if( entry->address[i] != 0 ) { @@ -188,9 +188,80 @@ int universal_address_compare(universal_address_container_t *entry, } } - if( memcmp(entry->address, addr, i+1) == 0 ) { - ret = 0; - *addr_size = i+1; + if( memcmp(entry->address, addr, i) == 0 ) { + /* if the bytes-1 equals we check the bits of the lowest byte */ + uint8_t bitmask = 0x00; + uint8_t j = 0; + /* get a bitmask for the trailing 0b */ + for( ; j < 8; ++j ) { + if ( (entry->address[i] >> j) & 0x01 ) { + bitmask = 0xff << j; + break; + } + } + if( (entry->address[i] & bitmask) == (addr[i] & bitmask) ) { + ret = entry->address[i] != addr[i]; + *addr_size_in_bits = (i<<3) + j; + if( ret == 0 ) { + /* check if the remaining bits from addr are significant */ + i++; + for(; i < entry->address_size; ++i) { + if( addr[i] != 0 ) { + ret = 1; + break; + } + } + } + } + } + + mutex_unlock(&mtx_access); + return ret; +} + +int universal_address_compare_prefix(universal_address_container_t *entry, + uint8_t *prefix, size_t prefix_size_in_bits) +{ + mutex_lock(&mtx_access); + int ret = -ENOENT; + /* If we have distinct sizes, the prefix is not comperable */ + if ((size_t)(entry->address_size<<3) != prefix_size_in_bits) { + mutex_unlock(&mtx_access); + return ret; + } + + /* Get the index of the first trailing `0` */ + int i = 0; + for( i = entry->address_size-1; i >= 0; --i) { + if( prefix[i] != 0 ) { + break; + } + } + + if( memcmp(entry->address, prefix, i) == 0 ) { + /* if the bytes-1 equals we check the bits of the lowest byte */ + uint8_t bitmask = 0x00; + /* get a bitmask for the trailing 0b */ + for( uint8_t j = 0; j < 8; ++j ) { + if ( (prefix[i] >> j) & 0x01 ) { + bitmask = 0xff << j; + break; + } + } + + if( (entry->address[i] & bitmask) == (prefix[i] & bitmask) ) { + ret = entry->address[i] != prefix[i]; + if( ret == 0 ) { + /* check if the remaining bits from entry are significant */ + i++; + for(; i < entry->address_size; ++i) { + if( entry->address[i] != 0 ) { + ret = 1; + break; + } + } + } + } } mutex_unlock(&mtx_access); diff --git a/tests/unittests/Makefile b/tests/unittests/Makefile index 5019fbe45155..6a733ce4331e 100644 --- a/tests/unittests/Makefile +++ b/tests/unittests/Makefile @@ -4,7 +4,7 @@ include ../Makefile.tests_common BOARD_INSUFFICIENT_RAM := airfy-beacon chronos msb-430 msb-430h pca10000 \ pca10005 redbee-econotag spark-core stm32f0discovery \ telosb wsn430-v1_3b wsn430-v1_4 z1 nucleo-f334 \ - yunjia-nrf51822 + yunjia-nrf51822 samr21-xpro USEMODULE += embunit diff --git a/tests/unittests/tests-fib/tests-fib.c b/tests/unittests/tests-fib/tests-fib.c index c20b1a4f3199..7430c6889cad 100644 --- a/tests/unittests/tests-fib/tests-fib.c +++ b/tests/unittests/tests-fib/tests-fib.c @@ -488,6 +488,7 @@ static void test_fib_14_exact_and_prefix_match(void) fib_add_entry(42, (uint8_t *)addr_dst, add_buf_size - 1, 0x1234, (uint8_t *)addr_nxt, add_buf_size - 1, 0x34, 100000); + memset(addr_lookup, 0, add_buf_size); /* exact match */ snprintf(addr_lookup, add_buf_size, "Test addr123"); int ret = fib_get_next_hop(&iface_id, @@ -495,6 +496,7 @@ static void test_fib_14_exact_and_prefix_match(void) (uint8_t *)addr_lookup, add_buf_size - 1, 0x123); TEST_ASSERT_EQUAL_INT(0, ret); + add_buf_size = 16; char addr_expect_01[] = "Test address 23"; ret = strncmp(addr_expect_01, addr_nxt, add_buf_size - 1); @@ -553,6 +555,148 @@ static void test_fib_15_get_lifetime(void) TEST_ASSERT_EQUAL_INT(1, timex_cmp(lifetime, cmp_lifetime)); /* make sure lifetime hasn't grown magically either */ TEST_ASSERT_EQUAL_INT(-1, timex_cmp(lifetime, cmp_max_lifetime)); + + fib_deinit(); +} + +/* +* @brief testing prefix with bits +*/ +static void test_fib_16_prefix_match(void) +{ + size_t add_buf_size = 16; + char addr_dst[add_buf_size]; + char addr_nxt[add_buf_size]; + char addr_lookup[add_buf_size]; + kernel_pid_t iface_id = KERNEL_PID_UNDEF; + uint32_t next_hop_flags = 0; + + memset(addr_dst, 0, add_buf_size); + memset(addr_nxt, 0, add_buf_size); + memset(addr_lookup, 0, add_buf_size); + + snprintf(addr_dst, add_buf_size, "Test address 1X"); + snprintf(addr_nxt, add_buf_size, "Test address 99"); + snprintf(addr_lookup, add_buf_size, "Test address 1X"); + + /* now we change the last byte of addr_dst to have defined trailing 0 bits */ + /* test success */ + addr_dst[14] = (char)0x80; /* 1000 0000 */ + addr_lookup[14] = (char)0x87; /* 1000 0111 */ + + fib_add_entry(42, (uint8_t *)addr_dst, add_buf_size - 1, 0x123, + (uint8_t *)addr_nxt, add_buf_size - 1, 0x23, 100000); + + addr_dst[14] = (char)0x3c; /* 0011 1100 */ + fib_add_entry(42, (uint8_t *)addr_dst, add_buf_size - 1, 0x123, + (uint8_t *)addr_nxt, add_buf_size - 1, 0x23, 100000); + + memset(addr_nxt, 0, add_buf_size); + + 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); + + /* test fail */ + addr_dst[14] = (char)0x3c; /* 0011 1100 */ + addr_lookup[14] = (char)0x34; /* 0011 0100 */ + add_buf_size = 16; + + fib_add_entry(42, (uint8_t *)addr_dst, add_buf_size - 1, 0x123, + (uint8_t *)addr_nxt, add_buf_size - 1, 0x23, 100000); + + memset(addr_nxt, 0, add_buf_size); + + 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(-EHOSTUNREACH, ret); + + /* test success (again) by adjusting the lsb */ + addr_lookup[14] = (char)0x3e; /* 0011 1110 */ + add_buf_size = 16; + + memset(addr_nxt, 0, add_buf_size); + + 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); + +#if (TEST_FIB_SHOW_OUTPUT == 1) + fib_print_fib_table(); + puts(""); + universal_address_print_table(); + puts(""); +#endif + fib_deinit(); +} + + +/* +* @brief testing receiving an destination address set matching a specific prefix +*/ +static void test_fib_17_get_entry_set(void) +{ + size_t addr_buf_size = 16; + char addr_dst[addr_buf_size]; + char addr_nxt[addr_buf_size]; + + /* fill 20 addresses */ + for (size_t i = 0; i < 20; ++i) { + /* construct "addresses" for the FIB */ + snprintf(addr_dst, addr_buf_size, "Test address %02d", (int)i); + snprintf(addr_nxt, addr_buf_size, "Test address %02d", i % 11); + fib_add_entry(42, (uint8_t *)addr_dst, addr_buf_size - 1, 0x0, + (uint8_t *)addr_nxt, addr_buf_size - 1, 0x0, 100000); + } + + size_t arr_size = 20; + fib_destination_set_entry_t arr_dst[arr_size]; + char prefix[addr_buf_size]; + memset(prefix,0, addr_buf_size); + snprintf(prefix, addr_buf_size, "Test address 1"); + + int ret = fib_get_destination_set((uint8_t *)prefix, addr_buf_size-1, &arr_dst[0], &arr_size); + TEST_ASSERT_EQUAL_INT(0, ret); + + /* we should receive 10 entries 10 to 19 */ + TEST_ASSERT_EQUAL_INT(10, arr_size); + arr_size = 20; + + memset(prefix,0, addr_buf_size); + snprintf(prefix, addr_buf_size, "Test address 0"); + + ret = fib_get_destination_set((uint8_t *)prefix, addr_buf_size-1, &arr_dst[0], &arr_size); + TEST_ASSERT_EQUAL_INT(0, ret); + + /* we should receive 20 entries 0-19 */ + TEST_ASSERT_EQUAL_INT(20, arr_size); + arr_size = 20; + + memset(prefix,0, addr_buf_size); + snprintf(prefix, addr_buf_size, "Test address"); + + ret = fib_get_destination_set((uint8_t *)prefix, addr_buf_size-1, &arr_dst[0], &arr_size); + TEST_ASSERT_EQUAL_INT(0, ret); + + /* we should receive 20 entries 0-19 */ + TEST_ASSERT_EQUAL_INT(20, arr_size); + +#if (TEST_FIB_SHOW_OUTPUT == 1) + puts(""); + for(size_t i = 0; i < arr_size; ++i) { + for( size_t j = 0; j < arr_dst[i].dest_size; ++j) { + printf("%c", (char)arr_dst[i].dest[j]); + } + puts(""); + } +#endif + fib_deinit(); } @@ -575,6 +719,8 @@ Test *tests_fib_tests(void) new_TestFixture(test_fib_13_get_next_hop_fail_on_buffer_size), new_TestFixture(test_fib_14_exact_and_prefix_match), new_TestFixture(test_fib_15_get_lifetime), + new_TestFixture(test_fib_16_prefix_match), + new_TestFixture(test_fib_17_get_entry_set), }; EMB_UNIT_TESTCALLER(fib_tests, NULL, NULL, fixtures);