From 4013c616f7460aac18dd6cefe9a773be3d688216 Mon Sep 17 00:00:00 2001 From: Martine Lenders Date: Mon, 9 Dec 2019 13:51:29 +0100 Subject: [PATCH] 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 637b29aefe..930ac69634 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 a62c04e552..c3b6d01451 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