mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
nanocoap_sock: implement DTLS socket
This commit is contained in:
parent
ad5b27df8e
commit
d660e3ebbd
@ -713,6 +713,11 @@ ifneq (,$(filter luid,$(USEMODULE)))
|
||||
FEATURES_OPTIONAL += periph_cpuid
|
||||
endif
|
||||
|
||||
ifneq (,$(filter nanocoap_dtls,$(USEMODULE)))
|
||||
USEMODULE += sock_dtls
|
||||
USEPKG += tinydtls
|
||||
endif
|
||||
|
||||
ifneq (,$(filter nanocoap_sock,$(USEMODULE)))
|
||||
USEMODULE += sock_udp
|
||||
USEMODULE += sock_util
|
||||
|
@ -31,6 +31,11 @@ extern "C" {
|
||||
*/
|
||||
#define COAP_PORT (5683)
|
||||
|
||||
/**
|
||||
* @brief Default CoAP DTLS port
|
||||
*/
|
||||
#define COAPS_PORT (5684)
|
||||
|
||||
#define COAP_V1 (1) /**< Identifier for CoAP version 1 (RFC 7252) */
|
||||
|
||||
/**
|
||||
|
@ -135,16 +135,57 @@
|
||||
#include "net/nanocoap.h"
|
||||
#include "net/sock/udp.h"
|
||||
#include "net/sock/util.h"
|
||||
#if IS_USED(MODULE_NANOCOAP_DTLS)
|
||||
#include "net/credman.h"
|
||||
#include "net/sock/dtls.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief nanocoap socket type
|
||||
* @brief Timeout for CoAP over DTLS queries in milliseconds
|
||||
*/
|
||||
#ifndef CONFIG_NANOCOAP_SOCK_DTLS_TIMEOUT_MS
|
||||
#define CONFIG_NANOCOAP_SOCK_DTLS_TIMEOUT_MS (1000U)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Number of CoAP over DTLS handshake retries
|
||||
*/
|
||||
#ifndef CONFIG_NANOCOAP_SOCK_DTLS_RETRIES
|
||||
#define CONFIG_NANOCOAP_SOCK_DTLS_RETRIES (2)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Credman tag used for NanoCoAP
|
||||
* Tag together with the credential type (PSK) needs to be unique
|
||||
*/
|
||||
#ifndef CONFIG_NANOCOAP_SOCK_DTLS_TAG
|
||||
#define CONFIG_NANOCOAP_SOCK_DTLS_TAG (0xc0ab)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief NanoCoAP socket types
|
||||
*/
|
||||
typedef enum {
|
||||
COAP_SOCKET_TYPE_UDP, /**< transport is plain UDP */
|
||||
COAP_SOCKET_TYPE_DTLS, /**< transport is DTLS */
|
||||
} nanocoap_socket_type_t;
|
||||
|
||||
/**
|
||||
* @brief NanoCoAP socket struct
|
||||
*/
|
||||
typedef struct {
|
||||
sock_udp_t udp; /**< UDP socket */
|
||||
sock_udp_t udp; /**< UDP socket */
|
||||
#if IS_USED(MODULE_NANOCOAP_DTLS) || defined(DOXYGEN)
|
||||
sock_dtls_t dtls; /**< DTLS socket */
|
||||
sock_dtls_session_t dtls_session; /**< Session object for the stored socket.
|
||||
Used for exchanging a session between
|
||||
functions. */
|
||||
nanocoap_socket_type_t type; /**< Socket type (UDP, DTLS) */
|
||||
#endif
|
||||
} nanocoap_sock_t;
|
||||
|
||||
/**
|
||||
@ -186,9 +227,30 @@ static inline int nanocoap_sock_connect(nanocoap_sock_t *sock,
|
||||
const sock_udp_ep_t *local,
|
||||
const sock_udp_ep_t *remote)
|
||||
{
|
||||
#if IS_USED(MODULE_NANOCOAP_DTLS)
|
||||
sock->type = COAP_SOCKET_TYPE_UDP;
|
||||
#endif
|
||||
|
||||
return sock_udp_create(&sock->udp, local, remote, 0);
|
||||
}
|
||||
|
||||
#if IS_USED(MODULE_NANOCOAP_DTLS) || DOXYGEN
|
||||
/**
|
||||
* @brief Create a DTLS secured CoAP client socket
|
||||
*
|
||||
* @param[out] sock CoAP UDP socket
|
||||
* @param[in] local Local UDP endpoint, may be NULL
|
||||
* @param[in] remote remote UDP endpoint
|
||||
* @param[in] tag Tag of the PSK credential to use
|
||||
* Has to be added with @ref credman_add
|
||||
*
|
||||
* @returns 0 on success
|
||||
* @returns <0 on error
|
||||
*/
|
||||
int nanocoap_sock_dtls_connect(nanocoap_sock_t *sock, sock_udp_ep_t *local,
|
||||
const sock_udp_ep_t *remote, credman_tag_t tag);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Create a CoAP client socket by URL
|
||||
*
|
||||
@ -207,6 +269,12 @@ int nanocoap_sock_url_connect(const char *url, nanocoap_sock_t *sock);
|
||||
*/
|
||||
static inline void nanocoap_sock_close(nanocoap_sock_t *sock)
|
||||
{
|
||||
#if IS_USED(MODULE_NANOCOAP_DTLS)
|
||||
if (sock->type == COAP_SOCKET_TYPE_DTLS) {
|
||||
sock_dtls_session_destroy(&sock->dtls, &sock->dtls_session);
|
||||
sock_dtls_close(&sock->dtls);
|
||||
}
|
||||
#endif
|
||||
sock_udp_close(&sock->udp);
|
||||
}
|
||||
|
||||
|
@ -26,9 +26,11 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include "atomic_utils.h"
|
||||
#include "net/credman.h"
|
||||
#include "net/nanocoap_sock.h"
|
||||
#include "net/sock/util.h"
|
||||
#include "net/sock/udp.h"
|
||||
#include "net/iana/portrange.h"
|
||||
#include "random.h"
|
||||
#include "sys/uio.h"
|
||||
#include "timex.h"
|
||||
@ -37,6 +39,17 @@
|
||||
#define ENABLE_DEBUG 0
|
||||
#include "debug.h"
|
||||
|
||||
/**
|
||||
* @brief Size of the buffer used for the DTLS handshake
|
||||
*
|
||||
* This size was found suitable for DTLS using a simple PSK in mode AES_128_CCM_8.
|
||||
* DTLS places no restriction on its handshake package size therefore this might need change,
|
||||
* if mode or key-size change especially if certificates instead of PSK are used.
|
||||
*/
|
||||
#ifndef CONFIG_NANOCOAP_DTLS_HANDSHAKE_BUF_SIZE
|
||||
#define CONFIG_NANOCOAP_DTLS_HANDSHAKE_BUF_SIZE (160)
|
||||
#endif
|
||||
|
||||
enum {
|
||||
STATE_REQUEST_SEND, /**< request was just sent or will be sent again */
|
||||
STATE_RESPONSE_RCVD, /**< response received but might be invalid */
|
||||
@ -56,6 +69,93 @@ static uint16_t _get_id(void)
|
||||
return atomic_fetch_add_u16(&id, 1);
|
||||
}
|
||||
|
||||
#if IS_USED(MODULE_NANOCOAP_DTLS)
|
||||
int nanocoap_sock_dtls_connect(nanocoap_sock_t *sock, sock_udp_ep_t *local,
|
||||
const sock_udp_ep_t *remote, credman_tag_t tag)
|
||||
{
|
||||
int res;
|
||||
uint32_t timeout_ms = CONFIG_NANOCOAP_SOCK_DTLS_TIMEOUT_MS;
|
||||
uint8_t retries = CONFIG_NANOCOAP_SOCK_DTLS_RETRIES;
|
||||
|
||||
bool auto_port = local->port == 0;
|
||||
do {
|
||||
if (auto_port) {
|
||||
/* choose random ephemeral port, since DTLS requires a local port */
|
||||
local->port = random_uint32_range(IANA_DYNAMIC_PORTRANGE_MIN,
|
||||
IANA_DYNAMIC_PORTRANGE_MAX);
|
||||
}
|
||||
/* connect UDP socket */
|
||||
res = nanocoap_sock_connect(sock, local, remote);
|
||||
} while (auto_port && (res == -EADDRINUSE));
|
||||
|
||||
if (res < 0) {
|
||||
return res;
|
||||
}
|
||||
|
||||
/* create DTLS socket on to of UDP socket */
|
||||
res = sock_dtls_create(&sock->dtls, &sock->udp, tag,
|
||||
SOCK_DTLS_1_2, SOCK_DTLS_CLIENT);
|
||||
if (res < 0) {
|
||||
DEBUG("Unable to create DTLS sock: %s\n", strerror(-res));
|
||||
nanocoap_sock_close(sock);
|
||||
return res;
|
||||
}
|
||||
sock->type = COAP_SOCKET_TYPE_DTLS;
|
||||
|
||||
while (1) {
|
||||
uint8_t buf[CONFIG_NANOCOAP_DTLS_HANDSHAKE_BUF_SIZE];
|
||||
mutex_t lock = MUTEX_INIT_LOCKED;
|
||||
ztimer_t timeout;
|
||||
|
||||
/* unlock lock after timeout */
|
||||
ztimer_mutex_unlock(ZTIMER_MSEC, &timeout, timeout_ms, &lock);
|
||||
|
||||
/* create DTLS session */
|
||||
res = sock_dtls_session_init(&sock->dtls, remote, &sock->dtls_session);
|
||||
if (res >= 0) {
|
||||
/* handle handshake */
|
||||
res = sock_dtls_recv(&sock->dtls, &sock->dtls_session, buf,
|
||||
sizeof(buf), timeout_ms * US_PER_MS);
|
||||
if (res == -SOCK_DTLS_HANDSHAKE) {
|
||||
DEBUG("DTLS handshake successful\n");
|
||||
ztimer_remove(ZTIMER_MSEC, &timeout);
|
||||
return 0;
|
||||
}
|
||||
DEBUG("Unable to establish DTLS handshake: %s\n", strerror(-res));
|
||||
|
||||
} else {
|
||||
DEBUG("Unable to initialize DTLS session: %s\n", strerror(-res));
|
||||
}
|
||||
|
||||
sock_dtls_session_destroy(&sock->dtls, &sock->dtls_session);
|
||||
|
||||
if (retries--) {
|
||||
/* wait for timeout to expire */
|
||||
mutex_lock(&lock);
|
||||
} else {
|
||||
ztimer_remove(ZTIMER_MSEC, &timeout);
|
||||
break;
|
||||
}
|
||||
|
||||
/* see https://datatracker.ietf.org/doc/html/rfc6347#section-4.2.4.1 */
|
||||
timeout_ms *= 2U;
|
||||
}
|
||||
|
||||
nanocoap_sock_close(sock);
|
||||
return res;
|
||||
}
|
||||
#else
|
||||
int nanocoap_sock_dtls_connect(nanocoap_sock_t *sock, const sock_udp_ep_t *local,
|
||||
const sock_udp_ep_t *remote, credman_tag_t tag)
|
||||
{
|
||||
(void)sock;
|
||||
(void)local;
|
||||
(void)remote;
|
||||
(void)tag;
|
||||
return -ENOTSUP;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int _get_error(const coap_pkt_t *pkt)
|
||||
{
|
||||
switch (coap_get_code_class(pkt)) {
|
||||
@ -68,15 +168,61 @@ static int _get_error(const coap_pkt_t *pkt)
|
||||
}
|
||||
}
|
||||
|
||||
static inline nanocoap_socket_type_t _get_type(nanocoap_sock_t *sock)
|
||||
{
|
||||
#if IS_USED(MODULE_NANOCOAP_DTLS)
|
||||
return sock->type;
|
||||
#else
|
||||
(void)sock;
|
||||
return COAP_SOCKET_TYPE_UDP;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int _sock_sendv(nanocoap_sock_t *sock, const iolist_t *snips)
|
||||
{
|
||||
switch (_get_type(sock)) {
|
||||
case COAP_SOCKET_TYPE_UDP:
|
||||
return sock_udp_sendv(&sock->udp, snips, NULL);
|
||||
#if IS_USED(MODULE_NANOCOAP_DTLS)
|
||||
case COAP_SOCKET_TYPE_DTLS:
|
||||
return sock_dtls_sendv(&sock->dtls, &sock->dtls_session, snips,
|
||||
CONFIG_NANOCOAP_SOCK_DTLS_TIMEOUT_MS);
|
||||
#endif
|
||||
default:
|
||||
assert(0);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int _sock_recv_buf(nanocoap_sock_t *sock, void **data, void **ctx, uint32_t timeout)
|
||||
{
|
||||
switch (_get_type(sock)) {
|
||||
case COAP_SOCKET_TYPE_UDP:
|
||||
return sock_udp_recv_buf(&sock->udp, data, ctx, timeout, NULL);
|
||||
#if IS_USED(MODULE_NANOCOAP_DTLS)
|
||||
case COAP_SOCKET_TYPE_DTLS:
|
||||
return sock_dtls_recv_buf(&sock->dtls, &sock->dtls_session, data, ctx, timeout);
|
||||
#endif
|
||||
default:
|
||||
assert(0);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int _send_ack(nanocoap_sock_t *sock, coap_pkt_t *pkt)
|
||||
{
|
||||
coap_hdr_t ack;
|
||||
unsigned tkl = coap_get_token_len(pkt);
|
||||
|
||||
const iolist_t snip = {
|
||||
.iol_base = &ack,
|
||||
.iol_len = sizeof(ack),
|
||||
};
|
||||
|
||||
coap_build_hdr(&ack, COAP_TYPE_ACK, coap_get_token(pkt), tkl,
|
||||
COAP_CODE_EMPTY, ntohs(pkt->hdr->id));
|
||||
|
||||
return sock_udp_send(&sock->udp, &ack, sizeof(ack), NULL);
|
||||
return _sock_sendv(sock, &snip);
|
||||
}
|
||||
|
||||
static bool _id_or_token_missmatch(const coap_pkt_t *pkt, unsigned id,
|
||||
@ -149,7 +295,7 @@ ssize_t nanocoap_sock_request_cb(nanocoap_sock_t *sock, coap_pkt_t *pkt,
|
||||
DEBUG("nanocoap: send %u bytes (%u tries left)\n",
|
||||
(unsigned)iolist_size(&head), tries_left);
|
||||
|
||||
res = sock_udp_sendv(&sock->udp, &head, NULL);
|
||||
res = _sock_sendv(sock, &head);
|
||||
if (res <= 0) {
|
||||
DEBUG("nanocoap: error sending coap request, %d\n", (int)res);
|
||||
return res;
|
||||
@ -170,7 +316,7 @@ ssize_t nanocoap_sock_request_cb(nanocoap_sock_t *sock, coap_pkt_t *pkt,
|
||||
_deadline_left_us(deadline));
|
||||
}
|
||||
const void *old_ctx = ctx;
|
||||
tmp = sock_udp_recv_buf(&sock->udp, &payload, &ctx, _deadline_left_us(deadline), NULL);
|
||||
tmp = _sock_recv_buf(sock, &payload, &ctx, _deadline_left_us(deadline));
|
||||
/* sock_udp_recv_buf() is supposed to return multiple packet fragments
|
||||
* when called multiple times with the same context.
|
||||
* In practise, this is not implemented and it will always return a pointer
|
||||
@ -611,7 +757,14 @@ int nanocoap_sock_url_connect(const char *url, nanocoap_sock_t *sock)
|
||||
char hostport[CONFIG_SOCK_HOSTPORT_MAXLEN];
|
||||
sock_udp_ep_t remote;
|
||||
|
||||
if (strncmp(url, "coap://", 7)) {
|
||||
bool is_coaps = false;
|
||||
|
||||
if (IS_USED(MODULE_NANOCOAP_DTLS) && !strncmp(url, "coaps://", 8)) {
|
||||
DEBUG("nanocoap: CoAPS URL detected\n");
|
||||
is_coaps = true;
|
||||
}
|
||||
|
||||
if (!is_coaps && strncmp(url, "coap://", 7)) {
|
||||
DEBUG("nanocoap: URL doesn't start with \"coap://\"\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -627,10 +780,26 @@ int nanocoap_sock_url_connect(const char *url, nanocoap_sock_t *sock)
|
||||
}
|
||||
|
||||
if (!remote.port) {
|
||||
remote.port = COAP_PORT;
|
||||
remote.port = is_coaps ? COAPS_PORT : COAP_PORT;
|
||||
}
|
||||
|
||||
return nanocoap_sock_connect(sock, NULL, &remote);
|
||||
if (is_coaps) {
|
||||
|
||||
/* tinydtls wants the interface to match */
|
||||
if (!remote.netif &&
|
||||
ipv6_addr_is_link_local((ipv6_addr_t *)remote.addr.ipv6)) {
|
||||
netif_t *iface = netif_iter(NULL);
|
||||
if (iface == NULL) {
|
||||
return -ENODEV;
|
||||
}
|
||||
remote.netif = netif_get_id(iface);
|
||||
}
|
||||
|
||||
sock_udp_ep_t local = SOCK_IPV6_EP_ANY;
|
||||
return nanocoap_sock_dtls_connect(sock, &local, &remote, CONFIG_NANOCOAP_SOCK_DTLS_TAG);
|
||||
} else {
|
||||
return nanocoap_sock_connect(sock, NULL, &remote);
|
||||
}
|
||||
}
|
||||
|
||||
int nanocoap_get_blockwise_url(const char *url,
|
||||
|
Loading…
Reference in New Issue
Block a user