diff --git a/sys/include/luid.h b/sys/include/luid.h index 5ad2857bff..b647958b44 100644 --- a/sys/include/luid.h +++ b/sys/include/luid.h @@ -55,6 +55,9 @@ #include +#include "net/eui48.h" +#include "net/eui64.h" + #ifdef __cplusplus extern "C" { #endif @@ -82,6 +85,43 @@ extern "C" { */ void luid_get(void *buf, size_t len); +/** + * @brief Get a unique short unicast address + * + * The resulting address is built from the base ID generated with luid_base(), which + * isXORed with an 8-bit incrementing counter value into the least significant + * byte. + * + * @note The resulting address will repeat after 255 calls. + * + * @param[out] addr memory location to copy the address into. + */ +void luid_get_short(network_uint16_t *addr); + +/** + * @brief Get a unique EUI48 address + * + * The resulting address is built from the base ID generated with luid_base(), which + * isXORed with an 8-bit incrementing counter value into the least significant byte. + * + * @note The resulting address will repeat after 255 calls. + * + * @param[out] addr memory location to copy the address into. + */ +void luid_get_eui48(eui48_t *addr); + +/** + * @brief Get a unique EUI64 address + * + * The resulting address is built from the base ID generated with luid_base(), which + * isXORed with an 8-bit incrementing counter value into the least significant byte. + * + * @note The resulting address will repeat after 255 calls. + * + * @param[out] addr memory location to copy the address into. + */ +void luid_get_eui64(eui64_t *addr); + /** * @brief Get a custom unique ID based on a user given generator value * diff --git a/sys/include/net/eui64.h b/sys/include/net/eui64.h index a8fd090de5..6211d20a5a 100644 --- a/sys/include/net/eui64.h +++ b/sys/include/net/eui64.h @@ -31,6 +31,24 @@ extern "C" { #endif +/** + * @name EUI-64 bit flags contained in the first octet + * + * @see IEEE 802-2001 section 9.2 + * @{ + */ + +/** + * @brief Locally administered address. + */ +#define EUI64_LOCAL_FLAG 0x02 + +/** + * @brief Group type address. + */ +#define EUI64_GROUP_FLAG 0x01 +/** @} */ + /** * @brief Data type to represent an EUI-64. */ @@ -40,6 +58,31 @@ typedef union { network_uint16_t uint16[4]; /**< split into 4 16-bit words. */ } eui64_t; +/** + * @brief Set the locally administrated bit in the EUI-64 address. + * + * @see IEEE 802-2001 section 9.2 + * + * @param addr eui64 address + */ +static inline void eui64_set_local(eui64_t *addr) +{ + addr->uint8[0] |= EUI64_LOCAL_FLAG; +} + +/** + * @brief Clear the group address bit to signal the address as individual + * address + * + * @see IEEE 802-2001 section 9.2 + * + * @param addr eui64 address + */ +static inline void eui64_clear_group(eui64_t *addr) +{ + addr->uint8[0] &= ~EUI64_GROUP_FLAG; +} + #ifdef __cplusplus } #endif diff --git a/sys/luid/luid.c b/sys/luid/luid.c index 1b7a876ce1..544eeb1c3f 100644 --- a/sys/luid/luid.c +++ b/sys/luid/luid.c @@ -26,7 +26,26 @@ #include "luid.h" -static uint8_t lastused = 1; +#ifndef MAX +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#endif + +void __attribute__((weak)) luid_base(void *buf, size_t len) +{ + memset(buf, LUID_BACKUP_SEED, len); + +#if CPUID_LEN + uint8_t *out = (uint8_t *)buf; + uint8_t cid[CPUID_LEN]; + + cpuid_get(cid); + for (size_t i = 0; i < MAX(len, CPUID_LEN); i++) { + out[i % len] ^= cid[i % CPUID_LEN]; + } +#endif +} + +static uint8_t lastused; void luid_get(void *buf, size_t len) { @@ -44,19 +63,30 @@ void luid_custom(void *buf, size_t len, int gen) } } -void luid_base(void *buf, size_t len) +void luid_get_short(network_uint16_t *addr) { - assert(buf && (len > 0)); + luid_base(addr, sizeof(*addr)); + addr->u8[1] ^= lastused++; - memset(buf, LUID_BACKUP_SEED, len); - -#if CPUID_LEN - uint8_t *out = (uint8_t *)buf; - uint8_t cid[CPUID_LEN]; - - cpuid_get(cid); - for (size_t i = 0; i < CPUID_LEN; i++) { - out[i % len] ^= cid[i]; - } -#endif + /* https://tools.ietf.org/html/rfc4944#section-12 requires the first bit to + * 0 for unicast addresses */ + addr->u8[0] &= 0x7F; +} + +void luid_get_eui48(eui48_t *addr) +{ + luid_base(addr, sizeof(*addr)); + addr->uint8[5] ^= lastused++; + + eui48_set_local(addr); + eui48_clear_group(addr); +} + +void luid_get_eui64(eui64_t *addr) +{ + luid_base(addr, sizeof(*addr)); + addr->uint8[7] ^= lastused++; + + eui64_set_local(addr); + eui64_clear_group(addr); } diff --git a/tests/unittests/tests-luid/Makefile b/tests/unittests/tests-luid/Makefile new file mode 100644 index 0000000000..48422e909a --- /dev/null +++ b/tests/unittests/tests-luid/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/tests/unittests/tests-luid/Makefile.include b/tests/unittests/tests-luid/Makefile.include new file mode 100644 index 0000000000..dd2d79e221 --- /dev/null +++ b/tests/unittests/tests-luid/Makefile.include @@ -0,0 +1 @@ +USEMODULE += luid diff --git a/tests/unittests/tests-luid/tests-luid.c b/tests/unittests/tests-luid/tests-luid.c new file mode 100644 index 0000000000..08af1de74b --- /dev/null +++ b/tests/unittests/tests-luid/tests-luid.c @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2019 ML!PA Consulting GmbH + * + * 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 +#include +#include + +#include "tests-luid.h" + +static void test_luid_uniqe_eui64(void) +{ + eui64_t mac[3]; + + luid_get_eui64(&mac[0]); + luid_get_eui64(&mac[1]); + luid_get_eui64(&mac[2]); + TEST_ASSERT_EQUAL_INT(0, !memcmp(&mac[0], &mac[1], sizeof(mac[0]))); + TEST_ASSERT_EQUAL_INT(0, !memcmp(&mac[1], &mac[2], sizeof(mac[1]))); +} + +static void test_luid_uniqe_eui48(void) +{ + eui48_t mac[3]; + + luid_get_eui48(&mac[0]); + luid_get_eui48(&mac[1]); + luid_get_eui48(&mac[2]); + TEST_ASSERT_EQUAL_INT(0, !memcmp(&mac[0], &mac[1], sizeof(mac[0]))); + TEST_ASSERT_EQUAL_INT(0, !memcmp(&mac[1], &mac[2], sizeof(mac[1]))); +} + +static void test_luid_custom(void) +{ + uint8_t a[2][8]; + uint8_t b[2][8]; + + luid_custom(a[0], sizeof(a[0]), 0xfefe); + luid_custom(a[1], sizeof(a[1]), 0xfefe); + luid_custom(b[0], sizeof(b[0]), 0xbeef); + luid_custom(b[1], sizeof(b[1]), 0xbeef); + + TEST_ASSERT_EQUAL_INT(0, !memcmp(a[0], b[0], sizeof(a[0]))); + TEST_ASSERT_EQUAL_INT(0, !memcmp(a[1], b[1], sizeof(a[1]))); + TEST_ASSERT_EQUAL_INT(0, memcmp(a[0], a[0], sizeof(a[0]))); + TEST_ASSERT_EQUAL_INT(0, memcmp(b[1], b[1], sizeof(b[0]))); +} + +Test *tests_luid_tests(void) +{ + EMB_UNIT_TESTFIXTURES(fixtures) { + new_TestFixture(test_luid_uniqe_eui48), + new_TestFixture(test_luid_uniqe_eui64), + new_TestFixture(test_luid_custom), + }; + + EMB_UNIT_TESTCALLER(luid_tests, NULL, NULL, fixtures); + + return (Test *)&luid_tests; +} + +void tests_luid(void) +{ + TESTS_RUN(tests_luid_tests()); +} diff --git a/tests/unittests/tests-luid/tests-luid.h b/tests/unittests/tests-luid/tests-luid.h new file mode 100644 index 0000000000..0de07ccc3f --- /dev/null +++ b/tests/unittests/tests-luid/tests-luid.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2019 ML!PA Consulting GmbH + * + * 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 ``luid`` module + * + * @author Benjamin Valentin + */ +#ifndef TESTS_LUID_H +#define TESTS_LUID_H + +#include "embUnit.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief The entry point of this test suite. + */ +void tests_luid(void); + +/** + * @brief Generates tests for luid + * + * @return embUnit tests if successful, NULL if not. + */ +Test *tests_luid_tests(void); + +#ifdef __cplusplus +} +#endif + +#endif /* TESTS_LUID_H */ +/** @} */