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, ð_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, ð_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