diff --git a/sys/include/net/gnrc/netif2.h b/sys/include/net/gnrc/netif2.h new file mode 100644 index 0000000000000..894d3606147ad --- /dev/null +++ b/sys/include/net/gnrc/netif2.h @@ -0,0 +1,322 @@ +/* + * 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. + */ + +/** + * @defgroup net_gnrc_netif2 New network interface API + * @ingroup net_gnrc + * @brief Abstraction layer for GNRC's network interfaces + * + * Network interfaces in the context of GNRC are threads for protocols that are + * below the network layer. + * + * @{ + * + * @file + * @brief Definition for GNRC's network interfaces + * + * @author Martine Lenders + */ +#ifndef GNRC_NETIF2_H +#define GNRC_NETIF2_H + +#include +#include +#include + +#include "kernel_types.h" +#include "msg.h" +#include "net/gnrc/netapi.h" +#include "net/gnrc/pkt.h" +#include "net/gnrc/netif2/conf.h" +#ifdef MODULE_GNRC_SIXLOWPAN +#include "net/gnrc/netif2/6lo.h" +#endif +#include "net/gnrc/netif2/flags.h" +#ifdef MODULE_GNRC_IPV6 +#include "net/gnrc/netif2/ipv6.h" +#endif +#ifdef MODULE_GNRC_MAC +#include "net/gnrc/netif2/mac.h" +#endif +#include "net/netdev.h" +#include "rmutex.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Operations to an interface + */ +typedef struct gnrc_netif2_ops gnrc_netif2_ops_t; + +/** + * @brief Representation of a network interface + */ +typedef struct { + const gnrc_netif2_ops_t *ops; /**< Operations of the network interface */ + netdev_t *dev; /**< Network device of the network interface */ + rmutex_t mutex; /**< Mutex of the interface */ +#if defined(MODULE_GNRC_IPV6) || DOXYGEN + gnrc_netif2_ipv6_t ipv6; /**< IPv6 component */ +#endif +#if defined(MODULE_GNRC_MAC) || DOXYGEN + gnrc_netif2_mac_t mac; /**< @ref gnrc_mac component */ +#endif /* MODULE_GNRC_MAC */ + /** + * @brief Flags for the interface + * + * @see net_gnrc_netif2_flags + */ + uint32_t flags; +#if (GNRC_NETIF2_L2ADDR_MAXLEN > 0) + /** + * @brief The link-layer address currently used as the source address + * on this interface. + * + * @note Only available if @ref GNRC_NETIF2_L2ADDR_MAXLEN > 0 + */ + uint8_t l2addr[GNRC_NETIF2_L2ADDR_MAXLEN]; + + /** + * @brief Length in bytes of gnrc_netif2_t::l2addr + * + * @note Only available if @ref GNRC_NETIF2_L2ADDR_MAXLEN > 0 + */ + uint8_t l2addr_len; +#endif +#if defined(MODULE_GNRC_SIXLOWPAN) || DOXYGEN + gnrc_netif2_6lo_t sixlo; /**< 6Lo component */ +#endif + uint8_t cur_hl; /**< Current hop-limit for out-going packets */ + uint8_t device_type; /**< Device type */ + kernel_pid_t pid; /**< PID of the network interface's thread */ +} gnrc_netif2_t; + +/** + * @see gnrc_netif2_ops_t + */ +struct gnrc_netif2_ops { + /** + * @brief Initializes network interface beyond the default settings + * + * @pre `netif != NULL` + * + * @param[in] netif The network interface. + * + * This is called after the default settings were set, right before the + * interface's thread starts receiving messages. It is not necessary to lock + * the interface's mutex gnrc_netif_t::mutex, since the thread will already + * lock it. Leave NULL if you do not need any special initialization. + */ + void (*init)(gnrc_netif2_t *netif); + + /** + * @brief Send a @ref net_gnrc_pkt "packet" over the network interface + * + * @pre `netif != NULL && pkt != NULL` + * + * @note The function re-formats the content of @p pkt to a format expected + * by the netdev_driver_t::send() method of gnrc_netif_t::dev and + * releases the it before returning (so no additional release should + * be required after calling this method). + * + * @param[in] netif The network interface. + * @param[in] pkt A packet to send. + * + * @return The number of bytes actually sent on success + * @return -EBADMSG, if the @ref net_gnrc_netif_hdr in @p pkt is missing + * or is in an unexpected format. + * @return -ENOTSUP, if sending @p pkt in the given format isn't supported + * (e.g. empty payload with Ethernet). + * @return Any negative error code reported by gnrc_netif2_t::dev. + */ + int (*send)(gnrc_netif2_t *netif, gnrc_pktsnip_t *pkt); + + /** + * @brief Receives a @ref net_gnrc_pkt "packet" from the network interface + * + * @pre `netif != NULL` + * + * @note The function takes the bytes received via netdev_driver_t::recv() + * from gnrc_netif_t::dev and re-formats it to a + * @ref net_gnrc_pkt "packet" containing a @ref net_gnrc_netif_hdr + * and a payload header in receive order. + * + * @param[in] netif The network interface. + * + * @return The packet received. Contains the payload (with the type marked + * accordingly) and a @ref net_gnrc_netif_hdr in receive order. + * @return NULL, if @ref net_gnrc_pktbuf was full. + */ + gnrc_pktsnip_t *(*recv)(gnrc_netif2_t *netif); + + /** + * @brief Gets an option from the network interface + * + * Use gnrc_netif2_get_from_netdev() to just get options from + * gnrc_netif2_t::dev. + * + * @param[in] netif The network interface. + * @param[in] opt The option parameters. + * + * @return Number of bytes in @p data. + * @return -EOVERFLOW, if @p max_len is lesser than the required space. + * @return -ENOTSUP, if @p opt is not supported to be set. + * @return Any negative error code reported by gnrc_netif2_t::dev. + */ + int (*get)(gnrc_netif2_t *netif, gnrc_netapi_opt_t *opt); + + /** + * @brief Sets an option from the network interface + * + * Use gnrc_netif2_set_from_netdev() to just set options from + * gnrc_netif2_t::dev. + * + * @param[in] netif The network interface. + * @param[in] opt The option parameters. + * + * @return Number of bytes written to gnrc_netif2_t::dev. + * @return -EOVERFLOW, if @p data_len is greater than the allotted space in + * gnrc_netif2_t::dev or gnrc_netif_t. + * @return -ENOTSUP, if @p opt is not supported to be set. + * @return Any negative error code reported by gnrc_netif2_t::dev. + */ + int (*set)(gnrc_netif2_t *netif, const gnrc_netapi_opt_t *opt); + + /** + * @brief Message handler for network interface + * + * This message handler is used, when the network interface needs to handle + * message types beyond the ones defined in @ref net_gnrc_netapi "netapi". + * Leave NULL if this is not the case. + * + * @param[in] netif The network interface. + * @param[in] msg Message to be handled. + */ + void (*msg_handler)(gnrc_netif2_t *netif, msg_t *msg); +}; + +/** + * @brief Creates a network interface + * + * @param[in] stack The stack for the network interface's thread. + * @param[in] stacksize Size of @p stack. + * @param[in] priority Priority for the network interface's thread. + * @param[in] name Name for the network interface. May be NULL. + * @param[in] dev Device for the interface. + * @param[in] ops Operations for the network interface. + * + * @note If @ref DEVELHELP is defined netif_params_t::name is used as the + * name of the network interface's thread. + * + * @return The network interface on success. + * @return NULL, on error. + */ +gnrc_netif2_t *gnrc_netif2_create(char *stack, int stacksize, char priority, + const char *name, netdev_t *dev, + const gnrc_netif2_ops_t *ops); + +/** + * @brief Get number of network interfaces actually allocated + * + * @return Number of network interfaces actually allocated + */ +unsigned gnrc_netif2_numof(void); + +/** + * @brief Iterate over all network interfaces. + * + * @param[in] prev previous interface in iteration. NULL to start iteration. + * + * @return The next network interface after @p prev. + * @return NULL, if @p prev was the last network interface. + */ +gnrc_netif2_t *gnrc_netif2_iter(const gnrc_netif2_t *prev); + +/** + * @brief Get network interface by PID + * + * @param[in] pid A PID of a network interface. + * + * @return The network interface on success. + * @return NULL, if no network interface with PID exists. + */ +gnrc_netif2_t *gnrc_netif2_get_by_pid(kernel_pid_t pid); + +/** + * @brief Default operation for gnrc_netif2_ops_t::get() + * + * @note Can also be used to be called *after* a custom operation. + * + * @param[in] netif The network interface. + * @param[out] opt The option parameters. + * + * @return Return value of netdev_driver_t::get() of gnrc_netif2_t::dev of + * @p netif. + */ +int gnrc_netif2_get_from_netdev(gnrc_netif2_t *netif, gnrc_netapi_opt_t *opt); + +/** + * @brief Default operation for gnrc_netif2_ops_t::set() + * + * @note Can also be used to be called *after* a custom operation. + * + * @param[in] netif The network interface. + * @param[in] opt The option parameters. + * + * @return Return value of netdev_driver_t::set() of gnrc_netif2_t::dev of + * @p netif. + */ +int gnrc_netif2_set_from_netdev(gnrc_netif2_t *netif, + const gnrc_netapi_opt_t *opt); + +/** + * @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 *gnrc_netif2_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_NETIF2_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_NETIF2_L2ADDR_MAXLEN bytes allocated. + * + * @return Actual length of @p out on success. + * @return 0, on failure. + */ +size_t gnrc_netif2_addr_from_str(const char *str, uint8_t *out); + +#ifdef __cplusplus +} +#endif + +#endif /* GNRC_NETIF2_H */ +/** @} */ diff --git a/sys/include/net/gnrc/netif2/6lo.h b/sys/include/net/gnrc/netif2/6lo.h new file mode 100644 index 0000000000000..be8a878f72cd0 --- /dev/null +++ b/sys/include/net/gnrc/netif2/6lo.h @@ -0,0 +1,44 @@ +/* + * 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 net_gnrc_netif2 + * @{ + * + * @file 6LoWPAN definitions for @ref net_gnrc_netif2 + * + * @author Martine Lenders + */ +#ifndef NET_GNRC_NETIF2_6LO_H +#define NET_GNRC_NETIF2_6LO_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief 6Lo component of @ref gnrc_netif2_t + */ +typedef struct { + /** + * @brief Maximum fragment size for 6Lo fragmentation. + * + * @note Only available with module + * @ref net_gnrc_sixlowpan_frag "gnrc_sixlowpan_frag". + */ + uint8_t max_frag_size; +} gnrc_netif2_6lo_t; + +#ifdef __cplusplus +} +#endif + +#endif /* NET_GNRC_NETIF2_6LO_H */ +/** @} */ diff --git a/sys/include/net/gnrc/netif2/conf.h b/sys/include/net/gnrc/netif2/conf.h new file mode 100644 index 0000000000000..1987c87eb793e --- /dev/null +++ b/sys/include/net/gnrc/netif2/conf.h @@ -0,0 +1,125 @@ +/* + * 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 net_gnrc_netif2 + * @{ + * + * @file + * @brief Configuration macros for @ref net_gnrc_netif2 + * + * @author Martine Lenders + */ +#ifndef NET_GNRC_NETIF2_CONF_H +#define NET_GNRC_NETIF2_CONF_H + +#include "net/ieee802154.h" +#include "net/ethernet/hdr.h" +#include "thread.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Maximum number of network interfaces + * + * @note Intentially not calling it `GNRC_NETIF2_NUMOF` to not require + * rewrites throughout the stack. + */ +#ifndef GNRC_NETIF_NUMOF +#define GNRC_NETIF_NUMOF (1) +#endif + +/** + * @brief Default priority for network interface threads + */ +#ifndef GNRC_NETIF2_PRIO +#define GNRC_NETIF2_PRIO (THREAD_PRIORITY_MAIN - 5) +#endif + +/** + * @brief Number of multicast addresses needed for @ref net_gnrc_rpl "RPL". + * + * @note Used for calculation of @ref GNRC_NETIF2_IPV6_GROUPS_NUMOF + */ +#ifdef MODULE_GNRC_RPL +#define GNRC_NETIF2_RPL_ADDR (1) +#else +#define GNRC_NETIF2_RPL_ADDR (0) +#endif + +/** + * @brief Number of multicast addresses needed for a @ref net_gnrc_ipv6 "IPv6" + * router + * + * @note Used for calculation of @ref GNRC_NETIF2_IPV6_GROUPS_NUMOF + */ +#ifdef MODULE_GNRC_IPV6_ROUTER +#define GNRC_NETIF2_IPV6_RTR_ADDR (1) +#else +#define GNRC_NETIF2_IPV6_RTR_ADDR (0) +#endif + +/** + * @brief Maximum number of unicast and anycast addresses per interface + * + * Default: 2 (link-local + corresponding global address) + */ +#ifndef GNRC_NETIF2_IPV6_ADDRS_NUMOF +#define GNRC_NETIF2_IPV6_ADDRS_NUMOF (2) +#endif + +/** + * @brief Maximum number of multicast groups per interface + * + * Default: 2 (all-nodes + solicited-nodes of link-local and global unicast + * address) + @ref GNRC_NETIF2_RPL_ADDR + @ref GNRC_NETIF2_IPV6_RTR_ADDR + */ +#ifndef GNRC_NETIF2_IPV6_GROUPS_NUMOF +#define GNRC_NETIF2_IPV6_GROUPS_NUMOF (2 + GNRC_NETIF2_RPL_ADDR + GNRC_NETIF2_IPV6_RTR_ADDR) +#endif + +/** + * @brief Maximum length of the link-layer address. + * + * The value for the maximum length of a link-layer address is dependent + * on the @ref drivers_netdev_api "netdev" adapters compiled in: + * - Setting it via `CFLAGS` has the most precedence. + * - The default is 8. + * - 1, if only @ref drivers_cc110x devices are compiled in. + * - @ref ETHERNET_ADDR_LEN, if additionally (or only) ethernet devices are + * compiled in. + * - @ref IEEE802154_LONG_ADDRESS_LEN, if additionally (or only) IEEE802.15.4 + * devices are compiled in. + * + * @note Implementers note: From longest to shortest extend, if new link-layer + * address types are included + */ +#ifndef GNRC_NETIF2_L2ADDR_MAXLEN +#if defined(MODULE_NETDEV_IEEE802154) || defined(MODULE_XBEE) +#define GNRC_NETIF2_L2ADDR_MAXLEN (IEEE802154_LONG_ADDRESS_LEN) +#elif MODULE_NETDEV_ETH +#define GNRC_NETIF2_L2ADDR_MAXLEN (ETHERNET_ADDR_LEN) +#elif MODULE_CC110X +#define GNRC_NETIF2_L2ADDR_MAXLEN (1U) +#else +#define GNRC_NETIF2_L2ADDR_MAXLEN (8U) +#endif +#endif + +#ifndef GNRC_NETIF2_DEFAULT_HL +#define GNRC_NETIF2_DEFAULT_HL (64U) /**< default hop limit */ +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* NET_GNRC_NETIF2_CONF_H */ +/** @} */ diff --git a/sys/include/net/gnrc/netif2/ethernet.h b/sys/include/net/gnrc/netif2/ethernet.h new file mode 100644 index 0000000000000..c143783b83c96 --- /dev/null +++ b/sys/include/net/gnrc/netif2/ethernet.h @@ -0,0 +1,49 @@ +/* + * 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 net_gnrc_netif2 + * @{ + * + * @file + * @brief Ethernet adaption for @ref net_gnrc_netif2 + * + * @author Martine Lenders + */ +#ifndef NET_GNRC_NETIF2_ETH_H +#define NET_GNRC_NETIF2_ETH_H + +#include "net/gnrc/netif2.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Creates an Ethernet network interface + * + * @param[in] stack The stack for the network interface's thread. + * @param[in] stacksize Size of @p stack. + * @param[in] priority Priority for the network interface's thread. + * @param[in] name Name for the network interface. May be NULL. + * @param[in] dev Device for the interface. + * + * @see @ref gnrc_netif2_create() + * + * @return The network interface on success. + * @return NULL, on error. + */ +gnrc_netif2_t *gnrc_netif2_ethernet_create(char *stack, int stacksize, char priority, + char *name, netdev_t *dev); + +#ifdef __cplusplus +} +#endif + +#endif /* NET_GNRC_NETIF2_ETH_H */ +/** @} */ diff --git a/sys/include/net/gnrc/netif2/flags.h b/sys/include/net/gnrc/netif2/flags.h new file mode 100644 index 0000000000000..973e675631240 --- /dev/null +++ b/sys/include/net/gnrc/netif2/flags.h @@ -0,0 +1,142 @@ +/* + * 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 net_gnrc_netif2 + * @{ + * + * @file + * @brief Flag definitions for @ref net_gnrc_netif2 + * + * @author Martine Lenders + */ +#ifndef NET_GNRC_NETIF2_FLAGS_H +#define NET_GNRC_NETIF2_FLAGS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Auto-address configuration modes + * @anchor net_gnrc_netif2_aac + */ +enum { + GNRC_NETIF2_AAC_NONE = 0, /**< no configuration */ + GNRC_NETIF2_AAC_AUTO, /**< Use some automatic bootstrapping (e.g. SLAAC with IPv6) */ + GNRC_NETIF2_AAC_DHCP, /**< Use DHCP(v6) */ + /* extend if needed */ +}; + +/** + * @name Network interface flags + * @anchor net_gnrc_netif2_flags + * @{ + */ +/** + * @brief Network interface has link-layer address + * + * There are link-layers (e.g. SLIP) that do not have (nor require) link-layer + * addresses. This flag signifies this fact to upper layers by leaving it unset. + */ +#define GNRC_NETIF2_FLAGS_HAS_L2ADDR (0x00000001U) + +/** + * @brief Network interface is enabled for IPv6 forwarding + */ +#define GNRC_NETIF2_FLAGS_IPV6_FORWARDING (0x00000002U) + +/** + * @brief Network interface advertises itself as an IPv6 router + * (implies @ref GNRC_NETIF2_FLAGS_IPV6_FORWARDING to be set) + */ +#define GNRC_NETIF2_FLAGS_IPV6_RTR_ADV (0x00000004U) + +/** + * @brief This interface advertises its gnrc_netif2_t::mtu to other nodes + * (implies @ref GNRC_NETIF2_FLAGS_IPV6_RTR_ADV to be set) + */ +#define GNRC_NETIF2_FLAGS_IPV6_ADV_MTU (0x00000008U) + +/** + * @brief This interface advertises its gnrc_netif2_t::cur_hl to other nodes + * (implies @ref GNRC_NETIF2_FLAGS_IPV6_RTR_ADV to be set) + */ +#define GNRC_NETIF2_FLAGS_IPV6_ADV_CUR_HL (0x00000010U) + +/** + * @brief This interface advertises its reachable time to other nodes + * (implies @ref GNRC_NETIF2_FLAGS_IPV6_RTR_ADV to be set) + */ +#define GNRC_NETIF2_FLAGS_IPV6_ADV_REACH_TIME (0x00000020U) + +/** + * @brief This interface advertises its retransmission timer to other nodes + * (implies @ref GNRC_NETIF2_FLAGS_IPV6_RTR_ADV to be set) + */ +#define GNRC_NETIF2_FLAGS_IPV6_ADV_RETRANS_TIMER (0x00000040U) + +/** + * @brief If gnrc_netif2_t::ipv6::aac_mode == GNRC_NETIF2_AAC_DHCP then this + * flag indicates that other configuration information is available via + * DHCPv6 (e.g. DNS-related information) + * + * @see [RFC 4861, section 4.2](https://tools.ietf.org/html/rfc4861#section-4.2) + */ +#define GNRC_NETIF2_FLAGS_IPV6_ADV_O_FLAG (0x00000080U) + +/** + * @brief This interface uses 6Lo header compression + * + * @see [RFC 6282](https://tools.ietf.org/html/rfc6282) + */ +#define GNRC_NETIF2_FLAGS_6LO_HC (0x00000100U) + +/** + * @brief This interface acts as a 6Lo border router to the LLN + */ +#define GNRC_NETIF2_FLAGS_6LO_ABR (0x00000200U) + +/** + * @brief This interface acts as a mesh-under node (route-over topology when + * unset) + */ +#define GNRC_NETIF2_FLAGS_6LO_MESH (0x00000400U) + +/** + * @brief Interface supports 6LoWPAN general header compression + * + * @attention GHC not implemented yet + * @see [RFC 7400, section 3.3](https://tools.ietf.org/html/rfc7400#section-3.3) + */ +#define GNRC_NETIF2_FLAGS_6LO_BACKBONE (0x00000800U) + +/** + * @brief Marks if the addresses of the interface were already registered + * to an interface or not + */ +#define GNRC_NETIF2_FLAGS_6LO_ADDRS_REG (0x00001000U) + +/** + * @brief Mask for @ref gnrc_mac_tx_feedback_t + */ +#define GNRC_NETIF2_FLAGS_MAC_TX_FEEDBACK_MASK (0x00006000U) + +/** + * @brief Flag to track if a transmission might have corrupted a received + * packet + */ +#define GNRC_NETIF2_FLAGS_MAC_RX_STARTED (0x00008000U) +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* NET_GNRC_NETIF2_FLAGS_H */ +/** @} */ diff --git a/sys/include/net/gnrc/netif2/internal.h b/sys/include/net/gnrc/netif2/internal.h new file mode 100644 index 0000000000000..28803ed3df067 --- /dev/null +++ b/sys/include/net/gnrc/netif2/internal.h @@ -0,0 +1,376 @@ +/* + * 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. + */ + +/** + * @addtogroup net_gnrc_netif + * @internal + * @attention **FOR GNRC-INTERNAL USE ONLY. PLEASE USE @ref net_gnrc_netapi + * "NETAPI" (WITH THE INTERFACE'S PID) FOR EXTERNAL MANIPULATION** + * @{ + * + * @file + * @brief GNRC-internal network interface definitions + * + * @author Martine Lenders + */ +#ifndef NET_GNRC_NETIF2_INTERNAL_H +#define NET_GNRC_NETIF2_INTERNAL_H + +#include "net/gnrc/netif2.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Message type for @ref netdev_event_t "netdev events" + */ +#define NETDEV_MSG_TYPE_EVENT (0x1234) + +/** + * @brief Acquires exclusive access to the interface + * + * @param[in] netif the network interface + * + * @internal + */ +static inline void gnrc_netif2_acquire(gnrc_netif2_t *netif) +{ + if (netif && (netif->ops)) { + rmutex_lock(&netif->mutex); + } +} + +/** + * @brief Releases exclusive access to the interface + * + * @param[in] netif the network interface + * + * @internal + */ +static inline void gnrc_netif2_release(gnrc_netif2_t *netif) +{ + if (netif && (netif->ops)) { + rmutex_unlock(&netif->mutex); + } +} + +#if defined(MODULE_GNRC_IPV6) || DOXYGEN +/** + * @brief Adds an IPv6 address to the interface + * + * @pre `(netif != NULL) && (addr != NULL)` + * @pre @p addr is not multicast (starts with `ff00::/8`), unspecified (`::`), + * or loopback + * @pre `(pfx_len > 0) && (pfx_len <= 128)` + * + * @param[in,out] netif the network interface. May not be NULL. + * @param[in] addr the address to add. If the address is already on the + * interface the function will return 0, but @p flags + * will be ignored. May not be NULL and may not be + * link-local or multicast + * @param[in] pfx_len length in bits of the prefix of @p addr + * @param[in] flags initial flags for the address. Setting the address' + * state to @ref GNRC_NETIF2_IPV6_ADDRS_FLAGS_STATE_VALID + * means that state-less auto-address configuration is + * skipped and the address is considered to be manually + * configured. If the state in the flgas is unset, + * GNRC_NETIF2_IPV6_ADDRS_FLAGS_STATE_TENTATIVE is assumed. + * + * @note Only available with @ref net_gnrc_ipv6 "gnrc_ipv6". + * + * @return >= 0, on success + * @return -ENOMEM, when no space for new addresses is left on the interface + */ +int gnrc_netif2_ipv6_addr_add(gnrc_netif2_t *netif, const ipv6_addr_t *addr, + unsigned pfx_len, uint8_t flags); + +/** + * @brief Removes an IPv6 address from the interface + * + * @pre `(netif != NULL) && (addr != NULL)` + * + * @param[in,out] netif the network interface + * @param[in] addr the address to remove + * + * @note Only available with @ref net_gnrc_ipv6 "gnrc_ipv6". + */ +void gnrc_netif2_ipv6_addr_remove(gnrc_netif2_t *netif, + const ipv6_addr_t *addr); + + +/** + * @brief Returns the index of @p addr in gnrc_netif2_t::ipv6_addrs of @p + * netif + * + * @pre `(netif != NULL) && (addr != NULL)` + * + * Can be used to check if an address is assigned to an interface. + * + * @param[in] netif the network interface + * @param[in] addr the address to check + * + * @note Only available with @ref net_gnrc_ipv6 "gnrc_ipv6". + * + * @return index of @p addr in gnrc_netif2_t::ipv6_addrs of @p netif + * @return -1, if @p addr isn't assigned to @p netif + */ +int gnrc_netif2_ipv6_addr_idx(gnrc_netif2_t *netif, + const ipv6_addr_t *addr); + +/** + * @brief Gets state from address flags + * + * @param[in] netif the network interface + * @param[in] idx index of the address flags + * + * @return the state of the address at @p idx + */ +static inline uint8_t gnrc_netif2_ipv6_addr_get_state(const gnrc_netif2_t *netif, + int idx) +{ + return netif->ipv6.addrs_flags[idx] & GNRC_NETIF2_IPV6_ADDRS_FLAGS_STATE_MASK; +} + +/** + * @brief Returns the index of an address in gnrc_netif2_t::ipv6_addrs of @p + * netif that matches @p addr best + * + * @pre `(netif != NULL) && (addr != NULL)` + * + * Can be used to check if a prefix is assigned to an interface. + * + * @param[in] netif the network interface + * @param[in] addr the prefix to match + * + * + * @note Only available with @ref net_gnrc_ipv6 "gnrc_ipv6". + * + * @return index of an address in gnrc_netif2_t::ipv6_addrs of @p netif that + * best matches @p addr. + * @return -1, if no address on @p netif matches @p addr + */ +int gnrc_netif2_ipv6_addr_match(gnrc_netif2_t *netif, + const ipv6_addr_t *addr); + +/** + * @brief Searches for the best address on an interface usable as a source + * address for a given destination address. + * + * @pre `(netif != NULL) && (dst != NULL)` + * + * @param[in] netif the network interface + * @param[in] dst the destination address you want to find a source for. + * @param[in] ll_only only link local addresses qualify + * + * @see [RFC 6724](https://tools.ietf.org/html/rfc6724) + * + * @note Only available with @ref net_gnrc_ipv6 "gnrc_ipv6". + * + * @todo Rule 4 from RFC 6724 is currently not implemented. Has to updated as + * soon as gnrc supports Mobile IP. + * + * @todo Rule 6 from RFC 6724 is currently not implemented. Has to updated as + * soon as gnrc supports flow labels. + * + * @todo Rule 7 from RFC 6724 is currently not implemented. Has to updated as + * soon as gnrc supports temporary addresses. + * + * @return The best source address for a packet addressed to @p dst + * @return NULL, if no matching address can be found on the interface. + */ +ipv6_addr_t *gnrc_netif2_ipv6_addr_best_src(gnrc_netif2_t *netif, + const ipv6_addr_t *dst, + bool ll_only); + +/** + * @brief Gets an interface by an address (incl. multicast groups) assigned + * to it. + * + * @pre `addr != NULL` + * + * @param[in] addr an IPv6 address + * + * @return The network interface that has @p addr assigned + * @return NULL, if no interface has @p addr assigned + */ +gnrc_netif2_t *gnrc_netif2_get_by_ipv6_addr(const ipv6_addr_t *addr); + +/** + * @brief Gets an interface by an address matching a given prefix best + * + * @param[in] prefix an IPv6 address or prefix + * + * @return The network interface that has an address assigned, that matches + * @p prefix best + * @return NULL, if there is no address on any interface that matches @prefix + */ +gnrc_netif2_t *gnrc_netif2_get_by_prefix(const ipv6_addr_t *prefix); + +/** + * @brief Joins interface to an IPv6 multicast group + * + * @pre `(netif != NULL) && (addr != NULL)` + * @pre @p addr is a multicast address (starts with `ff00::/8`) + * + * @param[in,out] netif the network interface + * @param[in] addr the address of the multicast group + * + * @note Only available with @ref net_gnrc_ipv6 "gnrc_ipv6". + * + * @return 0, on success + * @return -ENOMEM, when no space for new addresses is left on the interface + */ +int gnrc_netif2_ipv6_group_join(gnrc_netif2_t *netif, + const ipv6_addr_t *addr); + +/** + * @brief Let interface leave from an IPv6 multicast group + * + * @pre `(netif != NULL) && (addr != NULL)` + * + * @param[in,out] netif the network interface + * @param[in] addr the address of the multicast group + * + * @note Only available with @ref net_gnrc_ipv6 "gnrc_ipv6". + */ +void gnrc_netif2_ipv6_group_leave(gnrc_netif2_t *netif, + const ipv6_addr_t *addr); + +/** + * @brief Returns the index of @p addr in gnrc_netif2_t::ipv6_groups of @p + * netif + * + * @pre `(netif != NULL) && (addr != NULL)` + * + * Can be used to check if a multicast address is assigned to an interface. + * + * @param[in] netif the network interface + * @param[in] addr the multicast address to check + * + * @note Only available with @ref net_gnrc_ipv6 "gnrc_ipv6". + * + * @return index of @p addr in gnrc_netif2_t::ipv6_groups of @p netif + * @return -1, if @p netif is not in group @p addr + */ +int gnrc_netif2_ipv6_group_idx(gnrc_netif2_t *netif, + const ipv6_addr_t *addr); + +/** + * @brief Gets interface identifier (IID) of an interface's link-layer address + * + * @param[in] netif the network interface + * @param[out] eui64 the IID + * + * @return 0, on success + * @return -ENOTSUP, if interface has no link-layer address or if + * gnrc_netif2_t::device_type is not supported. + */ +int gnrc_netif2_ipv6_get_iid(gnrc_netif2_t *netif, eui64_t *eui64); +#endif /* MODULE_GNRC_IPV6 */ + +/** + * @brief Checks if the interface represents a router according to RFC 4861 + * + * @attention Requires prior locking + * + * @param[in] netif the network interface + * + * @see [RFC 4861, section 2.1](https://tools.ietf.org/html/rfc4861#section-2.1) + * + * @return true, if the interface represents a router + * @return false, if the interface does not represent a router + */ +static inline bool gnrc_netif2_is_rtr(const gnrc_netif2_t *netif) +{ + return (netif->flags & GNRC_NETIF2_FLAGS_IPV6_FORWARDING); +} + +/** + * @brief Checks if the interface is allowed to send out router advertisements + * + * @attention Requiors prior locking + * + * @param[in] netif the network interface + * + * @return true, if the interface is allowed to send out router adverisements + * @return false, if the interface is allowed to send out router adverisements + */ +static inline bool gnrc_netif2_is_rtr_adv(const gnrc_netif2_t *netif) +{ + return (netif->flags & GNRC_NETIF2_FLAGS_IPV6_RTR_ADV); +} + +/** + * @brief Checks if the interface represents a 6Lo node (6LN) according to + * RFC 6775 + * + * @attention Requires prior locking + * + * @param[in] netif the network interface + * + * @see [RFC 6775, section 2](https://tools.ietf.org/html/rfc6776#section-2) + * + * @return true, if the interface represents a 6LN + * @return false, if the interface does not represent a 6LN + */ +static inline bool gnrc_netif2_is_6ln(const gnrc_netif2_t *netif) +{ + switch (netif->device_type) { + case NETDEV_TYPE_IEEE802154: + case NETDEV_TYPE_CC110X: + case NETDEV_TYPE_NRFMIN: + return true; + default: + return false; + } +} + +/** + * @brief Checks if the interface represents a 6Lo router (6LR) according to + * RFC 6775 + * + * @attention Requires prior locking + * + * @param[in] netif the network interface + * + * @see [RFC 6775, section 2](https://tools.ietf.org/html/rfc6776#section-2) + * + * @return true, if the interface represents a 6LR + * @return false, if the interface does not represent a 6LR + */ +static inline bool gnrc_netif2_is_6lr(const gnrc_netif2_t *netif) +{ + return gnrc_netif2_is_rtr(netif) && gnrc_netif2_is_6ln(netif); +} + +/** + * @brief Checks if the interface represents a 6Lo border router (6LBR) + * according to RFC 6775 + * + * @attention Requires prior locking + * + * @param[in] netif the network interface + * + * @see [RFC 6775, section 2](https://tools.ietf.org/html/rfc6776#section-2) + * + * @return true, if the interface represents a 6LBR + * @return false, if the interface does not represent a 6LBR + */ +static inline bool gnrc_netif2_is_6lbr(const gnrc_netif2_t *netif) +{ + return (netif->flags & GNRC_NETIF2_FLAGS_6LO_ABR) && + gnrc_netif2_is_6lr(netif); +} + +#ifdef __cplusplus +} +#endif + +#endif /* NET_GNRC_NETIF2_INTERNAL_H */ +/** @} */ diff --git a/sys/include/net/gnrc/netif2/ipv6.h b/sys/include/net/gnrc/netif2/ipv6.h new file mode 100644 index 0000000000000..2c9dbb5b40998 --- /dev/null +++ b/sys/include/net/gnrc/netif2/ipv6.h @@ -0,0 +1,246 @@ +/* + * 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 net_gnrc_netif2 + * @{ + * + * @file + * @brief IPv6 defintions for @ref net_gnrc_netif2 + * + * @author Martine Lenders + */ +#ifndef NET_GNRC_NETIF2_IPV6_H +#define NET_GNRC_NETIF2_IPV6_H + +#include "evtimer_msg.h" +#include "net/ipv6/addr.h" +#ifdef MODULE_GNRC_IPV6_NIB +#include "net/gnrc/ipv6/nib/conf.h" +#endif +#include "net/gnrc/netif2/conf.h" +#ifdef MODULE_NETSTATS_IPV6 +#include "net/netstats.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name IPv6 unicast and anycast address flags + * @anchor net_gnrc_netif2_ipv6_addrs_flags + * @{ + */ +/** + * @brief Mask for the address' state + */ +#define GNRC_NETIF2_IPV6_ADDRS_FLAGS_STATE_MASK (0x03U) + +/** + * @brief Tentative address state + */ +#define GNRC_NETIF2_IPV6_ADDRS_FLAGS_STATE_TENTATIVE (0x01U) + +/** + * @brief Deprecated address state (still valid, but not preferred) + */ +#define GNRC_NETIF2_IPV6_ADDRS_FLAGS_STATE_DEPRECATED (0x02U) + +/** + * @brief Valid address state + */ +#define GNRC_NETIF2_IPV6_ADDRS_FLAGS_STATE_VALID (0x03U) + +/** + * @brief Address is an anycast address + */ +#define GNRC_NETIF2_IPV6_ADDRS_FLAGS_ANYCAST (0x04U) +/** @} */ + +/** + * @brief IPv6 component for @ref gnrc_netif2_t + * + * @note only available with @ref net_gnrc_ipv6. + */ +typedef struct { + /** + * @brief Flags for gnrc_netif2_t::ipv6_addrs + * + * @see net_gnrc_netif2_ipv6_addrs_flags + * + * @note Only available with module @ref net_gnrc_ipv6 "gnrc_ipv6". + */ + uint8_t addrs_flags[GNRC_NETIF2_IPV6_ADDRS_NUMOF]; + + /** + * @brief IPv6 unicast and anycast addresses of the interface + * + * @note Only available with module @ref net_gnrc_ipv6 "gnrc_ipv6". + */ + ipv6_addr_t addrs[GNRC_NETIF2_IPV6_ADDRS_NUMOF]; + + /** + * @brief IPv6 multicast groups of the interface + * + * @note Only available with module @ref net_gnrc_ipv6 "gnrc_ipv6". + */ + ipv6_addr_t groups[GNRC_NETIF2_IPV6_GROUPS_NUMOF]; +#ifdef MODULE_NETSTATS_IPV6 + /** + * @brief IPv6 packet statistics + * + * @note Only available with module `netstats_ipv6`. + */ + netstats_t stats; +#endif +#if defined(MODULE_GNRC_IPV6_NIB) || DOXYGEN +#if GNRC_IPV6_NIB_CONF_ROUTER || DOXYGEN + /** + * @brief Route info callback + * + * This callback is called by the @ref net_gnrc_ipv6_nib "NIB" to inform + * the routing protocol about state changes, route usages, missing routes + * etc. + * + * @todo Define types (RRQ, RRN, NSC) in NIB + * + * @param[in] type Type of the route info. + * @param[in] ctx_addr Context address of the route info. + * @param[in] ctx Further context of the route info. + */ + int (*route_info_cb)(unsigned type, const ipv6_addr_t *ctx_addr, + const void *ctx); + /** + * @brief Event for @ref GNRC_IPV6_NIB_SND_MC_RA + * + * @note Only available with module @ref net_gnrc_ipv6 "gnrc_ipv6". + * and @ref net_gnrc_ipv6_nib "NIB" and if + * @ref GNRC_IPV6_NIB_CONF_ROUTER != 0 + */ + evtimer_msg_event_t snd_mc_ra; +#endif /* GNRC_IPV6_NIB_CONF_ROUTER */ +#if GNRC_IPV6_NIB_CONF_ARSM || DOXYGEN + /** + * @brief Event for @ref GNRC_IPV6_NIB_RECALC_REACH_TIME + * + * @note Only available with module @ref net_gnrc_ipv6 "gnrc_ipv6". + * and @ref net_gnrc_ipv6_nib "NIB" and if + * @ref GNRC_IPV6_NIB_CONF_ARSM != 0 + */ + evtimer_msg_event_t recalc_reach_time; +#endif /* GNRC_IPV6_NIB_CONF_ARSM */ + /** + * @brief Event for @ref GNRC_IPV6_NIB_SEARCH_RTR + * + * @note Only available with module @ref net_gnrc_ipv6 "gnrc_ipv6". + * and @ref net_gnrc_ipv6_nib "NIB" + */ + evtimer_msg_event_t search_rtr; + +#if GNRC_IPV6_NIB_CONF_ROUTER || DOXYGEN + /** + * @brief Timestamp in milliseconds of last unsolicited router + * advertisement + * + * @note Only available with module @ref net_gnrc_ipv6 "gnrc_ipv6". + * and @ref net_gnrc_ipv6_nib "NIB" and if + * @ref GNRC_IPV6_NIB_CONF_ROUTER != 0 + */ + uint32_t last_ra; +#endif /* GNRC_IPV6_NIB_CONF_ROUTER */ +#if GNRC_IPV6_NIB_CONF_ARSM || defined(DOXYGEN) + /** + * @brief Base for random reachable time calculation and advertised + * reachable time in ms (if @ref GNRC_NETIF2_FLAGS_IPV6_RTR_ADV is + * set) + * + * @note Only available with module @ref net_gnrc_ipv6 "gnrc_ipv6". + * and @ref net_gnrc_ipv6_nib "NIB" and if + * @ref GNRC_IPV6_NIB_CONF_ARSM != 0 + */ + uint32_t reach_time_base; + + /** + * @brief Reachable time (in ms) + * + * @note Only available with module @ref net_gnrc_ipv6 "gnrc_ipv6". + * and @ref net_gnrc_ipv6_nib "NIB" and if + * @ref GNRC_IPV6_NIB_CONF_ARSM != 0 + */ + uint32_t reach_time; +#endif /* GNRC_IPV6_NIB_CONF_ARSM */ + /** + * @brief Retransmission time and advertised retransmission time (in ms) + * + * @note Only available with module @ref net_gnrc_ipv6 "gnrc_ipv6" and + * @ref net_gnrc_ipv6_nib "NIB" + */ + uint32_t retrans_time; +#if GNRC_IPV6_NIB_CONF_ROUTER || DOXYGEN + /** + * @brief (Advertised) Router lifetime (default 1800). + * + * @note Only available with module @ref net_gnrc_ipv6 "gnrc_ipv6". + * and @ref net_gnrc_ipv6_nib "NIB" and if + * @ref GNRC_IPV6_NIB_CONF_ROUTER != 0 + */ + uint16_t rtr_ltime; + /** + * @brief number of unsolicited router advertisements sent + * + * This only counts up to the first @ref NDP_MAX_INIT_RA_NUMOF on interface + * initialization. The last @ref NDP_MAX_FIN_RA_NUMOF of an advertising + * interface are counted from UINT8_MAX - @ref NDP_MAX_FIN_RA_NUMOF + 1. + * + * @note Only available with module @ref net_gnrc_ipv6 "gnrc_ipv6". + * and @ref net_gnrc_ipv6_nib "NIB" and if + * @ref GNRC_IPV6_NIB_CONF_ROUTER != 0 + */ + uint8_t ra_sent; +#endif +#if GNRC_IPV6_NIB_CONF_6LN || DOXYGEN + /** + * @brief number of unsolicited router solicitations scheduled + * + * @note Only available with module @ref net_gnrc_ipv6 "gnrc_ipv6" and + * @ref net_gnrc_ipv6_nib "NIB" and if + * @ref GNRC_IPV6_NIB_CONF_6LN != 0 + */ + uint8_t rs_sent; +#endif + /** + * @brief number of unsolicited neighbor advertisements scheduled + * + * @note Only available with module @ref net_gnrc_ipv6 "gnrc_ipv6" and + * @ref net_gnrc_ipv6_nib "NIB" + */ + uint8_t na_sent; +#endif /* MODULE_GNRC_IPV6_NIB */ + + /** + * @brief IPv6 auto-address configuration mode + * + * @note Only available with module @ref net_gnrc_ipv6 "gnrc_ipv6" + */ + uint8_t aac_mode; + + /** + * @brief Maximum transition unit (MTU) for IPv6 packets + * + * @note Only available with module @ref net_gnrc_ipv6 "gnrc_ipv6". + */ + uint16_t mtu; +} gnrc_netif2_ipv6_t; + +#ifdef __cplusplus +} +#endif + +#endif /* NET_GNRC_NETIF2_IPV6_H */ +/** @} */ diff --git a/sys/include/net/gnrc/netif2/mac.h b/sys/include/net/gnrc/netif2/mac.h new file mode 100644 index 0000000000000..f2028d1495261 --- /dev/null +++ b/sys/include/net/gnrc/netif2/mac.h @@ -0,0 +1,58 @@ +/* + * 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 net_gnrc_netif2 + * @{ + * + * @file + * @brief @ref net_gnrc_mac definitions for @ref net_gnrc_netif2 + * + * @author Martine Lenders + */ +#ifndef NET_GNRC_NETIF2_MAC_H +#define NET_GNRC_NETIF2_MAC_H + +#include "net/gnrc/mac/mac.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief @ref net_gnrc_mac component of @ref gnrc_netif2_mac_t + */ +typedef struct { +#if ((GNRC_MAC_RX_QUEUE_SIZE != 0) || (GNRC_MAC_DISPATCH_BUFFER_SIZE != 0)) || DOXYGEN + /** + * @brief MAC internal object which stores reception parameters, queues, and + * state machines. + * + * @note Only available if @ref GNRC_MAC_RX_QUEUE_SIZE or + * @ref GNRC_MAC_DISPATCH_BUFFER_SIZE is greater than 0. + */ + gnrc_mac_rx_t rx; +#endif /* ((GNRC_MAC_RX_QUEUE_SIZE != 0) || (GNRC_MAC_DISPATCH_BUFFER_SIZE != 0)) || DOXYGEN */ +#if ((GNRC_MAC_TX_QUEUE_SIZE != 0) || (GNRC_MAC_NEIGHBOR_COUNT != 0)) || DOXYGEN + /** + * @brief MAC internal object which stores transmission parameters, queues, and + * state machines. + * + * @note Only available if @ref GNRC_MAC_TX_QUEUE_SIZE or + * @ref GNRC_MAC_NEIGHBOR_COUNT is greater than 0. + */ + gnrc_mac_tx_t tx; +#endif /* ((GNRC_MAC_TX_QUEUE_SIZE != 0) || (GNRC_MAC_NEIGHBOR_COUNT == 0)) || DOXYGEN */ +} gnrc_netif2_mac_t; + +#ifdef __cplusplus +} +#endif + +#endif /* NET_GNRC_NETIF2_MAC_H */ +/** @} */ diff --git a/sys/include/net/netopt.h b/sys/include/net/netopt.h index fd56b81feee94..d1aaf730d6761 100644 --- a/sys/include/net/netopt.h +++ b/sys/include/net/netopt.h @@ -58,6 +58,11 @@ typedef enum { */ NETOPT_NID, + /** + * @brief get/set hop limit as uint8_t + */ + NETOPT_HOP_LIMIT, + /** * @brief get the IPv6 interface identifier of a network interface as * eui64_t. @@ -73,6 +78,58 @@ typedef enum { * RFC 4944). */ NETOPT_IPV6_IID, + + /** + * @brief get IPv6 addresses of an interface as array of @ref ipv6_addr_t + * or add an IPv6 address as @ref ipv6_addr_t to an interface + * + * When adding an IPv6 address to a GNRC interface using + * @ref GNRC_NETAPI_MSG_TYPE_SET, the gnrc_netapi_opt_t::context field can + * be used to pass the prefix length (8 MSB) and some flags (8 LSB) + * according to @ref net_gnrc_netif2_ipv6_addrs_flags. The address is however always + * considered to be manually added. + * When getting the option you can pass an array of @ref ipv6_addr_t of any + * length greater than 0 to the getter. The array will be filled up to to + * its maximum and the remaining addresses on the interface will be ignored + */ + NETOPT_IPV6_ADDR, + /** + * @brief Removes an IPv6 address as @ref ipv6_addr_t from an interface + */ + NETOPT_IPV6_ADDR_REMOVE, + /** + * @brief get the flags to the addresses returned by @ref NETOPT_IPV6_ADDR + * as array of uint8_t + * + * The information contained in the array is very specific to the + * interface's API. For GNRC e.g. the values are according to + * @ref net_gnrc_netif2_ipv6_addrs_flags. + */ + NETOPT_IPV6_ADDR_FLAGS, + /** + * @brief get IPv6 multicast groups of an interface as array of + * @ref ipv6_addr_t or join an IPv6 multicast group as + * @ref ipv6_addr_t on an interface + * + * When adding an IPv6 address to a GNRC interface using + * @ref GNRC_NETAPI_MSG_TYPE_SET, the gnrc_netapi_opt_t::context field can + * be used to pass the prefix length (8 MSB) and some flags (8 LSB) + * according to @ref net_gnrc_netif2_ipv6_addrs_flags. The address is however always + * considered to be manually added. + * When getting the option you can pass an array of @ref ipv6_addr_t of any + * length greater than 0 to the getter. The array will be filled up to to + * its maximum and the remaining addresses on the interface will be ignored + */ + NETOPT_IPV6_GROUP, + /** + * @brief Leaves an IPv6 multicast group as @ref ipv6_addr_t on an + * interface + */ + NETOPT_IPV6_GROUP_LEAVE, + NETOPT_IPV6_FORWARDING, /**< en/disable IPv6 forwarding or read the + * current state */ + NETOPT_IPV6_SND_RTR_ADV, /**< en/disable sending of IPv6 router + * advertisements or read the current state */ NETOPT_TX_POWER, /**< get/set the output power for radio * devices in dBm as int16_t in host byte * order */ @@ -369,6 +426,10 @@ typedef enum { */ NETOPT_IQ_INVERT, + NETOPT_6LO_IPHC, /**< en/disable header compression according to + * [RFC 6282](https://tools.ietf.org/html/rfc6282) + * or read the current state */ + /** * @brief Get retry amount from missing ACKs of the last transmission * diff --git a/sys/net/crosslayer/netopt/netopt.c b/sys/net/crosslayer/netopt/netopt.c index f91adf297708d..de6a2a93208ef 100644 --- a/sys/net/crosslayer/netopt/netopt.c +++ b/sys/net/crosslayer/netopt/netopt.c @@ -31,7 +31,15 @@ static const char *_netopt_strmap[] = { [NETOPT_ADDR_LEN] = "NETOPT_ADDR_LEN", [NETOPT_SRC_LEN] = "NETOPT_SRC_LEN", [NETOPT_NID] = "NETOPT_NID", + [NETOPT_HOP_LIMIT] = "NETOPT_HOP_LIMIT", [NETOPT_IPV6_IID] = "NETOPT_IPV6_IID", + [NETOPT_IPV6_ADDR] = "NETOPT_IPV6_ADDR", + [NETOPT_IPV6_ADDR_REMOVE] = "NETOPT_IPV6_ADDR_REMOVE", + [NETOPT_IPV6_ADDR_FLAGS] = "NETOPT_IPV6_ADDR_FLAGS", + [NETOPT_IPV6_GROUP] = "NETOPT_IPV6_GROUP", + [NETOPT_IPV6_GROUP_LEAVE] = "NETOPT_IPV6_GROUP_LEAVE", + [NETOPT_IPV6_FORWARDING] = "NETOPT_IPV6_FORWARDING", + [NETOPT_IPV6_SND_RTR_ADV] = "NETOPT_IPV6_SND_RTR_ADV", [NETOPT_TX_POWER] = "NETOPT_TX_POWER", [NETOPT_MAX_PACKET_SIZE] = "NETOPT_MAX_PACKET_SIZE", [NETOPT_PRELOADING] = "NETOPT_PRELOADING", @@ -77,6 +85,7 @@ static const char *_netopt_strmap[] = { [NETOPT_FIXED_HEADER] = "NETOPT_FIXED_HEADER", [NETOPT_IQ_INVERT] = "NETOPT_IQ_INVERT", [NETOPT_TX_RETRIES_NEEDED] = "NETOPT_TX_RETRIES_NEEDED", + [NETOPT_6LO_IPHC] = "NETOPT_6LO_IPHC", [NETOPT_NUMOF] = "NETOPT_NUMOF", }; diff --git a/sys/net/gnrc/Makefile b/sys/net/gnrc/Makefile index 7b9670b4fe6a8..bb256db02e424 100644 --- a/sys/net/gnrc/Makefile +++ b/sys/net/gnrc/Makefile @@ -49,6 +49,9 @@ endif ifneq (,$(filter gnrc_netapi,$(USEMODULE))) DIRS += netapi endif +ifneq (,$(filter gnrc_netif2,$(USEMODULE))) + DIRS += netif2 +endif ifneq (,$(filter gnrc_netif,$(USEMODULE))) DIRS += netif endif diff --git a/sys/net/gnrc/netif2/Makefile b/sys/net/gnrc/netif2/Makefile new file mode 100644 index 0000000000000..590336f5daeca --- /dev/null +++ b/sys/net/gnrc/netif2/Makefile @@ -0,0 +1,3 @@ +MODULE := gnrc_netif2 + +include $(RIOTBASE)/Makefile.base diff --git a/sys/net/gnrc/netif2/gnrc_netif2.c b/sys/net/gnrc/netif2/gnrc_netif2.c new file mode 100644 index 0000000000000..50c0552952973 --- /dev/null +++ b/sys/net/gnrc/netif2/gnrc_netif2.c @@ -0,0 +1,1262 @@ +/* + * Copyright (C) 2014-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. + */ + +/** + * @{ + * + * @file + * @author Martine Lenders + * @author René Kijewski + * @author Oliver Hahm + */ + +#include "bitfield.h" +#include "net/ethernet.h" +#include "net/ipv6.h" +#include "net/gnrc.h" +#ifdef MODULE_NETSTATS_IPV6 +#include "net/netstats.h" +#endif +#include "log.h" +#include "sched.h" + +#include "net/gnrc/netif2.h" +#include "net/gnrc/netif2/internal.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#if ENABLE_DEBUG +static char addr_str[IPV6_ADDR_MAX_STR_LEN]; +#endif + +#define _NETIF_NETAPI_MSG_QUEUE_SIZE (8) + +static gnrc_netif2_t _netifs[GNRC_NETIF_NUMOF]; + +static void _update_l2addr_from_dev(gnrc_netif2_t *netif); +static void *_gnrc_netif2_thread(void *args); +static void _event_cb(netdev_t *dev, netdev_event_t event); + +gnrc_netif2_t *gnrc_netif2_create(char *stack, int stacksize, char priority, + const char *name, netdev_t *netdev, + const gnrc_netif2_ops_t *ops) +{ + gnrc_netif2_t *netif = NULL; + int res; + + for (int i = 0; i < GNRC_NETIF_NUMOF; i++) { + if (_netifs[i].ops == NULL) { + netif = &_netifs[i]; + break; + } + } + if (netif == NULL) { + LOG_ERROR("gnrc_netif2: can not allocate network interface.\n" + "Set GNRC_NETIF_NUMOF to a higher value\n"); + return NULL; + } + rmutex_init(&netif->mutex); + netif->ops = ops; + assert(netif->dev == NULL); + netif->dev = netdev; + res = thread_create(stack, stacksize, priority, THREAD_CREATE_STACKTEST, + _gnrc_netif2_thread, (void *)netif, name); + (void)res; + assert(res > 0); + return netif; +} + +unsigned gnrc_netif2_numof(void) +{ + gnrc_netif2_t *netif = NULL; + unsigned res = 0; + + while ((netif = gnrc_netif2_iter(netif))) { + if (netif->ops != NULL) { + res++; + } + } + return res; +} + +gnrc_netif2_t *gnrc_netif2_iter(const gnrc_netif2_t *prev) +{ + assert((prev == NULL) || (prev >= _netifs)); + for (const gnrc_netif2_t *netif = (prev == NULL) ? _netifs : (prev + 1); + netif < (_netifs + GNRC_NETIF_NUMOF); netif++) { + if (netif->ops != NULL) { + /* we don't care about external modification */ + return (gnrc_netif2_t *)netif; + } + } + return NULL; +} + +int gnrc_netif2_get_from_netdev(gnrc_netif2_t *netif, gnrc_netapi_opt_t *opt) +{ + int res = -ENOTSUP; + + gnrc_netif2_acquire(netif); + switch (opt->opt) { + case NETOPT_HOP_LIMIT: + assert(opt->data_len == sizeof(uint8_t)); + *((uint8_t *)opt->data) = netif->cur_hl; + res = sizeof(uint8_t); + break; + case NETOPT_STATS: + /* XXX discussed this with Oleg, it's supposed to be a pointer */ + switch ((int16_t)opt->context) { +#if defined(MODULE_NETSTATS_IPV6) && defined(MODULE_GNRC_IPV6) + case NETSTATS_IPV6: + assert(opt->data_len == sizeof(netstats_t *)); + *((netstats_t **)opt->data) = &netif->ipv6.stats; + res = sizeof(&netif->ipv6.stats); + break; +#endif + default: + /* take from device */ + break; + } + break; +#ifdef MODULE_GNRC_IPV6 + case NETOPT_IPV6_ADDR: { + assert(opt->data_len >= sizeof(ipv6_addr_t)); + ipv6_addr_t *tgt = opt->data; + + res = 0; + for (unsigned i = 0; + (res < opt->data_len) && (i < GNRC_NETIF2_IPV6_ADDRS_NUMOF); + i++, tgt++) { + if (netif->ipv6.addrs_flags[i] != 0) { + memcpy(tgt, &netif->ipv6.addrs[i], sizeof(ipv6_addr_t)); + res += sizeof(ipv6_addr_t); + } + } + } + break; + case NETOPT_IPV6_ADDR_FLAGS: { + assert(opt->data_len >= sizeof(uint8_t)); + uint8_t *tgt = opt->data; + + res = 0; + for (unsigned i = 0; + (res < opt->data_len) && (i < GNRC_NETIF2_IPV6_ADDRS_NUMOF); + i++, tgt++) { + if (netif->ipv6.addrs_flags[i] != 0) { + *tgt = netif->ipv6.addrs_flags[i]; + res += sizeof(uint8_t); + } + } + } + break; + case NETOPT_IPV6_GROUP: { + assert(opt->data_len >= sizeof(ipv6_addr_t)); + ipv6_addr_t *tgt = opt->data; + + res = 0; + for (unsigned i = 0; + (res < opt->data_len) && (i < GNRC_NETIF2_IPV6_GROUPS_NUMOF); + i++, tgt++) { + if (!ipv6_addr_is_unspecified(&netif->ipv6.groups[i])) { + memcpy(tgt, &netif->ipv6.groups[i], sizeof(ipv6_addr_t)); + res += sizeof(ipv6_addr_t); + } + } + } + break; + case NETOPT_IPV6_IID: + assert(opt->data_len >= sizeof(eui64_t)); + if (gnrc_netif2_ipv6_get_iid(netif, opt->data) == 0) { + res = sizeof(eui64_t); + } + break; + case NETOPT_MAX_PACKET_SIZE: + if (opt->context == GNRC_NETTYPE_IPV6) { + assert(opt->data_len == sizeof(uint16_t)); + *((uint16_t *)opt->data) = netif->ipv6.mtu; + res = sizeof(uint16_t); + } + /* else ask device */ + break; +#if GNRC_IPV6_NIB_CONF_ROUTER + case NETOPT_IPV6_FORWARDING: + assert(opt->data_len == sizeof(netopt_enable_t)); + *((netopt_enable_t *)opt->data) = (gnrc_netif2_is_rtr(netif)) ? + NETOPT_ENABLE : NETOPT_DISABLE; + res = sizeof(netopt_enable_t); + break; + case NETOPT_IPV6_SND_RTR_ADV: + assert(opt->data_len == sizeof(netopt_enable_t)); + *((netopt_enable_t *)opt->data) = (gnrc_netif2_is_rtr_adv(netif)) ? + NETOPT_ENABLE : NETOPT_DISABLE; + res = sizeof(netopt_enable_t); + break; +#endif /* GNRC_IPV6_NIB_CONF_ROUTER */ +#endif /* MODULE_GNRC_IPV6 */ +#ifdef MODULE_GNRC_SIXLOWPAN_IPHC + case NETOPT_6LO_IPHC: + assert(opt->data_len == sizeof(netopt_enable_t)); + *((netopt_enable_t *)opt->data) = (netif->flags & GNRC_NETIF2_FLAGS_6LO_HC) ? + NETOPT_ENABLE : NETOPT_DISABLE; + res = sizeof(netopt_enable_t); + break; +#endif /* MODULE_GNRC_SIXLOWPAN_IPHC */ + default: + break; + } + if (res == -ENOTSUP) { + res = netif->dev->driver->get(netif->dev, opt->opt, opt->data, opt->data_len); + } + gnrc_netif2_release(netif); + return res; +} + +int gnrc_netif2_set_from_netdev(gnrc_netif2_t *netif, + const gnrc_netapi_opt_t *opt) +{ + int res = -ENOTSUP; + + gnrc_netif2_acquire(netif); + switch (opt->opt) { + case NETOPT_HOP_LIMIT: + assert(opt->data_len == sizeof(uint8_t)); + netif->cur_hl = *((uint8_t *)opt->data); + res = sizeof(uint8_t); + break; +#ifdef MODULE_GNRC_IPV6 + case NETOPT_IPV6_ADDR: { + assert(opt->data_len == sizeof(ipv6_addr_t)); + /* always assume manually added */ + uint8_t flags = ((((uint8_t)opt->context & 0xff) & + ~GNRC_NETIF2_IPV6_ADDRS_FLAGS_STATE_MASK) | + GNRC_NETIF2_IPV6_ADDRS_FLAGS_STATE_VALID); + uint8_t pfx_len = (uint8_t)(opt->context >> 8U); + /* acquire locks a recursive mutex so we are save calling this + * public function */ + gnrc_netif2_ipv6_addr_add(netif, opt->data, pfx_len, flags); + res = sizeof(ipv6_addr_t); + } + break; + case NETOPT_IPV6_ADDR_REMOVE: + assert(opt->data_len == sizeof(ipv6_addr_t)); + /* acquire locks a recursive mutex so we are save calling this + * public function */ + gnrc_netif2_ipv6_addr_remove(netif, opt->data); + res = sizeof(ipv6_addr_t); + break; + case NETOPT_IPV6_GROUP: + assert(opt->data_len == sizeof(ipv6_addr_t)); + /* acquire locks a recursive mutex so we are save calling this + * public function */ + gnrc_netif2_ipv6_group_join(netif, opt->data); + res = sizeof(ipv6_addr_t); + break; + case NETOPT_IPV6_GROUP_LEAVE: + assert(opt->data_len == sizeof(ipv6_addr_t)); + /* acquire locks a recursive mutex so we are save calling this + * public function */ + gnrc_netif2_ipv6_group_leave(netif, opt->data); + res = sizeof(ipv6_addr_t); + break; + case NETOPT_MAX_PACKET_SIZE: + if (opt->context == GNRC_NETTYPE_IPV6) { + assert(opt->data_len == sizeof(uint16_t)); + netif->ipv6.mtu = *((uint16_t *)opt->data); + res = sizeof(uint16_t); + } + /* else set device */ + break; +#if GNRC_IPV6_NIB_CONF_ROUTER + case NETOPT_IPV6_FORWARDING: + assert(opt->data_len == sizeof(netopt_enable_t)); + if (*(((netopt_enable_t *)opt->data)) == NETOPT_ENABLE) { + netif->flags |= GNRC_NETIF2_FLAGS_IPV6_FORWARDING; + } + else { + if (gnrc_netif2_is_rtr_adv(netif)) { + gnrc_ipv6_nib_iface_cease_rtr_adv(netif); + } + netif->flags &= ~GNRC_NETIF2_FLAGS_IPV6_FORWARDING; + } + res = sizeof(netopt_enable_t); + break; + case NETOPT_IPV6_SND_RTR_ADV: + assert(opt->data_len == sizeof(netopt_enable_t)); + if (*(((netopt_enable_t *)opt->data)) == NETOPT_ENABLE) { + gnrc_ipv6_nib_iface_start_rtr_adv(netif); + } + else { + gnrc_ipv6_nib_iface_cease_rtr_adv(netif); + } + res = sizeof(netopt_enable_t); + break; +#endif /* GNRC_IPV6_NIB_CONF_ROUTER */ +#endif /* MODULE_GNRC_IPV6 */ +#ifdef MODULE_GNRC_SIXLOWPAN_IPHC + case NETOPT_6LO_IPHC: + assert(opt->data_len == sizeof(netopt_enable_t)); + if (*(((netopt_enable_t *)opt->data)) == NETOPT_ENABLE) { + netif->flags |= GNRC_NETIF2_FLAGS_6LO_HC; + } + else { + netif->flags &= ~GNRC_NETIF2_FLAGS_6LO_HC; + } + res = sizeof(netopt_enable_t); + break; +#endif /* MODULE_GNRC_SIXLOWPAN_IPHC */ + default: + break; + } + if (res == -ENOTSUP) { + res = netif->dev->driver->set(netif->dev, opt->opt, opt->data, + opt->data_len); + if (res > 0) { + switch (opt->opt) { + case NETOPT_ADDRESS: + case NETOPT_ADDRESS_LONG: + case NETOPT_ADDR_LEN: + case NETOPT_SRC_LEN: + _update_l2addr_from_dev(netif); + break; + default: + break; + } + } + } + gnrc_netif2_release(netif); + return res; +} + +gnrc_netif2_t *gnrc_netif2_get_by_pid(kernel_pid_t pid) +{ + gnrc_netif2_t *netif = NULL; + + while ((netif = gnrc_netif2_iter(netif))) { + if (netif->pid == pid) { + return netif; + } + } + return NULL; +} + +static inline char _half_byte_to_char(uint8_t half_byte) +{ + return (half_byte < 10) ? ('0' + half_byte) : ('a' + (half_byte - 10)); +} + +char *gnrc_netif2_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++) = _half_byte_to_char(*(addr) >> 4); + *(out++) = _half_byte_to_char(*(addr++) & 0xf); + *(out++) = (i == (addr_len - 1)) ? '\0' : ':'; + } + return res; +} + +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_; + } +} + +size_t gnrc_netif2_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; +} + +#ifdef MODULE_GNRC_IPV6 +static inline bool _addr_anycast(const gnrc_netif2_t *netif, unsigned idx); +static int _addr_idx(const gnrc_netif2_t *netif, const ipv6_addr_t *addr); + +/** + * @brief Matches an address by prefix to an address on the interface + * + * @param[in] netif the network interface + * @param[in] addr the address to match + * @param[in] filter a bitfield with the bits at the position equal to the + * indexes of the addresses you want to include in the + * search set to one. NULL for all addresses + * @param[out] idx index of the best match. -1 if no match was found. + * + * @return bits up to which the best match matches @p addr + * @return 0, if no match was found + */ +static unsigned _match(const gnrc_netif2_t *netif, const ipv6_addr_t *addr, + const uint8_t *filter, int *idx); + +/** + * @brief Determines the scope of the given address. + * + * @param[in] addr The IPv6 address to check. + * + * @return The scope of the address. + * + * @pre address is not loopback or unspecified. + * see http://tools.ietf.org/html/rfc6724#section-4 + */ +static uint8_t _get_scope(const ipv6_addr_t *addr); +static inline unsigned _get_state(const gnrc_netif2_t *netif, unsigned idx); + +/** + * @brief selects potential source address candidates + * @see + * RFC6724, section 4 + * + * @param[in] netif the interface used for sending + * @param[in] dst the destination address + * @param[in] ll_only only consider link-local addresses + * @param[out] candidate_set a bitfield representing all addresses + * configured to @p netif, potential candidates + * will be marked as 1 + * + * @return -1 if no candidates were found + * @return the index of the first candidate otherwise + * + * @pre the interface entry and its set of addresses must not be changed during + * runtime of this function + */ +static int _create_candidate_set(const gnrc_netif2_t *netif, + const ipv6_addr_t *dst, bool ll_only, + uint8_t *candidate_set); + +/** @brief Find the best candidate among the configured addresses + * for a certain destination address according to the 8 rules + * specified in RFC 6734, section 5. + * @see + * RFC6724, section 5 + * + * + * @param[in] netif The interface for sending. + * @param[in] dst The destination IPv6 address. + * @param[in, out] candidate_set The preselected set of candidate addresses as + * a bitfield. + * + * @pre @p dst is not unspecified. + * + * @return The best matching candidate found on @p netif, may be NULL if none + * is found. + */ +static ipv6_addr_t *_src_addr_selection(gnrc_netif2_t *netif, + const ipv6_addr_t *dst, + uint8_t *candidate_set); +static int _group_idx(const gnrc_netif2_t *netif, const ipv6_addr_t *addr); + +int gnrc_netif2_ipv6_addr_add(gnrc_netif2_t *netif, const ipv6_addr_t *addr, + unsigned pfx_len, uint8_t flags) +{ + unsigned idx = UINT_MAX; + + assert((netif != NULL) && (addr != NULL)); + assert(!(ipv6_addr_is_multicast(addr) || ipv6_addr_is_unspecified(addr) || + ipv6_addr_is_loopback(addr))); + assert((pfx_len > 0) && (pfx_len <= 128)); + gnrc_netif2_acquire(netif); + if ((flags & GNRC_NETIF2_IPV6_ADDRS_FLAGS_STATE_MASK) == 0) { + flags |= GNRC_NETIF2_IPV6_ADDRS_FLAGS_STATE_TENTATIVE; + } + for (unsigned i = 0; i < GNRC_NETIF2_IPV6_ADDRS_NUMOF; i++) { + if (ipv6_addr_equal(&netif->ipv6.addrs[i], addr)) { + gnrc_netif2_release(netif); + return i; + } + if ((idx == UINT_MAX) && (netif->ipv6.addrs_flags[i] == 0)) { + idx = i; + } + } + if (idx == UINT_MAX) { + gnrc_netif2_release(netif); + return -ENOMEM; + } + netif->ipv6.addrs_flags[idx] = flags; + memcpy(&netif->ipv6.addrs[idx], addr, sizeof(netif->ipv6.addrs[idx])); + /* TODO: + * - update prefix list, if flags == VALID + * - with SLAAC, send out NS otherwise for DAD probing */ + (void)pfx_len; + gnrc_netif2_release(netif); + return idx; +} + +void gnrc_netif2_ipv6_addr_remove(gnrc_netif2_t *netif, + const ipv6_addr_t *addr) +{ + int idx; + + assert((netif != NULL) && (addr != NULL)); + gnrc_netif2_acquire(netif); + idx = _addr_idx(netif, addr); + if (idx >= 0) { + netif->ipv6.addrs_flags[idx] = 0; + ipv6_addr_set_unspecified(&netif->ipv6.addrs[idx]); + /* TODO: + * - update prefix list, if necessary */ + } + gnrc_netif2_release(netif); +} + +int gnrc_netif2_ipv6_addr_idx(gnrc_netif2_t *netif, + const ipv6_addr_t *addr) +{ + int idx; + + assert((netif != NULL) && (addr != NULL)); + DEBUG("gnrc_netif2: get index of %s from inteface %i\n", + ipv6_addr_to_str(addr_str, addr, sizeof(addr_str)), + netif->pid); + gnrc_netif2_acquire(netif); + idx = _addr_idx(netif, addr); + gnrc_netif2_release(netif); + return idx; +} + +int gnrc_netif2_ipv6_addr_match(gnrc_netif2_t *netif, + const ipv6_addr_t *addr) +{ + int idx; + + assert((netif != NULL) && (addr != NULL)); + gnrc_netif2_acquire(netif); + _match(netif, addr, NULL, &idx); + gnrc_netif2_release(netif); + return idx; +} + +ipv6_addr_t *gnrc_netif2_ipv6_addr_best_src(gnrc_netif2_t *netif, + const ipv6_addr_t *dst, + bool ll_only) +{ + ipv6_addr_t *best_src = NULL; + BITFIELD(candidate_set, GNRC_NETIF2_IPV6_ADDRS_NUMOF); + + assert((netif != NULL) && (dst != NULL)); + memset(candidate_set, 0, sizeof(candidate_set)); + gnrc_netif2_acquire(netif); + int first_candidate = _create_candidate_set(netif, dst, ll_only, + candidate_set); + if (first_candidate >= 0) { + best_src = _src_addr_selection(netif, dst, candidate_set); + if (best_src == NULL) { + best_src = &(netif->ipv6.addrs[first_candidate]); + } + } + gnrc_netif2_release(netif); + return best_src; +} + +gnrc_netif2_t *gnrc_netif2_get_by_ipv6_addr(const ipv6_addr_t *addr) +{ + gnrc_netif2_t *netif = NULL; + + DEBUG("gnrc_netif2: get interface by IPv6 address %s\n", + ipv6_addr_to_str(addr_str, addr, sizeof(addr_str))); + while ((netif = gnrc_netif2_iter(netif))) { + if (_addr_idx(netif, addr) >= 0) { + break; + } + if (_group_idx(netif, addr) >= 0) { + break; + } + } + return netif; +} + +gnrc_netif2_t *gnrc_netif2_get_by_prefix(const ipv6_addr_t *prefix) +{ + gnrc_netif2_t *netif = NULL, *best_netif = NULL; + unsigned best_match = 0; + + while ((netif = gnrc_netif2_iter(netif))) { + unsigned match; + int idx; + + if (((match = _match(netif, prefix, NULL, &idx)) > 0) && + (match > best_match)) { + best_match = match; + best_netif = netif; + } + } + return best_netif; +} + +int gnrc_netif2_ipv6_group_join(gnrc_netif2_t *netif, + const ipv6_addr_t *addr) +{ + unsigned idx = UINT_MAX; + + gnrc_netif2_acquire(netif); + for (unsigned i = 0; i < GNRC_NETIF2_IPV6_GROUPS_NUMOF; i++) { + if (ipv6_addr_equal(&netif->ipv6.groups[i], addr)) { + gnrc_netif2_release(netif); + return i; + } + if ((idx == UINT_MAX) && (ipv6_addr_is_unspecified(&netif->ipv6.groups[i]))) { + idx = i; + } + } + if (idx == UINT_MAX) { + gnrc_netif2_release(netif); + return -ENOMEM; + } + memcpy(&netif->ipv6.groups[idx], addr, sizeof(netif->ipv6.groups[idx])); + /* TODO: + * - MLD action + */ + gnrc_netif2_release(netif); + return idx; +} + +void gnrc_netif2_ipv6_group_leave(gnrc_netif2_t *netif, + const ipv6_addr_t *addr) +{ + int idx; + + assert((netif != NULL) && (addr != NULL)); + gnrc_netif2_acquire(netif); + idx = _group_idx(netif, addr); + if (idx >= 0) { + ipv6_addr_set_unspecified(&netif->ipv6.groups[idx]); + /* TODO: + * - MLD action */ + } + gnrc_netif2_release(netif); +} + +int gnrc_netif2_ipv6_group_idx(gnrc_netif2_t *netif, const ipv6_addr_t *addr) +{ + int idx; + + assert((netif != NULL) && (addr != NULL)); + gnrc_netif2_acquire(netif); + idx = _group_idx(netif, addr); + gnrc_netif2_release(netif); + return idx; +} + +int gnrc_netif2_ipv6_get_iid(gnrc_netif2_t *netif, eui64_t *eui64) +{ +#if GNRC_NETIF2_L2ADDR_MAXLEN > 0 + if (netif->flags & GNRC_NETIF2_FLAGS_HAS_L2ADDR) { + switch (netif->device_type) { +#ifdef MODULE_NETDEV_ETH + case NETDEV_TYPE_ETHERNET: + assert(netif->l2addr_len == ETHERNET_ADDR_LEN); + eui64->uint8[0] = netif->l2addr[0] ^ 0x02; + eui64->uint8[1] = netif->l2addr[1]; + eui64->uint8[2] = netif->l2addr[2]; + eui64->uint8[3] = 0xff; + eui64->uint8[4] = 0xfe; + eui64->uint8[5] = netif->l2addr[3]; + eui64->uint8[6] = netif->l2addr[4]; + eui64->uint8[7] = netif->l2addr[5]; + return 0; +#endif +#ifdef MODULE_NETDEV_IEEE802154 + case NETDEV_TYPE_IEEE802154: + switch (netif->l2addr_len) { + case IEEE802154_SHORT_ADDRESS_LEN: + eui64->uint8[0] = 0x0; + eui64->uint8[1] = 0x0; + eui64->uint8[2] = 0x0; + eui64->uint8[3] = 0xff; + eui64->uint8[4] = 0xfe; + eui64->uint8[5] = 0x0; + eui64->uint8[6] = netif->l2addr[0]; + eui64->uint8[7] = netif->l2addr[1]; + return 0; + case IEEE802154_LONG_ADDRESS_LEN: + memcpy(eui64, netif->l2addr, sizeof(eui64_t)); + eui64->uint8[0] ^= 0x02; + return 0; + default: + /* this should not happen */ + assert(false); + break; + } + break; +#endif +#ifdef MODULE_CC110X + case NETDEV_TYPE_CC110X: + assert(netif->l2addr_len == 1U); + eui64->uint8[0] = 0x0; + eui64->uint8[1] = 0x0; + eui64->uint8[2] = 0x0; + eui64->uint8[3] = 0xff; + eui64->uint8[4] = 0xfe; + eui64->uint8[5] = 0x0; + eui64->uint8[6] = 0x0; + eui64->uint8[7] = netif->l2addr[0]; + return 0; +#endif + default: + (void)eui64; + break; + } + } +#endif + return -ENOTSUP; +} + +static inline bool _addr_anycast(const gnrc_netif2_t *netif, unsigned idx) +{ + return (netif->ipv6.addrs_flags[idx] & GNRC_NETIF2_IPV6_ADDRS_FLAGS_ANYCAST); +} + +static int _addr_idx(const gnrc_netif2_t *netif, const ipv6_addr_t *addr) +{ + for (unsigned i = 0; i < GNRC_NETIF2_IPV6_ADDRS_NUMOF; i++) { + if (ipv6_addr_equal(&netif->ipv6.addrs[i], addr)) { + return i; + } + } + return -1; +} + +static unsigned _match(const gnrc_netif2_t *netif, const ipv6_addr_t *addr, + const uint8_t *filter, int *idx) +{ + unsigned best_match = 0; + + assert(idx != NULL); + *idx = -1; + for (int i = 0; i < GNRC_NETIF2_IPV6_ADDRS_NUMOF; i++) { + unsigned match; + + if ((netif->ipv6.addrs_flags[i] == 0) || + ((filter != NULL) && _addr_anycast(netif, i)) || + /* discard const intentionally */ + ((filter != NULL) && !(bf_isset((uint8_t *)filter, i)))) { + continue; + } + match = ipv6_addr_match_prefix(&(netif->ipv6.addrs[i]), addr); + if (((match > 64U) || !ipv6_addr_is_link_local(&(netif->ipv6.addrs[i]))) && + (match > best_match)) { + if (idx != NULL) { + *idx = i; + } + best_match = match; + } + } +#if ENABLE_DEBUG + if (*idx >= 0) { + DEBUG("gnrc_netif2: Found %s on interface %" PRIkernel_pid " matching ", + ipv6_addr_to_str(addr_str, &netif->ipv6.addrs[*idx], + sizeof(addr_str)), + netif->pid); + DEBUG("%s by %" PRIu8 " bits (used as source address = %s)\n", + ipv6_addr_to_str(addr_str, addr, sizeof(addr_str)), + best_match, + (filter != NULL) ? "true" : "false"); + } + else { + DEBUG("gnrc_netif2: Did not found any address on interface %" PRIkernel_pid + " matching %s (used as source address = %s)\n", + netif->pid, + ipv6_addr_to_str(addr_str, addr, sizeof(addr_str)), + (filter != NULL) ? "true" : "false"); + } +#endif + return best_match; +} + +static uint8_t _get_scope(const ipv6_addr_t *addr) +{ + if (ipv6_addr_is_link_local(addr)) { + return IPV6_ADDR_MCAST_SCP_LINK_LOCAL; + } + else if (ipv6_addr_is_site_local(addr)) { + return IPV6_ADDR_MCAST_SCP_SITE_LOCAL; + } + else { + return IPV6_ADDR_MCAST_SCP_GLOBAL; + } +} + +static inline unsigned _get_state(const gnrc_netif2_t *netif, unsigned idx) +{ + return (netif->ipv6.addrs_flags[idx] & + GNRC_NETIF2_IPV6_ADDRS_FLAGS_STATE_MASK); +} + +/** + * @brief selects potential source address candidates + * @see + * RFC6724, section 4 + * + * @param[in] netif the interface used for sending + * @param[in] dst the destination address + * @param[in] ll_only only consider link-local addresses + * @param[out] candidate_set a bitfield representing all addresses + * configured to @p netif, potential candidates + * will be marked as 1 + * + * @return -1 if no candidates were found + * @return the index of the first candidate otherwise + * + * @pre the interface entry and its set of addresses must not be changed during + * runtime of this function + */ +static int _create_candidate_set(const gnrc_netif2_t *netif, + const ipv6_addr_t *dst, bool ll_only, + uint8_t *candidate_set) +{ + int res = -1; + + DEBUG("gathering candidates\n"); + /* currently this implementation supports only addresses as source address + * candidates assigned to this interface. Thus we assume all addresses to be + * on interface @p netif */ + (void) dst; + for (int i = 0; i < GNRC_NETIF2_IPV6_ADDRS_NUMOF; i++) { + const ipv6_addr_t *tmp = &(netif->ipv6.addrs[i]); + + DEBUG("Checking address: %s\n", + ipv6_addr_to_str(addr_str, tmp, sizeof(addr_str))); + /* "In any case, multicast addresses and the unspecified address MUST NOT + * be included in a candidate set." + */ + if ((netif->ipv6.addrs_flags[i] == 0) || + (gnrc_netif2_ipv6_addr_get_state(netif, i) == + GNRC_NETIF2_IPV6_ADDRS_FLAGS_STATE_TENTATIVE)) { + continue; + } + /* Check if we only want link local addresses */ + if (ll_only && !ipv6_addr_is_link_local(tmp)) { + continue; + } + /* "For all multicast and link-local destination addresses, the set of + * candidate source addresses MUST only include addresses assigned to + * interfaces belonging to the same link as the outgoing interface." + * + * "For site-local unicast destination addresses, the set of candidate + * source addresses MUST only include addresses assigned to interfaces + * belonging to the same site as the outgoing interface." + * -> we should also be fine, since we're only iterating addresses of + * the sending interface + */ + /* put all other addresses into the candidate set */ + DEBUG("add to candidate set\n"); + bf_set(candidate_set, i); + if (res < 0) { + res = i; + } + } + return res; +} + +/* number of "points" assigned to an source address candidate with equal scope + * than destination address */ +#define RULE_2A_PTS (4) +/* number of "points" assigned to an source address candidate with smaller scope + * than destination address */ +#define RULE_2B_PTS (2) +/* number of "points" assigned to an source address candidate in preferred state */ +#define RULE_3_PTS (1) + +static ipv6_addr_t *_src_addr_selection(gnrc_netif2_t *netif, + const ipv6_addr_t *dst, + uint8_t *candidate_set) +{ + /* create temporary set for assigning "points" to candidates wining in the + * corresponding rules. + */ + uint8_t winner_set[GNRC_NETIF2_IPV6_ADDRS_NUMOF]; + + memset(winner_set, 0, GNRC_NETIF2_IPV6_ADDRS_NUMOF); + uint8_t max_pts = 0; + /* _create_candidate_set() assures that `dst` is not unspecified and if + * `dst` is loopback rule 1 will fire anyway. */ + uint8_t dst_scope = _get_scope(dst); + + DEBUG("finding the best match within the source address candidates\n"); + for (unsigned i = 0; i < GNRC_NETIF2_IPV6_ADDRS_NUMOF; i++) { + ipv6_addr_t *ptr = &(netif->ipv6.addrs[i]); + + DEBUG("Checking address: %s\n", + ipv6_addr_to_str(addr_str, ptr, sizeof(addr_str))); + /* entries which are not part of the candidate set can be ignored */ + if (!(bf_isset(candidate_set, i))) { + DEBUG("Not part of the candidate set - skipping\n"); + continue; + } + /* Rule 1: if we have an address configured that equals the destination + * use this one as source */ + if (ipv6_addr_equal(ptr, dst)) { + DEBUG("Ease one - rule 1\n"); + return ptr; + } + /* Rule 2: Prefer appropriate scope. */ + /* both link local */ + uint8_t candidate_scope = _get_scope(ptr); + if (candidate_scope == dst_scope) { + DEBUG("winner for rule 2 (same scope) found\n"); + winner_set[i] += RULE_2A_PTS; + if (winner_set[i] > max_pts) { + max_pts = RULE_2A_PTS; + } + } + else if (candidate_scope < dst_scope) { + DEBUG("winner for rule 2 (smaller scope) found\n"); + winner_set[i] += RULE_2B_PTS; + if (winner_set[i] > max_pts) { + max_pts = winner_set[i]; + } + } + /* Rule 3: Avoid deprecated addresses. */ + if (_get_state(netif, i) == GNRC_NETIF2_IPV6_ADDRS_FLAGS_STATE_DEPRECATED) { + DEBUG("winner for rule 3 found\n"); + winner_set[i] += RULE_3_PTS; + if (winner_set[i] > max_pts) { + max_pts = winner_set[i]; + } + } + + /* Rule 4: Prefer home addresses. + * Does not apply, gnrc does not support Mobile IP. + * TODO: update as soon as gnrc supports Mobile IP + */ + + /* Rule 5: Prefer outgoing interface. + * RFC 6724 says: + * "It is RECOMMENDED that the candidate source addresses be the set of + * unicast addresses assigned to the interface that will be used to + * send to the destination (the "outgoing" interface). On routers, + * the candidate set MAY include unicast addresses assigned to any + * interface that forwards packets, subject to the restrictions + * described below." + * Currently this implementation uses ALWAYS source addresses assigned + * to the outgoing interface. Hence, Rule 5 is always fulfilled. + */ + + /* Rule 6: Prefer matching label. + * Flow labels are currently not supported by gnrc. + * TODO: update as soon as gnrc supports flow labels + */ + + /* Rule 7: Prefer temporary addresses. + * Temporary addresses are currently not supported by gnrc. + * TODO: update as soon as gnrc supports temporary addresses + */ + } + /* reset candidate set to mark winners */ + memset(candidate_set, 0, (GNRC_NETIF2_IPV6_ADDRS_NUMOF + 7) / 8); + /* check if we have a clear winner */ + /* collect candidates with maximum points */ + for (int i = 0; i < GNRC_NETIF2_IPV6_ADDRS_NUMOF; i++) { + if (winner_set[i] == max_pts) { + bf_set(candidate_set, i); + } + } + /* otherwise apply rule 8: Use longest matching prefix. */ + int res; + _match(netif, dst, candidate_set, &res); + return (res < 0) ? NULL : &netif->ipv6.addrs[res]; +} + +static int _group_idx(const gnrc_netif2_t *netif, const ipv6_addr_t *addr) +{ + for (unsigned i = 0; i < GNRC_NETIF2_IPV6_GROUPS_NUMOF; i++) { + if (ipv6_addr_equal(&netif->ipv6.groups[i], addr)) { + return i; + } + } + return -1; +} + +#endif /* MODULE_GNRC_IPV6 */ + +static void _update_l2addr_from_dev(gnrc_netif2_t *netif) +{ + netdev_t *dev = netif->dev; + int res; + netopt_t opt = NETOPT_ADDRESS; + + switch (netif->device_type) { +#ifdef MODULE_NETDEV_IEEE802154 + case NETDEV_TYPE_IEEE802154: { + uint16_t tmp; + + res = dev->driver->get(dev, NETOPT_SRC_LEN, &tmp, sizeof(tmp)); + assert(res == sizeof(tmp)); + netif->l2addr_len = (uint8_t)tmp; + if (tmp == IEEE802154_LONG_ADDRESS_LEN) { + opt = NETOPT_ADDRESS_LONG; + } + } + break; +#endif + default: + break; + } + res = dev->driver->get(dev, opt, netif->l2addr, + sizeof(netif->l2addr)); + if (res != -ENOTSUP) { + netif->flags |= GNRC_NETIF2_FLAGS_HAS_L2ADDR; + } + if (res > 0) { + netif->l2addr_len = res; + } +} + +static void _init_from_device(gnrc_netif2_t *netif) +{ + int res; + netdev_t *dev = netif->dev; + uint16_t tmp; + + res = dev->driver->get(dev, NETOPT_DEVICE_TYPE, &tmp, sizeof(tmp)); + (void)res; + assert(res == sizeof(tmp)); + netif->device_type = (uint8_t)tmp; + switch (netif->device_type) { +#ifdef MODULE_NETDEV_IEEE802154 + case NETDEV_TYPE_IEEE802154: +#ifdef MODULE_GNRC_SIXLOWPAN_IPHC + netif->flags |= GNRC_NETIF2_FLAGS_6LO_HC; +#endif +#ifdef MODULE_GNRC_IPV6 + res = dev->driver->get(dev, NETOPT_MAX_PACKET_SIZE, &tmp, sizeof(tmp)); + assert(res == sizeof(tmp)); +#ifdef MODULE_GNRC_SIXLOWPAN + netif->ipv6.mtu = IPV6_MIN_MTU; + netif->sixlo.max_frag_size = tmp; +#else + netif->ipv6.mtu = tmp; +#endif +#endif + break; +#endif /* MODULE_NETDEV_IEEE802154 */ +#ifdef MODULE_NETDEV_ETH + case NETDEV_TYPE_ETHERNET: +#ifdef MODULE_GNRC_IPV6 + netif->ipv6.mtu = ETHERNET_DATA_LEN; +#endif + break; +#endif + default: + res = dev->driver->get(dev, NETOPT_MAX_PACKET_SIZE, &tmp, sizeof(tmp)); + assert(res == sizeof(tmp)); +#ifdef MODULE_GNRC_IPV6 + netif->ipv6.mtu = tmp; +#endif + } + _update_l2addr_from_dev(netif); +} + +static void *_gnrc_netif2_thread(void *args) +{ + gnrc_netapi_opt_t *opt; + gnrc_netif2_t *netif; + netdev_t *dev; + int res; + msg_t reply = { .type = GNRC_NETAPI_MSG_TYPE_ACK }; + msg_t msg, msg_queue[_NETIF_NETAPI_MSG_QUEUE_SIZE]; + + DEBUG("gnrc_netif2: starting thread %i\n", sched_active_pid); + netif = args; + gnrc_netif2_acquire(netif); + dev = netif->dev; + netif->pid = sched_active_pid; + /* setup the link-layer's message queue */ + msg_init_queue(msg_queue, _NETIF_NETAPI_MSG_QUEUE_SIZE); + /* register the event callback with the device driver */ + dev->event_callback = _event_cb; + dev->context = netif; + /* initialize low-level driver */ + dev->driver->init(dev); + _init_from_device(netif); + netif->cur_hl = GNRC_NETIF2_DEFAULT_HL; +#ifdef MODULE_GNRC_IPV6_NIB + gnrc_ipv6_nib_init_iface(netif); +#endif + if (netif->ops->init) { + netif->ops->init(netif); + } + /* now let rest of GNRC use the interface */ + gnrc_netif2_release(netif); + + while (1) { + DEBUG("gnrc_netif2: waiting for incoming messages\n"); + msg_receive(&msg); + /* dispatch netdev, MAC and gnrc_netapi messages */ + switch (msg.type) { + case NETDEV_MSG_TYPE_EVENT: + DEBUG("gnrc_netif2: GNRC_NETDEV_MSG_TYPE_EVENT received\n"); + dev->driver->isr(dev); + break; + case GNRC_NETAPI_MSG_TYPE_SND: + DEBUG("gnrc_netif2: GNRC_NETDEV_MSG_TYPE_SND received\n"); + res = netif->ops->send(netif, msg.content.ptr); +#if ENABLE_DEBUG + if (res < 0) { + DEBUG("gnrc_netif2: error sending packet %p (code: %u)\n", + msg.content.ptr, res); + } +#endif + break; + case GNRC_NETAPI_MSG_TYPE_SET: + opt = msg.content.ptr; +#ifdef MODULE_NETOPT + DEBUG("gnrc_netif2: GNRC_NETAPI_MSG_TYPE_SET received. opt=%s\n", + netopt2str(opt->opt)); +#else + DEBUG("gnrc_netif2: GNRC_NETAPI_MSG_TYPE_SET received. opt=%s\n", + opt->opt); +#endif + /* set option for device driver */ + res = netif->ops->set(netif, opt); + DEBUG("gnrc_netif2: response of netif->ops->set(): %i\n", res); + reply.content.value = (uint32_t)res; + msg_reply(&msg, &reply); + break; + case GNRC_NETAPI_MSG_TYPE_GET: + opt = msg.content.ptr; +#ifdef MODULE_NETOPT + DEBUG("gnrc_netif2: GNRC_NETAPI_MSG_TYPE_GET received. opt=%s\n", + netopt2str(opt->opt)); +#else + DEBUG("gnrc_netif2: GNRC_NETAPI_MSG_TYPE_GET received. opt=%s\n", + opt->opt); +#endif + /* get option from device driver */ + res = netif->ops->get(netif, opt); + DEBUG("gnrc_netif2: response of netif->ops->get(): %i\n", res); + reply.content.value = (uint32_t)res; + msg_reply(&msg, &reply); + break; + default: + if (netif->ops->msg_handler) { + DEBUG("gnrc_netif2: delegate message of type 0x%04x to " + "netif->ops->msg_handler()\n", msg.type); + netif->ops->msg_handler(netif, &msg); + } +#if ENABLE_DEBUG + else { + DEBUG("gnrc_netif2: unknown message type 0x%04x" + "(no message handler defined)\n", msg.type); + } +#endif + break; + } + } + /* never reached */ + return NULL; +} + +static void _pass_on_packet(gnrc_pktsnip_t *pkt) +{ + /* throw away packet if no one is interested */ + if (!gnrc_netapi_dispatch_receive(pkt->type, GNRC_NETREG_DEMUX_CTX_ALL, pkt)) { + DEBUG("gnrc_netif2: unable to forward packet of type %i\n", pkt->type); + gnrc_pktbuf_release(pkt); + return; + } +} + +static void _event_cb(netdev_t *dev, netdev_event_t event) +{ + gnrc_netif2_t *netif = (gnrc_netif2_t *) dev->context; + + if (event == NETDEV_EVENT_ISR) { + msg_t msg = { .type = NETDEV_MSG_TYPE_EVENT, + .content = { .ptr = netif } }; + + if (msg_send(&msg, netif->pid) <= 0) { + puts("gnrc_netif2: possibly lost interrupt."); + } + } + else { + DEBUG("gnrc_netif2: event triggered -> %i\n", event); + switch (event) { + case NETDEV_EVENT_RX_COMPLETE: { + gnrc_pktsnip_t *pkt = netif->ops->recv(netif); + + if (pkt) { + _pass_on_packet(pkt); + } + } + break; +#ifdef MODULE_NETSTATS_L2 + case NETDEV_EVENT_TX_MEDIUM_BUSY: + /* we are the only ones supposed to touch this variable, + * so no acquire necessary */ + dev->stats.tx_failed++; + break; + case NETDEV_EVENT_TX_COMPLETE: + /* we are the only ones supposed to touch this variable, + * so no acquire necessary */ + dev->stats.tx_success++; + break; +#endif + default: + DEBUG("gnrc_netif2: warning: unhandled event %u.\n", event); + } + } +} +/** @} */ diff --git a/sys/net/gnrc/netif2/gnrc_netif2_ethernet.c b/sys/net/gnrc/netif2/gnrc_netif2_ethernet.c new file mode 100644 index 0000000000000..d53c6706383cf --- /dev/null +++ b/sys/net/gnrc/netif2/gnrc_netif2_ethernet.c @@ -0,0 +1,249 @@ +/* + * Copyright (C) 2015 Kaspar Schleiser + * 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. + */ + +/** + * @{ + * + * @file + * @author Martine Lenders + * @author Kaspar Schleiser + */ + +#include "net/ethernet/hdr.h" +#include "net/gnrc.h" +#include "net/gnrc/netif2/ethernet.h" +#ifdef MODULE_GNRC_IPV6 +#include "net/ipv6/hdr.h" +#endif + +#define ENABLE_DEBUG (0) +#include "debug.h" + +static int _send(gnrc_netif2_t *netif, gnrc_pktsnip_t *pkt); +static gnrc_pktsnip_t *_recv(gnrc_netif2_t *netif); + +static const gnrc_netif2_ops_t ethernet_ops = { + .send = _send, + .recv = _recv, + .get = gnrc_netif2_get_from_netdev, + .set = gnrc_netif2_set_from_netdev, +}; + +gnrc_netif2_t *gnrc_netif2_ethernet_create(char *stack, int stacksize, + char priority, char *name, + netdev_t *dev) +{ + return gnrc_netif2_create(stack, stacksize, priority, name, dev, + ðernet_ops); +} + +static inline void _addr_set_broadcast(uint8_t *dst) +{ + memset(dst, 0xff, ETHERNET_ADDR_LEN); +} + +static inline void _addr_set_multicast(uint8_t *dst, gnrc_pktsnip_t *payload) +{ + switch (payload->type) { +#ifdef MODULE_GNRC_IPV6 + case GNRC_NETTYPE_IPV6: + /* https://tools.ietf.org/html/rfc2464#section-7 */ + dst[0] = 0x33; + dst[1] = 0x33; + ipv6_hdr_t *ipv6 = payload->data; + memcpy(dst + 2, ipv6->dst.u8 + 12, 4); + break; +#endif + default: + _addr_set_broadcast(dst); + break; + } +} + +static int _send(gnrc_netif2_t *netif, gnrc_pktsnip_t *pkt) +{ + ethernet_hdr_t hdr; + gnrc_netif_hdr_t *netif_hdr; + gnrc_pktsnip_t *payload; + int res; + + netdev_t *dev = netif->dev; + + if (pkt == NULL) { + DEBUG("gnrc_netif2_ethernet: pkt was NULL\n"); + return -EINVAL; + } + + payload = pkt->next; + + if (pkt->type != GNRC_NETTYPE_NETIF) { + DEBUG("gnrc_netif2_ethernet: First header was not generic netif header\n"); + return -EBADMSG; + } + + if (payload) { + hdr.type = byteorder_htons(gnrc_nettype_to_ethertype(payload->type)); + } + else { + hdr.type = byteorder_htons(ETHERTYPE_UNKNOWN); + } + + netif_hdr = pkt->data; + + /* set ethernet header */ + if (netif_hdr->src_l2addr_len == ETHERNET_ADDR_LEN) { + memcpy(hdr.dst, gnrc_netif_hdr_get_src_addr(netif_hdr), + netif_hdr->src_l2addr_len); + } + else { + dev->driver->get(dev, NETOPT_ADDRESS, hdr.src, ETHERNET_ADDR_LEN); + } + + if (netif_hdr->flags & GNRC_NETIF_HDR_FLAGS_BROADCAST) { + _addr_set_broadcast(hdr.dst); + } + else if (netif_hdr->flags & GNRC_NETIF_HDR_FLAGS_MULTICAST) { + if (payload == NULL) { + DEBUG("gnrc_netif2_ethernet: empty multicast packets over Ethernet " + "are not yet supported\n"); + return -ENOTSUP; + } + _addr_set_multicast(hdr.dst, payload); + } + else if (netif_hdr->dst_l2addr_len == ETHERNET_ADDR_LEN) { + memcpy(hdr.dst, gnrc_netif_hdr_get_dst_addr(netif_hdr), + ETHERNET_ADDR_LEN); + } + else { + DEBUG("gnrc_netif2_ethernet: destination address had unexpected " + "format\n"); + return -EBADMSG; + } + + DEBUG("gnrc_netif2_ethernet: send to %02x:%02x:%02x:%02x:%02x:%02x\n", + hdr.dst[0], hdr.dst[1], hdr.dst[2], + hdr.dst[3], hdr.dst[4], hdr.dst[5]); + + size_t n; + payload = gnrc_pktbuf_get_iovec(pkt, &n); /* use payload as temporary + * variable */ + res = -ENOBUFS; + if (payload != NULL) { + pkt = payload; /* reassign for later release; vec_snip is prepended to pkt */ + struct iovec *vector = (struct iovec *)pkt->data; + vector[0].iov_base = (char *)&hdr; + vector[0].iov_len = sizeof(ethernet_hdr_t); +#ifdef MODULE_NETSTATS_L2 + if ((netif_hdr->flags & GNRC_NETIF_HDR_FLAGS_BROADCAST) || + (netif_hdr->flags & GNRC_NETIF_HDR_FLAGS_MULTICAST)) { + dev->stats.tx_mcast_count++; + } + else { + dev->stats.tx_unicast_count++; + } +#endif + res = dev->driver->send(dev, vector, n); + } + + gnrc_pktbuf_release(pkt); + + return res; +} + +static gnrc_pktsnip_t *_recv(gnrc_netif2_t *netif) +{ + netdev_t *dev = netif->dev; + int bytes_expected = dev->driver->recv(dev, NULL, 0, NULL); + gnrc_pktsnip_t *pkt = NULL; + + if (bytes_expected > 0) { + pkt = gnrc_pktbuf_add(NULL, NULL, + bytes_expected, + GNRC_NETTYPE_UNDEF); + + if (!pkt) { + DEBUG("gnrc_netif2_ethernet: cannot allocate pktsnip.\n"); + + /* drop the packet */ + dev->driver->recv(dev, NULL, bytes_expected, NULL); + + goto out; + } + + int nread = dev->driver->recv(dev, pkt->data, bytes_expected, NULL); + if (nread <= 0) { + DEBUG("gnrc_netif2_ethernet: read error.\n"); + goto safe_out; + } + + if (nread < bytes_expected) { + /* we've got less then the expected packet size, + * so free the unused space.*/ + + DEBUG("gnrc_netif2_ethernet: reallocating.\n"); + gnrc_pktbuf_realloc_data(pkt, nread); + } + + /* mark ethernet header */ + gnrc_pktsnip_t *eth_hdr = gnrc_pktbuf_mark(pkt, sizeof(ethernet_hdr_t), GNRC_NETTYPE_UNDEF); + if (!eth_hdr) { + DEBUG("gnrc_netif2_ethernet: no space left in packet buffer\n"); + goto safe_out; + } + + ethernet_hdr_t *hdr = (ethernet_hdr_t *)eth_hdr->data; + +#ifdef MODULE_L2FILTER + if (!l2filter_pass(dev->filter, hdr->src, ETHERNET_ADDR_LEN)) { + DEBUG("gnrc_netif2_ethernet: incoming packet filtered by l2filter\n"); + goto safe_out; + } +#endif + + /* set payload type from ethertype */ + pkt->type = gnrc_nettype_from_ethertype(byteorder_ntohs(hdr->type)); + + /* create netif header */ + gnrc_pktsnip_t *netif_hdr; + netif_hdr = gnrc_pktbuf_add(NULL, NULL, + sizeof(gnrc_netif_hdr_t) + (2 * ETHERNET_ADDR_LEN), + GNRC_NETTYPE_NETIF); + + if (netif_hdr == NULL) { + DEBUG("gnrc_netif2_ethernet: no space left in packet buffer\n"); + pkt = eth_hdr; + goto safe_out; + } + + gnrc_netif_hdr_init(netif_hdr->data, ETHERNET_ADDR_LEN, ETHERNET_ADDR_LEN); + gnrc_netif_hdr_set_src_addr(netif_hdr->data, hdr->src, ETHERNET_ADDR_LEN); + gnrc_netif_hdr_set_dst_addr(netif_hdr->data, hdr->dst, ETHERNET_ADDR_LEN); + ((gnrc_netif_hdr_t *)netif_hdr->data)->if_pid = thread_getpid(); + + DEBUG("gnrc_netif2_ethernet: received packet from %02x:%02x:%02x:%02x:%02x:%02x " + "of length %d\n", + hdr->src[0], hdr->src[1], hdr->src[2], hdr->src[3], hdr->src[4], + hdr->src[5], nread); +#if defined(MODULE_OD) && ENABLE_DEBUG + od_hex_dump(hdr, nread, OD_WIDTH_DEFAULT); +#endif + + gnrc_pktbuf_remove_snip(pkt, eth_hdr); + LL_APPEND(pkt, netif_hdr); + } + +out: + return pkt; + +safe_out: + gnrc_pktbuf_release(pkt); + return NULL; +} + +/** @} */ diff --git a/tests/gnrc_netif2/Makefile b/tests/gnrc_netif2/Makefile new file mode 100644 index 0000000000000..cc57c4f8a7851 --- /dev/null +++ b/tests/gnrc_netif2/Makefile @@ -0,0 +1,29 @@ +# name of your application +APPLICATION = gnrc_ipv6_nib +include ../Makefile.tests_common + +BOARD_INSUFFICIENT_MEMORY := + +USEMODULE += embunit +USEMODULE += gnrc_netif2 +USEMODULE += gnrc_pktdump +USEMODULE += gnrc_sixlowpan +USEMODULE += gnrc_sixlowpan_iphc +USEMODULE += gnrc_ipv6 +USEMODULE += netdev_eth +USEMODULE += netdev_test +USEMODULE += od + +CFLAGS += -DDEVELHELP +CFLAGS += -DGNRC_NETIF_NUMOF=4 +CFLAGS += -DGNRC_NETIF2_ADDRS_NUMOF=16 +CFLAGS += -DGNRC_NETIF2_GROUPS_NUMOF=8 +CFLAGS += -DLOG_LEVEL=LOG_NONE +CFLAGS += -DTEST_SUITES + +include $(RIOTBASE)/Makefile.include + +test: +# `testrunner` calls `make term` recursively, results in duplicated `TERMFLAGS`. +# So clears `TERMFLAGS` before run. + TERMFLAGS= tests/01-run.py diff --git a/tests/gnrc_netif2/common.c b/tests/gnrc_netif2/common.c new file mode 100644 index 0000000000000..d106ee3d098a1 --- /dev/null +++ b/tests/gnrc_netif2/common.c @@ -0,0 +1,163 @@ +/* + * 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. + */ + +/** + * @{ + * + * @file + * @author Martine Lenders + */ + +#include +#include + +#include "common.h" +#include "net/ethernet.h" +#include "net/gnrc/netreg.h" +#include "net/gnrc/pktdump.h" +#include "net/ipv6.h" +#include "net/netdev_test.h" +#include "od.h" + +static netdev_test_t _devs[GNRC_NETIF_NUMOF]; + +netdev_t *ethernet_dev = (netdev_t *)&_devs[0]; +netdev_t *devs[DEFAULT_DEVS_NUMOF]; + +#define MSG_QUEUE_SIZE (8) + +static gnrc_netreg_entry_t dumper_undef, dumper_ipv6; +static msg_t _main_msg_queue[MSG_QUEUE_SIZE]; +static uint8_t tmp_buffer[ETHERNET_DATA_LEN]; +static size_t tmp_buffer_bytes = 0; + +static int _dump_send_packet(netdev_t *netdev, const struct iovec *vector, + int count) +{ + int res; + + tmp_buffer_bytes = 0; + + printf("Sending data from "); + if (netdev == ethernet_dev) { + printf("Ethernet "); + } + else { + printf("unknown "); + } + puts("device:"); + for (int i = 0; i < count; i++) { + if ((tmp_buffer_bytes + vector[i].iov_len) > ETHERNET_DATA_LEN) { + return -ENOBUFS; + } + memcpy(&tmp_buffer[tmp_buffer_bytes], vector[i].iov_base, + vector[i].iov_len); + tmp_buffer_bytes += vector[i].iov_len; + } + od_hex_dump(tmp_buffer, tmp_buffer_bytes, OD_WIDTH_DEFAULT); + res = (int)tmp_buffer_bytes; + return res; +} + +void _test_trigger_recv(gnrc_netif2_t *netif, const uint8_t *data, + size_t data_len) +{ + netdev_t *dev = netif->dev; + + assert(data_len <= ETHERNET_DATA_LEN); + if ((data != NULL) || (data_len > 0)) { + tmp_buffer_bytes = data_len; + memcpy(tmp_buffer, data, data_len); + } + else { + tmp_buffer_bytes = 0; + } + assert(dev->event_callback); + dev->event_callback(dev, NETDEV_EVENT_ISR); +} + +static int _netdev_recv(netdev_t *dev, char *buf, int len, void *info) +{ + int res; + + (void)dev; + (void)info; + res = (int)tmp_buffer_bytes; + if (buf == NULL) { + if (len > 0) { + tmp_buffer_bytes = 0; + } + return res; + } + if (((unsigned)len) < tmp_buffer_bytes) { + return -ENOBUFS; + } + memcpy(buf, tmp_buffer, tmp_buffer_bytes); + return res; +} + +static void _netdev_isr(netdev_t *dev) +{ + assert(dev->event_callback); + dev->event_callback(dev, NETDEV_EVENT_RX_COMPLETE); +} + +static int _get_netdev_device_type(netdev_t *netdev, void *value, size_t max_len) +{ + netdev_test_t *dev = (netdev_test_t *)netdev; + assert(max_len == sizeof(uint16_t)); + if (dev->state == 0x0) { + *((uint16_t *)value) = NETDEV_TYPE_ETHERNET; + } + else { + *((uint16_t *)value) = NETDEV_TYPE_UNKNOWN; + } + return sizeof(uint16_t); +} +static int _get_netdev_max_packet_size(netdev_t *netdev, void *value, size_t max_len) +{ + netdev_test_t *dev = (netdev_test_t *)netdev; + assert(max_len == sizeof(uint16_t)); + if (dev->state == 0x0) { + *((uint16_t *)value) = ETHERNET_DATA_LEN; + } + else { + *((uint16_t *)value) = IPV6_MIN_MTU; + } + return sizeof(uint16_t); +} + + +void _tests_init(void) +{ + msg_init_queue(_main_msg_queue, MSG_QUEUE_SIZE); + netdev_test_setup((netdev_test_t *)ethernet_dev, 0); + netdev_test_set_send_cb((netdev_test_t *)ethernet_dev, _dump_send_packet); + netdev_test_set_recv_cb((netdev_test_t *)ethernet_dev, _netdev_recv); + netdev_test_set_isr_cb((netdev_test_t *)ethernet_dev, _netdev_isr); + netdev_test_set_get_cb((netdev_test_t *)ethernet_dev, NETOPT_DEVICE_TYPE, + _get_netdev_device_type); + netdev_test_set_get_cb((netdev_test_t *)ethernet_dev, NETOPT_MAX_PACKET_SIZE, + _get_netdev_max_packet_size); + for (intptr_t i = SPECIAL_DEVS; i < GNRC_NETIF_NUMOF; i++) { + devs[i - SPECIAL_DEVS] = (netdev_t *)&_devs[i]; + netdev_test_setup(&_devs[i], (void *)i); + netdev_test_set_get_cb(&_devs[i], NETOPT_DEVICE_TYPE, + _get_netdev_device_type); + netdev_test_set_get_cb(&_devs[i], NETOPT_MAX_PACKET_SIZE, + _get_netdev_max_packet_size); + } + gnrc_netreg_entry_init_pid(&dumper_undef, GNRC_NETREG_DEMUX_CTX_ALL, + gnrc_pktdump_pid); + gnrc_netreg_entry_init_pid(&dumper_ipv6, GNRC_NETREG_DEMUX_CTX_ALL, + gnrc_pktdump_pid); + gnrc_netreg_register(GNRC_NETTYPE_UNDEF, &dumper_undef); + gnrc_netreg_register(GNRC_NETTYPE_IPV6, &dumper_ipv6); +} + +/** @} */ diff --git a/tests/gnrc_netif2/common.h b/tests/gnrc_netif2/common.h new file mode 100644 index 0000000000000..7144bd723d19c --- /dev/null +++ b/tests/gnrc_netif2/common.h @@ -0,0 +1,88 @@ +/* + * 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. + */ + +/** + * @defgroup tests_gnrc_ipv6_nib Common header for GNRC's NIB tests + * @ingroup tests + * @brief Common definitions for GNRC's NIB tests + * @{ + * + * @file + * + * @author Martine Lenders + */ +#ifndef COMMON_H +#define COMMON_H + + +#include "net/gnrc/netif2.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define SPECIAL_DEVS (1) +#define DEFAULT_DEVS_NUMOF (GNRC_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 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 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_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 } + +extern netdev_t *ethernet_dev; +extern netdev_t *devs[DEFAULT_DEVS_NUMOF]; + +void _tests_init(void); +void _test_trigger_recv(gnrc_netif2_t *netif, const uint8_t *data, + size_t data_len); + +#ifdef __cplusplus +} +#endif + +#endif /* COMMON_H */ +/** @} */ diff --git a/tests/gnrc_netif2/main.c b/tests/gnrc_netif2/main.c new file mode 100644 index 0000000000000..2aec29fe187c0 --- /dev/null +++ b/tests/gnrc_netif2/main.c @@ -0,0 +1,1038 @@ +/* + * 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 + * @{ + * + * @file + * @brief Tests default configuration of GNRC's Network Information Base + * + * @author Martine Lenders + * + * @} + */ + +#include +#include + +#include "common.h" +#include "embUnit.h" +#include "embUnit/embUnit.h" +#include "net/ethernet.h" +#include "net/ipv6.h" +#include "net/gnrc.h" +#include "net/gnrc/ipv6/hdr.h" +#include "net/gnrc/netif/hdr.h" +#include "net/gnrc/netif2.h" +#include "net/gnrc/netif2/ethernet.h" +#include "net/gnrc/netif2/internal.h" +#include "net/netdev_test.h" +#include "utlist.h" +#include "xtimer.h" + +#define ETHERNET_STACKSIZE (THREAD_STACKSIZE_MAIN) + +static gnrc_netif2_t *ethernet_netif = NULL; +static gnrc_netif2_t *netifs[DEFAULT_DEVS_NUMOF]; +static char ethernet_netif_stack[ETHERNET_STACKSIZE]; +static char netifs_stack[DEFAULT_DEVS_NUMOF][THREAD_STACKSIZE_DEFAULT]; +static bool init_called = false; + +static inline void _test_init(gnrc_netif2_t *netif); +static inline int _mock_netif_send(gnrc_netif2_t *netif, gnrc_pktsnip_t *pkt); +static inline gnrc_pktsnip_t *_mock_netif_recv(gnrc_netif2_t * netif); +static int _get_netdev_address(netdev_t *dev, void *value, size_t max_len); +static int _set_netdev_address(netdev_t *dev, void *value, size_t value_len); + +static const gnrc_netif2_ops_t default_ops = { + .init = _test_init, + .send = _mock_netif_send, + .recv = _mock_netif_recv, + .get = gnrc_netif2_get_from_netdev, + .set = gnrc_netif2_set_from_netdev, + .msg_handler = NULL, +}; + +static void _set_up(void) +{ + msg_t msg; + + if (ethernet_netif != NULL) { + memset(ethernet_netif->ipv6.addrs_flags, 0, + sizeof(ethernet_netif->ipv6.addrs_flags)); + memset(ethernet_netif->ipv6.addrs, 0, + sizeof(ethernet_netif->ipv6.addrs)); + memset(ethernet_netif->ipv6.groups, 0, + sizeof(ethernet_netif->ipv6.groups)); + } + for (unsigned i = 0; i < DEFAULT_DEVS_NUMOF; i++) { + if (netifs[i] != NULL) { + memset(netifs[i]->ipv6.addrs_flags, 0, + sizeof(netifs[i]->ipv6.addrs_flags)); + memset(netifs[i]->ipv6.addrs, 0, sizeof(netifs[i]->ipv6.addrs)); + memset(netifs[i]->ipv6.groups, 0, sizeof(netifs[i]->ipv6.groups)); + } + } + /* empty message queue */ + while (msg_try_receive(&msg) > 0) {} +} + +static inline void _test_init(gnrc_netif2_t *netif) +{ + (void)netif; + init_called = true; +} + +static void test_creation(void) +{ + char test_stack[4]; + gnrc_netif2_t *ptr = NULL; + + TEST_ASSERT_EQUAL_INT(0, gnrc_netif2_numof()); + TEST_ASSERT_NULL(gnrc_netif2_iter(ptr)); + TEST_ASSERT_NOT_NULL((ethernet_netif = gnrc_netif2_ethernet_create( + ethernet_netif_stack, ETHERNET_STACKSIZE, GNRC_NETIF2_PRIO, + "eth", ethernet_dev + ))); + TEST_ASSERT_EQUAL_INT(1, gnrc_netif2_numof()); + TEST_ASSERT_NOT_NULL((ptr = gnrc_netif2_iter(ptr))); + TEST_ASSERT_NULL((ptr = gnrc_netif2_iter(ptr))); + TEST_ASSERT_NOT_NULL(ethernet_netif->ops); + TEST_ASSERT_NOT_NULL(ethernet_netif->dev); + TEST_ASSERT_EQUAL_INT(ETHERNET_DATA_LEN, ethernet_netif->ipv6.mtu); + TEST_ASSERT_EQUAL_INT(GNRC_NETIF2_DEFAULT_HL, ethernet_netif->cur_hl); + TEST_ASSERT_EQUAL_INT(NETDEV_TYPE_ETHERNET, ethernet_netif->device_type); + TEST_ASSERT(ethernet_netif->pid > KERNEL_PID_UNDEF); +#ifdef DEVELHELP + TEST_ASSERT_EQUAL_STRING("eth", sched_threads[ethernet_netif->pid]->name); +#endif + TEST_ASSERT_NOT_NULL(sched_threads[ethernet_netif->pid]->msg_array); + for (unsigned i = 0; i < DEFAULT_DEVS_NUMOF; i++) { + TEST_ASSERT_NOT_NULL((netifs[i] = gnrc_netif2_create( + netifs_stack[i], THREAD_STACKSIZE_DEFAULT, + GNRC_NETIF2_PRIO, "netif", devs[i], &default_ops + ))); + TEST_ASSERT_NOT_NULL(netifs[i]->ops); + TEST_ASSERT_NOT_NULL(netifs[i]->dev); + TEST_ASSERT_EQUAL_INT(GNRC_NETIF2_DEFAULT_HL, netifs[i]->cur_hl); + TEST_ASSERT_EQUAL_INT(NETDEV_TYPE_UNKNOWN, netifs[i]->device_type); + TEST_ASSERT(netifs[i]->pid > KERNEL_PID_UNDEF); + TEST_ASSERT_NOT_NULL(sched_threads[netifs[i]->pid]->msg_array); + TEST_ASSERT_EQUAL_INT(i + SPECIAL_DEVS + 1, gnrc_netif2_numof()); + for (unsigned j = 0; j < (i + SPECIAL_DEVS + 1); j++) { + TEST_ASSERT_NOT_NULL((ptr = gnrc_netif2_iter(ptr))); + } + TEST_ASSERT_NULL((ptr = gnrc_netif2_iter(ptr))); + } + TEST_ASSERT(init_called); + TEST_ASSERT_NULL(gnrc_netif2_create(test_stack, 4, GNRC_NETIF2_PRIO, + "netif", NULL, &default_ops)); +} + +static void test_get_by_pid(void) +{ + TEST_ASSERT(ethernet_netif == gnrc_netif2_get_by_pid(ethernet_netif->pid)); + for (kernel_pid_t i = 0; i < DEFAULT_DEVS_NUMOF; i++) { + TEST_ASSERT(netifs[i] == gnrc_netif2_get_by_pid(netifs[i]->pid)); + } +} + +static void test_addr_to_str(void) +{ + static const uint8_t ethernet_l2addr[] = ETHERNET_SRC; + static const uint8_t netif0_l2addr[] = NETIF0_SRC; + char out[sizeof(netif0_l2addr) * 3]; + + TEST_ASSERT(out == gnrc_netif2_addr_to_str(NULL, 0, out)); + TEST_ASSERT_EQUAL_STRING("", &out[0]); + TEST_ASSERT(out == gnrc_netif2_addr_to_str(ethernet_l2addr, + sizeof(ethernet_l2addr), out)); + TEST_ASSERT_EQUAL_STRING("3e:e6:b5:22:fd:0a", &out[0]); + TEST_ASSERT(out == gnrc_netif2_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_addr_from_str(void) +{ + static const uint8_t ethernet_l2addr[] = ETHERNET_SRC; + uint8_t out[GNRC_NETIF2_L2ADDR_MAXLEN]; + + TEST_ASSERT_EQUAL_INT(0, gnrc_netif2_addr_from_str("", out)); + TEST_ASSERT_EQUAL_INT(sizeof(ethernet_l2addr), + gnrc_netif2_addr_from_str("3e:e6:b5:22:fd:0a", out)); + TEST_ASSERT_EQUAL_INT(0, memcmp(ethernet_l2addr, out, + sizeof(ethernet_l2addr))); +} + +static void test_ipv6_addr_add__ENOMEM(void) +{ + ipv6_addr_t addr = { .u8 = NETIF0_IPV6_G }; + + for (unsigned i = 0; i < GNRC_NETIF2_IPV6_ADDRS_NUMOF; + i++, addr.u16[3].u16++) { + TEST_ASSERT(0 <= gnrc_netif2_ipv6_addr_add(netifs[0], &addr, 64U, + GNRC_NETIF2_IPV6_ADDRS_FLAGS_STATE_VALID)); + } + TEST_ASSERT_EQUAL_INT(-ENOMEM, + gnrc_netif2_ipv6_addr_add(netifs[0], &addr, 64U, + GNRC_NETIF2_IPV6_ADDRS_FLAGS_STATE_VALID)); +} + +static void test_ipv6_addr_add__success(void) +{ + static const ipv6_addr_t addr = { .u8 = NETIF0_IPV6_LL }; + int idx; + + TEST_ASSERT(0 <= (idx = gnrc_netif2_ipv6_addr_add(netifs[0], &addr, 64U, + GNRC_NETIF2_IPV6_ADDRS_FLAGS_STATE_VALID))); + /* check duplicate addition */ + TEST_ASSERT_EQUAL_INT(idx, + gnrc_netif2_ipv6_addr_add(netifs[0], &addr, 64U, + GNRC_NETIF2_IPV6_ADDRS_FLAGS_STATE_VALID)); + TEST_ASSERT_EQUAL_INT(GNRC_NETIF2_IPV6_ADDRS_FLAGS_STATE_VALID, + netifs[0]->ipv6.addrs_flags[idx]); + TEST_ASSERT(ipv6_addr_equal(&addr, &netifs[0]->ipv6.addrs[idx])); +} + +static void test_ipv6_addr_add__readd_with_free_entry(void) +{ + /* Tests for possible duplicates (see #2965) */ + static const ipv6_addr_t addr1 = { .u8 = NETIF0_IPV6_LL }; + static const ipv6_addr_t addr2 = { .u8 = NETIF0_IPV6_G }; + int idx; + + TEST_ASSERT(0 <= gnrc_netif2_ipv6_addr_add(netifs[0], &addr1, 64U, + GNRC_NETIF2_IPV6_ADDRS_FLAGS_STATE_VALID)); + TEST_ASSERT(0 <= (idx = gnrc_netif2_ipv6_addr_add(netifs[0], &addr2, 64U, + GNRC_NETIF2_IPV6_ADDRS_FLAGS_STATE_VALID))); + gnrc_netif2_ipv6_addr_remove(netifs[0], &addr1); + TEST_ASSERT_EQUAL_INT(idx, + gnrc_netif2_ipv6_addr_add(netifs[0], &addr2, 64U, + GNRC_NETIF2_IPV6_ADDRS_FLAGS_STATE_VALID)); +} + +static void test_ipv6_addr_remove__not_allocated(void) +{ + static const ipv6_addr_t addr1 = { .u8 = NETIF0_IPV6_LL }; + static const ipv6_addr_t addr2 = { .u8 = NETIF0_IPV6_G }; + + test_ipv6_addr_add__success(); + TEST_ASSERT(0 <= gnrc_netif2_ipv6_addr_idx(netifs[0], &addr1)); + TEST_ASSERT(0 <= gnrc_netif2_ipv6_addr_add(netifs[0], &addr2, 64U, + GNRC_NETIF2_IPV6_ADDRS_FLAGS_STATE_VALID)); + gnrc_netif2_ipv6_addr_remove(netifs[0], &addr2); + TEST_ASSERT(0 <= gnrc_netif2_ipv6_addr_idx(netifs[0], &addr1)); +} + +static void test_ipv6_addr_remove__success(void) +{ + static const ipv6_addr_t addr = { .u8 = NETIF0_IPV6_LL }; + + test_ipv6_addr_add__success(); + gnrc_netif2_ipv6_addr_remove(netifs[0], &addr); + TEST_ASSERT_EQUAL_INT(-1, gnrc_netif2_ipv6_addr_idx(netifs[0], &addr)); +} + +static void test_ipv6_addr_idx__empty(void) +{ + static const ipv6_addr_t addr = { .u8 = NETIF0_IPV6_LL }; + + TEST_ASSERT_EQUAL_INT(-1, gnrc_netif2_ipv6_addr_idx(netifs[0], &addr)); +} + +static void test_ipv6_addr_idx__wrong_netif(void) +{ + static const ipv6_addr_t addr = { .u8 = NETIF0_IPV6_LL }; + + test_ipv6_addr_add__success(); + TEST_ASSERT_EQUAL_INT(-1, gnrc_netif2_ipv6_addr_idx(netifs[1], &addr)); +} + +static void test_ipv6_addr_idx__wrong_addr(void) +{ + static const ipv6_addr_t addr2 = { .u8 = NETIF0_IPV6_G }; + + test_ipv6_addr_add__success(); + TEST_ASSERT_EQUAL_INT(-1, gnrc_netif2_ipv6_addr_idx(netifs[0], &addr2)); +} + +static void test_ipv6_addr_idx__success(void) +{ + static const ipv6_addr_t addr = { .u8 = NETIF0_IPV6_LL }; + + test_ipv6_addr_add__success(); + TEST_ASSERT(0 <= gnrc_netif2_ipv6_addr_idx(netifs[0], &addr)); +} + +static void test_ipv6_addr_match__empty(void) +{ + static const ipv6_addr_t addr = { .u8 = NETIF0_IPV6_G }; + + TEST_ASSERT_EQUAL_INT(-1, gnrc_netif2_ipv6_addr_match(netifs[0], &addr)); +} + +static void test_ipv6_addr_match__wrong_netif(void) +{ + static const ipv6_addr_t addr = { .u8 = NETIF0_IPV6_G }; + + test_ipv6_addr_add__success(); + TEST_ASSERT_EQUAL_INT(-1, gnrc_netif2_ipv6_addr_match(netifs[1], &addr)); +} + +static void test_ipv6_addr_match__wrong_addr(void) +{ + static const ipv6_addr_t addr2 = { .u8 = NETIF0_IPV6_G }; + + test_ipv6_addr_add__success(); + TEST_ASSERT_EQUAL_INT(-1, gnrc_netif2_ipv6_addr_match(netifs[0], &addr2)); +} + +static void test_ipv6_addr_match__success18(void) +{ + static const ipv6_addr_t addr = { .u8 = NETIF0_IPV6_G }; + + static const ipv6_addr_t pfx = { .u8 = GLOBAL_PFX18 }; + int idx; + + TEST_ASSERT(0 <= (idx = gnrc_netif2_ipv6_addr_add(netifs[0], &addr, 64U, + GNRC_NETIF2_IPV6_ADDRS_FLAGS_STATE_VALID))); + TEST_ASSERT_EQUAL_INT(idx, gnrc_netif2_ipv6_addr_match(netifs[0], &pfx)); + TEST_ASSERT_EQUAL_INT(18, ipv6_addr_match_prefix(&netifs[0]->ipv6.addrs[idx], + &pfx)); +} + +static void test_ipv6_addr_match__success23(void) +{ + static const ipv6_addr_t addr = { .u8 = NETIF0_IPV6_G }; + + static const ipv6_addr_t pfx = { .u8 = GLOBAL_PFX23 }; + int idx; + + TEST_ASSERT(0 <= (idx = gnrc_netif2_ipv6_addr_add(netifs[0], &addr, 64U, + GNRC_NETIF2_IPV6_ADDRS_FLAGS_STATE_VALID))); + TEST_ASSERT_EQUAL_INT(idx, gnrc_netif2_ipv6_addr_match(netifs[0], &pfx)); + TEST_ASSERT_EQUAL_INT(23, ipv6_addr_match_prefix(&netifs[0]->ipv6.addrs[idx], + &pfx)); +} + +static void test_ipv6_addr_match__success64(void) +{ + static const ipv6_addr_t addr = { .u8 = NETIF0_IPV6_G }; + + static const ipv6_addr_t pfx = { .u8 = GLOBAL_PFX64 }; + int idx; + + TEST_ASSERT(0 <= (idx = gnrc_netif2_ipv6_addr_add(netifs[0], &addr, 64U, + GNRC_NETIF2_IPV6_ADDRS_FLAGS_STATE_VALID))); + TEST_ASSERT_EQUAL_INT(idx, gnrc_netif2_ipv6_addr_match(netifs[0], &pfx)); + TEST_ASSERT_EQUAL_INT(64, ipv6_addr_match_prefix(&netifs[0]->ipv6.addrs[idx], + &pfx)); +} + +static void test_ipv6_addr_best_src__multicast_input(void) +{ + static const ipv6_addr_t addr1 = { .u8 = NETIF0_IPV6_G }; + + static const ipv6_addr_t addr2 = { .u8 = GLOBAL_PFX18 }; + ipv6_addr_t *out; + + /* adds a link-local address */ + test_ipv6_addr_add__success(); + TEST_ASSERT(0 <= gnrc_netif2_ipv6_addr_add(netifs[0], &addr1, 64U, + GNRC_NETIF2_IPV6_ADDRS_FLAGS_STATE_VALID)); + TEST_ASSERT_NOT_NULL((out = gnrc_netif2_ipv6_addr_best_src(netifs[0], + &addr2, + false))); + TEST_ASSERT(!ipv6_addr_equal(&addr2, out)); + TEST_ASSERT(ipv6_addr_equal(&addr1, out)); +} + +static void test_ipv6_addr_best_src__other_subnet(void) +{ + static const ipv6_addr_t mc_addr = IPV6_ADDR_ALL_ROUTERS_SITE_LOCAL; + ipv6_addr_t *out = NULL; + + test_ipv6_addr_add__success(); + TEST_ASSERT_NOT_NULL((out = gnrc_netif2_ipv6_addr_best_src(netifs[0], + &mc_addr, + false))); + TEST_ASSERT(!ipv6_addr_equal(&mc_addr, out)); + TEST_ASSERT(!ipv6_addr_is_multicast(out)); + TEST_ASSERT(!ipv6_addr_is_unspecified(out)); +} + +static void test_get_by_ipv6_addr__empty(void) +{ + static const ipv6_addr_t addr = { .u8 = NETIF0_IPV6_LL }; + + TEST_ASSERT_NULL(gnrc_netif2_get_by_ipv6_addr(&addr)); +} + +static void test_get_by_ipv6_addr__success(void) +{ + static const ipv6_addr_t addr = { .u8 = NETIF0_IPV6_LL }; + + test_ipv6_addr_add__success(); + TEST_ASSERT_NOT_NULL(netifs[0]); + TEST_ASSERT(netifs[0] == gnrc_netif2_get_by_ipv6_addr(&addr)); +} + +static void test_get_by_prefix__empty(void) +{ + static const ipv6_addr_t addr = { .u8 = NETIF0_IPV6_G }; + + TEST_ASSERT_NULL(gnrc_netif2_get_by_prefix(&addr)); +} + +static void test_get_by_prefix__success18(void) +{ + static const ipv6_addr_t addr = { .u8 = NETIF0_IPV6_G }; + static const ipv6_addr_t pfx = { .u8 = GLOBAL_PFX18 }; + + TEST_ASSERT(0 <= gnrc_netif2_ipv6_addr_add(netifs[0], &addr, 64U, + GNRC_NETIF2_IPV6_ADDRS_FLAGS_STATE_VALID)); + TEST_ASSERT_NOT_NULL(netifs[0]); + TEST_ASSERT(netifs[0] == gnrc_netif2_get_by_prefix(&pfx)); + test_ipv6_addr_match__success18(); +} + +static void test_get_by_prefix__success23(void) +{ + static const ipv6_addr_t addr = { .u8 = NETIF0_IPV6_G }; + static const ipv6_addr_t pfx = { .u8 = GLOBAL_PFX23 }; + + TEST_ASSERT(0 <= gnrc_netif2_ipv6_addr_add(netifs[0], &addr, 64U, + GNRC_NETIF2_IPV6_ADDRS_FLAGS_STATE_VALID)); + TEST_ASSERT_NOT_NULL(netifs[0]); + TEST_ASSERT(netifs[0] == gnrc_netif2_get_by_prefix(&pfx)); + test_ipv6_addr_match__success23(); +} + +static void test_get_by_prefix__success64(void) +{ + static const ipv6_addr_t addr = { .u8 = NETIF0_IPV6_G }; + static const ipv6_addr_t pfx = { .u8 = GLOBAL_PFX64 }; + + TEST_ASSERT(0 <= gnrc_netif2_ipv6_addr_add(netifs[0], &addr, 64U, + GNRC_NETIF2_IPV6_ADDRS_FLAGS_STATE_VALID)); + TEST_ASSERT_NOT_NULL(netifs[0]); + TEST_ASSERT(netifs[0] == gnrc_netif2_get_by_prefix(&pfx)); + test_ipv6_addr_match__success64(); +} + +static void test_ipv6_group_join__ENOMEM(void) +{ + ipv6_addr_t addr = IPV6_ADDR_ALL_NODES_LINK_LOCAL; + + for (unsigned i = 0; i < GNRC_NETIF2_IPV6_ADDRS_NUMOF; + i++, addr.u16[7].u16++) { + TEST_ASSERT(0 <= gnrc_netif2_ipv6_group_join(netifs[0], &addr)); + } + TEST_ASSERT_EQUAL_INT(-ENOMEM, + gnrc_netif2_ipv6_group_join(netifs[0], &addr)); +} + +static void test_ipv6_group_join__success(void) +{ + int idx; + + TEST_ASSERT(0 <= (idx = gnrc_netif2_ipv6_group_join(netifs[0], + &ipv6_addr_all_nodes_link_local))); + /* check duplicate addition */ + TEST_ASSERT_EQUAL_INT(idx, + gnrc_netif2_ipv6_group_join(netifs[0], + &ipv6_addr_all_nodes_link_local)); + TEST_ASSERT(ipv6_addr_equal(&ipv6_addr_all_nodes_link_local, + &netifs[0]->ipv6.groups[idx])); +} + +static void test_ipv6_group_join__readd_with_free_entry(void) +{ + /* Tests for possible duplicates (see #2965) */ + int idx; + + TEST_ASSERT(0 <= gnrc_netif2_ipv6_group_join(netifs[0], + &ipv6_addr_all_nodes_link_local)); + TEST_ASSERT(0 <= (idx = gnrc_netif2_ipv6_group_join(netifs[0], + &ipv6_addr_all_routers_link_local))); + gnrc_netif2_ipv6_group_leave(netifs[0], &ipv6_addr_all_nodes_link_local); + TEST_ASSERT_EQUAL_INT(idx, + gnrc_netif2_ipv6_group_join(netifs[0], + &ipv6_addr_all_routers_link_local)); +} + +static void test_ipv6_group_leave__not_allocated(void) +{ + test_ipv6_group_join__success(); + TEST_ASSERT(0 <= gnrc_netif2_ipv6_group_idx(netifs[0], + &ipv6_addr_all_nodes_link_local)); + TEST_ASSERT(0 <= gnrc_netif2_ipv6_group_join(netifs[0], + &ipv6_addr_all_routers_link_local)); + gnrc_netif2_ipv6_group_leave(netifs[0], &ipv6_addr_all_routers_link_local); + TEST_ASSERT(0 <= gnrc_netif2_ipv6_group_idx(netifs[0], + &ipv6_addr_all_nodes_link_local)); +} + +static void test_ipv6_group_leave__success(void) +{ + test_ipv6_group_join__success(); + gnrc_netif2_ipv6_group_leave(netifs[0], &ipv6_addr_all_nodes_link_local); + TEST_ASSERT_EQUAL_INT(-1, gnrc_netif2_ipv6_group_idx(netifs[0], + &ipv6_addr_all_nodes_link_local)); +} + +static void test_ipv6_group_idx__empty(void) +{ + TEST_ASSERT_EQUAL_INT(-1, gnrc_netif2_ipv6_group_idx(netifs[0], + &ipv6_addr_all_nodes_link_local)); +} + +static void test_ipv6_group_idx__wrong_netif(void) +{ + test_ipv6_group_join__success(); + TEST_ASSERT_EQUAL_INT(-1, gnrc_netif2_ipv6_group_idx(netifs[1], + &ipv6_addr_all_nodes_link_local)); +} + +static void test_ipv6_group_idx__wrong_addr(void) +{ + test_ipv6_group_join__success(); + TEST_ASSERT_EQUAL_INT(-1, gnrc_netif2_ipv6_group_idx(netifs[0], + &ipv6_addr_all_routers_link_local)); +} + +static void test_ipv6_group_idx__success(void) +{ + test_ipv6_group_join__success(); + TEST_ASSERT(0 <= gnrc_netif2_ipv6_group_idx(netifs[0], &ipv6_addr_all_nodes_link_local)); +} + +static void test_ipv6_get_iid(void) +{ + static const ipv6_addr_t ethernet_ipv6_ll = { .u8 = ETHERNET_IPV6_LL }; + eui64_t res; + + TEST_ASSERT_EQUAL_INT(0, gnrc_netif2_ipv6_get_iid(ethernet_netif, &res)); + TEST_ASSERT_EQUAL_INT(0, memcmp(&res, ðernet_ipv6_ll.u64[1], + sizeof(res))); + for (unsigned i = 0; i < DEFAULT_DEVS_NUMOF; i++) { + TEST_ASSERT_EQUAL_INT(-ENOTSUP, gnrc_netif2_ipv6_get_iid(netifs[i], &res)); + } +} + +static void test_netapi_get__HOP_LIMIT(void) +{ + uint8_t value; + + TEST_ASSERT_EQUAL_INT(sizeof(value), + gnrc_netapi_get(netifs[0]->pid, NETOPT_HOP_LIMIT, + 0, &value, sizeof(value))); + TEST_ASSERT_EQUAL_INT(netifs[0]->cur_hl, value); +} + +static void test_netapi_get__IPV6_ADDR(void) +{ + static const ipv6_addr_t exp = { NETIF0_IPV6_LL }; + ipv6_addr_t value[GNRC_NETIF2_IPV6_ADDRS_NUMOF]; + + test_ipv6_addr_add__success(); + TEST_ASSERT_EQUAL_INT(sizeof(ipv6_addr_t), gnrc_netapi_get(netifs[0]->pid, + NETOPT_IPV6_ADDR, + 0, &value, + sizeof(value))); + TEST_ASSERT(ipv6_addr_equal(&exp, &value[0])); +} + +static void test_netapi_get__IPV6_ADDR_FLAGS(void) +{ + uint8_t value[GNRC_NETIF2_IPV6_ADDRS_NUMOF]; + + test_ipv6_addr_add__success(); + TEST_ASSERT_EQUAL_INT(sizeof(uint8_t), gnrc_netapi_get(netifs[0]->pid, + NETOPT_IPV6_ADDR_FLAGS, + 0, &value, + sizeof(value))); + TEST_ASSERT_EQUAL_INT(GNRC_NETIF2_IPV6_ADDRS_FLAGS_STATE_VALID, + value[0]); +} + +static void test_netapi_get__IPV6_GROUP(void) +{ + ipv6_addr_t value[GNRC_NETIF2_IPV6_GROUPS_NUMOF]; + + test_ipv6_group_join__success(); + TEST_ASSERT_EQUAL_INT(sizeof(ipv6_addr_t), gnrc_netapi_get(netifs[0]->pid, + NETOPT_IPV6_GROUP, + 0, &value, + sizeof(value))); + TEST_ASSERT(ipv6_addr_equal(&ipv6_addr_all_nodes_link_local, &value[0])); +} + +static void test_netapi_get__IPV6_IID(void) +{ + static const ipv6_addr_t ethernet_ipv6_ll = { .u8 = ETHERNET_IPV6_LL }; + eui64_t value; + + TEST_ASSERT_EQUAL_INT(sizeof(eui64_t), gnrc_netapi_get(ethernet_netif->pid, + NETOPT_IPV6_IID, + 0, &value, sizeof(value))); + TEST_ASSERT_EQUAL_INT(0, memcmp(&value, ðernet_ipv6_ll.u64[1], + sizeof(value))); + TEST_ASSERT_EQUAL_INT(-ENOTSUP, gnrc_netapi_get(netifs[0]->pid, + NETOPT_IPV6_IID, + 0, &value, sizeof(value))); +} + +static void test_netapi_get__MAX_PACKET_SIZE(void) +{ + uint16_t value; + + TEST_ASSERT_EQUAL_INT(sizeof(uint16_t), gnrc_netapi_get(ethernet_netif->pid, + NETOPT_MAX_PACKET_SIZE, + GNRC_NETTYPE_IPV6, + &value, sizeof(value))); + TEST_ASSERT_EQUAL_INT(ETHERNET_DATA_LEN, value); + TEST_ASSERT_EQUAL_INT(sizeof(uint16_t), gnrc_netapi_get(ethernet_netif->pid, + NETOPT_MAX_PACKET_SIZE, + GNRC_NETTYPE_NETIF, + &value, sizeof(value))); + TEST_ASSERT_EQUAL_INT(ETHERNET_DATA_LEN, value); + TEST_ASSERT_EQUAL_INT(sizeof(uint16_t), gnrc_netapi_get(netifs[0]->pid, + NETOPT_MAX_PACKET_SIZE, + GNRC_NETTYPE_IPV6, + &value, sizeof(value))); + TEST_ASSERT_EQUAL_INT(IPV6_MIN_MTU, value); + TEST_ASSERT_EQUAL_INT(sizeof(uint16_t), gnrc_netapi_get(netifs[0]->pid, + NETOPT_MAX_PACKET_SIZE, + GNRC_NETTYPE_NETIF, + &value, sizeof(value))); + TEST_ASSERT_EQUAL_INT(IPV6_MIN_MTU, value); +} + +static void test_netapi_get__6LO_IPHC(void) +{ + netopt_enable_t value; + + TEST_ASSERT_EQUAL_INT(sizeof(netopt_enable_t), + gnrc_netapi_get(ethernet_netif->pid, + NETOPT_6LO_IPHC, 0, + &value, sizeof(value))); + TEST_ASSERT_EQUAL_INT(NETOPT_DISABLE, value); + TEST_ASSERT_EQUAL_INT(sizeof(netopt_enable_t), + gnrc_netapi_get(netifs[0]->pid, + NETOPT_6LO_IPHC, 0, + &value, sizeof(value))); + TEST_ASSERT_EQUAL_INT(NETOPT_DISABLE, value); +} + +static void test_netapi_get__ADDRESS(void) +{ + static const uint8_t exp_ethernet[] = ETHERNET_SRC; + uint8_t value[GNRC_NETIF2_L2ADDR_MAXLEN]; + + TEST_ASSERT_EQUAL_INT(sizeof(exp_ethernet), + gnrc_netapi_get(ethernet_netif->pid, + NETOPT_ADDRESS, 0, + &value, sizeof(value))); + TEST_ASSERT_EQUAL_INT(0, memcmp(exp_ethernet, value, sizeof(exp_ethernet))); +} + +static void test_netapi_get__ADDRESS_LONG(void) +{ + uint8_t value[GNRC_NETIF2_L2ADDR_MAXLEN]; + + TEST_ASSERT_EQUAL_INT(-ENOTSUP, + gnrc_netapi_get(ethernet_netif->pid, + NETOPT_ADDRESS_LONG, 0, + &value, sizeof(value))); + TEST_ASSERT_EQUAL_INT(-ENOTSUP, + gnrc_netapi_get(netifs[0]->pid, + NETOPT_ADDRESS_LONG, 0, + &value, sizeof(value))); +} + +static void test_netapi_set__HOP_LIMIT(void) +{ + uint8_t value = 89; + + TEST_ASSERT_EQUAL_INT(sizeof(value), + gnrc_netapi_set(netifs[0]->pid, + NETOPT_HOP_LIMIT, 0, + &value, sizeof(value))); + TEST_ASSERT_EQUAL_INT(value, netifs[0]->cur_hl); +} + +static void test_netapi_set__IPV6_ADDR(void) +{ + ipv6_addr_t value = { .u8 = NETIF0_IPV6_LL }; + static const uint16_t context = (64U << 8) | + (GNRC_NETIF2_IPV6_ADDRS_FLAGS_STATE_VALID); + + TEST_ASSERT(0 > gnrc_netif2_ipv6_addr_idx(netifs[0], &value)); + TEST_ASSERT_EQUAL_INT(sizeof(value), + gnrc_netapi_set(netifs[0]->pid, + NETOPT_IPV6_ADDR, context, + &value, sizeof(value))); + TEST_ASSERT(0 <= gnrc_netif2_ipv6_addr_idx(netifs[0], &value)); +} + +static void test_netapi_set__IPV6_ADDR_REMOVE(void) +{ + ipv6_addr_t value = { .u8 = NETIF0_IPV6_LL }; + + test_ipv6_addr_add__success(); + TEST_ASSERT(0 <= gnrc_netif2_ipv6_addr_idx(netifs[0], &value)); + TEST_ASSERT_EQUAL_INT(sizeof(value), + gnrc_netapi_set(netifs[0]->pid, + NETOPT_IPV6_ADDR_REMOVE, 0, + &value, sizeof(value))); + TEST_ASSERT(0 > gnrc_netif2_ipv6_addr_idx(netifs[0], &value)); +} + +static void test_netapi_set__IPV6_GROUP(void) +{ + ipv6_addr_t value = IPV6_ADDR_ALL_NODES_LINK_LOCAL; + + TEST_ASSERT(0 > gnrc_netif2_ipv6_group_idx(netifs[0], &value)); + TEST_ASSERT_EQUAL_INT(sizeof(value), + gnrc_netapi_set(netifs[0]->pid, + NETOPT_IPV6_GROUP, 0, + &value, sizeof(value))); + TEST_ASSERT(0 <= gnrc_netif2_ipv6_group_idx(netifs[0], &value)); +} + +static void test_netapi_set__IPV6_GROUP_LEAVE(void) +{ + ipv6_addr_t value = IPV6_ADDR_ALL_NODES_LINK_LOCAL; + + test_ipv6_group_join__success(); + TEST_ASSERT(0 <= gnrc_netif2_ipv6_group_idx(netifs[0], &value)); + TEST_ASSERT_EQUAL_INT(sizeof(value), + gnrc_netapi_set(netifs[0]->pid, + NETOPT_IPV6_GROUP_LEAVE, 0, + &value, sizeof(value))); + TEST_ASSERT(0 > gnrc_netif2_ipv6_group_idx(netifs[0], &value)); +} + +static void test_netapi_set__MAX_PACKET_SIZE(void) +{ + uint16_t value = 57194; + + TEST_ASSERT_EQUAL_INT(sizeof(value), + gnrc_netapi_set(netifs[0]->pid, + NETOPT_MAX_PACKET_SIZE, + GNRC_NETTYPE_IPV6, + &value, sizeof(value))); + TEST_ASSERT_EQUAL_INT(value, netifs[0]->ipv6.mtu); + TEST_ASSERT_EQUAL_INT(-ENOTSUP, + gnrc_netapi_set(netifs[0]->pid, + NETOPT_MAX_PACKET_SIZE, 0, + &value, sizeof(value))); +} + +static void test_netapi_set__6LO_IPHC(void) +{ + netopt_enable_t value = NETOPT_ENABLE; + + TEST_ASSERT_EQUAL_INT(sizeof(value), + gnrc_netapi_set(netifs[0]->pid, + NETOPT_6LO_IPHC, 0, + &value, sizeof(value))); + TEST_ASSERT(netifs[0]->flags & GNRC_NETIF2_FLAGS_6LO_HC); +} + +static void test_netapi_set__ADDRESS(void) +{ + static const uint8_t exp_ethernet[] = ETHERNET_SRC; + uint8_t value[] = { LA1 + 1, LA2 + 2, LA3 + 3, LA4 + 4, LA5 + 5, LA6 + 6 }; + + TEST_ASSERT_EQUAL_INT(sizeof(exp_ethernet), + gnrc_netapi_set(ethernet_netif->pid, + NETOPT_ADDRESS, 0, + &value, sizeof(value))); + TEST_ASSERT_EQUAL_INT(sizeof(value), ethernet_netif->l2addr_len); + TEST_ASSERT_EQUAL_INT(0, memcmp(value, ethernet_netif->l2addr, + sizeof(value))); + TEST_ASSERT_EQUAL_INT(-ENOTSUP, + gnrc_netapi_set(netifs[0]->pid, + NETOPT_ADDRESS_LONG, 0, + &value, sizeof(value))); + /* return addresses to previous state for further testing */ + memcpy(value, exp_ethernet, sizeof(exp_ethernet)); + TEST_ASSERT_EQUAL_INT(sizeof(exp_ethernet), + gnrc_netapi_set(ethernet_netif->pid, + NETOPT_ADDRESS, 0, + &value, sizeof(value))); + TEST_ASSERT_EQUAL_INT(sizeof(value), ethernet_netif->l2addr_len); + TEST_ASSERT_EQUAL_INT(0, memcmp(value, ethernet_netif->l2addr, + sizeof(value))); +} + +static void test_netapi_set__ADDRESS_LONG(void) +{ + uint8_t value[GNRC_NETIF2_L2ADDR_MAXLEN]; + + TEST_ASSERT_EQUAL_INT(-ENOTSUP, + gnrc_netapi_set(ethernet_netif->pid, + NETOPT_ADDRESS_LONG, 0, + &value, sizeof(value))); + TEST_ASSERT_EQUAL_INT(-ENOTSUP, + gnrc_netapi_set(netifs[0]->pid, + NETOPT_ADDRESS_LONG, 0, + &value, sizeof(value))); +} + +static void test_netapi_send__raw_unicast_ethernet_packet(void) +{ + uint8_t dst[] = { LA1, LA2, LA3, LA4, LA5, LA6 + 1 }; + gnrc_pktsnip_t *pkt = gnrc_pktbuf_add(NULL, "ABCDEFG", + sizeof("ABCDEFG"), + GNRC_NETTYPE_UNDEF); + TEST_ASSERT_NOT_NULL(pkt); + gnrc_pktsnip_t *netif = gnrc_netif_hdr_build(NULL, 0, dst, sizeof(dst)); + TEST_ASSERT_NOT_NULL(netif); + LL_PREPEND(pkt, netif); + gnrc_netapi_send(ethernet_netif->pid, pkt); +} + +static void test_netapi_send__raw_broadcast_ethernet_packet(void) +{ + gnrc_netif_hdr_t *hdr; + gnrc_pktsnip_t *pkt = gnrc_pktbuf_add(NULL, "ABCDEFG", + sizeof("ABCDEFG"), + GNRC_NETTYPE_UNDEF); + TEST_ASSERT_NOT_NULL(pkt); + gnrc_pktsnip_t *netif = gnrc_netif_hdr_build(NULL, 0, NULL, 0); + TEST_ASSERT_NOT_NULL(netif); + hdr = netif->data; + hdr->flags |= GNRC_NETIF_HDR_FLAGS_BROADCAST; + LL_PREPEND(pkt, netif); + gnrc_netapi_send(ethernet_netif->pid, pkt); +} + +static void test_netapi_send__ipv6_unicast_ethernet_packet(void) +{ + ipv6_hdr_t *ipv6_hdr; + uint8_t dst_netif[] = { LA1, LA2, LA3, LA4, LA5, LA6 + 1 }; + static const ipv6_addr_t dst_ipv6 = { .u8 = { LP1, LP2, LP3, LP4, + LP5, LP6, LP7, LP8, + LA1 ^ 0x2, LA2, LA3, 0xff, + 0xfe, LA4, LA5, LA6 + 1} }; + static const ipv6_addr_t src_ipv6 = { .u8 = ETHERNET_IPV6_LL }; + gnrc_pktsnip_t *payload = gnrc_pktbuf_add(NULL, "ABCDEFG", + sizeof("ABCDEFG"), + GNRC_NETTYPE_UNDEF); + TEST_ASSERT_NOT_NULL(payload); + /* we don't send through gnrc_ipv6 (because we are lazy and don't want + * to update the neighbor cache ;-)) so we need to set the IPv6 source + * address */ + gnrc_pktsnip_t *pkt = gnrc_ipv6_hdr_build(payload, &src_ipv6, &dst_ipv6); + TEST_ASSERT_NOT_NULL(pkt); + ipv6_hdr = pkt->data; + ipv6_hdr->len = byteorder_htons(sizeof("ABCDEFG")); + ipv6_hdr->nh = PROTNUM_IPV6_NONXT; + ipv6_hdr->hl = ethernet_netif->cur_hl; + gnrc_pktsnip_t *netif = gnrc_netif_hdr_build(NULL, 0, dst_netif, + sizeof(dst_netif)); + TEST_ASSERT_NOT_NULL(netif); + LL_PREPEND(pkt, netif); + gnrc_netapi_send(ethernet_netif->pid, pkt); +} + +static void test_netapi_send__ipv6_multicast_ethernet_packet(void) +{ + ipv6_hdr_t *ipv6_hdr; + gnrc_netif_hdr_t *netif_hdr; + static const ipv6_addr_t src_ipv6 = { .u8 = ETHERNET_IPV6_LL }; + gnrc_pktsnip_t *payload = gnrc_pktbuf_add(NULL, "ABCDEFG", + sizeof("ABCDEFG"), + GNRC_NETTYPE_UNDEF); + TEST_ASSERT_NOT_NULL(payload); + /* we don't send through gnrc_ipv6 (because we are lazy and don't want + * to update the neighbor cache ;-)) so we need to set the IPv6 source + * address */ + gnrc_pktsnip_t *pkt = gnrc_ipv6_hdr_build(payload, &src_ipv6, + &ipv6_addr_all_nodes_link_local); + TEST_ASSERT_NOT_NULL(pkt); + ipv6_hdr = pkt->data; + ipv6_hdr->len = byteorder_htons(sizeof("ABCDEFG")); + ipv6_hdr->nh = PROTNUM_IPV6_NONXT; + ipv6_hdr->hl = ethernet_netif->cur_hl; + gnrc_pktsnip_t *netif = gnrc_netif_hdr_build(NULL, 0, NULL, 0); + TEST_ASSERT_NOT_NULL(netif); + netif_hdr = netif->data; + netif_hdr->flags |= GNRC_NETIF_HDR_FLAGS_MULTICAST; + LL_PREPEND(pkt, netif); + gnrc_netapi_send(ethernet_netif->pid, pkt); +} + +static void test_netapi_recv__empty_ethernet_payload(void) +{ + static const uint8_t data[] = { LA1, LA2, LA3, LA6, LA7, LA8, + LA1, LA2, LA3, LA6, LA7, LA8 + 1, + 0xff, 0xff }; + + puts("pktdump dumping Ethernet packet with empty payload"); + _test_trigger_recv(ethernet_netif, data, sizeof(data)); +} + +static void test_netapi_recv__raw_ethernet_payload(void) +{ + static const uint8_t data[] = { LA1, LA2, LA3, LA6, LA7, LA8, + LA1, LA2, LA3, LA6, LA7, LA8 + 1, + 0xff, 0xff, 0x12, 0x34, 0x45, 0x56 }; + + puts("pktdump dumping Ethernet packet with payload 12 34 45 56"); + _test_trigger_recv(ethernet_netif, data, sizeof(data)); +} + +static void test_netapi_recv__ipv6_ethernet_payload(void) +{ + static const uint8_t data[] = { LA1, LA2, LA3, LA6, LA7, LA8, + LA1, LA2, LA3, LA6, LA7, LA8 + 1, + 0x86, 0xdd, /* Ethertype: IPv6 */ + 0x60, 0, 0, 0, /* Version + TC + FL */ + 0, 1, /* payload length 1 */ + 59, /* next header: no next header */ + 64, /* hop limit */ + /* IPv6 source */ + LP1, LP2, LP3, LP4, LP5, LP6, LP7, LP8, + LA1 ^ 1, LA2, LA3, 0xff, 0xfe, LA6, LA7, LA8, + /* IPv6 destination */ + LP1, LP2, LP3, LP4, LP5, LP6, LP7, LP8, + LA1 ^ 1, LA2, LA3, 0xff, 0xfe, LA6, LA7, LA8 + 1, + 0x01 /* payload */ + }; + + puts("pktdump dumping IPv6 over Ethernet packet with payload 01"); + _test_trigger_recv(ethernet_netif, data, sizeof(data)); +} + +static Test *embunit_tests_gnrc_netif2(void) +{ + EMB_UNIT_TESTFIXTURES(fixtures) { + new_TestFixture(test_creation), + new_TestFixture(test_get_by_pid), + new_TestFixture(test_addr_to_str), + new_TestFixture(test_addr_from_str), + new_TestFixture(test_ipv6_addr_add__ENOMEM), + new_TestFixture(test_ipv6_addr_add__success), + new_TestFixture(test_ipv6_addr_add__readd_with_free_entry), + new_TestFixture(test_ipv6_addr_remove__not_allocated), + new_TestFixture(test_ipv6_addr_remove__success), + new_TestFixture(test_ipv6_addr_idx__empty), + new_TestFixture(test_ipv6_addr_idx__wrong_netif), + new_TestFixture(test_ipv6_addr_idx__wrong_addr), + new_TestFixture(test_ipv6_addr_idx__success), + new_TestFixture(test_ipv6_addr_match__empty), + new_TestFixture(test_ipv6_addr_match__wrong_netif), + new_TestFixture(test_ipv6_addr_match__wrong_addr), + new_TestFixture(test_ipv6_addr_match__success18), + new_TestFixture(test_ipv6_addr_match__success23), + new_TestFixture(test_ipv6_addr_match__success64), + new_TestFixture(test_ipv6_addr_best_src__multicast_input), + new_TestFixture(test_ipv6_addr_best_src__other_subnet), + new_TestFixture(test_get_by_ipv6_addr__empty), + new_TestFixture(test_get_by_ipv6_addr__success), + new_TestFixture(test_get_by_prefix__empty), + new_TestFixture(test_get_by_prefix__success18), + new_TestFixture(test_get_by_prefix__success23), + new_TestFixture(test_get_by_prefix__success64), + new_TestFixture(test_ipv6_group_join__ENOMEM), + new_TestFixture(test_ipv6_group_join__success), + new_TestFixture(test_ipv6_group_join__readd_with_free_entry), + new_TestFixture(test_ipv6_group_leave__not_allocated), + new_TestFixture(test_ipv6_group_leave__success), + new_TestFixture(test_ipv6_group_idx__empty), + new_TestFixture(test_ipv6_group_idx__wrong_netif), + new_TestFixture(test_ipv6_group_idx__wrong_addr), + new_TestFixture(test_ipv6_group_idx__success), + new_TestFixture(test_ipv6_get_iid), + new_TestFixture(test_netapi_get__HOP_LIMIT), + new_TestFixture(test_netapi_get__IPV6_ADDR), + new_TestFixture(test_netapi_get__IPV6_ADDR_FLAGS), + new_TestFixture(test_netapi_get__IPV6_GROUP), + new_TestFixture(test_netapi_get__IPV6_IID), + new_TestFixture(test_netapi_get__MAX_PACKET_SIZE), + new_TestFixture(test_netapi_get__6LO_IPHC), + new_TestFixture(test_netapi_get__ADDRESS), + new_TestFixture(test_netapi_get__ADDRESS_LONG), + new_TestFixture(test_netapi_set__HOP_LIMIT), + new_TestFixture(test_netapi_set__IPV6_ADDR), + new_TestFixture(test_netapi_set__IPV6_ADDR_REMOVE), + new_TestFixture(test_netapi_set__IPV6_GROUP), + new_TestFixture(test_netapi_set__IPV6_GROUP_LEAVE), + new_TestFixture(test_netapi_set__MAX_PACKET_SIZE), + new_TestFixture(test_netapi_set__6LO_IPHC), + new_TestFixture(test_netapi_set__ADDRESS), + new_TestFixture(test_netapi_set__ADDRESS_LONG), + /* only add tests not involving output here */ + }; + EMB_UNIT_TESTCALLER(tests, _set_up, NULL, fixtures); + + return (Test *)&tests; +} + +int main(void) +{ + _tests_init(); + netdev_test_set_get_cb((netdev_test_t *)ethernet_dev, NETOPT_ADDRESS, + _get_netdev_address); + netdev_test_set_set_cb((netdev_test_t *)ethernet_dev, NETOPT_ADDRESS, + _set_netdev_address); + TESTS_START(); + TESTS_RUN(embunit_tests_gnrc_netif2()); + TESTS_END(); + /* add netapi send and receive tests here */ + test_netapi_send__raw_unicast_ethernet_packet(); + test_netapi_send__raw_broadcast_ethernet_packet(); + test_netapi_send__ipv6_unicast_ethernet_packet(); + test_netapi_send__ipv6_multicast_ethernet_packet(); + test_netapi_recv__empty_ethernet_payload(); + test_netapi_recv__raw_ethernet_payload(); + test_netapi_recv__ipv6_ethernet_payload(); + return 0; +} + +static inline int _mock_netif_send(gnrc_netif2_t *netif, gnrc_pktsnip_t *pkt) +{ + (void)netif; + (void)pkt; + return -1; +} + +static inline gnrc_pktsnip_t *_mock_netif_recv(gnrc_netif2_t * netif) +{ + (void)netif; + return NULL; +} + +static uint8_t ethernet_l2addr[] = ETHERNET_SRC; + +static int _get_netdev_address(netdev_t *dev, void *value, size_t max_len) +{ + if (dev == ethernet_dev) { + assert(max_len >= sizeof(ethernet_l2addr)); + memcpy(value, ethernet_l2addr, sizeof(ethernet_l2addr)); + return sizeof(ethernet_l2addr); + } + return -ENOTSUP; +} + +static int _set_netdev_address(netdev_t *dev, void *value, size_t value_len) +{ + if (dev == ethernet_dev) { + assert(value_len <= sizeof(ethernet_l2addr)); + memcpy(ethernet_l2addr, value, value_len); + return value_len; + } + return -ENOTSUP; +} diff --git a/tests/gnrc_netif2/tests/01-run.py b/tests/gnrc_netif2/tests/01-run.py new file mode 100755 index 0000000000000..584bf4465b763 --- /dev/null +++ b/tests/gnrc_netif2/tests/01-run.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2016 Kaspar Schleiser +# Copyright (C) 2016 Takuo Yonezawa +# +# 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 os +import sys + +sys.path.append(os.path.join(os.environ['RIOTBASE'], 'dist/tools/testrunner')) +import testrunner + +def testfunc(child): + # embUnit tests + child.expect(r"OK \(\d+ tests\)") + # output cross-checked hex data with WireShark -> "Import from Hex Dump..." + # test_netapi_send__raw_unicast_ethernet_packet + child.expect("Sending data from Ethernet device:") + child.expect("00000000 3E E6 B5 0F 19 23 3E E6 B5 22 FD 0A " + + "FF FF 41 42") + child.expect("00000010 43 44 45 46 47 00") + # test_netapi_send__raw_broadcast_ethernet_packet + child.expect("Sending data from Ethernet device:") + child.expect("00000000 FF FF FF FF FF FF 3E E6 B5 22 FD 0A " + + "FF FF 41 42") + child.expect("00000010 43 44 45 46 47 00") + # test_netapi_send__ipv6_unicast_ethernet_packet + child.expect("Sending data from Ethernet device:") + child.expect("00000000 3E E6 B5 0F 19 23 3E E6 B5 22 FD 0A 86 DD 60 00") + child.expect("00000010 00 00 00 08 3B 40 FE 80 00 00 00 00 00 00 3C E6") + child.expect("00000020 B5 FF FE 22 FD 0A FE 80 00 00 00 00 00 00 3C E6") + child.expect("00000030 B5 FF FE 0F 19 23 41 42 43 44 45 46 47 00") + # test_netapi_send__ipv6_multicast_ethernet_packet + child.expect("Sending data from Ethernet device:") + child.expect("00000000 33 33 00 00 00 01 3E E6 B5 22 FD 0A 86 DD 60 00") + child.expect("00000010 00 00 00 08 3B 40 FE 80 00 00 00 00 00 00 3C E6") + child.expect("00000020 B5 FF FE 22 FD 0A FF 02 00 00 00 00 00 00 00 00") + child.expect("00000030 00 00 00 00 00 01 41 42 43 44 45 46 47 00") + # test_netapi_recv__empty_ethernet_payload + child.expect("pktdump dumping Ethernet packet with empty payload") + child.expect("PKTDUMP: data received:") + child.expect(r"~~ SNIP 0 - size: 0 byte, type: NETTYPE_UNDEF \(0\)") + child.expect(r"00000000~~ SNIP 1 - size: 20 byte, type: NETTYPE_NETIF \(-1\)") + child.expect(r"if_pid: \d+ rssi: 0 lqi: 0") + child.expect("flags: 0x0") + child.expect("src_l2addr: 3e:e6:b5:22:fd:0b") + child.expect("dst_l2addr: 3e:e6:b5:22:fd:0a") + child.expect("~~ PKT - 2 snips, total size: 20 byte") + # test_netapi_recv__raw_ethernet_payload + child.expect("pktdump dumping Ethernet packet with payload 12 34 45 56") + child.expect("PKTDUMP: data received:") + child.expect(r"~~ SNIP 0 - size: 4 byte, type: NETTYPE_UNDEF \(0\)") + child.expect("00000000 12 34 45 56") + child.expect(r"~~ SNIP 1 - size: 20 byte, type: NETTYPE_NETIF \(-1\)") + child.expect(r"if_pid: \d+ rssi: 0 lqi: 0") + child.expect("flags: 0x0") + child.expect("src_l2addr: 3e:e6:b5:22:fd:0b") + child.expect("dst_l2addr: 3e:e6:b5:22:fd:0a") + child.expect("~~ PKT - 2 snips, total size: 24 byte") + # test_netapi_recv__ipv6_ethernet_payload + child.expect("pktdump dumping IPv6 over Ethernet packet with payload 01") + child.expect("PKTDUMP: data received:") + # payload not dumped because not parsed yet, but header looks okay, so let's + # assume the payload is as well + child.expect(r"~~ SNIP 0 - size: 41 byte, type: NETTYPE_IPV6 \(2\)") + child.expect(r"traffic class: 0x00 \(ECN: 0x0, DSCP: 0x00\)") + child.expect("flow label: 0x00000") + child.expect("length: 1 next header: 59 hop limit: 64") + child.expect("source address: fe80::3fe6:b5ff:fe22:fd0a") + child.expect("destination address: fe80::3fe6:b5ff:fe22:fd0b") + child.expect(r"~~ SNIP 1 - size: 20 byte, type: NETTYPE_NETIF \(-1\)") + child.expect(r"if_pid: \d+ rssi: 0 lqi: 0") + child.expect("flags: 0x0") + child.expect("src_l2addr: 3e:e6:b5:22:fd:0b") + child.expect("dst_l2addr: 3e:e6:b5:22:fd:0a") + child.expect("~~ PKT - 2 snips, total size: 61 byte") + +if __name__ == "__main__": + sys.exit(testrunner.run(testfunc, timeout=1, traceback=True))