diff --git a/sys/include/net/gnrc/netif2.h b/sys/include/net/gnrc/netif2.h
new file mode 100644
index 0000000000000000000000000000000000000000..517e91c66a5242e6250bec848b8bb5e2d7d6a687
--- /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 0000000000000000000000000000000000000000..7a9415e017cabe3f6fb6f7b17f86aa3fab88b0ae
--- /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 0000000000000000000000000000000000000000..80544f71690a85ad700afba203657f80fd4e3f9d
--- /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 0000000000000000000000000000000000000000..c39f5d6745ddae924b2e7c0051375271642196eb
--- /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 0000000000000000000000000000000000000000..dbbbd40b700ef1e063647be70f753069df9004f8
--- /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 0000000000000000000000000000000000000000..494d70e0e05fa9231a76b4b43586e96bf01c5631
--- /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 0000000000000000000000000000000000000000..ba9b9811dc411be946502697fd2a0d3450fadcb0
--- /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 0000000000000000000000000000000000000000..8aed068f3fd01a49ef6722fc1d369aa561774e5c
--- /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 55d9c3ef81a70e077ea6655833555f1059f1f78d..7d688c08c462b20aa60624689242fad0138ba4a3 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 33cb2352652ecefc9ba8124f254200ddf63ceb37..39b9fd0c25bb8b7fd4f1271c45a41ea1bb4b4c9b 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 74bc85647c4e4277446a33d4fde6ffd0a6a30023..28e08a942ca0ce45e2d360d79c652f6a5831750a 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 0000000000000000000000000000000000000000..590336f5daecaf7c1bb550bf2eb7518418512882
--- /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 0000000000000000000000000000000000000000..9166b18793fde1e41b071dc64745fab82afb4335
--- /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 0000000000000000000000000000000000000000..70152ea23aa52e5deb87a7a6d8fbea14377e74ce
--- /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,
+                              &ethernet_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 */
+
+/** @} */