diff --git a/Makefile.dep b/Makefile.dep index d0305449d8503450804ae94f16fb79f9c76a798d..a09ccd12897f369ae5d8893c62a9b4a7a2519894 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -436,6 +436,10 @@ ifneq (,$(filter lwip_sock_ip,$(USEMODULE))) USEMODULE += lwip_raw endif +ifneq (,$(filter lwip_sock_tcp,$(USEMODULE))) + USEMODULE += lwip_tcp +endif + ifneq (,$(filter lwip_sock_udp,$(USEMODULE))) USEMODULE += lwip_udp endif diff --git a/pkg/lwip/Makefile.include b/pkg/lwip/Makefile.include index feb293bd91e023d118632596096433168666b2a8..b2ffbcd8c03a0ccf47fedbfec3088844fa32fdba 100644 --- a/pkg/lwip/Makefile.include +++ b/pkg/lwip/Makefile.include @@ -25,6 +25,9 @@ endif ifneq (,$(filter lwip_sock_ip,$(USEMODULE))) DIRS += $(RIOTBASE)/pkg/lwip/contrib/sock/ip endif +ifneq (,$(filter lwip_sock_tcp,$(USEMODULE))) + DIRS += $(RIOTBASE)/pkg/lwip/contrib/sock/tcp +endif ifneq (,$(filter lwip_sock_udp,$(USEMODULE))) DIRS += $(RIOTBASE)/pkg/lwip/contrib/sock/udp endif diff --git a/pkg/lwip/contrib/sock/lwip_sock.c b/pkg/lwip/contrib/sock/lwip_sock.c index 4a31281e62ace163edbe39789ad3f9a1f9bf257e..a46790b04c41dfdf9da1e798a35b71d8395ee41b 100644 --- a/pkg/lwip/contrib/sock/lwip_sock.c +++ b/pkg/lwip/contrib/sock/lwip_sock.c @@ -295,6 +295,11 @@ int lwip_sock_create(struct netconn **conn, const struct _sock_tl_ep *local, * local->port) demand binding */ if (bind) { switch (netconn_bind(*conn, &local_addr, local_port)) { +#if LWIP_TCP + case ERR_BUF: + res = -ENOMEM; + break; +#endif case ERR_USE: res = -EADDRINUSE; break; @@ -311,6 +316,24 @@ int lwip_sock_create(struct netconn **conn, const struct _sock_tl_ep *local, } if (remote != NULL) { switch (netconn_connect(*conn, &remote_addr, remote_port)) { +#if LWIP_TCP + case ERR_BUF: + res = -ENOMEM; + break; + case ERR_INPROGRESS: + res = -EINPROGRESS; + break; + case ERR_ISCONN: + res = -EISCONN; + break; + case ERR_IF: + case ERR_RTE: + res = -ENETUNREACH; + break; + case ERR_ABRT: + res = -ETIMEDOUT; + break; +#endif case ERR_USE: res = -EADDRINUSE; break; @@ -403,6 +426,7 @@ int lwip_sock_get_addr(struct netconn *conn, struct _sock_tl_ep *ep, u8_t local) return 0; } +#if defined(MODULE_LWIP_SOCK_UDP) || defined(MODULE_LWIP_SOCK_IP) int lwip_sock_recv(struct netconn *conn, uint32_t timeout, struct netbuf **buf) { int res; @@ -441,6 +465,7 @@ int lwip_sock_recv(struct netconn *conn, uint32_t timeout, struct netbuf **buf) #endif return res; } +#endif /* defined(MODULE_LWIP_SOCK_UDP) || defined(MODULE_LWIP_SOCK_IP) */ ssize_t lwip_sock_send(struct netconn **conn, const void *data, size_t len, int proto, const struct _sock_tl_ep *remote, int type) @@ -494,15 +519,20 @@ ssize_t lwip_sock_send(struct netconn **conn, const void *data, size_t len, netbuf_delete(buf); return -ENOTCONN; } + res = len; /* set for non-TCP calls */ if (remote != NULL) { err = netconn_sendto(tmp, buf, &remote_addr, remote_port); } +#if LWIP_TCP + else if (tmp->type & NETCONN_TCP) { + err = netconn_write_partly(tmp, data, len, 0, (size_t *)(&res)); + } +#endif /* LWIP_TCP */ else { err = netconn_send(tmp, buf); } switch (err) { case ERR_OK: - res = len; if (conn != NULL) { *conn = tmp; } diff --git a/pkg/lwip/contrib/sock/tcp/Makefile b/pkg/lwip/contrib/sock/tcp/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..2a6b23441f29019e1ba483662fca5f72c6b2f000 --- /dev/null +++ b/pkg/lwip/contrib/sock/tcp/Makefile @@ -0,0 +1,3 @@ +MODULE := lwip_sock_tcp + +include $(RIOTBASE)/Makefile.base diff --git a/pkg/lwip/contrib/sock/tcp/lwip_sock_tcp.c b/pkg/lwip/contrib/sock/tcp/lwip_sock_tcp.c new file mode 100644 index 0000000000000000000000000000000000000000..df879ac3a9ea3d688e507c9762a9e53371472d0f --- /dev/null +++ b/pkg/lwip/contrib/sock/tcp/lwip_sock_tcp.c @@ -0,0 +1,376 @@ +/* + * 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 "mutex.h" + +#include "net/sock/tcp.h" +#include "timex.h" + +#include "lwip/sock_internal.h" +#include "lwip/api.h" +#include "lwip/opt.h" + +static inline void _tcp_sock_init(sock_tcp_t *sock, struct netconn *conn, + sock_tcp_queue_t *queue) +{ + mutex_init(&sock->mutex); + mutex_lock(&sock->mutex); + netconn_set_noautorecved(conn, 1); + sock->conn = conn; + sock->queue = queue; + sock->last_buf = NULL; + sock->last_offset = 0; + mutex_unlock(&sock->mutex); +} + +int sock_tcp_connect(sock_tcp_t *sock, const sock_tcp_ep_t *remote, + uint16_t local_port, uint16_t flags) +{ + assert(sock != NULL); + assert((remote != NULL) && (remote->port != 0)); + + int res; + struct netconn *tmp = NULL; + struct _sock_tl_ep local = { .family = remote->family, + .netif = remote->netif, + .port = local_port }; + + if ((res = lwip_sock_create(&tmp, &local, (struct _sock_tl_ep *)remote, 0, + flags, NETCONN_TCP)) == 0) { + _tcp_sock_init(sock, tmp, NULL); + } + return res; +} + +int sock_tcp_listen(sock_tcp_queue_t *queue, const sock_tcp_ep_t *local, + sock_tcp_t *queue_array, unsigned queue_len, + uint16_t flags) +{ + assert(queue != NULL); + assert((local != NULL) && (local->port != 0)); + assert((queue_array != NULL) && (queue_len != 0)); + + int res; + struct netconn *tmp = NULL; + + if (queue_len > USHRT_MAX) { + return -EFAULT; + } + if ((res = lwip_sock_create(&tmp, (struct _sock_tl_ep *)local, NULL, 0, + flags, NETCONN_TCP)) < 0) { + return res; + } + assert(tmp != NULL); /* just in case lwIP is trolling */ + mutex_init(&queue->mutex); + mutex_lock(&queue->mutex); + queue->conn = tmp; + queue->array = queue_array; + queue->len = queue_len; + queue->used = 0; + memset(queue->array, 0, sizeof(sock_tcp_t) * queue_len); + mutex_unlock(&queue->mutex); + switch (netconn_listen_with_backlog(queue->conn, queue->len)) { + case ERR_OK: + break; + case ERR_MEM: + return -ENOMEM; + case ERR_USE: + return -EADDRINUSE; + case ERR_VAL: + return -EINVAL; + default: + assert(false); /* should not happen since queue->conn is not closed + * and we have a TCP conn */ + break; + } + return 0; +} + +void sock_tcp_disconnect(sock_tcp_t *sock) +{ + assert(sock != NULL); + mutex_lock(&sock->mutex); + if (sock->conn != NULL) { + netconn_close(sock->conn); + netconn_delete(sock->conn); + sock->conn = NULL; + /* if sock came from a sock_tcp_queue_t: since sock is a pointer in it's + * array it is also deleted from there, but we need to decrement the used + * counter */ + if (sock->queue != NULL) { + assert(sock->queue->used > 0); + sock->queue->used--; + sock->queue = NULL; + } + } + mutex_unlock(&sock->mutex); + memset(&sock->mutex, 0, sizeof(mutex_t)); +} + +void sock_tcp_stop_listen(sock_tcp_queue_t *queue) +{ + assert(queue != NULL); + mutex_lock(&queue->mutex); + if (queue->conn != NULL) { + netconn_close(queue->conn); + netconn_delete(queue->conn); + queue->conn = NULL; + /* sever connections established through this queue */ + for (unsigned i = 0; i < queue->len; i++) { + sock_tcp_disconnect(&queue->array[i]); + } + queue->array = NULL; + queue->len = 0; + queue->used = 0; + } + mutex_unlock(&queue->mutex); + memset(&queue->mutex, 0, sizeof(mutex_t)); +} + +int sock_tcp_get_local(sock_tcp_t *sock, sock_tcp_ep_t *ep) +{ + int res = 0; + assert(sock != NULL); + mutex_lock(&sock->mutex); + if ((sock->conn == NULL) || lwip_sock_get_addr(sock->conn, + (struct _sock_tl_ep *)ep, + 1)) { + res = -EADDRNOTAVAIL; + } + mutex_unlock(&sock->mutex); + return res; +} + +int sock_tcp_get_remote(sock_tcp_t *sock, sock_tcp_ep_t *ep) +{ + int res = 0; + assert(sock != NULL); + mutex_lock(&sock->mutex); + if ((sock->conn == NULL) || lwip_sock_get_addr(sock->conn, + (struct _sock_tl_ep *)ep, + 0)) { + res = -ENOTCONN; + } + mutex_unlock(&sock->mutex); + return res; +} + +int sock_tcp_queue_get_local(sock_tcp_queue_t *queue, sock_tcp_ep_t *ep) +{ + int res = 0; + + assert(queue != NULL); + mutex_lock(&queue->mutex); + if ((queue->conn == NULL) || lwip_sock_get_addr(queue->conn, + (struct _sock_tl_ep *)ep, + 1)) { + res = -EADDRNOTAVAIL; + } + mutex_unlock(&queue->mutex); + return res; +} + +int sock_tcp_accept(sock_tcp_queue_t *queue, sock_tcp_t **sock, + uint32_t timeout) +{ + struct netconn *tmp = NULL; + int res = 0; + + assert((queue != NULL) && (sock != NULL)); + if (queue->conn == NULL) { + return -EINVAL; + } + if (timeout == 0) { + if (!mutex_trylock(&queue->mutex)) { + return -EAGAIN; + } + } + else if (timeout != 0) { + mutex_lock(&queue->mutex); + } + if (queue->used < queue->len) { +#if LWIP_SO_RCVTIMEO + if ((timeout != 0) && (timeout != SOCK_NO_TIMEOUT)) { + netconn_set_recvtimeout(queue->conn, timeout / US_PER_MS); + } + else +#endif + if ((timeout == 0) && !cib_avail(&queue->conn->acceptmbox.mbox.cib)) { + mutex_unlock(&queue->mutex); + return -EAGAIN; + } + switch (netconn_accept(queue->conn, &tmp)) { + case ERR_OK: + for (unsigned short i = 0; i < queue->len; i++) { + sock_tcp_t *s = &queue->array[i]; + if (s->conn == NULL) { + _tcp_sock_init(s, tmp, queue); + queue->used++; + assert(queue->used > 0); + *sock = s; + break; + } + } + break; + case ERR_ABRT: + res = -ECONNABORTED; + break; + case ERR_MEM: + res = -ENOMEM; + break; +#if LWIP_SO_RCVTIMEO + case ERR_TIMEOUT: + res = -ETIMEDOUT; + break; +#endif + default: + assert(false); + res = -1; + break; + } + } + else { + res = -ENOMEM; + } +#if LWIP_SO_RCVTIMEO + netconn_set_recvtimeout(queue->conn, 0); +#endif + mutex_unlock(&queue->mutex); + return res; +} + +ssize_t sock_tcp_read(sock_tcp_t *sock, void *data, size_t max_len, + uint32_t timeout) +{ + uint8_t *data_ptr = data; + struct pbuf *buf; + ssize_t offset = 0, res = 0; + bool done = false; + + assert((sock != NULL) && (data != NULL) && (max_len > 0)); + if (sock->conn == NULL) { + return -ENOTCONN; + } + if (timeout == 0) { + if (!mutex_trylock(&sock->mutex)) { + return -EAGAIN; + } + } + else { + mutex_lock(&sock->mutex); + } +#if LWIP_SO_RCVTIMEO + if ((timeout != 0) && (timeout != SOCK_NO_TIMEOUT)) { + netconn_set_recvtimeout(sock->conn, timeout / US_PER_MS); + } + else +#endif + if ((timeout == 0) && !cib_avail(&sock->conn->recvmbox.mbox.cib)) { + mutex_unlock(&sock->mutex); + return -EAGAIN; + } + while (!done) { + uint16_t copylen, buf_len; + if (sock->last_buf != NULL) { + buf = sock->last_buf; + } + else { + err_t err; + if ((err = netconn_recv_tcp_pbuf(sock->conn, &buf)) < 0) { + switch (err) { + case ERR_ABRT: + res = -ECONNABORTED; + break; + case ERR_CONN: + res = -EADDRNOTAVAIL; + break; + case ERR_RST: + case ERR_CLSD: + res = -ECONNRESET; + break; + case ERR_MEM: + res = -ENOMEM; + break; +#if LWIP_SO_RCVTIMEO + case ERR_TIMEOUT: + res = -ETIMEDOUT; + break; +#endif + default: + /* no applicable error */ + res = -1; + break; + } + break; + } + sock->last_buf = buf; + } + buf_len = buf->tot_len - sock->last_offset; + copylen = (buf_len > max_len) ? (uint16_t)max_len : buf_len; + pbuf_copy_partial(buf, data_ptr + offset, copylen, sock->last_offset); + offset += copylen; + max_len -= copylen; /* should be 0 at minimum due to copylen setting above */ + if (max_len == 0) { + done = true; + res = offset; /* in case offset == 0 */ + } + /* post-process buf */ + if (buf_len > copylen) { + /* there is still data in the buffer */ + sock->last_buf = buf; + sock->last_offset = copylen; + } + else { + sock->last_buf = NULL; + sock->last_offset = 0; + pbuf_free(buf); + break; + } + } + if (offset > 0) { + /* inform lwIP how much we receive*/ + netconn_recved(sock->conn, (u32_t)offset); + res = offset; /* we received data so return it */ + } + /* unset flags */ +#if LWIP_SO_RCVTIMEO + netconn_set_recvtimeout(sock->conn, 0); +#endif + netconn_set_nonblocking(sock->conn, false); + mutex_unlock(&sock->mutex); + return res; +} + +ssize_t sock_tcp_write(sock_tcp_t *sock, const void *data, size_t len) +{ + struct netconn *conn; + int res = 0; + + assert(sock != NULL); + assert((len == 0) || (data != NULL)); /* (len != 0) => (data != NULL) */ + mutex_lock(&sock->mutex); + if (sock->conn == NULL) { + mutex_unlock(&sock->mutex); + return -ENOTCONN; + } + conn = sock->conn; + mutex_unlock(&sock->mutex); /* we won't change anything to sock here + (lwip_sock_send neither, since it remote is + NULL) so we can leave the mutex */ + res = lwip_sock_send(&conn, data, len, 0, NULL, NETCONN_TCP); + return res; +} + +/** @} */ diff --git a/pkg/lwip/include/lwip/sock_internal.h b/pkg/lwip/include/lwip/sock_internal.h index 1ac5860eecbdbecdf5179fcfe5371ef87495b7f2..f47d39f0c1c9ee4fa1873c24f143be30d6cfe061 100644 --- a/pkg/lwip/include/lwip/sock_internal.h +++ b/pkg/lwip/include/lwip/sock_internal.h @@ -34,6 +34,14 @@ extern "C" { #endif +/** + * @brief Configures @ref sock_tcp_accept() timeout in milliseconds + * (0 by default, which means no timeout) + */ +#ifndef LWIP_SOCK_TCP_ACCEPT_TIMEOUT +#define LWIP_SOCK_TCP_ACCEPT_TIMEOUT (0) +#endif + /** * @brief Internal helper functions for lwIP * @internal @@ -44,7 +52,9 @@ int lwip_sock_create(struct netconn **conn, const struct _sock_tl_ep *local, 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); +#if defined(MODULE_LWIP_SOCK_UDP) || defined(MODULE_LWIP_SOCK_IP) int lwip_sock_recv(struct netconn *conn, uint32_t timeout, struct netbuf **buf); +#endif ssize_t lwip_sock_send(struct netconn **conn, const void *data, size_t len, int proto, const struct _sock_tl_ep *remote, int type); /** diff --git a/pkg/lwip/include/sock_types.h b/pkg/lwip/include/sock_types.h index 02cae53a8d48c8a7b40d26d93dd62e34e225d056..ddb890d18bac5edadf5fac68d37298e1f98fd952 100644 --- a/pkg/lwip/include/sock_types.h +++ b/pkg/lwip/include/sock_types.h @@ -33,6 +33,29 @@ struct sock_ip { struct netconn *conn; }; +/** + * @brief TCP sock type + * @internal + */ +struct sock_tcp { + struct netconn *conn; + struct sock_tcp_queue *queue; + mutex_t mutex; + struct pbuf *last_buf; + ssize_t last_offset; +}; + +/** + * @brief TCP queue type + */ +struct sock_tcp_queue { + struct netconn *conn; + struct sock_tcp *array; + mutex_t mutex; + unsigned short len; + unsigned short used; +}; + /** * @brief UDP sock type * @internal diff --git a/tests/lwip_sock_tcp/Makefile b/tests/lwip_sock_tcp/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..f0bf0a59ebeab42193125255b854e217ef7f2dec --- /dev/null +++ b/tests/lwip_sock_tcp/Makefile @@ -0,0 +1,49 @@ +APPLICATION = lwip_sock_tcp + +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 nucleo32-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_tcp +USEMODULE += netdev2_eth +USEMODULE += netdev2_test +USEMODULE += ps + +DISABLE_MODULE += auto_init + +CFLAGS += -DDEVELHELP +CFLAGS += -DSO_REUSE +CFLAGS += -DLWIP_SO_RCVTIMEO +CFLAGS += -DLWIP_SOCK_TCP_ACCEPT_TIMEOUT=500 +CFLAGS += -DLWIP_NETIF_LOOPBACK=1 +CFLAGS += -DLWIP_HAVE_LOOPIF=1 + +include $(RIOTBASE)/Makefile.include + +test: + ./tests/01-run.py diff --git a/tests/lwip_sock_tcp/README.md b/tests/lwip_sock_tcp/README.md new file mode 100644 index 0000000000000000000000000000000000000000..513116b6ececa7e437a28f01b3e544b2007a21a0 --- /dev/null +++ b/tests/lwip_sock_tcp/README.md @@ -0,0 +1,33 @@ +Tests for lwIP's sock_tcp port +============================== + +This tests the `sock_tcp` 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_tcp/constants.h b/tests/lwip_sock_tcp/constants.h new file mode 100644 index 0000000000000000000000000000000000000000..1d768d2e22b10c20819f391d0d51e790a86a9dcc --- /dev/null +++ b/tests/lwip_sock_tcp/constants.h @@ -0,0 +1,49 @@ +/* + * 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_PORT_LOCAL (0x2c94) +#define _TEST_PORT_REMOTE (0xa615) +#define _TEST_NETIF (1) +#define _TEST_TIMEOUT (1000000U) +#define _TEST_ADDR4_LOCAL (0xc0a84f96U) /* 192.168.79.150 */ +#define _TEST_ADDR4_REMOTE (0x7f000001U) /* 127.0.0.1 */ +#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 { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } +#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_tcp/main.c b/tests/lwip_sock_tcp/main.c new file mode 100644 index 0000000000000000000000000000000000000000..85291bd599a755d9153e23ded00157bdf41fa07c --- /dev/null +++ b/tests/lwip_sock_tcp/main.c @@ -0,0 +1,1192 @@ +/* + * 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 tests + * @{ + * + * @file + * @brief Test for UDP socks + * + * @author Martine Lenders <m.lenders@fu-berlin.de> + * @} + */ + +#include <assert.h> +#include <errno.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <sys/uio.h> + +#include "net/ipv6/addr.h" +#include "net/sock/tcp.h" +#include "sched.h" +#include "thread.h" +#include "xtimer.h" + +#include "constants.h" +#include "stack.h" + +#define _TEST_BUFFER_SIZE (128) +#define _QUEUE_SIZE (1) + +#define _MSG_QUEUE_SIZE (4) +#define _CLIENT_BUF_SIZE (128) +#define _SERVER_BUF_SIZE (128) +#define _SERVER_QUEUE_SIZE (1) +#define _CLIENT_MSG_START (0xe307) +#define _CLIENT_MSG_READ (0xe308) +#define _CLIENT_MSG_WRITE (0xe309) +#define _CLIENT_MSG_STOP (0xe30a) +#define _SERVER_MSG_START (0xe30b) +#define _SERVER_MSG_ACCEPT (0xe30c) +#define _SERVER_MSG_READ (0xe30d) +#define _SERVER_MSG_WRITE (0xe30e) +#define _SERVER_MSG_CLOSE (0xe30f) +#define _SERVER_MSG_STOP (0xe310) + +static uint8_t _test_buffer[_TEST_BUFFER_SIZE]; + +static char _client_stack[THREAD_STACKSIZE_DEFAULT]; +static char _server_stack[THREAD_STACKSIZE_DEFAULT]; +static uint8_t _client_buf[_CLIENT_BUF_SIZE]; +static uint8_t _server_buf[_SERVER_BUF_SIZE]; +static msg_t _client_msg_queue[_MSG_QUEUE_SIZE]; +static msg_t _server_msg_queue[_MSG_QUEUE_SIZE]; +static sock_tcp_t _sock, _client_sock; +static sock_tcp_t _queue_array[_QUEUE_SIZE]; +static sock_tcp_t _server_queue_array[_SERVER_QUEUE_SIZE]; +static sock_tcp_queue_t _queue, _server_queue; +static sock_tcp_ep_t _server_addr; +static kernel_pid_t _server, _client; + +#define CALL(fn) puts("Calling " # fn); fn; tear_down() + +static void *_server_func(void *arg); +static void *_client_func(void *arg); + +static void tear_down(void) +{ + msg_t msg = { .type = _CLIENT_MSG_STOP }; + msg_send(&msg, _client); + msg.type = _SERVER_MSG_STOP; + msg_send(&msg, _server); + sock_tcp_disconnect(&_sock); + sock_tcp_stop_listen(&_queue); + memset(&_sock, 0, sizeof(_sock)); + memset(&_queue, 0, sizeof(_queue)); + memset(&_server_addr, 0, sizeof(_server_addr)); +} + +#ifdef MODULE_LWIP_IPV4 +#ifdef SO_REUSE +static void test_tcp_connect4__EADDRINUSE(void) +{ + const sock_tcp_ep_t remote = { .addr = { .ipv4_u32 = HTONL(_TEST_ADDR4_REMOTE) }, + .family = AF_INET, + .port = _TEST_PORT_REMOTE, + .netif = SOCK_ADDR_ANY_NETIF }; + msg_t msg = { .type = _SERVER_MSG_START }; + static const uint16_t local_port = _TEST_PORT_REMOTE; + + _server_addr.family = AF_INET; + _server_addr.port = _TEST_PORT_REMOTE; + _server_addr.netif = SOCK_ADDR_ANY_NETIF; + + msg_send(&msg, _server); /* start server on _TEST_PORT_REMOTE */ + + assert(-EADDRINUSE == sock_tcp_connect(&_sock, &remote, local_port, 0)); +} +#endif + +static void test_tcp_connect4__EAFNOSUPPORT(void) +{ + const sock_tcp_ep_t remote = { .addr = { .ipv4_u32 = HTONL(_TEST_ADDR4_REMOTE) }, + .port = _TEST_PORT_REMOTE, + .netif = SOCK_ADDR_ANY_NETIF }; + + assert(-EAFNOSUPPORT == sock_tcp_connect(&_sock, &remote, 0, + SOCK_FLAGS_REUSE_EP)); +} + +/* ECONNREFUSED does not apply for lwIP; netconn_connect does not wait for + * connection build-up */ + +static void test_tcp_connect4__EINVAL_addr(void) +{ + static const sock_tcp_ep_t remote = { .family = AF_INET, + .port = _TEST_PORT_REMOTE, + .netif = SOCK_ADDR_ANY_NETIF }; + + assert(-EINVAL == sock_tcp_connect(&_sock, &remote, 0, SOCK_FLAGS_REUSE_EP)); +} + +static void test_tcp_connect4__EINVAL_netif(void) +{ + const sock_tcp_ep_t remote = { .addr = { .ipv4_u32 = HTONL(_TEST_ADDR4_REMOTE) }, + .family = AF_INET, + .port = _TEST_PORT_REMOTE, + .netif = (_TEST_NETIF + 1) }; + + assert(-EINVAL == sock_tcp_connect(&_sock, &remote, 0, SOCK_FLAGS_REUSE_EP)); +} + +/* ENETUNREACH not testable in given loopback setup */ +/* ETIMEDOUT doesn't work because lwIP's connect doesn't timeout */ + +static void test_tcp_connect4__success_without_port(void) +{ + const sock_tcp_ep_t remote = { .addr = { .ipv4_u32 = HTONL(_TEST_ADDR4_REMOTE) }, + .family = AF_INET, + .port = _TEST_PORT_REMOTE, + .netif = _TEST_NETIF }; + msg_t msg = { .type = _SERVER_MSG_START }; + sock_tcp_ep_t ep; + + _server_addr.family = AF_INET; + _server_addr.port = _TEST_PORT_REMOTE; + _server_addr.netif = SOCK_ADDR_ANY_NETIF; + + msg_send(&msg, _server); /* start server on _TEST_PORT_REMOTE */ + + assert(0 == sock_tcp_connect(&_sock, &remote, 0, SOCK_FLAGS_REUSE_EP)); + assert(0 == sock_tcp_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); + assert(_TEST_PORT_REMOTE == ep.port); +} +static void test_tcp_connect4__success_local_port(void) +{ + const sock_tcp_ep_t remote = { .addr = { .ipv4_u32 = HTONL(_TEST_ADDR4_REMOTE) }, + .family = AF_INET, + .port = _TEST_PORT_REMOTE, + .netif = SOCK_ADDR_ANY_NETIF }; + msg_t msg = { .type = _SERVER_MSG_START }; + static const uint16_t local_port = _TEST_PORT_LOCAL; + sock_tcp_ep_t ep; + + _server_addr.family = AF_INET; + _server_addr.port = _TEST_PORT_REMOTE; + _server_addr.netif = SOCK_ADDR_ANY_NETIF; + + msg_send(&msg, _server); /* start server on _TEST_PORT_REMOTE */ + + assert(0 == sock_tcp_connect(&_sock, &remote, local_port, SOCK_FLAGS_REUSE_EP)); + assert(0 == sock_tcp_get_local(&_sock, &ep)); + assert(AF_INET == ep.family); + assert(_TEST_PORT_LOCAL == ep.port); + assert(0 == sock_tcp_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); + assert(_TEST_PORT_REMOTE == ep.port); +} + +#ifdef SO_REUSE +static void test_tcp_listen4__EADDRINUSE(void) +{ + const sock_tcp_ep_t local = { .addr = { .ipv4_u32 = HTONL(_TEST_ADDR4_LOCAL) }, + .family = AF_INET, + .port = _TEST_PORT_LOCAL, + .netif = SOCK_ADDR_ANY_NETIF }; + msg_t msg = { .type = _SERVER_MSG_START }; + + _server_addr.family = AF_INET; + _server_addr.port = _TEST_PORT_LOCAL; + _server_addr.netif = SOCK_ADDR_ANY_NETIF; + + msg_send(&msg, _server); /* start server on _TEST_PORT_LOCAL */ + + assert(-EADDRINUSE == sock_tcp_listen(&_queue, &local, _queue_array, + _QUEUE_SIZE, 0)); +} +#endif + +static void test_tcp_listen4__EAFNOSUPPORT(void) +{ + const sock_tcp_ep_t local = { .addr = { .ipv4_u32 = HTONL(_TEST_ADDR4_LOCAL) }, + .port = _TEST_PORT_LOCAL, + .netif = SOCK_ADDR_ANY_NETIF }; + + assert(-EAFNOSUPPORT == sock_tcp_listen(&_queue, &local, _queue_array, + _QUEUE_SIZE, 0)); +} + +static void test_tcp_listen4__EINVAL(void) +{ + const sock_tcp_ep_t local = { .addr = { .ipv4_u32 = HTONL(_TEST_ADDR4_LOCAL) }, + .family = AF_INET, + .port = _TEST_PORT_LOCAL, + .netif = (_TEST_NETIF + 1) }; + + assert(-EINVAL == sock_tcp_listen(&_queue, &local, _queue_array, + _QUEUE_SIZE, 0)); +} + +static void test_tcp_listen4__success_any_netif(void) +{ + const sock_tcp_ep_t local = { .addr = { .ipv4_u32 = HTONL(_TEST_ADDR4_LOCAL) }, + .family = AF_INET, + .port = _TEST_PORT_LOCAL, + .netif = SOCK_ADDR_ANY_NETIF }; + sock_tcp_ep_t ep; + + assert(0 == sock_tcp_listen(&_queue, &local, _queue_array, + _QUEUE_SIZE, 0)); + assert(0 == sock_tcp_queue_get_local(&_queue, &ep)); + assert(AF_INET == ep.family); + assert(HTONL(_TEST_ADDR4_LOCAL) == ep.addr.ipv4_u32); + assert(SOCK_ADDR_ANY_NETIF == ep.netif); + assert(_TEST_PORT_LOCAL == ep.port); +} + +static void test_tcp_listen4__success_spec_netif(void) +{ + static const sock_tcp_ep_t local = { .family = AF_INET, + .port = _TEST_PORT_LOCAL, + .netif = _TEST_NETIF }; + sock_tcp_ep_t ep; + + assert(0 == sock_tcp_listen(&_queue, &local, _queue_array, + _QUEUE_SIZE, 0)); + assert(0 == sock_tcp_queue_get_local(&_queue, &ep)); + assert(AF_INET == ep.family); + assert(_TEST_NETIF == ep.netif); + assert(_TEST_PORT_LOCAL == ep.port); +} + +/* ECONNABORTED can't be tested in this setup */ + +static void test_tcp_accept4__EAGAIN(void) +{ + static const sock_tcp_ep_t local = { .family = AF_INET, + .port = _TEST_PORT_LOCAL }; + sock_tcp_t *sock; + + assert(0 == sock_tcp_listen(&_queue, &local, _queue_array, + _QUEUE_SIZE, SOCK_FLAGS_REUSE_EP)); + assert(-EAGAIN == sock_tcp_accept(&_queue, &sock, 0)); +} + +static void test_tcp_accept4__EINVAL(void) +{ + sock_tcp_t *sock; + + assert(-EINVAL == sock_tcp_accept(&_queue, &sock, SOCK_NO_TIMEOUT)); +} + +static void test_tcp_accept4__ETIMEDOUT(void) +{ + static const sock_tcp_ep_t local = { .family = AF_INET, + .port = _TEST_PORT_LOCAL }; + sock_tcp_t *sock; + + assert(0 == sock_tcp_listen(&_queue, &local, _queue_array, + _QUEUE_SIZE, SOCK_FLAGS_REUSE_EP)); + puts(" * Calling sock_tcp_accept()"); + assert(-ETIMEDOUT == sock_tcp_accept(&_queue, &sock, _TEST_TIMEOUT)); + printf(" * (timed out with timeout %u)\n", _TEST_TIMEOUT); +} + +static void test_tcp_accept4__success(void) +{ + static const sock_tcp_ep_t local = { .family = AF_INET, + .port = _TEST_PORT_LOCAL }; + msg_t msg = { .type = _CLIENT_MSG_START, + .content = { .value = _TEST_PORT_REMOTE } }; + sock_tcp_ep_t ep; + sock_tcp_t *sock; + + _server_addr.addr.ipv4_u32 = HTONL(_TEST_ADDR4_REMOTE); /* loopback */ + _server_addr.family = AF_INET; + _server_addr.port = _TEST_PORT_LOCAL; + _server_addr.netif = SOCK_ADDR_ANY_NETIF; + + assert(0 == sock_tcp_listen(&_queue, &local, _queue_array, + _QUEUE_SIZE, 0)); + msg_send(&msg, _client); /* start client on _TEST_PORT_REMOTE, connecting + * to _TEST_PORT_LOCAL */ + assert(0 == sock_tcp_accept(&_queue, &sock, SOCK_NO_TIMEOUT)); + assert(0 == sock_tcp_get_local(sock, &ep)); + assert(AF_INET == ep.family); + assert(_TEST_PORT_LOCAL == ep.port); + assert(0 == sock_tcp_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); + assert(_TEST_PORT_REMOTE == ep.port); +} + +/* ECONNABORTED can't be tested in this setup */ + +static void test_tcp_read4__EAGAIN(void) +{ + const sock_tcp_ep_t remote = { .addr = { .ipv4_u32 = HTONL(_TEST_ADDR4_REMOTE) }, + .family = AF_INET, + .port = _TEST_PORT_REMOTE, + .netif = SOCK_ADDR_ANY_NETIF }; + msg_t msg = { .type = _SERVER_MSG_START }; + + _server_addr.family = AF_INET; + _server_addr.port = _TEST_PORT_REMOTE; + _server_addr.netif = SOCK_ADDR_ANY_NETIF; + + msg_send(&msg, _server); /* start server on _TEST_PORT_LOCAL */ + msg.type = _SERVER_MSG_ACCEPT; + msg_send(&msg, _server); /* let server accept */ + + assert(0 == sock_tcp_connect(&_sock, &remote, 0, SOCK_FLAGS_REUSE_EP)); + assert(-EAGAIN == sock_tcp_read(&_sock, _test_buffer, sizeof(_test_buffer), 0)); +} + +static void test_tcp_read4__ECONNRESET(void) +{ + const sock_tcp_ep_t remote = { .addr = { .ipv4_u32 = HTONL(_TEST_ADDR4_REMOTE) }, + .family = AF_INET, + .port = _TEST_PORT_REMOTE, + .netif = SOCK_ADDR_ANY_NETIF }; + msg_t msg = { .type = _SERVER_MSG_START }; + + _server_addr.family = AF_INET; + _server_addr.port = _TEST_PORT_REMOTE; + _server_addr.netif = SOCK_ADDR_ANY_NETIF; + + msg_send(&msg, _server); /* start server on _TEST_PORT_LOCAL */ + msg.type = _SERVER_MSG_ACCEPT; + msg_send(&msg, _server); /* let server accept */ + + assert(0 == sock_tcp_connect(&_sock, &remote, 0, SOCK_FLAGS_REUSE_EP)); + msg.type = _SERVER_MSG_CLOSE; + msg_send(&msg, _server); /* close connection at server side */ + assert(-ECONNRESET == sock_tcp_read(&_sock, _test_buffer, + sizeof(_test_buffer), + SOCK_NO_TIMEOUT)); +} + +static void test_tcp_read4__ENOTCONN(void) +{ + assert(-ENOTCONN == sock_tcp_read(&_sock, _test_buffer, + sizeof(_test_buffer), SOCK_NO_TIMEOUT)); +} + +static void test_tcp_read4__ETIMEDOUT(void) +{ + const sock_tcp_ep_t remote = { .addr = { .ipv4_u32 = HTONL(_TEST_ADDR4_REMOTE) }, + .family = AF_INET, + .port = _TEST_PORT_REMOTE, + .netif = SOCK_ADDR_ANY_NETIF }; + msg_t msg = { .type = _SERVER_MSG_START }; + + _server_addr.family = AF_INET; + _server_addr.port = _TEST_PORT_REMOTE; + _server_addr.netif = SOCK_ADDR_ANY_NETIF; + + msg_send(&msg, _server); /* start server on _TEST_PORT_LOCAL */ + msg.type = _SERVER_MSG_ACCEPT; + msg_send(&msg, _server); /* let server accept */ + + assert(0 == sock_tcp_connect(&_sock, &remote, 0, SOCK_FLAGS_REUSE_EP)); + puts(" * Calling sock_tcp_read()"); + assert(-ETIMEDOUT == sock_tcp_read(&_sock, _test_buffer, sizeof(_test_buffer), + _TEST_TIMEOUT)); + printf(" * (timed out with timeout %u)\n", _TEST_TIMEOUT); +} + +static void test_tcp_read4__success(void) +{ + const sock_tcp_ep_t remote = { .addr = { .ipv4_u32 = HTONL(_TEST_ADDR4_REMOTE) }, + .family = AF_INET, + .port = _TEST_PORT_REMOTE, + .netif = SOCK_ADDR_ANY_NETIF }; + msg_t msg = { .type = _SERVER_MSG_START }; + static const struct iovec exp_data = { .iov_base = "Hello!", + .iov_len = sizeof("Hello!") }; + + _server_addr.family = AF_INET; + _server_addr.port = _TEST_PORT_REMOTE; + _server_addr.netif = SOCK_ADDR_ANY_NETIF; + + msg_send(&msg, _server); /* start server on _TEST_PORT_LOCAL */ + msg.type = _SERVER_MSG_ACCEPT; + msg_send(&msg, _server); /* let server accept */ + + assert(0 == sock_tcp_connect(&_sock, &remote, 0, SOCK_FLAGS_REUSE_EP)); + msg.type = _SERVER_MSG_WRITE; + msg.content.ptr = (void *)&exp_data; + msg_send(&msg, _server); /* write expected data at server */ + assert(((ssize_t)exp_data.iov_len) == sock_tcp_read(&_sock, _test_buffer, + sizeof(_test_buffer), + SOCK_NO_TIMEOUT)); + assert(memcmp(exp_data.iov_base, _test_buffer, exp_data.iov_len) == 0); +} + +static void test_tcp_read4__success_with_timeout(void) +{ + const sock_tcp_ep_t remote = { .addr = { .ipv4_u32 = HTONL(_TEST_ADDR4_REMOTE) }, + .family = AF_INET, + .port = _TEST_PORT_REMOTE, + .netif = SOCK_ADDR_ANY_NETIF }; + msg_t msg = { .type = _SERVER_MSG_START }; + static const struct iovec exp_data = { .iov_base = "Hello!", + .iov_len = sizeof("Hello!") }; + + _server_addr.family = AF_INET; + _server_addr.port = _TEST_PORT_REMOTE; + _server_addr.netif = SOCK_ADDR_ANY_NETIF; + + msg_send(&msg, _server); /* start server on _TEST_PORT_LOCAL */ + msg.type = _SERVER_MSG_ACCEPT; + msg_send(&msg, _server); /* let server accept */ + + assert(0 == sock_tcp_connect(&_sock, &remote, 0, SOCK_FLAGS_REUSE_EP)); + msg.type = _SERVER_MSG_WRITE; + msg.content.ptr = (void *)&exp_data; + msg_send(&msg, _server); /* write expected data at server */ + assert(((ssize_t)exp_data.iov_len) == sock_tcp_read(&_sock, _test_buffer, + sizeof(_test_buffer), + _TEST_TIMEOUT)); + assert(memcmp(exp_data.iov_base, _test_buffer, exp_data.iov_len) == 0); +} + +static void test_tcp_read4__success_non_blocking(void) +{ + const sock_tcp_ep_t remote = { .addr = { .ipv4_u32 = HTONL(_TEST_ADDR4_REMOTE) }, + .family = AF_INET, + .port = _TEST_PORT_REMOTE, + .netif = SOCK_ADDR_ANY_NETIF }; + msg_t msg = { .type = _SERVER_MSG_START }; + static const struct iovec exp_data = { .iov_base = "Hello!", + .iov_len = sizeof("Hello!") }; + + _server_addr.family = AF_INET; + _server_addr.port = _TEST_PORT_REMOTE; + _server_addr.netif = SOCK_ADDR_ANY_NETIF; + + msg_send(&msg, _server); /* start server on _TEST_PORT_LOCAL */ + msg.type = _SERVER_MSG_ACCEPT; + msg_send(&msg, _server); /* let server accept */ + + assert(0 == sock_tcp_connect(&_sock, &remote, 0, SOCK_FLAGS_REUSE_EP)); + msg.type = _SERVER_MSG_WRITE; + msg.content.ptr = (void *)&exp_data; + msg_send(&msg, _server); /* write expected data at server */ + assert(((ssize_t)exp_data.iov_len) == sock_tcp_read(&_sock, _test_buffer, + sizeof(_test_buffer), + 0)); + assert(memcmp(exp_data.iov_base, _test_buffer, exp_data.iov_len) == 0); +} + +/* ENOTCONN not applicable since lwIP always tries to send */ + +static void test_tcp_write4__ENOTCONN(void) +{ + assert(-ENOTCONN == sock_tcp_write(&_sock, "Hello!", sizeof("Hello!"))); +} + +static void test_tcp_write4__success(void) +{ + const sock_tcp_ep_t remote = { .addr = { .ipv4_u32 = HTONL(_TEST_ADDR4_REMOTE) }, + .family = AF_INET, + .port = _TEST_PORT_REMOTE, + .netif = SOCK_ADDR_ANY_NETIF }; + msg_t msg = { .type = _SERVER_MSG_START }; + static const struct iovec exp_data = { .iov_base = "Hello!", + .iov_len = sizeof("Hello!") }; + + _server_addr.family = AF_INET; + _server_addr.port = _TEST_PORT_REMOTE; + _server_addr.netif = SOCK_ADDR_ANY_NETIF; + + msg_send(&msg, _server); /* start server on _TEST_PORT_LOCAL */ + msg.type = _SERVER_MSG_ACCEPT; + msg_send(&msg, _server); /* let server accept */ + + assert(0 == sock_tcp_connect(&_sock, &remote, 0, SOCK_FLAGS_REUSE_EP)); + msg.type = _SERVER_MSG_READ; + msg.content.ptr = (void *)&exp_data; + msg_send(&msg, _server); /* write expected data at server */ + assert(((ssize_t)exp_data.iov_len) == sock_tcp_write(&_sock, "Hello!", + sizeof("Hello!"))); + assert(memcmp(exp_data.iov_base, _test_buffer, exp_data.iov_len) == 0); + xtimer_usleep(5000); /* wait for server */ +} +#endif /* MODULE_LWIP_IPV4 */ + +#ifdef MODULE_LWIP_IPV6 +#ifdef SO_REUSE +static void test_tcp_connect6__EADDRINUSE(void) +{ + static const sock_tcp_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR6_REMOTE }, + .family = AF_INET6, + .port = _TEST_PORT_REMOTE, + .netif = SOCK_ADDR_ANY_NETIF }; + msg_t msg = { .type = _SERVER_MSG_START }; + static const uint16_t local_port = _TEST_PORT_REMOTE; + + _server_addr.family = AF_INET6; + _server_addr.port = _TEST_PORT_REMOTE; + _server_addr.netif = SOCK_ADDR_ANY_NETIF; + + msg_send(&msg, _server); /* start server on _TEST_PORT_REMOTE */ + + assert(-EADDRINUSE == sock_tcp_connect(&_sock, &remote, local_port, 0)); +} +#endif + +static void test_tcp_connect6__EAFNOSUPPORT(void) +{ + static const sock_tcp_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR6_REMOTE }, + .port = _TEST_PORT_REMOTE, + .netif = SOCK_ADDR_ANY_NETIF }; + + assert(-EAFNOSUPPORT == sock_tcp_connect(&_sock, &remote, 0, + SOCK_FLAGS_REUSE_EP)); +} + +/* ECONNREFUSED does not apply for lwIP; netconn_connect does not wait for + * connection build-up */ + +static void test_tcp_connect6__EINVAL_addr(void) +{ + static const sock_tcp_ep_t remote = { .family = AF_INET6, + .port = _TEST_PORT_REMOTE, + .netif = SOCK_ADDR_ANY_NETIF }; + + assert(-EINVAL == sock_tcp_connect(&_sock, &remote, 0, SOCK_FLAGS_REUSE_EP)); +} + +static void test_tcp_connect6__EINVAL_netif(void) +{ + static const sock_tcp_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR6_REMOTE }, + .family = AF_INET6, + .port = _TEST_PORT_REMOTE, + .netif = (_TEST_NETIF + 1) }; + + assert(-EINVAL == sock_tcp_connect(&_sock, &remote, 0, SOCK_FLAGS_REUSE_EP)); +} + +/* ENETUNREACH not testable in given loopback setup */ +/* ETIMEDOUT doesn't work because lwIP's connect doesn't timeout */ + +static void test_tcp_connect6__success_without_port(void) +{ + static const ipv6_addr_t remote_addr = { .u8 = _TEST_ADDR6_REMOTE }; + static const sock_tcp_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR6_REMOTE }, + .family = AF_INET6, + .port = _TEST_PORT_REMOTE, + .netif = _TEST_NETIF }; + msg_t msg = { .type = _SERVER_MSG_START }; + sock_tcp_ep_t ep; + + _server_addr.family = AF_INET6; + _server_addr.port = _TEST_PORT_REMOTE; + _server_addr.netif = SOCK_ADDR_ANY_NETIF; + + msg_send(&msg, _server); /* start server on _TEST_PORT_REMOTE */ + + assert(0 == sock_tcp_connect(&_sock, &remote, 0, SOCK_FLAGS_REUSE_EP)); + assert(0 == sock_tcp_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); + assert(_TEST_PORT_REMOTE == ep.port); +} +static void test_tcp_connect6__success_local_port(void) +{ + static const ipv6_addr_t remote_addr = { .u8 = _TEST_ADDR6_REMOTE }; + static const sock_tcp_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR6_REMOTE }, + .family = AF_INET6, + .port = _TEST_PORT_REMOTE, + .netif = SOCK_ADDR_ANY_NETIF }; + msg_t msg = { .type = _SERVER_MSG_START }; + static const uint16_t local_port = _TEST_PORT_LOCAL; + sock_tcp_ep_t ep; + + _server_addr.family = AF_INET6; + _server_addr.port = _TEST_PORT_REMOTE; + _server_addr.netif = SOCK_ADDR_ANY_NETIF; + + msg_send(&msg, _server); /* start server on _TEST_PORT_REMOTE */ + + assert(0 == sock_tcp_connect(&_sock, &remote, local_port, SOCK_FLAGS_REUSE_EP)); + assert(0 == sock_tcp_get_local(&_sock, &ep)); + assert(AF_INET6 == ep.family); + assert(_TEST_PORT_LOCAL == ep.port); + assert(0 == sock_tcp_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); + assert(_TEST_PORT_REMOTE == ep.port); +} + +#ifdef SO_REUSE +static void test_tcp_listen6__EADDRINUSE(void) +{ + static const sock_tcp_ep_t local = { .addr = { .ipv6 = _TEST_ADDR6_LOCAL }, + .family = AF_INET6, + .port = _TEST_PORT_LOCAL, + .netif = SOCK_ADDR_ANY_NETIF }; + msg_t msg = { .type = _SERVER_MSG_START }; + + _server_addr.family = AF_INET6; + _server_addr.port = _TEST_PORT_LOCAL; + _server_addr.netif = SOCK_ADDR_ANY_NETIF; + + msg_send(&msg, _server); /* start server on _TEST_PORT_LOCAL */ + + assert(-EADDRINUSE == sock_tcp_listen(&_queue, &local, _queue_array, + _QUEUE_SIZE, 0)); +} +#endif + +static void test_tcp_listen6__EAFNOSUPPORT(void) +{ + static const sock_tcp_ep_t local = { .addr = { .ipv6 = _TEST_ADDR6_LOCAL }, + .port = _TEST_PORT_LOCAL, + .netif = SOCK_ADDR_ANY_NETIF }; + + assert(-EAFNOSUPPORT == sock_tcp_listen(&_queue, &local, _queue_array, + _QUEUE_SIZE, 0)); +} + +static void test_tcp_listen6__EINVAL(void) +{ + static const sock_tcp_ep_t local = { .addr = { .ipv6 = _TEST_ADDR6_LOCAL }, + .family = AF_INET6, + .port = _TEST_PORT_LOCAL, + .netif = (_TEST_NETIF + 1) }; + + assert(-EINVAL == sock_tcp_listen(&_queue, &local, _queue_array, + _QUEUE_SIZE, 0)); +} + +static void test_tcp_listen6__success_any_netif(void) +{ + static const ipv6_addr_t local_addr = { .u8 = _TEST_ADDR6_LOCAL }; + static const sock_tcp_ep_t local = { .addr = { .ipv6 = _TEST_ADDR6_LOCAL }, + .family = AF_INET6, + .port = _TEST_PORT_LOCAL, + .netif = SOCK_ADDR_ANY_NETIF }; + sock_tcp_ep_t ep; + + assert(0 == sock_tcp_listen(&_queue, &local, _queue_array, + _QUEUE_SIZE, 0)); + assert(0 == sock_tcp_queue_get_local(&_queue, &ep)); + assert(AF_INET6 == ep.family); + assert(memcmp(&local_addr, &ep.addr.ipv6, sizeof(ipv6_addr_t)) == 0); + assert(SOCK_ADDR_ANY_NETIF == ep.netif); + assert(_TEST_PORT_LOCAL == ep.port); +} + +static void test_tcp_listen6__success_spec_netif(void) +{ + static const sock_tcp_ep_t local = { .family = AF_INET6, + .port = _TEST_PORT_LOCAL, + .netif = _TEST_NETIF }; + sock_tcp_ep_t ep; + + assert(0 == sock_tcp_listen(&_queue, &local, _queue_array, + _QUEUE_SIZE, 0)); + assert(0 == sock_tcp_queue_get_local(&_queue, &ep)); + assert(AF_INET6 == ep.family); + assert(_TEST_NETIF == ep.netif); + assert(_TEST_PORT_LOCAL == ep.port); +} + +/* ECONNABORTED can't be tested in this setup */ + +static void test_tcp_accept6__EAGAIN(void) +{ + static const sock_tcp_ep_t local = { .family = AF_INET6, + .port = _TEST_PORT_LOCAL }; + sock_tcp_t *sock; + + assert(0 == sock_tcp_listen(&_queue, &local, _queue_array, + _QUEUE_SIZE, 0)); + assert(-EAGAIN == sock_tcp_accept(&_queue, &sock, 0)); +} + +static void test_tcp_accept6__EINVAL(void) +{ + sock_tcp_t *sock; + + assert(-EINVAL == sock_tcp_accept(&_queue, &sock, SOCK_NO_TIMEOUT)); +} + +static void test_tcp_accept6__ETIMEDOUT(void) +{ + static const sock_tcp_ep_t local = { .family = AF_INET6, + .port = _TEST_PORT_LOCAL }; + sock_tcp_t *sock; + + assert(0 == sock_tcp_listen(&_queue, &local, _queue_array, + _QUEUE_SIZE, 0)); + puts(" * Calling sock_tcp_accept()"); + assert(-ETIMEDOUT == sock_tcp_accept(&_queue, &sock, _TEST_TIMEOUT)); + printf(" * (timed out with timeout %u)\n", _TEST_TIMEOUT); +} + +static void test_tcp_accept6__success(void) +{ + static const ipv6_addr_t remote_addr = { .u8 = _TEST_ADDR6_REMOTE }; + static const sock_tcp_ep_t local = { .family = AF_INET6, + .port = _TEST_PORT_LOCAL }; + msg_t msg = { .type = _CLIENT_MSG_START, + .content = { .value = _TEST_PORT_REMOTE } }; + sock_tcp_ep_t ep; + sock_tcp_t *sock; + + _server_addr.addr.ipv6[15] = 1; /* make unspecified address to loopback */ + _server_addr.family = AF_INET6; + _server_addr.port = _TEST_PORT_LOCAL; + _server_addr.netif = SOCK_ADDR_ANY_NETIF; + + assert(0 == sock_tcp_listen(&_queue, &local, _queue_array, + _QUEUE_SIZE, 0)); + msg_send(&msg, _client); /* start client on _TEST_PORT_REMOTE, connecting + * to _TEST_PORT_LOCAL */ + assert(0 == sock_tcp_accept(&_queue, &sock, SOCK_NO_TIMEOUT)); + assert(0 == sock_tcp_get_local(sock, &ep)); + assert(AF_INET6 == ep.family); + assert(_TEST_PORT_LOCAL == ep.port); + assert(0 == sock_tcp_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); + assert(_TEST_PORT_REMOTE == ep.port); +} + +/* ECONNABORTED can't be tested in this setup */ + +static void test_tcp_read6__EAGAIN(void) +{ + static const sock_tcp_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR6_REMOTE }, + .family = AF_INET6, + .port = _TEST_PORT_REMOTE, + .netif = SOCK_ADDR_ANY_NETIF }; + msg_t msg = { .type = _SERVER_MSG_START }; + + _server_addr.family = AF_INET6; + _server_addr.port = _TEST_PORT_REMOTE; + _server_addr.netif = SOCK_ADDR_ANY_NETIF; + + msg_send(&msg, _server); /* start server on _TEST_PORT_LOCAL */ + msg.type = _SERVER_MSG_ACCEPT; + msg_send(&msg, _server); /* let server accept */ + + assert(0 == sock_tcp_connect(&_sock, &remote, 0, SOCK_FLAGS_REUSE_EP)); + assert(-EAGAIN == sock_tcp_read(&_sock, _test_buffer, sizeof(_test_buffer), 0)); +} + +static void test_tcp_read6__ECONNRESET(void) +{ + static const sock_tcp_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR6_REMOTE }, + .family = AF_INET6, + .port = _TEST_PORT_REMOTE, + .netif = SOCK_ADDR_ANY_NETIF }; + msg_t msg = { .type = _SERVER_MSG_START }; + + _server_addr.family = AF_INET6; + _server_addr.port = _TEST_PORT_REMOTE; + _server_addr.netif = SOCK_ADDR_ANY_NETIF; + + msg_send(&msg, _server); /* start server on _TEST_PORT_LOCAL */ + msg.type = _SERVER_MSG_ACCEPT; + msg_send(&msg, _server); /* let server accept */ + + assert(0 == sock_tcp_connect(&_sock, &remote, 0, SOCK_FLAGS_REUSE_EP)); + msg.type = _SERVER_MSG_CLOSE; + msg_send(&msg, _server); /* close connection at server side */ + assert(-ECONNRESET == sock_tcp_read(&_sock, _test_buffer, + sizeof(_test_buffer), SOCK_NO_TIMEOUT)); +} + +static void test_tcp_read6__ENOTCONN(void) +{ + assert(-ENOTCONN == sock_tcp_read(&_sock, _test_buffer, + sizeof(_test_buffer), SOCK_NO_TIMEOUT)); +} + +static void test_tcp_read6__ETIMEDOUT(void) +{ + static const sock_tcp_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR6_REMOTE }, + .family = AF_INET6, + .port = _TEST_PORT_REMOTE, + .netif = SOCK_ADDR_ANY_NETIF }; + msg_t msg = { .type = _SERVER_MSG_START }; + + _server_addr.family = AF_INET6; + _server_addr.port = _TEST_PORT_REMOTE; + _server_addr.netif = SOCK_ADDR_ANY_NETIF; + + msg_send(&msg, _server); /* start server on _TEST_PORT_LOCAL */ + msg.type = _SERVER_MSG_ACCEPT; + msg_send(&msg, _server); /* let server accept */ + + assert(0 == sock_tcp_connect(&_sock, &remote, 0, SOCK_FLAGS_REUSE_EP)); + puts(" * Calling sock_tcp_read()"); + assert(-ETIMEDOUT == sock_tcp_read(&_sock, _test_buffer, + sizeof(_test_buffer), _TEST_TIMEOUT)); + printf(" * (timed out with timeout %u)\n", _TEST_TIMEOUT); +} +static void test_tcp_read6__success(void) +{ + static const sock_tcp_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR6_REMOTE }, + .family = AF_INET6, + .port = _TEST_PORT_REMOTE, + .netif = SOCK_ADDR_ANY_NETIF }; + msg_t msg = { .type = _SERVER_MSG_START }; + static const struct iovec exp_data = { .iov_base = "Hello!", + .iov_len = sizeof("Hello!") }; + + _server_addr.family = AF_INET6; + _server_addr.port = _TEST_PORT_REMOTE; + _server_addr.netif = SOCK_ADDR_ANY_NETIF; + + msg_send(&msg, _server); /* start server on _TEST_PORT_LOCAL */ + msg.type = _SERVER_MSG_ACCEPT; + msg_send(&msg, _server); /* let server accept */ + + assert(0 == sock_tcp_connect(&_sock, &remote, 0, SOCK_FLAGS_REUSE_EP)); + msg.type = _SERVER_MSG_WRITE; + msg.content.ptr = (void *)&exp_data; + msg_send(&msg, _server); /* write expected data at server */ + assert(((ssize_t)exp_data.iov_len) == sock_tcp_read(&_sock, _test_buffer, + sizeof(_test_buffer), + SOCK_NO_TIMEOUT)); + assert(memcmp(exp_data.iov_base, _test_buffer, exp_data.iov_len) == 0); +} + +static void test_tcp_read6__success_with_timeout(void) +{ + static const sock_tcp_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR6_REMOTE }, + .family = AF_INET6, + .port = _TEST_PORT_REMOTE, + .netif = SOCK_ADDR_ANY_NETIF }; + msg_t msg = { .type = _SERVER_MSG_START }; + static const struct iovec exp_data = { .iov_base = "Hello!", + .iov_len = sizeof("Hello!") }; + + _server_addr.family = AF_INET6; + _server_addr.port = _TEST_PORT_REMOTE; + _server_addr.netif = SOCK_ADDR_ANY_NETIF; + + msg_send(&msg, _server); /* start server on _TEST_PORT_LOCAL */ + msg.type = _SERVER_MSG_ACCEPT; + msg_send(&msg, _server); /* let server accept */ + + assert(0 == sock_tcp_connect(&_sock, &remote, 0, SOCK_FLAGS_REUSE_EP)); + msg.type = _SERVER_MSG_WRITE; + msg.content.ptr = (void *)&exp_data; + msg_send(&msg, _server); /* write expected data at server */ + assert(((ssize_t)exp_data.iov_len) == sock_tcp_read(&_sock, _test_buffer, + sizeof(_test_buffer), + _TEST_TIMEOUT)); + assert(memcmp(exp_data.iov_base, _test_buffer, exp_data.iov_len) == 0); +} + +static void test_tcp_read6__success_non_blocking(void) +{ + static const sock_tcp_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR6_REMOTE }, + .family = AF_INET6, + .port = _TEST_PORT_REMOTE, + .netif = SOCK_ADDR_ANY_NETIF }; + msg_t msg = { .type = _SERVER_MSG_START }; + static const struct iovec exp_data = { .iov_base = "Hello!", + .iov_len = sizeof("Hello!") }; + + _server_addr.family = AF_INET6; + _server_addr.port = _TEST_PORT_REMOTE; + _server_addr.netif = SOCK_ADDR_ANY_NETIF; + + msg_send(&msg, _server); /* start server on _TEST_PORT_LOCAL */ + msg.type = _SERVER_MSG_ACCEPT; + msg_send(&msg, _server); /* let server accept */ + + assert(0 == sock_tcp_connect(&_sock, &remote, 0, SOCK_FLAGS_REUSE_EP)); + msg.type = _SERVER_MSG_WRITE; + msg.content.ptr = (void *)&exp_data; + msg_send(&msg, _server); /* write expected data at server */ + assert(((ssize_t)exp_data.iov_len) == sock_tcp_read(&_sock, _test_buffer, + sizeof(_test_buffer), + 0)); + assert(memcmp(exp_data.iov_base, _test_buffer, exp_data.iov_len) == 0); +} + +/* ENOTCONN not applicable since lwIP always tries to send */ + +static void test_tcp_write6__ENOTCONN(void) +{ + assert(-ENOTCONN == sock_tcp_write(&_sock, "Hello!", sizeof("Hello!"))); +} + +static void test_tcp_write6__success(void) +{ + static const sock_tcp_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR6_REMOTE }, + .family = AF_INET6, + .port = _TEST_PORT_REMOTE, + .netif = SOCK_ADDR_ANY_NETIF }; + msg_t msg = { .type = _SERVER_MSG_START }; + static const struct iovec exp_data = { .iov_base = "Hello!", + .iov_len = sizeof("Hello!") }; + + _server_addr.family = AF_INET6; + _server_addr.port = _TEST_PORT_REMOTE; + _server_addr.netif = SOCK_ADDR_ANY_NETIF; + + msg_send(&msg, _server); /* start server on _TEST_PORT_LOCAL */ + msg.type = _SERVER_MSG_ACCEPT; + msg_send(&msg, _server); /* let server accept */ + + assert(0 == sock_tcp_connect(&_sock, &remote, 0, SOCK_FLAGS_REUSE_EP)); + msg.type = _SERVER_MSG_READ; + msg.content.ptr = (void *)&exp_data; + msg_send(&msg, _server); /* write expected data at server */ + assert(((ssize_t)exp_data.iov_len) == sock_tcp_write(&_sock, "Hello!", + sizeof("Hello!"))); + assert(memcmp(exp_data.iov_base, _test_buffer, exp_data.iov_len) == 0); + xtimer_usleep(5000); /* wait for server */ +} +#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); + xtimer_init(); + _net_init(); + assert(0 < thread_create(_client_stack, sizeof(_client_stack), + THREAD_PRIORITY_MAIN - 1, THREAD_CREATE_STACKTEST, + _client_func, NULL, "tcp_client")); + assert(0 < thread_create(_server_stack, sizeof(_server_stack), + THREAD_PRIORITY_MAIN - 2, THREAD_CREATE_STACKTEST, + _server_func, NULL, "tcp_server")); + tear_down(); +#ifdef MODULE_LWIP_IPV4 +#ifdef SO_REUSE + CALL(test_tcp_connect4__EADDRINUSE()); +#endif + CALL(test_tcp_connect4__EAFNOSUPPORT()); + /* ECONNREFUSED does not apply for lwIP; netconn_connect does not wait for + * connection build-up */ + CALL(test_tcp_connect4__EINVAL_addr()); + CALL(test_tcp_connect4__EINVAL_netif()); + /* ENETUNREACH not testable in given loopback setup */ + /* ETIMEDOUT doesn't work because lwIP's connect doesn't timeout */ + CALL(test_tcp_connect4__success_without_port()); + CALL(test_tcp_connect4__success_local_port()); +#ifdef SO_REUSE + CALL(test_tcp_listen4__EADDRINUSE()); +#endif + CALL(test_tcp_listen4__EAFNOSUPPORT()); + CALL(test_tcp_listen4__EINVAL()); + CALL(test_tcp_listen4__success_any_netif()); + CALL(test_tcp_listen4__success_spec_netif()); + /* sock_tcp_disconnect() is tested in tear_down() */ + /* sock_tcp_stop_listen() is tested in tear_down() */ + /* sock_tcp_get_local() is tested in sock_tcp_connect() tests */ + /* sock_tcp_get_remote() is tested in sock_tcp_connect() tests */ + /* sock_tcp_queue_get_local() is tested in sock_tcp_listen() tests */ + /* ECONNABORTED can't be tested in this setup */ + CALL(test_tcp_accept4__EAGAIN()); + CALL(test_tcp_accept4__EINVAL()); + CALL(test_tcp_accept4__ETIMEDOUT()); + CALL(test_tcp_accept4__success()); + /* ECONNABORTED can't be tested in this setup */ + CALL(test_tcp_read4__EAGAIN()); + CALL(test_tcp_read4__ECONNRESET()); + CALL(test_tcp_read4__ENOTCONN()); + CALL(test_tcp_read4__ETIMEDOUT()); + CALL(test_tcp_read4__success()); + CALL(test_tcp_read4__success_with_timeout()); + CALL(test_tcp_read4__success_non_blocking()); + /* ECONNABORTED can't be tested in this setup */ + /* ENOTCONN not applicable since lwIP always tries to send */ + CALL(test_tcp_write4__ENOTCONN()); + CALL(test_tcp_write4__success()); +#endif /* MODULE_LWIP_IPV4 */ +#ifdef MODULE_LWIP_IPV6 +#ifdef SO_REUSE + CALL(test_tcp_connect6__EADDRINUSE()); +#endif + CALL(test_tcp_connect6__EAFNOSUPPORT()); + /* ECONNREFUSED does not apply for lwIP; netconn_connect does not wait for + * connection build-up */ + CALL(test_tcp_connect6__EINVAL_addr()); + CALL(test_tcp_connect6__EINVAL_netif()); + /* ENETUNREACH not testable in given loopback setup */ + /* ETIMEDOUT doesn't work because lwIP's connect doesn't timeout */ + CALL(test_tcp_connect6__success_without_port()); + CALL(test_tcp_connect6__success_local_port()); +#ifdef SO_REUSE + CALL(test_tcp_listen6__EADDRINUSE()); +#endif + CALL(test_tcp_listen6__EAFNOSUPPORT()); + CALL(test_tcp_listen6__EINVAL()); + CALL(test_tcp_listen6__success_any_netif()); + CALL(test_tcp_listen6__success_spec_netif()); + /* sock_tcp_disconnect() is tested in tear_down() */ + /* sock_tcp_stop_listen() is tested in tear_down() */ + /* sock_tcp_get_local() is tested in sock_tcp_connect() tests */ + /* sock_tcp_get_remote() is tested in sock_tcp_connect() tests */ + /* sock_tcp_queue_get_local() is tested in sock_tcp_listen() tests */ + /* ECONNABORTED can't be tested in this setup */ + CALL(test_tcp_accept6__EAGAIN()); + CALL(test_tcp_accept6__EINVAL()); + CALL(test_tcp_accept6__ETIMEDOUT()); + CALL(test_tcp_accept6__success()); + /* ECONNABORTED can't be tested in this setup */ + CALL(test_tcp_read6__EAGAIN()); + CALL(test_tcp_read6__ECONNRESET()); + CALL(test_tcp_read6__ENOTCONN()); + CALL(test_tcp_read6__ETIMEDOUT()); + CALL(test_tcp_read6__success()); + CALL(test_tcp_read6__success_with_timeout()); + CALL(test_tcp_read6__success_non_blocking()); + /* ECONNABORTED can't be tested in this setup */ + /* ENOTCONN not applicable since lwIP always tries to send */ + CALL(test_tcp_write6__ENOTCONN()); + CALL(test_tcp_write6__success()); +#endif /* MODULE_LWIP_IPV6 */ + + puts("ALL TESTS SUCCESSFUL"); + + return 0; +} + +static void *_server_func(void *arg) +{ + bool server_started = false; + sock_tcp_t *sock = NULL; + + (void)arg; + msg_init_queue(_server_msg_queue, _MSG_QUEUE_SIZE); + _server = sched_active_pid; + while (1) { + msg_t msg; + + msg_receive(&msg); + switch (msg.type) { + case _SERVER_MSG_START: + if (!server_started) { + assert(0 == sock_tcp_listen(&_server_queue, &_server_addr, + _server_queue_array, + _SERVER_QUEUE_SIZE, + SOCK_FLAGS_REUSE_EP)); + server_started = true; + } + break; + case _SERVER_MSG_ACCEPT: + if (server_started) { + assert(0 == sock_tcp_accept(&_server_queue, &sock, + SOCK_NO_TIMEOUT)); + } + break; + case _SERVER_MSG_READ: + if (sock != NULL) { + const struct iovec *exp = msg.content.ptr; + + assert(((ssize_t)exp->iov_len) == + sock_tcp_read(sock, _server_buf, sizeof(_server_buf), + SOCK_NO_TIMEOUT)); + assert(memcmp(exp->iov_base, _server_buf, exp->iov_len) == 0); + } + break; + case _SERVER_MSG_WRITE: + if (sock != NULL) { + const struct iovec *data = msg.content.ptr; + + assert(((ssize_t)data->iov_len) == + sock_tcp_write(sock, data->iov_base, data->iov_len)); + } + break; + case _SERVER_MSG_CLOSE: + if (sock != NULL) { + sock_tcp_disconnect(sock); + sock = NULL; + } + break; + case _SERVER_MSG_STOP: + if (server_started) { + sock_tcp_stop_listen(&_server_queue); + server_started = false; + /* sock_tcp_stop_listen is also supposed to close sock */ + sock = NULL; + } + break; + default: + break; + } + } + return NULL; +} + +static void *_client_func(void *arg) +{ + bool client_started = false; + + (void)arg; + msg_init_queue(_client_msg_queue, _MSG_QUEUE_SIZE); + _client = sched_active_pid; + while (1) { + msg_t msg; + + msg_receive(&msg); + switch (msg.type) { + case _CLIENT_MSG_START: + if (!client_started) { + const uint16_t local_port = (uint16_t)msg.content.value; + assert(0 == sock_tcp_connect(&_client_sock, &_server_addr, + local_port, SOCK_FLAGS_REUSE_EP)); + client_started = true; + } + break; + case _CLIENT_MSG_READ: + if (client_started) { + const struct iovec *exp = msg.content.ptr; + + assert(((ssize_t)exp->iov_len) == + sock_tcp_read(&_client_sock, _client_buf, + sizeof(_client_buf), SOCK_NO_TIMEOUT)); + assert(memcmp(exp->iov_base, _client_buf, exp->iov_len) == 0); + } + break; + case _CLIENT_MSG_WRITE: + if (client_started) { + const struct iovec *data = msg.content.ptr; + + assert(((ssize_t)data->iov_len) == + sock_tcp_write(&_client_sock, data->iov_base, + data->iov_len)); + } + break; + case _CLIENT_MSG_STOP: + if (client_started) { + sock_tcp_disconnect(&_client_sock); + memset(&_client_sock, 0, sizeof(sock_tcp_t)); + client_started = false; + } + break; + default: + break; + } + } + return NULL; +} diff --git a/tests/lwip_sock_tcp/stack.c b/tests/lwip_sock_tcp/stack.c new file mode 100644 index 0000000000000000000000000000000000000000..879f751b21f5d776e6aa73a1fb2d89ea845a307f --- /dev/null +++ b/tests/lwip_sock_tcp/stack.c @@ -0,0 +1,30 @@ +/* + * 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 "xtimer.h" + +#include "lwip.h" +#include "lwip/netif.h" + +#include "stack.h" + +void _net_init(void) +{ + xtimer_init(); + lwip_bootstrap(); +} + +/** @} */ diff --git a/tests/lwip_sock_tcp/stack.h b/tests/lwip_sock_tcp/stack.h new file mode 100644 index 0000000000000000000000000000000000000000..57221f65d6b97f92e29cde7779b2af4dcc2facf5 --- /dev/null +++ b/tests/lwip_sock_tcp/stack.h @@ -0,0 +1,37 @@ +/* + * 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_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initializes networking for tests + */ +void _net_init(void); + +#ifdef __cplusplus +} +#endif + +#endif /* STACK_H_ */ +/** @} */ diff --git a/tests/lwip_sock_tcp/tests/01-run.py b/tests/lwip_sock_tcp/tests/01-run.py new file mode 100755 index 0000000000000000000000000000000000000000..d024d6494fd34ab0793b702172a16a6d6c4f683a --- /dev/null +++ b/tests/lwip_sock_tcp/tests/01-run.py @@ -0,0 +1,141 @@ +#!/usr/bin/env python3 + +# 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. + +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 _reuse_tests(code): + return code & 1 + +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): + if _reuse_tests(code): + child.expect_exact("Calling test_tcp_connect4__EADDRINUSE()") + child.expect_exact("Calling test_tcp_connect4__EAFNOSUPPORT()") + child.expect_exact("Calling test_tcp_connect4__EINVAL_addr()") + child.expect_exact("Calling test_tcp_connect4__EINVAL_netif()") + child.expect_exact("Calling test_tcp_connect4__success_without_port()") + child.expect_exact("Calling test_tcp_connect4__success_local_port()") + if _reuse_tests(code): + child.expect_exact("Calling test_tcp_listen4__EADDRINUSE()") + child.expect_exact("Calling test_tcp_listen4__EAFNOSUPPORT()") + child.expect_exact("Calling test_tcp_listen4__EINVAL()") + child.expect_exact("Calling test_tcp_listen4__success_any_netif()") + child.expect_exact("Calling test_tcp_listen4__success_spec_netif()") + child.expect_exact("Calling test_tcp_accept4__EAGAIN()") + child.expect_exact("Calling test_tcp_accept4__EINVAL()") + child.expect_exact("Calling test_tcp_accept4__ETIMEDOUT()") + start = datetime.now() + child.expect_exact(" * Calling sock_tcp_accept()") + 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("Calling test_tcp_accept4__success()") + child.expect_exact("Calling test_tcp_read4__EAGAIN()") + child.expect_exact("Calling test_tcp_read4__ECONNRESET()") + child.expect_exact("Calling test_tcp_read4__ENOTCONN()") + child.expect_exact("Calling test_tcp_read4__ETIMEDOUT()") + start = datetime.now() + child.expect_exact(" * Calling sock_tcp_read()") + 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("Calling test_tcp_read4__success()") + child.expect_exact("Calling test_tcp_read4__success_with_timeout()") + child.expect_exact("Calling test_tcp_read4__success_non_blocking()") + child.expect_exact("Calling test_tcp_write4__ENOTCONN()") + child.expect_exact("Calling test_tcp_write4__success()") + if _ipv6_tests(code): + if _reuse_tests(code): + child.expect_exact("Calling test_tcp_connect6__EADDRINUSE()") + child.expect_exact("Calling test_tcp_connect6__EAFNOSUPPORT()") + child.expect_exact("Calling test_tcp_connect6__EINVAL_addr()") + child.expect_exact("Calling test_tcp_connect6__EINVAL_netif()") + child.expect_exact("Calling test_tcp_connect6__success_without_port()") + child.expect_exact("Calling test_tcp_connect6__success_local_port()") + if _reuse_tests(code): + child.expect_exact("Calling test_tcp_listen6__EADDRINUSE()") + child.expect_exact("Calling test_tcp_listen6__EAFNOSUPPORT()") + child.expect_exact("Calling test_tcp_listen6__EINVAL()") + child.expect_exact("Calling test_tcp_listen6__success_any_netif()") + child.expect_exact("Calling test_tcp_listen6__success_spec_netif()") + child.expect_exact("Calling test_tcp_accept6__EAGAIN()") + child.expect_exact("Calling test_tcp_accept6__EINVAL()") + child.expect_exact("Calling test_tcp_accept6__ETIMEDOUT()") + start = datetime.now() + child.expect_exact(" * Calling sock_tcp_accept()") + 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("Calling test_tcp_accept6__success()") + child.expect_exact("Calling test_tcp_read6__EAGAIN()") + child.expect_exact("Calling test_tcp_read6__ECONNRESET()") + child.expect_exact("Calling test_tcp_read6__ENOTCONN()") + child.expect_exact("Calling test_tcp_read6__ETIMEDOUT()") + start = datetime.now() + child.expect_exact(" * Calling sock_tcp_read()") + 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("Calling test_tcp_read6__success()") + child.expect_exact("Calling test_tcp_read6__success_with_timeout()") + child.expect_exact("Calling test_tcp_read6__success_non_blocking()") + child.expect_exact("Calling test_tcp_write6__ENOTCONN()") + child.expect_exact("Calling test_tcp_write6__success()") + child.expect_exact(u"ALL TESTS SUCCESSFUL") + +if __name__ == "__main__": + sys.exit(testrunner.run(testfunc, timeout=60))