1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

net/dtls/dsm: add DTLS session management module

This commit is contained in:
János Brodbeck 2021-01-28 10:20:29 +01:00
parent ccfd3ee980
commit 5567f13258
No known key found for this signature in database
GPG Key ID: 65C193B0D8D1BCE6
6 changed files with 320 additions and 0 deletions

View File

@ -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

View File

@ -174,6 +174,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
View 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 */
/** @} */

2
sys/net/dsm/Makefile Normal file
View File

@ -0,0 +1,2 @@
MODULE = dsm
include $(RIOTBASE)/Makefile.base

1
sys/net/dsm/Makefile.dep Normal file
View File

@ -0,0 +1 @@
FEATURES_REQUIRED += sock_dtls

186
sys/net/dsm/dsm.c Normal file
View 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;
}