diff --git a/Makefile.dep b/Makefile.dep index a728401918..5fe8f4edd7 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/drivers/include/net/netdev.h b/drivers/include/net/netdev.h index c94c786e89..fdfb63cb82 100644 --- a/drivers/include/net/netdev.h +++ b/drivers/include/net/netdev.h @@ -204,6 +204,13 @@ extern "C" { #include "net/l2filter.h" #endif +/** + * @name Network device types + * @anchor net_netdev_type + * @attention When implementing a new type that is able to carry IPv6, have + * a look if you need to update @ref net_l2util as well. + * @{ + */ enum { NETDEV_TYPE_UNKNOWN, NETDEV_TYPE_TEST, @@ -217,6 +224,7 @@ enum { NETDEV_TYPE_SLIP, NETDEV_TYPE_ESP_NOW, }; +/** @} */ /** * @brief Possible event types that are send from the device driver to the diff --git a/examples/gnrc_networking/Makefile b/examples/gnrc_networking/Makefile index 78a9eb7f56..fe82f8ad1a 100644 --- a/examples/gnrc_networking/Makefile +++ b/examples/gnrc_networking/Makefile @@ -8,9 +8,9 @@ BOARD ?= native RIOTBASE ?= $(CURDIR)/../.. BOARD_INSUFFICIENT_MEMORY := arduino-duemilanove arduino-mega2560 arduino-nano \ - arduino-uno calliope-mini chronos hifive1 \ - mega-xplained microbit msb-430 msb-430h \ - nucleo-f031k6 nucleo-f042k6 nucleo-f303k8 \ + arduino-uno blackpill bluepill calliope-mini \ + chronos hifive1 mega-xplained microbit msb-430 \ + msb-430h nucleo-f031k6 nucleo-f042k6 nucleo-f303k8 \ nucleo-l031k6 nucleo-f030r8 nucleo-f070rb \ nucleo-f072rb nucleo-f103rb nucleo-f302r8 \ nucleo-f334r8 nucleo-l053r8 saml10-xpro \ diff --git a/sys/Makefile b/sys/Makefile index bc51a950e1..308103ba94 100644 --- a/sys/Makefile +++ b/sys/Makefile @@ -124,6 +124,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/gnrc/netif/internal.h b/sys/include/net/gnrc/netif/internal.h index 2b07074613..fcbd6ec50d 100644 --- a/sys/include/net/gnrc/netif/internal.h +++ b/sys/include/net/gnrc/netif/internal.h @@ -22,6 +22,7 @@ #define NET_GNRC_NETIF_INTERNAL_H #include "net/gnrc/netif.h" +#include "net/l2util.h" #include "net/netopt.h" #ifdef MODULE_GNRC_IPV6_NIB @@ -539,8 +540,12 @@ int gnrc_netif_ipv6_iid_from_addr(const gnrc_netif_t *netif, * @return `-ENOTSUP`, when gnrc_netif_t::device_type of @p netif does not * support reverse IID conversion. */ -int gnrc_netif_ipv6_iid_to_addr(const gnrc_netif_t *netif, const eui64_t *iid, - uint8_t *addr); +static inline int gnrc_netif_ipv6_iid_to_addr(const gnrc_netif_t *netif, + const eui64_t *iid, uint8_t *addr) +{ + assert(netif->flags & GNRC_NETIF_FLAGS_HAS_L2ADDR); + return l2util_ipv6_iid_to_addr(netif->device_type, iid, addr); +} /** * @brief Converts an interface IID of an interface's hardware address @@ -602,8 +607,12 @@ static inline int gnrc_netif_ipv6_get_iid(gnrc_netif_t *netif, eui64_t *iid) * @return `-EINVAL` if `opt->len` was an invalid value for the given * gnrc_netif_t::device_type of @p netif. */ -int gnrc_netif_ndp_addr_len_from_l2ao(gnrc_netif_t *netif, - const ndp_opt_t *opt); +static inline int gnrc_netif_ndp_addr_len_from_l2ao(gnrc_netif_t *netif, + const ndp_opt_t *opt) +{ + assert(netif->flags & GNRC_NETIF_FLAGS_HAS_L2ADDR); + return l2util_ndp_addr_len_from_l2ao(netif->device_type, opt); +} #else /* defined(MODULE_GNRC_IPV6) || defined(DOXYGEN) */ #define gnrc_netif_ipv6_init_mtu(netif) (void)netif #define gnrc_netif_ipv6_iid_from_addr(netif, addr, addr_len, iid) (-ENOTSUP) 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/gnrc/netif/gnrc_netif_device_type.c b/sys/net/gnrc/netif/gnrc_netif_device_type.c index b4575b2094..119ba8fe66 100644 --- a/sys/net/gnrc/netif/gnrc_netif_device_type.c +++ b/sys/net/gnrc/netif/gnrc_netif_device_type.c @@ -24,6 +24,7 @@ #include "net/eui48.h" #include "net/ethernet.h" #include "net/ieee802154.h" +#include "net/l2util.h" netopt_t gnrc_netif_get_l2addr_opt(const gnrc_netif_t *netif) { @@ -54,19 +55,6 @@ netopt_t gnrc_netif_get_l2addr_opt(const gnrc_netif_t *netif) return res; } -#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 gnrc_netif_eui64_from_addr(const gnrc_netif_t *netif, const uint8_t *addr, size_t addr_len, eui64_t *eui64) @@ -74,55 +62,24 @@ int gnrc_netif_eui64_from_addr(const gnrc_netif_t *netif, #if GNRC_NETIF_L2ADDR_MAXLEN > 0 if (netif->flags & GNRC_NETIF_FLAGS_HAS_L2ADDR) { switch (netif->device_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: + /* try getting EUI-64 from device if short address is + * provided */ switch (addr_len) { case IEEE802154_SHORT_ADDRESS_LEN: { netdev_t *dev = netif->dev; return dev->driver->get(dev, NETOPT_ADDRESS_LONG, eui64, sizeof(eui64_t)); } - case IEEE802154_LONG_ADDRESS_LEN: - memcpy(eui64, addr, addr_len); - return sizeof(eui64_t); default: - return -EINVAL; + break; } + /* Intentionally falls through */ #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("gnrc_netif: can't convert hardware address to EUI-64" - " on interface %u\n", netif->pid); -#endif /* DEVELHELP */ - assert(false); - break; + return l2util_eui64_from_addr(netif->device_type, addr, + addr_len, eui64); } } #endif /* GNRC_NETIF_L2ADDR_MAXLEN > 0 */ @@ -204,141 +161,13 @@ int gnrc_netif_ipv6_iid_from_addr(const gnrc_netif_t *netif, { #if GNRC_NETIF_L2ADDR_MAXLEN > 0 if (netif->flags & GNRC_NETIF_FLAGS_HAS_L2ADDR) { - switch (netif->device_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 = gnrc_netif_eui64_from_addr(netif, addr, addr_len, - iid); - if (res == sizeof(eui64_t)) { - iid->uint8[0] ^= 0x02; - } - return res; - } - } + return l2util_ipv6_iid_from_addr(netif->device_type, + addr, addr_len, iid); } #endif /* GNRC_NETIF_L2ADDR_MAXLEN > 0 */ return -ENOTSUP; } -int gnrc_netif_ipv6_iid_to_addr(const gnrc_netif_t *netif, const eui64_t *iid, - uint8_t *addr) -{ - assert(netif->flags & GNRC_NETIF_FLAGS_HAS_L2ADDR); - switch (netif->device_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) */ -#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("gnrc_netif: can't convert IID to hardware address " - "on interface %u\n", netif->pid); -#endif /* DEVELHELP */ - assert(false); - break; - } - return -ENOTSUP; -} - -int gnrc_netif_ndp_addr_len_from_l2ao(gnrc_netif_t *netif, - const ndp_opt_t *opt) -{ - assert(netif->flags & GNRC_NETIF_FLAGS_HAS_L2ADDR); - switch (netif->device_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 ETHERNET_ADDR_LEN; - } - 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("gnrc_netif: can't get address length from NDP link-layer " - "address option on interface %u\n", netif->pid); -#endif - assert(false); - break; - } - return -ENOTSUP; -} #endif /* MODULE_GNRC_IPV6 */ /** @} */ 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; +} + + +/** @} */ diff --git a/tests/l2util/Makefile b/tests/l2util/Makefile new file mode 100644 index 0000000000..2e05429822 --- /dev/null +++ b/tests/l2util/Makefile @@ -0,0 +1,25 @@ +DEVELHELP = 0 + +include ../Makefile.tests_common + +BOARD_INSUFFICIENT_MEMORY := blackpill bluepill + +USEMODULE += embunit +USEMODULE += l2util + +# defining the "module" path defines this way in the hopes to make it more +#clearer. +CHECKED_IFDEF_PATHS = cc110x \ + esp_now \ + netdev_eth \ + netdev_ieee802154 \ + xbee \ + nordic_softdevice_ble \ + nrfmin + +CFLAGS += $(foreach path,$(CHECKED_IFDEF_PATHS),\ + -DMODULE_$(shell echo $(path) | tr a-z A-Z)) + +TEST_ON_CI_WHITELIST += all + +include $(RIOTBASE)/Makefile.include diff --git a/tests/l2util/main.c b/tests/l2util/main.c new file mode 100644 index 0000000000..f2ed346a21 --- /dev/null +++ b/tests/l2util/main.c @@ -0,0 +1,407 @@ +/* + * 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. + */ + + /** + * @ingroup tests + * @{ + * + * @file + * @brief Unit tests for l2util + * + * @author Martine Lenders + */ + +#include + +#include "embUnit.h" +#include "net/eui48.h" +#include "net/eui64.h" +#include "net/ieee802154.h" +#include "net/netdev.h" + +#include "net/l2util.h" + +#define TEST_ADDR { 0x21, 0x55, 0x31, 0x02, 0x41, 0xfd, 0xfb, 0xfd } +#define TEST_802154_S_IID { 0x00, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x21, 0x55 } +#define TEST_CC110X_IID { 0x00, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x00, 0x21 } +#define TEST_EUI48_EUI64 { 0x21, 0x55, 0x31, 0xff, 0xfe, 0x02, 0x41, 0xfd } +#define TEST_EUI48_IID { 0x23, 0x55, 0x31, 0xff, 0xfe, 0x02, 0x41, 0xfd } +#define TEST_EUI64_IID { 0x23, 0x55, 0x31, 0x02, 0x41, 0xfd, 0xfb, 0xfd } + +static void test_eui64_from_addr__success(void) +{ + static const uint8_t test_addr[L2UTIL_ADDR_MAX_LEN] = TEST_ADDR; + static const eui64_t test_802154_s = { .uint8 = TEST_802154_S_IID }; + static const eui64_t test_cc110x = { .uint8 = TEST_CC110X_IID }; + static const eui64_t test_eui48 = { .uint8 = TEST_EUI48_EUI64 }; + static const eui64_t test_eui64 = { .uint8 = TEST_ADDR }; + eui64_t res; + + /* test Ethernet */ + res.uint64.u64 = 0; + TEST_ASSERT_EQUAL_INT(sizeof(eui64_t), + l2util_eui64_from_addr(NETDEV_TYPE_ETHERNET, + test_addr, sizeof(eui48_t), + &res)); + TEST_ASSERT_EQUAL_INT(0, memcmp(&test_eui48, &res, sizeof(eui64_t))); + /* test IEEE 802.15.4 */ + res.uint64.u64 = 0; + TEST_ASSERT_EQUAL_INT(sizeof(eui64_t), + l2util_eui64_from_addr(NETDEV_TYPE_IEEE802154, + test_addr, sizeof(eui64_t), + &res)); + TEST_ASSERT_EQUAL_INT(0, memcmp(&test_eui64, &res, sizeof(eui64_t))); + /* test (nordic softdevice) BLE */ + res.uint64.u64 = 0; + TEST_ASSERT_EQUAL_INT(sizeof(eui64_t), + l2util_eui64_from_addr(NETDEV_TYPE_BLE, + test_addr, sizeof(eui48_t), + &res)); + TEST_ASSERT_EQUAL_INT(0, memcmp(&test_eui48, &res, sizeof(eui64_t))); + /* test cc110x */ + res.uint64.u64 = 0; + TEST_ASSERT_EQUAL_INT(sizeof(eui64_t), + l2util_eui64_from_addr(NETDEV_TYPE_CC110X, + test_addr, sizeof(uint8_t), + &res)); + TEST_ASSERT_EQUAL_INT(0, memcmp(&test_cc110x, &res, sizeof(eui64_t))); + /* test NRFMIN */ + res.uint64.u64 = 0; + TEST_ASSERT_EQUAL_INT(sizeof(eui64_t), + l2util_eui64_from_addr(NETDEV_TYPE_NRFMIN, + test_addr, sizeof(uint16_t), + &res)); + TEST_ASSERT_EQUAL_INT(0, memcmp(&test_802154_s, &res, sizeof(eui64_t))); + /* test ESP-Now */ + res.uint64.u64 = 0; + TEST_ASSERT_EQUAL_INT(sizeof(eui64_t), + l2util_eui64_from_addr(NETDEV_TYPE_ESP_NOW, + test_addr, sizeof(eui48_t), + &res)); + TEST_ASSERT_EQUAL_INT(0, memcmp(&test_eui48, &res, sizeof(eui64_t))); +} + +static void test_eui64_from_addr__EINVAL(void) +{ + static const uint8_t test_addr[L2UTIL_ADDR_MAX_LEN] = TEST_ADDR; + eui64_t res = { .uint8 = { 0 } }; + + /* test Ethernet */ + TEST_ASSERT_EQUAL_INT(-EINVAL, + l2util_eui64_from_addr(NETDEV_TYPE_ETHERNET, + test_addr, sizeof(eui64_t), + &res)); + /* test IEEE 802.15.4 */ + TEST_ASSERT_EQUAL_INT(-EINVAL, + l2util_eui64_from_addr(NETDEV_TYPE_IEEE802154, + test_addr, sizeof(uint16_t), + &res)); + TEST_ASSERT_EQUAL_INT(-EINVAL, + l2util_eui64_from_addr(NETDEV_TYPE_IEEE802154, + test_addr, sizeof(eui48_t), + &res)); + /* test (nordic softdevice) BLE */ + TEST_ASSERT_EQUAL_INT(-EINVAL, + l2util_eui64_from_addr(NETDEV_TYPE_BLE, + test_addr, sizeof(uint16_t), + &res)); + /* test cc110x */ + TEST_ASSERT_EQUAL_INT(-EINVAL, + l2util_eui64_from_addr(NETDEV_TYPE_CC110X, + test_addr, sizeof(uint64_t), + &res)); + /* test NRFMIN */ + TEST_ASSERT_EQUAL_INT(-EINVAL, + l2util_eui64_from_addr(NETDEV_TYPE_NRFMIN, + test_addr, sizeof(uint64_t), + &res)); + /* test ESP-Now */ + TEST_ASSERT_EQUAL_INT(-EINVAL, + l2util_eui64_from_addr(NETDEV_TYPE_ESP_NOW, + test_addr, 0, + &res)); +} + +static void test_eui64_from_addr__ENOTSUP(void) +{ + static const uint8_t test_addr[L2UTIL_ADDR_MAX_LEN] = TEST_ADDR; + eui64_t res = { .uint8 = { 0 } }; + + TEST_ASSERT_EQUAL_INT(-ENOTSUP, + l2util_eui64_from_addr(NETDEV_TYPE_UNKNOWN, + test_addr, 0, + &res)); +} + +static void test_iid_from_addr__success(void) +{ + static const uint8_t test_addr[L2UTIL_ADDR_MAX_LEN] = TEST_ADDR; + static const eui64_t test_802154_s = { .uint8 = TEST_802154_S_IID }; + static const eui64_t test_cc110x = { .uint8 = TEST_CC110X_IID }; + static const eui64_t test_eui48 = { .uint8 = TEST_EUI48_IID }; + static const eui64_t test_eui64 = { .uint8 = TEST_EUI64_IID }; + eui64_t res; + + /* test Ethernet */ + res.uint64.u64 = 0; + TEST_ASSERT_EQUAL_INT(sizeof(eui64_t), + l2util_ipv6_iid_from_addr(NETDEV_TYPE_ETHERNET, + test_addr, sizeof(eui48_t), + &res)); + TEST_ASSERT_EQUAL_INT(0, memcmp(&test_eui48, &res, sizeof(eui64_t))); + /* test IEEE 802.15.4 */ + res.uint64.u64 = 0; + TEST_ASSERT_EQUAL_INT(sizeof(eui64_t), + l2util_ipv6_iid_from_addr(NETDEV_TYPE_IEEE802154, + test_addr, sizeof(eui64_t), + &res)); + TEST_ASSERT_EQUAL_INT(0, memcmp(&test_eui64, &res, sizeof(eui64_t))); + res.uint64.u64 = 0; + TEST_ASSERT_EQUAL_INT(sizeof(eui64_t), + l2util_ipv6_iid_from_addr(NETDEV_TYPE_IEEE802154, + test_addr, + IEEE802154_SHORT_ADDRESS_LEN, + &res)); + TEST_ASSERT_EQUAL_INT(0, memcmp(&test_802154_s, &res, sizeof(eui64_t))); + /* test (nordic softdevice) BLE */ + res.uint64.u64 = 0; + TEST_ASSERT_EQUAL_INT(sizeof(eui64_t), + l2util_ipv6_iid_from_addr(NETDEV_TYPE_BLE, + test_addr, sizeof(eui48_t), + &res)); + TEST_ASSERT_EQUAL_INT(0, memcmp(&test_eui48, &res, sizeof(eui64_t))); + /* test cc110x */ + res.uint64.u64 = 0; + TEST_ASSERT_EQUAL_INT(sizeof(eui64_t), + l2util_ipv6_iid_from_addr(NETDEV_TYPE_CC110X, + test_addr, sizeof(uint8_t), + &res)); + TEST_ASSERT_EQUAL_INT(0, memcmp(&test_cc110x, &res, sizeof(eui64_t))); + /* test NRFMIN */ + res.uint64.u64 = 0; + TEST_ASSERT_EQUAL_INT(sizeof(eui64_t), + l2util_ipv6_iid_from_addr(NETDEV_TYPE_NRFMIN, + test_addr, sizeof(uint16_t), + &res)); + TEST_ASSERT_EQUAL_INT(0, memcmp(&test_802154_s, &res, sizeof(eui64_t))); + /* test ESP-Now */ + res.uint64.u64 = 0; + TEST_ASSERT_EQUAL_INT(sizeof(eui64_t), + l2util_ipv6_iid_from_addr(NETDEV_TYPE_ESP_NOW, + test_addr, sizeof(eui48_t), + &res)); + TEST_ASSERT_EQUAL_INT(0, memcmp(&test_eui48, &res, sizeof(eui64_t))); +} + +static void test_iid_from_addr__EINVAL(void) +{ + static const uint8_t test_addr[L2UTIL_ADDR_MAX_LEN] = TEST_ADDR; + eui64_t res = { .uint8 = { 0 } }; + + /* test Ethernet */ + TEST_ASSERT_EQUAL_INT(-EINVAL, + l2util_ipv6_iid_from_addr(NETDEV_TYPE_ETHERNET, + test_addr, sizeof(eui64_t), + &res)); + /* test IEEE 802.15.4 */ + TEST_ASSERT_EQUAL_INT(-EINVAL, + l2util_ipv6_iid_from_addr(NETDEV_TYPE_IEEE802154, + test_addr, sizeof(eui48_t), + &res)); + /* test (nordic softdevice) BLE */ + TEST_ASSERT_EQUAL_INT(-EINVAL, + l2util_ipv6_iid_from_addr(NETDEV_TYPE_BLE, + test_addr, sizeof(uint16_t), + &res)); + /* test cc110x */ + TEST_ASSERT_EQUAL_INT(-EINVAL, + l2util_ipv6_iid_from_addr(NETDEV_TYPE_CC110X, + test_addr, sizeof(uint64_t), + &res)); + /* test NRFMIN */ + TEST_ASSERT_EQUAL_INT(-EINVAL, + l2util_ipv6_iid_from_addr(NETDEV_TYPE_NRFMIN, + test_addr, sizeof(uint64_t), + &res)); + /* test ESP-Now */ + TEST_ASSERT_EQUAL_INT(-EINVAL, + l2util_ipv6_iid_from_addr(NETDEV_TYPE_ESP_NOW, + test_addr, 0, + &res)); +} + +static void test_iid_from_addr__ENOTSUP(void) +{ + static const uint8_t test_addr[L2UTIL_ADDR_MAX_LEN] = TEST_ADDR; + eui64_t res = { .uint8 = { 0 } }; + + TEST_ASSERT_EQUAL_INT(-ENOTSUP, + l2util_ipv6_iid_from_addr(NETDEV_TYPE_UNKNOWN, + test_addr, 0, + &res)); +} + +static void test_iid_to_addr__success(void) +{ + static const uint8_t test_addr[L2UTIL_ADDR_MAX_LEN] = TEST_ADDR; + static const eui64_t test_802154_s = { .uint8 = TEST_802154_S_IID }; + static const eui64_t test_cc110x = { .uint8 = TEST_CC110X_IID }; + static const eui64_t test_eui48 = { .uint8 = TEST_EUI48_IID }; + static const eui64_t test_eui64 = { .uint8 = TEST_EUI64_IID }; + uint8_t res[L2UTIL_ADDR_MAX_LEN]; + + /* test Ethernet */ + memset(res, 0, sizeof(res)); + TEST_ASSERT_EQUAL_INT(sizeof(eui48_t), + l2util_ipv6_iid_to_addr(NETDEV_TYPE_ETHERNET, + &test_eui48, res)); + TEST_ASSERT_EQUAL_INT(0, memcmp(test_addr, res, sizeof(eui48_t))); + /* test IEEE 802.15.4 */ + memset(res, 0, sizeof(res)); + TEST_ASSERT_EQUAL_INT(sizeof(eui64_t), + l2util_ipv6_iid_to_addr(NETDEV_TYPE_IEEE802154, + &test_eui64, res)); + TEST_ASSERT_EQUAL_INT(0, memcmp(test_addr, res, sizeof(eui64_t))); + /* test (nordic softdevice) BLE */ + memset(res, 0, sizeof(res)); + TEST_ASSERT_EQUAL_INT(sizeof(eui48_t), + l2util_ipv6_iid_to_addr(NETDEV_TYPE_BLE, + &test_eui48, res)); + TEST_ASSERT_EQUAL_INT(0, memcmp(test_addr, res, sizeof(eui48_t))); + /* test cc110x */ + memset(res, 0, sizeof(res)); + TEST_ASSERT_EQUAL_INT(sizeof(uint8_t), + l2util_ipv6_iid_to_addr(NETDEV_TYPE_CC110X, + &test_cc110x, res)); + TEST_ASSERT_EQUAL_INT(0, memcmp(test_addr, res, sizeof(uint8_t))); + /* test NRFMIN */ + memset(res, 0, sizeof(res)); + TEST_ASSERT_EQUAL_INT(sizeof(uint16_t), + l2util_ipv6_iid_to_addr(NETDEV_TYPE_NRFMIN, + &test_802154_s, res)); + TEST_ASSERT_EQUAL_INT(0, memcmp(test_addr, res, sizeof(uint16_t))); + /* test ESP-Now */ + memset(res, 0, sizeof(res)); + TEST_ASSERT_EQUAL_INT(sizeof(eui48_t), + l2util_ipv6_iid_to_addr(NETDEV_TYPE_ESP_NOW, + &test_eui48, res)); + TEST_ASSERT_EQUAL_INT(0, memcmp(test_addr, res, sizeof(eui48_t))); +} + +static void test_iid_to_addr__ENOTSUP(void) +{ + static const eui64_t test_eui48 = { .uint8 = TEST_EUI48_IID }; + uint8_t res[L2UTIL_ADDR_MAX_LEN]; + + TEST_ASSERT_EQUAL_INT(-ENOTSUP, + l2util_ipv6_iid_to_addr(NETDEV_TYPE_UNKNOWN, + &test_eui48, res)); +} + +static void test_addr_len_from_l2ao__success(void) +{ + ndp_opt_t opt = { .type = NDP_OPT_SL2A }; + + /* test Ethernet */ + opt.len = 1; + TEST_ASSERT_EQUAL_INT(sizeof(eui48_t), + l2util_ndp_addr_len_from_l2ao(NETDEV_TYPE_ETHERNET, + &opt)); + /* test IEEE 802.15.4 */ + opt.len = 1; + TEST_ASSERT_EQUAL_INT(IEEE802154_SHORT_ADDRESS_LEN, + l2util_ndp_addr_len_from_l2ao(NETDEV_TYPE_IEEE802154, + &opt)); + opt.len = 2; + TEST_ASSERT_EQUAL_INT(sizeof(eui64_t), + l2util_ndp_addr_len_from_l2ao(NETDEV_TYPE_IEEE802154, + &opt)); + /* test BLE */ + opt.len = 1; + TEST_ASSERT_EQUAL_INT(sizeof(eui48_t), + l2util_ndp_addr_len_from_l2ao(NETDEV_TYPE_BLE, + &opt)); + /* test cc110x */ + opt.len = 1; + TEST_ASSERT_EQUAL_INT(sizeof(uint8_t), + l2util_ndp_addr_len_from_l2ao(NETDEV_TYPE_CC110X, + &opt)); + /* test NRFMIN */ + opt.len = 1; + TEST_ASSERT_EQUAL_INT(sizeof(uint16_t), + l2util_ndp_addr_len_from_l2ao(NETDEV_TYPE_NRFMIN, + &opt)); + /* test ESP-Now */ + opt.len = 1; + TEST_ASSERT_EQUAL_INT(sizeof(eui48_t), + l2util_ndp_addr_len_from_l2ao(NETDEV_TYPE_ESP_NOW, + &opt)); +} + +static void test_addr_len_from_l2ao__EINVAL(void) +{ + ndp_opt_t opt = { .type = NDP_OPT_SL2A }; + + /* test Ethernet */ + opt.len = 0; + TEST_ASSERT_EQUAL_INT(-EINVAL, + l2util_ndp_addr_len_from_l2ao(NETDEV_TYPE_ETHERNET, + &opt)); + /* test IEEE 802.15.4 */ + opt.len = 0; + TEST_ASSERT_EQUAL_INT(-EINVAL, + l2util_ndp_addr_len_from_l2ao(NETDEV_TYPE_IEEE802154, + &opt)); + /* test BLE */ + opt.len = 0; + TEST_ASSERT_EQUAL_INT(-EINVAL, + l2util_ndp_addr_len_from_l2ao(NETDEV_TYPE_BLE, + &opt)); + /* test ESP-Now */ + opt.len = 0; + TEST_ASSERT_EQUAL_INT(-EINVAL, + l2util_ndp_addr_len_from_l2ao(NETDEV_TYPE_ESP_NOW, + &opt)); +} + +static void test_addr_len_from_l2ao__ENOTSUP(void) +{ + ndp_opt_t opt = { .type = NDP_OPT_SL2A }; + + opt.len = 1; + TEST_ASSERT_EQUAL_INT(-ENOTSUP, + l2util_ndp_addr_len_from_l2ao(NETDEV_TYPE_UNKNOWN, + &opt)); +} + +TestRef test_l2util(void) +{ + EMB_UNIT_TESTFIXTURES(fixtures) { + new_TestFixture(test_eui64_from_addr__success), + new_TestFixture(test_eui64_from_addr__EINVAL), + new_TestFixture(test_eui64_from_addr__ENOTSUP), + new_TestFixture(test_iid_from_addr__success), + new_TestFixture(test_iid_from_addr__EINVAL), + new_TestFixture(test_iid_from_addr__ENOTSUP), + new_TestFixture(test_iid_to_addr__success), + new_TestFixture(test_iid_to_addr__ENOTSUP), + new_TestFixture(test_addr_len_from_l2ao__success), + new_TestFixture(test_addr_len_from_l2ao__EINVAL), + new_TestFixture(test_addr_len_from_l2ao__ENOTSUP), + }; + + EMB_UNIT_TESTCALLER(tests_l2util, NULL, NULL, fixtures); + return (TestRef) & tests_l2util; +} + +int main(void) +{ + TESTS_START(); + TESTS_RUN(test_l2util()); + TESTS_END(); +} diff --git a/tests/l2util/tests/01-run.py b/tests/l2util/tests/01-run.py new file mode 100755 index 0000000000..5fc8788f24 --- /dev/null +++ b/tests/l2util/tests/01-run.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 + +# 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. + +import sys +from testrunner import run + + +def testfunc(child): + child.expect(r'OK \(\d+ tests\)') + + +if __name__ == "__main__": + sys.exit(run(testfunc))