From bf60c2fbfaf5c3618cb3e3f9c666f87f31e1436e Mon Sep 17 00:00:00 2001
From: Martine Lenders <mail@martine-lenders.eu>
Date: Fri, 2 Sep 2016 19:03:35 +0200
Subject: [PATCH] pkg: provide sock_ip support for lwip

---
 Makefile.dep                            |    9 +
 pkg/lwip/Makefile.include               |    9 +
 pkg/lwip/contrib/sock/Makefile          |    3 +
 pkg/lwip/contrib/sock/ip/Makefile       |    3 +
 pkg/lwip/contrib/sock/ip/lwip_sock_ip.c |  181 ++++
 pkg/lwip/contrib/sock/lwip_sock.c       |  526 ++++++++++
 pkg/lwip/include/lwip/sock_internal.h   |   59 ++
 pkg/lwip/include/lwipopts.h             |    2 +-
 pkg/lwip/include/sock_types.h           |   41 +
 tests/lwip_sock_ip/Makefile             |   46 +
 tests/lwip_sock_ip/README.md            |   33 +
 tests/lwip_sock_ip/constants.h          |   48 +
 tests/lwip_sock_ip/main.c               | 1190 +++++++++++++++++++++++
 tests/lwip_sock_ip/stack.c              |  370 +++++++
 tests/lwip_sock_ip/stack.h              |  123 +++
 tests/lwip_sock_ip/tests/01-run.py      |  128 +++
 16 files changed, 2770 insertions(+), 1 deletion(-)
 create mode 100644 pkg/lwip/contrib/sock/Makefile
 create mode 100644 pkg/lwip/contrib/sock/ip/Makefile
 create mode 100644 pkg/lwip/contrib/sock/ip/lwip_sock_ip.c
 create mode 100644 pkg/lwip/contrib/sock/lwip_sock.c
 create mode 100644 pkg/lwip/include/lwip/sock_internal.h
 create mode 100644 pkg/lwip/include/sock_types.h
 create mode 100644 tests/lwip_sock_ip/Makefile
 create mode 100644 tests/lwip_sock_ip/README.md
 create mode 100644 tests/lwip_sock_ip/constants.h
 create mode 100644 tests/lwip_sock_ip/main.c
 create mode 100644 tests/lwip_sock_ip/stack.c
 create mode 100644 tests/lwip_sock_ip/stack.h
 create mode 100755 tests/lwip_sock_ip/tests/01-run.py

diff --git a/Makefile.dep b/Makefile.dep
index 1cf4125dea..f232c96d39 100644
--- a/Makefile.dep
+++ b/Makefile.dep
@@ -413,6 +413,14 @@ ifneq (,$(filter lwip_conn_udp,$(USEMODULE)))
   USEMODULE += lwip_udp
 endif
 
+ifneq (,$(filter lwip_sock_%,$(USEMODULE)))
+  USEMODULE += lwip_sock
+endif
+
+ifneq (,$(filter lwip_sock_ip,$(USEMODULE)))
+  USEMODULE += lwip_raw
+endif
+
 ifneq (,$(filter lwip_%,$(USEMODULE)))
   USEMODULE += lwip
 endif
@@ -438,6 +446,7 @@ endif
 
 ifneq (,$(filter lwip_contrib,$(USEMODULE)))
   USEMODULE += sema
+  USEMODULE += xtimer
 endif
 
 ifneq (,$(filter sema,$(USEMODULE)))
diff --git a/pkg/lwip/Makefile.include b/pkg/lwip/Makefile.include
index f858b60853..d2c6326301 100644
--- a/pkg/lwip/Makefile.include
+++ b/pkg/lwip/Makefile.include
@@ -16,3 +16,12 @@ endif
 ifneq (,$(filter lwip_netdev2,$(USEMODULE)))
   DIRS += $(RIOTBASE)/pkg/lwip/contrib/netdev2
 endif
