diff --git a/Makefile.dep b/Makefile.dep index 802f98b2aa5c..ccd736ad5538 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -57,6 +57,19 @@ ifneq (,$(filter gnrc_conn_udp,$(USEMODULE))) USEMODULE += gnrc_udp endif +ifneq (,$(filter gnrc_sock_%,$(USEMODULE))) + USEMODULE += gnrc_sock +endif + +ifneq (,$(filter gnrc_sock_udp,$(USEMODULE))) + USEMODULE += gnrc_udp + USEMODULE += random # to generate random ports +endif + +ifneq (,$(filter gnrc_sock,$(USEMODULE))) + USEMODULE += gnrc_netapi_mbox +endif + ifneq (,$(filter gnrc_netapi_mbox,$(USEMODULE))) USEMODULE += core_mbox endif diff --git a/Makefile.pseudomodules b/Makefile.pseudomodules index 27976a1dab9b..cf0349561dd0 100644 --- a/Makefile.pseudomodules +++ b/Makefile.pseudomodules @@ -21,6 +21,7 @@ PSEUDOMODULES += gnrc_sixlowpan_iphc_nhc PSEUDOMODULES += gnrc_sixlowpan_nd_border_router PSEUDOMODULES += gnrc_sixlowpan_router PSEUDOMODULES += gnrc_sixlowpan_router_default +PSEUDOMODULES += gnrc_sock_check_reuse PSEUDOMODULES += log PSEUDOMODULES += log_printfnoformat PSEUDOMODULES += lwip_arp @@ -51,6 +52,10 @@ PSEUDOMODULES += saul_adc PSEUDOMODULES += saul_default PSEUDOMODULES += saul_gpio PSEUDOMODULES += schedstatistics +PSEUDOMODULES += sock +PSEUDOMODULES += sock_ip +PSEUDOMODULES += sock_tcp +PSEUDOMODULES += sock_udp # include variants of the AT86RF2xx drivers as pseudo modules PSEUDOMODULES += at86rf23% diff --git a/sys/Makefile.include b/sys/Makefile.include index 51790bc3ec01..7a460af0f8c2 100644 --- a/sys/Makefile.include +++ b/sys/Makefile.include @@ -9,6 +9,12 @@ endif ifneq (,$(filter fib,$(USEMODULE))) USEMODULE_INCLUDES += $(RIOTBASE)/sys/posix/include endif +ifneq (,$(filter gnrc_sock,$(USEMODULE))) + USEMODULE_INCLUDES += $(RIOTBASE)/sys/net/gnrc/sock/include + ifneq (,$(filter gnrc_ipv6,$(USEMODULE))) + CFLAGS += -DSOCK_HAS_IPV6 + endif +endif ifneq (,$(filter posix,$(USEMODULE))) USEMODULE_INCLUDES += $(RIOTBASE)/sys/posix/include endif diff --git a/sys/net/gnrc/Makefile b/sys/net/gnrc/Makefile index a4b69955ff01..0eb7f61ef740 100644 --- a/sys/net/gnrc/Makefile +++ b/sys/net/gnrc/Makefile @@ -112,6 +112,15 @@ endif ifneq (,$(filter gnrc_slip,$(USEMODULE))) DIRS += link_layer/slip endif +ifneq (,$(filter gnrc_sock,$(USEMODULE))) + DIRS += sock +endif +ifneq (,$(filter gnrc_sock_ip,$(USEMODULE))) + DIRS += sock/ip +endif +ifneq (,$(filter gnrc_sock_udp,$(USEMODULE))) + DIRS += sock/udp +endif ifneq (,$(filter gnrc_udp,$(USEMODULE))) DIRS += transport_layer/udp endif diff --git a/sys/net/gnrc/sock/Makefile b/sys/net/gnrc/sock/Makefile new file mode 100644 index 000000000000..667aec4086c3 --- /dev/null +++ b/sys/net/gnrc/sock/Makefile @@ -0,0 +1,3 @@ +MODULE = gnrc_sock + +include $(RIOTBASE)/Makefile.base diff --git a/sys/net/gnrc/sock/gnrc_sock.c b/sys/net/gnrc/sock/gnrc_sock.c new file mode 100644 index 000000000000..8b81758c0740 --- /dev/null +++ b/sys/net/gnrc/sock/gnrc_sock.c @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2016 Martine Lenders + * + * 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 + */ + +#include + +#include "net/af.h" +#include "net/ipv6/hdr.h" +#include "net/gnrc/ipv6/hdr.h" +#include "net/gnrc/ipv6/netif.h" +#include "net/gnrc/netreg.h" +#include "net/udp.h" +#include "utlist.h" +#include "xtimer.h" + +#include "sock_types.h" +#include "gnrc_sock_internal.h" + +#ifdef MODULE_XTIMER +#define _TIMEOUT_MAGIC (0xF38A0B63U) +#define _TIMEOUT_MSG_TYPE (0x8474) + +static void _callback_put(void *arg) +{ + msg_t timeout_msg = { .sender_pid = KERNEL_PID_UNDEF, + .type = _TIMEOUT_MSG_TYPE, + .content = { .value = _TIMEOUT_MAGIC } }; + gnrc_sock_reg_t *reg = arg; + + /* should be safe, because otherwise if mbox were filled this callback is + * senseless */ + mbox_try_put(®->mbox, &timeout_msg); +} +#endif + +void gnrc_sock_create(gnrc_sock_reg_t *reg, gnrc_nettype_t type, uint32_t demux_ctx) +{ + mbox_init(®->mbox, reg->mbox_queue, SOCK_MBOX_SIZE); + gnrc_netreg_entry_init_mbox(®->entry, demux_ctx, ®->mbox); + gnrc_netreg_register(type, ®->entry); +} + +ssize_t gnrc_sock_recv(gnrc_sock_reg_t *reg, gnrc_pktsnip_t **pkt_out, + uint32_t timeout, sock_ip_ep_t *remote) +{ + gnrc_pktsnip_t *pkt, *ip, *netif; + msg_t msg; + +#ifdef MODULE_XTIMER + xtimer_t timeout_timer; + + if ((timeout != SOCK_NO_TIMEOUT) && (timeout != 0)) { + timeout_timer.callback = _callback_put; + timeout_timer.arg = reg; + xtimer_set(&timeout_timer, timeout); + } +#endif + if (timeout != 0) { + mbox_get(®->mbox, &msg); + } + else { + if (!mbox_try_get(®->mbox, &msg)) { + return -EAGAIN; + } + } +#ifdef MODULE_XTIMER + xtimer_remove(&timeout_timer); +#endif + switch (msg.type) { + case GNRC_NETAPI_MSG_TYPE_RCV: + pkt = msg.content.ptr; + break; +#ifdef MODULE_XTIMER + case _TIMEOUT_MSG_TYPE: + if (msg.content.value == _TIMEOUT_MAGIC) { + return -ETIMEDOUT; + } +#endif + default: + return -EINTR; + } + /* TODO: discern NETTYPE from remote->family (set in caller), when IPv4 + * was implemented */ + ipv6_hdr_t *ipv6_hdr; + ip = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_IPV6); + assert((ip != NULL) && (ip->size >= 40)); + ipv6_hdr = ip->data; + memcpy(&remote->addr, &ipv6_hdr->src, sizeof(ipv6_addr_t)); + remote->family = AF_INET6; + netif = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_NETIF); + if (netif == NULL) { + remote->netif = SOCK_ADDR_ANY_NETIF; + } + else { + gnrc_netif_hdr_t *netif_hdr = netif->data; + /* TODO: use API in #5511 */ + remote->netif = (uint16_t)netif_hdr->if_pid; + } + *pkt_out = pkt; /* set out parameter */ + return 0; +} + +ssize_t gnrc_sock_send(gnrc_pktsnip_t *payload, sock_ip_ep_t *local, + const sock_ip_ep_t *remote, uint8_t nh) +{ + gnrc_pktsnip_t *pkt; + kernel_pid_t iface = KERNEL_PID_UNDEF; + gnrc_nettype_t type; + size_t payload_len = gnrc_pkt_len(payload); + + if (local->family != remote->family) { + gnrc_pktbuf_release(payload); + return -EAFNOSUPPORT; + } + switch (local->family) { +#ifdef SOCK_HAS_IPV6 + case AF_INET6: { + ipv6_hdr_t *hdr; + pkt = gnrc_ipv6_hdr_build(payload, (ipv6_addr_t *)&local->addr.ipv6, + (ipv6_addr_t *)&remote->addr.ipv6); + if (pkt == NULL) { + return -ENOMEM; + } + if (payload->type == GNRC_NETTYPE_UNDEF) { + payload->type = GNRC_NETTYPE_IPV6; + type = GNRC_NETTYPE_IPV6; + } + else { + type = payload->type; + } + hdr = pkt->data; + hdr->nh = nh; + break; + } +#endif + default: + (void)nh; + gnrc_pktbuf_release(payload); + return -EAFNOSUPPORT; + } + if (local->netif != SOCK_ADDR_ANY_NETIF) { + /* TODO: use API in #5511 */ + iface = (kernel_pid_t)local->netif; + } + else if (remote->netif != SOCK_ADDR_ANY_NETIF) { + /* TODO: use API in #5511 */ + iface = (kernel_pid_t)remote->netif; + } + if (iface != KERNEL_PID_UNDEF) { + gnrc_pktsnip_t *netif = gnrc_netif_hdr_build(NULL, 0, NULL, 0); + gnrc_netif_hdr_t *netif_hdr; + + if (netif == NULL) { + gnrc_pktbuf_release(pkt); + return -ENOMEM; + } + netif_hdr = netif->data; + netif_hdr->if_pid = iface; + LL_PREPEND(pkt, netif); + } +#ifdef MODULE_GNRC_NETERR + gnrc_neterr_reg(pkt); /* no error should occur since pkt was created here */ +#endif + if (!gnrc_netapi_dispatch_send(type, GNRC_NETREG_DEMUX_CTX_ALL, pkt)) { + /* this should not happen, but just in case */ + gnrc_pktbuf_release(pkt); + return -EBADMSG; + } +#ifdef MODULE_GNRC_NETERR + msg_t err_report; + err_report.type = 0; + + while (err_report.type != GNRC_NETERR_MSG_TYPE) { + msg_try_receive(err_report); + if (err_report.type != GNRC_NETERR_MSG_TYPE) { + msg_try_send(err_report, sched_active_pid); + } + } + if (err_report.content.value != GNRC_NETERR_SUCCESS) { + return (int)(-err_report.content.value); + } +#endif + return payload_len; +} + +/** @} */ diff --git a/sys/net/gnrc/sock/include/gnrc_sock_internal.h b/sys/net/gnrc/sock/include/gnrc_sock_internal.h new file mode 100644 index 000000000000..49cb47638552 --- /dev/null +++ b/sys/net/gnrc/sock/include/gnrc_sock_internal.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2016 Martine Lenders + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup net_gnrc_sock GNRC-specific implementation of the sock API + * @ingroup net_gnrc + * @brief Provides an implementation of the @ref net_sock by the + * @ref net_gnrc + * + * @{ + * + * @file + * @brief GNRC-specific types and function definitions + * + * @author Martine Lenders + */ +#ifndef GNRC_SOCK_INTERNAL_H_ +#define GNRC_SOCK_INTERNAL_H_ + +#include +#include +#include "mbox.h" +#include "net/af.h" +#include "net/gnrc.h" +#include "net/gnrc/netreg.h" +#include "net/sock/ip.h" + +#include "sock_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Internal helper functions for GNRC + * @internal + * @{ + */ +/** + * @brief Checks if address family is not supported + * @internal + */ +static inline bool gnrc_af_not_supported(int af) +{ + /* TODO: add AF_INET support */ + return (af != AF_INET6); +} + +/** + * @brief Check if end point points to any address + * @internal + */ +static inline bool gnrc_ep_addr_any(const sock_ip_ep_t *ep) +{ + assert(ep != NULL); + const uint8_t *p = (uint8_t *)&ep->addr; + for (uint8_t i = 0; i < sizeof(ep->addr); i++) { + if (p[i] != 0) { + return false; + } + } + return true; +} + +/** + * @brief Create a sock internally + * @internal + */ +void gnrc_sock_create(gnrc_sock_reg_t *reg, gnrc_nettype_t type, uint32_t demux_ctx); + +/** + * @brief Receive a packet internally + * @internal + */ +ssize_t gnrc_sock_recv(gnrc_sock_reg_t *reg, gnrc_pktsnip_t **pkt, uint32_t timeout, + sock_ip_ep_t *remote); + +/** + * @brief Send a packet internally + * @internal + */ +ssize_t gnrc_sock_send(gnrc_pktsnip_t *payload, sock_ip_ep_t *local, + const sock_ip_ep_t *remote, uint8_t nh); +/** + * @} + */ + + +#ifdef __cplusplus +} +#endif + +#endif /* GNRC_SOCK_INTERNAL_H_ */ +/** @} */ diff --git a/sys/net/gnrc/sock/include/sock_types.h b/sys/net/gnrc/sock/include/sock_types.h new file mode 100644 index 000000000000..1e86b0c37526 --- /dev/null +++ b/sys/net/gnrc/sock/include/sock_types.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2016 Martine Lenders + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup net_gnrc_sock GNRC-specific implementation of the sock API + * @ingroup net_gnrc + * @brief Provides an implementation of the @ref net_sock by the + * @ref net_gnrc + * + * @{ + * + * @file + * @brief GNRC-specific types and function definitions + * + * @author Martine Lenders + */ +#ifndef SOCK_TYPES_H_ +#define SOCK_TYPES_H_ + +#include +#include +#include "mbox.h" +#include "net/gnrc.h" +#include "net/gnrc/netreg.h" +#include "net/sock/ip.h" +#include "net/sock/udp.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef SOCK_MBOX_SIZE +#define SOCK_MBOX_SIZE (8) /**< Size for gnrc_sock_reg_t::mbox_queue */ +#endif + +/** + * @brief sock @ref net_gnrc_netreg info + * @internal + */ +typedef struct gnrc_sock_reg { +#ifdef MODULE_GNRC_SOCK_CHECK_REUSE + struct gnrc_sock_reg *next; /**< list-like for internal storage */ +#endif + gnrc_netreg_entry_t entry; /**< @ref net_gnrc_netreg entry for mbox */ + mbox_t mbox; /**< @ref core_mbox target for the sock */ + msg_t mbox_queue[SOCK_MBOX_SIZE]; /**< queue for gnrc_sock_reg_t::mbox */ +} gnrc_sock_reg_t; + +/** + * @brief Raw IP sock type + * @internal + */ +struct sock_ip { + gnrc_sock_reg_t reg; /**< netreg info */ + sock_ip_ep_t local; /**< local end-point */ + sock_ip_ep_t remote; /**< remote end-point */ + uint16_t flags; /**< option flags */ +}; + +/** + * @brief UDP sock type + * @internal + */ +struct sock_udp { + gnrc_sock_reg_t reg; /**< netreg info */ + sock_udp_ep_t local; /**< local end-point */ + sock_udp_ep_t remote; /**< remote end-point */ + uint16_t flags; /**< option flags */ +}; + +#ifdef __cplusplus +} +#endif + +#endif /* SOCK_TYPES_H_ */ +/** @} */ diff --git a/sys/net/gnrc/sock/ip/Makefile b/sys/net/gnrc/sock/ip/Makefile new file mode 100644 index 000000000000..5004d566c83f --- /dev/null +++ b/sys/net/gnrc/sock/ip/Makefile @@ -0,0 +1,3 @@ +MODULE = gnrc_sock_ip + +include $(RIOTBASE)/Makefile.base diff --git a/sys/net/gnrc/sock/ip/gnrc_sock_ip.c b/sys/net/gnrc/sock/ip/gnrc_sock_ip.c new file mode 100644 index 000000000000..a72716e2fcf6 --- /dev/null +++ b/sys/net/gnrc/sock/ip/gnrc_sock_ip.c @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2015 Martine Lenders + * + * 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 + * @brief GNRC implementation of @ref net_sock_ip + * + * @author Martine Lenders + */ + +#include + +#include "byteorder.h" +#include "net/af.h" +#include "net/protnum.h" +#include "net/gnrc/ipv6.h" +#include "net/sock/ip.h" +#include "random.h" + +#include "gnrc_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); + if ((local != NULL) && (remote != NULL) && + (local->netif != SOCK_ADDR_ANY_NETIF) && + (remote->netif != SOCK_ADDR_ANY_NETIF) && + (local->netif != remote->netif)) { + return -EINVAL; + } + memset(&sock->local, 0, sizeof(sock_ip_t)); + if (local != NULL) { + if (gnrc_af_not_supported(local->family)) { + return -EAFNOSUPPORT; + } + memcpy(&sock->local, local, sizeof(sock_ip_t)); + } + memset(&sock->remote, 0, sizeof(sock_ip_t)); + if (remote != NULL) { + if (gnrc_af_not_supported(remote->family)) { + return -EAFNOSUPPORT; + } + if (gnrc_ep_addr_any(remote)) { + return -EINVAL; + } + memcpy(&sock->remote, remote, sizeof(sock_ip_t)); + } + gnrc_sock_create(&sock->reg, GNRC_NETTYPE_IPV6, + proto); + sock->flags = flags; + return 0; +} + +void sock_ip_close(sock_ip_t *sock) +{ + assert(sock != NULL); + gnrc_netreg_unregister(GNRC_NETTYPE_IPV6, &sock->reg.entry); +} + +int sock_ip_get_local(sock_ip_t *sock, sock_ip_ep_t *local) +{ + assert(sock && local); + if (sock->local.family == AF_UNSPEC) { + return -EADDRNOTAVAIL; + } + memcpy(local, &sock->local, sizeof(sock_ip_ep_t)); + return 0; +} + +int sock_ip_get_remote(sock_ip_t *sock, sock_ip_ep_t *remote) +{ + assert(sock && remote); + if (sock->remote.family == AF_UNSPEC) { + return -ENOTCONN; + } + memcpy(remote, &sock->remote, sizeof(sock_ip_ep_t)); + return 0; +} + +ssize_t sock_ip_recv(sock_ip_t *sock, void *data, size_t max_len, + uint32_t timeout, sock_ip_ep_t *remote) +{ + gnrc_pktsnip_t *pkt; + sock_ip_ep_t tmp; + int res; + + assert((sock != NULL) && (data != NULL) && (max_len > 0)); + if (sock->local.family == 0) { + return -EADDRNOTAVAIL; + } + tmp.family = sock->local.family; + res = gnrc_sock_recv((gnrc_sock_reg_t *)sock, &pkt, timeout, &tmp); + if (res < 0) { + return res; + } + if (pkt->size > max_len) { + gnrc_pktbuf_release(pkt); + return -ENOBUFS; + } + if (remote != NULL) { + /* return remote to possibly block if wrong remote */ + memcpy(remote, &tmp, sizeof(tmp)); + } + if ((sock->remote.family != AF_UNSPEC) && /* check remote end-point if set */ + /* We only have IPv6 for now, so just comparing the whole end point + * should suffice */ + ((memcmp(&sock->remote.addr, &ipv6_addr_unspecified, + sizeof(ipv6_addr_t)) != 0) && + (memcmp(&sock->remote.addr, &tmp.addr, sizeof(ipv6_addr_t)) != 0))) { + gnrc_pktbuf_release(pkt); + return -EPROTO; + } + memcpy(data, pkt->data, pkt->size); + gnrc_pktbuf_release(pkt); + return (int)pkt->size; +} + +ssize_t sock_ip_send(sock_ip_t *sock, const void *data, size_t len, + uint8_t proto, const sock_ip_ep_t *remote) +{ + int res; + gnrc_pktsnip_t *pkt; + sock_ip_ep_t local; + sock_ip_ep_t rem; + + assert((sock != NULL) || (remote != NULL)); + assert((len == 0) || (data != NULL)); /* (len != 0) => (data != NULL) */ + if ((remote != NULL) && (sock != NULL) && + (sock->local.netif != SOCK_ADDR_ANY_NETIF) && + (remote->netif != SOCK_ADDR_ANY_NETIF) && + (sock->local.netif != remote->netif)) { + return -EINVAL; + } + if ((remote == NULL) && + /* sock can't be NULL as per assertion above */ + (sock->remote.family == AF_UNSPEC)) { + return -ENOTCONN; + } + else if ((remote != NULL) && (gnrc_ep_addr_any(remote))) { + return -EINVAL; + } + /* compiler evaluates lazily so this isn't a redundundant check and cppcheck + * is being weird here anyways */ + /* cppcheck-suppress nullPointerRedundantCheck */ + /* cppcheck-suppress nullPointer */ + if ((sock == NULL) || (sock->local.family == AF_UNSPEC)) { + /* no sock or sock currently unbound */ + memset(&local, 0, sizeof(local)); + } + else { + if (sock != NULL) { + proto = (uint8_t)sock->reg.entry.demux_ctx; + } + memcpy(&local, &sock->local, sizeof(local)); + } + if (remote == NULL) { + /* sock can't be NULL at this point */ + memcpy(&rem, &sock->remote, sizeof(rem)); + } + else { + memcpy(&rem, remote, sizeof(rem)); + } + if ((remote != NULL) && (remote->family == AF_UNSPEC) && + (sock->remote.family != AF_UNSPEC)) { + /* remote was set on create so take its family */ + rem.family = sock->remote.family; + } + else if ((remote != NULL) && gnrc_af_not_supported(remote->family)) { + return -EAFNOSUPPORT; + } + else if ((local.family == AF_UNSPEC) && (rem.family != AF_UNSPEC)) { + /* local was set to 0 above */ + local.family = rem.family; + } + else if ((local.family != AF_UNSPEC) && (rem.family == AF_UNSPEC)) { + /* local was given on create, but remote family wasn't given by user and + * there was no remote given on create, take from local */ + rem.family = local.family; + } + pkt = gnrc_pktbuf_add(NULL, (void *)data, len, GNRC_NETTYPE_UNDEF); + if (pkt == NULL) { + return -ENOMEM; + } + res = gnrc_sock_send(pkt, &local, &rem, proto); + if (res <= 0) { + return res; + } + return res; +} + +/** @} */ diff --git a/sys/net/gnrc/sock/udp/Makefile b/sys/net/gnrc/sock/udp/Makefile new file mode 100644 index 000000000000..257f780a5c2c --- /dev/null +++ b/sys/net/gnrc/sock/udp/Makefile @@ -0,0 +1,3 @@ +MODULE = gnrc_sock_udp + +include $(RIOTBASE)/Makefile.base diff --git a/sys/net/gnrc/sock/udp/gnrc_sock_udp.c b/sys/net/gnrc/sock/udp/gnrc_sock_udp.c new file mode 100644 index 000000000000..31bb5897b8fb --- /dev/null +++ b/sys/net/gnrc/sock/udp/gnrc_sock_udp.c @@ -0,0 +1,277 @@ +/* + * Copyright (C) 2015 Martine Lenders + * + * 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 + * @brief GNRC implementation of @ref net_sock_udp + * + * @author Martine Lenders + */ + +#include + +#include "byteorder.h" +#include "net/af.h" +#include "net/protnum.h" +#include "net/gnrc/ipv6.h" +#include "net/gnrc/udp.h" +#include "net/sock/udp.h" +#include "net/udp.h" +#include "random.h" + +#include "gnrc_sock_internal.h" + +#ifdef MODULE_GNRC_SOCK_CHECK_REUSE +static sock_udp_t *_udp_socks = NULL; +#endif + +int sock_udp_create(sock_udp_t *sock, const sock_udp_ep_t *local, + const sock_udp_ep_t *remote, uint16_t flags) +{ + assert(sock); + assert(local == NULL || local->port != 0); + assert(remote == NULL || remote->port != 0); + if ((local != NULL) && (remote != NULL) && + (local->netif != SOCK_ADDR_ANY_NETIF) && + (remote->netif != SOCK_ADDR_ANY_NETIF) && + (local->netif != remote->netif)) { + return -EINVAL; + } + memset(&sock->local, 0, sizeof(sock_udp_ep_t)); + if (local != NULL) { +#ifdef MODULE_GNRC_SOCK_CHECK_REUSE + if (!(flags & SOCK_FLAGS_REUSE_EP)) { + for (sock_udp_t *ptr = _udp_socks; ptr != NULL; + ptr = (sock_udp_t *)ptr->reg.next) { + if (memcmp(&ptr->local, local, sizeof(sock_udp_ep_t)) == 0) { + return -EADDRINUSE; + } + } + } + /* prepend to current socks */ + sock->reg.next = (gnrc_sock_reg_t *)_udp_socks; + _udp_socks = sock; +#endif + if (gnrc_af_not_supported(local->family)) { + return -EAFNOSUPPORT; + } + memcpy(&sock->local, local, sizeof(sock_udp_ep_t)); + } + memset(&sock->remote, 0, sizeof(sock_udp_ep_t)); + if (remote != NULL) { + if (gnrc_af_not_supported(remote->family)) { + return -EAFNOSUPPORT; + } + if (gnrc_ep_addr_any((const sock_ip_ep_t *)remote)) { + return -EINVAL; + } + memcpy(&sock->remote, remote, sizeof(sock_udp_ep_t)); + } + if (local != NULL) { + /* listen only with local given */ + gnrc_sock_create(&sock->reg, GNRC_NETTYPE_UDP, + local->port); + } + sock->flags = flags; + return 0; +} + +void sock_udp_close(sock_udp_t *sock) +{ + assert(sock != NULL); + gnrc_netreg_unregister(GNRC_NETTYPE_UDP, &sock->reg.entry); +#ifdef MODULE_GNRC_SOCK_CHECK_REUSE + if (_udp_socks != NULL) { + gnrc_sock_reg_t *head = (gnrc_sock_reg_t *)_udp_socks; + LL_DELETE(head, (gnrc_sock_reg_t *)sock); + } +#endif +} + +int sock_udp_get_local(sock_udp_t *sock, sock_udp_ep_t *local) +{ + assert(sock && local); + if (sock->local.family == AF_UNSPEC) { + return -EADDRNOTAVAIL; + } + memcpy(local, &sock->local, sizeof(sock_udp_ep_t)); + return 0; +} + +int sock_udp_get_remote(sock_udp_t *sock, sock_udp_ep_t *remote) +{ + assert(sock && remote); + if (sock->remote.family == AF_UNSPEC) { + return -ENOTCONN; + } + memcpy(remote, &sock->remote, sizeof(sock_udp_ep_t)); + return 0; +} + +ssize_t sock_udp_recv(sock_udp_t *sock, void *data, size_t max_len, + uint32_t timeout, sock_udp_ep_t *remote) +{ + gnrc_pktsnip_t *pkt, *udp; + udp_hdr_t *hdr; + sock_ip_ep_t tmp; + int res; + + assert((sock != NULL) && (data != NULL) && (max_len > 0)); + if (sock->local.family == AF_UNSPEC) { + return -EADDRNOTAVAIL; + } + tmp.family = sock->local.family; + res = gnrc_sock_recv((gnrc_sock_reg_t *)sock, &pkt, timeout, &tmp); + if (res < 0) { + return res; + } + if (pkt->size > max_len) { + gnrc_pktbuf_release(pkt); + return -ENOBUFS; + } + udp = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_UDP); + assert(udp); + hdr = udp->data; + if (remote != NULL) { + /* return remote to possibly block if wrong remote */ + memcpy(remote, &tmp, sizeof(tmp)); + remote->port = byteorder_ntohs(hdr->src_port); + } + if ((sock->remote.family != AF_UNSPEC) && /* check remote end-point if set */ + ((sock->remote.port != byteorder_ntohs(hdr->src_port)) || + /* We only have IPv6 for now, so just comparing the whole end point + * should suffice */ + ((memcmp(&sock->remote.addr, &ipv6_addr_unspecified, + sizeof(ipv6_addr_t)) != 0) && + (memcmp(&sock->remote.addr, &tmp.addr, sizeof(ipv6_addr_t)) != 0)))) { + gnrc_pktbuf_release(pkt); + return -EPROTO; + } + memcpy(data, pkt->data, pkt->size); + gnrc_pktbuf_release(pkt); + return (int)pkt->size; +} + +ssize_t sock_udp_send(sock_udp_t *sock, const void *data, size_t len, + const sock_udp_ep_t *remote) +{ + int res; + gnrc_pktsnip_t *payload, *pkt; + uint16_t src_port = 0, dst_port; + sock_ip_ep_t local; + sock_ip_ep_t rem; + + assert((sock != NULL) || (remote != NULL)); + assert((len == 0) || (data != NULL)); /* (len != 0) => (data != NULL) */ + if ((remote != NULL) && (sock != NULL) && + (sock->local.netif != SOCK_ADDR_ANY_NETIF) && + (remote->netif != SOCK_ADDR_ANY_NETIF) && + (sock->local.netif != remote->netif)) { + return -EINVAL; + } + if ((remote != NULL) && ((remote->port == 0) || + gnrc_ep_addr_any((const sock_ip_ep_t *)remote))) { + return -EINVAL; + } + if ((remote == NULL) && + /* sock can't be NULL as per assertion above */ + (sock->remote.port == AF_UNSPEC)) { + return -ENOTCONN; + } + /* compiler evaluates lazily so this isn't a redundundant check and cppcheck + * is being weird here anyways */ + /* cppcheck-suppress nullPointerRedundantCheck */ + /* cppcheck-suppress nullPointer */ + if ((sock == NULL) || (sock->local.family == AF_UNSPEC)) { + /* no sock or sock currently unbound */ + while (src_port == 0) { + src_port = (uint16_t)(random_uint32() & UINT16_MAX); +#ifdef MODULE_GNRC_SOCK_CHECK_REUSE + if ((sock == NULL) || !(sock->flags & SOCK_FLAGS_REUSE_EP)) { + /* check if port already registered somewhere */ + for (sock_udp_t *ptr = _udp_socks; ptr != NULL; + ptr = (sock_udp_t *)ptr->reg.next) { + bool spec_addr = false; + for (unsigned i = 0; i < sizeof(ptr->local.addr); i++) { + const uint8_t *const p = (uint8_t *)&ptr->local.addr; + if (p[i] != 0) { + spec_addr = true; + } + } + if (spec_addr) { + continue; + } + if (ptr->local.port == src_port) { + /* we already have one of this port registered + * => generate a new one */ + src_port = 0; + } + } + } +#endif + } + memset(&local, 0, sizeof(local)); + if (sock != NULL) { + /* bind sock object implicitly */ + sock->local.port = src_port; +#ifdef MODULE_GNRC_SOCK_CHECK_REUSE + /* prepend to current socks */ + sock->reg.next = (gnrc_sock_reg_t *)_udp_socks; + _udp_socks = sock; +#endif + } + } + else { + src_port = sock->local.port; + memcpy(&local, &sock->local, sizeof(local)); + } + if (remote == NULL) { + /* sock can't be NULL at this point */ + memcpy(&rem, &sock->remote, sizeof(rem)); + dst_port = sock->remote.port; + } + else { + memcpy(&rem, remote, sizeof(rem)); + dst_port = remote->port; + } + if ((remote != NULL) && (remote->family == AF_UNSPEC) && + (sock->remote.family != AF_UNSPEC)) { + /* remote was set on create so take its family */ + rem.family = sock->remote.family; + } + else if ((remote != NULL) && gnrc_af_not_supported(remote->family)) { + return -EAFNOSUPPORT; + } + else if ((local.family == AF_UNSPEC) && (rem.family != AF_UNSPEC)) { + /* local was set to 0 above */ + local.family = rem.family; + } + else if ((local.family != AF_UNSPEC) && (rem.family == AF_UNSPEC)) { + /* local was given on create, but remote family wasn't given by user and + * there was no remote given on create, take from local */ + rem.family = local.family; + } + payload = gnrc_pktbuf_add(NULL, (void *)data, len, GNRC_NETTYPE_UNDEF); + if (payload == NULL) { + return -ENOMEM; + } + pkt = gnrc_udp_hdr_build(payload, src_port, dst_port); + if (pkt == NULL) { + gnrc_pktbuf_release(payload); + return -ENOMEM; + } + res = gnrc_sock_send(pkt, &local, &rem, PROTNUM_UDP); + if (res <= 0) { + return res; + } + return res - sizeof(udp_hdr_t); +} + +/** @} */ diff --git a/tests/gnrc_sock_ip/Makefile b/tests/gnrc_sock_ip/Makefile new file mode 100644 index 000000000000..8c06d87bbe17 --- /dev/null +++ b/tests/gnrc_sock_ip/Makefile @@ -0,0 +1,20 @@ +APPLICATION = gnrc_sock_ip + +BOARD ?= native + +RIOTBASE ?= $(CURDIR)/../.. + +USEMODULE += gnrc_sock_ip +USEMODULE += gnrc_ipv6 +USEMODULE += ps + +CFLAGS += -DDEVELHELP +CFLAGS += -DGNRC_PKTBUF_SIZE=200 +CFLAGS += -DTEST_SUITES + +QUIET ?= 1 + +include $(RIOTBASE)/Makefile.include + +test: + ./tests/01-run.py diff --git a/tests/gnrc_sock_ip/constants.h b/tests/gnrc_sock_ip/constants.h new file mode 100644 index 000000000000..88776e4ee242 --- /dev/null +++ b/tests/gnrc_sock_ip/constants.h @@ -0,0 +1,43 @@ +/* + * 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 + */ +#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 (31) +#define _TEST_TIMEOUT (1000000U) +#define _TEST_ADDR_LOCAL { 0x7f, 0xc4, 0x11, 0x5a, 0xe6, 0x91, 0x8d, 0x5d, \ + 0x8c, 0xd1, 0x47, 0x07, 0xb7, 0x6f, 0x9b, 0x48 } +#define _TEST_ADDR_REMOTE { 0xe8, 0xb3, 0xb2, 0xe6, 0x70, 0xd4, 0x55, 0xba, \ + 0x93, 0xcf, 0x11, 0xe1, 0x72, 0x44, 0xc5, 0x9d } +#define _TEST_ADDR_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/gnrc_sock_ip/main.c b/tests/gnrc_sock_ip/main.c new file mode 100644 index 000000000000..f4c6c0774b75 --- /dev/null +++ b/tests/gnrc_sock_ip/main.c @@ -0,0 +1,640 @@ +/* + * 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 + * @} + */ + +#include +#include +#include +#include + +#include "net/af.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)); +} + +static void test_sock_ip_create__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_create__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_create__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_ADDR_REMOTE } }; + + assert(-EINVAL == sock_ip_create(&_sock, &local, &remote, _TEST_PROTO, + SOCK_FLAGS_REUSE_EP)); +} + +static void test_sock_ip_create__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_create__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_create__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_create__only_remote(void) +{ + static const ipv6_addr_t remote_addr = { .u8 = _TEST_ADDR_REMOTE }; + static const sock_ip_ep_t remote = { .family = AF_INET6, + .addr = { .ipv6 = _TEST_ADDR_REMOTE } }; + sock_ip_ep_t ep; + + assert(0 == sock_ip_create(&_sock, NULL, &remote, _TEST_PROTO, + SOCK_FLAGS_REUSE_EP)); + assert(-EADDRNOTAVAIL == 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_create__full(void) +{ + static const ipv6_addr_t remote_addr = { .u8 = _TEST_ADDR_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_ADDR_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); + 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_recv__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), SOCK_NO_TIMEOUT, + NULL)); +} + +static void test_sock_ip_recv__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_recv__ENOBUFS(void) +{ + static const ipv6_addr_t src_addr = { .u8 = _TEST_ADDR_REMOTE }; + static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR_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_packet(&src_addr, &dst_addr, _TEST_PROTO, "ABCD", + sizeof("ABCD"), _TEST_NETIF)); + assert(-ENOBUFS == sock_ip_recv(&_sock, _test_buffer, 2, SOCK_NO_TIMEOUT, + NULL)); + assert(_check_net()); +} + +static void test_sock_ip_recv__EPROTO(void) +{ + static const ipv6_addr_t src_addr = { .u8 = _TEST_ADDR_WRONG }; + static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR_LOCAL }; + static const sock_ip_ep_t local = { .family = AF_INET6 }; + static const sock_ip_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR_REMOTE }, + .family = AF_INET6 }; + + assert(0 == sock_ip_create(&_sock, &local, &remote, _TEST_PROTO, + SOCK_FLAGS_REUSE_EP)); + assert(_inject_packet(&src_addr, &dst_addr, _TEST_PROTO, "ABCD", + sizeof("ABCD"), _TEST_NETIF)); + assert(-EPROTO == sock_ip_recv(&_sock, _test_buffer, sizeof(_test_buffer), + SOCK_NO_TIMEOUT, NULL)); + assert(_check_net()); +} + +static void test_sock_ip_recv__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 %lu)\n", (long unsigned)_TEST_TIMEOUT); +} + +static void test_sock_ip_recv__socketed(void) +{ + static const ipv6_addr_t src_addr = { .u8 = _TEST_ADDR_REMOTE }; + static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR_LOCAL }; + static const sock_ip_ep_t local = { .family = AF_INET6 }; + static const sock_ip_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR_REMOTE }, + .family = AF_INET6 }; + + assert(0 == sock_ip_create(&_sock, &local, &remote, _TEST_PROTO, + SOCK_FLAGS_REUSE_EP)); + assert(_inject_packet(&src_addr, &dst_addr, _TEST_PROTO, "ABCD", + sizeof("ABCD"), _TEST_NETIF)); + assert(sizeof("ABCD") == sock_ip_recv(&_sock, _test_buffer, + sizeof(_test_buffer), SOCK_NO_TIMEOUT, + NULL)); + assert(_check_net()); +} + +static void test_sock_ip_recv__socketed_with_remote(void) +{ + static const ipv6_addr_t src_addr = { .u8 = _TEST_ADDR_REMOTE }; + static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR_LOCAL }; + static const sock_ip_ep_t local = { .family = AF_INET6 }; + static const sock_ip_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR_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_packet(&src_addr, &dst_addr, _TEST_PROTO, "ABCD", + sizeof("ABCD"), _TEST_NETIF)); + assert(sizeof("ABCD") == sock_ip_recv(&_sock, _test_buffer, + sizeof(_test_buffer), SOCK_NO_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_recv__unsocketed(void) +{ + static const ipv6_addr_t src_addr = { .u8 = _TEST_ADDR_REMOTE }; + static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR_LOCAL }; + static const sock_ip_ep_t local = { .addr = { .ipv6 = _TEST_ADDR_LOCAL }, + .family = AF_INET6 }; + + assert(0 == sock_ip_create(&_sock, &local, NULL, _TEST_PROTO, + SOCK_FLAGS_REUSE_EP)); + assert(_inject_packet(&src_addr, &dst_addr, _TEST_PROTO, "ABCD", + sizeof("ABCD"), _TEST_NETIF)); + assert(sizeof("ABCD") == sock_ip_recv(&_sock, _test_buffer, + sizeof(_test_buffer), SOCK_NO_TIMEOUT, + NULL)); + assert(_check_net()); +} + +static void test_sock_ip_recv__unsocketed_with_remote(void) +{ + static const ipv6_addr_t src_addr = { .u8 = _TEST_ADDR_REMOTE }; + static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR_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_packet(&src_addr, &dst_addr, _TEST_PROTO, "ABCD", + sizeof("ABCD"), _TEST_NETIF)); + assert(sizeof("ABCD") == sock_ip_recv(&_sock, _test_buffer, + sizeof(_test_buffer), SOCK_NO_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_recv__with_timeout(void) +{ + static const ipv6_addr_t src_addr = { .u8 = _TEST_ADDR_REMOTE }; + static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR_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_packet(&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_recv__non_blocking(void) +{ + static const ipv6_addr_t src_addr = { .u8 = _TEST_ADDR_REMOTE }; + static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR_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_packet(&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_send__EAFNOSUPPORT(void) +{ + static const sock_ip_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR_REMOTE }, + .family = AF_INET }; + + assert(-EAFNOSUPPORT == sock_ip_send(NULL, "ABCD", sizeof("ABCD"), + _TEST_PROTO, &remote)); + assert(_check_net()); +} + +static void test_sock_ip_send__EINVAL_addr(void) +{ + static const sock_ip_ep_t local = { .addr = { .ipv6 = _TEST_ADDR_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_send__EINVAL_netif(void) +{ + static const sock_ip_ep_t local = { .addr = { .ipv6 = _TEST_ADDR_LOCAL }, + .family = AF_INET6, + .netif = _TEST_NETIF }; + static const sock_ip_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR_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_send__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_send__socketed_no_local_no_netif(void) +{ + static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR_REMOTE }; + static const sock_ip_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR_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_packet(&ipv6_addr_unspecified, &dst_addr, _TEST_PROTO, "ABCD", + sizeof("ABCD"), SOCK_ADDR_ANY_NETIF)); + xtimer_usleep(1000); /* let GNRC stack finish */ + assert(_check_net()); +} + +static void test_sock_ip_send__socketed_no_netif(void) +{ + static const ipv6_addr_t src_addr = { .u8 = _TEST_ADDR_LOCAL }; + static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR_REMOTE }; + static const sock_ip_ep_t local = { .addr = { .ipv6 = _TEST_ADDR_LOCAL }, + .family = AF_INET6 }; + static const sock_ip_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR_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_packet(&src_addr, &dst_addr, _TEST_PROTO, "ABCD", + sizeof("ABCD"), SOCK_ADDR_ANY_NETIF)); + xtimer_usleep(1000); /* let GNRC stack finish */ + assert(_check_net()); +} + +static void test_sock_ip_send__socketed_no_local(void) +{ + static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR_REMOTE }; + static const sock_ip_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR_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_packet(&ipv6_addr_unspecified, &dst_addr, _TEST_PROTO, "ABCD", + sizeof("ABCD"), _TEST_NETIF)); + xtimer_usleep(1000); /* let GNRC stack finish */ + assert(_check_net()); +} + +static void test_sock_ip_send__socketed(void) +{ + static const ipv6_addr_t src_addr = { .u8 = _TEST_ADDR_LOCAL }; + static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR_REMOTE }; + static const sock_ip_ep_t local = { .addr = { .ipv6 = _TEST_ADDR_LOCAL }, + .family = AF_INET6, + .netif = _TEST_NETIF }; + static const sock_ip_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR_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_packet(&src_addr, &dst_addr, _TEST_PROTO, "ABCD", + sizeof("ABCD"), _TEST_NETIF)); + xtimer_usleep(1000); /* let GNRC stack finish */ + assert(_check_net()); +} + +static void test_sock_ip_send__socketed_other_remote(void) +{ + static const ipv6_addr_t src_addr = { .u8 = _TEST_ADDR_LOCAL }; + static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR_REMOTE }; + static const sock_ip_ep_t local = { .addr = { .ipv6 = _TEST_ADDR_LOCAL }, + .family = AF_INET6, + .netif = _TEST_NETIF }; + static const sock_ip_ep_t sock_remote = { .addr = { .ipv6 = _TEST_ADDR_WRONG }, + .family = AF_INET6 }; + static const sock_ip_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR_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_packet(&src_addr, &dst_addr, _TEST_PROTO, "ABCD", + sizeof("ABCD"), _TEST_NETIF)); + xtimer_usleep(1000); /* let GNRC stack finish */ + assert(_check_net()); +} + +static void test_sock_ip_send__unsocketed_no_local_no_netif(void) +{ + static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR_REMOTE }; + static const sock_ip_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR_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_packet(&ipv6_addr_unspecified, &dst_addr, _TEST_PROTO, "ABCD", + sizeof("ABCD"), SOCK_ADDR_ANY_NETIF)); + xtimer_usleep(1000); /* let GNRC stack finish */ + assert(_check_net()); +} + +static void test_sock_ip_send__unsocketed_no_netif(void) +{ + static const ipv6_addr_t src_addr = { .u8 = _TEST_ADDR_LOCAL }; + static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR_REMOTE }; + static const sock_ip_ep_t local = { .addr = { .ipv6 = _TEST_ADDR_LOCAL }, + .family = AF_INET6 }; + static const sock_ip_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR_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_packet(&src_addr, &dst_addr, _TEST_PROTO, "ABCD", + sizeof("ABCD"), SOCK_ADDR_ANY_NETIF)); + xtimer_usleep(1000); /* let GNRC stack finish */ + assert(_check_net()); +} + +static void test_sock_ip_send__unsocketed_no_local(void) +{ + static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR_REMOTE }; + static const sock_ip_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR_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_packet(&ipv6_addr_unspecified, &dst_addr, _TEST_PROTO, "ABCD", + sizeof("ABCD"), _TEST_NETIF)); + xtimer_usleep(1000); /* let GNRC stack finish */ + assert(_check_net()); +} + +static void test_sock_ip_send__unsocketed(void) +{ + static const ipv6_addr_t src_addr = { .u8 = _TEST_ADDR_LOCAL }; + static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR_REMOTE }; + static const sock_ip_ep_t local = { .addr = { .ipv6 = _TEST_ADDR_LOCAL }, + .family = AF_INET6, + .netif = _TEST_NETIF }; + static const sock_ip_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR_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_packet(&src_addr, &dst_addr, _TEST_PROTO, "ABCD", + sizeof("ABCD"), _TEST_NETIF)); + xtimer_usleep(1000); /* let GNRC stack finish */ + assert(_check_net()); +} + +static void test_sock_ip_send__no_sock_no_netif(void) +{ + static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR_REMOTE }; + static const sock_ip_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR_REMOTE }, + .family = AF_INET6 }; + + assert(sizeof("ABCD") == sock_ip_send(NULL, "ABCD", sizeof("ABCD"), + _TEST_PROTO, &remote)); + assert(_check_packet(&ipv6_addr_unspecified, &dst_addr, _TEST_PROTO, "ABCD", + sizeof("ABCD"), SOCK_ADDR_ANY_NETIF)); + xtimer_usleep(1000); /* let GNRC stack finish */ + assert(_check_net()); +} + +static void test_sock_ip_send__no_sock(void) +{ + static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR_REMOTE }; + static const sock_ip_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR_REMOTE }, + .family = AF_INET6, + .netif = _TEST_NETIF }; + + assert(sizeof("ABCD") == sock_ip_send(NULL, "ABCD", sizeof("ABCD"), + _TEST_PROTO, &remote)); + assert(_check_packet(&ipv6_addr_unspecified, &dst_addr, _TEST_PROTO, "ABCD", + sizeof("ABCD"), _TEST_NETIF)); + xtimer_usleep(1000); /* let GNRC stack finish */ + assert(_check_net()); +} + +int main(void) +{ + _net_init(); + tear_down(); + /* EADDRINUSE does not apply for GNRC */ + CALL(test_sock_ip_create__EAFNOSUPPORT()); + CALL(test_sock_ip_create__EINVAL_addr()); + CALL(test_sock_ip_create__EINVAL_netif()); + /* EPROTONOSUPPORT does not apply for gnrc_ipv6 */ + CALL(test_sock_ip_create__no_endpoints()); + CALL(test_sock_ip_create__only_local()); + CALL(test_sock_ip_create__only_local_reuse_ep()); + CALL(test_sock_ip_create__only_remote()); + CALL(test_sock_ip_create__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_recv__EADDRNOTAVAIL()); + CALL(test_sock_ip_recv__EAGAIN()); + CALL(test_sock_ip_recv__ENOBUFS()); + CALL(test_sock_ip_recv__EPROTO()); + CALL(test_sock_ip_recv__ETIMEDOUT()); + CALL(test_sock_ip_recv__socketed()); + CALL(test_sock_ip_recv__socketed_with_remote()); + CALL(test_sock_ip_recv__unsocketed()); + CALL(test_sock_ip_recv__unsocketed_with_remote()); + CALL(test_sock_ip_recv__with_timeout()); + CALL(test_sock_ip_recv__non_blocking()); + _prepare_send_checks(); + CALL(test_sock_ip_send__EAFNOSUPPORT()); + CALL(test_sock_ip_send__EINVAL_addr()); + CALL(test_sock_ip_send__EINVAL_netif()); + CALL(test_sock_ip_send__ENOTCONN()); + /* EPROTOTYPE does not apply for gnrc_ipv6 */ + CALL(test_sock_ip_send__socketed_no_local_no_netif()); + CALL(test_sock_ip_send__socketed_no_netif()); + CALL(test_sock_ip_send__socketed_no_local()); + CALL(test_sock_ip_send__socketed()); + CALL(test_sock_ip_send__socketed_other_remote()); + CALL(test_sock_ip_send__unsocketed_no_local_no_netif()); + CALL(test_sock_ip_send__unsocketed_no_netif()); + CALL(test_sock_ip_send__unsocketed_no_local()); + CALL(test_sock_ip_send__unsocketed()); + CALL(test_sock_ip_send__no_sock_no_netif()); + CALL(test_sock_ip_send__no_sock()); + + puts("ALL TESTS SUCCESSFUL"); + + return 0; +} diff --git a/tests/gnrc_sock_ip/stack.c b/tests/gnrc_sock_ip/stack.c new file mode 100644 index 000000000000..62196798290f --- /dev/null +++ b/tests/gnrc_sock_ip/stack.c @@ -0,0 +1,142 @@ +/* + * 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 + */ + +#include "stack.h" + +#include "net/gnrc/ipv6.h" +#include "net/gnrc/netif/hdr.h" +#include "net/gnrc/netreg.h" +#include "net/sock.h" +#include "sched.h" + +#define _MSG_QUEUE_SIZE (4) + +static msg_t _msg_queue[_MSG_QUEUE_SIZE]; +static gnrc_netreg_entry_t _ip_handler; + +void _net_init(void) +{ + msg_init_queue(_msg_queue, _MSG_QUEUE_SIZE); + gnrc_netreg_entry_init_pid(&_ip_handler, GNRC_NETREG_DEMUX_CTX_ALL, + sched_active_pid); +} + +void _prepare_send_checks(void) +{ + gnrc_netreg_register(GNRC_NETTYPE_IPV6, &_ip_handler); +} + +static gnrc_pktsnip_t *_build_ipv6_packet(const ipv6_addr_t *src, + const ipv6_addr_t *dst, uint8_t nh, + void *data, size_t data_len, + uint16_t netif) +{ + gnrc_pktsnip_t *netif_hdr, *ipv6, *payload; + ipv6_hdr_t *ipv6_hdr; + + if ((netif > INT16_MAX) || (data_len > UINT16_MAX)) { + return NULL; + } + + payload = gnrc_pktbuf_add(NULL, data, data_len, GNRC_NETTYPE_UNDEF); + if (payload == NULL) { + return NULL; + } + ipv6 = gnrc_ipv6_hdr_build(NULL, src, dst); + if (ipv6 == NULL) { + return NULL; + } + ipv6_hdr = ipv6->data; + ipv6_hdr->len = byteorder_htons((uint16_t)payload->size); + ipv6_hdr->nh = nh; + ipv6_hdr->hl = 64; + LL_APPEND(payload, ipv6); + netif_hdr = gnrc_netif_hdr_build(NULL, 0, NULL, 0); + if (netif_hdr == NULL) { + return NULL; + } + ((gnrc_netif_hdr_t *)netif_hdr->data)->if_pid = (kernel_pid_t)netif; + LL_APPEND(payload, netif_hdr); + return payload; +} + + +bool _inject_packet(const ipv6_addr_t *src, const ipv6_addr_t *dst, + uint8_t proto, void *data, size_t data_len, + uint16_t netif) +{ + gnrc_pktsnip_t *pkt = _build_ipv6_packet(src, dst, proto, data, data_len, + netif); + + if (pkt == NULL) { + return false; + } + /* put directly in mbox, dispatching to IPv6 would result in the packet + * being dropped, since dst is not on any interface */ + return (gnrc_netapi_dispatch_receive(GNRC_NETTYPE_IPV6, proto, pkt) > 0); +} + +bool _check_net(void) +{ + return (gnrc_pktbuf_is_sane() && gnrc_pktbuf_is_empty()); +} + +static inline bool _res(gnrc_pktsnip_t *pkt, bool res) +{ + gnrc_pktbuf_release(pkt); + return res; +} + +bool _check_packet(const ipv6_addr_t *src, const ipv6_addr_t *dst, + uint8_t proto, void *data, size_t data_len, + uint16_t netif) +{ + gnrc_pktsnip_t *pkt, *ipv6; + ipv6_hdr_t *ipv6_hdr; + msg_t msg; + + msg_receive(&msg); + if (msg.type != GNRC_NETAPI_MSG_TYPE_SND) { + return false; + } + pkt = msg.content.ptr; + if (netif != SOCK_ADDR_ANY_NETIF) { + gnrc_netif_hdr_t *netif_hdr; + + if (pkt->type != GNRC_NETTYPE_NETIF) { + return _res(pkt, false); + } + netif_hdr = pkt->data; + if (netif_hdr->if_pid != netif) { + return _res(pkt, false); + } + ipv6 = pkt->next; + } + else { + ipv6 = pkt; + } + if (ipv6->type != GNRC_NETTYPE_IPV6) { + return _res(pkt, false); + } + ipv6_hdr = ipv6->data; + return _res(pkt, (memcmp(src, &ipv6_hdr->src, sizeof(ipv6_addr_t)) == 0) && + (memcmp(dst, &ipv6_hdr->dst, sizeof(ipv6_addr_t)) == 0) && + (ipv6_hdr->nh == proto) && + (ipv6->next != NULL) && + (data_len == ipv6->next->size) && + (memcmp(data, ipv6->next->data, data_len) == 0)); +} + +/** @} */ diff --git a/tests/gnrc_sock_ip/stack.h b/tests/gnrc_sock_ip/stack.h new file mode 100644 index 000000000000..637ef466f5ec --- /dev/null +++ b/tests/gnrc_sock_ip/stack.h @@ -0,0 +1,90 @@ +/* + * 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 + */ +#ifndef STACK_H_ +#define STACK_H_ + +#include +#include + +#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 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 occured during injection + */ +bool _inject_packet(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 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_packet(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/gnrc_sock_ip/tests/01-run.py b/tests/gnrc_sock_ip/tests/01-run.py new file mode 100755 index 000000000000..a42c3f5c660e --- /dev/null +++ b/tests/gnrc_sock_ip/tests/01-run.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2016 Kaspar Schleiser +# +# 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 testfunc(child): + child.expect_exact(u"Calling test_sock_ip_create__EAFNOSUPPORT()") + child.expect_exact(u"Calling test_sock_ip_create__EINVAL_addr()") + child.expect_exact(u"Calling test_sock_ip_create__EINVAL_netif()") + child.expect_exact(u"Calling test_sock_ip_create__no_endpoints()") + child.expect_exact(u"Calling test_sock_ip_create__only_local()") + child.expect_exact(u"Calling test_sock_ip_create__only_local_reuse_ep()") + child.expect_exact(u"Calling test_sock_ip_create__only_remote()") + child.expect_exact(u"Calling test_sock_ip_create__full()") + child.expect_exact(u"Calling test_sock_ip_recv__EADDRNOTAVAIL()") + child.expect_exact(u"Calling test_sock_ip_recv__ENOBUFS()") + child.expect_exact(u"Calling test_sock_ip_recv__EPROTO()") + child.expect_exact(u"Calling test_sock_ip_recv__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_recv__socketed()") + child.expect_exact(u"Calling test_sock_ip_recv__socketed_with_remote()") + child.expect_exact(u"Calling test_sock_ip_recv__unsocketed()") + child.expect_exact(u"Calling test_sock_ip_recv__unsocketed_with_remote()") + child.expect_exact(u"Calling test_sock_ip_recv__with_timeout()") + child.expect_exact(u"Calling test_sock_ip_send__EAFNOSUPPORT()") + child.expect_exact(u"Calling test_sock_ip_send__EINVAL_addr()") + child.expect_exact(u"Calling test_sock_ip_send__EINVAL_netif()") + child.expect_exact(u"Calling test_sock_ip_send__ENOTCONN()") + child.expect_exact(u"Calling test_sock_ip_send__socketed_no_local_no_netif()") + child.expect_exact(u"Calling test_sock_ip_send__socketed_no_netif()") + child.expect_exact(u"Calling test_sock_ip_send__socketed_no_local()") + child.expect_exact(u"Calling test_sock_ip_send__socketed()") + child.expect_exact(u"Calling test_sock_ip_send__socketed_other_remote()") + child.expect_exact(u"Calling test_sock_ip_send__unsocketed_no_local_no_netif()") + child.expect_exact(u"Calling test_sock_ip_send__unsocketed_no_netif()") + child.expect_exact(u"Calling test_sock_ip_send__unsocketed_no_local()") + child.expect_exact(u"Calling test_sock_ip_send__unsocketed()") + child.expect_exact(u"Calling test_sock_ip_send__no_sock_no_netif()") + child.expect_exact(u"Calling test_sock_ip_send__no_sock()") + child.expect_exact(u"ALL TESTS SUCCESSFUL") + +if __name__ == "__main__": + sys.exit(testrunner.run(testfunc)) diff --git a/tests/gnrc_sock_udp/Makefile b/tests/gnrc_sock_udp/Makefile new file mode 100644 index 000000000000..bbfdfc3a7eb8 --- /dev/null +++ b/tests/gnrc_sock_udp/Makefile @@ -0,0 +1,21 @@ +APPLICATION = gnrc_sock_udp + +BOARD ?= native + +RIOTBASE ?= $(CURDIR)/../.. + +USEMODULE += gnrc_sock_check_reuse +USEMODULE += gnrc_sock_udp +USEMODULE += gnrc_ipv6 +USEMODULE += ps + +CFLAGS += -DDEVELHELP +CFLAGS += -DGNRC_PKTBUF_SIZE=200 +CFLAGS += -DTEST_SUITES + +QUIET ?= 1 + +include $(RIOTBASE)/Makefile.include + +test: + ./tests/01-run.py diff --git a/tests/gnrc_sock_udp/constants.h b/tests/gnrc_sock_udp/constants.h new file mode 100644 index 000000000000..fefe41663889 --- /dev/null +++ b/tests/gnrc_sock_udp/constants.h @@ -0,0 +1,44 @@ +/* + * 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 + */ +#ifndef CONSTANTS_H_ +#define CONSTANTS_H_ + + +#ifdef __cplusplus +extern "C" { +#endif + +#define _TEST_PORT_LOCAL (0x2c94) +#define _TEST_PORT_REMOTE (0xa615) +#define _TEST_NETIF (31) +#define _TEST_TIMEOUT (1000000U) +#define _TEST_ADDR_LOCAL { 0x7f, 0xc4, 0x11, 0x5a, 0xe6, 0x91, 0x8d, 0x5d, \ + 0x8c, 0xd1, 0x47, 0x07, 0xb7, 0x6f, 0x9b, 0x48 } +#define _TEST_ADDR_REMOTE { 0xe8, 0xb3, 0xb2, 0xe6, 0x70, 0xd4, 0x55, 0xba, \ + 0x93, 0xcf, 0x11, 0xe1, 0x72, 0x44, 0xc5, 0x9d } +#define _TEST_ADDR_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/gnrc_sock_udp/main.c b/tests/gnrc_sock_udp/main.c new file mode 100644 index 000000000000..043eae3e26e0 --- /dev/null +++ b/tests/gnrc_sock_udp/main.c @@ -0,0 +1,705 @@ +/* + * 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 + * @} + */ + +#include +#include +#include +#include + +#include "net/af.h" +#include "net/sock/udp.h" +#include "xtimer.h" + +#include "constants.h" +#include "stack.h" + +#define _TEST_BUFFER_SIZE (128) + +static uint8_t _test_buffer[_TEST_BUFFER_SIZE]; +static sock_udp_t _sock, _sock2; + +#define CALL(fn) puts("Calling " # fn); fn; tear_down() + +static void tear_down(void) +{ + sock_udp_close(&_sock); + memset(&_sock, 0, sizeof(_sock)); +} + +#ifdef MODULE_GNRC_SOCK_CHECK_REUSE +static void test_sock_udp_create__EADDRINUSE(void) +{ + static const sock_udp_ep_t local = { .family = AF_INET6, + .port = _TEST_PORT_LOCAL }; + + assert(0 == sock_udp_create(&_sock, &local, NULL, 0)); + assert(-EADDRINUSE == sock_udp_create(&_sock2, &local, NULL, 0)); +} +#endif + +static void test_sock_udp_create__EAFNOSUPPORT(void) +{ + /* port may not be NULL according to doc */ + static const sock_udp_ep_t local = { .family = AF_UNSPEC, + .port = _TEST_PORT_LOCAL }; + /* port may not be NULL according to doc */ + static const sock_udp_ep_t remote = { .family = AF_UNSPEC, + .port = _TEST_PORT_REMOTE }; + + assert(-EAFNOSUPPORT == sock_udp_create(&_sock, &local, NULL, SOCK_FLAGS_REUSE_EP)); + assert(-EAFNOSUPPORT == sock_udp_create(&_sock, NULL, &remote, SOCK_FLAGS_REUSE_EP)); +} + +static void test_sock_udp_create__EINVAL_addr(void) +{ + /* port may not be NULL according to doc */ + static const sock_udp_ep_t local = { .family = AF_INET6, .netif = _TEST_NETIF, + .port = _TEST_PORT_LOCAL }; + /* port may not be NULL according to doc */ + static const sock_udp_ep_t remote = { .family = AF_INET6, + .netif = _TEST_NETIF, + .port = _TEST_PORT_REMOTE }; + + assert(-EINVAL == sock_udp_create(&_sock, &local, &remote, SOCK_FLAGS_REUSE_EP)); +} + +static void test_sock_udp_create__EINVAL_netif(void) +{ + /* port may not be NULL according to doc */ + static const sock_udp_ep_t local = { .family = AF_INET6, .netif = _TEST_NETIF, + .port = _TEST_PORT_LOCAL }; + /* port may not be NULL according to doc */ + static const sock_udp_ep_t remote = { .family = AF_INET6, + .netif = (_TEST_NETIF + 1), + .port = _TEST_PORT_REMOTE, + .addr = { .ipv6 = _TEST_ADDR_REMOTE } }; + + assert(-EINVAL == sock_udp_create(&_sock, &local, &remote, SOCK_FLAGS_REUSE_EP)); +} + +static void test_sock_udp_create__no_endpoints(void) +{ + sock_udp_ep_t ep; + + assert(0 == sock_udp_create(&_sock, NULL, NULL, SOCK_FLAGS_REUSE_EP)); + assert(-EADDRNOTAVAIL == sock_udp_get_local(&_sock, &ep)); + assert(-ENOTCONN == sock_udp_get_remote(&_sock, &ep)); +} + +static void test_sock_udp_create__only_local(void) +{ + static const sock_udp_ep_t local = { .family = AF_INET6, + .port = _TEST_PORT_LOCAL }; + sock_udp_ep_t ep; + + assert(0 == sock_udp_create(&_sock, &local, NULL, SOCK_FLAGS_REUSE_EP)); + assert(0 == sock_udp_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(_TEST_PORT_LOCAL == ep.port); + assert(-ENOTCONN == sock_udp_get_remote(&_sock, &ep)); +} + +static void test_sock_udp_create__only_local_reuse_ep(void) +{ + static const sock_udp_ep_t local = { .family = AF_INET6, + .port = _TEST_PORT_LOCAL }; + sock_udp_ep_t ep, ep2; + + assert(0 == sock_udp_create(&_sock, &local, NULL, SOCK_FLAGS_REUSE_EP)); + assert(0 == sock_udp_create(&_sock2, &local, NULL, SOCK_FLAGS_REUSE_EP)); + assert(0 == sock_udp_get_local(&_sock, &ep)); + assert(0 == sock_udp_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(_TEST_PORT_LOCAL == ep.port); + assert(-ENOTCONN == sock_udp_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(_TEST_PORT_LOCAL == ep2.port); + assert(-ENOTCONN == sock_udp_get_remote(&_sock, &ep2)); + sock_udp_close(&_sock2); +} + +static void test_sock_udp_create__only_remote(void) +{ + static const ipv6_addr_t remote_addr = { .u8 = _TEST_ADDR_REMOTE }; + static const sock_udp_ep_t remote = { .family = AF_INET6, + .port = _TEST_PORT_REMOTE, + .addr = { .ipv6 = _TEST_ADDR_REMOTE } }; + sock_udp_ep_t ep; + + assert(0 == sock_udp_create(&_sock, NULL, &remote, SOCK_FLAGS_REUSE_EP)); + assert(-EADDRNOTAVAIL == sock_udp_get_local(&_sock, &ep)); + assert(0 == sock_udp_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_sock_udp_create__full(void) +{ + static const ipv6_addr_t remote_addr = { .u8 = _TEST_ADDR_REMOTE }; + static const sock_udp_ep_t local = { .family = AF_INET6, .netif = _TEST_NETIF, + .port = _TEST_PORT_LOCAL }; + static const sock_udp_ep_t remote = { .family = AF_INET6, + .port = _TEST_PORT_REMOTE, + .addr = { .ipv6 = _TEST_ADDR_REMOTE } }; + sock_udp_ep_t ep; + + assert(0 == sock_udp_create(&_sock, &local, &remote, SOCK_FLAGS_REUSE_EP)); + assert(0 == sock_udp_get_local(&_sock, &ep)); + assert(AF_INET6 == ep.family); + assert(memcmp(&ipv6_addr_unspecified, &ep.addr.ipv6, + sizeof(ipv6_addr_t)) == 0); + assert(_TEST_NETIF == ep.netif); + assert(_TEST_PORT_LOCAL == ep.port); + assert(0 == sock_udp_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_sock_udp_recv__EADDRNOTAVAIL(void) +{ + assert(0 == sock_udp_create(&_sock, NULL, NULL, SOCK_FLAGS_REUSE_EP)); + + assert(-EADDRNOTAVAIL == sock_udp_recv(&_sock, _test_buffer, + sizeof(_test_buffer), + SOCK_NO_TIMEOUT, NULL)); +} + +static void test_sock_udp_recv__EAGAIN(void) +{ + static const sock_udp_ep_t local = { .family = AF_INET6, .netif = _TEST_NETIF, + .port = _TEST_PORT_LOCAL }; + + assert(0 == sock_udp_create(&_sock, &local, NULL, SOCK_FLAGS_REUSE_EP)); + + assert(-EAGAIN == sock_udp_recv(&_sock, _test_buffer, sizeof(_test_buffer), + 0, NULL)); +} + +static void test_sock_udp_recv__ENOBUFS(void) +{ + static const ipv6_addr_t src_addr = { .u8 = _TEST_ADDR_REMOTE }; + static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR_LOCAL }; + static const sock_udp_ep_t local = { .family = AF_INET6, + .port = _TEST_PORT_LOCAL }; + + assert(0 == sock_udp_create(&_sock, &local, NULL, SOCK_FLAGS_REUSE_EP)); + assert(_inject_packet(&src_addr, &dst_addr, _TEST_PORT_REMOTE, + _TEST_PORT_LOCAL, "ABCD", sizeof("ABCD"), _TEST_NETIF)); + assert(-ENOBUFS == sock_udp_recv(&_sock, _test_buffer, 2, SOCK_NO_TIMEOUT, + NULL)); + assert(_check_net()); +} + +static void test_sock_udp_recv__EPROTO(void) +{ + static const ipv6_addr_t src_addr = { .u8 = _TEST_ADDR_WRONG }; + static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR_LOCAL }; + static const sock_udp_ep_t local = { .family = AF_INET6, + .port = _TEST_PORT_LOCAL }; + static const sock_udp_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR_REMOTE }, + .family = AF_INET6, + .port = _TEST_PORT_REMOTE }; + + assert(0 == sock_udp_create(&_sock, &local, &remote, SOCK_FLAGS_REUSE_EP)); + assert(_inject_packet(&src_addr, &dst_addr, _TEST_PORT_REMOTE, + _TEST_PORT_LOCAL, "ABCD", sizeof("ABCD"), + _TEST_NETIF)); + assert(-EPROTO == sock_udp_recv(&_sock, _test_buffer, sizeof(_test_buffer), + SOCK_NO_TIMEOUT, NULL)); + assert(_check_net()); +} + +static void test_sock_udp_recv__ETIMEDOUT(void) +{ + static const sock_udp_ep_t local = { .family = AF_INET6, .netif = _TEST_NETIF, + .port = _TEST_PORT_LOCAL }; + + assert(0 == sock_udp_create(&_sock, &local, NULL, SOCK_FLAGS_REUSE_EP)); + + puts(" * Calling sock_udp_recv()"); + assert(-ETIMEDOUT == sock_udp_recv(&_sock, _test_buffer, + sizeof(_test_buffer), _TEST_TIMEOUT, + NULL)); + printf(" * (timed out with timeout %lu)\n", (long unsigned)_TEST_TIMEOUT); +} + +static void test_sock_udp_recv__socketed(void) +{ + static const ipv6_addr_t src_addr = { .u8 = _TEST_ADDR_REMOTE }; + static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR_LOCAL }; + static const sock_udp_ep_t local = { .family = AF_INET6, + .port = _TEST_PORT_LOCAL }; + static const sock_udp_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR_REMOTE }, + .family = AF_INET6, + .port = _TEST_PORT_REMOTE }; + + assert(0 == sock_udp_create(&_sock, &local, &remote, SOCK_FLAGS_REUSE_EP)); + assert(_inject_packet(&src_addr, &dst_addr, _TEST_PORT_REMOTE, + _TEST_PORT_LOCAL, "ABCD", sizeof("ABCD"), + _TEST_NETIF)); + assert(sizeof("ABCD") == sock_udp_recv(&_sock, _test_buffer, + sizeof(_test_buffer), + SOCK_NO_TIMEOUT, NULL)); + assert(_check_net()); +} + +static void test_sock_udp_recv__socketed_with_remote(void) +{ + static const ipv6_addr_t src_addr = { .u8 = _TEST_ADDR_REMOTE }; + static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR_LOCAL }; + static const sock_udp_ep_t local = { .family = AF_INET6, + .port = _TEST_PORT_LOCAL }; + static const sock_udp_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR_REMOTE }, + .family = AF_INET6, + .port = _TEST_PORT_REMOTE }; + sock_udp_ep_t result; + + assert(0 == sock_udp_create(&_sock, &local, &remote, SOCK_FLAGS_REUSE_EP)); + assert(_inject_packet(&src_addr, &dst_addr, _TEST_PORT_REMOTE, + _TEST_PORT_LOCAL, "ABCD", sizeof("ABCD"), + _TEST_NETIF)); + assert(sizeof("ABCD") == sock_udp_recv(&_sock, _test_buffer, + sizeof(_test_buffer), + SOCK_NO_TIMEOUT, &result)); + assert(AF_INET6 == result.family); + assert(memcmp(&result.addr, &src_addr, sizeof(result.addr)) == 0); + assert(_TEST_PORT_REMOTE == result.port); + assert(_TEST_NETIF == result.netif); + assert(_check_net()); +} + +static void test_sock_udp_recv__unsocketed(void) +{ + static const ipv6_addr_t src_addr = { .u8 = _TEST_ADDR_REMOTE }; + static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR_LOCAL }; + static const sock_udp_ep_t local = { .addr = { .ipv6 = _TEST_ADDR_LOCAL }, + .family = AF_INET6, + .port = _TEST_PORT_LOCAL }; + + assert(0 == sock_udp_create(&_sock, &local, NULL, SOCK_FLAGS_REUSE_EP)); + assert(_inject_packet(&src_addr, &dst_addr, _TEST_PORT_REMOTE, + _TEST_PORT_LOCAL, "ABCD", sizeof("ABCD"), + _TEST_NETIF)); + assert(sizeof("ABCD") == sock_udp_recv(&_sock, _test_buffer, + sizeof(_test_buffer), + SOCK_NO_TIMEOUT, NULL)); + assert(_check_net()); +} + +static void test_sock_udp_recv__unsocketed_with_remote(void) +{ + static const ipv6_addr_t src_addr = { .u8 = _TEST_ADDR_REMOTE }; + static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR_LOCAL }; + static const sock_udp_ep_t local = { .family = AF_INET6, + .port = _TEST_PORT_LOCAL }; + sock_udp_ep_t result; + + assert(0 == sock_udp_create(&_sock, &local, NULL, SOCK_FLAGS_REUSE_EP)); + assert(_inject_packet(&src_addr, &dst_addr, _TEST_PORT_REMOTE, + _TEST_PORT_LOCAL, "ABCD", sizeof("ABCD"), + _TEST_NETIF)); + assert(sizeof("ABCD") == sock_udp_recv(&_sock, _test_buffer, + sizeof(_test_buffer), + SOCK_NO_TIMEOUT, &result)); + assert(AF_INET6 == result.family); + assert(memcmp(&result.addr, &src_addr, sizeof(result.addr)) == 0); + assert(_TEST_PORT_REMOTE == result.port); + assert(_TEST_NETIF == result.netif); + assert(_check_net()); +} + +static void test_sock_udp_recv__with_timeout(void) +{ + static const ipv6_addr_t src_addr = { .u8 = _TEST_ADDR_REMOTE }; + static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR_LOCAL }; + static const sock_udp_ep_t local = { .family = AF_INET6, + .port = _TEST_PORT_LOCAL }; + sock_udp_ep_t result; + + assert(0 == sock_udp_create(&_sock, &local, NULL, SOCK_FLAGS_REUSE_EP)); + assert(_inject_packet(&src_addr, &dst_addr, _TEST_PORT_REMOTE, + _TEST_PORT_LOCAL, "ABCD", sizeof("ABCD"), + _TEST_NETIF)); + assert(sizeof("ABCD") == sock_udp_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_PORT_REMOTE == result.port); + assert(_TEST_NETIF == result.netif); + assert(_check_net()); +} + +static void test_sock_udp_recv__non_blocking(void) +{ + static const ipv6_addr_t src_addr = { .u8 = _TEST_ADDR_REMOTE }; + static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR_LOCAL }; + static const sock_udp_ep_t local = { .family = AF_INET6, + .port = _TEST_PORT_LOCAL }; + sock_udp_ep_t result; + + assert(0 == sock_udp_create(&_sock, &local, NULL, SOCK_FLAGS_REUSE_EP)); + assert(_inject_packet(&src_addr, &dst_addr, _TEST_PORT_REMOTE, + _TEST_PORT_LOCAL, "ABCD", sizeof("ABCD"), + _TEST_NETIF)); + assert(sizeof("ABCD") == sock_udp_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_PORT_REMOTE == result.port); + assert(_TEST_NETIF == result.netif); + assert(_check_net()); +} + +static void test_sock_udp_send__EAFNOSUPPORT(void) +{ + static const sock_udp_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR_REMOTE }, + .family = AF_INET, + .port = _TEST_PORT_REMOTE }; + + assert(-EAFNOSUPPORT == sock_udp_send(NULL, "ABCD", sizeof("ABCD"), + &remote)); + assert(_check_net()); +} + +static void test_sock_udp_send__EINVAL_addr(void) +{ + static const sock_udp_ep_t local = { .addr = { .ipv6 = _TEST_ADDR_LOCAL }, + .family = AF_INET6, + .port = _TEST_PORT_REMOTE, + .netif = _TEST_NETIF }; + static const sock_udp_ep_t remote = { .family = AF_INET6, + .port = _TEST_PORT_REMOTE, + .netif = _TEST_NETIF }; + + assert(0 == sock_udp_create(&_sock, &local, NULL, SOCK_FLAGS_REUSE_EP)); + assert(-EINVAL == sock_udp_send(&_sock, "ABCD", sizeof("ABCD"), &remote)); + assert(_check_net()); +} + +static void test_sock_udp_send__EINVAL_netif(void) +{ + static const sock_udp_ep_t local = { .addr = { .ipv6 = _TEST_ADDR_LOCAL }, + .family = AF_INET6, + .port = _TEST_PORT_REMOTE, + .netif = _TEST_NETIF }; + static const sock_udp_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR_REMOTE }, + .family = AF_INET6, + .port = _TEST_PORT_REMOTE, + .netif = _TEST_NETIF + 1 }; + + assert(0 == sock_udp_create(&_sock, &local, NULL, SOCK_FLAGS_REUSE_EP)); + assert(-EINVAL == sock_udp_send(&_sock, "ABCD", sizeof("ABCD"), &remote)); + assert(_check_net()); +} + +static void test_sock_udp_send__EINVAL_port(void) +{ + static const sock_udp_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR_REMOTE }, + .family = AF_INET6 }; + + assert(-EINVAL == sock_udp_send(NULL, "ABCD", sizeof("ABCD"), &remote)); + assert(_check_net()); +} + +static void test_sock_udp_send__ENOTCONN(void) +{ + assert(0 == sock_udp_create(&_sock, NULL, NULL, SOCK_FLAGS_REUSE_EP)); + assert(-ENOTCONN == sock_udp_send(&_sock, "ABCD", sizeof("ABCD"), NULL)); + assert(_check_net()); +} + +static void test_sock_udp_send__socketed_no_local_no_netif(void) +{ + static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR_REMOTE }; + static const sock_udp_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR_REMOTE }, + .family = AF_INET6, + .port = _TEST_PORT_REMOTE }; + + assert(0 == sock_udp_create(&_sock, NULL, &remote, SOCK_FLAGS_REUSE_EP)); + assert(sizeof("ABCD") == sock_udp_send(&_sock, "ABCD", sizeof("ABCD"), + NULL)); + assert(_check_packet(&ipv6_addr_unspecified, &dst_addr, 0, + _TEST_PORT_REMOTE, "ABCD", sizeof("ABCD"), + SOCK_ADDR_ANY_NETIF, true)); + xtimer_usleep(1000); /* let GNRC stack finish */ + assert(_check_net()); +} + +static void test_sock_udp_send__socketed_no_netif(void) +{ + static const ipv6_addr_t src_addr = { .u8 = _TEST_ADDR_LOCAL }; + static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR_REMOTE }; + static const sock_udp_ep_t local = { .addr = { .ipv6 = _TEST_ADDR_LOCAL }, + .family = AF_INET6, + .port = _TEST_PORT_LOCAL }; + static const sock_udp_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR_REMOTE }, + .family = AF_INET6, + .port = _TEST_PORT_REMOTE }; + + assert(0 == sock_udp_create(&_sock, &local, &remote, SOCK_FLAGS_REUSE_EP)); + assert(sizeof("ABCD") == sock_udp_send(&_sock, "ABCD", sizeof("ABCD"), + NULL)); + assert(_check_packet(&src_addr, &dst_addr, _TEST_PORT_LOCAL, + _TEST_PORT_REMOTE, "ABCD", sizeof("ABCD"), + SOCK_ADDR_ANY_NETIF, false)); + xtimer_usleep(1000); /* let GNRC stack finish */ + assert(_check_net()); +} + +static void test_sock_udp_send__socketed_no_local(void) +{ + static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR_REMOTE }; + static const sock_udp_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR_REMOTE }, + .family = AF_INET6, + .netif = _TEST_NETIF, + .port = _TEST_PORT_REMOTE }; + + assert(0 == sock_udp_create(&_sock, NULL, &remote, SOCK_FLAGS_REUSE_EP)); + assert(sizeof("ABCD") == sock_udp_send(&_sock, "ABCD", sizeof("ABCD"), + NULL)); + assert(_check_packet(&ipv6_addr_unspecified, &dst_addr, 0, + _TEST_PORT_REMOTE, "ABCD", sizeof("ABCD"), _TEST_NETIF, + true)); + xtimer_usleep(1000); /* let GNRC stack finish */ + assert(_check_net()); +} + +static void test_sock_udp_send__socketed(void) +{ + static const ipv6_addr_t src_addr = { .u8 = _TEST_ADDR_LOCAL }; + static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR_REMOTE }; + static const sock_udp_ep_t local = { .addr = { .ipv6 = _TEST_ADDR_LOCAL }, + .family = AF_INET6, + .netif = _TEST_NETIF, + .port = _TEST_PORT_LOCAL }; + static const sock_udp_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR_REMOTE }, + .family = AF_INET6, + .port = _TEST_PORT_REMOTE }; + + assert(0 == sock_udp_create(&_sock, &local, &remote, SOCK_FLAGS_REUSE_EP)); + assert(sizeof("ABCD") == sock_udp_send(&_sock, "ABCD", sizeof("ABCD"), + NULL)); + assert(_check_packet(&src_addr, &dst_addr, _TEST_PORT_LOCAL, + _TEST_PORT_REMOTE, "ABCD", sizeof("ABCD"), + _TEST_NETIF, false)); + xtimer_usleep(1000); /* let GNRC stack finish */ + assert(_check_net()); +} + +static void test_sock_udp_send__socketed_other_remote(void) +{ + static const ipv6_addr_t src_addr = { .u8 = _TEST_ADDR_LOCAL }; + static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR_REMOTE }; + static const sock_udp_ep_t local = { .addr = { .ipv6 = _TEST_ADDR_LOCAL }, + .family = AF_INET6, + .netif = _TEST_NETIF, + .port = _TEST_PORT_LOCAL }; + static const sock_udp_ep_t sock_remote = { .addr = { .ipv6 = _TEST_ADDR_WRONG }, + .family = AF_INET6, + .port = _TEST_PORT_REMOTE + _TEST_PORT_LOCAL }; + static const sock_udp_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR_REMOTE }, + .family = AF_INET6, + .port = _TEST_PORT_REMOTE }; + + assert(0 == sock_udp_create(&_sock, &local, &sock_remote, SOCK_FLAGS_REUSE_EP)); + assert(sizeof("ABCD") == sock_udp_send(&_sock, "ABCD", sizeof("ABCD"), + &remote)); + assert(_check_packet(&src_addr, &dst_addr, _TEST_PORT_LOCAL, + _TEST_PORT_REMOTE, "ABCD", sizeof("ABCD"), + _TEST_NETIF, false)); + xtimer_usleep(1000); /* let GNRC stack finish */ + assert(_check_net()); +} + +static void test_sock_udp_send__unsocketed_no_local_no_netif(void) +{ + static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR_REMOTE }; + static const sock_udp_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR_REMOTE }, + .family = AF_INET6, + .port = _TEST_PORT_REMOTE }; + + assert(0 == sock_udp_create(&_sock, NULL, NULL, SOCK_FLAGS_REUSE_EP)); + assert(sizeof("ABCD") == sock_udp_send(&_sock, "ABCD", sizeof("ABCD"), + &remote)); + assert(_check_packet(&ipv6_addr_unspecified, &dst_addr, 0, + _TEST_PORT_REMOTE, "ABCD", sizeof("ABCD"), + SOCK_ADDR_ANY_NETIF, true)); + xtimer_usleep(1000); /* let GNRC stack finish */ + assert(_check_net()); +} + +static void test_sock_udp_send__unsocketed_no_netif(void) +{ + static const ipv6_addr_t src_addr = { .u8 = _TEST_ADDR_LOCAL }; + static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR_REMOTE }; + static const sock_udp_ep_t local = { .addr = { .ipv6 = _TEST_ADDR_LOCAL }, + .family = AF_INET6, + .port = _TEST_PORT_LOCAL }; + static const sock_udp_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR_REMOTE }, + .family = AF_INET6, + .port = _TEST_PORT_REMOTE }; + + assert(0 == sock_udp_create(&_sock, &local, NULL, SOCK_FLAGS_REUSE_EP)); + assert(sizeof("ABCD") == sock_udp_send(&_sock, "ABCD", sizeof("ABCD"), + &remote)); + assert(_check_packet(&src_addr, &dst_addr, _TEST_PORT_LOCAL, + _TEST_PORT_REMOTE, "ABCD", sizeof("ABCD"), + SOCK_ADDR_ANY_NETIF, false)); + xtimer_usleep(1000); /* let GNRC stack finish */ + assert(_check_net()); +} + +static void test_sock_udp_send__unsocketed_no_local(void) +{ + static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR_REMOTE }; + static const sock_udp_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR_REMOTE }, + .family = AF_INET6, + .netif = _TEST_NETIF, + .port = _TEST_PORT_REMOTE }; + + assert(0 == sock_udp_create(&_sock, NULL, NULL, SOCK_FLAGS_REUSE_EP)); + assert(sizeof("ABCD") == sock_udp_send(&_sock, "ABCD", sizeof("ABCD"), + &remote)); + assert(_check_packet(&ipv6_addr_unspecified, &dst_addr, 0, + _TEST_PORT_REMOTE, "ABCD", sizeof("ABCD"), _TEST_NETIF, + true)); + xtimer_usleep(1000); /* let GNRC stack finish */ + assert(_check_net()); +} + +static void test_sock_udp_send__unsocketed(void) +{ + static const ipv6_addr_t src_addr = { .u8 = _TEST_ADDR_LOCAL }; + static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR_REMOTE }; + static const sock_udp_ep_t local = { .addr = { .ipv6 = _TEST_ADDR_LOCAL }, + .family = AF_INET6, + .netif = _TEST_NETIF, + .port = _TEST_PORT_LOCAL }; + static const sock_udp_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR_REMOTE }, + .family = AF_INET6, + .port = _TEST_PORT_REMOTE }; + + assert(0 == sock_udp_create(&_sock, &local, NULL, SOCK_FLAGS_REUSE_EP)); + assert(sizeof("ABCD") == sock_udp_send(&_sock, "ABCD", sizeof("ABCD"), + &remote)); + assert(_check_packet(&src_addr, &dst_addr, _TEST_PORT_LOCAL, + _TEST_PORT_REMOTE, "ABCD", sizeof("ABCD"), + _TEST_NETIF, false)); + xtimer_usleep(1000); /* let GNRC stack finish */ + assert(_check_net()); +} + +static void test_sock_udp_send__no_sock_no_netif(void) +{ + static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR_REMOTE }; + static const sock_udp_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR_REMOTE }, + .family = AF_INET6, + .port = _TEST_PORT_REMOTE }; + + assert(sizeof("ABCD") == sock_udp_send(NULL, "ABCD", sizeof("ABCD"), + &remote)); + assert(_check_packet(&ipv6_addr_unspecified, &dst_addr, 0, + _TEST_PORT_REMOTE, "ABCD", sizeof("ABCD"), + SOCK_ADDR_ANY_NETIF, true)); + xtimer_usleep(1000); /* let GNRC stack finish */ + assert(_check_net()); +} + +static void test_sock_udp_send__no_sock(void) +{ + static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR_REMOTE }; + static const sock_udp_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR_REMOTE }, + .family = AF_INET6, + .netif = _TEST_NETIF, + .port = _TEST_PORT_REMOTE }; + + assert(sizeof("ABCD") == sock_udp_send(NULL, "ABCD", sizeof("ABCD"), + &remote)); + assert(_check_packet(&ipv6_addr_unspecified, &dst_addr, 0, + _TEST_PORT_REMOTE, "ABCD", sizeof("ABCD"), + _TEST_NETIF, true)); + xtimer_usleep(1000); /* let GNRC stack finish */ + assert(_check_net()); +} + +int main(void) +{ + _net_init(); + tear_down(); +#ifdef MODULE_GNRC_SOCK_CHECK_REUSE + CALL(test_sock_udp_create__EADDRINUSE()); +#endif + CALL(test_sock_udp_create__EAFNOSUPPORT()); + CALL(test_sock_udp_create__EINVAL_addr()); + CALL(test_sock_udp_create__EINVAL_netif()); + CALL(test_sock_udp_create__no_endpoints()); + CALL(test_sock_udp_create__only_local()); + CALL(test_sock_udp_create__only_local_reuse_ep()); + CALL(test_sock_udp_create__only_remote()); + CALL(test_sock_udp_create__full()); + /* sock_udp_close() is tested in tear_down() */ + /* sock_udp_get_local() is tested in sock_udp_create() tests */ + /* sock_udp_get_remote() is tested in sock_udp_create() tests */ + CALL(test_sock_udp_recv__EADDRNOTAVAIL()); + CALL(test_sock_udp_recv__EAGAIN()); + CALL(test_sock_udp_recv__ENOBUFS()); + CALL(test_sock_udp_recv__EPROTO()); + CALL(test_sock_udp_recv__ETIMEDOUT()); + CALL(test_sock_udp_recv__socketed()); + CALL(test_sock_udp_recv__socketed_with_remote()); + CALL(test_sock_udp_recv__unsocketed()); + CALL(test_sock_udp_recv__unsocketed_with_remote()); + CALL(test_sock_udp_recv__with_timeout()); + CALL(test_sock_udp_recv__non_blocking()); + _prepare_send_checks(); + CALL(test_sock_udp_send__EAFNOSUPPORT()); + CALL(test_sock_udp_send__EINVAL_addr()); + CALL(test_sock_udp_send__EINVAL_netif()); + CALL(test_sock_udp_send__EINVAL_port()); + CALL(test_sock_udp_send__ENOTCONN()); + CALL(test_sock_udp_send__socketed_no_local_no_netif()); + CALL(test_sock_udp_send__socketed_no_netif()); + CALL(test_sock_udp_send__socketed_no_local()); + CALL(test_sock_udp_send__socketed()); + CALL(test_sock_udp_send__socketed_other_remote()); + CALL(test_sock_udp_send__unsocketed_no_local_no_netif()); + CALL(test_sock_udp_send__unsocketed_no_netif()); + CALL(test_sock_udp_send__unsocketed_no_local()); + CALL(test_sock_udp_send__unsocketed()); + CALL(test_sock_udp_send__no_sock_no_netif()); + CALL(test_sock_udp_send__no_sock()); + + puts("ALL TESTS SUCCESSFUL"); + + return 0; +} diff --git a/tests/gnrc_sock_udp/stack.c b/tests/gnrc_sock_udp/stack.c new file mode 100644 index 000000000000..f9a556abcc50 --- /dev/null +++ b/tests/gnrc_sock_udp/stack.c @@ -0,0 +1,172 @@ +/* + * 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 + */ + + +#include "msg.h" +#include "net/gnrc/ipv6.h" +#include "net/gnrc/netif/hdr.h" +#include "net/gnrc/netreg.h" +#include "net/gnrc/udp.h" +#include "net/sock.h" +#include "sched.h" + +#include "stack.h" + +#define _MSG_QUEUE_SIZE (4) + +static msg_t _msg_queue[_MSG_QUEUE_SIZE]; +static gnrc_netreg_entry_t _udp_handler; + +void _net_init(void) +{ + msg_init_queue(_msg_queue, _MSG_QUEUE_SIZE); + gnrc_netreg_entry_init_pid(&_udp_handler, GNRC_NETREG_DEMUX_CTX_ALL, + sched_active_pid); +} + +void _prepare_send_checks(void) +{ + gnrc_netreg_register(GNRC_NETTYPE_UDP, &_udp_handler); +} + +static gnrc_pktsnip_t *_build_udp_packet(const ipv6_addr_t *src, + const ipv6_addr_t *dst, + uint16_t src_port, uint16_t dst_port, + void *data, size_t data_len, + uint16_t netif) +{ + gnrc_pktsnip_t *netif_hdr, *ipv6, *udp; + udp_hdr_t *udp_hdr; + ipv6_hdr_t *ipv6_hdr; + uint16_t csum = 0; + + if ((netif > INT16_MAX) || ((sizeof(udp_hdr_t) + data_len) > UINT16_MAX)) { + return NULL; + } + + udp = gnrc_pktbuf_add(NULL, NULL, sizeof(udp_hdr_t) + data_len, + GNRC_NETTYPE_UNDEF); + if (udp == NULL) { + return NULL; + } + udp_hdr = udp->data; + udp_hdr->src_port = byteorder_htons(src_port); + udp_hdr->dst_port = byteorder_htons(dst_port); + udp_hdr->length = byteorder_htons((uint16_t)udp->size); + udp_hdr->checksum.u16 = 0; + memcpy(udp_hdr + 1, data, data_len); + csum = inet_csum(csum, (uint8_t *)udp->data, udp->size); + ipv6 = gnrc_ipv6_hdr_build(NULL, src, dst); + if (ipv6 == NULL) { + return NULL; + } + ipv6_hdr = ipv6->data; + ipv6_hdr->len = byteorder_htons((uint16_t)udp->size); + ipv6_hdr->nh = PROTNUM_UDP; + ipv6_hdr->hl = 64; + csum = ipv6_hdr_inet_csum(csum, ipv6_hdr, PROTNUM_UDP, (uint16_t)udp->size); + if (csum == 0xffff) { + udp_hdr->checksum = byteorder_htons(csum); + } + else { + udp_hdr->checksum = byteorder_htons(~csum); + } + LL_APPEND(udp, ipv6); + netif_hdr = gnrc_netif_hdr_build(NULL, 0, NULL, 0); + if (netif_hdr == NULL) { + return NULL; + } + ((gnrc_netif_hdr_t *)netif_hdr->data)->if_pid = (kernel_pid_t)netif; + LL_APPEND(udp, netif_hdr); + return udp; +} + + +bool _inject_packet(const ipv6_addr_t *src, const ipv6_addr_t *dst, + uint16_t src_port, uint16_t dst_port, + void *data, size_t data_len, uint16_t netif) +{ + gnrc_pktsnip_t *pkt = _build_udp_packet(src, dst, src_port, dst_port, + data, data_len, netif); + + if (pkt == NULL) { + return false; + } + return (gnrc_netapi_dispatch_receive(GNRC_NETTYPE_UDP, + GNRC_NETREG_DEMUX_CTX_ALL, pkt) > 0); +} + +bool _check_net(void) +{ + return (gnrc_pktbuf_is_sane() && gnrc_pktbuf_is_empty()); +} + +static inline bool _res(gnrc_pktsnip_t *pkt, bool res) +{ + gnrc_pktbuf_release(pkt); + return res; +} + +bool _check_packet(const ipv6_addr_t *src, const ipv6_addr_t *dst, + uint16_t src_port, uint16_t dst_port, + void *data, size_t data_len, uint16_t iface, + bool random_src_port) +{ + gnrc_pktsnip_t *pkt, *ipv6, *udp; + ipv6_hdr_t *ipv6_hdr; + udp_hdr_t *udp_hdr; + msg_t msg; + + msg_receive(&msg); + if (msg.type != GNRC_NETAPI_MSG_TYPE_SND) { + return false; + } + pkt = msg.content.ptr; + if (iface != SOCK_ADDR_ANY_NETIF) { + gnrc_netif_hdr_t *netif_hdr; + + if (pkt->type != GNRC_NETTYPE_NETIF) { + return _res(pkt, false); + } + netif_hdr = pkt->data; + if (netif_hdr->if_pid != iface) { + return _res(pkt, false); + } + ipv6 = pkt->next; + } + else { + ipv6 = pkt; + } + if (ipv6->type != GNRC_NETTYPE_IPV6) { + return _res(pkt, false); + } + ipv6_hdr = ipv6->data; + udp = gnrc_pktsnip_search_type(ipv6, GNRC_NETTYPE_UDP); + if (udp == NULL) { + return _res(pkt, false); + } + udp_hdr = udp->data; + return _res(pkt, (memcmp(src, &ipv6_hdr->src, sizeof(ipv6_addr_t)) == 0) && + (memcmp(dst, &ipv6_hdr->dst, sizeof(ipv6_addr_t)) == 0) && + (ipv6_hdr->nh == PROTNUM_UDP) && + (random_src_port || (src_port == byteorder_ntohs(udp_hdr->src_port))) && + (dst_port == byteorder_ntohs(udp_hdr->dst_port)) && + (udp->next != NULL) && + (data_len == udp->next->size) && + (memcmp(data, udp->next->data, data_len) == 0)); +} + + +/** @} */ diff --git a/tests/gnrc_sock_udp/stack.h b/tests/gnrc_sock_udp/stack.h new file mode 100644 index 000000000000..415057a3a912 --- /dev/null +++ b/tests/gnrc_sock_udp/stack.h @@ -0,0 +1,95 @@ +/* + * 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 + */ +#ifndef STACK_H_ +#define STACK_H_ + +#include +#include + +#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 UDP packet into the stack + * + * @param[in] src The source address of the UDP packet + * @param[in] dst The destination address of the UDP packet + * @param[in] src_port The source port of the UDP packet + * @param[in] dst_port The destination port of the UDP packet + * @param[in] data The payload of the UDP packet + * @param[in] data_len The payload length of the UDP packet + * @param[in] netif The interface the packet came over + * + * @return true, if packet was successfully injected + * @return false, if an error occured during injection + */ +bool _inject_packet(const ipv6_addr_t *src, const ipv6_addr_t *dst, + uint16_t src_port, uint16_t dst_port, + 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 UDP packet was sent by the networking component + * + * @param[in] src Expected source address of the UDP packet + * @param[in] dst Expected destination address of the UDP packet + * @param[in] src_port Expected source port of the UDP packet + * @param[in] dst_port Expected destination port of the UDP packet + * @param[in] data Expected payload of the UDP packet + * @param[in] data_len Expected payload length of the UDP packet + * @param[in] netif Expected interface the packet is supposed to + * be send over + * @param[in] random_src_port Do not check source port, it might be random + * + * @return true, if all parameters match as expected + * @return false, if not. + */ +bool _check_packet(const ipv6_addr_t *src, const ipv6_addr_t *dst, + uint16_t src_port, uint16_t dst_port, + void *data, size_t data_len, uint16_t netif, + bool random_src_port); + + +#ifdef __cplusplus +} +#endif + +#endif /* STACK_H_ */ +/** @} */ diff --git a/tests/gnrc_sock_udp/tests/01-run.py b/tests/gnrc_sock_udp/tests/01-run.py new file mode 100755 index 000000000000..8d0df09d6327 --- /dev/null +++ b/tests/gnrc_sock_udp/tests/01-run.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2016 Kaspar Schleiser +# +# 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 testfunc(child): + child.expect_exact(u"Calling test_sock_udp_create__EADDRINUSE()") + child.expect_exact(u"Calling test_sock_udp_create__EAFNOSUPPORT()") + child.expect_exact(u"Calling test_sock_udp_create__EINVAL_addr()") + child.expect_exact(u"Calling test_sock_udp_create__EINVAL_netif()") + child.expect_exact(u"Calling test_sock_udp_create__no_endpoints()") + child.expect_exact(u"Calling test_sock_udp_create__only_local()") + child.expect_exact(u"Calling test_sock_udp_create__only_local_reuse_ep()") + child.expect_exact(u"Calling test_sock_udp_create__only_remote()") + child.expect_exact(u"Calling test_sock_udp_create__full()") + child.expect_exact(u"Calling test_sock_udp_recv__EADDRNOTAVAIL()") + child.expect_exact(u"Calling test_sock_udp_recv__EAGAIN()") + child.expect_exact(u"Calling test_sock_udp_recv__ENOBUFS()") + child.expect_exact(u"Calling test_sock_udp_recv__EPROTO()") + child.expect_exact(u"Calling test_sock_udp_recv__ETIMEDOUT()") + child.match # get to ensure program reached that point + start = datetime.now() + child.expect_exact(u" * Calling sock_udp_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_udp_recv__socketed()") + child.expect_exact(u"Calling test_sock_udp_recv__socketed_with_remote()") + child.expect_exact(u"Calling test_sock_udp_recv__unsocketed()") + child.expect_exact(u"Calling test_sock_udp_recv__unsocketed_with_remote()") + child.expect_exact(u"Calling test_sock_udp_recv__with_timeout()") + child.expect_exact(u"Calling test_sock_udp_recv__non_blocking()") + child.expect_exact(u"Calling test_sock_udp_send__EAFNOSUPPORT()") + child.expect_exact(u"Calling test_sock_udp_send__EINVAL_addr()") + child.expect_exact(u"Calling test_sock_udp_send__EINVAL_netif()") + child.expect_exact(u"Calling test_sock_udp_send__EINVAL_port()") + child.expect_exact(u"Calling test_sock_udp_send__ENOTCONN()") + child.expect_exact(u"Calling test_sock_udp_send__socketed_no_local_no_netif()") + child.expect_exact(u"Calling test_sock_udp_send__socketed_no_netif()") + child.expect_exact(u"Calling test_sock_udp_send__socketed_no_local()") + child.expect_exact(u"Calling test_sock_udp_send__socketed()") + child.expect_exact(u"Calling test_sock_udp_send__socketed_other_remote()") + child.expect_exact(u"Calling test_sock_udp_send__unsocketed_no_local_no_netif()") + child.expect_exact(u"Calling test_sock_udp_send__unsocketed_no_netif()") + child.expect_exact(u"Calling test_sock_udp_send__unsocketed_no_local()") + child.expect_exact(u"Calling test_sock_udp_send__unsocketed()") + child.expect_exact(u"Calling test_sock_udp_send__no_sock_no_netif()") + child.expect_exact(u"Calling test_sock_udp_send__no_sock()") + child.expect_exact(u"ALL TESTS SUCCESSFUL") + +if __name__ == "__main__": + sys.exit(testrunner.run(testfunc))