mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
Merge pull request #15549 from janosbrodbeck/gcoap/pr/dtls
net/gcoap: support DTLS
This commit is contained in:
commit
eacbaf5295
@ -30,6 +30,28 @@
|
||||
#define ENABLE_DEBUG 0
|
||||
#include "debug.h"
|
||||
|
||||
#if IS_USED(MODULE_GCOAP_DTLS)
|
||||
#include "net/credman.h"
|
||||
#include "net/dsm.h"
|
||||
#include "tinydtls_keys.h"
|
||||
|
||||
/* Example credential tag for credman. Tag together with the credential type needs to be unique. */
|
||||
#define GCOAP_DTLS_CREDENTIAL_TAG 10
|
||||
|
||||
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 = GCOAP_DTLS_CREDENTIAL_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 bool _proxied = false;
|
||||
static sock_udp_ep_t _proxy_remote;
|
||||
static char proxy_uri[64];
|
||||
@ -316,7 +338,16 @@ int gcoap_cli_cmd(int argc, char **argv)
|
||||
if (strcmp(argv[1], "info") == 0) {
|
||||
uint8_t open_reqs = gcoap_op_state();
|
||||
|
||||
printf("CoAP server is listening on port %u\n", CONFIG_GCOAP_PORT);
|
||||
if (IS_USED(MODULE_GCOAP_DTLS)) {
|
||||
printf("CoAP server is listening on port %u\n", CONFIG_GCOAPS_PORT);
|
||||
} else {
|
||||
printf("CoAP server is listening on port %u\n", CONFIG_GCOAP_PORT);
|
||||
}
|
||||
#if IS_USED(MODULE_GCOAP_DTLS)
|
||||
printf("Connection secured with DTLS\n");
|
||||
printf("Free DTLS session slots: %d/%d\n", dsm_get_num_available_slots(),
|
||||
dsm_get_num_maximum_slots());
|
||||
#endif
|
||||
printf(" CLI requests sent: %u\n", req_count);
|
||||
printf("CoAP open requests: %u\n", open_reqs);
|
||||
printf("Configured Proxy: ");
|
||||
@ -466,5 +497,19 @@ int gcoap_cli_cmd(int argc, char **argv)
|
||||
|
||||
void gcoap_cli_init(void)
|
||||
{
|
||||
#if IS_USED(MODULE_GCOAP_DTLS)
|
||||
int res = credman_add(&credential);
|
||||
if (res < 0 && res != CREDMAN_EXIST) {
|
||||
/* ignore duplicate credentials */
|
||||
printf("gcoap: cannot add credential to system: %d\n", res);
|
||||
return;
|
||||
}
|
||||
sock_dtls_t *gcoap_sock_dtls = gcoap_get_sock_dtls();
|
||||
res = sock_dtls_add_credential(gcoap_sock_dtls, GCOAP_DTLS_CREDENTIAL_TAG);
|
||||
if (res < 0) {
|
||||
printf("gcoap: cannot add credential to DTLS sock: %d\n", res);
|
||||
}
|
||||
#endif
|
||||
|
||||
gcoap_register_listener(&_listener);
|
||||
}
|
||||
|
85
examples/gcoap_dtls/Makefile
Normal file
85
examples/gcoap_dtls/Makefile
Normal file
@ -0,0 +1,85 @@
|
||||
# Default Makefile, for host native GNRC-based networking
|
||||
|
||||
# name of your application
|
||||
APPLICATION = gcoap_example
|
||||
|
||||
# If no BOARD is found in the environment, use this default:
|
||||
BOARD ?= native
|
||||
|
||||
# This has to be the absolute path to the RIOT base directory:
|
||||
RIOTBASE ?= $(CURDIR)/../..
|
||||
|
||||
# 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
|
||||
USEMODULE += gnrc_ipv6_default
|
||||
USEMODULE += gcoap
|
||||
# Additional networking modules that can be dropped if not needed
|
||||
USEMODULE += gnrc_icmpv6_echo
|
||||
|
||||
# Required by gcoap example
|
||||
USEMODULE += od
|
||||
USEMODULE += fmt
|
||||
# Add also the shell, some shell commands
|
||||
USEMODULE += shell
|
||||
USEMODULE += shell_commands
|
||||
USEMODULE += ps
|
||||
|
||||
# Comment this out to disable code in RIOT that does safety checking
|
||||
# which is not needed in a production environment but helps in the
|
||||
# development process:
|
||||
DEVELHELP ?= 1
|
||||
|
||||
# Change this to 0 show compiler invocation lines by default:
|
||||
QUIET ?= 1
|
||||
|
||||
# Enables DTLS-secured CoAP messaging
|
||||
GCOAP_ENABLE_DTLS ?= 1
|
||||
ifeq (1,$(GCOAP_ENABLE_DTLS))
|
||||
# Required by DTLS. Currently, only tinyDTLS is supported by sock_dtls.
|
||||
USEPKG += tinydtls
|
||||
USEMODULE += sock_dtls
|
||||
USEMODULE += tinydtls_sock_dtls
|
||||
USEMODULE += gcoap_dtls
|
||||
# tinydtls needs crypto secure PRNG
|
||||
USEMODULE += prng_sha1prng
|
||||
|
||||
# Maximum number of DTLS sessions
|
||||
CFLAGS += -DDTLS_PEER_MAX=1
|
||||
endif
|
||||
|
||||
# Instead of simulating an Ethernet connection, we can also simulate
|
||||
# an IEEE 802.15.4 radio using ZEP
|
||||
USE_ZEP ?= 0
|
||||
|
||||
# set the ZEP port for native
|
||||
ZEP_PORT_BASE ?= 17754
|
||||
ifeq (1,$(USE_ZEP))
|
||||
TERMFLAGS += -z [::1]:$(ZEP_PORT_BASE)
|
||||
USEMODULE += socket_zep
|
||||
|
||||
ifneq (,$(ZEP_MAC))
|
||||
TERMFLAGS += --eui64=$(ZEP_MAC)
|
||||
endif
|
||||
endif
|
||||
|
||||
include $(RIOTBASE)/Makefile.include
|
||||
|
||||
# For now this goes after the inclusion of Makefile.include so Kconfig symbols
|
||||
# are available. Only set configuration via CFLAGS if Kconfig is not being used
|
||||
# for this module.
|
||||
ifndef CONFIG_KCONFIG_MODULE_GCOAP
|
||||
## Uncomment to redefine port, for example use 61616 for RFC 6282 UDP compression.
|
||||
#GCOAP_PORT = 5683
|
||||
#CFLAGS += -DCONFIG_GCOAP_PORT=$(GCOAP_PORT)
|
||||
|
||||
## Uncomment to redefine request token length, max 8.
|
||||
#GCOAP_TOKENLEN = 2
|
||||
#CFLAGS += -DCONFIG_GCOAP_TOKENLEN=$(GCOAP_TOKENLEN)
|
||||
|
||||
# Increase from default for confirmable block2 follow-on requests
|
||||
GCOAP_RESEND_BUFS_MAX ?= 2
|
||||
CFLAGS += -DCONFIG_GCOAP_RESEND_BUFS_MAX=$(GCOAP_RESEND_BUFS_MAX)
|
||||
endif
|
53
examples/gcoap_dtls/Makefile.ci
Normal file
53
examples/gcoap_dtls/Makefile.ci
Normal file
@ -0,0 +1,53 @@
|
||||
BOARD_INSUFFICIENT_MEMORY := \
|
||||
airfy-beacon \
|
||||
blackpill \
|
||||
bluepill \
|
||||
hifive1 \
|
||||
hifive1b \
|
||||
im880b \
|
||||
microbit \
|
||||
nrf51dongle \
|
||||
nrf6310 \
|
||||
nucleo-f070rb \
|
||||
nucleo-f072rb \
|
||||
nucleo-f302r8 \
|
||||
saml10-xpro \
|
||||
saml11-xpro \
|
||||
stm32mp157c-dk2 \
|
||||
yunjia-nrf51822 \
|
||||
arduino-duemilanove \
|
||||
arduino-leonardo \
|
||||
arduino-mega2560 \
|
||||
arduino-nano \
|
||||
arduino-uno \
|
||||
atmega1284p \
|
||||
atmega328p \
|
||||
atmega328p-xplained-mini \
|
||||
atxmega-a1u-xpro \
|
||||
atxmega-a3bu-xplained \
|
||||
bluepill-stm32f030c8 \
|
||||
derfmega128 \
|
||||
i-nucleo-lrwan1 \
|
||||
mega-xplained \
|
||||
microduino-corerf \
|
||||
msb-430 \
|
||||
msb-430h \
|
||||
nucleo-f030r8 \
|
||||
nucleo-f031k6 \
|
||||
nucleo-f042k6 \
|
||||
nucleo-f303k8 \
|
||||
nucleo-f334r8 \
|
||||
nucleo-l011k4 \
|
||||
nucleo-l031k6 \
|
||||
nucleo-l053r8 \
|
||||
samd10-xmini \
|
||||
slstk3400a \
|
||||
stk3200 \
|
||||
stm32f030f4-demo \
|
||||
stm32f0discovery \
|
||||
stm32l0538-disco \
|
||||
telosb \
|
||||
waspmote-pro \
|
||||
z1 \
|
||||
zigduino \
|
||||
#
|
1
examples/gcoap_dtls/Makefile.slip
Symbolic link
1
examples/gcoap_dtls/Makefile.slip
Symbolic link
@ -0,0 +1 @@
|
||||
../gcoap/Makefile.slip
|
12
examples/gcoap_dtls/README.md
Normal file
12
examples/gcoap_dtls/README.md
Normal file
@ -0,0 +1,12 @@
|
||||
## About
|
||||
|
||||
This is an additional gcoap example, but with enabled DTLS. It only provides a
|
||||
custom configured makefile, while the code is a symlink to the original gcoap
|
||||
example. Therefore, the infos and usage notes of the other README also applies to this
|
||||
example.
|
||||
|
||||
Please note, that with DTLS the default port is 5684 and not 5683, thus CoAP requests
|
||||
must be sent to this port.
|
||||
|
||||
Since DTLS has higher memory and and ROM requirements, more boards are blacklisted
|
||||
for this example compared to the non-DTLS gcoap example.
|
1
examples/gcoap_dtls/gcoap_cli.c
Symbolic link
1
examples/gcoap_dtls/gcoap_cli.c
Symbolic link
@ -0,0 +1 @@
|
||||
../gcoap/gcoap_cli.c
|
1
examples/gcoap_dtls/main.c
Symbolic link
1
examples/gcoap_dtls/main.c
Symbolic link
@ -0,0 +1 @@
|
||||
../gcoap/main.c
|
63
examples/gcoap_dtls/tinydtls_keys.h
Normal file
63
examples/gcoap_dtls/tinydtls_keys.h
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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 <raul.fuentes-samaniego@inria.fr>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#ifndef TINYDTLS_KEYS_H
|
||||
#define TINYDTLS_KEYS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Default keys examples for tinyDTLS (for RIOT, Linux and Contiki)
|
||||
*/
|
||||
#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
|
||||
|
||||
#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 */
|
@ -32,6 +32,7 @@ PSEUDOMODULES += event_timeout_ztimer
|
||||
PSEUDOMODULES += evtimer_mbox
|
||||
PSEUDOMODULES += evtimer_on_ztimer
|
||||
PSEUDOMODULES += fmt_%
|
||||
PSEUDOMODULES += gcoap_dtls
|
||||
PSEUDOMODULES += gnrc_dhcpv6_%
|
||||
PSEUDOMODULES += gnrc_dhcpv6_client_mud_url
|
||||
PSEUDOMODULES += gnrc_ipv6_default
|
||||
|
@ -38,6 +38,9 @@ endif
|
||||
ifneq (,$(filter dhcpv6,$(USEMODULE)))
|
||||
DIRS += net/application_layer/dhcpv6
|
||||
endif
|
||||
ifneq (,$(filter dsm,$(USEMODULE)))
|
||||
DIRS += net/dsm
|
||||
endif
|
||||
ifneq (,$(filter dummy_thread,$(USEMODULE)))
|
||||
DIRS += test_utils/dummy_thread
|
||||
endif
|
||||
|
@ -501,6 +501,15 @@ ifneq (,$(filter l2filter_%,$(USEMODULE)))
|
||||
USEMODULE += l2filter
|
||||
endif
|
||||
|
||||
ifneq (,$(filter gcoap_dtls,$(USEMODULE)))
|
||||
USEMODULE += gcoap
|
||||
USEMODULE += dsm
|
||||
USEMODULE += sock_async_event
|
||||
USEMODULE += sock_dtls
|
||||
USEMODULE += event_thread
|
||||
USEMODULE += event_timeout
|
||||
endif
|
||||
|
||||
ifneq (,$(filter gcoap,$(USEMODULE)))
|
||||
USEMODULE += nanocoap
|
||||
USEMODULE += sock_async_event
|
||||
|
@ -179,6 +179,11 @@ void auto_init(void)
|
||||
extern void auto_init_loramac(void);
|
||||
auto_init_loramac();
|
||||
}
|
||||
if (IS_USED(MODULE_DSM)) {
|
||||
LOG_DEBUG("Auto init dsm.\n");
|
||||
extern void dsm_init(void);
|
||||
dsm_init();
|
||||
}
|
||||
|
||||
/* initialize USB devices */
|
||||
if (IS_USED(MODULE_AUTO_INIT_USBUS)) {
|
||||
|
123
sys/include/net/dsm.h
Normal file
123
sys/include/net/dsm.h
Normal file
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (C) 2021 ML!PA Consulting GmbH
|
||||
*
|
||||
* 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_dsm DTLS Session Management (DSM)
|
||||
* @ingroup net net_dtls
|
||||
* @brief This module provides functionality to store and retrieve session
|
||||
* information of DTLS connections.
|
||||
*
|
||||
* dsm allows to store necessary session information so that not every application
|
||||
* has to provide the potentially maximum number of possible session objects.
|
||||
* Session storage can be offloaded to this generic module.
|
||||
*
|
||||
* @{\
|
||||
*
|
||||
* @file
|
||||
* @brief DTLS session management module definition
|
||||
*
|
||||
* @note This module does not accept or close DTLS sessions, it merely
|
||||
* provides a place to store session objects.
|
||||
*
|
||||
* @author János Brodbeck <janos.brodbeck@ml-pa.com>
|
||||
*/
|
||||
|
||||
#ifndef NET_DSM_H
|
||||
#define NET_DSM_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "net/sock/dtls.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Maximum number of maintained DTLS sessions (tinyDTLS)
|
||||
*/
|
||||
#ifndef DTLS_PEER_MAX
|
||||
#define DTLS_PEER_MAX (1)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Session management states
|
||||
*/
|
||||
typedef enum {
|
||||
NO_SPACE = -1,
|
||||
SESSION_STATE_NONE = 0,
|
||||
SESSION_STATE_HANDSHAKE,
|
||||
SESSION_STATE_ESTABLISHED
|
||||
} dsm_state_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize the DTLS session management
|
||||
*
|
||||
* Must call once before first use.
|
||||
*/
|
||||
void dsm_init(void);
|
||||
|
||||
/**
|
||||
* @brief Stores a session
|
||||
*
|
||||
* Stores a given session in the internal storage of the session management.
|
||||
* If the session is already stored only the state will be updated when the session
|
||||
* gets established.
|
||||
*
|
||||
* @param[in] sock @ref sock_dtls_t, which the session is created on
|
||||
* @param[in] session Session to store
|
||||
* @param[in] new_state New state of the session
|
||||
* @param[in] restore Indicates, whether the session object should be restored
|
||||
* when an already established session is found
|
||||
*
|
||||
* @return Previous state of the session. If no session existed before it returns
|
||||
* SESSION_STATE_NONE. If no space is available it returns NO_SPACE.
|
||||
*/
|
||||
dsm_state_t dsm_store(sock_dtls_t *sock, sock_dtls_session_t *session,
|
||||
dsm_state_t new_state, bool restore);
|
||||
|
||||
/**
|
||||
* @brief Removes a session
|
||||
*
|
||||
* Removes a given session in the internal storage of the session management.
|
||||
*
|
||||
* @param[in] sock @ref sock_dtls_t, which the session is created on
|
||||
* @param[in] session Session to store
|
||||
*/
|
||||
void dsm_remove(sock_dtls_t *sock, sock_dtls_session_t *session);
|
||||
|
||||
/**
|
||||
* @brief Returns the maximum number of sessions slots
|
||||
*
|
||||
* @return Number of session slots.
|
||||
*/
|
||||
uint8_t dsm_get_num_maximum_slots(void);
|
||||
|
||||
/**
|
||||
* @brief Returns the number of available session slots
|
||||
*
|
||||
* @return Number of available session slots in the session management.
|
||||
*/
|
||||
uint8_t dsm_get_num_available_slots(void);
|
||||
|
||||
/**
|
||||
* @brief Returns the least recently used session
|
||||
*
|
||||
* @param[in] sock @ref sock_dtls_t, which the session is created on
|
||||
* @param[out] session Oldest used session
|
||||
*
|
||||
* @return 1, on success
|
||||
* @return -1, when no session is stored
|
||||
*/
|
||||
ssize_t dsm_get_least_recently_used_session(sock_dtls_t *sock, sock_dtls_session_t *session);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* NET_DSM_H */
|
||||
/** @} */
|
@ -38,6 +38,7 @@
|
||||
* - Observe Server Operation
|
||||
* - Block Operation
|
||||
* - Proxy Operation
|
||||
* - DTLS for transport security
|
||||
* - Implementation Notes
|
||||
* - Implementation Status
|
||||
*
|
||||
@ -338,6 +339,22 @@
|
||||
*
|
||||
* Not implemented yet.
|
||||
*
|
||||
* ## DTLS as transport security ##
|
||||
*
|
||||
* Gcoap allows to use DTLS for transport security by using the @ref net_sock_dtls
|
||||
* "DTLS sock API". Using the module gcoap_dtls enables the support. Gcoap
|
||||
* listens for requests on CONFIG_GCOAPS_PORT, 5684 by default when DTLS is enabled.
|
||||
*
|
||||
* Credentials have to been configured before use. See @ref net_credman "Credman"
|
||||
* and @ref net_sock_dtls_creds "DTLS sock credentials API" for credential managing.
|
||||
* Access to the DTLS socket is provided by gcoap_get_sock_dtls().
|
||||
*
|
||||
* Gcoap includes a DTLS session management component that stores active sessions.
|
||||
* By default, it tries to have CONFIG_GCOAP_DTLS_MINIMUM_AVAILABLE_SESSIONS
|
||||
* session slots available to keep the server responsive. If not enough sessions
|
||||
* are available the server destroys the session that has not been used for the
|
||||
* longest time after CONFIG_GCOAP_DTLS_MINIMUM_AVAILABLE_SESSIONS_TIMEOUT_USEC.
|
||||
*
|
||||
* ## Implementation Notes ##
|
||||
*
|
||||
* ### Waiting for a response ###
|
||||
@ -384,6 +401,9 @@
|
||||
#include "event/timeout.h"
|
||||
#include "net/ipv6/addr.h"
|
||||
#include "net/sock/udp.h"
|
||||
#if IS_USED(MODULE_GCOAP_DTLS)
|
||||
#include "net/sock/dtls.h"
|
||||
#endif
|
||||
#include "net/nanocoap.h"
|
||||
#include "xtimer.h"
|
||||
|
||||
@ -403,6 +423,36 @@ extern "C" {
|
||||
#ifndef CONFIG_GCOAP_PORT
|
||||
#define CONFIG_GCOAP_PORT (5683)
|
||||
#endif
|
||||
/**
|
||||
* @brief Secure Server port; use RFC 7252 default if not defined
|
||||
*/
|
||||
#ifndef CONFIG_GCOAPS_PORT
|
||||
#define CONFIG_GCOAPS_PORT (5684)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Timeout for the DTLS handshake process. Set to 0 for infinite time
|
||||
*/
|
||||
#ifndef CONFIG_GCOAP_DTLS_HANDSHAKE_TIMEOUT_USEC
|
||||
#define CONFIG_GCOAP_DTLS_HANDSHAKE_TIMEOUT_USEC (3 * US_PER_SEC)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Number of minimum available sessions. If the count of available
|
||||
* sessions falls below this threshold, the oldest used session will be
|
||||
* closed after a timeout time. Set to 0 to deactivate this feature.
|
||||
*/
|
||||
#ifndef CONFIG_GCOAP_DTLS_MINIMUM_AVAILABLE_SESSIONS
|
||||
#define CONFIG_GCOAP_DTLS_MINIMUM_AVAILABLE_SESSIONS (1)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Timeout for freeing up a session when minimum number of available
|
||||
* sessions is not given.
|
||||
*/
|
||||
#ifndef CONFIG_GCOAP_DTLS_MINIMUM_AVAILABLE_SESSIONS_TIMEOUT_USEC
|
||||
#define CONFIG_GCOAP_DTLS_MINIMUM_AVAILABLE_SESSIONS_TIMEOUT_USEC (15 * US_PER_SEC)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Size of the buffer used to build a CoAP request or response
|
||||
@ -565,12 +615,20 @@ extern "C" {
|
||||
|
||||
/**
|
||||
* @brief Stack size for module thread
|
||||
* @{
|
||||
*/
|
||||
#ifndef GCOAP_STACK_SIZE
|
||||
#define GCOAP_STACK_SIZE (THREAD_STACKSIZE_DEFAULT + DEBUG_EXTRA_STACKSIZE \
|
||||
+ sizeof(coap_pkt_t))
|
||||
#if IS_USED(MODULE_GCOAP_DTLS)
|
||||
#define GCOAP_DTLS_EXTRA_STACKSIZE (THREAD_STACKSIZE_DEFAULT)
|
||||
#else
|
||||
#define GCOAP_DTLS_EXTRA_STACKSIZE (0)
|
||||
#endif
|
||||
|
||||
#define GCOAP_STACK_SIZE (THREAD_STACKSIZE_DEFAULT + DEBUG_EXTRA_STACKSIZE \
|
||||
+ sizeof(coap_pkt_t) + GCOAP_DTLS_EXTRA_STACKSIZE)
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @ingroup net_gcoap_conf
|
||||
* @brief Count of PDU buffers available for resending confirmable messages
|
||||
@ -586,6 +644,7 @@ extern "C" {
|
||||
*/
|
||||
#define COAP_LINK_FLAG_INIT_RESLIST (1) /**< initialize as for first resource
|
||||
* in a list */
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
@ -719,6 +778,33 @@ typedef struct {
|
||||
unsigned token_len; /**< Actual length of token attribute */
|
||||
} gcoap_observe_memo_t;
|
||||
|
||||
/**
|
||||
* @brief Coap socket types
|
||||
*/
|
||||
typedef enum {
|
||||
COAP_SOCKET_TYPE_UNDEF = 0,
|
||||
COAP_SOCKET_TYPE_UDP,
|
||||
COAP_SOCKET_TYPE_DTLS
|
||||
} coap_socket_type_t;
|
||||
|
||||
/**
|
||||
* @brief Coap socket to handle multiple transport types
|
||||
*/
|
||||
typedef struct {
|
||||
coap_socket_type_t type; /**< Type of stored socket */
|
||||
union {
|
||||
sock_udp_t *udp;
|
||||
#if IS_USED(MODULE_GCOAP_DTLS) || defined(DOXYGEN)
|
||||
sock_dtls_t *dtls;
|
||||
#endif
|
||||
} socket; /**< Stored socket */
|
||||
#if IS_USED(MODULE_GCOAP_DTLS) || defined(DOXYGEN)
|
||||
sock_dtls_session_t ctx_dtls_session; /**< Session object for the stored socket.
|
||||
Used for exchanging a session between
|
||||
functions. */
|
||||
#endif
|
||||
} coap_socket_t;
|
||||
|
||||
/**
|
||||
* @brief Initializes the gcoap thread and device
|
||||
*
|
||||
@ -798,6 +884,7 @@ static inline ssize_t gcoap_request(coap_pkt_t *pdu, uint8_t *buf, size_t len,
|
||||
* @param[in] context User defined context passed to the response handler
|
||||
*
|
||||
* @return length of the packet
|
||||
* @return -ENOTCONN, if DTLS was used and session establishment failed
|
||||
* @return 0 if cannot send
|
||||
*/
|
||||
ssize_t gcoap_req_send(const uint8_t *buf, size_t len,
|
||||
@ -917,6 +1004,17 @@ int gcoap_get_resource_list(void *buf, size_t maxlen, uint8_t cf);
|
||||
ssize_t gcoap_encode_link(const coap_resource_t *resource, char *buf,
|
||||
size_t maxlen, coap_link_encoder_ctx_t *context);
|
||||
|
||||
#if IS_USED(MODULE_GCOAP_DTLS) || defined(DOXYGEN)
|
||||
/**
|
||||
* @brief Get the underlying DTLS socket of gcoap.
|
||||
*
|
||||
* Useful for managing credentials of gcoap.
|
||||
*
|
||||
* @return pointer to the @ref sock_dtls_t object
|
||||
*/
|
||||
sock_dtls_t *gcoap_get_sock_dtls(void);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -13,6 +13,42 @@ menuconfig KCONFIG_USEMODULE_GCOAP
|
||||
|
||||
if KCONFIG_USEMODULE_GCOAP
|
||||
|
||||
menu "DTLS options"
|
||||
config GCOAP_DTLS_CREDENTIAL_TAG
|
||||
int "Credential tag"
|
||||
default 5
|
||||
range 0 65535
|
||||
help
|
||||
DTLS credential tag to determine, which credential to use for
|
||||
authentication.
|
||||
|
||||
config GCOAP_DTLS_HANDSHAKE_TIMEOUT_USEC
|
||||
int "DTLS handshake timeout in microseconds"
|
||||
default 4000000
|
||||
help
|
||||
Time, expressed in microseconds, that the DTLS send function waits for
|
||||
a handshake process and the send process itself to complete.
|
||||
|
||||
config GCOAP_DTLS_MINIMUM_AVAILABLE_SESSIONS
|
||||
int "Minimum number of available sessions"
|
||||
default 1
|
||||
help
|
||||
Number of sessions that should be kept free. If the count of free
|
||||
sessions falls below this threshold, oldest used sessions will be closed
|
||||
after a timeout time.
|
||||
|
||||
config GCOAP_DTLS_MINIMUM_AVAILABLE_SESSIONS_TIMEOUT_USEC
|
||||
int "DTLS session slot automatic free up timeout in microseconds"
|
||||
default 3000000
|
||||
help
|
||||
Time, expressed in microseconds. When the last session slot is occupied
|
||||
this time indicates when a session will be freed up automatically.
|
||||
|
||||
Prevents that the server can be blocked by lack of available session
|
||||
slots and not properly closed sessions.
|
||||
|
||||
endmenu # DTLS options
|
||||
|
||||
config GCOAP_PDU_BUF_SIZE
|
||||
int "Request or response buffer size"
|
||||
default 128
|
||||
@ -91,6 +127,13 @@ config GCOAP_PORT
|
||||
help
|
||||
Server port, the default is the one specified in RFC 7252.
|
||||
|
||||
config GCOAPS_PORT
|
||||
int "Secured server port"
|
||||
default 5684
|
||||
help
|
||||
Server port for secured connections, the default is the one specified in
|
||||
RFC 7252.
|
||||
|
||||
config GCOAP_REQ_WAITING_MAX
|
||||
int "Maximum awaiting requests"
|
||||
default 2
|
||||
|
@ -33,6 +33,12 @@
|
||||
#include "random.h"
|
||||
#include "thread.h"
|
||||
|
||||
#if IS_USED(MODULE_GCOAP_DTLS)
|
||||
#include "net/sock/dtls.h"
|
||||
#include "net/credman.h"
|
||||
#include "net/dsm.h"
|
||||
#endif
|
||||
|
||||
#define ENABLE_DEBUG 0
|
||||
#include "debug.h"
|
||||
|
||||
@ -44,9 +50,14 @@
|
||||
|
||||
/* Internal functions */
|
||||
static void *_event_loop(void *arg);
|
||||
static void _on_sock_evt(sock_udp_t *sock, sock_async_flags_t type, void *arg);
|
||||
static void _process_coap_pdu(sock_udp_t *sock, sock_udp_ep_t *remote,
|
||||
static void _on_sock_udp_evt(sock_udp_t *sock, sock_async_flags_t type, void *arg);
|
||||
static void _process_coap_pdu(coap_socket_t *sock, sock_udp_ep_t *remote,
|
||||
uint8_t *buf, size_t len);
|
||||
static void _tl_init_coap_socket(coap_socket_t *sock);
|
||||
static ssize_t _tl_send(coap_socket_t *sock, const void *data, size_t len,
|
||||
const sock_udp_ep_t *remote);
|
||||
static ssize_t _tl_authenticate(coap_socket_t *sock, const sock_udp_ep_t *remote,
|
||||
uint32_t timeout);
|
||||
static ssize_t _well_known_core_handler(coap_pkt_t* pdu, uint8_t *buf, size_t len, void *ctx);
|
||||
static void _cease_retransmission(gcoap_request_memo_t *memo);
|
||||
static size_t _handle_req(coap_pkt_t *pdu, uint8_t *buf, size_t len,
|
||||
@ -67,6 +78,11 @@ static int _request_matcher_default(gcoap_listener_t *listener,
|
||||
const coap_resource_t **resource,
|
||||
const coap_pkt_t *pdu);
|
||||
|
||||
#if IS_USED(MODULE_GCOAP_DTLS)
|
||||
static void _on_sock_dtls_evt(sock_dtls_t *sock, sock_async_flags_t type, void *arg);
|
||||
static void _dtls_free_up_session(void *arg);
|
||||
#endif
|
||||
|
||||
/* Internal variables */
|
||||
const coap_resource_t _default_resources[] = {
|
||||
{ "/.well-known/core", COAP_GET, _well_known_core_handler, NULL },
|
||||
@ -110,6 +126,17 @@ static event_queue_t _queue;
|
||||
static uint8_t _listen_buf[CONFIG_GCOAP_PDU_BUF_SIZE];
|
||||
static sock_udp_t _sock_udp;
|
||||
|
||||
#if IS_USED(MODULE_GCOAP_DTLS)
|
||||
/* DTLS variables and definitions */
|
||||
#define SOCK_DTLS_CLIENT_TAG (2)
|
||||
|
||||
static sock_dtls_t _sock_dtls;
|
||||
static kernel_pid_t _auth_waiting_thread;
|
||||
|
||||
static event_timeout_t _dtls_session_free_up_tmout;
|
||||
static event_callback_t _dtls_session_free_up_tmout_cb;
|
||||
#endif
|
||||
|
||||
/* Event loop for gcoap _pid thread. */
|
||||
static void *_event_loop(void *arg)
|
||||
{
|
||||
@ -119,8 +146,11 @@ static void *_event_loop(void *arg)
|
||||
memset(&local, 0, sizeof(sock_udp_ep_t));
|
||||
local.family = AF_INET6;
|
||||
local.netif = SOCK_ADDR_ANY_NETIF;
|
||||
local.port = CONFIG_GCOAP_PORT;
|
||||
|
||||
if (IS_USED(MODULE_GCOAP_DTLS)) {
|
||||
local.port = CONFIG_GCOAPS_PORT;
|
||||
} else {
|
||||
local.port = CONFIG_GCOAP_PORT;
|
||||
}
|
||||
int res = sock_udp_create(&_sock_udp, &local, NULL, 0);
|
||||
if (res < 0) {
|
||||
DEBUG("gcoap: cannot create sock: %d\n", res);
|
||||
@ -128,14 +158,127 @@ static void *_event_loop(void *arg)
|
||||
}
|
||||
|
||||
event_queue_init(&_queue);
|
||||
sock_udp_event_init(&_sock_udp, &_queue, _on_sock_evt, NULL);
|
||||
event_loop(&_queue);
|
||||
if (IS_USED(MODULE_GCOAP_DTLS)) {
|
||||
#if IS_USED(MODULE_GCOAP_DTLS)
|
||||
if (sock_dtls_create(&_sock_dtls, &_sock_udp,
|
||||
CREDMAN_TAG_EMPTY,
|
||||
SOCK_DTLS_1_2, SOCK_DTLS_SERVER) < 0) {
|
||||
DEBUG("gcoap: error creating DTLS sock");
|
||||
sock_udp_close(&_sock_udp);
|
||||
return 0;
|
||||
}
|
||||
sock_dtls_event_init(&_sock_dtls, &_queue, _on_sock_dtls_evt,
|
||||
NULL);
|
||||
#endif
|
||||
} else {
|
||||
sock_udp_event_init(&_sock_udp, &_queue, _on_sock_udp_evt, NULL);
|
||||
}
|
||||
|
||||
event_loop(&_queue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if IS_USED(MODULE_GCOAP_DTLS)
|
||||
/* Handles DTLS socket events from the event queue */
|
||||
static void _on_sock_dtls_evt(sock_dtls_t *sock, sock_async_flags_t type, void *arg) {
|
||||
(void)arg;
|
||||
coap_socket_t socket = { .type = COAP_SOCKET_TYPE_DTLS, .socket.dtls = sock};
|
||||
|
||||
if (type & SOCK_ASYNC_CONN_RECV) {
|
||||
ssize_t res = sock_dtls_recv(sock, &socket.ctx_dtls_session,
|
||||
_listen_buf, sizeof(_listen_buf),
|
||||
CONFIG_GCOAP_DTLS_HANDSHAKE_TIMEOUT_USEC);
|
||||
if (res != -SOCK_DTLS_HANDSHAKE) {
|
||||
DEBUG("gcoap: could not establish DTLS session: %zd\n", res);
|
||||
sock_dtls_session_destroy(sock, &socket.ctx_dtls_session);
|
||||
return;
|
||||
}
|
||||
dsm_state_t prev_state = dsm_store(sock, &socket.ctx_dtls_session,
|
||||
SESSION_STATE_ESTABLISHED, false);
|
||||
|
||||
/* If session is already stored and the state was SESSION_STATE_HANDSHAKE
|
||||
before, the handshake has been initiated internally by a gcoap client request
|
||||
and another thread is waiting for the handshake. Send message to the
|
||||
waiting thread to inform about established session */
|
||||
if (prev_state == SESSION_STATE_HANDSHAKE) {
|
||||
msg_t msg = { .type = DTLS_EVENT_CONNECTED };
|
||||
msg_send(&msg, _auth_waiting_thread);
|
||||
} else if (prev_state == NO_SPACE) {
|
||||
/* No space in session management. Should not happen. If it occurs,
|
||||
we lost track of sessions */
|
||||
DEBUG("gcoap: no space in session management. We lost track of sessions!")
|
||||
sock_dtls_session_destroy(sock, &socket.ctx_dtls_session);
|
||||
}
|
||||
|
||||
/* If not enough session slots left: set timeout to free up session */
|
||||
uint8_t minimum_free = CONFIG_GCOAP_DTLS_MINIMUM_AVAILABLE_SESSIONS;
|
||||
if (dsm_get_num_available_slots() < minimum_free)
|
||||
{
|
||||
uint32_t timeout = CONFIG_GCOAP_DTLS_MINIMUM_AVAILABLE_SESSIONS_TIMEOUT_USEC;
|
||||
event_callback_init(&_dtls_session_free_up_tmout_cb,
|
||||
_dtls_free_up_session, NULL);
|
||||
event_timeout_init(&_dtls_session_free_up_tmout, &_queue,
|
||||
&_dtls_session_free_up_tmout_cb.super);
|
||||
event_timeout_set(&_dtls_session_free_up_tmout, timeout);
|
||||
}
|
||||
}
|
||||
|
||||
if (type & SOCK_ASYNC_CONN_FIN) {
|
||||
if (sock_dtls_get_event_session(sock, &socket.ctx_dtls_session)) {
|
||||
/* Session is already destroyed, only remove it from dsm */
|
||||
dsm_remove(sock, &socket.ctx_dtls_session);
|
||||
} else {
|
||||
puts("gcoap: A session was closed, but the corresponding session " \
|
||||
"could not be retrieved from the socket!");
|
||||
return;
|
||||
}
|
||||
sock_udp_ep_t ep;
|
||||
sock_dtls_session_get_udp_ep(&socket.ctx_dtls_session, &ep);
|
||||
|
||||
/* Remove all memos of the concerned session. TODO: oberservable memos! */
|
||||
for (int i = 0; i < CONFIG_GCOAP_REQ_WAITING_MAX; i++) {
|
||||
if (_coap_state.open_reqs[i].state == GCOAP_MEMO_UNUSED) {
|
||||
continue;
|
||||
}
|
||||
gcoap_request_memo_t *memo = &_coap_state.open_reqs[i];
|
||||
if (sock_udp_ep_equal(&memo->remote_ep, &ep)) {
|
||||
_expire_request(memo);
|
||||
event_timeout_clear(&memo->resp_evt_tmout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (type & SOCK_ASYNC_MSG_RECV) {
|
||||
ssize_t res = sock_dtls_recv(sock, &socket.ctx_dtls_session, _listen_buf,
|
||||
sizeof(_listen_buf), 0);
|
||||
if (res <= 0) {
|
||||
DEBUG("gcoap: DTLS recv failure: %d\n", (int)res);
|
||||
return;
|
||||
}
|
||||
sock_udp_ep_t ep;
|
||||
sock_dtls_session_get_udp_ep(&socket.ctx_dtls_session, &ep);
|
||||
_process_coap_pdu(&socket, &ep, _listen_buf, res);
|
||||
}
|
||||
}
|
||||
|
||||
/* Timeout function to free up a session when too many session slots are occupied */
|
||||
static void _dtls_free_up_session(void *arg) {
|
||||
(void)arg;
|
||||
sock_dtls_session_t session;
|
||||
|
||||
uint8_t minimum_free = CONFIG_GCOAP_DTLS_MINIMUM_AVAILABLE_SESSIONS;
|
||||
if (dsm_get_num_available_slots() < minimum_free) {
|
||||
if (dsm_get_least_recently_used_session(&_sock_dtls, &session) != -1) {
|
||||
/* free up session */
|
||||
dsm_remove(&_sock_dtls, &session);
|
||||
sock_dtls_session_destroy(&_sock_dtls, &session);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* MODULE_GCOAP_DTLS */
|
||||
|
||||
/* Handles UDP socket events from the event queue. */
|
||||
static void _on_sock_evt(sock_udp_t *sock, sock_async_flags_t type, void *arg)
|
||||
static void _on_sock_udp_evt(sock_udp_t *sock, sock_async_flags_t type, void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
sock_udp_ep_t remote;
|
||||
@ -147,12 +290,13 @@ static void _on_sock_evt(sock_udp_t *sock, sock_async_flags_t type, void *arg)
|
||||
DEBUG("gcoap: udp recv failure: %d\n", (int)res);
|
||||
return;
|
||||
}
|
||||
_process_coap_pdu(sock, &remote, _listen_buf, res);
|
||||
coap_socket_t socket = { .type = COAP_SOCKET_TYPE_UDP, .socket.udp = sock };
|
||||
_process_coap_pdu(&socket, &remote, _listen_buf, res);
|
||||
}
|
||||
}
|
||||
|
||||
/* Processes and evaluates the coap pdu */
|
||||
static void _process_coap_pdu(sock_udp_t *sock, sock_udp_ep_t *remote,
|
||||
static void _process_coap_pdu(coap_socket_t *sock, sock_udp_ep_t *remote,
|
||||
uint8_t *buf, size_t len)
|
||||
{
|
||||
coap_pkt_t pdu;
|
||||
@ -201,7 +345,7 @@ static void _process_coap_pdu(sock_udp_t *sock, sock_udp_ep_t *remote,
|
||||
size_t pdu_len = _handle_req(&pdu, _listen_buf, sizeof(_listen_buf),
|
||||
remote);
|
||||
if (pdu_len > 0) {
|
||||
ssize_t bytes = sock_udp_send(sock, _listen_buf, pdu_len,
|
||||
ssize_t bytes = _tl_send(sock, _listen_buf, pdu_len,
|
||||
remote);
|
||||
if (bytes <= 0) {
|
||||
DEBUG("gcoap: send response failed: %d\n", (int)bytes);
|
||||
@ -262,7 +406,7 @@ static void _process_coap_pdu(sock_udp_t *sock, sock_udp_ep_t *remote,
|
||||
* */
|
||||
pdu.hdr->ver_t_tkl &= 0xf0;
|
||||
|
||||
ssize_t bytes = sock_udp_send(sock, buf,
|
||||
ssize_t bytes = _tl_send(sock, buf,
|
||||
sizeof(coap_hdr_t), remote);
|
||||
if (bytes <= 0) {
|
||||
DEBUG("gcoap: empty response failed: %d\n", (int)bytes);
|
||||
@ -299,8 +443,10 @@ static void _on_resp_timeout(void *arg) {
|
||||
return;
|
||||
}
|
||||
|
||||
ssize_t bytes = sock_udp_send(&_sock_udp, memo->msg.data.pdu_buf,
|
||||
memo->msg.data.pdu_len, &memo->remote_ep);
|
||||
coap_socket_t socket;
|
||||
_tl_init_coap_socket(&socket);
|
||||
ssize_t bytes = _tl_send(&socket, memo->msg.data.pdu_buf,
|
||||
memo->msg.data.pdu_len, &memo->remote_ep);
|
||||
if (bytes <= 0) {
|
||||
DEBUG("gcoap: sock resend failed: %d\n", (int)bytes);
|
||||
_expire_request(memo);
|
||||
@ -732,6 +878,109 @@ static void _find_obs_memo_resource(gcoap_observe_memo_t **memo,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Transport layer functions
|
||||
*/
|
||||
|
||||
static void _tl_init_coap_socket(coap_socket_t *sock)
|
||||
{
|
||||
#if IS_USED(MODULE_GCOAP_DTLS)
|
||||
sock->type = COAP_SOCKET_TYPE_DTLS;
|
||||
sock->socket.dtls = &_sock_dtls;
|
||||
#else
|
||||
sock->type = COAP_SOCKET_TYPE_UDP;
|
||||
sock->socket.udp = &_sock_udp;
|
||||
#endif
|
||||
}
|
||||
|
||||
static ssize_t _tl_send(coap_socket_t *sock, const void *data, size_t len,
|
||||
const sock_udp_ep_t *remote)
|
||||
{
|
||||
ssize_t res = -1;
|
||||
if (sock->type == COAP_SOCKET_TYPE_DTLS) {
|
||||
#if IS_USED(MODULE_GCOAP_DTLS)
|
||||
/* prepare session */
|
||||
sock_dtls_session_set_udp_ep(&sock->ctx_dtls_session, remote);
|
||||
dsm_state_t session_state = dsm_store(sock->socket.dtls, &sock->ctx_dtls_session,
|
||||
SESSION_STATE_HANDSHAKE, true);
|
||||
if (session_state == NO_SPACE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* send application data */
|
||||
res = sock_dtls_send(sock->socket.dtls, &sock->ctx_dtls_session, data, len,
|
||||
SOCK_NO_TIMEOUT);
|
||||
if (res <= 0 ) {
|
||||
dsm_remove(sock->socket.dtls, &sock->ctx_dtls_session);
|
||||
sock_dtls_session_destroy(sock->socket.dtls, &sock->ctx_dtls_session);
|
||||
}
|
||||
#endif
|
||||
} else if (sock->type == COAP_SOCKET_TYPE_UDP) {
|
||||
res = sock_udp_send(sock->socket.udp, data, len, remote);
|
||||
} else {
|
||||
DEBUG("gcoap: undefined socket type\n");
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static ssize_t _tl_authenticate(coap_socket_t *sock, const sock_udp_ep_t *remote,
|
||||
uint32_t timeout)
|
||||
{
|
||||
#if !IS_USED(MODULE_GCOAP_DTLS)
|
||||
(void)sock;
|
||||
(void)remote;
|
||||
(void)timeout;
|
||||
return 0;
|
||||
#else
|
||||
int res;
|
||||
|
||||
/* prepare session */
|
||||
sock_dtls_session_set_udp_ep(&sock->ctx_dtls_session, remote);
|
||||
dsm_state_t session_state = dsm_store(sock->socket.dtls, &sock->ctx_dtls_session,
|
||||
SESSION_STATE_HANDSHAKE, true);
|
||||
if (session_state == SESSION_STATE_ESTABLISHED) {
|
||||
return 0;
|
||||
}
|
||||
if (session_state == NO_SPACE) {
|
||||
DEBUG("gcoap: no space in dsm\n")
|
||||
return -ENOTCONN;
|
||||
}
|
||||
|
||||
/* start handshake */
|
||||
_auth_waiting_thread = thread_getpid();
|
||||
res = sock_dtls_session_init(sock->socket.dtls, remote, &sock->ctx_dtls_session);
|
||||
if (res == 0) {
|
||||
/* session already exists */
|
||||
_auth_waiting_thread = -1;
|
||||
return res;
|
||||
}
|
||||
|
||||
msg_t msg;
|
||||
bool is_timed_out = false;
|
||||
do {
|
||||
uint32_t start = xtimer_now_usec();
|
||||
res = xtimer_msg_receive_timeout(&msg, timeout);
|
||||
|
||||
/* ensure whole timeout time for the case we receive other messages than DTLS_EVENT_CONNECTED */
|
||||
if (timeout != SOCK_NO_TIMEOUT) {
|
||||
uint32_t diff = (xtimer_now_usec() - start);
|
||||
timeout = (diff > timeout) ? 0: timeout - diff;
|
||||
is_timed_out = (res < 0) || (timeout == 0);
|
||||
}
|
||||
}
|
||||
while (!is_timed_out && (msg.type != DTLS_EVENT_CONNECTED));
|
||||
if (is_timed_out && (msg.type != DTLS_EVENT_CONNECTED)) {
|
||||
DEBUG("gcoap: authentication timed out\n");
|
||||
dsm_remove(sock->socket.dtls, &sock->ctx_dtls_session);
|
||||
sock_dtls_session_destroy(sock->socket.dtls, &sock->ctx_dtls_session);
|
||||
return -ENOTCONN;
|
||||
}
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* gcoap interface functions
|
||||
*/
|
||||
@ -887,8 +1136,16 @@ ssize_t gcoap_req_send(const uint8_t *buf, size_t len,
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t res = 0;
|
||||
coap_socket_t socket = { 0 };
|
||||
|
||||
_tl_init_coap_socket(&socket);
|
||||
if (IS_USED(MODULE_GCOAP_DTLS) && socket.type == COAP_SOCKET_TYPE_DTLS) {
|
||||
res = _tl_authenticate(&socket, remote, CONFIG_GCOAP_DTLS_HANDSHAKE_TIMEOUT_USEC);
|
||||
}
|
||||
|
||||
/* set response timeout; may be zero for non-confirmable */
|
||||
if (memo != NULL) {
|
||||
if (memo != NULL && res == 0) {
|
||||
if (timeout > 0) {
|
||||
event_callback_init(&memo->resp_tmout_cb, _on_resp_timeout, memo);
|
||||
event_timeout_init(&memo->resp_evt_tmout, &_queue,
|
||||
@ -900,7 +1157,9 @@ ssize_t gcoap_req_send(const uint8_t *buf, size_t len,
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t res = sock_udp_send(&_sock_udp, buf, len, remote);
|
||||
if (res == 0) {
|
||||
res = _tl_send(&socket, buf, len, remote);
|
||||
}
|
||||
if (res <= 0) {
|
||||
if (memo != NULL) {
|
||||
if (msg_type == COAP_TYPE_CON) {
|
||||
@ -913,7 +1172,7 @@ ssize_t gcoap_req_send(const uint8_t *buf, size_t len,
|
||||
}
|
||||
DEBUG("gcoap: sock send failed: %d\n", (int)res);
|
||||
}
|
||||
return ((res > 0) ? res : 0);
|
||||
return ((res > 0 || res == -ENOTCONN) ? res : 0);
|
||||
}
|
||||
|
||||
int gcoap_resp_init(coap_pkt_t *pdu, uint8_t *buf, size_t len, unsigned code)
|
||||
@ -974,11 +1233,12 @@ size_t gcoap_obs_send(const uint8_t *buf, size_t len,
|
||||
const coap_resource_t *resource)
|
||||
{
|
||||
gcoap_observe_memo_t *memo = NULL;
|
||||
|
||||
coap_socket_t socket;
|
||||
_tl_init_coap_socket(&socket);
|
||||
_find_obs_memo_resource(&memo, resource);
|
||||
|
||||
if (memo) {
|
||||
ssize_t bytes = sock_udp_send(&_sock_udp, buf, len, memo->observer);
|
||||
ssize_t bytes = _tl_send(&socket, buf, len, memo->observer);
|
||||
return (size_t)((bytes > 0) ? bytes : 0);
|
||||
}
|
||||
else {
|
||||
@ -1067,4 +1327,13 @@ ssize_t gcoap_encode_link(const coap_resource_t *resource, char *buf,
|
||||
return exp_size;
|
||||
}
|
||||
|
||||
#if IS_USED(MODULE_GCOAP_DTLS)
|
||||
sock_dtls_t *gcoap_get_sock_dtls(void)
|
||||
{
|
||||
return &_sock_dtls;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* */
|
||||
|
||||
/** @} */
|
||||
|
2
sys/net/dsm/Makefile
Normal file
2
sys/net/dsm/Makefile
Normal file
@ -0,0 +1,2 @@
|
||||
MODULE = dsm
|
||||
include $(RIOTBASE)/Makefile.base
|
1
sys/net/dsm/Makefile.dep
Normal file
1
sys/net/dsm/Makefile.dep
Normal file
@ -0,0 +1 @@
|
||||
FEATURES_REQUIRED += sock_dtls
|
186
sys/net/dsm/dsm.c
Normal file
186
sys/net/dsm/dsm.c
Normal file
@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright (C) 2021 ML!PA Consulting GmbH
|
||||
*
|
||||
* 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 net_dsm
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief DTLS Session Management module implementation
|
||||
*
|
||||
* @author János Brodbeck <janos.brodbeck@ml-pa.com>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include "net/dsm.h"
|
||||
#include "mutex.h"
|
||||
#include "net/sock/util.h"
|
||||
#include "xtimer.h"
|
||||
|
||||
#define ENABLE_DEBUG 0
|
||||
#include "debug.h"
|
||||
|
||||
typedef struct {
|
||||
sock_dtls_t *sock;
|
||||
sock_dtls_session_t session;
|
||||
dsm_state_t state;
|
||||
uint32_t last_used_sec;
|
||||
} dsm_session_t;
|
||||
|
||||
static int _find_session(sock_dtls_t *sock, sock_dtls_session_t *to_find,
|
||||
dsm_session_t **session);
|
||||
|
||||
static mutex_t _lock;
|
||||
static dsm_session_t _sessions[DTLS_PEER_MAX];
|
||||
static uint8_t _available_slots;
|
||||
|
||||
void dsm_init(void)
|
||||
{
|
||||
mutex_init(&_lock);
|
||||
_available_slots = DTLS_PEER_MAX;
|
||||
}
|
||||
|
||||
dsm_state_t dsm_store(sock_dtls_t *sock, sock_dtls_session_t *session,
|
||||
dsm_state_t new_state, bool restore)
|
||||
{
|
||||
sock_udp_ep_t ep;
|
||||
dsm_session_t *session_slot = NULL;
|
||||
dsm_state_t prev_state = NO_SPACE;
|
||||
mutex_lock(&_lock);
|
||||
|
||||
ssize_t res = _find_session(sock, session, &session_slot);
|
||||
if (res < 0) {
|
||||
DEBUG("dsm: no space for session to store\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
prev_state = session_slot->state;
|
||||
if (session_slot->state != SESSION_STATE_ESTABLISHED) {
|
||||
session_slot->state = new_state;
|
||||
}
|
||||
|
||||
/* no existing session found */
|
||||
if (res == 0) {
|
||||
DEBUG("dsm: no existing session found, storing as new session\n")
|
||||
sock_dtls_session_get_udp_ep(session, &ep);
|
||||
sock_dtls_session_set_udp_ep(&session_slot->session, &ep);
|
||||
session_slot->sock = sock;
|
||||
_available_slots--;
|
||||
}
|
||||
|
||||
/* existing session found and session should be restored */
|
||||
if (res == 1 && restore) {
|
||||
DEBUG("dsm: existing session found, restoring\n")
|
||||
memcpy(session, &session_slot->session, sizeof(sock_dtls_session_t));
|
||||
}
|
||||
session_slot->last_used_sec = (uint32_t)(xtimer_now_usec64() / US_PER_SEC);
|
||||
|
||||
out:
|
||||
mutex_unlock(&_lock);
|
||||
return prev_state;
|
||||
}
|
||||
|
||||
void dsm_remove(sock_dtls_t *sock, sock_dtls_session_t *session)
|
||||
{
|
||||
dsm_session_t *session_slot = NULL;
|
||||
mutex_lock(&_lock);
|
||||
if (_find_session(sock, session, &session_slot) == 1) {
|
||||
if (session_slot->state == SESSION_STATE_NONE) {
|
||||
/* session has already been removed. Can happen when we remove the session
|
||||
before we get the close ACK of the remote peer (e.g. force reset of peer)
|
||||
and then get an ACK (= SOCK_ASYNC_CONN_FIN event) of the remote and
|
||||
call this function again. */
|
||||
goto out;
|
||||
}
|
||||
|
||||
session_slot->state = SESSION_STATE_NONE;
|
||||
_available_slots++;
|
||||
DEBUG("dsm: removed session\n");
|
||||
} else {
|
||||
DEBUG("dsm: could not find session to remove, it was probably already removed\n");
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&_lock);
|
||||
}
|
||||
|
||||
uint8_t dsm_get_num_available_slots(void)
|
||||
{
|
||||
return _available_slots;
|
||||
}
|
||||
|
||||
uint8_t dsm_get_num_maximum_slots(void)
|
||||
{
|
||||
return DTLS_PEER_MAX;
|
||||
}
|
||||
|
||||
ssize_t dsm_get_least_recently_used_session(sock_dtls_t *sock, sock_dtls_session_t *session)
|
||||
{
|
||||
int res = -1;
|
||||
dsm_session_t *session_slot = NULL;
|
||||
|
||||
if (dsm_get_num_available_slots() == DTLS_PEER_MAX) {
|
||||
return res;
|
||||
}
|
||||
|
||||
mutex_lock(&_lock);
|
||||
for (uint8_t i=0; i < DTLS_PEER_MAX; i++) {
|
||||
if (_sessions[i].state != SESSION_STATE_ESTABLISHED) {
|
||||
continue;
|
||||
}
|
||||
if (_sessions[i].sock != sock) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (session_slot == NULL ||
|
||||
session_slot->last_used_sec > _sessions[i].last_used_sec) {
|
||||
session_slot = &_sessions[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (session_slot) {
|
||||
memcpy(session, &session_slot->session, sizeof(sock_dtls_session_t));
|
||||
res = 1;
|
||||
}
|
||||
mutex_unlock(&_lock);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Search for existing session or empty slot for new one
|
||||
* Returns 1, if existing session found
|
||||
* Returns 0, if empty slot found
|
||||
* Returns -1, if no existing or empty session found */
|
||||
static int _find_session(sock_dtls_t *sock, sock_dtls_session_t *to_find,
|
||||
dsm_session_t **session)
|
||||
{
|
||||
|
||||
/* FIXME: optimize search / data structure */
|
||||
sock_udp_ep_t to_find_ep, curr_ep;
|
||||
dsm_session_t *empty_session = NULL;
|
||||
|
||||
sock_dtls_session_get_udp_ep(to_find, &to_find_ep);
|
||||
for (uint8_t i=0; i < DTLS_PEER_MAX; i++) {
|
||||
if (_sessions[i].state == SESSION_STATE_NONE) {
|
||||
empty_session = &_sessions[i];
|
||||
continue;
|
||||
}
|
||||
|
||||
sock_dtls_session_get_udp_ep(&_sessions[i].session, &curr_ep);
|
||||
if (sock_udp_ep_equal(&curr_ep, &to_find_ep) && _sessions[i].sock == sock) {
|
||||
/* found existing session */
|
||||
*session = &_sessions[i];
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty_session) {
|
||||
*session = empty_session;
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
Loading…
Reference in New Issue
Block a user