diff --git a/Makefile.dep b/Makefile.dep index 5e5872a9e7..d239528363 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -146,6 +146,7 @@ endif ifneq (,$(filter gnrc_netif,$(USEMODULE))) USEMODULE += netif + USEMODULE += l2util USEMODULE += fmt ifneq (,$(filter netdev_ieee802154,$(USEMODULE))) USEMODULE += gnrc_netif_ieee802154 diff --git a/sys/Makefile b/sys/Makefile index 5a7f2c340b..ea7b11e3f8 100644 --- a/sys/Makefile +++ b/sys/Makefile @@ -121,6 +121,9 @@ endif ifneq (,$(filter l2filter,$(USEMODULE))) DIRS += net/link_layer/l2filter endif +ifneq (,$(filter l2util,$(USEMODULE))) + DIRS += net/link_layer/l2util +endif ifneq (,$(filter nanocoap,$(USEMODULE))) DIRS += net/application_layer/nanocoap endif diff --git a/sys/include/net/l2util.h b/sys/include/net/l2util.h new file mode 100644 index 0000000000..e5030b7291 --- /dev/null +++ b/sys/include/net/l2util.h @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2018 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. + */ + +/** + * @defgroup net_l2util Stack-independent helpers for IPv6 over X + * @ingroup net + * @brief This implements some common helper functions for IPv6 over X + * implementations based on [network device types] + * (@ref net_netdev_type). + * + * @attention If you add a new [network device type](@ref net_netdev_type) + * have at least a look at the implementation of these functions. + * @{ + * + * @file + * @brief Link-layer helper function definitions + * + * @author Martine Lenders + */ +#ifndef NET_L2UTIL_H +#define NET_L2UTIL_H + +#include + +#include "net/eui64.h" +#include "net/ndp.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define L2UTIL_ADDR_MAX_LEN (8U) /**< maximum expected length for addresses */ + +/** + * @brief Converts a given hardware address to an EUI-64. + * + * @attention When the link-layer of the interface has link-layer addresses, and + * `NDEBUG` is not defined, the node fails with an assertion instead + * returning `-ENOTSUP`. + * + * @param[in] dev_type The network device type of the device @p addr came from + * (either because it is the configured address of the + * device or from a packet that came over it). + * @param[in] addr A hardware address. + * @param[in] addr_len Number of bytes in @p addr. + * @param[out] eui64 The EUI-64 based on gnrc_netif_t::device_type + * + * @return `sizeof(eui64_t)` on success. + * @return `-ENOTSUP`, when @p dev_type does not support EUI-64 conversion. + * @return `-EINVAL`, when @p addr_len is invalid for the @p dev_type. + */ +int l2util_eui64_from_addr(int dev_type, const uint8_t *addr, size_t addr_len, + eui64_t *eui64); + +/** + * @brief Converts a given hardware address to an IPv6 IID. + * + * @attention When the link-layer of the interface has link-layer addresses, and + * `NDEBUG` is not defined, the node fails with an assertion instead + * returning `-ENOTSUP`. + * + * @param[in] dev_type The network device type of the device @p addr came from + * (either because it is the configured address of the + * device or from a packet that came over it). + * @param[in] addr A hardware address. + * @param[in] addr_len Number of bytes in @p addr. + * @param[out] iid The IID based on gnrc_netif_t::device_type + * + * @return `sizeof(eui64_t)` on success. + * @return `-ENOTSUP`, when @p dev_type does not support IID conversion. + * @return `-EINVAL`, when @p addr_len is invalid for the @p dev_type. + */ +int l2util_ipv6_iid_from_addr(int dev_type, + const uint8_t *addr, size_t addr_len, + eui64_t *iid); + +/** + * @brief Converts an IPv6 IID to a hardware address + * + * @pre @p iid was based on a hardware address + * @pre The number of bytes available at @p addr is less or equal to + * @ref L2UTIL_ADDR_MAX_LEN. + * + * @attention When `NDEBUG` is not defined, the node fails with an assertion + * instead of returning `-ENOTSUP` + * + * @param[in] dev_type The network device type of the device the @p iid came + * from (either because it is based on the configured + * address of the device or from a packet that came over + * it). + * @param[in] iid An IID based on @p dev_type. + * @param[out] addr The hardware address. It is assumed that @p iid was + * based on a hardware address and that the available + * number of bytes in @p addr are greater or equal to + * @ref L2UTIL_ADDR_MAX_LEN. + * + * @return Length of resulting @p addr on success. + * @return `-ENOTSUP`, when @p dev_type does not support reverse IID + * conversion. + */ +int l2util_ipv6_iid_to_addr(int dev_type, const eui64_t *iid, uint8_t *addr); + +/** + * @brief Derives the length of the link-layer address in an NDP link-layer + * address option from that option's length field and the given device + * type. + * + * @note If an RFC exists that specifies how IPv6 operates over a link-layer, + * this function usually implements the section "Unicast Address + * Mapping". + * + * @see [RFC 4861, section 4.6.1](https://tools.ietf.org/html/rfc4861#section-4.6.1) + * + * @attention When `NDEBUG` is not defined, the node fails with an assertion + * instead of returning `-ENOTSUP` + * + * @param[in] dev_type The network device type of the device the @p opt came + * over in an NDP message. + * @param[in] opt An NDP source/target link-layer address option. + * + * @return Length of the link-layer address in @p opt on success + * @return `-ENOTSUP`, when implementation does not know how to derive the + * length of the link-layer address from @p opt's length field based + * on @p dev_type. + * @return `-EINVAL` if `opt->len` was an invalid value for the given + * @p dev_type. + */ +int l2util_ndp_addr_len_from_l2ao(int dev_type, + const ndp_opt_t *opt); + + +#ifdef __cplusplus +} +#endif + +#endif /* NET_L2UTIL_H */ +/** @} */ diff --git a/sys/net/link_layer/l2util/Makefile b/sys/net/link_layer/l2util/Makefile new file mode 100644 index 0000000000..48422e909a --- /dev/null +++ b/sys/net/link_layer/l2util/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/sys/net/link_layer/l2util/l2util.c b/sys/net/link_layer/l2util/l2util.c new file mode 100644 index 0000000000..8beec94584 --- /dev/null +++ b/sys/net/link_layer/l2util/l2util.c @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2018 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. + */ + +/** + * @{ + * + * @file + * @author Martine Lenders + */ + +#include + +#include "log.h" +#include "net/eui48.h" +#include "net/ieee802154.h" +#include "net/ipv6.h" +#include "net/netdev.h" + +#include "net/l2util.h" + +#if defined(MODULE_CC110X) || defined(MODULE_NRFMIN) +static void _create_eui64_from_short(const uint8_t *addr, size_t addr_len, + eui64_t *eui64) +{ + const unsigned offset = sizeof(eui64_t) - addr_len; + + memset(eui64->uint8, 0, sizeof(eui64->uint8)); + eui64->uint8[3] = 0xff; + eui64->uint8[4] = 0xfe; + memcpy(&eui64->uint8[offset], addr, addr_len); +} +#endif /* defined(MODULE_CC110X) || defined(MODULE_NRFMIN) */ + +int l2util_eui64_from_addr(int dev_type, const uint8_t *addr, size_t addr_len, + eui64_t *eui64) +{ + switch (dev_type) { +#if defined(MODULE_NETDEV_ETH) || defined(MODULE_ESP_NOW) || \ +defined(MODULE_NORDIC_SOFTDEVICE_BLE) + case NETDEV_TYPE_ETHERNET: + case NETDEV_TYPE_ESP_NOW: + case NETDEV_TYPE_BLE: + if (addr_len == sizeof(eui48_t)) { + eui48_to_eui64(eui64, (const eui48_t *)addr); + return sizeof(eui64_t); + } + else { + return -EINVAL; + } +#endif /* defined(MODULE_NETDEV_ETH) || defined(MODULE_ESP_NOW) */ +#if defined(MODULE_NETDEV_IEEE802154) || defined(MODULE_XBEE) + case NETDEV_TYPE_IEEE802154: + switch (addr_len) { + /* EUI-64 can *not* be generated from the short address */ + case IEEE802154_LONG_ADDRESS_LEN: + memcpy(eui64, addr, addr_len); + return sizeof(eui64_t); + default: + return -EINVAL; + } +#endif /* defined(MODULE_NETDEV_IEEE802154) || defined(MODULE_XBEE) */ +#if defined(MODULE_CC110X) || defined(MODULE_NRFMIN) + case NETDEV_TYPE_CC110X: + case NETDEV_TYPE_NRFMIN: + if (addr_len <= 3) { + _create_eui64_from_short(addr, addr_len, eui64); + return sizeof(eui64_t); + } + else { + return -EINVAL; + } +#endif /* defined(MODULE_CC110X) || defined(MODULE_NRFMIN) */ + default: + (void)addr; + (void)addr_len; + (void)eui64; +#ifdef DEVELHELP + LOG_ERROR("l2util: can't convert hardware address to EUI-64 " + "for device type %d\n", dev_type); +#endif /* DEVELHELP */ + assert(false); + break; + } + return -ENOTSUP; +} + +int l2util_ipv6_iid_from_addr(int dev_type, + const uint8_t *addr, size_t addr_len, + eui64_t *iid) +{ + switch (dev_type) { +#if defined(MODULE_NETDEV_IEEE802154) || defined(MODULE_XBEE) + case NETDEV_TYPE_IEEE802154: + if (ieee802154_get_iid(iid, addr, addr_len) != NULL) { + return sizeof(eui64_t); + } + else { + return -EINVAL; + } +#endif /* defined(MODULE_NETDEV_IEEE802154) || defined(MODULE_XBEE) */ +#if defined(MODULE_CC110X) || defined(MODULE_NRFMIN) + case NETDEV_TYPE_CC110X: + case NETDEV_TYPE_NRFMIN: + if (addr_len <= 3) { + _create_eui64_from_short(addr, addr_len, iid); + /* since this address conversion is based on the IEEE + * 802.15.4 address conversion for short addresses, the + * U/L bit doesn't need to be flipped. + * see https://tools.ietf.org/html/rfc6282#section-3.2.2 */ + return sizeof(eui64_t); + } + else { + return -EINVAL; + } +#endif /* defined(MODULE_CC110X) || defined(MODULE_NRFMIN) */ + default: { + int res = l2util_eui64_from_addr(dev_type, addr, addr_len, iid); + if (res == sizeof(eui64_t)) { + iid->uint8[0] ^= 0x02; + } + return res; + } + } + return -ENOTSUP; +} + +int l2util_ipv6_iid_to_addr(int dev_type, const eui64_t *iid, uint8_t *addr) +{ + switch (dev_type) { +#if defined(MODULE_NETDEV_ETH) || defined(MODULE_ESP_NOW) || \ + defined(MODULE_NORDIC_SOFTDEVICE_BLE) + case NETDEV_TYPE_ETHERNET: + case NETDEV_TYPE_ESP_NOW: + case NETDEV_TYPE_BLE: + eui48_from_ipv6_iid((eui48_t *)addr, iid); + return sizeof(eui48_t); +#endif /* defined(MODULE_NETDEV_ETH) || defined(MODULE_ESP_NOW) || \ + * defined(MODULE_NORDIC_SOFTDEVICE_BLE) */ +#if defined(MODULE_NETDEV_IEEE802154) || defined(MODULE_XBEE) + case NETDEV_TYPE_IEEE802154: + /* assume address was based on EUI-64 + * (see https://tools.ietf.org/html/rfc6775#section-5.2) */ + memcpy(addr, iid, sizeof(eui64_t)); + addr[0] ^= 0x02; + return sizeof(eui64_t); +#endif /* defined(MODULE_NETDEV_IEEE802154) || defined(MODULE_XBEE) */ +#ifdef MODULE_NRFMIN + case NETDEV_TYPE_NRFMIN: + addr[0] = iid->uint8[6]; + addr[1] = iid->uint8[7]; + return sizeof(uint16_t); +#endif /* MODULE_NETDEV_IEEE802154 */ +#ifdef MODULE_CC110X + case NETDEV_TYPE_CC110X: + addr[0] = iid->uint8[7]; + return sizeof(uint8_t); +#endif /* MODULE_CC110X */ + default: + (void)iid; + (void)addr; +#ifdef DEVELHELP + LOG_ERROR("l2util: can't convert IID to hardware address for " + "device type %d\n", dev_type); +#endif /* DEVELHELP */ + assert(false); + break; + } + return -ENOTSUP; +} + +int l2util_ndp_addr_len_from_l2ao(int dev_type, + const ndp_opt_t *opt) +{ + switch (dev_type) { +#ifdef MODULE_CC110X + case NETDEV_TYPE_CC110X: + (void)opt; + return sizeof(uint8_t); +#endif /* MODULE_CC110X */ +#if defined(MODULE_NETDEV_ETH) || defined(MODULE_ESP_NOW) || \ + defined(MODULE_NORDIC_SOFTDEVICE_BLE) + case NETDEV_TYPE_ETHERNET: + case NETDEV_TYPE_ESP_NOW: + case NETDEV_TYPE_BLE: + /* see https://tools.ietf.org/html/rfc2464#section-6*/ + if (opt->len == 1U) { + return sizeof(eui48_t); + } + else { + return -EINVAL; + } +#endif /* defined(MODULE_NETDEV_ETH) || defined(MODULE_ESP_NOW) */ +#ifdef MODULE_NRFMIN + case NETDEV_TYPE_NRFMIN: + (void)opt; + return sizeof(uint16_t); +#endif /* MODULE_NRFMIN */ +#if defined(MODULE_NETDEV_IEEE802154) || defined(MODULE_XBEE) + case NETDEV_TYPE_IEEE802154: + /* see https://tools.ietf.org/html/rfc4944#section-8 */ + switch (opt->len) { + case 1U: + return IEEE802154_SHORT_ADDRESS_LEN; + case 2U: + return IEEE802154_LONG_ADDRESS_LEN; + default: + return -EINVAL; + } +#endif /* defined(MODULE_NETDEV_IEEE802154) || defined(MODULE_XBEE) */ + default: + (void)opt; +#ifdef DEVELHELP + LOG_ERROR("l2util: can't get address length from NDP link-layer " + "address option for device type %d\n", dev_type); +#endif + assert(false); + break; + } + return -ENOTSUP; +} + + +/** @} */