diff --git a/sys/Makefile.dep b/sys/Makefile.dep index a4551af11c..0775ab7c11 100644 --- a/sys/Makefile.dep +++ b/sys/Makefile.dep @@ -476,6 +476,10 @@ ifneq (,$(filter ieee802154_submac,$(USEMODULE))) USEMODULE += xtimer endif +ifneq (,$(filter l2util,$(USEMODULE))) + USEMODULE += fmt +endif + ifneq (,$(filter od,$(USEMODULE))) USEMODULE += fmt endif diff --git a/sys/include/net/l2util.h b/sys/include/net/l2util.h index 12eeb68b89..0159072b67 100644 --- a/sys/include/net/l2util.h +++ b/sys/include/net/l2util.h @@ -153,6 +153,44 @@ int l2util_ipv6_group_to_l2_group(int dev_type, const ipv6_addr_t *ipv6_group, uint8_t *l2_group); +/** + * @brief Converts a hardware address to a human readable string. + * + * @details The format will be like `xx:xx:xx:xx` where `xx` are the bytes + * of @p addr in hexadecimal representation. + * + * @pre `(out != NULL) && ((addr != NULL) || (addr_len == 0))` + * @pre @p out **MUST** have allocated at least 3 * @p addr_len bytes. + * + * @param[in] addr A hardware address. + * @param[in] addr_len Length of @p addr. + * @param[out] out A string to store the output in. Must at least have + * 3 * @p addr_len bytes allocated. + * + * @return @p out. + */ +char *l2util_addr_to_str(const uint8_t *addr, size_t addr_len, char *out); + +/** + * @brief Parses a string of colon-separated hexadecimals to a hardware + * address. + * + * @details The input format must be like `xx:xx:xx:xx` where `xx` will be the + * bytes of @p addr in hexadecimal representation. + * + * @pre `(out != NULL)` + * @pre @p out **MUST** have allocated at least + * @ref GNRC_NETIF_L2ADDR_MAXLEN bytes. + * + * @param[in] str A string of colon-separated hexadecimals. + * @param[out] out The resulting hardware address. Must at least have + * @ref GNRC_NETIF_L2ADDR_MAXLEN bytes allocated. + * + * @return Actual length of @p out on success. + * @return 0, on failure. + */ +size_t l2util_addr_from_str(const char *str, uint8_t *out); + #ifdef __cplusplus } #endif diff --git a/sys/net/link_layer/l2util/l2util.c b/sys/net/link_layer/l2util/l2util.c index 389c46c402..1a7c07bacb 100644 --- a/sys/net/link_layer/l2util/l2util.c +++ b/sys/net/link_layer/l2util/l2util.c @@ -16,6 +16,7 @@ #include #include "log.h" +#include "fmt.h" #include "net/eui48.h" #include "net/ieee802154.h" #include "net/ipv6.h" @@ -23,6 +24,22 @@ #include "net/l2util.h" +static inline int _dehex(char c, int default_) +{ + if ('0' <= c && c <= '9') { + return c - '0'; + } + else if ('A' <= c && c <= 'F') { + return c - 'A' + 10; + } + else if ('a' <= c && c <= 'f') { + return c - 'a' + 10; + } + else { + return default_; + } +} + #if defined(MODULE_CC110X) || defined(MODULE_NRFMIN) static void _create_eui64_from_short(const uint8_t *addr, size_t addr_len, eui64_t *eui64) @@ -261,4 +278,67 @@ int l2util_ipv6_group_to_l2_group(int dev_type, } } +char *l2util_addr_to_str(const uint8_t *addr, size_t addr_len, char *out) +{ + char *res = out; + + assert((out != NULL) && ((addr != NULL) || (addr_len == 0U))); + out[0] = '\0'; + for (size_t i = 0; i < addr_len; i++) { + out += fmt_byte_hex((out), *(addr++)); + *(out++) = (i == (addr_len - 1)) ? '\0' : ':'; + } + return res; +} + +size_t l2util_addr_from_str(const char *str, uint8_t *out) +{ + /* Walk over str from the end. */ + /* Take two chars a time as one hex value (%hhx). */ + /* Leading zeros can be omitted. */ + /* Every non-hexadimal character is a delimiter. */ + /* Leading, tailing and adjacent delimiters are forbidden. */ + const char *end_str = str; + uint8_t *out_end = out; + size_t count = 0; + int assert_cell = 1; + + assert(out != NULL); + if ((str == NULL) || (str[0] == '\0')) { + return 0; + } + /* find end of string */ + while (end_str[1]) { + ++end_str; + } + while (end_str >= str) { + int a = 0, b = _dehex(*end_str--, -1); + + if (b < 0) { + if (assert_cell) { + return 0; + } + else { + assert_cell = 1; + continue; + } + } + assert_cell = 0; + if (end_str >= str) { + a = _dehex(*end_str--, 0); + } + count++; + *out_end++ = (a << 4) | b; + } + if (assert_cell) { + return 0; + } + /* out is reversed */ + while (out < --out_end) { + uint8_t tmp = *out_end; + *out_end = *out; + *out++ = tmp; + } + return count; +} /** @} */ diff --git a/tests/l2util/Makefile.ci b/tests/l2util/Makefile.ci index 998fbf0184..75912a97e3 100644 --- a/tests/l2util/Makefile.ci +++ b/tests/l2util/Makefile.ci @@ -1,5 +1,6 @@ BOARD_INSUFFICIENT_MEMORY := \ arduino-duemilanove \ + arduino-leonardo \ arduino-nano \ arduino-uno \ atmega328p \ diff --git a/tests/l2util/common.h b/tests/l2util/common.h new file mode 100644 index 0000000000..87a37cbd22 --- /dev/null +++ b/tests/l2util/common.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2017 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup tests + * @brief Common definitions for GNRC's NIB tests + * @{ + * + * @file + * + * @author Martine Lenders + */ +#ifndef COMMON_H +#define COMMON_H + +#include "net/gnrc/netif.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define NETIF_NUMOF (4) +#define SPECIAL_DEVS (2) +#define DEFAULT_DEVS_NUMOF (NETIF_NUMOF - SPECIAL_DEVS) + +#define GP1 (0x20U) +#define GP2 (0x01U) +#define GP3 (0x0dU) +#define GP4 (0xb8U) +#define GP5 (0x00U) +#define GP6 (0x00U) +#define GP7 (0x5aU) +#define GP8 (0x1aU) + +#define LP1 (0xfeU) +#define LP2 (0x80U) +#define LP3 (0x00U) +#define LP4 (0x00U) +#define LP5 (0x00U) +#define LP6 (0x00U) +#define LP7 (0x00U) +#define LP8 (0x00U) + +#define LA1 (0x3eU) +#define LA2 (0xe6U) +#define LA3 (0xb5U) +#define LA4 (0x0fU) +#define LA5 (0x19U) +#define LA6 (0x22U) +#define LA7 (0xfdU) +#define LA8 (0x0aU) + +#define ULA1 (0xfdU) +#define ULA2 (0x01U) +#define ULA3 (0x0dU) +#define ULA4 (0xb8U) +#define ULA5 (0x00U) +#define ULA6 (0x00U) +#define ULA7 (0x5aU) +#define ULA8 (0x1aU) + +#define TEST_IEEE802154_MAX_FRAG_SIZE (102) + +#define ETHERNET_SRC { LA1, LA2, LA3, LA6, LA7, LA8 } +#define ETHERNET_IPV6_LL { LP1, LP2, LP3, LP4, LP5, LP6, LP7, LP8, \ + LA1 ^ 0x2, LA2, LA3, 0xff, 0xfe, LA6, LA7, LA8 } +#define ETHERNET_IPV6_G { GP1, GP2, GP3, GP4, GP5, GP6, GP7, GP8, \ + LA1 ^ 0x2, LA2, LA3, 0xff, 0xfe, LA6, LA7, LA8 } +#define IEEE802154_LONG_SRC { LA1, LA2, LA3, LA4, LA5, LA6, LA7, LA8 } +#define IEEE802154_SHORT_SRC { LA7, LA8 } +#define IEEE802154_IPV6_LL { LP1, LP2, LP3, LP4, LP5, LP6, LP7, LP8, \ + LA1 ^ 0x2, LA2, LA3, LA4, LA5, LA6, LA7, LA8 } +#define IEEE802154_IPV6_G { GP1, GP2, GP3, GP4, GP5, GP6, GP7, GP8, \ + LA1 ^ 0x2, LA2, LA3, LA4, LA5, LA6, LA7, LA8 } +#define NETIF0_SRC { LA1, LA2 + 1, LA3, LA4, LA5, LA6, LA7, LA8 } +#define NETIF0_IPV6_LL { LP1, LP2, LP3, LP4, LP5, LP6, LP7, LP8, \ + LA1 ^ 0x2, LA2 + 1, LA3, LA4, LA5, LA6, LA7, LA8 } +#define NETIF0_IPV6_ULA { ULA1, ULA2, ULA3, ULA4, ULA5, ULA6, ULA7, ULA8, \ + LA1 ^ 0x2, LA2 + 1, LA3, LA4, LA5, LA6, LA7, LA8 } +#define NETIF0_IPV6_G { GP1, GP2, GP3, GP4, GP5, GP6, GP7, GP8, \ + LA1 ^ 0x2, LA2 + 1, LA3, LA4, LA5, LA6, LA7, LA8 } +#define GLOBAL_PFX18 { GP1, GP2, GP3 ^ 0x3f, GP4, GP5, GP6, GP7, GP8, \ + LA1 ^ 0x2, LA2, LA3, LA4, LA5, LA6, LA7, LA8 } +#define GLOBAL_PFX23 { GP1, GP2, GP3 ^ 0x1, GP4, GP5, GP6, GP7, GP8, \ + LA1 ^ 0x2, LA2, LA3, LA4, LA5, LA6, LA7, LA8 } +#define GLOBAL_PFX64 { GP1, GP2, GP3, GP4, GP5, GP6, GP7, GP8, \ + LA1 ^ 0x82, LA2, LA3, LA4, LA5, LA6, LA7, LA8 } + +#ifdef __cplusplus +} +#endif + +#endif /* COMMON_H */ +/** @} */ diff --git a/tests/l2util/main.c b/tests/l2util/main.c index e06bf3bc0e..4eb3f49cfc 100644 --- a/tests/l2util/main.c +++ b/tests/l2util/main.c @@ -18,6 +18,7 @@ #include +#include "common.h" #include "embUnit.h" #include "net/eui48.h" #include "net/eui64.h" @@ -416,6 +417,56 @@ static void test_ipv6_group_to_l2group__ENOTSUP(void) &test_group, res)); } +static void test_l2util_addr_to_str(void) +{ + static const uint8_t ethernet_l2addr[] = ETHERNET_SRC; + static const uint8_t ieee802154_l2addr_long[] = IEEE802154_LONG_SRC; + static const uint8_t ieee802154_l2addr_short[] = IEEE802154_SHORT_SRC; + static const uint8_t netif0_l2addr[] = NETIF0_SRC; + char out[sizeof(netif0_l2addr) * 3]; + + TEST_ASSERT(out == l2util_addr_to_str(NULL, 0, out)); + TEST_ASSERT_EQUAL_STRING("", &out[0]); + TEST_ASSERT(out == l2util_addr_to_str(ethernet_l2addr, + sizeof(ethernet_l2addr), out)); + TEST_ASSERT_EQUAL_STRING("3E:E6:B5:22:FD:0A", &out[0]); + TEST_ASSERT(out == l2util_addr_to_str(ieee802154_l2addr_long, + sizeof(ieee802154_l2addr_long), + out)); + TEST_ASSERT_EQUAL_STRING("3E:E6:B5:0F:19:22:FD:0A", &out[0]); + TEST_ASSERT(out == l2util_addr_to_str(ieee802154_l2addr_short, + sizeof(ieee802154_l2addr_short), + out)); + TEST_ASSERT_EQUAL_STRING("FD:0A", &out[0]); + TEST_ASSERT(out == l2util_addr_to_str(netif0_l2addr, + sizeof(netif0_l2addr), + out)); + TEST_ASSERT_EQUAL_STRING("3E:E7:B5:0F:19:22:FD:0A", &out[0]); +} + +static void test_l2util_addr_from_str(void) +{ + static const uint8_t ethernet_l2addr[] = ETHERNET_SRC; + static const uint8_t ieee802154_l2addr_long[] = IEEE802154_LONG_SRC; + static const uint8_t ieee802154_l2addr_short[] = IEEE802154_SHORT_SRC; + uint8_t out[GNRC_NETIF_L2ADDR_MAXLEN]; + + TEST_ASSERT_EQUAL_INT(0, l2util_addr_from_str("", out)); + TEST_ASSERT_EQUAL_INT(sizeof(ethernet_l2addr), + l2util_addr_from_str("3E:E6:B5:22:FD:0A", out)); + TEST_ASSERT_EQUAL_INT(0, memcmp(ethernet_l2addr, out, + sizeof(ethernet_l2addr))); + TEST_ASSERT_EQUAL_INT(sizeof(ieee802154_l2addr_long), + l2util_addr_from_str("3E:E6:B5:0F:19:22:FD:0A", + out)); + TEST_ASSERT_EQUAL_INT(0, memcmp(ieee802154_l2addr_long, out, + sizeof(ieee802154_l2addr_long))); + TEST_ASSERT_EQUAL_INT(sizeof(ieee802154_l2addr_short), + l2util_addr_from_str("FD:0A", out)); + TEST_ASSERT_EQUAL_INT(0, memcmp(ieee802154_l2addr_short, out, + sizeof(ieee802154_l2addr_short))); +} + TestRef test_l2util(void) { EMB_UNIT_TESTFIXTURES(fixtures) { @@ -432,6 +483,8 @@ TestRef test_l2util(void) new_TestFixture(test_addr_len_from_l2ao__ENOTSUP), new_TestFixture(test_ipv6_group_to_l2group__success), new_TestFixture(test_ipv6_group_to_l2group__ENOTSUP), + new_TestFixture(test_l2util_addr_to_str), + new_TestFixture(test_l2util_addr_from_str), }; EMB_UNIT_TESTCALLER(tests_l2util, NULL, NULL, fixtures);