From 1b2303771e8343fcf7f670d19e96d6b6eeb881ac Mon Sep 17 00:00:00 2001 From: "Martine S. Lenders" Date: Fri, 22 May 2020 16:24:38 +0200 Subject: [PATCH 1/4] sock_async: change flags for new sock_dtls behavior --- sys/include/net/sock/async/types.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sys/include/net/sock/async/types.h b/sys/include/net/sock/async/types.h index 9d80376bc764..f5bd1498910d 100644 --- a/sys/include/net/sock/async/types.h +++ b/sys/include/net/sock/async/types.h @@ -47,9 +47,9 @@ typedef struct sock_dtls sock_dtls_t; /**< forward declare for async */ * * @param[in] sock The sock the event happened on * @param[in] flags The event flags. Expected values are - * - @ref SOCK_ASYNC_CONN_RDY (if a session you created becomes ready), + * - @ref SOCK_ASYNC_CONN_RDY (if a session becomes ready), * - @ref SOCK_ASYNC_CONN_FIN (if a created session was destroyed), - * - @ref SOCK_ASYNC_CONN_RECV (if a peer tries to create a session), + * - @ref SOCK_ASYNC_CONN_RECV (if a handshake message needs to be completed) * - @ref SOCK_ASYNC_MSG_RECV, * - @ref SOCK_ASYNC_MSG_SENT, * - @ref SOCK_ASYNC_PATH_PROP, or From 4013c616f7460aac18dd6cefe9a773be3d688216 Mon Sep 17 00:00:00 2001 From: Martine Lenders Date: Mon, 9 Dec 2019 13:51:29 +0100 Subject: [PATCH 2/4] tinydtls: add `sock_async` support for `sock_dtls` --- pkg/tinydtls/contrib/sock_dtls.c | 224 ++++++++++++++++++++++--- pkg/tinydtls/include/sock_dtls_types.h | 24 +++ 2 files changed, 222 insertions(+), 26 deletions(-) diff --git a/pkg/tinydtls/contrib/sock_dtls.c b/pkg/tinydtls/contrib/sock_dtls.c index 637b29aefe59..930ac6963471 100644 --- a/pkg/tinydtls/contrib/sock_dtls.c +++ b/pkg/tinydtls/contrib/sock_dtls.c @@ -17,9 +17,15 @@ */ #include "dtls.h" +#include "log.h" #include "net/sock/dtls.h" #include "net/credman.h" +#if SOCK_HAS_ASYNC +#include "net/sock/async.h" +#include "net/sock/async/event.h" +#endif + #define ENABLE_DEBUG (0) #include "debug.h" #include "dtls_debug.h" @@ -77,6 +83,11 @@ static int _read(struct dtls_context_t *ctx, session_t *session, uint8_t *buf, sock->buffer.data = buf; sock->buffer.datalen = len; sock->buffer.session = session; +#ifdef SOCK_HAS_ASYNC + if (sock->async_cb != NULL) { + sock->async_cb(sock, SOCK_ASYNC_MSG_RECV, sock->async_cb_arg); + } +#endif return len; } @@ -103,7 +114,7 @@ static int _event(struct dtls_context_t *ctx, session_t *session, (void)session; sock_dtls_t *sock = dtls_get_app_data(ctx); - msg_t msg = { .type = code }; + msg_t msg = { .type = code, .content.ptr = session }; #ifdef ENABLE_DEBUG switch (code) { case DTLS_EVENT_CONNECT: @@ -120,6 +131,23 @@ static int _event(struct dtls_context_t *ctx, session_t *session, if (!level && (code != DTLS_EVENT_CONNECT)) { mbox_put(&sock->mbox, &msg); } +#ifdef SOCK_HAS_ASYNC + if (sock->async_cb != NULL) { + switch (code) { + case DTLS_ALERT_CLOSE_NOTIFY: + /* peer closed their session */ + sock->async_cb(sock, SOCK_ASYNC_CONN_FIN, sock->async_cb_arg); + break; + case DTLS_EVENT_CONNECTED: + /* we received a session handshake initialization */ + sock->async_cb(sock, SOCK_ASYNC_CONN_RECV, + sock->async_cb_arg); + break; + default: + break; + } + } +#endif return 0; } @@ -250,6 +278,10 @@ int sock_dtls_create(sock_dtls_t *sock, sock_udp_t *udp_sock, sock->udp_sock = udp_sock; sock->buffer.data = NULL; +#ifdef SOCK_HAS_ASYNC + sock->async_cb = NULL; + sock->buf_ctx = NULL; +#endif /* SOCK_HAS_ASYNC */ sock->role = role; sock->tag = tag; sock->dtls_ctx = dtls_new_context(sock); @@ -320,14 +352,14 @@ void sock_dtls_session_destroy(sock_dtls_t *sock, sock_dtls_session_t *remote) ssize_t sock_dtls_send(sock_dtls_t *sock, sock_dtls_session_t *remote, const void *data, size_t len, uint32_t timeout) { + int res; + assert(sock); assert(remote); assert(data); /* check if session exists, if not create session first then send */ if (!dtls_get_peer(sock->dtls_ctx, &remote->dtls_session)) { - int res; - if (timeout == 0) { return -ENOTCONN; } @@ -366,8 +398,47 @@ ssize_t sock_dtls_send(sock_dtls_t *sock, sock_dtls_session_t *remote, } } - return dtls_write(sock->dtls_ctx, &remote->dtls_session, - (uint8_t *)data, len); + res = dtls_write(sock->dtls_ctx, &remote->dtls_session, + (uint8_t *)data, len); +#ifdef SOCK_HAS_ASYNC + if ((res >= 0) && (sock->async_cb != NULL)) { + sock->async_cb(sock, SOCK_ASYNC_MSG_SENT, sock->async_cb_arg); + } +#endif /* SOCK_HAS_ASYNC */ + return res; +} + +#if SOCK_HAS_ASYNC +/** + * @brief Checks for and iterates for more data chunks within the network + * stacks anternal packet buffer + * + * When no more chunks exists, `data_ctx` assures cleaning up the internal + * buffer state and `sock_udp_recv_buf()` returns 0. + * + * @see @ref sock_udp_recv_buf(). + */ +static void _check_more_chunks(sock_udp_t *udp_sock, void **data, + void **data_ctx, sock_udp_ep_t *remote) +{ + ssize_t res; + + while ((res = sock_udp_recv_buf(udp_sock, data, data_ctx, 0, remote)) > 0) { + /* TODO: remove and adapt _copy_buffer() to add remaining data when + * tinydtls supports chunked datagram payload */ + if (IS_ACTIVE(DEVELHELP)) { + LOG_ERROR("sock_dtls: Chunked datagram payload currently not " + "supported yet by tinydtls\n"); + } + } +} +#endif + +static inline void _copy_session(sock_dtls_t *sock, sock_dtls_session_t *remote) +{ + memcpy(&remote->dtls_session, sock->buffer.session, + sizeof(remote->dtls_session)); + _session_to_ep(&remote->dtls_session, &remote->ep); } static ssize_t _copy_buffer(sock_dtls_t *sock, sock_dtls_session_t *remote, @@ -380,15 +451,61 @@ static ssize_t _copy_buffer(sock_dtls_t *sock, sock_dtls_session_t *remote, if (buflen > max_len) { return -ENOBUFS; } +#if SOCK_HAS_ASYNC + if (sock->buf_ctx != NULL) { + memcpy(data, buf, sock->buffer.datalen); + _copy_session(sock, remote); + _check_more_chunks(sock->udp_sock, (void **)&buf, &sock->buf_ctx, + &remote->ep); + if (sock->async_cb && + /* is there a message in the sock's mbox? */ + cib_avail(&sock->mbox.cib)) { + if (sock->buffer.data) { + sock->async_cb(sock, SOCK_ASYNC_MSG_RECV, + sock->async_cb_arg); + } + else { + sock->async_cb(sock, SOCK_ASYNC_CONN_RECV, + sock->async_cb_arg); + } + } + return buflen; + } +#else + (void)remote; +#endif /* use `memmove()` as tinydtls reuses `data` to store decrypted data with an * offset in `buf`. This prevents problems with overlapping buffers. */ memmove(data, buf, buflen); - memcpy(&remote->dtls_session, sock->buffer.session, - sizeof(remote->dtls_session)); - _session_to_ep(&remote->dtls_session, &remote->ep); + _copy_session(sock, remote); return buflen; } +static ssize_t _complete_handshake(sock_dtls_t *sock, + sock_dtls_session_t *remote, + const session_t *session) +{ + memcpy(&remote->dtls_session, session, sizeof(remote->dtls_session)); +#ifdef SOCK_HAS_ASYNC + if (sock->async_cb) { + sock_async_flags_t flags = SOCK_ASYNC_CONN_RDY; + + if (cib_avail(&sock->mbox.cib)) { + if (sock->buffer.data) { + flags |= SOCK_ASYNC_MSG_RECV; + } + else { + flags |= SOCK_ASYNC_CONN_RECV; + } + } + sock->async_cb(sock, flags, sock->async_cb_arg); + } +#else + (void)sock; +#endif + return -SOCK_DTLS_HANDSHAKE; +} + ssize_t sock_dtls_recv(sock_dtls_t *sock, sock_dtls_session_t *remote, void *data, size_t max_len, uint32_t timeout) { @@ -396,16 +513,21 @@ ssize_t sock_dtls_recv(sock_dtls_t *sock, sock_dtls_session_t *remote, assert(data); assert(remote); - if (sock->buffer.data != NULL) { - /* there is already decrypted data available */ - return _copy_buffer(sock, remote, data, max_len); - } - /* loop breaks when timeout or application data read */ while (1) { + ssize_t res; uint32_t start_recv = xtimer_now_usec(); - ssize_t res = sock_udp_recv(sock->udp_sock, data, max_len, timeout, - &remote->ep); + msg_t msg; + + if (sock->buffer.data != NULL) { + return _copy_buffer(sock, remote, data, max_len); + } + else if (mbox_try_get(&sock->mbox, &msg) && + msg.type == DTLS_EVENT_CONNECTED) { + return _complete_handshake(sock, remote, msg.content.ptr); + } + res = sock_udp_recv(sock->udp_sock, data, max_len, timeout, + &remote->ep); if (res <= 0) { DEBUG("sock_dtls: error receiving UDP packet: %d\n", (int)res); return res; @@ -418,21 +540,11 @@ ssize_t sock_dtls_recv(sock_dtls_t *sock, sock_dtls_session_t *remote, if ((timeout != SOCK_NO_TIMEOUT) && (timeout != 0)) { timeout = _update_timeout(start_recv, timeout); } - - msg_t msg; - if (sock->buffer.data != NULL) { - return _copy_buffer(sock, remote, data, max_len); - } - else if (mbox_try_get(&sock->mbox, &msg) && - msg.type == DTLS_EVENT_CONNECTED) { - return -SOCK_DTLS_HANDSHAKE; - } - else if (timeout == 0) { + if (timeout == 0) { DEBUG("sock_dtls: timed out while decrypting message\n"); return -ETIMEDOUT; } } - } void sock_dtls_close(sock_dtls_t *sock) @@ -468,4 +580,64 @@ static inline uint32_t _update_timeout(uint32_t start, uint32_t timeout) return (diff > timeout) ? 0: timeout - diff; } +#ifdef SOCK_HAS_ASYNC +void _udp_cb(sock_udp_t *udp_sock, sock_async_flags_t flags, void *ctx) +{ + sock_dtls_t *sock = ctx; + + if (flags & SOCK_ASYNC_MSG_RECV) { + sock_dtls_session_t remote; + void *data = NULL; + void *data_ctx = NULL; + + ssize_t res = sock_udp_recv_buf(udp_sock, &data, &data_ctx, 0, + &remote.ep); + if (res <= 0) { + DEBUG("sock_dtls: error receiving UDP packet: %d\n", (int)res); + return; + } + + /* prevent overriding already set `buf_ctx` */ + if (sock->buf_ctx != NULL) { + DEBUG("sock_dtls: unable to store buffer asynchronously\n"); + _check_more_chunks(udp_sock, &data, &data_ctx, &remote.ep); + return; + } + _ep_to_session(&remote.ep, &remote.dtls_session); + sock->buf_ctx = data_ctx; + res = dtls_handle_message(sock->dtls_ctx, &remote.dtls_session, + data, res); + if (sock->buffer.data == NULL) { + _check_more_chunks(udp_sock, &data, &data_ctx, &remote.ep); + sock->buf_ctx = NULL; + } + } + if ((flags & SOCK_ASYNC_PATH_PROP) && sock->async_cb) { + /* just hand this event type up the stack */ + sock->async_cb(sock, SOCK_ASYNC_PATH_PROP, sock->async_cb_arg); + } +} + +void sock_dtls_set_cb(sock_dtls_t *sock, sock_dtls_cb_t cb, void *cb_arg) +{ + sock->async_cb = cb; + sock->async_cb_arg = cb_arg; + if (IS_USED(MODULE_SOCK_ASYNC_EVENT)) { + sock_async_ctx_t *ctx = sock_dtls_get_async_ctx(sock); + if (ctx->queue) { + sock_udp_event_init(sock->udp_sock, ctx->queue, _udp_cb, sock); + return; + } + } + sock_udp_set_cb(sock->udp_sock, _udp_cb, sock); +} + +#ifdef SOCK_HAS_ASYNC_CTX +sock_async_ctx_t *sock_dtls_get_async_ctx(sock_dtls_t *sock) +{ + return &sock->async_ctx; +} +#endif /* SOCK_HAS_ASYNC_CTX */ +#endif /* SOCK_HAS_ASYNC */ + /** @} */ diff --git a/pkg/tinydtls/include/sock_dtls_types.h b/pkg/tinydtls/include/sock_dtls_types.h index a62c04e552e8..c3b6d014510c 100644 --- a/pkg/tinydtls/include/sock_dtls_types.h +++ b/pkg/tinydtls/include/sock_dtls_types.h @@ -22,6 +22,9 @@ #include "dtls.h" #include "net/sock/udp.h" #include "net/credman.h" +#ifdef SOCK_HAS_ASYNC +#include "net/sock/async/types.h" +#endif #ifdef __cplusplus extern "C" { @@ -37,6 +40,27 @@ extern "C" { struct sock_dtls { dtls_context_t *dtls_ctx; /**< TinyDTLS context for sock */ sock_udp_t *udp_sock; /**< Underlying UDP sock to use */ +#if defined(SOCK_HAS_ASYNC) || defined(DOXYGEN) + /** + * @brief Network stack internal buffer context. + */ + void *buf_ctx; + /** + * @brief Asynchronous event callback + * + * @note Only available if @ref SOCK_HAS_ASYNC is defined + */ + sock_dtls_cb_t async_cb; + void *async_cb_arg; /**< asynchronous callback arg */ +#if defined(SOCK_HAS_ASYNC_CTX) || defined(DOXYGEN) + /** + * @brief Asynchronous event context + * + * @note Only available if @ref SOCK_HAS_ASYNC_CTX is defined + */ + sock_async_ctx_t async_ctx; +#endif +#endif mbox_t mbox; /**< Mailbox for internal event handling */ msg_t mbox_queue[SOCK_DTLS_MBOX_SIZE]; /**< Queue for struct From 7dc4f78d0cb2c4b4b87fcbd855c3a37fe6cde99e Mon Sep 17 00:00:00 2001 From: "Martine S. Lenders" Date: Tue, 16 Jun 2020 14:04:48 +0200 Subject: [PATCH 3/4] sock_async: add missing license header to `net/sock/async/types.h` --- sys/include/net/sock/async/types.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sys/include/net/sock/async/types.h b/sys/include/net/sock/async/types.h index f5bd1498910d..ee24db994039 100644 --- a/sys/include/net/sock/async/types.h +++ b/sys/include/net/sock/async/types.h @@ -1,3 +1,11 @@ +/* + * Copyright (C) 2019-20 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 Sock extension for asynchronous access * From 155953fd6061b67ef9296ec3b826ea34f19f6aaa Mon Sep 17 00:00:00 2001 From: "Martine S. Lenders" Date: Fri, 22 May 2020 16:25:05 +0200 Subject: [PATCH 4/4] tests: provide test for tinydtls with sock_async --- tests/pkg_tinydtls_sock_async/Makefile | 45 ++++ tests/pkg_tinydtls_sock_async/Makefile.ci | 42 +++ tests/pkg_tinydtls_sock_async/README.md | 42 +++ tests/pkg_tinydtls_sock_async/dtls-client.c | 243 ++++++++++++++++++ tests/pkg_tinydtls_sock_async/dtls-server.c | 203 +++++++++++++++ tests/pkg_tinydtls_sock_async/main.c | 43 ++++ .../pkg_tinydtls_sock_async/tinydtls_common.h | 39 +++ tests/pkg_tinydtls_sock_async/tinydtls_keys.h | 66 +++++ 8 files changed, 723 insertions(+) create mode 100644 tests/pkg_tinydtls_sock_async/Makefile create mode 100644 tests/pkg_tinydtls_sock_async/Makefile.ci create mode 100644 tests/pkg_tinydtls_sock_async/README.md create mode 100644 tests/pkg_tinydtls_sock_async/dtls-client.c create mode 100644 tests/pkg_tinydtls_sock_async/dtls-server.c create mode 100644 tests/pkg_tinydtls_sock_async/main.c create mode 100644 tests/pkg_tinydtls_sock_async/tinydtls_common.h create mode 100644 tests/pkg_tinydtls_sock_async/tinydtls_keys.h diff --git a/tests/pkg_tinydtls_sock_async/Makefile b/tests/pkg_tinydtls_sock_async/Makefile new file mode 100644 index 000000000000..3b27398ef34e --- /dev/null +++ b/tests/pkg_tinydtls_sock_async/Makefile @@ -0,0 +1,45 @@ +include ../Makefile.tests_common + +# TinyDTLS only has support for 32-bit architectures ATM +FEATURES_REQUIRED += arch_32bit + +# Include packages that pull up and auto-init the link layer. +# NOTE: 6LoWPAN will be included if IEEE802.15.4 devices are present +USEMODULE += gnrc_netdev_default +USEMODULE += auto_init_gnrc_netif +# Specify the mandatory networking modules for IPv6 and UDP +USEMODULE += gnrc_ipv6_default +USEMODULE += gnrc_sock_async +USEMODULE += gnrc_sock_udp +USEMODULE += sock_async_event +USEMODULE += event_thread_medium +USEMODULE += event_timeout + +# Use tinydtls for sock_dtls +USEMODULE += tinydtls_sock_dtls + +# Add also the shell, some shell commands +USEMODULE += shell +USEMODULE += shell_commands + +# Bitfield to log open sessions +USEMODULE += bitfield + +# UDP Port to use (20220 is default for DTLS). +DTLS_PORT ?= 20220 +CFLAGS += -DDTLS_DEFAULT_PORT=$(DTLS_PORT) + +# NOTE: If no cipher suite is selected, CONFIG_DTLS_PSK is used by default. +# This section should be commented out if using Kconfig +# This adds support for TLS_PSK_WITH_AES_128_CCM_8 +# CFLAGS += -DCONFIG_DTLS_PSK +# This adds support for TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 +# CFLAGS += -DCONFIG_DTLS_ECC + +# Uncomment to enable debug logs +# CFLAGS += -DCONFIG_DTLS_DEBUG + +# FIXME: This is a temporary patch +CFLAGS += -DTHREAD_STACKSIZE_MAIN=\(2*THREAD_STACKSIZE_LARGE\) + +include $(RIOTBASE)/Makefile.include diff --git a/tests/pkg_tinydtls_sock_async/Makefile.ci b/tests/pkg_tinydtls_sock_async/Makefile.ci new file mode 100644 index 000000000000..e531fcf862a1 --- /dev/null +++ b/tests/pkg_tinydtls_sock_async/Makefile.ci @@ -0,0 +1,42 @@ +BOARD_INSUFFICIENT_MEMORY := \ + airfy-beacon \ + b-l072z-lrwan1 \ + blackpill \ + blackpill-128kib \ + bluepill \ + bluepill-128kib \ + calliope-mini \ + cc2650-launchpad \ + cc2650stk \ + hifive1 \ + hifive1b \ + i-nucleo-lrwan1 \ + im880b \ + lsn50 \ + maple-mini \ + microbit \ + nrf51dongle \ + nrf6310 \ + nucleo-f030r8 \ + nucleo-f031k6 \ + nucleo-f042k6 \ + nucleo-f070rb \ + nucleo-f072rb \ + nucleo-f103rb \ + nucleo-f302r8 \ + nucleo-f303k8 \ + nucleo-f334r8 \ + nucleo-l031k6 \ + nucleo-l053r8 \ + nucleo-l073rz \ + olimexino-stm32 \ + opencm904 \ + saml10-xpro \ + saml11-xpro \ + spark-core \ + stm32f030f4-demo \ + stm32f0discovery \ + stm32l0538-disco \ + stm32mindev \ + yunjia-nrf51822 \ + # diff --git a/tests/pkg_tinydtls_sock_async/README.md b/tests/pkg_tinydtls_sock_async/README.md new file mode 100644 index 000000000000..031a496a6136 --- /dev/null +++ b/tests/pkg_tinydtls_sock_async/README.md @@ -0,0 +1,42 @@ +# DTLS sock async test application + +This is a test application for DTLS sock `sock_dtls_t` with `sock_async`. + +## Testing using RIOT `native` + +For testing, we can use two RIOT `native` RIOT instances. For that first we +need to prepare the network interfaces: + +```bash +$ ./../../dist/tools/tapsetup/tapsetup --create 2 +``` + +For the server instance: + +``` +$ PORT=tap0 make all term +[...] +> dtlss start +ifconfig +``` + +For the client: + +``` +$ PORT=tap1 make all term +[...] +> dtlsc "DATA to send" +``` + +## Debug logs + +To enable debug logs uncomment `CFLAGS += -DCONFIG_DTLS_DEBUG` in the Makefile. +Tinydtls supports setting the log level. See Makefile for more info. + +## Configs and constraints + +DTLS sock acts as a wrapper for the underlying DTLS stack and as such, the +constraints that applies specifically to the stack are also applied here. +For tinydtls, please refer to [dtls-echo README][1]. + +[1]: https://github.com/RIOT-OS/RIOT/blob/master/examples/dtls-echo/README.md diff --git a/tests/pkg_tinydtls_sock_async/dtls-client.c b/tests/pkg_tinydtls_sock_async/dtls-client.c new file mode 100644 index 000000000000..7831bd8ec90d --- /dev/null +++ b/tests/pkg_tinydtls_sock_async/dtls-client.c @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2019 HAW Hamburg + * Copyright (C) 2020 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 DTLS sock client test + * + * @author Aiman Ismail + */ + +#include + +#include "event/thread.h" +#include "event/timeout.h" +#include "net/sock/async/event.h" +#include "net/sock/udp.h" +#include "net/sock/dtls.h" +#include "net/ipv6/addr.h" +#include "net/credman.h" + +#include "tinydtls_common.h" +#include "tinydtls_keys.h" + +#ifndef CLIENT_SEND_TIMEOUT + /* timeout for client_send command */ +#define CLIENT_SEND_TIMEOUT (1U * US_PER_SEC) +#endif + +#define SOCK_DTLS_CLIENT_TAG (2) + +#ifdef CONFIG_DTLS_ECC +static const ecdsa_public_key_t other_pubkeys[] = { + { .x = ecdsa_pub_key_x, .y = ecdsa_pub_key_y }, +}; + +static const credman_credential_t credential = { + .type = CREDMAN_TYPE_ECDSA, + .tag = SOCK_DTLS_CLIENT_TAG, + .params = { + .ecdsa = { + .private_key = ecdsa_priv_key, + .public_key = { + .x = ecdsa_pub_key_x, + .y = ecdsa_pub_key_y, + }, + .client_keys = (ecdsa_public_key_t *)other_pubkeys, + .client_keys_size = ARRAY_SIZE(other_pubkeys), + } + }, +}; + +#else /* ifdef CONFIG_DTLS_PSK */ +static const uint8_t psk_id_0[] = PSK_DEFAULT_IDENTITY; +static const uint8_t psk_key_0[] = PSK_DEFAULT_KEY; + +static const credman_credential_t credential = { + .type = CREDMAN_TYPE_PSK, + .tag = SOCK_DTLS_CLIENT_TAG, + .params = { + .psk = { + .key = { .s = psk_key_0, .len = sizeof(psk_key_0) - 1, }, + .id = { .s = psk_id_0, .len = sizeof(psk_id_0) - 1, }, + } + }, +}; +#endif + +static void _timeout_handler(event_t *); + +static uint8_t _recv_buf[512]; +static sock_udp_t _udp_sock; +static sock_dtls_t _dtls_sock; +static event_timeout_t _timeouter; +static tinydtls_sock_event_t _timeout = { + .super.handler = _timeout_handler, + .sock = &_dtls_sock, +}; +static bool _sending = false; + +static void _close_sock(sock_dtls_t *sock) +{ + sock_udp_t *udp_sock = sock_dtls_get_udp_sock(sock); + + sock_dtls_close(sock); + sock_udp_close(udp_sock); + _sending = false; +} + +static void _timeout_handler(event_t *ev) +{ + tinydtls_sock_event_t *event = (tinydtls_sock_event_t *)ev; + puts("Session handshake timed out"); + _close_sock(event->sock); +} + +static void _dtls_handler(sock_dtls_t *sock, sock_async_flags_t type, void *arg) +{ + sock_dtls_session_t session = { 0 }; + + event_timeout_clear(&_timeouter); + if (type & SOCK_ASYNC_CONN_RECV) { + char *send_data = arg; + puts("Session handshake received"); + if (sock_dtls_recv(sock, &session, _recv_buf, sizeof(_recv_buf), + 0) != -SOCK_DTLS_HANDSHAKE) { + puts("Error creating session"); + return; + } + puts("Connection to server successful"); + printf("Sending data \"%s\"\n", send_data); + if (sock_dtls_send(sock, &session, send_data, strlen(send_data), + 0) < 0) { + puts("Error sending data"); + sock_dtls_session_destroy(sock, &session); + _close_sock(sock); + } + else { + printf("Sent DTLS message\n"); + event_timeout_set(&_timeouter, CLIENT_SEND_TIMEOUT); + } + } + if (type & SOCK_ASYNC_CONN_FIN) { + puts("Session was destroyed"); + _close_sock(sock); + } + if (type & SOCK_ASYNC_CONN_RDY) { + puts("Session became ready"); + } + if (type & SOCK_ASYNC_MSG_RECV) { + int res; + + if ((res = sock_dtls_recv(sock, &session, _recv_buf, sizeof(_recv_buf), + 0)) < 0) { + printf("Error receiving DTLS message\n"); + } + else { + printf("Received %d bytes: \"%.*s\"\n", (int)res, (int)res, + (char *)_recv_buf); + } + puts("Terminating session"); + sock_dtls_session_destroy(sock, &session); + _close_sock(sock); + } + else if (type & SOCK_ASYNC_MSG_SENT) { + puts("DTLS message was sent"); + } + else if (type & SOCK_ASYNC_PATH_PROP) { + puts("Path property changed"); + } +} + +static int client_send(char *addr_str, char *data) +{ + ssize_t res; + sock_dtls_session_t session; + sock_udp_ep_t remote = SOCK_IPV6_EP_ANY; + sock_udp_ep_t local = SOCK_IPV6_EP_ANY; + local.port = 12345; + remote.port = DTLS_DEFAULT_PORT; + + if (_sending) { + puts("Already in the process of sending"); + } + event_timeout_init(&_timeouter, &event_queue_medium, &_timeout.super); + /* get interface */ + char* iface = ipv6_addr_split_iface(addr_str); + if (iface) { + int pid = atoi(iface); + if (gnrc_netif_get_by_pid(pid) == NULL) { + puts("Invalid network interface"); + return -1; + } + remote.netif = pid; + } else if (gnrc_netif_numof() == 1) { + /* assign the single interface found in gnrc_netif_numof() */ + remote.netif = gnrc_netif_iter(NULL)->pid; + } else { + /* no interface is given, or given interface is invalid */ + /* FIXME This probably is not valid with multiple interfaces */ + remote.netif = SOCK_ADDR_ANY_NETIF; + } + + if (!ipv6_addr_from_str((ipv6_addr_t *)remote.addr.ipv6, addr_str)) { + puts("Error parsing destination address"); + return -1; + } + + if (sock_udp_create(&_udp_sock, &local, NULL, 0) < 0) { + puts("Error creating UDP sock"); + return -1; + } + + if (sock_dtls_create(&_dtls_sock, &_udp_sock, + SOCK_DTLS_CLIENT_TAG, + SOCK_DTLS_1_2, SOCK_DTLS_CLIENT) < 0) { + puts("Error creating DTLS sock"); + sock_udp_close(&_udp_sock); + return -1; + } + + sock_dtls_event_init(&_dtls_sock, &event_queue_medium, _dtls_handler, + data); + res = credman_add(&credential); + if (res < 0 && res != CREDMAN_EXIST) { + /* ignore duplicate credentials */ + printf("Error cannot add credential to system: %d\n", (int)res); + sock_dtls_close(&_dtls_sock); + sock_udp_close(&_udp_sock); + return -1; + } + + _sending = true; + res = sock_dtls_session_init(&_dtls_sock, &remote, &session); + if (res <= 0) { + /* we have UDP sock, so we don't need to use _close_sock() */ + sock_dtls_close(&_dtls_sock); + sock_udp_close(&_udp_sock); + _sending = false; + return res; + } + + event_timeout_set(&_timeouter, CLIENT_SEND_TIMEOUT); + return 0; +} + +int dtls_client_cmd(int argc, char **argv) +{ + if (argc != 3) { + printf("usage %s \n", argv[0]); + return 1; + } + + return client_send(argv[1], argv[2]); +} diff --git a/tests/pkg_tinydtls_sock_async/dtls-server.c b/tests/pkg_tinydtls_sock_async/dtls-server.c new file mode 100644 index 000000000000..13ac2bce8d7d --- /dev/null +++ b/tests/pkg_tinydtls_sock_async/dtls-server.c @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2019 HAW Hamburg + * Copyright (C) 2020 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 DTLS sock server test + * + * @author Aiman Ismail + */ + +#include + +#include "bitfield.h" +#include "event/thread.h" +#include "event/timeout.h" +#include "net/sock/async/event.h" +#include "net/sock/udp.h" +#include "net/sock/dtls.h" +#include "net/credman.h" +#include "msg.h" +#include "thread.h" + +#include "tinydtls_common.h" +#include "tinydtls_keys.h" + +#define SOCK_DTLS_SERVER_TAG (10) +#define READER_QUEUE_SIZE (8U) + +#ifdef CONFIG_DTLS_ECC +static const ecdsa_public_key_t other_pubkeys[] = { + { .x = ecdsa_pub_key_x, .y = ecdsa_pub_key_y }, +}; + +static const credman_credential_t credential = { + .type = CREDMAN_TYPE_ECDSA, + .tag = SOCK_DTLS_SERVER_TAG, + .params = { + .ecdsa = { + .private_key = ecdsa_priv_key, + .public_key = { + .x = ecdsa_pub_key_x, + .y = ecdsa_pub_key_y, + }, + .client_keys = (ecdsa_public_key_t *)other_pubkeys, + .client_keys_size = ARRAY_SIZE(other_pubkeys), + }, + }, +}; +#else /* #ifdef CONFIG_DTLS_PSK */ +static const uint8_t psk_key_0[] = PSK_DEFAULT_KEY; + +static const credman_credential_t credential = { + .type = CREDMAN_TYPE_PSK, + .tag = SOCK_DTLS_SERVER_TAG, + .params = { + .psk = { + .key = { .s = psk_key_0, .len = sizeof(psk_key_0) - 1, }, + }, + }, +}; +#endif + +static void _close_handler(event_t *); + +static uint8_t _recv_buf[512]; +static sock_udp_t _udp_sock; +static sock_dtls_t _dtls_sock; +static tinydtls_sock_event_t _close = { + .super.handler = _close_handler, + .sock = &_dtls_sock, +}; +static bool _active = false; + +static void _close_sock(sock_dtls_t *sock) +{ + sock_udp_t *udp_sock = sock_dtls_get_udp_sock(sock); + + sock_dtls_close(sock); + sock_udp_close(udp_sock); + _active = false; + puts("Terminated server"); +} + +static void _close_handler(event_t *ev) +{ + tinydtls_sock_event_t *event = (tinydtls_sock_event_t *)ev; + puts("Terminating server"); + _close_sock(event->sock); +} + +static void _dtls_handler(sock_dtls_t *sock, sock_async_flags_t type, void *arg) +{ + (void)arg; + if (!_active) { + puts("Error: DTLS server is not running"); + return; + } + + if (type & SOCK_ASYNC_CONN_RECV) { + sock_dtls_session_t session = { 0 }; + + puts("Session handshake received"); + if (sock_dtls_recv(sock, &session, _recv_buf, sizeof(_recv_buf), + 0) != -SOCK_DTLS_HANDSHAKE) { + puts("Error creating session"); + return; + } + puts("New client connected"); + } + if (type & SOCK_ASYNC_CONN_FIN) { + puts("Session was destroyed by peer"); + } + if (type & SOCK_ASYNC_CONN_RDY) { + puts("Session became ready"); + } + if (type & SOCK_ASYNC_MSG_RECV) { + sock_dtls_session_t session = { 0 }; + ssize_t res; + + res = sock_dtls_recv(sock, &session, _recv_buf, sizeof(_recv_buf), 0); + if (res >= 0) { + printf("Received %d bytes -- (echo)\n", (int)res); + res = sock_dtls_send(sock, &session, _recv_buf, (size_t)res, 0); + if (res < 0) { + printf("Error resending DTLS message: %d\n", (int)res); + } + } + } +} + +static int start_server(void) +{ + /* Only one instance of the server */ + if (_active) { + puts("Error: server already running"); + return 1; + } + + /* Start the server */ + ssize_t res; + + sock_udp_ep_t local = SOCK_IPV6_EP_ANY; + local.port = DTLS_DEFAULT_PORT; + sock_udp_create(&_udp_sock, &local, NULL, 0); + + res = credman_add(&credential); + if (res < 0 && res != CREDMAN_EXIST) { + /* ignore duplicate credentials */ + printf("Error cannot add credential to system: %d\n", (int)res); + return 1; + } + res = sock_dtls_create(&_dtls_sock, &_udp_sock, SOCK_DTLS_SERVER_TAG, + SOCK_DTLS_1_2, SOCK_DTLS_SERVER); + if (res < 0) { + puts("Error creating DTLS sock"); + return 1; + } + _active = true; + + sock_dtls_event_init(&_dtls_sock, &event_queue_medium, _dtls_handler, + NULL); + return 0; +} + +static int stop_server(void) +{ + /* check if server is running at all */ + if (!_active) { + puts("Error: DTLS server is not running"); + return 1; + } + + event_post(&event_queue_medium, &_close.super); + return 0; +} + +int dtls_server_cmd(int argc, char **argv) +{ + if (argc < 2) { + printf("usage: %s start | stop\n", argv[0]); + return 1; + } + if (strcmp(argv[1], "start") == 0) { + return start_server(); + } + else if (strcmp(argv[1], "stop") == 0) { + return stop_server(); + } + else { + printf("Error: invalid command. Usage: %s start | stop\n", argv[0]); + return 1; + } + return 0; +} diff --git a/tests/pkg_tinydtls_sock_async/main.c b/tests/pkg_tinydtls_sock_async/main.c new file mode 100644 index 000000000000..8d8a744a2fa9 --- /dev/null +++ b/tests/pkg_tinydtls_sock_async/main.c @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2019 HAW Hamburg + * + * 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 application for DTLS sock + * + * @author Aiman Ismail + */ + +#include + +#include "shell.h" + +extern int dtls_client_cmd(int argc, char **argv); +extern int dtls_server_cmd(int argc, char **argv); + +static const shell_command_t shell_commands[] = { + { "dtlsc", "Start a DTLS client", dtls_client_cmd }, + { "dtlss", "Start and stop a DTLS server", dtls_server_cmd }, + { NULL, NULL, NULL } +}; + +int main(void) +{ + puts("DTLS sock example application"); + + /* start shell */ + puts("All up, running the shell now"); + char line_buf[SHELL_DEFAULT_BUFSIZE]; + shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE); + + /* should never be reached */ + return 0; +} diff --git a/tests/pkg_tinydtls_sock_async/tinydtls_common.h b/tests/pkg_tinydtls_sock_async/tinydtls_common.h new file mode 100644 index 000000000000..797c07d70776 --- /dev/null +++ b/tests/pkg_tinydtls_sock_async/tinydtls_common.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2020 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 Common definitions for client and server in tinyDTLS `sock_async` test + * + * @author Martine Lenders + */ +#ifndef TINYDTLS_COMMON_H +#define TINYDTLS_COMMON_H + +#include "event.h" + +#ifdef __cplusplus +extern "C" +{ +#endif +#ifndef DTLS_DEFAULT_PORT +#define DTLS_DEFAULT_PORT (20220) /* DTLS default port */ +#endif + +typedef struct { + event_t super; + sock_dtls_t *sock; +} tinydtls_sock_event_t; + +#ifdef __cplusplus +} +#endif +#endif /* TINYDTLS_COMMON_H */ diff --git a/tests/pkg_tinydtls_sock_async/tinydtls_keys.h b/tests/pkg_tinydtls_sock_async/tinydtls_keys.h new file mode 100644 index 000000000000..ca4b5eca112d --- /dev/null +++ b/tests/pkg_tinydtls_sock_async/tinydtls_keys.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2018 Inria + * + * 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 PSK and RPK keys for the dtls-sock example. + * + * @author Raul Fuentes + * + * @} + */ + +#ifndef TINYDTLS_KEYS_H +#define TINYDTLS_KEYS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Default keys examples for tinyDTLS (for RIOT, Linux and Contiki) + */ +#ifdef CONFIG_DTLS_PSK +#define PSK_DEFAULT_IDENTITY "Client_identity" +#define PSK_DEFAULT_KEY "secretPSK" +#define PSK_OPTIONS "i:k:" +#define PSK_ID_MAXLEN 32 +#define PSK_MAXLEN 32 + +#endif /* CONFIG_DTLS_PSK */ + +#ifdef CONFIG_DTLS_ECC +static const unsigned char ecdsa_priv_key[] = { + 0x41, 0xC1, 0xCB, 0x6B, 0x51, 0x24, 0x7A, 0x14, + 0x43, 0x21, 0x43, 0x5B, 0x7A, 0x80, 0xE7, 0x14, + 0x89, 0x6A, 0x33, 0xBB, 0xAD, 0x72, 0x94, 0xCA, + 0x40, 0x14, 0x55, 0xA1, 0x94, 0xA9, 0x49, 0xFA +}; + +static const unsigned char ecdsa_pub_key_x[] = { + 0x36, 0xDF, 0xE2, 0xC6, 0xF9, 0xF2, 0xED, 0x29, + 0xDA, 0x0A, 0x9A, 0x8F, 0x62, 0x68, 0x4E, 0x91, + 0x63, 0x75, 0xBA, 0x10, 0x30, 0x0C, 0x28, 0xC5, + 0xE4, 0x7C, 0xFB, 0xF2, 0x5F, 0xA5, 0x8F, 0x52 +}; + +static const unsigned char ecdsa_pub_key_y[] = { + 0x71, 0xA0, 0xD4, 0xFC, 0xDE, 0x1A, 0xB8, 0x78, + 0x5A, 0x3C, 0x78, 0x69, 0x35, 0xA7, 0xCF, 0xAB, + 0xE9, 0x3F, 0x98, 0x72, 0x09, 0xDA, 0xED, 0x0B, + 0x4F, 0xAB, 0xC3, 0x6F, 0xC7, 0x72, 0xF8, 0x29 +}; +#endif /* CONFIG_DTLS_ECC */ +#ifdef __cplusplus +} +#endif + +#endif /* TINYDTLS_KEYS_H */