From 0b80c7ed173d642b7fab9b3926dc70a92bf89539 Mon Sep 17 00:00:00 2001 From: Martine Lenders <m.lenders@fu-berlin.de> Date: Tue, 16 May 2017 13:23:54 +0200 Subject: [PATCH] gnrc_netif2: Introduction of a new GNRC network interface API --- sys/include/net/gnrc/netif2.h | 322 +++++ sys/include/net/gnrc/netif2/6lo.h | 45 + sys/include/net/gnrc/netif2/conf.h | 125 ++ sys/include/net/gnrc/netif2/ethernet.h | 49 + sys/include/net/gnrc/netif2/flags.h | 144 +++ sys/include/net/gnrc/netif2/internal.h | 401 +++++++ sys/include/net/gnrc/netif2/ipv6.h | 253 ++++ sys/include/net/gnrc/netif2/mac.h | 58 + sys/include/net/netopt.h | 61 + sys/net/crosslayer/netopt/netopt.c | 9 + sys/net/gnrc/Makefile | 3 + sys/net/gnrc/netif2/Makefile | 3 + sys/net/gnrc/netif2/gnrc_netif2.c | 1265 ++++++++++++++++++++ sys/net/gnrc/netif2/gnrc_netif2_ethernet.c | 253 ++++ 14 files changed, 2991 insertions(+) create mode 100644 sys/include/net/gnrc/netif2.h create mode 100644 sys/include/net/gnrc/netif2/6lo.h create mode 100644 sys/include/net/gnrc/netif2/conf.h create mode 100644 sys/include/net/gnrc/netif2/ethernet.h create mode 100644 sys/include/net/gnrc/netif2/flags.h create mode 100644 sys/include/net/gnrc/netif2/internal.h create mode 100644 sys/include/net/gnrc/netif2/ipv6.h create mode 100644 sys/include/net/gnrc/netif2/mac.h create mode 100644 sys/net/gnrc/netif2/Makefile create mode 100644 sys/net/gnrc/netif2/gnrc_netif2.c create mode 100644 sys/net/gnrc/netif2/gnrc_netif2_ethernet.c diff --git a/sys/include/net/gnrc/netif2.h b/sys/include/net/gnrc/netif2.h new file mode 100644 index 0000000000..517e91c66a --- /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 <mlenders@inf.fu-berlin.de> + */ +#ifndef NET_GNRC_NETIF2_H +#define NET_GNRC_NETIF2_H + +#include <stddef.h> +#include <stdint.h> +#include <stdbool.h> + +#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 net_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 packet 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 /* NET_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 0000000000..7a9415e017 --- /dev/null +++ b/sys/include/net/gnrc/netif2/6lo.h @@ -0,0 +1,45 @@ +/* + * 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 6LoWPAN definitions for @ref net_gnrc_netif2 + * + * @author Martine Lenders <m.lenders@fu-berlin.de> + */ +#ifndef NET_GNRC_NETIF2_6LO_H +#define NET_GNRC_NETIF2_6LO_H + +#include <stdint.h> + +#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 0000000000..80544f7169 --- /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 <mlenders@inf.fu-berlin.de> + */ +#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 Intentionally 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 0000000000..c39f5d6745 --- /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 <mlenders@inf.fu-berlin.de> + */ +#ifndef NET_GNRC_NETIF2_ETHERNET_H +#define NET_GNRC_NETIF2_ETHERNET_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_ETHERNET_H */ +/** @} */ diff --git a/sys/include/net/gnrc/netif2/flags.h b/sys/include/net/gnrc/netif2/flags.h new file mode 100644 index 0000000000..dbbbd40b70 --- /dev/null +++ b/sys/include/net/gnrc/netif2/flags.h @@ -0,0 +1,144 @@ +/* + * 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 <m.lenders@fu-berlin.de> + */ +#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 6CIO (which propagates this flag throughout) not implemented yet + * @see [RFC 7400, section 3.3](https://tools.ietf.org/html/rfc7400#section-3.3) + * @see [draft-ietf-6lo-rfc6775-update-09, section 6.3] + * (https://tools.ietf.org/html/draft-ietf-6lo-rfc6775-update-09#section-6.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 0000000000..494d70e0e0 --- /dev/null +++ b/sys/include/net/gnrc/netif2/internal.h @@ -0,0 +1,401 @@ +/* + * 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 <m.lenders@fu-berlin.de> + */ +#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_TENTATIVE + * means that this address is assumed to be added due to + * stateless auto-address configuration and actions + * related to that may be performed in the background. + * - Setting the address' state to + * @ref GNRC_NETIF2_IPV6_ADDRS_FLAGS_STATE_VALID means + * that the address is assumed to be manually configured + * and its prefix will be added to the node's prefix + * list (valid and preferred lifetime will be set to + * infinite, but can be changed using + * @ref gnrc_ipv6_nib_pl_set()). + * + * + * @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 Gets number of duplicate address detection transmissions already + * performed for an address + * + * @param[in] netif the network interface + * @param[in] idx index of the address (and its flags) + * + * @return the number of duplicate address detection transmissions already + * performed + */ +static inline uint8_t gnrc_netif2_ipv6_addr_dad_trans(const gnrc_netif2_t *netif, + int idx) +{ + return netif->ipv6.addrs_flags[idx] & GNRC_NETIF2_IPV6_ADDRS_FLAGS_STATE_TENTATIVE; +} + +/** + * @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 Requires prior locking + * + * @param[in] netif the network interface + * + * @return true, if the interface is allowed to send out router advertisements + * @return false, if the interface is not allowed to send out router + * advertisements + */ +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/rfc6775#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/rfc6775#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/rfc6775#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 0000000000..ba9b9811dc --- /dev/null +++ b/sys/include/net/gnrc/netif2/ipv6.h @@ -0,0 +1,253 @@ +/* + * 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 <mlenders@inf.fu-berlin.de> + */ +#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 (0x1fU) + +/** + * @brief Tentative states (with encoded DAD retransmissions) + * + * The retransmissions of DAD transmits can be decoded from this state by + * applying it as a mask to the [flags](gnrc_netif2_ipv6_t::addrs_flags) of the + * address. + */ +#define GNRC_NETIF2_IPV6_ADDRS_FLAGS_STATE_TENTATIVE (0x07U) + +/** + * @brief Deprecated address state (still valid, but not preferred) + */ +#define GNRC_NETIF2_IPV6_ADDRS_FLAGS_STATE_DEPRECATED (0x08U) + +/** + * @brief Valid address state + */ +#define GNRC_NETIF2_IPV6_ADDRS_FLAGS_STATE_VALID (0x10U) + +/** + * @brief Address is an anycast address + */ +#define GNRC_NETIF2_IPV6_ADDRS_FLAGS_ANYCAST (0x20U) +/** @} */ + +/** + * @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. + * + * The callback may be `NULL` if no such behavior is required by the routing + * protocol (or no routing protocol is present). + * + * @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. + */ + void (*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 transmission 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 0000000000..8aed068f3f --- /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 <mlenders@inf.fu-berlin.de> + */ +#ifndef NET_GNRC_NETIF2_MAC_H +#define NET_GNRC_NETIF2_MAC_H + +#include "net/gnrc/mac/types.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 55d9c3ef81..7d688c08c4 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 { * <a href="https://tools.ietf.org/html/rfc4944">RFC 4944</a>). */ 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 */ @@ -379,6 +436,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 33cb235265..39b9fd0c25 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", @@ -78,6 +86,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 74bc85647c..28e08a942c 100644 --- a/sys/net/gnrc/Makefile +++ b/sys/net/gnrc/Makefile @@ -52,6 +52,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 0000000000..590336f5da --- /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 0000000000..9166b18793 --- /dev/null +++ b/sys/net/gnrc/netif2/gnrc_netif2.c @@ -0,0 +1,1265 @@ +/* + * 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 <m.lenders@fu-berlin.de> + * @author René Kijewski <rene.kijewski@fu-berlin.de> + * @author Oliver Hahm <oliver.hahm@inria.fr> + */ + +#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 safe 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 safe 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 safe 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 safe 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 <a href="http://tools.ietf.org/html/rfc6724#section-4"> + * RFC6724, section 4 + * </a> + * @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 <a href="http://tools.ietf.org/html/rfc6724#section-5"> + * RFC6724, section 5 + * </a> + * + * @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) == + GNRC_NETIF2_IPV6_ADDRS_FLAGS_STATE_TENTATIVE) { + /* set to first retransmission */ + flags &= ~GNRC_NETIF2_IPV6_ADDRS_FLAGS_STATE_TENTATIVE; + flags |= 0x1; + } + 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 <a href="http://tools.ietf.org/html/rfc6724#section-4"> + * RFC6724, section 4 + * </a> + * @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 winning 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 0000000000..70152ea23a --- /dev/null +++ b/sys/net/gnrc/netif2/gnrc_netif2_ethernet.c @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2015 Kaspar Schleiser <kaspar@schleiser.de> + * 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 <mlenders@inf.fu-berlin.de> + * @author Kaspar Schleiser <kaspar@schleiser.de> + */ + +#ifdef MODULE_NETDEV_ETH +#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 than 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; +} +#else /* MODULE_NETDEV_ETH */ +typedef int dont_be_pedantic; +#endif /* MODULE_NETDEV_ETH */ + +/** @} */ -- GitLab