diff --git a/sys/Makefile b/sys/Makefile index 8ba54ef260f5..b376737e164e 100644 --- a/sys/Makefile +++ b/sys/Makefile @@ -140,6 +140,9 @@ endif ifneq (,$(filter sock_async_event,$(USEMODULE))) DIRS += net/sock/async/event endif +ifneq (,$(filter sock_async_msg,$(USEMODULE))) + DIRS += net/sock/async/msg +endif ifneq (,$(filter sock_dns,$(USEMODULE))) DIRS += net/application_layer/sock_dns endif diff --git a/sys/Makefile.include b/sys/Makefile.include index a2f6112d0d29..ad34871da278 100644 --- a/sys/Makefile.include +++ b/sys/Makefile.include @@ -119,6 +119,10 @@ ifneq (,$(filter sock_async_event,$(USEMODULE))) include $(RIOTBASE)/sys/net/sock/async/event/Makefile.include endif +ifneq (,$(filter sock_async_msg,$(USEMODULE))) + include $(RIOTBASE)/sys/net/sock/async/msg/Makefile.include +endif + ifneq (,$(filter ssp,$(USEMODULE))) include $(RIOTBASE)/sys/ssp/Makefile.include endif diff --git a/sys/include/net/sock/async/msg.h b/sys/include/net/sock/async/msg.h new file mode 100644 index 000000000000..ce64ea75f637 --- /dev/null +++ b/sys/include/net/sock/async/msg.h @@ -0,0 +1,313 @@ +/* + * Copyright (C) 2019 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup net_sock_async_msg Asynchronous sock with messaging / IPC + * @ingroup net_sock + * @brief Provides an implementation of asynchronous sock for + * @ref core_msg + * + * How To Use + * ---------- + * + * You need to [include](@ref including-modules) at least one module that + * implements a [`sock` API](@ref net_sock) and the module `sock_async_event` in + * your application's Makefile. + * + * For the following example [`sock_udp`](@ref net_sock_udp) is used. It is + * however easily adaptable for other `sock` types: + * + * ### An asynchronous UDP Echo server using the event API + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c} + * #include + * + * #include "msg.h" + * #include "thread.h" + * #include "net/sock/udp.h" + * #include "net/sock/async/msg.h" + * + * msg_t queue[8]; + * uint8_t buf[128]; + * + * void handler(sock_udp_t *sock, sock_async_flags_t type, void *arg) + * { + * (void)arg; + * if (type & SOCK_ASYNC_MSG_RECV) { + * sock_udp_ep_t remote; + * ssize_t res; + * + * if ((res = sock_udp_recv(sock, buf, sizeof(buf), 0, + * &remote)) >= 0) { + * puts("Received a message"); + * if (sock_udp_send(sock, buf, res, &remote) < 0) { + * puts("Error sending reply"); + * } + * } + * } + * } + * + * int main(void) + * { + * sock_udp_ep_t local = SOCK_IPV6_EP_ANY; + * + * local.port = 12345; + * + * if (sock_udp_create(&sock, &local, NULL, 0) < 0) { + * puts("Error creating UDP sock"); + * return 1; + * } + * + * msg_init_queue(&queue, 8); + * sock_udp_msg_init(&sock, thread_getpid(), handler, NULL); + * while (1) { + * msg_t msg; + * + * msg_receive(&msg); + * if (sock_async_msg_is(&msg)) { + * sock_async_msg_handler(&msg); + * } + * } + * return 0; + * } + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Above you see a simple UDP echo server. Don't forget to also + * @ref including-modules "include" the IPv6 module of your networking + * implementation (e.g. `gnrc_ipv6_default` for @ref net_gnrc GNRC) and at least + * one network device. + * + * After including the header file for @ref net_sock_udp "UDP sock" and + * @ref net_sock_async_msg "asynchronous handling", we create a message queue + * `queue` (note: [must be of a size equal to a power of two] + * (@ref msg_init_queue)) and allocate some buffer space `buf` to store the + * data received by the server: + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c} + * #include "net/sock/udp.h" + * #include "net/sock/async/msg.h" + * + * msg_t queue[8]; + * uint8_t buf[128]; + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * We then define an event handler in form of a function. The event handler + * checks if the triggering event was a receive event by checking the flags + * provided with sock_event_t::type. If it is a receive event it copies the + * incoming message to `buf` and its sender into `remote` using @ref + * sock_udp_recv(). Note, that we use @ref sock_udp_recv() non-blocking by + * setting `timeout` to 0. If an error occurs on receive, we just ignore it and + * return from the event handler. + * + * If we receive a message we use its `remote` to reply. In case of an error on + * send, we print an according message: + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c} + * void handler(sock_udp_t *sock, sock_async_flags_t type, void *arg) + * { + * (void)arg; + * if (type & SOCK_ASYNC_MSG_RECV) { + * sock_udp_ep_t remote; + * ssize_t res; + * + * if ((res = sock_udp_recv(sock, buf, sizeof(buf), 0, + * &remote)) >= 0) { + * puts("Received a message"); + * if (sock_udp_send(sock, buf, res, &remote) < 0) { + * puts("Error sending reply"); + * } + * } + * } + * } + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * To be able to listen for incoming packets we bind the `sock` by setting a + * local end point with a port (`12345` in this case). + * + * We then proceed to create the `sock`. It is bound to `local` and thus listens + * for UDP packets with @ref udp_hdr_t::dst_port "destination port" `12345`. + * Since we don't need any further configuration we set the flags to 0. + * In case of an error we stop the program: + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c} + * sock_udp_ep_t local = SOCK_IPV6_EP_ANY; + * sock_udp_t sock; + * + * local.port = 12345; + * + * if (sock_udp_create(&sock, &local, NULL, 0) < 0) { + * puts("Error creating UDP sock"); + * return 1; + * } + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * We then initialize the message queue for the running thread to allow for + * asynchronous IPC (see @ref core_msg) and register the current thread as the + * asynchronous event handler for `sock` using the previously defined + * `handler()` function. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c} + * msg_init_queue(&queue, 8); + * sock_udp_msg_init(&sock, thread_getpid(), handler, NULL); + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * The thread then blocks to wait for an IPC message and check if the event + * triggering the IPC message was an asynchronous sock event. + * If it is an asynchronous sock event, the handler function is called, which + * in turn calls the registered callback: + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c} + * msg_t msg; + * + * msg_receive(&msg); + * if (sock_async_msg_is(&msg)) { + * sock_async_msg_handler(&msg); + * } + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * @{ + * + * @file + * @brief + * + * @author Martine Lenders + */ +#ifndef NET_SOCK_ASYNC_MSG_H +#define NET_SOCK_ASYNC_MSG_H + +#include "kernel_types.h" +#include "msg.h" +#include "net/sock/async.h" +#ifdef MODULE_SOCK_DTLS +#include "net/sock/dtls.h" +#endif /* MODULE_SOCK_DTLS */ +#include "net/sock/ip.h" +#include "net/sock/tcp.h" +#include "net/sock/udp.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Message type mask for all messages notifying events on the sock. + * + * The event flags of a sock event are identified by checking the respective + * bits in the message type: + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c} + * if (sock_async_msg_type(&msg) && msg.type & SOCK_EVENT_CONN_RDY) { + * // ... the connection of msg.content.ptr is ready + * } + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#define SOCK_ASYNC_MSG_TYPE_MASK (0xff00) + +/** + * @brief Message type ID for all messages notifying events on the sock. + */ +#define SOCK_ASYNC_MSG_TYPE_ID (0x8500) + +/** + * @brief Checks if a given message is a @ref net_sock_async_msg message + * + * @param[in] msg A message. + */ +static inline bool sock_async_msg_is(msg_t *msg) +{ + return (msg->type & SOCK_ASYNC_MSG_TYPE_MASK) == SOCK_ASYNC_MSG_TYPE_ID; +} + +static inline sock_async_flags_t sock_async_msg_flags(msg_t *msg) +{ + return msg->type & ~SOCK_ASYNC_MSG_TYPE_MASK; +} + +/** + * @brief Handles a `sock_async_msg` IPC message and calls the associated + * callback. + * + * @pre @ref sock_async_msg_is(@p msg) + * + * @param[in] msg An IPC message. + */ +void sock_async_msg_handler(msg_t *msg); + +#if defined(MODULE_SOCK_DTLS) || defined(DOXYGEN) +/** + * @brief Makes a DTLS sock able to handle asynchronous events using + * @ref core_msg. + * + * @param[in] sock A DTLS sock object. + * @param[in] target Thread to send the events to. + * + * @note Only available with module `sock_dtls`. + */ +void sock_dtls_msg_init(sock_dtls_t *sock, kernel_pid_t target, + sock_dtls_cb_t handler, void *handler_arg); +#endif /* defined(MODULE_SOCK_DTLS) || defined(DOXYGEN) */ + +#if defined(MODULE_SOCK_IP) || defined(DOXYGEN) +/** + * @brief Makes a raw IPv4/IPv6 sock able to handle asynchronous events using + * @ref core_msg. + * + * @param[in] sock A raw IPv4/IPv6 sock object. + * @param[in] target Thread to send the events to. + * + * @note Only available with module `sock_ip`. + */ +void sock_ip_msg_init(sock_ip_t *sock, kernel_pid_t target, + sock_ip_cb_t handler, void *handler_arg); +#endif /* defined(MODULE_SOCK_IP) || defined(DOXYGEN) */ + +#if defined(MODULE_SOCK_TCP) || defined(DOXYGEN) +/** + * @brief Makes a TCP sock able to handle asynchronous events using + * @ref core_msg. + * + * @param[in] sock A TCP sock object. + * @param[in] target Thread to send the events to. + * + * @note Only available with module `sock_tcp`. + */ +void sock_tcp_msg_init(sock_tcp_t *sock, kernel_pid_t target, + sock_tcp_cb_t handler, void *handler_arg); + +/** + * @brief Makes a TCP listening queue able to handle asynchronous events using + * @ref core_msg. + * + * @param[in] sock A TCP listening queue. + * @param[in] target Thread to send the events to. + * + * @note Only available with module `sock_tcp`. + */ +void sock_tcp_queue_msg_init(sock_tcp_queue_t *sock, kernel_pid_t target, + sock_udp_tcp_t handler, void *handler_arg); +#endif /* defined(MODULE_SOCK_TCP) || defined(DOXYGEN) */ + +#if defined(MODULE_SOCK_UDP) || defined(DOXYGEN) +/** + * @brief Makes a UDP sock able to handle asynchronous events using + * @ref core_msg. + * + * @param[in] sock A UDP sock object. + * @param[in] target Thread to send the events to. + * + * @note Only available with module `sock_udp`. + */ +void sock_udp_msg_init(sock_udp_t *sock, kernel_pid_t target, + sock_udp_cb_t handler, void *handler_arg); +#endif /* defined(MODULE_SOCK_UDP) || defined(DOXYGEN) */ + +#ifdef __cplusplus +} +#endif + +#endif /* NET_SOCK_ASYNC_MSG_H */ +/** @} */ diff --git a/sys/net/sock/async/msg/Makefile b/sys/net/sock/async/msg/Makefile new file mode 100644 index 000000000000..2f40ab1731eb --- /dev/null +++ b/sys/net/sock/async/msg/Makefile @@ -0,0 +1,3 @@ +MODULE := sock_async_msg + +include $(RIOTBASE)/Makefile.base diff --git a/sys/net/sock/async/msg/Makefile.include b/sys/net/sock/async/msg/Makefile.include new file mode 100644 index 000000000000..b0ad28b0e849 --- /dev/null +++ b/sys/net/sock/async/msg/Makefile.include @@ -0,0 +1,2 @@ +USEMODULE_INCLUDES += $(RIOTBASE)/sys/net/sock/async/msg +CFLAGS += -DSOCK_HAS_ASYNC -DSOCK_HAS_ASYNC_CTX diff --git a/sys/net/sock/async/msg/sock_async_ctx.h b/sys/net/sock/async/msg/sock_async_ctx.h new file mode 100644 index 000000000000..e38432c757c5 --- /dev/null +++ b/sys/net/sock/async/msg/sock_async_ctx.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2019 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @addtogroup net_sock_async_msg + * @brief + * @{ + * + * @file + * @brief Type definitions for asynchronous socks with @ref core_msg + * + * @author Martine Lenders + */ +#ifndef SOCK_ASYNC_CTX_H +#define SOCK_ASYNC_CTX_H + +#include "kernel_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Generalized callback type + */ +typedef union { + /** + * @brief anything goes + */ + void (*generic)(void *, sock_async_flags_t, void *); +#ifdef MODULE_SOCK_DTLS + sock_dtls_cb_t dtls; /**< DTLS callback */ +#endif +#ifdef MODULE_SOCK_IP + sock_ip_cb_t ip; /**< IP callback */ +#endif +#ifdef MODULE_SOCK_TCP + sock_tcp_cb_t tcp; /**< TCP callback */ + sock_tcp_queue_cb_t tcp_queue; /**< TCP queue callback */ +#endif +#ifdef MODULE_SOCK_UDP + sock_udp_cb_t udp; /**< UDP callback */ +#endif +} sock_msg_cb_t; + +/** + * @brief Asynchronous context for @ref net_sock_async_msg + */ +typedef struct { + void *sock; /**< generic pointer to a @ref net_sock object */ + sock_msg_cb_t cb; /**< callback for the message handler */ + void *cb_arg; /**< callback argument for sock_async_ctx_t::cb */ + kernel_pid_t pid; /**< PID to send the IPC message to */ +} sock_async_ctx_t; + +#ifdef __cplusplus +} +#endif + +#endif /* SOCK_ASYNC_CTX_H */ +/** @} */ diff --git a/sys/net/sock/async/msg/sock_async_msg.c b/sys/net/sock/async/msg/sock_async_msg.c new file mode 100644 index 000000000000..92ce84095781 --- /dev/null +++ b/sys/net/sock/async/msg/sock_async_msg.c @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2019 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 + +#include "net/sock/async/msg.h" + +void sock_async_msg_handler(msg_t *msg) +{ + sock_async_ctx_t *ctx = msg->content.ptr; + + assert(sock_async_msg_is(msg)); + ctx->cb.generic(ctx->sock, sock_async_msg_flags(msg), ctx->cb_arg); +} + +static void _cb(sock_async_ctx_t *ctx, sock_async_flags_t flags, void *arg) +{ + msg_t msg = { + .type = SOCK_ASYNC_MSG_TYPE_ID | flags, + .content = { .ptr = ctx }, + }; + ctx->cb_arg = arg; + msg_try_send(&msg, ctx->pid); +} + +#ifdef MODULE_SOCK_DTLS +static void _dtls_cb(sock_dtls_t *sock, sock_async_flags_t flags, void *arg) +{ + _cb(sock_dtls_get_async_ctx(sock), flags, arg); +} + +void sock_dtls_msg_init(sock_dtls_t *sock, kernel_pid_t target, + sock_dtls_cb_t handler, void *handler_arg) +{ + sock_async_ctx_t *ctx = sock_dtls_get_async_ctx(sock); + + ctx->sock = sock; + ctx->cb.dtls = handler; + ctx->pid = target; + sock_dtls_set_cb(sock, _dtls_cb, handler_arg); +} +#endif /* MODULE_SOCK_DTLS */ + +#ifdef MODULE_SOCK_IP +static void _ip_cb(sock_ip_t *sock, sock_async_flags_t flags, void *arg) +{ + _cb(sock_ip_get_async_ctx(sock), flags, arg); +} + +void sock_ip_msg_init(sock_ip_t *sock, kernel_pid_t target, + sock_ip_cb_t handler, void *handler_arg) +{ + sock_async_ctx_t *ctx = sock_ip_get_async_ctx(sock); + + ctx->sock = sock; + ctx->cb.ip = handler; + ctx->pid = target; + sock_ip_set_cb(sock, _ip_cb, handler_arg); +} +#endif /* MODULE_SOCK_IP */ + +#ifdef MODULE_SOCK_TCP +static void _tcp_cb(sock_tcp_t *sock, sock_async_flags_t flags, void *arg) +{ + _cb(sock_tcp_get_async_ctx(sock), flags, arg); +} + +void sock_tcp_msg_init(sock_tcp_t *sock, kernel_pid_t target, + sock_tcp_cb_t handler, void *handler_arg) +{ + sock_async_ctx_t *ctx = sock_tcp_get_async_ctx(sock); + + ctx->sock = sock; + ctx->cb.tcp = handler; + ctx->pid = target; + sock_tcp_set_cb(sock, _tcp_cb, handler_arg); +} + +static void _tcp_queue_cb(sock_tcp_queue_t *queue, sock_async_flags_t flags, + void *arg) +{ + _cb(sock_tcp_queue_get_async_ctx(queue), flags, arg); +} + +void sock_tcp_queue_msg_init(sock_tcp_queue_t *queue, kernel_pid_t target, + sock_tcp_queue_cb_t handler, void *handler_arg) +{ + sock_async_ctx_t *ctx = sock_tcp_queue_get_async_ctx(queue); + + ctx->sock = sock; + ctx->cb.tcp_queue = handler; + ctx->pid = target; + sock_tcp_queue_set_cb(queue, _tcp_queue_cb, handler_arg); +} +#endif /* MODULE_SOCK_TCP */ + +#ifdef MODULE_SOCK_UDP +static void _udp_cb(sock_udp_t *sock, sock_async_flags_t flags, void *arg) +{ + _cb(sock_udp_get_async_ctx(sock), flags, arg); +} + +void sock_udp_msg_init(sock_udp_t *sock, kernel_pid_t target, + sock_udp_cb_t handler, void *handler_arg) +{ + sock_async_ctx_t *ctx = sock_udp_get_async_ctx(sock); + + ctx->sock = sock; + ctx->cb.udp = handler; + ctx->pid = target; + sock_udp_set_cb(sock, _udp_cb, handler_arg); +} +#endif /* MODULE_SOCK_UDP */ + +/** @} */ diff --git a/tests/gnrc_sock_async_msg/Makefile b/tests/gnrc_sock_async_msg/Makefile new file mode 100644 index 000000000000..e68df6aa655b --- /dev/null +++ b/tests/gnrc_sock_async_msg/Makefile @@ -0,0 +1,21 @@ +include ../Makefile.tests_common + +USEMODULE += auto_init_gnrc_netif +USEMODULE += gnrc_ipv6_hdr +USEMODULE += gnrc_pktdump +USEMODULE += gnrc_sock_async +USEMODULE += gnrc_sock_ip +USEMODULE += gnrc_sock_udp +USEMODULE += sock_async_msg +USEMODULE += od +USEMODULE += xtimer + +CFLAGS += -DSOCK_HAS_IPV6 +# Set GNRC_PKTBUF_SIZE via CFLAGS if not being set via Kconfig. +ifndef CONFIG_GNRC_PKTBUF_SIZE + CFLAGS += -DCONFIG_GNRC_PKTBUF_SIZE=200 +endif +# mock IPv6 gnrc_nettype +CFLAGS += -DTEST_SUITES -DGNRC_NETTYPE_IPV6=GNRC_NETTYPE_TEST + +include $(RIOTBASE)/Makefile.include diff --git a/tests/gnrc_sock_async_msg/main.c b/tests/gnrc_sock_async_msg/main.c new file mode 100644 index 000000000000..be65fa5072e8 --- /dev/null +++ b/tests/gnrc_sock_async_msg/main.c @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2019 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 + * + * @author Martine Lenders + * + * @} + */ + +#include +#include "msg.h" +#include "net/ipv6/addr.h" +#include "net/ipv6/hdr.h" +#include "net/sock/ip.h" +#include "net/sock/udp.h" +#include "net/gnrc.h" +#include "net/gnrc/ipv6/hdr.h" +#include "net/gnrc/pktdump.h" +#include "net/gnrc/udp.h" +#include "net/protnum.h" +#include "od.h" +#include "test_utils/expect.h" +#include "thread.h" + +#include "net/sock/async/msg.h" + +#define RECV_BUFFER_SIZE (128) + +#define MSG_QUEUE_SIZE (4U) + +#define TEST_PORT (38664U) +#define TEST_LOCAL { 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } +#define TEST_REMOTE { 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 } +#define TEST_PAYLOAD { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef } + +static const uint8_t _test_local[] = TEST_LOCAL; +static const uint8_t _test_remote[] = TEST_REMOTE; +static const uint8_t _test_payload[] = TEST_PAYLOAD; + +static gnrc_netreg_entry_t _pktdump; + +static char _addr_str[IPV6_ADDR_MAX_STR_LEN]; +static uint8_t _buffer[128]; +static sock_ip_t _ip_sock; +static sock_udp_t _udp_sock; + +/* module is not compiled in, so provide this function for the test */ +ipv6_hdr_t *gnrc_ipv6_get_header(gnrc_pktsnip_t *pkt) +{ + gnrc_pktsnip_t *tmp = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_IPV6); + if (tmp == NULL) { + return NULL; + } + + expect(tmp->data != NULL); + expect(tmp->size >= sizeof(ipv6_hdr_t)); + expect(ipv6_hdr_is(tmp->data)); + + return ((ipv6_hdr_t*) tmp->data); +} + +static void _recv_udp(sock_udp_t *sock, sock_async_flags_t flags, void *arg) +{ + expect(strcmp(arg, "test") == 0); + printf("UDP event triggered: %04X\n", flags); + if (flags & SOCK_ASYNC_MSG_RECV) { + sock_udp_ep_t remote; + ssize_t res; + + if ((res = sock_udp_recv(sock, _buffer, sizeof(_buffer), 0, + &remote)) >= 0) { + printf("Received UDP packet from [%s]:%u:\n", + ipv6_addr_to_str(_addr_str, (ipv6_addr_t *)remote.addr.ipv6, + sizeof(_addr_str)), + remote.port); + od_hex_dump(_buffer, res, OD_WIDTH_DEFAULT); + } + } + if (flags & SOCK_ASYNC_MSG_SENT) { + puts("UDP message successfully sent"); + } +} + +static void _recv_ip(sock_ip_t *sock, sock_async_flags_t flags, void *arg) +{ + expect(strcmp(arg, "test") == 0); + printf("IP event triggered: %04X\n", flags); + if (flags & SOCK_ASYNC_MSG_RECV) { + sock_ip_ep_t remote; + ssize_t res; + + if ((res = sock_ip_recv(sock, _buffer, sizeof(_buffer), 0, + &remote)) >= 0) { + printf("Received IP packet from [%s]:\n", + ipv6_addr_to_str(_addr_str, (ipv6_addr_t *)remote.addr.ipv6, + sizeof(_addr_str))); + od_hex_dump(_buffer, res, OD_WIDTH_DEFAULT); + } + } + if (flags & SOCK_ASYNC_MSG_SENT) { + puts("IP message successfully sent"); + } +} + +int main(void) +{ + gnrc_pktsnip_t *pkt; + sock_udp_ep_t local = SOCK_IPV6_EP_ANY; + sock_udp_ep_t remote = SOCK_IPV6_EP_ANY; + msg_t msgq[MSG_QUEUE_SIZE]; + + msg_init_queue(msgq, ARRAY_SIZE(msgq)); + + /* register for IPv6 to have a target */ + gnrc_netreg_entry_init_pid(&_pktdump, GNRC_NETREG_DEMUX_CTX_ALL, + gnrc_pktdump_pid); + gnrc_netreg_register(GNRC_NETTYPE_IPV6, &_pktdump); + + local.port = TEST_PORT; + sock_udp_create(&_udp_sock, &local, NULL, 0); + sock_ip_create(&_ip_sock, (sock_ip_ep_t *)&local, NULL, PROTNUM_UDP, 0); + + sock_udp_msg_init(&_udp_sock, thread_getpid(), _recv_udp, "test"); + sock_ip_msg_init(&_ip_sock, thread_getpid(), _recv_ip, "test"); + memcpy(remote.addr.ipv6, _test_remote, sizeof(_test_remote)); + remote.port = TEST_PORT - 1; + + sock_udp_send(&_udp_sock, _test_payload, sizeof(_test_payload), &remote); + sock_ip_send(&_ip_sock, _test_payload, sizeof(_test_payload), + PROTNUM_RESERVED, (sock_ip_ep_t *)&remote); + + /* create packet to inject for reception */ + pkt = gnrc_netif_hdr_build(NULL, 0, NULL, 0); + expect(pkt != NULL); + memset(pkt->data, 0, pkt->size); + pkt = gnrc_ipv6_hdr_build(pkt, (ipv6_addr_t *)&_test_remote, + (ipv6_addr_t *)&_test_local); + expect(pkt != NULL); + /* module is not compiled in, so set header type manually */ + pkt->type = GNRC_NETTYPE_IPV6; + pkt = gnrc_udp_hdr_build(pkt, TEST_PORT - 1, TEST_PORT); + expect(pkt != NULL); + pkt = gnrc_pktbuf_add(pkt, _test_payload, sizeof(_test_payload), + GNRC_NETTYPE_UNDEF); + expect(pkt != NULL); + /* we dispatch twice, so hold one time */ + gnrc_pktbuf_hold(pkt, 1); + + /* trigger receive on UDP sock */ + gnrc_netapi_dispatch_receive(GNRC_NETTYPE_UDP, TEST_PORT, pkt); + /* trigger receive on IP sock */ + gnrc_netapi_dispatch_receive(GNRC_NETTYPE_IPV6, PROTNUM_UDP, pkt); + while (true) { + msg_t msg; + + msg_receive(&msg); + + if (sock_async_msg_is(&msg)) { + sock_async_msg_handler(&msg); + } + } + return 0; +} + +/** @} */ diff --git a/tests/gnrc_sock_async_msg/tests/01-run.py b/tests/gnrc_sock_async_msg/tests/01-run.py new file mode 100755 index 000000000000..f08f75378038 --- /dev/null +++ b/tests/gnrc_sock_async_msg/tests/01-run.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2019 Freie Universität Berlin +# +# This file is subject to the terms and conditions of the GNU Lesser +# General Public License v2.1. See the file LICENSE in the top level +# directory for more details. + +import sys +from testrunner import run + + +def testfunc(child): + child.expect_exact("UDP event triggered: 0020") + child.expect_exact("UDP message successfully sent") + child.expect_exact("IP event triggered: 0020") + child.expect_exact("IP message successfully sent") + child.expect_exact("UDP event triggered: 0010") + child.expect_exact("Received UDP packet from [fe80::2]:38663:") + child.expect_exact("00000000 01 23 45 67 89 AB CD EF") + child.expect_exact("IP event triggered: 0010") + child.expect_exact("Received IP packet from [fe80::2]:") + child.expect_exact("00000000 01 23 45 67 89 AB CD EF") + + +if __name__ == "__main__": + sys.exit(run(testfunc))