+ifneq (,$(filter lwip_sock,$(USEMODULE)))
+  ifneq (,$(filter lwip_ipv6,$(USEMODULE)))
+    CFLAGS += -DSOCK_HAS_IPV6
+  endif
+  DIRS += $(RIOTBASE)/pkg/lwip/contrib/sock
+endif
+ifneq (,$(filter lwip_sock_ip,$(USEMODULE)))
+  DIRS += $(RIOTBASE)/pkg/lwip/contrib/sock/ip
+endif
diff --git a/pkg/lwip/contrib/sock/Makefile b/pkg/lwip/contrib/sock/Makefile
new file mode 100644
index 0000000000..1dfc90033b
--- /dev/null
+++ b/pkg/lwip/contrib/sock/Makefile
@@ -0,0 +1,3 @@
+MODULE := lwip_sock
+
+include $(RIOTBASE)/Makefile.base
diff --git a/pkg/lwip/contrib/sock/ip/Makefile b/pkg/lwip/contrib/sock/ip/Makefile
new file mode 100644
index 0000000000..149f0a0ae7
--- /dev/null
+++ b/pkg/lwip/contrib/sock/ip/Makefile
@@ -0,0 +1,3 @@
+MODULE := lwip_sock_ip
+
+include $(RIOTBASE)/Makefile.base
diff --git a/pkg/lwip/contrib/sock/ip/lwip_sock_ip.c b/pkg/lwip/contrib/sock/ip/lwip_sock_ip.c
new file mode 100644
index 0000000000..0a8d6b32f6
--- /dev/null
+++ b/pkg/lwip/contrib/sock/ip/lwip_sock_ip.c
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2016 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>
+ */
+
+#include <errno.h>
+
+#include "net/ipv4/addr.h"
+#include "net/ipv6/addr.h"
+#include "net/ipv6/hdr.h"
+#include "net/sock/ip.h"
+#include "timex.h"
+
+#include "lwip/api.h"
+#include "lwip/ip4.h"
+#include "lwip/ip6.h"
+#include "lwip/opt.h"
+#include "lwip/sys.h"
+#include "lwip/sock_internal.h"
+
+
+int sock_ip_create(sock_ip_t *sock, const sock_ip_ep_t *local,
+                   const sock_ip_ep_t *remote, uint8_t proto, uint16_t flags)
+{
+    assert(sock != NULL);
+
+    int res;
+    struct netconn *tmp = NULL;
+
+    /* we pay attention in lwip_sock_create that _sock_tl_ep::port is not
+     * touched for RAW */
+    if ((res = lwip_sock_create(&tmp, (struct _sock_tl_ep *)local,
+                                (struct _sock_tl_ep *)remote, proto, flags,
+                                NETCONN_RAW)) == 0) {
+        sock->conn = tmp;
+    }
+    return res;
+}
+
+void sock_ip_close(sock_ip_t *sock)
+{
+    assert(sock != NULL);
+    if (sock->conn != NULL) {
+        netconn_delete(sock->conn);
+        sock->conn = NULL;
+    }
+}
+
+int sock_ip_get_local(sock_ip_t *sock, sock_ip_ep_t *ep)
+{
+    assert(sock != NULL);
+    return (lwip_sock_get_addr(sock->conn, (struct _sock_tl_ep *)ep,
+                               1)) ? -EADDRNOTAVAIL : 0;
+}
+
+int sock_ip_get_remote(sock_ip_t *sock, sock_ip_ep_t *ep)
+{
+    assert(sock != NULL);
+    return (lwip_sock_get_addr(sock->conn, (struct _sock_tl_ep *)ep,
+                               0)) ? -ENOTCONN : 0;
+}
+
+#if LWIP_IPV4
+static uint16_t _ip4_addr_to_netif(const ip4_addr_p_t *addr)
+{
+    assert(addr != NULL);
+
+    if (!ip4_addr_isany(addr)) {
+        for (struct netif *netif = netif_list; netif != NULL; netif = netif->next) {
+            if (netif_ip4_addr(netif)->addr == addr->addr) {
+                return (int)netif->num + 1;
+            }
+        }
+    }
+    return SOCK_ADDR_ANY_NETIF;
+}
+#endif
+
+#if LWIP_IPV6
+static uint16_t _ip6_addr_to_netif(const ip6_addr_p_t *_addr)
+{
+    ip6_addr_t addr;
+
+    assert(_addr != NULL);
+    ip6_addr_copy(addr, *_addr);
+    if (!ip6_addr_isany_val(addr)) {
+        for (struct netif *netif = netif_list; netif != NULL; netif = netif->next) {
+            if (netif_get_ip6_addr_match(netif, &addr) >= 0) {
+                return (int)netif->num + 1;
+            }
+        }
+    }
+    return SOCK_ADDR_ANY_NETIF;
+}
+#endif
+
+static int _parse_iphdr(const struct netbuf *buf, void *data, size_t max_len,
+                        sock_ip_ep_t *remote)
+{
+    uint8_t *data_ptr = buf->p->payload;
+    size_t data_len = buf->p->len;
+
+    assert(buf->p->next == NULL);   /* TODO this might not be generally the case
+                                     * check later with larger payloads */
+    switch (data_ptr[0] >> 4) {
+#if LWIP_IPV4
+        case 4:
+            if ((data_len - sizeof(struct ip_hdr)) > max_len) {
+                return -ENOBUFS;
+            }
+            if (remote != NULL) {
+                struct ip_hdr *iphdr = (struct ip_hdr *)data_ptr;
+
+                assert(buf->p->len > sizeof(struct ip_hdr));
+                remote->family = AF_INET;
+                memcpy(&remote->addr, &iphdr->src, sizeof(ip4_addr_t));
+                remote->netif = _ip4_addr_to_netif(&iphdr->dest);
+            }
+            data_ptr += sizeof(struct ip_hdr);
+            data_len -= sizeof(struct ip_hdr);
+            break;
+#endif
+#if LWIP_IPV6
+        case 6:
+            if ((data_len - sizeof(struct ip6_hdr)) > max_len) {
+                return -ENOBUFS;
+            }
+            if (remote != NULL) {
+                struct ip6_hdr *iphdr = (struct ip6_hdr *)data_ptr;
+
+                assert(buf->p->len > sizeof(struct ip6_hdr));
+                remote->family = AF_INET6;
+                memcpy(&remote->addr, &iphdr->src, sizeof(ip6_addr_t));
+                remote->netif = _ip6_addr_to_netif(&iphdr->dest);
+            }
+            data_ptr += sizeof(struct ip6_hdr);
+            data_len -= sizeof(struct ip6_hdr);
+            break;
+#endif
+        default:
+            return -EPROTO;
+    }
+    memcpy(data, data_ptr, data_len);
+    return (ssize_t)data_len;
+}
+
+ssize_t sock_ip_recv(sock_ip_t *sock, void *data, size_t max_len,
+                     uint32_t timeout, sock_ip_ep_t *remote)
+{
+    struct netbuf *buf;
+    int res;
+
+    assert((sock != NULL) && (data != NULL) && (max_len > 0));
+    if ((res = lwip_sock_recv(sock->conn, timeout, &buf)) < 0) {
+        return res;
+    }
+    res = _parse_iphdr(buf, data, max_len, remote);
+    netbuf_delete(buf);
+    return res;
+}
+
+ssize_t sock_ip_send(sock_ip_t *sock, const void *data, size_t len,
+                     uint8_t proto, const sock_ip_ep_t *remote)
+{
+    assert((sock != NULL) || (remote != NULL));
+    assert((len == 0) || (data != NULL)); /* (len != 0) => (data != NULL) */
+    return lwip_sock_send(&sock->conn, data, len, proto,
+                          (struct _sock_tl_ep *)remote, NETCONN_RAW);
+}
+
+/** @} */
diff --git a/pkg/lwip/contrib/sock/lwip_sock.c b/pkg/lwip/contrib/sock/lwip_sock.c
new file mode 100644
index 0000000000..8ae799bced
--- /dev/null
+++ b/pkg/lwip/contrib/sock/lwip_sock.c
@@ -0,0 +1,526 @@
+/*
+ * Copyright (C) 2016 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>
+ */
+
+#include "lwip/sock_internal.h"
+
+#include "net/af.h"
+#include "net/ipv4/addr.h"
+#include "net/ipv6/addr.h"
+#include "net/sock.h"
+
+#include "lwip/err.h"
+#include "lwip/ip.h"
+#include "lwip/netif.h"
+#include "lwip/opt.h"
+
+#if !LWIP_IPV4 && !LWIP_IPV6
+#error "lwip_sock needs IPv4 or IPv6 support"
+#endif
+
+/**
+ * @brief   Checks if an address family is *not* supported by the lwIP
+ *          implementation
+ *
+ * @param[in] af    An address family
+ *
+ * @return  true, if @p af is *not* supported.
+ * @return  false, if @p af is supported.
+ */
+static inline bool _af_not_supported(int af)
+{
+    switch (af) {
+#if LWIP_IPV4
+        case AF_INET:
+            return false;
+#endif
+#if LWIP_IPV6
+        case AF_INET6:
+            return false;
+#endif
+        default:
+            return true;
+    }
+}
+
+#if LWIP_IPV4 && LWIP_IPV6
+static inline u8_t lwip_af_to_ip_addr_type(int af)
+{
+    switch (af) {
+        case AF_INET:
+            return IPADDR_TYPE_V4;
+        case AF_INET6:
+        case AF_UNSPEC: /* in case of any address */
+            return IPADDR_TYPE_V6;
+        default:
+            return 0xff;
+    }
+}
+#endif
+
+static bool _ep_isany(const struct _sock_tl_ep *ep)
+{
+    uint8_t *ep_addr;
+
+    if (ep == NULL) {
+        return true;
+    }
+    ep_addr = (uint8_t *)&ep->addr;
+    for (unsigned i = 0; i < sizeof(ep->addr); i++) {
+#if LWIP_IPV4
+        /* stop checking IPv6 for IPv4 addresses */
+        if ((ep->family == AF_INET) && i >= sizeof(ep->addr.ipv4)) {
+            break;
+        }
+#endif
+        if (ep_addr[i] != 0) {
+            return false;
+        }
+    }
+    return true;
+}
+
+static const ip_addr_t *_netif_to_bind_addr(int family, uint16_t netif_num)
+{
+    if (netif_num > UINT8_MAX) {
+        return NULL;
+    }
+    for (struct netif *netif = netif_list; netif != NULL; netif = netif->next) {
+        if (netif->num == (netif_num - 1)) {
+            switch (family) {
+#if LWIP_IPV4
+                case AF_INET:
+                    return &netif->ip_addr;
+#endif
+#if LWIP_IPV6
+                case AF_INET6:
+                    /* link-local address is always the 0th */
+                    return &netif->ip6_addr[0];
+#endif
+                default:
+                    return NULL;
+            }
+        }
+    }
+    return NULL;
+}
+
+static bool _addr_on_netif(int family, int netif_num, const ip_addr_t *addr)
+{
+    assert(addr != NULL);
+    assert((netif_num >= 0) && (netif_num <= UINT8_MAX));
+    for (struct netif *netif = netif_list; netif != NULL; netif = netif->next) {
+        if (netif->num == (netif_num - 1)) {
+            switch (family) {
+#if LWIP_IPV4
+                case AF_INET:
+                    return ip_2_ip4(&netif->ip_addr)->addr == ip_2_ip4(addr)->addr;
+#endif
+#if LWIP_IPV6
+                case AF_INET6:
+                    /* link-local address is always the 0th */
+                    return (netif_get_ip6_addr_match(netif, ip_2_ip6(addr)) >= 0);
+#endif
+                default:
+                    return false;
+            }
+        }
+    }
+    return false;
+}
+
+#ifdef MODULE_LWIP_SOCK_IP
+#define _set_port(p, ep, type)   \
+    if (!((type) & NETCONN_RAW)) { \
+        p = (ep)->port; \
+    }
+#else
+#define _set_port(p, ep, type)   \
+    p = (ep)->port;
+#endif
+
+static int _sock_ep_to_netconn_pars(const struct _sock_tl_ep *local,
+                                    const struct _sock_tl_ep *remote,
+                                    ip_addr_t *local_addr, u16_t *local_port,
+                                    ip_addr_t *remote_addr, u16_t *remote_port,
+                                    int *type)
+{
+    bool res = 0;
+    int family = AF_UNSPEC;
+    uint16_t netif = SOCK_ADDR_ANY_NETIF;
+
+    if ((local != NULL) && (remote != NULL) &&
+        (remote->netif != SOCK_ADDR_ANY_NETIF) &&
+        (local->netif != SOCK_ADDR_ANY_NETIF) &&
+        (remote->netif != local->netif)) {
+        return -EINVAL;
+    }
+
+#if LWIP_IPV6
+    *type &= ~NETCONN_TYPE_IPV6;
+#else
+    (void)type; /* is read but not set => compiler complains */
+#endif
+    if (local != NULL) {
+        if (_af_not_supported(local->family)) {
+            return -EAFNOSUPPORT;
+        }
+        if (local->netif != SOCK_ADDR_ANY_NETIF) {
+            netif = local->netif;
+        }
+        family = local->family;
+        _set_port(*local_port, local, *type);
+    }
+    if (remote != NULL) {
+        if (_af_not_supported(remote->family) ||
+            ((local != NULL) && (local->family != remote->family))) {
+            return -EAFNOSUPPORT;
+        }
+        if ((remote->netif != SOCK_ADDR_ANY_NETIF) &&
+            (local != NULL) && (local->netif != SOCK_ADDR_ANY_NETIF)) {
+            netif = remote->netif;
+        }
+        family = remote->family;
+        memcpy(remote_addr, &remote->addr, sizeof(remote->addr));
+#if LWIP_IPV6 && LWIP_IPV4
+        remote_addr->type = lwip_af_to_ip_addr_type(family);
+#endif
+        if (ip_addr_isany(remote_addr)) {
+            return -EINVAL;
+        }
+        _set_port(*remote_port, remote, *type);
+    }
+
+#if LWIP_IPV6
+    if (family == AF_INET6) {
+        *type |= NETCONN_TYPE_IPV6;
+    }
+#endif
+
+    if (netif != SOCK_ADDR_ANY_NETIF) {
+        if (_ep_isany(local)) {
+            const ip_addr_t *tmp = _netif_to_bind_addr(family, netif);
+            if (tmp != NULL) {
+                memcpy(local_addr, tmp, sizeof(ip_addr_t));
+                res = 1;
+            }
+            else {
+                /* netif was not a valid interface */
+                return -EINVAL;
+            }
+        }
+        /* case (local == NULL) is included in _ep_isany() */
+        /* cast to ip_addr_t alright, since type field is never used */
+        else if (_addr_on_netif(family, netif, (ip_addr_t *)&local->addr)) {
+            memcpy(local_addr, &local->addr, sizeof(local->addr));
+            res = 1;
+        }
+        else {
+            return -EINVAL;
+        }
+    }
+    else if (local != NULL) {
+        memcpy(local_addr, &local->addr, sizeof(local->addr));
+        res = 1;
+    }
+#if LWIP_IPV6 && LWIP_IPV4
+    if (local_addr != NULL) {
+        local_addr->type = lwip_af_to_ip_addr_type(family);
+    }
+#endif
+
+    return res;
+}
+
+static int _create(int type, int proto, uint16_t flags, struct netconn **out)
+{
+    if ((*out = netconn_new_with_proto_and_callback(type, proto, NULL)) == NULL) {
+        return -ENOMEM;
+    }
+#if SO_REUSE
+    if (flags & SOCK_FLAGS_REUSE_EP) {
+        ip_set_option((*out)->pcb.ip, SOF_REUSEADDR);
+    }
+#else
+    (void)flags;
+#endif
+    return 0;
+}
+
+#include <stdio.h>
+
+int lwip_sock_create(struct netconn **conn, const struct _sock_tl_ep *local,
+                     const struct _sock_tl_ep *remote, int proto,
+                     uint16_t flags, int type)
+{
+    assert(conn != NULL);
+#if LWIP_IPV6
+    assert(!(type & NETCONN_TYPE_IPV6));
+#endif
+
+    ip_addr_t local_addr, remote_addr;
+    u16_t local_port = 0, remote_port = 0;   /* 0 is used by lwIP to
+                                              * automatically generate
+                                              * port */
+    /* convert parameters */
+    int bind = _sock_ep_to_netconn_pars(local, remote, &local_addr, &local_port,
+                                        &remote_addr, &remote_port, &type);
+
+    /* error occurred during parameter conversion */
+    if (bind < 0) {
+        return bind;
+    }
+    if ((remote != NULL) && ip_addr_isany_val(remote_addr)) {
+        return -EINVAL;
+    }
+    /* if local or remote parameters are given */
+    else if ((local != NULL) || (remote != NULL)) {
+        int res = 0;
+        if ((res = _create(type, proto, flags, conn)) < 0) {
+            return res;
+        }
+        /* if parameters (local->netif, remote->netif, local->addr or
+         * local->port) demand binding */
+        if (bind) {
+            switch (netconn_bind(*conn, &local_addr, local_port)) {
+                case ERR_USE:
+                    res = -EADDRINUSE;
+                    break;
+                case ERR_VAL:
+                    res = -EINVAL;
+                    break;
+                default:
+                    break;
+            }
+            if (res < 0) {
+                netconn_delete(*conn);
+                return res;
+            }
+        }
+        if (remote != NULL) {
+            switch (netconn_connect(*conn, &remote_addr, remote_port)) {
+                case ERR_USE:
+                    res = -EADDRINUSE;
+                    break;
+                case ERR_VAL:
+                    res = -EINVAL;
+                    break;
+                default:
+                    break;
+            }
+            if (res < 0) {
+                netconn_delete(*conn);
+                return res;
+            }
+        }
+    }
+    return 0;
+}
+
+uint16_t lwip_sock_bind_addr_to_netif(const ip_addr_t *bind_addr)
+{
+    assert(bind_addr != NULL);
+
+    if (!ip_addr_isany(bind_addr)) {
+        for (struct netif *netif = netif_list; netif != NULL; netif = netif->next) {
+            if (IP_IS_V6(bind_addr)) {  /* XXX crappy API yields crappy code */
+#if LWIP_IPV6
+                if (netif_get_ip6_addr_match(netif, ip_2_ip6(bind_addr)) >= 0) {
+                    return (int)netif->num + 1;
+                }
+#endif
+            }
+            else {
+#if LWIP_IPV4
+                if (netif_ip4_addr(netif)->addr == ip_2_ip4(bind_addr)->addr) {
+                    return (int)netif->num + 1;
+                }
+#endif
+            }
+        }
+    }
+    return SOCK_ADDR_ANY_NETIF;
+}
+
+int lwip_sock_get_addr(struct netconn *conn, struct _sock_tl_ep *ep, u8_t local)
+{
+    ip_addr_t addr;
+    int res;
+#ifdef MODULE_LWIP_SOCK_IP
+    u16_t port = UINT16_MAX;
+    u16_t *port_ptr = &port;
+    /* addr needs to be NULL because netconn_getaddr returns error on connected
+     * conns as "as connecting is only a helper for upper layers [sic]" */
+    memset(&addr, 0, sizeof(addr));
+#else
+    u16_t *port_ptr = &ep->port;
+#endif
+
+    assert(ep != NULL);
+    if (conn == NULL) {
+        return 1;
+    }
+#ifdef MODULE_LWIP_SOCK_IP
+    if (!(conn->type & NETCONN_RAW)) {
+        port_ptr = &ep->port;
+    }
+#endif
+    if ((res = netconn_getaddr(conn, &addr, port_ptr, local)) != ERR_OK
+#ifdef MODULE_LWIP_SOCK_IP
+        /* XXX lwIP's API is very inconsistent here so we need to check if addr
+         * was changed */
+            && !local && ip_addr_isany_val(addr)
+#endif
+        ) {
+        return res;
+    }
+#if LWIP_IPV6 && LWIP_IPV4
+    ep->family = (addr.type == IPADDR_TYPE_V6) ? AF_INET6 : AF_INET;
+#elif LWIP_IPV6
+    ep->family = (conn->type & NETCONN_TYPE_IPV6) ? AF_INET6 : AF_INET;
+#elif LWIP_IPV4
+    ep->family = AF_INET;
+#endif
+    if (local) {
+        ep->netif = lwip_sock_bind_addr_to_netif(&addr);
+    }
+    else {
+        ep->netif = SOCK_ADDR_ANY_NETIF;
+    }
+    memcpy(&ep->addr, &addr, sizeof(ep->addr));
+    return 0;
+}
+
+int lwip_sock_recv(struct netconn *conn, uint32_t timeout, struct netbuf **buf)
+{
+    int res;
+
+    if (conn == NULL) {
+        return -EADDRNOTAVAIL;
+    }
+#if LWIP_SO_RCVTIMEO
+    if ((timeout != 0) && (timeout != SOCK_NO_TIMEOUT)) {
+        netconn_set_recvtimeout(conn, timeout / MS_IN_USEC);
+    }
+    else
+#endif
+    if ((timeout == 0) && !cib_avail(&conn->recvmbox.mbox.cib)) {
+        return -EAGAIN;
+    }
+    switch (netconn_recv(conn, buf)) {
+        case ERR_OK:
+            res = 0;
+            break;
+#if LWIP_SO_RCVTIMEO
+        case ERR_TIMEOUT:
+            res = -ETIMEDOUT;
+            break;
+#endif
+        case ERR_MEM:
+            res = -ENOMEM;
+            break;
+        default:
+            res = -EPROTO;
+            break;
+    }
+    /* unset flags */
+#if LWIP_SO_RCVTIMEO
+    netconn_set_recvtimeout(conn, 0);
+#endif
+    return res;
+}
+
+ssize_t lwip_sock_send(struct netconn **conn, const void *data, size_t len,
+                       int proto, const struct _sock_tl_ep *remote, int type)
+{
+    ip_addr_t remote_addr;
+    struct netconn *tmp;
+    struct netbuf *buf;
+    int res;
+    err_t err;
+    u16_t remote_port = 0;
+
+#if LWIP_IPV6
+    assert(!(type & NETCONN_TYPE_IPV6));
+#endif
+    if (remote != NULL) {
+        if ((res = _sock_ep_to_netconn_pars(NULL, remote, NULL, NULL,
+                                            &remote_addr, &remote_port,
+                                            &type)) < 0) {
+            return res;
+        }
+        if (ip_addr_isany_val(remote_addr)) {
+            return -EINVAL;
+        }
+    }
+
+    buf = netbuf_new();
+    if ((buf == NULL) || (netbuf_alloc(buf, len) == NULL) ||
+        (netbuf_take(buf, data, len) != ERR_OK)) {
+        netbuf_delete(buf);
+        return -ENOMEM;
+    }
+    if (((conn == NULL) || (*conn == NULL)) && (remote != NULL)) {
+        if ((res = _create(type, proto, 0, &tmp)) < 0) {
+            netbuf_delete(buf);
+            return res;
+        }
+    }
+    else if (*conn != NULL) {
+        ip_addr_t addr;
+        u16_t port;
+
+        if (((remote != NULL) &&
+             (remote->netif != SOCK_ADDR_ANY_NETIF) &&
+             (netconn_getaddr(*conn, &addr, &port, 1) == 0) &&
+             (remote->netif != lwip_sock_bind_addr_to_netif(&addr)))) {
+            return -EINVAL;
+        }
+        tmp = *conn;
+    }
+    else {
+        netbuf_delete(buf);
+        return -ENOTCONN;
+    }
+    if (remote != NULL) {
+        err = netconn_sendto(tmp, buf, &remote_addr, remote_port);
+    }
+    else {
+        err = netconn_send(tmp, buf);
+    }
+    switch (err) {
+        case ERR_OK:
+            res = len;
+            if (conn != NULL) {
+                *conn = tmp;
+            }
+            break;
+        case ERR_BUF:
+        case ERR_MEM:
+            res = -ENOMEM;
+            break;
+        case ERR_RTE:
+        case ERR_IF:
+            res = -EHOSTUNREACH;
+            break;
+        case ERR_VAL:
+        default:
+            res = -EINVAL;
+            break;
+    }
+    netbuf_delete(buf);
+    return res;
+}
+
+/** @} */
diff --git a/pkg/lwip/include/lwip/sock_internal.h b/pkg/lwip/include/lwip/sock_internal.h
new file mode 100644
index 0000000000..a0c0e464c6
--- /dev/null
+++ b/pkg/lwip/include/lwip/sock_internal.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 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    pkg_lwip_sock   lwIP-specific implementation of sock API
+ * @ingroup     pkg_lwip
+ * @brief       Provides an implementation of the @ref net_sock for the
+ *              @ref pkg_lwip
+ * @{
+ *
+ * @file
+ * @brief       lwIP-specific function @ref  definitions
+ *
+ * @author  Martine Lenders <mlenders@inf.fu-berlin.de>
+ */
+#ifndef SOCK_INTERNAL_H_
+#define SOCK_INTERNAL_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "net/af.h"
+#include "net/sock.h"
+
+#include "lwip/ip_addr.h"
+#include "lwip/api.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Internal helper functions for lwIP
+ * @internal
+ * @{
+ */
+int lwip_sock_create(struct netconn **conn, const struct _sock_tl_ep *local,
+                      const struct _sock_tl_ep *remote, int proto,
+                      uint16_t flags, int type);
+uint16_t lwip_sock_bind_addr_to_netif(const ip_addr_t *bind_addr);
+int lwip_sock_get_addr(struct netconn *conn, struct _sock_tl_ep *ep, u8_t local);
+int lwip_sock_recv(struct netconn *conn, uint32_t timeout, struct netbuf **buf);
+ssize_t lwip_sock_send(struct netconn **conn, const void *data, size_t len,
+                       int proto, const struct _sock_tl_ep *remote, int type);
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SOCK_INTERNAL_H_ */
+/** @} */
diff --git a/pkg/lwip/include/lwipopts.h b/pkg/lwip/include/lwipopts.h
index 69c8cdab0f..404864eb25 100644
--- a/pkg/lwip/include/lwipopts.h
+++ b/pkg/lwip/include/lwipopts.h
@@ -129,7 +129,7 @@ extern "C" {
 #define LWIP_UDPLITE            (0)
 #endif /* MODULE_LWIP_UDPLITE */
 
-#ifdef MODULE_LWIP_CONN
+#if defined(MODULE_LWIP_CONN) || defined(MODULE_LWIP_SOCK)
 #define LWIP_NETCONN            (1)
 #else
 #define LWIP_NETCONN            (0)
diff --git a/pkg/lwip/include/sock_types.h b/pkg/lwip/include/sock_types.h
new file mode 100644
index 0000000000..e0d8050116
--- /dev/null
+++ b/pkg/lwip/include/sock_types.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2016 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 pkg_lwip_sokc
+ * @{
+ *
+ * @file
+ * @brief lwIP-specific types
+ *
+ * @author  Martine Lenders <mlenders@inf.fu-berlin.de>
+ */
+#ifndef SOCK_TYPES_H_
+#define SOCK_TYPES_H_
+
+#include "net/af.h"
+#include "lwip/api.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief   Raw IP sock type
+ * @internal
+ */
+struct sock_ip {
+    struct netconn *conn;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SOCK_TYPES_H_ */
+/** @} */
diff --git a/tests/lwip_sock_ip/Makefile b/tests/lwip_sock_ip/Makefile
new file mode 100644
index 0000000000..d34d1e3835
--- /dev/null
+++ b/tests/lwip_sock_ip/Makefile
@@ -0,0 +1,46 @@
+APPLICATION = lwip_sock_ip
+
+include ../Makefile.tests_common
+
+# lwIP's memory management doesn't seem to work on non 32-bit platforms at the
+# moment.
+BOARD_BLACKLIST := arduino-uno arduino-duemilanove arduino-mega2560 chronos \
+                   msb-430 msb-430h telosb waspmote-pro wsn430-v1_3b \
+                   wsn430-v1_4 z1
+BOARD_INSUFFICIENT_MEMORY = nucleo-f030 nucleo-f042 nucleo-f334 \
+                            stm32f0discovery weio
+
+LWIP_IPV4 ?= 0
+
+ifneq (0, $(LWIP_IPV4))
+  USEMODULE += ipv4_addr
+  USEMODULE += lwip_arp
+  USEMODULE += lwip_ipv4
+  CFLAGS += -DETHARP_SUPPORT_STATIC_ENTRIES=1
+  LWIP_IPV6 ?= 0
+else
+  LWIP_IPV6 ?= 1
+endif
+
+ifneq (0, $(LWIP_IPV6))
+  USEMODULE += ipv6_addr
+  USEMODULE += lwip_ipv6_autoconfig
+endif
+
+USEMODULE += inet_csum
+USEMODULE += lwip_ethernet lwip_netdev2
+USEMODULE += lwip_sock_ip
+USEMODULE += netdev2_eth
+USEMODULE += netdev2_test
+USEMODULE += ps
+
+DISABLE_MODULE += auto_init
+
+CFLAGS += -DDEVELHELP
+CFLAGS += -DSO_REUSE
+CFLAGS += -DLWIP_SO_RCVTIMEO
+
+include $(RIOTBASE)/Makefile.include
+
+test:
+	./tests/01-run.py
diff --git a/tests/lwip_sock_ip/README.md b/tests/lwip_sock_ip/README.md
new file mode 100644
index 0000000000..b826a0458d
--- /dev/null
+++ b/tests/lwip_sock_ip/README.md
@@ -0,0 +1,33 @@
+Tests for lwIP's sock_ip port
+=============================
+
+This tests the `sock_ip` port of lwIP. There is no network device needed since
+a [virtual device](http://doc.riot-os.org/group__sys__netdev2__test.html) is
+provided at the backend.
+
+These tests test both IPv4 and IPv6 capabilities. They can be activated by
+the `LWIP_IPV4` and `LWIP_IPV6` environment variables to a non-zero value.
+IPv6 is activated by default:
+
+```sh
+make all test
+# or
+LWIP_IPV6=1 make all test
+```
+
+To just test IPv4 set the `LWIP_IPV4` to a non-zero value (IPv6 will be
+deactivated automatically):
+
+```sh
+LWIP_IPV4=1 make all test
+```
+
+To test both set the `LWIP_IPV4` and `LWIP_IPV6` to a non-zero value:
+
+```sh
+LWIP_IPV4=1 LWIP_IPV6=1 make all test
+```
+
+Since lwIP uses a lot of macro magic to activate/deactivate these capabilities
+it is advisable to **test all three configurations individually** (just IPv4,
+just IPv6, IPv4/IPv6 dual stack mode).
diff --git a/tests/lwip_sock_ip/constants.h b/tests/lwip_sock_ip/constants.h
new file mode 100644
index 0000000000..7e0aef2843
--- /dev/null
+++ b/tests/lwip_sock_ip/constants.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2016 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
+ * @ingroup
+ * @brief
+ * @{
+ *
+ * @file
+ * @brief
+ *
+ * @author  Martine Lenders <m.lenders@fu-berlin.de>
+ */
+#ifndef CONSTANTS_H_
+#define CONSTANTS_H_
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define _TEST_PROTO         (254) /* https://tools.ietf.org/html/rfc3692#section-2.1 */
+#define _TEST_NETIF         (1)
+#define _TEST_TIMEOUT       (1000000U)
+#define _TEST_ADDR4_LOCAL   (0xc0a84f96U)   /* 192.168.79.150 */
+#define _TEST_ADDR4_REMOTE  (0xc0a84f6bU)   /* 192.168.79.107 */
+#define _TEST_ADDR4_WRONG   (0x254c6b4cU)
+#define _TEST_ADDR4_MASK    (0xffffff00U)   /* 255.255.255.0 */
+#define _TEST_ADDR4_GW      (0UL)           /* so we can test unreachability */
+#define _TEST_ADDR6_LOCAL   { 0x2f, 0xc4, 0x11, 0x5a, 0xe6, 0x91, 0x8d, 0x5d, \
+                              0x8c, 0xd1, 0x47, 0x07, 0xb7, 0x6f, 0x9b, 0x48 }
+#define _TEST_ADDR6_REMOTE  { 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+                              0x93, 0xcf, 0x11, 0xe1, 0x72, 0x44, 0xc5, 0x9d }
+#define _TEST_ADDR6_WRONG   { 0x2a, 0xce, 0x5d, 0x4e, 0xc8, 0xbf, 0x86, 0xf7, \
+                              0x85, 0x49, 0xb4, 0x19, 0xf2, 0x28, 0xde, 0x9b }
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CONSTANTS_H_ */
+/** @} */
diff --git a/tests/lwip_sock_ip/main.c b/tests/lwip_sock_ip/main.c
new file mode 100644
index 0000000000..8408603942
--- /dev/null
+++ b/tests/lwip_sock_ip/main.c
@@ -0,0 +1,1190 @@
+/*
+ * Copyright (C) 2016 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     examples
+ * @{
+ *
+ * @file
+ * @brief       Test for raw IP socks
+ *
+ * @author      Martine Lenders <m.lenders@fu-berlin.de>
+ * @}
+ */
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include "net/sock/ip.h"
+#include "xtimer.h"
+
+#include "constants.h"
+#include "stack.h"
+
+#define _TEST_BUFFER_SIZE   (128)
+
+#define CALL(fn)            puts("Calling " # fn); fn; tear_down()
+
+static sock_ip_t _sock, _sock2;
+static uint8_t _test_buffer[_TEST_BUFFER_SIZE];
+
+static void tear_down(void)
+{
+    sock_ip_close(&_sock);
+    memset(&_sock, 0, sizeof(_sock));
+}
+
+#ifdef MODULE_LWIP_IPV4
+static void test_sock_ip_create4__EAFNOSUPPORT(void)
+{
+    static const sock_ip_ep_t local = { .family = AF_UNSPEC };
+    static const sock_ip_ep_t remote = { .family = AF_UNSPEC };
+
+    assert(-EAFNOSUPPORT == sock_ip_create(&_sock, &local, NULL, _TEST_PROTO,
+                                           SOCK_FLAGS_REUSE_EP));
+    assert(-EAFNOSUPPORT == sock_ip_create(&_sock, NULL, &remote, _TEST_PROTO,
+                                           SOCK_FLAGS_REUSE_EP));
+}
+
+static void test_sock_ip_create4__EINVAL_addr(void)
+{
+    static const sock_ip_ep_t local = { .family = AF_INET, .netif = _TEST_NETIF };
+    static const sock_ip_ep_t remote = { .family = AF_INET,
+                                         .netif = _TEST_NETIF };
+
+    assert(-EINVAL == sock_ip_create(&_sock, &local, &remote, _TEST_PROTO,
+                                     SOCK_FLAGS_REUSE_EP));
+}
+
+static void test_sock_ip_create4__EINVAL_netif(void)
+{
+    static const sock_ip_ep_t local = { .family = AF_INET, .netif = _TEST_NETIF };
+    const sock_ip_ep_t remote = { .family = AF_INET,
+                                         .netif = (_TEST_NETIF + 1),
+                                         .addr = { .ipv4_u32 = HTONL(_TEST_ADDR4_REMOTE) } };
+
+    assert(-EINVAL == sock_ip_create(&_sock, &local, &remote, _TEST_PROTO,
+                                     SOCK_FLAGS_REUSE_EP));
+}
+
+static void test_sock_ip_create4__no_endpoints(void)
+{
+    sock_ip_ep_t ep;
+
+    assert(0 == sock_ip_create(&_sock, NULL, NULL, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+    assert(-EADDRNOTAVAIL == sock_ip_get_local(&_sock, &ep));
+    assert(-ENOTCONN == sock_ip_get_remote(&_sock, &ep));
+}
+
+static void test_sock_ip_create4__only_local(void)
+{
+    static const sock_ip_ep_t local = { .family = AF_INET };
+    sock_ip_ep_t ep;
+
+    assert(0 == sock_ip_create(&_sock, &local, NULL, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+    assert(0 == sock_ip_get_local(&_sock, &ep));
+    assert(AF_INET == ep.family);
+    assert(0 == ep.addr.ipv4_u32);
+    assert(SOCK_ADDR_ANY_NETIF == ep.netif);
+    assert(-ENOTCONN == sock_ip_get_remote(&_sock, &ep));
+}
+
+static void test_sock_ip_create4__only_local_reuse_ep(void)
+{
+    static const sock_ip_ep_t local = { .family = AF_INET };
+    sock_ip_ep_t ep, ep2;
+
+    assert(0 == sock_ip_create(&_sock, &local, NULL, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+    assert(0 == sock_ip_create(&_sock2, &local, NULL, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+    assert(0 == sock_ip_get_local(&_sock, &ep));
+    assert(0 == sock_ip_get_local(&_sock2, &ep2));
+    assert(AF_INET == ep.family);
+    assert(0 == ep.addr.ipv4_u32);
+    assert(SOCK_ADDR_ANY_NETIF == ep.netif);
+    assert(-ENOTCONN == sock_ip_get_remote(&_sock, &ep));
+    assert(AF_INET == ep2.family);
+    assert(0 == ep2.addr.ipv4_u32);
+    assert(SOCK_ADDR_ANY_NETIF == ep2.netif);
+    assert(-ENOTCONN == sock_ip_get_remote(&_sock, &ep2));
+    sock_ip_close(&_sock2);
+}
+
+static void test_sock_ip_create4__only_remote(void)
+{
+    const sock_ip_ep_t remote = { .family = AF_INET,
+                                         .addr = { .ipv4_u32 = HTONL(_TEST_ADDR4_REMOTE) } };
+    sock_ip_ep_t ep;
+
+    assert(0 == sock_ip_create(&_sock, NULL, &remote, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+    /* lwIP binds connected sock implicitly */
+    assert(0 == sock_ip_get_local(&_sock, &ep));
+    assert(0 == sock_ip_get_remote(&_sock, &ep));
+    assert(AF_INET == ep.family);
+    assert(HTONL(_TEST_ADDR4_REMOTE) == ep.addr.ipv4_u32);
+    assert(SOCK_ADDR_ANY_NETIF == ep.netif);
+}
+
+static void test_sock_ip_create4__full(void)
+{
+    static const sock_ip_ep_t local = { .family = AF_INET, .netif = _TEST_NETIF };
+    const sock_ip_ep_t remote = { .family = AF_INET,
+                                         .addr = { .ipv4_u32 = HTONL(_TEST_ADDR4_REMOTE) } };
+    sock_ip_ep_t ep;
+
+    assert(0 == sock_ip_create(&_sock, &local, &remote, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+    assert(0 == sock_ip_get_local(&_sock, &ep));
+    assert(AF_INET == ep.family);
+    /* this can't be guaranteed with lwIP */
+    /* assert(0 == ep.addr.ipv4_u32); */
+    assert(_TEST_NETIF == ep.netif);
+    assert(0 == sock_ip_get_remote(&_sock, &ep));
+    assert(AF_INET == ep.family);
+    assert(HTONL(_TEST_ADDR4_REMOTE) == ep.addr.ipv4_u32);
+    assert(SOCK_ADDR_ANY_NETIF == ep.netif);
+}
+
+static void test_sock_ip_recv4__EADDRNOTAVAIL(void)
+{
+    assert(0 == sock_ip_create(&_sock, NULL, NULL, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+
+    assert(-EADDRNOTAVAIL == sock_ip_recv(&_sock, _test_buffer,
+                                          sizeof(_test_buffer), 0, NULL));
+}
+
+static void test_sock_ip_recv4__ENOBUFS(void)
+{
+    static const sock_ip_ep_t local = { .family = AF_INET };
+
+    assert(0 == sock_ip_create(&_sock, &local, NULL, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+    assert(_inject_4packet(_TEST_ADDR4_REMOTE, _TEST_ADDR4_LOCAL, _TEST_PROTO, "ABCD",
+                           sizeof("ABCD"), _TEST_NETIF));
+    assert(-ENOBUFS == sock_ip_recv(&_sock, _test_buffer, 2, 0, NULL));
+    assert(_check_net());
+}
+
+static void test_sock_ip_recv4__EAGAIN(void)
+{
+    static const sock_ip_ep_t local = { .family = AF_INET, .netif = _TEST_NETIF };
+
+    assert(0 == sock_ip_create(&_sock, &local, NULL, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+
+    assert(-EAGAIN == sock_ip_recv(&_sock, _test_buffer, sizeof(_test_buffer),
+                                   0, NULL));
+}
+
+static void test_sock_ip_recv4__ETIMEDOUT(void)
+{
+    static const sock_ip_ep_t local = { .family = AF_INET, .netif = _TEST_NETIF };
+
+    assert(0 == sock_ip_create(&_sock, &local, NULL, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+
+    puts(" * Calling sock_ip_recv()");
+    assert(-ETIMEDOUT == sock_ip_recv(&_sock, _test_buffer,
+                                      sizeof(_test_buffer), _TEST_TIMEOUT,
+                                      NULL));
+    printf(" * (timed out with timeout %u)\n", _TEST_TIMEOUT);
+}
+
+static void test_sock_ip_recv4__socketed(void)
+{
+    static const sock_ip_ep_t local = { .family = AF_INET };
+    const sock_ip_ep_t remote = { .addr = { .ipv4_u32 = HTONL(_TEST_ADDR4_REMOTE) },
+                                         .family = AF_INET };
+
+    assert(0 == sock_ip_create(&_sock, &local, &remote, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+    assert(_inject_4packet(_TEST_ADDR4_REMOTE, _TEST_ADDR4_LOCAL, _TEST_PROTO, "ABCD",
+                           sizeof("ABCD"), _TEST_NETIF));
+    assert(sizeof("ABCD") == sock_ip_recv(&_sock, _test_buffer,
+                                          sizeof(_test_buffer), 0, NULL));
+    assert(_check_net());
+}
+
+static void test_sock_ip_recv4__socketed_with_remote(void)
+{
+    static const sock_ip_ep_t local = { .family = AF_INET };
+    const sock_ip_ep_t remote = { .addr = { .ipv4_u32 = HTONL(_TEST_ADDR4_REMOTE) },
+                                         .family = AF_INET };
+    sock_ip_ep_t result;
+
+    assert(0 == sock_ip_create(&_sock, &local, &remote, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+    assert(_inject_4packet(_TEST_ADDR4_REMOTE, _TEST_ADDR4_LOCAL, _TEST_PROTO, "ABCD",
+                           sizeof("ABCD"), _TEST_NETIF));
+    assert(sizeof("ABCD") == sock_ip_recv(&_sock, _test_buffer,
+                                          sizeof(_test_buffer), 0, &result));
+    assert(AF_INET == result.family);
+    assert(HTONL(_TEST_ADDR4_REMOTE) == result.addr.ipv4_u32);
+    assert(_TEST_NETIF == result.netif);
+    assert(_check_net());
+}
+
+static void test_sock_ip_recv4__unsocketed(void)
+{
+    const sock_ip_ep_t local = { .addr = { .ipv4_u32 = HTONL(_TEST_ADDR4_LOCAL) },
+                                        .family = AF_INET };
+
+    assert(0 == sock_ip_create(&_sock, &local, NULL, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+    assert(_inject_4packet(_TEST_ADDR4_REMOTE, _TEST_ADDR4_LOCAL, _TEST_PROTO, "ABCD",
+                           sizeof("ABCD"), _TEST_NETIF));
+    assert(sizeof("ABCD") == sock_ip_recv(&_sock, _test_buffer,
+                                          sizeof(_test_buffer), 0, NULL));
+    assert(_check_net());
+}
+
+static void test_sock_ip_recv4__unsocketed_with_remote(void)
+{
+    static const sock_ip_ep_t local = { .family = AF_INET };
+    sock_ip_ep_t result;
+
+    assert(0 == sock_ip_create(&_sock, &local, NULL, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+    assert(_inject_4packet(_TEST_ADDR4_REMOTE, _TEST_ADDR4_LOCAL, _TEST_PROTO, "ABCD",
+                           sizeof("ABCD"), _TEST_NETIF));
+    assert(sizeof("ABCD") == sock_ip_recv(&_sock, _test_buffer,
+                                          sizeof(_test_buffer), 0, &result));
+    assert(AF_INET == result.family);
+    assert(HTONL(_TEST_ADDR4_REMOTE) == result.addr.ipv4_u32);
+    assert(_TEST_NETIF == result.netif);
+    assert(_check_net());
+}
+
+static void test_sock_ip_recv4__with_timeout(void)
+{
+    static const sock_ip_ep_t local = { .family = AF_INET };
+    sock_ip_ep_t result;
+
+    assert(0 == sock_ip_create(&_sock, &local, NULL, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+    assert(_inject_4packet(_TEST_ADDR4_REMOTE, _TEST_ADDR4_LOCAL, _TEST_PROTO, "ABCD",
+                           sizeof("ABCD"), _TEST_NETIF));
+    assert(sizeof("ABCD") == sock_ip_recv(&_sock, _test_buffer,
+                                          sizeof(_test_buffer), _TEST_TIMEOUT,
+                                          &result));
+    assert(AF_INET == result.family);
+    assert(HTONL(_TEST_ADDR4_REMOTE) == result.addr.ipv4_u32);
+    assert(_TEST_NETIF == result.netif);
+    assert(_check_net());
+}
+
+static void test_sock_ip_recv4__non_blocking(void)
+{
+    static const sock_ip_ep_t local = { .family = AF_INET };
+    sock_ip_ep_t result;
+
+    assert(0 == sock_ip_create(&_sock, &local, NULL, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+    assert(_inject_4packet(_TEST_ADDR4_REMOTE, _TEST_ADDR4_LOCAL, _TEST_PROTO, "ABCD",
+                           sizeof("ABCD"), _TEST_NETIF));
+    assert(sizeof("ABCD") == sock_ip_recv(&_sock, _test_buffer,
+                                          sizeof(_test_buffer), 0, &result));
+    assert(AF_INET == result.family);
+    assert(HTONL(_TEST_ADDR4_REMOTE) == result.addr.ipv4_u32);
+    assert(_TEST_NETIF == result.netif);
+    assert(_check_net());
+}
+
+static void test_sock_ip_send4__EAFNOSUPPORT(void)
+{
+    const sock_ip_ep_t remote = { .addr = { .ipv4_u32 = HTONL(_TEST_ADDR4_REMOTE) },
+                                         .family = AF_UNSPEC };
+
+    assert(-EAFNOSUPPORT == sock_ip_send(NULL, "ABCD", sizeof("ABCD"),
+                                         _TEST_PROTO, &remote));
+    assert(_check_net());
+}
+
+static void test_sock_ip_send4__EINVAL_addr(void)
+{
+    const sock_ip_ep_t local = { .addr = { .ipv4_u32 = HTONL(_TEST_ADDR4_LOCAL) },
+                                        .family = AF_INET,
+                                        .netif = _TEST_NETIF };
+    static const sock_ip_ep_t remote = { .family = AF_INET,
+                                         .netif = _TEST_NETIF };
+
+    assert(0 == sock_ip_create(&_sock, &local, NULL, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+    assert(-EINVAL == sock_ip_send(&_sock, "ABCD", sizeof("ABCD"), _TEST_PROTO,
+                                   &remote));
+    assert(_check_net());
+}
+
+static void test_sock_ip_send4__EINVAL_netif(void)
+{
+    const sock_ip_ep_t local = { .addr = { .ipv4_u32 = HTONL(_TEST_ADDR4_LOCAL) },
+                                        .family = AF_INET,
+                                        .netif = _TEST_NETIF };
+    const sock_ip_ep_t remote = { .addr = { .ipv4_u32 = HTONL(_TEST_ADDR4_REMOTE) },
+                                         .family = AF_INET,
+                                         .netif = _TEST_NETIF + 1 };
+
+    assert(0 == sock_ip_create(&_sock, &local, NULL, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+    assert(-EINVAL == sock_ip_send(&_sock, "ABCD", sizeof("ABCD"), _TEST_PROTO,
+                                   &remote));
+    assert(_check_net());
+}
+
+static void test_sock_ip_send4__EHOSTUNREACH(void)
+{
+    const sock_ip_ep_t remote = { .addr = { .ipv4_u32 = HTONL(_TEST_ADDR4_WRONG) },
+                                         .family = AF_INET };
+
+    assert(-EHOSTUNREACH == sock_ip_send(NULL, "ABCD", sizeof("ABCD"), _TEST_PROTO,
+                                         &remote));
+}
+
+static void test_sock_ip_send4__ENOTCONN(void)
+{
+    assert(0 == sock_ip_create(&_sock, NULL, NULL, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+    assert(-ENOTCONN == sock_ip_send(&_sock, "ABCD", sizeof("ABCD"),
+                                     _TEST_PROTO, NULL));
+    assert(_check_net());
+}
+
+static void test_sock_ip_send4__socketed_no_local_no_netif(void)
+{
+    const sock_ip_ep_t remote = { .addr = { .ipv4_u32 = HTONL(_TEST_ADDR4_REMOTE) },
+                                         .family = AF_INET };
+
+    assert(0 == sock_ip_create(&_sock, NULL, &remote, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+    assert(sizeof("ABCD") == sock_ip_send(&_sock, "ABCD", sizeof("ABCD"),
+                                          _TEST_PROTO, NULL));
+    assert(_check_4packet(0, _TEST_ADDR4_REMOTE, _TEST_PROTO, "ABCD",
+                          sizeof("ABCD"), SOCK_ADDR_ANY_NETIF));
+    xtimer_usleep(1000);    /* let lwIP stack finish */
+    assert(_check_net());
+}
+
+static void test_sock_ip_send4__socketed_no_netif(void)
+{
+    const sock_ip_ep_t local = { .addr = { .ipv4_u32 = HTONL(_TEST_ADDR4_LOCAL) },
+                                        .family = AF_INET };
+    const sock_ip_ep_t remote = { .addr = { .ipv4_u32 = HTONL(_TEST_ADDR4_REMOTE) },
+                                         .family = AF_INET };
+
+    assert(0 == sock_ip_create(&_sock, &local, &remote, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+    assert(sizeof("ABCD") == sock_ip_send(&_sock, "ABCD", sizeof("ABCD"),
+                                          _TEST_PROTO, NULL));
+    assert(_check_4packet(_TEST_ADDR4_LOCAL, _TEST_ADDR4_REMOTE, _TEST_PROTO, "ABCD",
+                          sizeof("ABCD"), SOCK_ADDR_ANY_NETIF));
+    xtimer_usleep(1000);    /* let lwIP stack finish */
+    assert(_check_net());
+}
+
+static void test_sock_ip_send4__socketed_no_local(void)
+{
+    const sock_ip_ep_t remote = { .addr = { .ipv4_u32 = HTONL(_TEST_ADDR4_REMOTE) },
+                                         .family = AF_INET,
+                                         .netif = _TEST_NETIF };
+
+    assert(0 == sock_ip_create(&_sock, NULL, &remote, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+    assert(sizeof("ABCD") == sock_ip_send(&_sock, "ABCD", sizeof("ABCD"),
+                                          _TEST_PROTO, NULL));
+    assert(_check_4packet(0, _TEST_ADDR4_REMOTE, _TEST_PROTO, "ABCD",
+                          sizeof("ABCD"), _TEST_NETIF));
+    xtimer_usleep(1000);    /* let lwIP stack finish */
+    assert(_check_net());
+}
+
+static void test_sock_ip_send4__socketed(void)
+{
+    const sock_ip_ep_t local = { .addr = { .ipv4_u32 = HTONL(_TEST_ADDR4_LOCAL) },
+                                        .family = AF_INET,
+                                        .netif = _TEST_NETIF };
+    const sock_ip_ep_t remote = { .addr = { .ipv4_u32 = HTONL(_TEST_ADDR4_REMOTE) },
+                                         .family = AF_INET };
+
+    assert(0 == sock_ip_create(&_sock, &local, &remote, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+    assert(sizeof("ABCD") == sock_ip_send(&_sock, "ABCD", sizeof("ABCD"),
+                                          _TEST_PROTO, NULL));
+    assert(_check_4packet(_TEST_ADDR4_LOCAL, _TEST_ADDR4_REMOTE, _TEST_PROTO, "ABCD",
+                          sizeof("ABCD"), _TEST_NETIF));
+    xtimer_usleep(1000);    /* let lwIP stack finish */
+    assert(_check_net());
+}
+
+static void test_sock_ip_send4__socketed_other_remote(void)
+{
+    const sock_ip_ep_t local = { .addr = { .ipv4_u32 = HTONL(_TEST_ADDR4_LOCAL) },
+                                        .family = AF_INET,
+                                        .netif = _TEST_NETIF };
+    const sock_ip_ep_t sock_remote = { .addr = { .ipv4_u32 = HTONL(_TEST_ADDR4_WRONG) },
+                                              .family = AF_INET };
+    const sock_ip_ep_t remote = { .addr = { .ipv4_u32 = HTONL(_TEST_ADDR4_REMOTE) },
+                                         .family = AF_INET };
+
+    assert(0 == sock_ip_create(&_sock, &local, &sock_remote, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+    assert(sizeof("ABCD") == sock_ip_send(&_sock, "ABCD", sizeof("ABCD"),
+                                          _TEST_PROTO, &remote));
+    assert(_check_4packet(_TEST_ADDR4_LOCAL, _TEST_ADDR4_REMOTE, _TEST_PROTO, "ABCD",
+                          sizeof("ABCD"), _TEST_NETIF));
+    xtimer_usleep(1000);    /* let lwIP stack finish */
+    assert(_check_net());
+}
+
+static void test_sock_ip_send4__unsocketed_no_local_no_netif(void)
+{
+    const sock_ip_ep_t remote = { .addr = { .ipv4_u32 = HTONL(_TEST_ADDR4_REMOTE) },
+                                         .family = AF_INET };
+
+    assert(0 == sock_ip_create(&_sock, NULL, NULL, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+    assert(sizeof("ABCD") == sock_ip_send(&_sock, "ABCD", sizeof("ABCD"),
+                                          _TEST_PROTO, &remote));
+    assert(_check_4packet(0, _TEST_ADDR4_REMOTE, _TEST_PROTO, "ABCD",
+                          sizeof("ABCD"), SOCK_ADDR_ANY_NETIF));
+    xtimer_usleep(1000);    /* let lwIP stack finish */
+    assert(_check_net());
+}
+
+static void test_sock_ip_send4__unsocketed_no_netif(void)
+{
+    const sock_ip_ep_t local = { .addr = { .ipv4_u32 = HTONL(_TEST_ADDR4_LOCAL) },
+                                        .family = AF_INET };
+    const sock_ip_ep_t remote = { .addr = { .ipv4_u32 = HTONL(_TEST_ADDR4_REMOTE) },
+                                         .family = AF_INET };
+
+    assert(0 == sock_ip_create(&_sock, &local, NULL, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+    assert(sizeof("ABCD") == sock_ip_send(&_sock, "ABCD", sizeof("ABCD"),
+                                          _TEST_PROTO, &remote));
+    assert(_check_4packet(_TEST_ADDR4_LOCAL, _TEST_ADDR4_REMOTE, _TEST_PROTO, "ABCD",
+                          sizeof("ABCD"), SOCK_ADDR_ANY_NETIF));
+    xtimer_usleep(1000);    /* let lwIP stack finish */
+    assert(_check_net());
+}
+
+static void test_sock_ip_send4__unsocketed_no_local(void)
+{
+    const sock_ip_ep_t remote = { .addr = { .ipv4_u32 = HTONL(_TEST_ADDR4_REMOTE) },
+                                         .family = AF_INET,
+                                         .netif = _TEST_NETIF };
+
+    assert(0 == sock_ip_create(&_sock, NULL, NULL, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+    assert(sizeof("ABCD") == sock_ip_send(&_sock, "ABCD", sizeof("ABCD"),
+                                          _TEST_PROTO, &remote));
+    assert(_check_4packet(0, _TEST_ADDR4_REMOTE, _TEST_PROTO, "ABCD",
+                          sizeof("ABCD"), _TEST_NETIF));
+    xtimer_usleep(1000);    /* let lwIP stack finish */
+    assert(_check_net());
+}
+
+static void test_sock_ip_send4__unsocketed(void)
+{
+    const sock_ip_ep_t local = { .addr = { .ipv4_u32 = HTONL(_TEST_ADDR4_LOCAL) },
+                                        .family = AF_INET,
+                                        .netif = _TEST_NETIF };
+    const sock_ip_ep_t remote = { .addr = { .ipv4_u32 = HTONL(_TEST_ADDR4_REMOTE) },
+                                         .family = AF_INET };
+
+    assert(0 == sock_ip_create(&_sock, &local, NULL, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+    assert(sizeof("ABCD") == sock_ip_send(&_sock, "ABCD", sizeof("ABCD"),
+                                          _TEST_PROTO, &remote));
+    assert(_check_4packet(_TEST_ADDR4_LOCAL, _TEST_ADDR4_REMOTE, _TEST_PROTO, "ABCD",
+                          sizeof("ABCD"), _TEST_NETIF));
+    xtimer_usleep(1000);    /* let lwIP stack finish */
+    assert(_check_net());
+}
+
+static void test_sock_ip_send4__no_sock_no_netif(void)
+{
+    const sock_ip_ep_t remote = { .addr = { .ipv4_u32 = HTONL(_TEST_ADDR4_REMOTE) },
+                                         .family = AF_INET };
+
+    assert(sizeof("ABCD") == sock_ip_send(NULL, "ABCD", sizeof("ABCD"),
+                                          _TEST_PROTO, &remote));
+    assert(_check_4packet(0, _TEST_ADDR4_REMOTE, _TEST_PROTO, "ABCD",
+                          sizeof("ABCD"), SOCK_ADDR_ANY_NETIF));
+    xtimer_usleep(1000);    /* let lwIP stack finish */
+    assert(_check_net());
+}
+
+static void test_sock_ip_send4__no_sock(void)
+{
+    const sock_ip_ep_t remote = { .addr = { .ipv4_u32 = HTONL(_TEST_ADDR4_REMOTE) },
+                                         .family = AF_INET,
+                                         .netif = _TEST_NETIF };
+
+    assert(sizeof("ABCD") == sock_ip_send(NULL, "ABCD", sizeof("ABCD"),
+                                          _TEST_PROTO, &remote));
+    assert(_check_4packet(0, _TEST_ADDR4_REMOTE, _TEST_PROTO, "ABCD",
+                          sizeof("ABCD"), _TEST_NETIF));
+    xtimer_usleep(1000);    /* let lwIP stack finish */
+    assert(_check_net());
+}
+#endif  /* MODULE_LWIP_IPV4 */
+
+#ifdef MODULE_LWIP_IPV6
+static void test_sock_ip_create6__EAFNOSUPPORT(void)
+{
+    static const sock_ip_ep_t local = { .family = AF_UNSPEC };
+    static const sock_ip_ep_t remote = { .family = AF_UNSPEC };
+
+    assert(-EAFNOSUPPORT == sock_ip_create(&_sock, &local, NULL, _TEST_PROTO,
+                                           SOCK_FLAGS_REUSE_EP));
+    assert(-EAFNOSUPPORT == sock_ip_create(&_sock, NULL, &remote, _TEST_PROTO,
+                                           SOCK_FLAGS_REUSE_EP));
+}
+
+static void test_sock_ip_create6__EINVAL_addr(void)
+{
+    static const sock_ip_ep_t local = { .family = AF_INET6, .netif = _TEST_NETIF };
+    static const sock_ip_ep_t remote = { .family = AF_INET6,
+                                         .netif = _TEST_NETIF };
+
+    assert(-EINVAL == sock_ip_create(&_sock, &local, &remote, _TEST_PROTO,
+                                     SOCK_FLAGS_REUSE_EP));
+}
+
+static void test_sock_ip_create6__EINVAL_netif(void)
+{
+    static const sock_ip_ep_t local = { .family = AF_INET6, .netif = _TEST_NETIF };
+    static const sock_ip_ep_t remote = { .family = AF_INET6,
+                                         .netif = (_TEST_NETIF + 1),
+                                         .addr = { .ipv6 = _TEST_ADDR6_REMOTE } };
+
+    assert(-EINVAL == sock_ip_create(&_sock, &local, &remote, _TEST_PROTO,
+                                     SOCK_FLAGS_REUSE_EP));
+}
+
+static void test_sock_ip_create6__no_endpoints(void)
+{
+    sock_ip_ep_t ep;
+
+    assert(0 == sock_ip_create(&_sock, NULL, NULL, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+    assert(-EADDRNOTAVAIL == sock_ip_get_local(&_sock, &ep));
+    assert(-ENOTCONN == sock_ip_get_remote(&_sock, &ep));
+}
+
+static void test_sock_ip_create6__only_local(void)
+{
+    static const sock_ip_ep_t local = { .family = AF_INET6 };
+    sock_ip_ep_t ep;
+
+    assert(0 == sock_ip_create(&_sock, &local, NULL, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+    assert(0 == sock_ip_get_local(&_sock, &ep));
+    assert(AF_INET6 == ep.family);
+    assert(memcmp(&ipv6_addr_unspecified, &ep.addr.ipv6,
+                  sizeof(ipv6_addr_t)) == 0);
+    assert(SOCK_ADDR_ANY_NETIF == ep.netif);
+    assert(-ENOTCONN == sock_ip_get_remote(&_sock, &ep));
+}
+
+static void test_sock_ip_create6__only_local_reuse_ep(void)
+{
+    static const sock_ip_ep_t local = { .family = AF_INET6 };
+    sock_ip_ep_t ep, ep2;
+
+    assert(0 == sock_ip_create(&_sock, &local, NULL, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+    assert(0 == sock_ip_create(&_sock2, &local, NULL, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+    assert(0 == sock_ip_get_local(&_sock, &ep));
+    assert(0 == sock_ip_get_local(&_sock2, &ep2));
+    assert(AF_INET6 == ep.family);
+    assert(memcmp(&ipv6_addr_unspecified, &ep.addr.ipv6,
+                  sizeof(ipv6_addr_t)) == 0);
+    assert(SOCK_ADDR_ANY_NETIF == ep.netif);
+    assert(-ENOTCONN == sock_ip_get_remote(&_sock, &ep));
+    assert(AF_INET6 == ep2.family);
+    assert(memcmp(&ipv6_addr_unspecified, &ep2.addr.ipv6,
+                  sizeof(ipv6_addr_t)) == 0);
+    assert(SOCK_ADDR_ANY_NETIF == ep2.netif);
+    assert(-ENOTCONN == sock_ip_get_remote(&_sock, &ep2));
+    sock_ip_close(&_sock2);
+}
+
+static void test_sock_ip_create6__only_remote(void)
+{
+    static const ipv6_addr_t remote_addr = { .u8 = _TEST_ADDR6_REMOTE };
+    static const sock_ip_ep_t remote = { .family = AF_INET6,
+                                         .addr = { .ipv6 = _TEST_ADDR6_REMOTE } };
+    sock_ip_ep_t ep;
+
+    assert(0 == sock_ip_create(&_sock, NULL, &remote, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+    /* lwIP binds connected sock implicitly */
+    assert(0 == sock_ip_get_local(&_sock, &ep));
+    assert(0 == sock_ip_get_remote(&_sock, &ep));
+    assert(AF_INET6 == ep.family);
+    assert(memcmp(&remote_addr, &ep.addr.ipv6, sizeof(ipv6_addr_t)) == 0);
+    assert(SOCK_ADDR_ANY_NETIF == ep.netif);
+}
+
+static void test_sock_ip_create6__full(void)
+{
+    static const ipv6_addr_t remote_addr = { .u8 = _TEST_ADDR6_REMOTE };
+    static const sock_ip_ep_t local = { .family = AF_INET6, .netif = _TEST_NETIF };
+    static const sock_ip_ep_t remote = { .family = AF_INET6,
+                                         .addr = { .ipv6 = _TEST_ADDR6_REMOTE } };
+    sock_ip_ep_t ep;
+
+    assert(0 == sock_ip_create(&_sock, &local, &remote, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+    assert(0 == sock_ip_get_local(&_sock, &ep));
+    assert(AF_INET6 == ep.family);
+    /* this can't be guaranteed with lwIP */
+    /* assert(memcmp(&ipv6_addr_unspecified, &ep.addr.ipv6, */
+    /*               sizeof(ipv6_addr_t)) == 0); */
+    assert(_TEST_NETIF == ep.netif);
+    assert(0 == sock_ip_get_remote(&_sock, &ep));
+    assert(AF_INET6 == ep.family);
+    assert(memcmp(&remote_addr, &ep.addr.ipv6, sizeof(ipv6_addr_t)) == 0);
+    assert(SOCK_ADDR_ANY_NETIF == ep.netif);
+}
+
+static void test_sock_ip_recv6__EADDRNOTAVAIL(void)
+{
+    assert(0 == sock_ip_create(&_sock, NULL, NULL, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+
+    assert(-EADDRNOTAVAIL == sock_ip_recv(&_sock, _test_buffer,
+                                          sizeof(_test_buffer), 0, NULL));
+}
+
+static void test_sock_ip_recv6__EAGAIN(void)
+{
+    static const sock_ip_ep_t local = { .family = AF_INET6, .netif = _TEST_NETIF };
+
+    assert(0 == sock_ip_create(&_sock, &local, NULL, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+
+    assert(-EAGAIN == sock_ip_recv(&_sock, _test_buffer, sizeof(_test_buffer),
+                                   0, NULL));
+}
+
+static void test_sock_ip_recv6__ENOBUFS(void)
+{
+    static const ipv6_addr_t src_addr = { .u8 = _TEST_ADDR6_REMOTE };
+    static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR6_LOCAL };
+    static const sock_ip_ep_t local = { .family = AF_INET6 };
+
+    assert(0 == sock_ip_create(&_sock, &local, NULL, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+    assert(_inject_6packet(&src_addr, &dst_addr, _TEST_PROTO, "ABCD",
+                           sizeof("ABCD"), _TEST_NETIF));
+    assert(-ENOBUFS == sock_ip_recv(&_sock, _test_buffer, 2, 0, NULL));
+    assert(_check_net());
+}
+
+static void test_sock_ip_recv6__ETIMEDOUT(void)
+{
+    static const sock_ip_ep_t local = { .family = AF_INET6, .netif = _TEST_NETIF };
+
+    assert(0 == sock_ip_create(&_sock, &local, NULL, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+
+    puts(" * Calling sock_ip_recv()");
+    assert(-ETIMEDOUT == sock_ip_recv(&_sock, _test_buffer,
+                                      sizeof(_test_buffer), _TEST_TIMEOUT,
+                                      NULL));
+    printf(" * (timed out with timeout %u)\n", _TEST_TIMEOUT);
+}
+
+static void test_sock_ip_recv6__socketed(void)
+{
+    static const ipv6_addr_t src_addr = { .u8 = _TEST_ADDR6_REMOTE };
+    static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR6_LOCAL };
+    static const sock_ip_ep_t local = { .family = AF_INET6 };
+    static const sock_ip_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR6_REMOTE },
+                                         .family = AF_INET6 };
+
+    assert(0 == sock_ip_create(&_sock, &local, &remote, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+    assert(_inject_6packet(&src_addr, &dst_addr, _TEST_PROTO, "ABCD",
+                           sizeof("ABCD"), _TEST_NETIF));
+    assert(sizeof("ABCD") == sock_ip_recv(&_sock, _test_buffer,
+                                          sizeof(_test_buffer), 0, NULL));
+    assert(_check_net());
+}
+
+static void test_sock_ip_recv6__socketed_with_remote(void)
+{
+    static const ipv6_addr_t src_addr = { .u8 = _TEST_ADDR6_REMOTE };
+    static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR6_LOCAL };
+    static const sock_ip_ep_t local = { .family = AF_INET6 };
+    static const sock_ip_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR6_REMOTE },
+                                         .family = AF_INET6 };
+    sock_ip_ep_t result;
+
+    assert(0 == sock_ip_create(&_sock, &local, &remote, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+    assert(_inject_6packet(&src_addr, &dst_addr, _TEST_PROTO, "ABCD",
+                           sizeof("ABCD"), _TEST_NETIF));
+    assert(sizeof("ABCD") == sock_ip_recv(&_sock, _test_buffer,
+                                          sizeof(_test_buffer), 0, &result));
+    assert(AF_INET6 == result.family);
+    assert(memcmp(&result.addr, &src_addr, sizeof(result.addr)) == 0);
+    assert(_TEST_NETIF == result.netif);
+    assert(_check_net());
+}
+
+static void test_sock_ip_recv6__unsocketed(void)
+{
+    static const ipv6_addr_t src_addr = { .u8 = _TEST_ADDR6_REMOTE };
+    static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR6_LOCAL };
+    static const sock_ip_ep_t local = { .addr = { .ipv6 = _TEST_ADDR6_LOCAL },
+                                        .family = AF_INET6 };
+
+    assert(0 == sock_ip_create(&_sock, &local, NULL, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+    assert(_inject_6packet(&src_addr, &dst_addr, _TEST_PROTO, "ABCD",
+                           sizeof("ABCD"), _TEST_NETIF));
+    assert(sizeof("ABCD") == sock_ip_recv(&_sock, _test_buffer,
+                                          sizeof(_test_buffer), 0, NULL));
+    assert(_check_net());
+}
+
+static void test_sock_ip_recv6__unsocketed_with_remote(void)
+{
+    static const ipv6_addr_t src_addr = { .u8 = _TEST_ADDR6_REMOTE };
+    static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR6_LOCAL };
+    static const sock_ip_ep_t local = { .family = AF_INET6 };
+    sock_ip_ep_t result;
+
+    assert(0 == sock_ip_create(&_sock, &local, NULL, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+    assert(_inject_6packet(&src_addr, &dst_addr, _TEST_PROTO, "ABCD",
+                           sizeof("ABCD"), _TEST_NETIF));
+    assert(sizeof("ABCD") == sock_ip_recv(&_sock, _test_buffer,
+                                          sizeof(_test_buffer), 0, &result));
+    assert(AF_INET6 == result.family);
+    assert(memcmp(&result.addr, &src_addr, sizeof(result.addr)) == 0);
+    assert(_TEST_NETIF == result.netif);
+    assert(_check_net());
+}
+
+static void test_sock_ip_recv6__with_timeout(void)
+{
+    static const ipv6_addr_t src_addr = { .u8 = _TEST_ADDR6_REMOTE };
+    static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR6_LOCAL };
+    static const sock_ip_ep_t local = { .family = AF_INET6 };
+    sock_ip_ep_t result;
+
+    assert(0 == sock_ip_create(&_sock, &local, NULL, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+    assert(_inject_6packet(&src_addr, &dst_addr, _TEST_PROTO, "ABCD",
+                           sizeof("ABCD"), _TEST_NETIF));
+    assert(sizeof("ABCD") == sock_ip_recv(&_sock, _test_buffer,
+                                          sizeof(_test_buffer), _TEST_TIMEOUT,
+                                          &result));
+    assert(AF_INET6 == result.family);
+    assert(memcmp(&result.addr, &src_addr, sizeof(result.addr)) == 0);
+    assert(_TEST_NETIF == result.netif);
+    assert(_check_net());
+}
+
+static void test_sock_ip_recv6__non_blocking(void)
+{
+    static const ipv6_addr_t src_addr = { .u8 = _TEST_ADDR6_REMOTE };
+    static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR6_LOCAL };
+    static const sock_ip_ep_t local = { .family = AF_INET6 };
+    sock_ip_ep_t result;
+
+    assert(0 == sock_ip_create(&_sock, &local, NULL, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+    assert(_inject_6packet(&src_addr, &dst_addr, _TEST_PROTO, "ABCD",
+                           sizeof("ABCD"), _TEST_NETIF));
+    assert(sizeof("ABCD") == sock_ip_recv(&_sock, _test_buffer,
+                                          sizeof(_test_buffer), 0, &result));
+    assert(AF_INET6 == result.family);
+    assert(memcmp(&result.addr, &src_addr, sizeof(result.addr)) == 0);
+    assert(_TEST_NETIF == result.netif);
+    assert(_check_net());
+}
+
+static void test_sock_ip_send6__EAFNOSUPPORT(void)
+{
+    static const sock_ip_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR6_REMOTE },
+                                         .family = AF_UNSPEC };
+
+    assert(-EAFNOSUPPORT == sock_ip_send(NULL, "ABCD", sizeof("ABCD"),
+                                         _TEST_PROTO, &remote));
+    assert(_check_net());
+}
+
+static void test_sock_ip_send6__EINVAL_addr(void)
+{
+    static const sock_ip_ep_t local = { .addr = { .ipv6 = _TEST_ADDR6_LOCAL },
+                                        .family = AF_INET6,
+                                        .netif = _TEST_NETIF };
+    static const sock_ip_ep_t remote = { .family = AF_INET6,
+                                         .netif = _TEST_NETIF };
+
+    assert(0 == sock_ip_create(&_sock, &local, NULL, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+    assert(-EINVAL == sock_ip_send(&_sock, "ABCD", sizeof("ABCD"), _TEST_PROTO,
+                                   &remote));
+    assert(_check_net());
+}
+
+static void test_sock_ip_send6__EINVAL_netif(void)
+{
+    static const sock_ip_ep_t local = { .addr = { .ipv6 = _TEST_ADDR6_LOCAL },
+                                        .family = AF_INET6,
+                                        .netif = _TEST_NETIF };
+    static const sock_ip_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR6_REMOTE },
+                                         .family = AF_INET6,
+                                         .netif = _TEST_NETIF + 1 };
+
+    assert(0 == sock_ip_create(&_sock, &local, NULL, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+    assert(-EINVAL == sock_ip_send(&_sock, "ABCD", sizeof("ABCD"), _TEST_PROTO,
+                                   &remote));
+    assert(_check_net());
+}
+
+static void test_sock_ip_send6__EHOSTUNREACH(void)
+{
+    static const sock_ip_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR6_WRONG },
+                                         .family = AF_INET6 };
+
+    /* lwIP returns ENOMEM on failed neighbor cache lookup, since it "tries" to
+     * create one so we have to live with this weird behavior */
+    assert(-ENOMEM == sock_ip_send(NULL, "ABCD", sizeof("ABCD"), _TEST_PROTO,
+                                   &remote));
+}
+
+static void test_sock_ip_send6__ENOTCONN(void)
+{
+    assert(0 == sock_ip_create(&_sock, NULL, NULL, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+    assert(-ENOTCONN == sock_ip_send(&_sock, "ABCD", sizeof("ABCD"),
+                                     _TEST_PROTO, NULL));
+    assert(_check_net());
+}
+
+static void test_sock_ip_send6__socketed_no_local_no_netif(void)
+{
+    static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR6_REMOTE };
+    static const sock_ip_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR6_REMOTE },
+                                         .family = AF_INET6 };
+
+    assert(0 == sock_ip_create(&_sock, NULL, &remote, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+    assert(sizeof("ABCD") == sock_ip_send(&_sock, "ABCD", sizeof("ABCD"),
+                                          _TEST_PROTO, NULL));
+    assert(_check_6packet(&ipv6_addr_unspecified, &dst_addr, _TEST_PROTO, "ABCD",
+                          sizeof("ABCD"), SOCK_ADDR_ANY_NETIF));
+    xtimer_usleep(1000);    /* let lwIP stack finish */
+    assert(_check_net());
+}
+
+static void test_sock_ip_send6__socketed_no_netif(void)
+{
+    static const ipv6_addr_t src_addr = { .u8 = _TEST_ADDR6_LOCAL };
+    static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR6_REMOTE };
+    static const sock_ip_ep_t local = { .addr = { .ipv6 = _TEST_ADDR6_LOCAL },
+                                        .family = AF_INET6 };
+    static const sock_ip_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR6_REMOTE },
+                                         .family = AF_INET6 };
+
+    assert(0 == sock_ip_create(&_sock, &local, &remote, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+    assert(sizeof("ABCD") == sock_ip_send(&_sock, "ABCD", sizeof("ABCD"),
+                                          _TEST_PROTO, NULL));
+    assert(_check_6packet(&src_addr, &dst_addr, _TEST_PROTO, "ABCD",
+                          sizeof("ABCD"), SOCK_ADDR_ANY_NETIF));
+    xtimer_usleep(1000);    /* let lwIP stack finish */
+    assert(_check_net());
+}
+
+static void test_sock_ip_send6__socketed_no_local(void)
+{
+    static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR6_REMOTE };
+    static const sock_ip_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR6_REMOTE },
+                                         .family = AF_INET6,
+                                         .netif = _TEST_NETIF };
+
+    assert(0 == sock_ip_create(&_sock, NULL, &remote, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+    assert(sizeof("ABCD") == sock_ip_send(&_sock, "ABCD", sizeof("ABCD"),
+                                          _TEST_PROTO, NULL));
+    assert(_check_6packet(&ipv6_addr_unspecified, &dst_addr, _TEST_PROTO, "ABCD",
+                          sizeof("ABCD"), _TEST_NETIF));
+    xtimer_usleep(1000);    /* let lwIP stack finish */
+    assert(_check_net());
+}
+
+static void test_sock_ip_send6__socketed(void)
+{
+    static const ipv6_addr_t src_addr = { .u8 = _TEST_ADDR6_LOCAL };
+    static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR6_REMOTE };
+    static const sock_ip_ep_t local = { .addr = { .ipv6 = _TEST_ADDR6_LOCAL },
+                                        .family = AF_INET6,
+                                        .netif = _TEST_NETIF };
+    static const sock_ip_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR6_REMOTE },
+                                         .family = AF_INET6 };
+
+    assert(0 == sock_ip_create(&_sock, &local, &remote, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+    assert(sizeof("ABCD") == sock_ip_send(&_sock, "ABCD", sizeof("ABCD"),
+                                          _TEST_PROTO, NULL));
+    assert(_check_6packet(&src_addr, &dst_addr, _TEST_PROTO, "ABCD",
+                          sizeof("ABCD"), _TEST_NETIF));
+    xtimer_usleep(1000);    /* let lwIP stack finish */
+    assert(_check_net());
+}
+
+static void test_sock_ip_send6__socketed_other_remote(void)
+{
+    static const ipv6_addr_t src_addr = { .u8 = _TEST_ADDR6_LOCAL };
+    static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR6_REMOTE };
+    static const sock_ip_ep_t local = { .addr = { .ipv6 = _TEST_ADDR6_LOCAL },
+                                        .family = AF_INET6,
+                                        .netif = _TEST_NETIF };
+    static const sock_ip_ep_t sock_remote = { .addr = { .ipv6 = _TEST_ADDR6_WRONG },
+                                              .family = AF_INET6 };
+    static const sock_ip_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR6_REMOTE },
+                                         .family = AF_INET6 };
+
+    assert(0 == sock_ip_create(&_sock, &local, &sock_remote, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+    assert(sizeof("ABCD") == sock_ip_send(&_sock, "ABCD", sizeof("ABCD"),
+                                          _TEST_PROTO, &remote));
+    assert(_check_6packet(&src_addr, &dst_addr, _TEST_PROTO, "ABCD",
+                          sizeof("ABCD"), _TEST_NETIF));
+    xtimer_usleep(1000);    /* let lwIP stack finish */
+    assert(_check_net());
+}
+
+static void test_sock_ip_send6__unsocketed_no_local_no_netif(void)
+{
+    static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR6_REMOTE };
+    static const sock_ip_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR6_REMOTE },
+                                         .family = AF_INET6 };
+
+    assert(0 == sock_ip_create(&_sock, NULL, NULL, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+    assert(sizeof("ABCD") == sock_ip_send(&_sock, "ABCD", sizeof("ABCD"),
+                                          _TEST_PROTO, &remote));
+    assert(_check_6packet(&ipv6_addr_unspecified, &dst_addr, _TEST_PROTO, "ABCD",
+                          sizeof("ABCD"), SOCK_ADDR_ANY_NETIF));
+    xtimer_usleep(1000);    /* let lwIP stack finish */
+    assert(_check_net());
+}
+
+static void test_sock_ip_send6__unsocketed_no_netif(void)
+{
+    static const ipv6_addr_t src_addr = { .u8 = _TEST_ADDR6_LOCAL };
+    static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR6_REMOTE };
+    static const sock_ip_ep_t local = { .addr = { .ipv6 = _TEST_ADDR6_LOCAL },
+                                        .family = AF_INET6 };
+    static const sock_ip_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR6_REMOTE },
+                                         .family = AF_INET6 };
+
+    assert(0 == sock_ip_create(&_sock, &local, NULL, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+    assert(sizeof("ABCD") == sock_ip_send(&_sock, "ABCD", sizeof("ABCD"),
+                                          _TEST_PROTO, &remote));
+    assert(_check_6packet(&src_addr, &dst_addr, _TEST_PROTO, "ABCD",
+                          sizeof("ABCD"), SOCK_ADDR_ANY_NETIF));
+    xtimer_usleep(1000);    /* let lwIP stack finish */
+    assert(_check_net());
+}
+
+static void test_sock_ip_send6__unsocketed_no_local(void)
+{
+    static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR6_REMOTE };
+    static const sock_ip_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR6_REMOTE },
+                                         .family = AF_INET6,
+                                         .netif = _TEST_NETIF };
+
+    assert(0 == sock_ip_create(&_sock, NULL, NULL, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+    assert(sizeof("ABCD") == sock_ip_send(&_sock, "ABCD", sizeof("ABCD"),
+                                          _TEST_PROTO, &remote));
+    assert(_check_6packet(&ipv6_addr_unspecified, &dst_addr, _TEST_PROTO, "ABCD",
+                          sizeof("ABCD"), _TEST_NETIF));
+    xtimer_usleep(1000);    /* let lwIP stack finish */
+    assert(_check_net());
+}
+
+static void test_sock_ip_send6__unsocketed(void)
+{
+    static const ipv6_addr_t src_addr = { .u8 = _TEST_ADDR6_LOCAL };
+    static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR6_REMOTE };
+    static const sock_ip_ep_t local = { .addr = { .ipv6 = _TEST_ADDR6_LOCAL },
+                                        .family = AF_INET6,
+                                        .netif = _TEST_NETIF };
+    static const sock_ip_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR6_REMOTE },
+                                         .family = AF_INET6 };
+
+    assert(0 == sock_ip_create(&_sock, &local, NULL, _TEST_PROTO,
+                               SOCK_FLAGS_REUSE_EP));
+    assert(sizeof("ABCD") == sock_ip_send(&_sock, "ABCD", sizeof("ABCD"),
+                                          _TEST_PROTO, &remote));
+    assert(_check_6packet(&src_addr, &dst_addr, _TEST_PROTO, "ABCD",
+                          sizeof("ABCD"), _TEST_NETIF));
+    xtimer_usleep(1000);    /* let lwIP stack finish */
+    assert(_check_net());
+}
+
+static void test_sock_ip_send6__no_sock_no_netif(void)
+{
+    static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR6_REMOTE };
+    static const sock_ip_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR6_REMOTE },
+                                         .family = AF_INET6 };
+
+    assert(sizeof("ABCD") == sock_ip_send(NULL, "ABCD", sizeof("ABCD"),
+                                          _TEST_PROTO, &remote));
+    assert(_check_6packet(&ipv6_addr_unspecified, &dst_addr, _TEST_PROTO, "ABCD",
+                          sizeof("ABCD"), SOCK_ADDR_ANY_NETIF));
+    xtimer_usleep(1000);    /* let lwIP stack finish */
+    assert(_check_net());
+}
+
+static void test_sock_ip_send6__no_sock(void)
+{
+    static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR6_REMOTE };
+    static const sock_ip_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR6_REMOTE },
+                                         .family = AF_INET6,
+                                         .netif = _TEST_NETIF };
+
+    assert(sizeof("ABCD") == sock_ip_send(NULL, "ABCD", sizeof("ABCD"),
+                                          _TEST_PROTO, &remote));
+    assert(_check_6packet(&ipv6_addr_unspecified, &dst_addr, _TEST_PROTO, "ABCD",
+                          sizeof("ABCD"), _TEST_NETIF));
+    xtimer_usleep(1000);    /* let lwIP stack finish */
+    assert(_check_net());
+}
+#endif  /* MODULE_LWIP_IPV6 */
+
+int main(void)
+{
+    uint8_t code = 0;
+
+#ifdef SO_REUSE
+    code |= 1;
+#endif
+#ifdef MODULE_LWIP_IPV4
+    code |= (1 << 4);
+#endif
+#ifdef MODULE_LWIP_IPV6
+    code |= (1 << 6);
+#endif
+    printf("code 0x%02x\n", code);
+    _net_init();
+    tear_down();
+#ifdef MODULE_LWIP_IPV4
+    /* EADDRINUSE does not apply for lwIP */
+    CALL(test_sock_ip_create4__EAFNOSUPPORT());
+    CALL(test_sock_ip_create4__EINVAL_addr());
+    CALL(test_sock_ip_create4__EINVAL_netif());
+    /* EPROTONOSUPPORT does not apply for lwIP */
+    CALL(test_sock_ip_create4__no_endpoints());
+    CALL(test_sock_ip_create4__only_local());
+    CALL(test_sock_ip_create4__only_local_reuse_ep());
+    CALL(test_sock_ip_create4__only_remote());
+    CALL(test_sock_ip_create4__full());
+    /* sock_ip_close() is tested in tear_down() */
+    /* sock_ip_get_local() is tested in sock_ip_create() tests */
+    /* sock_ip_get_remote() is tested in sock_ip_create() tests */
+    CALL(test_sock_ip_recv4__EADDRNOTAVAIL());
+    CALL(test_sock_ip_recv4__EAGAIN());
+    CALL(test_sock_ip_recv4__ENOBUFS());
+    /* EPROTO not applicable for lwIP */
+    CALL(test_sock_ip_recv4__ETIMEDOUT());
+    CALL(test_sock_ip_recv4__socketed());
+    CALL(test_sock_ip_recv4__socketed_with_remote());
+    CALL(test_sock_ip_recv4__unsocketed());
+    CALL(test_sock_ip_recv4__unsocketed_with_remote());
+    CALL(test_sock_ip_recv4__with_timeout());
+    CALL(test_sock_ip_recv4__non_blocking());
+    _prepare_send_checks();
+    CALL(test_sock_ip_send4__EAFNOSUPPORT());
+    CALL(test_sock_ip_send4__EINVAL_addr());
+    CALL(test_sock_ip_send4__EINVAL_netif());
+    CALL(test_sock_ip_send4__EHOSTUNREACH());
+    CALL(test_sock_ip_send4__ENOTCONN());
+    /* EPROTOTYPE does not apply for lwIP */
+    CALL(test_sock_ip_send4__socketed_no_local_no_netif());
+    CALL(test_sock_ip_send4__socketed_no_netif());
+    CALL(test_sock_ip_send4__socketed_no_local());
+    CALL(test_sock_ip_send4__socketed());
+    CALL(test_sock_ip_send4__socketed_other_remote());
+    CALL(test_sock_ip_send4__unsocketed_no_local_no_netif());
+    CALL(test_sock_ip_send4__unsocketed_no_netif());
+    CALL(test_sock_ip_send4__unsocketed_no_local());
+    CALL(test_sock_ip_send4__unsocketed());
+    CALL(test_sock_ip_send4__no_sock_no_netif());
+    CALL(test_sock_ip_send4__no_sock());
+#endif  /* MODULE_LWIP_IPV4 */
+#ifdef MODULE_LWIP_IPV6
+    /* EADDRINUSE does not apply for lwIP */
+    CALL(test_sock_ip_create6__EAFNOSUPPORT());
+    CALL(test_sock_ip_create6__EINVAL_addr());
+    CALL(test_sock_ip_create6__EINVAL_netif());
+    /* EPROTONOSUPPORT does not apply for lwIP */
+    CALL(test_sock_ip_create6__no_endpoints());
+    CALL(test_sock_ip_create6__only_local());
+    CALL(test_sock_ip_create6__only_local_reuse_ep());
+    CALL(test_sock_ip_create6__only_remote());
+    CALL(test_sock_ip_create6__full());
+    /* sock_ip_close() is tested in tear_down() */
+    /* sock_ip_get_local() is tested in sock_ip_create() tests */
+    /* sock_ip_get_remote() is tested in sock_ip_create() tests */
+    CALL(test_sock_ip_recv6__EADDRNOTAVAIL());
+    CALL(test_sock_ip_recv6__EAGAIN());
+    CALL(test_sock_ip_recv6__ENOBUFS());
+    /* EPROTO not applicable for lwIP */
+    CALL(test_sock_ip_recv6__ETIMEDOUT());
+    CALL(test_sock_ip_recv6__socketed());
+    CALL(test_sock_ip_recv6__socketed_with_remote());
+    CALL(test_sock_ip_recv6__unsocketed());
+    CALL(test_sock_ip_recv6__unsocketed_with_remote());
+    CALL(test_sock_ip_recv6__with_timeout());
+    CALL(test_sock_ip_recv6__non_blocking());
+    _prepare_send_checks();
+    CALL(test_sock_ip_send6__EAFNOSUPPORT());
+    CALL(test_sock_ip_send6__EINVAL_addr());
+    CALL(test_sock_ip_send6__EINVAL_netif());
+    CALL(test_sock_ip_send6__EHOSTUNREACH());
+    CALL(test_sock_ip_send6__ENOTCONN());
+    /* EPROTOTYPE does not apply for lwIP */
+    CALL(test_sock_ip_send6__socketed_no_local_no_netif());
+    CALL(test_sock_ip_send6__socketed_no_netif());
+    CALL(test_sock_ip_send6__socketed_no_local());
+    CALL(test_sock_ip_send6__socketed());
+    CALL(test_sock_ip_send6__socketed_other_remote());
+    CALL(test_sock_ip_send6__unsocketed_no_local_no_netif());
+    CALL(test_sock_ip_send6__unsocketed_no_netif());
+    CALL(test_sock_ip_send6__unsocketed_no_local());
+    CALL(test_sock_ip_send6__unsocketed());
+    CALL(test_sock_ip_send6__no_sock_no_netif());
+    CALL(test_sock_ip_send6__no_sock());
+#endif /* MODULE_LWIP_IPV6 */
+
+    puts("ALL TESTS SUCCESSFUL");
+
+    return 0;
+}
diff --git a/tests/lwip_sock_ip/stack.c b/tests/lwip_sock_ip/stack.c
new file mode 100644
index 0000000000..1c4d0f826a
--- /dev/null
+++ b/tests/lwip_sock_ip/stack.c
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2016 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>
+ */
+
+
+#include "msg.h"
+#include "net/ethernet.h"
+#include "net/ipv6.h"
+#include "net/netdev2/eth.h"
+#include "net/netdev2_test.h"
+#include "net/sock.h"
+#include "sched.h"
+#include "xtimer.h"
+
+#include "lwip.h"
+#include "lwip/ip4.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/nd6.h"
+#include "lwip/netif.h"
+#include "lwip/netif/netdev2.h"
+#include "lwip/tcpip.h"
+#include "netif/etharp.h"
+
+#include "constants.h"
+#include "stack.h"
+
+#define _MSG_QUEUE_SIZE     (1)
+#define _SEND_DONE  (0x92d7)
+#define _NETDEV_BUFFER_SIZE     (128)
+
+static msg_t _msg_queue[_MSG_QUEUE_SIZE];
+static uint8_t _netdev_buffer[_NETDEV_BUFFER_SIZE];
+netdev2_test_t netdev;
+static struct netif netif;
+static kernel_pid_t _check_pid = KERNEL_PID_UNDEF;
+static mutex_t _netdev_buffer_mutex = MUTEX_INIT;
+static uint8_t _netdev_buffer_size;
+
+static inline void _get_iid(uint8_t *iid)
+{
+    uint8_t _local_ip[] = _TEST_ADDR6_LOCAL;
+
+    memcpy(iid, &_local_ip[8], sizeof(uint64_t));
+    iid[0] ^= 0x2;
+}
+
+static int _get_max_pkt_size(netdev2_t *dev, void *value, size_t max_len)
+{
+    return netdev2_eth_get(dev, NETOPT_MAX_PACKET_SIZE, value, max_len);
+}
+
+static int _get_src_len(netdev2_t *dev, void *value, size_t max_len)
+{
+    uint16_t *v = value;
+
+    (void)dev;
+    if (max_len != sizeof(uint16_t)) {
+        return -EOVERFLOW;
+    }
+
+    *v = sizeof(uint64_t);
+
+    return sizeof(uint16_t);
+}
+
+static int _get_addr(netdev2_t *dev, void *value, size_t max_len)
+{
+    uint8_t iid[ETHERNET_ADDR_LEN + 2];
+    uint8_t *addr = value;
+
+    (void)dev;
+    if (max_len < ETHERNET_ADDR_LEN) {
+        return -EOVERFLOW;
+    }
+
+    _get_iid(iid);
+
+    addr[0] = iid[0];
+    addr[1] = iid[1];
+    addr[2] = iid[2];
+    addr[3] = iid[5];
+    addr[4] = iid[6];
+    addr[5] = iid[7];
+
+    return ETHERNET_ADDR_LEN;
+}
+
+static int _get_addr_len(netdev2_t *dev, void *value, size_t max_len)
+{
+    return netdev2_eth_get(dev, NETOPT_ADDR_LEN, value, max_len);
+}
+
+static int _get_device_type(netdev2_t *dev, void *value, size_t max_len)
+{
+    return netdev2_eth_get(dev, NETOPT_DEVICE_TYPE, value, max_len);
+}
+
+static int _get_ipv6_iid(netdev2_t *dev, void *value, size_t max_len)
+{
+    (void)dev;
+    if (max_len != sizeof(uint64_t)) {
+        return -EOVERFLOW;
+    }
+    _get_iid(value);
+    return sizeof(uint64_t);
+}
+
+static void _netdev_isr(netdev2_t *dev)
+{
+    dev->event_callback(dev, NETDEV2_EVENT_RX_COMPLETE);
+}
+
+static int _netdev_recv(netdev2_t *dev, char *buf, int len, void *info)
+{
+    int res;
+
+    (void)dev;
+    (void)info;
+    mutex_lock(&_netdev_buffer_mutex);
+    if (buf != NULL) {
+        if ((unsigned)len < _netdev_buffer_size) {
+            mutex_unlock(&_netdev_buffer_mutex);
+            return -ENOBUFS;
+        }
+        memcpy(buf, _netdev_buffer, _netdev_buffer_size);
+    }
+    res = _netdev_buffer_size;
+    mutex_unlock(&_netdev_buffer_mutex);
+    return res;
+}
+
+static int _netdev_send(netdev2_t *dev, const struct iovec *vector, int count)
+{
+    msg_t done = { .type = _SEND_DONE };
+    unsigned offset = 0;
+
+    (void)dev;
+    mutex_lock(&_netdev_buffer_mutex);
+    for (int i = 0; i < count; i++) {
+        memcpy(&_netdev_buffer[offset], vector[i].iov_base, vector[i].iov_len);
+        offset += vector[i].iov_len;
+        if (offset > sizeof(_netdev_buffer)) {
+            mutex_unlock(&_netdev_buffer_mutex);
+            return -ENOBUFS;
+        }
+    }
+    mutex_unlock(&_netdev_buffer_mutex);
+    done.content.value = (uint32_t)offset - sizeof(ethernet_hdr_t);
+    msg_send(&done, _check_pid);
+    return offset;
+}
+
+void _net_init(void)
+{
+    xtimer_init();
+    msg_init_queue(_msg_queue, _MSG_QUEUE_SIZE);
+    _check_pid = sched_active_pid;
+
+    netdev2_test_setup(&netdev, NULL);
+    netdev2_test_set_get_cb(&netdev, NETOPT_SRC_LEN, _get_src_len);
+    netdev2_test_set_get_cb(&netdev, NETOPT_MAX_PACKET_SIZE,
+                            _get_max_pkt_size);
+    netdev2_test_set_get_cb(&netdev, NETOPT_ADDRESS, _get_addr);
+    netdev2_test_set_get_cb(&netdev, NETOPT_ADDR_LEN,
+                            _get_addr_len);
+    netdev2_test_set_get_cb(&netdev, NETOPT_SRC_LEN,
+                            _get_addr_len);
+    netdev2_test_set_get_cb(&netdev, NETOPT_DEVICE_TYPE,
+                            _get_device_type);
+    netdev2_test_set_get_cb(&netdev, NETOPT_IPV6_IID,
+                            _get_ipv6_iid);
+    netdev2_test_set_recv_cb(&netdev, _netdev_recv);
+    netdev2_test_set_isr_cb(&netdev, _netdev_isr);
+    /* netdev needs to be set-up */
+    assert(netdev.netdev.driver);
+#if LWIP_IPV4
+    ip4_addr_t local4, mask4, gw4;
+    local4.addr = HTONL(_TEST_ADDR4_LOCAL);
+    mask4.addr = HTONL(_TEST_ADDR4_MASK);
+    gw4.addr = HTONL(_TEST_ADDR4_GW);
+    netif_add(&netif, &local4, &mask4, &gw4, &netdev, lwip_netdev2_init, tcpip_input);
+#else
+    netif_add(&netif, &netdev, lwip_netdev2_init, tcpip_input);
+#endif
+#if LWIP_IPV6
+    static const uint8_t local6[] = _TEST_ADDR6_LOCAL;
+    s8_t idx;
+    netif_add_ip6_address(&netif, (ip6_addr_t *)&local6, &idx);
+    netif_ip6_addr_set_state(&netif, idx, IP6_ADDR_VALID);
+#endif
+    netif_set_default(&netif);
+    lwip_bootstrap();
+    xtimer_sleep(3);    /* Let the auto-configuration run warm */
+}
+
+void _prepare_send_checks(void)
+{
+    uint8_t remote6[] = _TEST_ADDR6_REMOTE;
+    uint8_t mac[sizeof(uint64_t)];
+
+    memcpy(mac, &remote6[8], sizeof(uint64_t));
+    mac[0] ^= 0x2;
+    mac[3] = mac[5];
+    mac[4] = mac[6];
+    mac[5] = mac[7];
+
+    netdev2_test_set_send_cb(&netdev, _netdev_send);
+#if LWIP_ARP
+    const ip4_addr_t remote4 = { .addr = HTONL(_TEST_ADDR4_REMOTE) };
+    assert(ERR_OK == etharp_add_static_entry(&remote4, (struct eth_addr *)mac));
+#endif
+#if LWIP_IPV6
+    memset(destination_cache, 0,
+           LWIP_ND6_NUM_DESTINATIONS * sizeof(struct nd6_destination_cache_entry));
+    memset(neighbor_cache, 0,
+           LWIP_ND6_NUM_NEIGHBORS * sizeof(struct nd6_neighbor_cache_entry));
+    for (int i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) {
+        struct nd6_neighbor_cache_entry *nc = &neighbor_cache[i];
+        if (nc->state == ND6_NO_ENTRY) {
+            nc->state = ND6_REACHABLE;
+            memcpy(&nc->next_hop_address, remote6, sizeof(ip6_addr_t));
+            memcpy(&nc->lladdr, mac, 6);
+            nc->netif = &netif;
+            nc->counter.reachable_time = UINT32_MAX;
+            break;
+        }
+    }
+#endif
+}
+
+bool _inject_4packet(uint32_t src, uint32_t dst, uint8_t proto, void *data,
+                     size_t data_len, uint16_t netif)
+{
+#if LWIP_IPV4
+    mutex_lock(&_netdev_buffer_mutex);
+    ethernet_hdr_t *eth_hdr = (ethernet_hdr_t *)_netdev_buffer;
+    struct ip_hdr *ip_hdr = (struct ip_hdr *)(eth_hdr + 1);
+    uint8_t *payload = (uint8_t *)(ip_hdr + 1);
+    (void)netif;
+
+    _get_addr((netdev2_t *)&netdev, &eth_hdr->dst, sizeof(eth_hdr->dst));
+    eth_hdr->type = byteorder_htons(ETHERTYPE_IPV4);
+    IPH_VHL_SET(ip_hdr, 4, 5);
+    IPH_TOS_SET(ip_hdr, 0);
+    IPH_LEN_SET(ip_hdr, HTONS(sizeof(struct ip_hdr) + data_len));
+    IPH_TTL_SET(ip_hdr, 64);
+    IPH_PROTO_SET(ip_hdr, proto);
+    ip_hdr->src.addr = HTONL(src);
+    ip_hdr->dest.addr = HTONL(dst);
+    IPH_CHKSUM_SET(ip_hdr, 0);
+    IPH_CHKSUM_SET(ip_hdr, inet_chksum(ip_hdr, sizeof(struct ip_hdr)));
+
+    memcpy(payload, data, data_len);
+    _netdev_buffer_size = sizeof(ethernet_hdr_t) + sizeof(struct ip_hdr) +
+                          data_len;
+    mutex_unlock(&_netdev_buffer_mutex);
+    ((netdev2_t *)&netdev)->event_callback((netdev2_t *)&netdev, NETDEV2_EVENT_ISR);
+
+    return true;
+#else
+    (void)src; (void)dst; (void)proto; (void)netif; (void)data; (void)data_len;
+    return false;
+#endif
+}
+
+bool _inject_6packet(const ipv6_addr_t *src, const ipv6_addr_t *dst,
+                     uint8_t proto, void *data, size_t data_len, uint16_t netif)
+{
+#if LWIP_IPV6
+    mutex_lock(&_netdev_buffer_mutex);
+    ethernet_hdr_t *eth_hdr = (ethernet_hdr_t *)_netdev_buffer;
+    ipv6_hdr_t *ipv6_hdr = (ipv6_hdr_t *)(eth_hdr + 1);
+    uint8_t *payload = (uint8_t *)(ipv6_hdr + 1);
+    (void)netif;
+
+    _get_addr((netdev2_t *)&netdev, &eth_hdr->dst, sizeof(eth_hdr->dst));
+    eth_hdr->type = byteorder_htons(ETHERTYPE_IPV6);
+    ipv6_hdr_set_version(ipv6_hdr);
+    ipv6_hdr->len = byteorder_htons(data_len);
+    ipv6_hdr->nh = proto;
+    ipv6_hdr->hl = 64;
+    memcpy(&ipv6_hdr->src, src, sizeof(ipv6_hdr->src));
+    memcpy(&ipv6_hdr->dst, dst, sizeof(ipv6_hdr->dst));
+
+    memcpy(payload, data, data_len);
+    _netdev_buffer_size = sizeof(ethernet_hdr_t) + sizeof(ipv6_hdr_t) +
+                          data_len;
+    mutex_unlock(&_netdev_buffer_mutex);
+    ((netdev2_t *)&netdev)->event_callback((netdev2_t *)&netdev, NETDEV2_EVENT_ISR);
+
+    return true;
+#else
+    (void)src; (void)dst; (void)proto; (void)netif; (void)data; (void)data_len;
+    return false;
+#endif
+}
+
+bool _check_net(void)
+{
+    /* TODO maybe check packet buffer here too? */
+    return true;
+}
+
+bool _check_4packet(uint32_t src, uint32_t dst, uint8_t proto,
+                    void *data, size_t data_len, uint16_t netif)
+{
+#if LWIP_IPV4
+    msg_t msg;
+
+    (void)netif;
+    while (data_len != (msg.content.value - sizeof(struct ip_hdr))) {
+        msg_receive(&msg);
+    }
+    mutex_lock(&_netdev_buffer_mutex);
+    ethernet_hdr_t *eth_hdr = (ethernet_hdr_t *)_netdev_buffer;
+    struct ip_hdr *ip_hdr = (struct ip_hdr *)(eth_hdr + 1);
+    uint8_t *payload = (uint8_t *)(ip_hdr + 1);
+    uint16_t payload_len = HTONS(IPH_LEN(ip_hdr)) - sizeof(struct ip_hdr);
+    const bool ip_correct = ((src == 0) || (src = ip_hdr->src.addr)) &&
+                            (dst = ip_hdr->dest.addr) &&
+                            (IPH_PROTO(ip_hdr) == proto);
+    const bool payload_correct = (data_len == payload_len) &&
+                                 (memcmp(data, payload, data_len) == 0);
+    mutex_unlock(&_netdev_buffer_mutex);
+    return ip_correct && payload_correct;
+#else
+    (void)src; (void)dst; (void)proto; (void)netif; (void)data; (void)data_len;
+    return false;
+#endif
+}
+
+bool _check_6packet(const ipv6_addr_t *src, const ipv6_addr_t *dst,
+                    uint8_t proto, void *data, size_t data_len, uint16_t netif)
+{
+#if LWIP_IPV6
+    msg_t msg;
+
+    (void)netif;
+    while (data_len != (msg.content.value - sizeof(ipv6_hdr_t))) {
+        msg_receive(&msg);
+    }
+    mutex_lock(&_netdev_buffer_mutex);
+    ethernet_hdr_t *eth_hdr = (ethernet_hdr_t *)_netdev_buffer;
+    ipv6_hdr_t *ipv6_hdr = (ipv6_hdr_t *)(eth_hdr + 1);
+    uint8_t *payload = (uint8_t *)(ipv6_hdr + 1);
+    uint16_t payload_len = byteorder_ntohs(ipv6_hdr->len);
+    bool ip_correct = (ipv6_addr_is_unspecified(src) || ipv6_addr_equal(src, &ipv6_hdr->src)) &&
+                      ipv6_addr_equal(dst, &ipv6_hdr->dst) && (proto == ipv6_hdr->nh);
+    bool payload_correct = (data_len == payload_len) &&
+                           (memcmp(data, payload, data_len) == 0);
+    mutex_unlock(&_netdev_buffer_mutex);
+    return ip_correct && payload_correct;
+#else
+    (void)src; (void)dst; (void)proto; (void)netif; (void)data; (void)data_len;
+    return false;
+#endif
+}
+
+/** @} */
diff --git a/tests/lwip_sock_ip/stack.h b/tests/lwip_sock_ip/stack.h
new file mode 100644
index 0000000000..ad05a37668
--- /dev/null
+++ b/tests/lwip_sock_ip/stack.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2016 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
+ * @ingroup
+ * @brief
+ * @{
+ *
+ * @file
+ * @brief
+ *
+ * @author  Martine Lenders <mlenders@inf.fu-berlin.de>
+ */
+#ifndef STACK_H_
+#define STACK_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "net/ipv6/addr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief   Initializes networking for tests
+ */
+void _net_init(void);
+
+/**
+ * @brief   Does what ever preparations are needed to check the packets sent
+ */
+void _prepare_send_checks(void);
+
+/**
+ * @brief   Injects a received IPv4 packet into the stack
+ *
+ * @param[in] src       The source address of the IPv4 packet
+ * @param[in] dst       The destination address of the IPv4 packet
+ * @param[in] proto     The protocol field of the IPv4 packet
+ * @param[in] data      The payload of the IPv4 packet
+ * @param[in] data_len  The payload length of the IPv4 packet
+ * @param[in] netif     The interface the packet came over
+ *
+ * @return  true, if packet was successfully injected
+ * @return  false, if an error occurred during injection
+ */
+bool _inject_4packet(uint32_t src, uint32_t dst, uint8_t proto, void *data,
+                     size_t data_len, uint16_t netif);
+
+/**
+ * @brief   Injects a received IPv6 packet into the stack
+ *
+ * @param[in] src       The source address of the IPv6 packet
+ * @param[in] dst       The destination address of the IPv6 packet
+ * @param[in] proto     The next header field of the IPv6 packet
+ * @param[in] data      The payload of the IPv6 packet
+ * @param[in] data_len  The payload length of the IPv6 packet
+ * @param[in] netif     The interface the packet came over
+ *
+ * @return  true, if packet was successfully injected
+ * @return  false, if an error occurred during injection
+ */
+bool _inject_6packet(const ipv6_addr_t *src, const ipv6_addr_t *dst,
+                     uint8_t proto, void *data, size_t data_len,
+                     uint16_t netif);
+
+/**
+ * @brief   Checks networking state (e.g. packet buffer state)
+ *
+ * @return  true, if networking component is still in valid state
+ * @return  false, if networking component is in an invalid state
+ */
+bool _check_net(void);
+
+/**
+ * @brief   Checks if a IPv4 packet was sent by the networking component
+ *
+ * @param[in] src               Expected source address of the IPv4 packet
+ * @param[in] dst               Expected destination address of the IPv4 packet
+ * @param[in] proto             Expected next header field of the IPv4 packet
+ * @param[in] data              Expected payload of the IPv4 packet
+ * @param[in] data_len          Expected payload length of the IPv4 packet
+ * @param[in] netif             Expected interface the packet is supposed to
+ *                              be send over
+ *
+ * @return  true, if all parameters match as expected
+ * @return  false, if not.
+ */
+bool _check_4packet(uint32_t src, uint32_t dst, uint8_t proto,
+                    void *data, size_t data_len, uint16_t netif);
+
+/**
+ * @brief   Checks if a IPv6 packet was sent by the networking component
+ *
+ * @param[in] src               Expected source address of the IPv6 packet
+ * @param[in] dst               Expected destination address of the IPv6 packet
+ * @param[in] proto             Expected next header field of the IPv6 packet
+ * @param[in] data              Expected payload of the IPv6 packet
+ * @param[in] data_len          Expected payload length of the IPv6 packet
+ * @param[in] netif             Expected interface the packet is supposed to
+ *                              be send over
+ *
+ * @return  true, if all parameters match as expected
+ * @return  false, if not.
+ */
+bool _check_6packet(const ipv6_addr_t *src, const ipv6_addr_t *dst,
+                    uint8_t proto, void *data, size_t data_len, uint16_t netif);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* STACK_H_ */
+/** @} */
diff --git a/tests/lwip_sock_ip/tests/01-run.py b/tests/lwip_sock_ip/tests/01-run.py
new file mode 100755
index 0000000000..ff37e71d86
--- /dev/null
+++ b/tests/lwip_sock_ip/tests/01-run.py
@@ -0,0 +1,128 @@
+#!/usr/bin/env python3
+
+# Copyright (C) 2016 Kaspar Schleiser <kaspar@schleiser.de>
+#
+# This file is subject to the terms and conditions of the GNU Lesser
+# General Public License v2.1. See the file LICENSE in the top level
+# directory for more details.
+
+import os
+import sys
+
+from datetime import datetime
+
+sys.path.append(os.path.join(os.environ['RIOTBASE'], 'dist/tools/testrunner'))
+import testrunner
+
+class InvalidTimeout(Exception):
+    pass
+
+def _ipv6_tests(code):
+    return code & (1 << 6)
+
+def _ipv4_tests(code):
+    return code & (1 << 4)
+
+def testfunc(child):
+    child.expect(u"code (0x[0-9a-f]{2})")
+    code = int(child.match.group(1), base=16)
+    if _ipv4_tests(code):
+        child.expect_exact(u"Calling test_sock_ip_create4__EAFNOSUPPORT()")
+        child.expect_exact(u"Calling test_sock_ip_create4__EINVAL_addr()")
+        child.expect_exact(u"Calling test_sock_ip_create4__EINVAL_netif()")
+        child.expect_exact(u"Calling test_sock_ip_create4__no_endpoints()")
+        child.expect_exact(u"Calling test_sock_ip_create4__only_local()")
+        child.expect_exact(u"Calling test_sock_ip_create4__only_local_reuse_ep()")
+        child.expect_exact(u"Calling test_sock_ip_create4__only_remote()")
+        child.expect_exact(u"Calling test_sock_ip_create4__full()")
+        child.expect_exact(u"Calling test_sock_ip_recv4__EADDRNOTAVAIL()")
+        child.expect_exact(u"Calling test_sock_ip_recv4__EAGAIN()")
+        child.expect_exact(u"Calling test_sock_ip_recv4__ENOBUFS()")
+        child.expect_exact(u"Calling test_sock_ip_recv4__ETIMEDOUT()")
+        child.match         # get to ensure program reached that point
+        start = datetime.now()
+        child.expect_exact(u" * Calling sock_ip_recv()")
+        child.expect(u" \\* \\(timed out with timeout (\\d+)\\)")
+        exp_diff = int(child.match.group(1))
+        stop = datetime.now()
+        diff = (stop - start)
+        diff = (diff.seconds * 1000000) + diff.microseconds
+        # fail within 5% of expected
+        if diff > (exp_diff + (exp_diff * 0.05)) or \
+           diff < (exp_diff - (exp_diff * 0.05)):
+            raise InvalidTimeout("Invalid timeout %d (expected %d)" % (diff, exp_diff));
+        else:
+            print("Timed out correctly: %d (expected %d)" % (diff, exp_diff))
+        child.expect_exact(u"Calling test_sock_ip_recv4__socketed()")
+        child.expect_exact(u"Calling test_sock_ip_recv4__socketed_with_remote()")
+        child.expect_exact(u"Calling test_sock_ip_recv4__unsocketed()")
+        child.expect_exact(u"Calling test_sock_ip_recv4__unsocketed_with_remote()")
+        child.expect_exact(u"Calling test_sock_ip_recv4__with_timeout()")
+        child.expect_exact(u"Calling test_sock_ip_recv4__non_blocking()")
+        child.expect_exact(u"Calling test_sock_ip_send4__EAFNOSUPPORT()")
+        child.expect_exact(u"Calling test_sock_ip_send4__EINVAL_addr()")
+        child.expect_exact(u"Calling test_sock_ip_send4__EINVAL_netif()")
+        child.expect_exact(u"Calling test_sock_ip_send4__ENOTCONN()")
+        child.expect_exact(u"Calling test_sock_ip_send4__socketed_no_local_no_netif()")
+        child.expect_exact(u"Calling test_sock_ip_send4__socketed_no_netif()")
+        child.expect_exact(u"Calling test_sock_ip_send4__socketed_no_local()")
+        child.expect_exact(u"Calling test_sock_ip_send4__socketed()")
+        child.expect_exact(u"Calling test_sock_ip_send4__socketed_other_remote()")
+        child.expect_exact(u"Calling test_sock_ip_send4__unsocketed_no_local_no_netif()")
+        child.expect_exact(u"Calling test_sock_ip_send4__unsocketed_no_netif()")
+        child.expect_exact(u"Calling test_sock_ip_send4__unsocketed_no_local()")
+        child.expect_exact(u"Calling test_sock_ip_send4__unsocketed()")
+        child.expect_exact(u"Calling test_sock_ip_send4__no_sock_no_netif()")
+        child.expect_exact(u"Calling test_sock_ip_send4__no_sock()")
+    if _ipv6_tests(code):
+        child.expect_exact(u"Calling test_sock_ip_create6__EAFNOSUPPORT()")
+        child.expect_exact(u"Calling test_sock_ip_create6__EINVAL_addr()")
+        child.expect_exact(u"Calling test_sock_ip_create6__EINVAL_netif()")
+        child.expect_exact(u"Calling test_sock_ip_create6__no_endpoints()")
+        child.expect_exact(u"Calling test_sock_ip_create6__only_local()")
+        child.expect_exact(u"Calling test_sock_ip_create6__only_local_reuse_ep()")
+        child.expect_exact(u"Calling test_sock_ip_create6__only_remote()")
+        child.expect_exact(u"Calling test_sock_ip_create6__full()")
+        child.expect_exact(u"Calling test_sock_ip_recv6__EADDRNOTAVAIL()")
+        child.expect_exact(u"Calling test_sock_ip_recv6__EAGAIN()")
+        child.expect_exact(u"Calling test_sock_ip_recv6__ENOBUFS()")
+        child.expect_exact(u"Calling test_sock_ip_recv6__ETIMEDOUT()")
+        child.match         # get to ensure program reached that point
+        start = datetime.now()
+        child.expect_exact(u" * Calling sock_ip_recv()")
+        child.expect(u" \\* \\(timed out with timeout (\\d+)\\)")
+        exp_diff = int(child.match.group(1))
+        stop = datetime.now()
+        diff = (stop - start)
+        diff = (diff.seconds * 1000000) + diff.microseconds
+        # fail within 5% of expected
+        if diff > (exp_diff + (exp_diff * 0.05)) or \
+           diff < (exp_diff - (exp_diff * 0.05)):
+            raise InvalidTimeout("Invalid timeout %d (expected %d)" % (diff, exp_diff));
+        else:
+            print("Timed out correctly: %d (expected %d)" % (diff, exp_diff))
+        child.expect_exact(u"Calling test_sock_ip_recv6__socketed()")
+        child.expect_exact(u"Calling test_sock_ip_recv6__socketed_with_remote()")
+        child.expect_exact(u"Calling test_sock_ip_recv6__unsocketed()")
+        child.expect_exact(u"Calling test_sock_ip_recv6__unsocketed_with_remote()")
+        child.expect_exact(u"Calling test_sock_ip_recv6__with_timeout()")
+        child.expect_exact(u"Calling test_sock_ip_recv6__non_blocking()")
+        child.expect_exact(u"Calling test_sock_ip_send6__EAFNOSUPPORT()")
+        child.expect_exact(u"Calling test_sock_ip_send6__EINVAL_addr()")
+        child.expect_exact(u"Calling test_sock_ip_send6__EINVAL_netif()")
+        child.expect_exact(u"Calling test_sock_ip_send6__ENOTCONN()")
+        child.expect_exact(u"Calling test_sock_ip_send6__socketed_no_local_no_netif()")
+        child.expect_exact(u"Calling test_sock_ip_send6__socketed_no_netif()")
+        child.expect_exact(u"Calling test_sock_ip_send6__socketed_no_local()")
+        child.expect_exact(u"Calling test_sock_ip_send6__socketed()")
+        child.expect_exact(u"Calling test_sock_ip_send6__socketed_other_remote()")
+        child.expect_exact(u"Calling test_sock_ip_send6__unsocketed_no_local_no_netif()")
+        child.expect_exact(u"Calling test_sock_ip_send6__unsocketed_no_netif()")
+        child.expect_exact(u"Calling test_sock_ip_send6__unsocketed_no_local()")
+        child.expect_exact(u"Calling test_sock_ip_send6__unsocketed()")
+        child.expect_exact(u"Calling test_sock_ip_send6__no_sock_no_netif()")
+        child.expect_exact(u"Calling test_sock_ip_send6__no_sock()")
+    child.expect_exact(u"ALL TESTS SUCCESSFUL")
+
+if __name__ == "__main__":
+    sys.exit(testrunner.run(testfunc))
-- 
GitLab