mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
442 lines
14 KiB
C
442 lines
14 KiB
C
/*
|
|
* Copyright (C) 2018 Beduino Master Projekt - University of Bremen
|
|
* 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 lwm2m_client
|
|
*
|
|
* @file
|
|
* @brief LwM2M client implementation using Wakaama
|
|
*
|
|
* @author Christian Manal <manal@uni-bremen.de>
|
|
* @author Leandro Lanzieri <leandro.lanzieri@haw-hamburg.de>
|
|
* @}
|
|
*/
|
|
|
|
#include <string.h>
|
|
|
|
#include "kernel_defines.h"
|
|
#include "timex.h"
|
|
#include "liblwm2m.h"
|
|
#include "uri_parser.h"
|
|
#include "event/timeout.h"
|
|
#include "event/thread.h"
|
|
#include "net/sock/async/event.h"
|
|
#include "net/sock/util.h"
|
|
#include "objects/common.h"
|
|
#include "objects/security.h"
|
|
#include "objects/device.h"
|
|
|
|
#include "lwm2m_platform.h"
|
|
#include "lwm2m_client.h"
|
|
#include "lwm2m_client_config.h"
|
|
#include "lwm2m_client_connection.h"
|
|
|
|
#define ENABLE_DEBUG 0
|
|
#include "debug.h"
|
|
|
|
/**
|
|
* @brief Callback for event timeout that performs a step on the LwM2M FSM.
|
|
*/
|
|
static void _lwm2m_step_cb(event_t *arg);
|
|
|
|
/**
|
|
* @brief Callback to handle UDP sock events.
|
|
*/
|
|
static void _udp_event_handler(sock_udp_t *sock, sock_async_flags_t type, void *arg);
|
|
|
|
/**
|
|
* @brief Handle an incoming packet from a remote peer.
|
|
*/
|
|
static void _handle_packet_from_remote(const sock_udp_ep_t *remote,
|
|
lwm2m_client_connection_type_t type, uint8_t *buffer,
|
|
size_t len);
|
|
|
|
#if IS_USED(MODULE_WAKAAMA_CLIENT_DTLS)
|
|
/**
|
|
* @brief Callback to handle DTLS sock events.
|
|
*/
|
|
static void _dtls_event_handler(sock_dtls_t *sock, sock_async_flags_t type, void *arg);
|
|
|
|
/**
|
|
* @brief Try to find a server credential given an UDP endpoint.
|
|
*
|
|
* @param[in] ep UDP endpoint to match
|
|
* @param[in] security_mode Security mode of the instance to find
|
|
*
|
|
* @return Tag of the credential to use when a suitable one is found
|
|
* @retval CREDMAN_TAG_EMPTY otherwise
|
|
*/
|
|
static credman_tag_t _get_credential(const sock_udp_ep_t *ep, uint8_t security_mode);
|
|
#endif
|
|
|
|
static event_t _lwm2m_step_event = { .handler = _lwm2m_step_cb };
|
|
static event_timeout_t _lwm2m_step_event_timeout;
|
|
static lwm2m_client_data_t *_client_data = NULL;
|
|
|
|
#if IS_USED(MODULE_WAKAAMA_CLIENT_DTLS)
|
|
/**
|
|
* @brief Callback registered to the client DTLS sock to select a PSK credential to use.
|
|
*/
|
|
static credman_tag_t _client_psk_cb(sock_dtls_t *sock, sock_udp_ep_t *ep, credman_tag_t tags[],
|
|
unsigned tags_len, const char *hint, size_t hint_len)
|
|
{
|
|
(void) sock;
|
|
(void) tags;
|
|
(void) tags_len;
|
|
(void) hint;
|
|
(void) hint_len;
|
|
|
|
DEBUG("[lwm2m:client:PSK] getting credential\n");
|
|
return _get_credential(ep, LWM2M_SECURITY_MODE_PRE_SHARED_KEY);
|
|
}
|
|
|
|
static credman_tag_t _client_rpk_cb(sock_dtls_t *sock, sock_udp_ep_t *ep, credman_tag_t tags[],
|
|
unsigned tags_len)
|
|
{
|
|
(void) sock;
|
|
(void) tags;
|
|
(void) tags_len;
|
|
|
|
DEBUG("[lwm2m:client:RPK] getting credential\n");
|
|
return _get_credential(ep, LWM2M_SECURITY_MODE_RAW_PUBLIC_KEY);
|
|
}
|
|
|
|
static credman_tag_t _get_credential(const sock_udp_ep_t *ep, uint8_t security_mode)
|
|
{
|
|
lwm2m_object_t *sec = lwm2m_get_object_by_id(_client_data, LWM2M_SECURITY_OBJECT_ID);
|
|
if (!sec) {
|
|
DEBUG("[lwm2m:client] no security object found\n");
|
|
return CREDMAN_TAG_EMPTY;
|
|
}
|
|
|
|
/* prepare query */
|
|
lwm2m_uri_t query_uri;
|
|
LWM2M_URI_RESET(&query_uri);
|
|
query_uri.objectId = LWM2M_SECURITY_OBJECT_ID;
|
|
|
|
lwm2m_list_t *instance = sec->instanceList;
|
|
|
|
/* check all registered security object instances */
|
|
while (instance) {
|
|
query_uri.instanceId = instance->id;
|
|
|
|
/* first check the security mode */
|
|
query_uri.resourceId = LWM2M_SECURITY_SECURITY_ID;
|
|
int64_t mode;
|
|
int res = lwm2m_get_int(_client_data, &query_uri, &mode);
|
|
if (res < 0) {
|
|
DEBUG("[lwm2m:client] could not get security mode of %" PRIu16 "\n", instance->id);
|
|
goto check_next;
|
|
}
|
|
|
|
if (mode != security_mode) {
|
|
goto check_next;
|
|
}
|
|
|
|
/* if security mode matches, check the URI */
|
|
char uri[CONFIG_LWM2M_URI_MAX_SIZE] = { 0 };
|
|
size_t uri_len = sizeof(uri);
|
|
res = lwm2m_get_string(_client_data, &query_uri, uri, &uri_len);
|
|
if (res < 0) {
|
|
DEBUG("[lwm2m:client] could not get URI of %" PRIu16 "\n", instance->id);
|
|
goto check_next;
|
|
}
|
|
|
|
sock_udp_ep_t inst_ep;
|
|
uri_parser_result_t parsed_uri;
|
|
res = uri_parser_process_string(&parsed_uri, uri);
|
|
if (res < 0) {
|
|
DEBUG("[lwm2m:client] could not parse URI\n");
|
|
goto check_next;
|
|
}
|
|
|
|
res = sock_udp_str2ep(&inst_ep, parsed_uri.host);
|
|
if (res < 0) {
|
|
DEBUG("[lwm2m:client] could not convert URI to EP (%s)\n", parsed_uri.host);
|
|
goto check_next;
|
|
}
|
|
|
|
if (sock_udp_ep_equal(ep, &inst_ep)) {
|
|
credman_tag_t tag = lwm2m_object_security_get_credential(instance->id);
|
|
|
|
DEBUG("[lwm2m:client:PSK] found matching EP on instance %" PRIu16 "\n", instance->id);
|
|
DEBUG("[lwm2m:client:PSK] tag: %" PRIu16 "\n", tag);
|
|
|
|
return tag;
|
|
}
|
|
|
|
check_next:
|
|
instance = instance->next;
|
|
}
|
|
|
|
return CREDMAN_TAG_EMPTY;
|
|
}
|
|
|
|
#endif /* MODULE_WAKAAMA_CLIENT_DTLS */
|
|
|
|
void lwm2m_client_init(lwm2m_client_data_t *client_data)
|
|
{
|
|
(void)client_data;
|
|
lwm2m_platform_init();
|
|
}
|
|
|
|
lwm2m_context_t *lwm2m_client_run(lwm2m_client_data_t *client_data,
|
|
lwm2m_object_t *obj_list[],
|
|
uint16_t obj_numof)
|
|
{
|
|
int res;
|
|
|
|
_client_data = client_data;
|
|
_client_data->local_ep.family = AF_INET6;
|
|
_client_data->local_ep.netif = SOCK_ADDR_ANY_NETIF;
|
|
|
|
/* create sock for UDP server */
|
|
_client_data->local_ep.port = atoi(CONFIG_LWM2M_LOCAL_PORT);
|
|
if (sock_udp_create(&_client_data->sock, &_client_data->local_ep, NULL, 0) < 0) {
|
|
DEBUG("[lwm2m_client_run] Can't create server socket\n");
|
|
return NULL;
|
|
}
|
|
|
|
#if IS_USED(MODULE_WAKAAMA_CLIENT_DTLS)
|
|
/* create sock for DTLS server */
|
|
_client_data->dtls_local_ep.family = AF_INET6;
|
|
_client_data->dtls_local_ep.netif = SOCK_ADDR_ANY_NETIF;
|
|
_client_data->dtls_local_ep.port = atoi(CONFIG_LWM2M_LOCAL_DTLS_PORT);
|
|
res = sock_udp_create(&_client_data->dtls_udp_sock, &_client_data->dtls_local_ep, NULL, 0);
|
|
if (res < 0) {
|
|
DEBUG("[lwm2m_client_run] Can't create DTLS server UDP sock\n");
|
|
return NULL;
|
|
}
|
|
|
|
res = sock_dtls_create(&_client_data->dtls_sock, &_client_data->dtls_udp_sock,
|
|
CREDMAN_TAG_EMPTY, SOCK_DTLS_1_2, SOCK_DTLS_CLIENT);
|
|
if (res < 0) {
|
|
DEBUG("[lwm2m_client_run] Can't create DTLS server sock\n");
|
|
sock_udp_close(&_client_data->dtls_udp_sock);
|
|
sock_udp_close(&_client_data->sock);
|
|
return NULL;
|
|
}
|
|
|
|
/* register callback for credential selection */
|
|
sock_dtls_set_client_psk_cb(&_client_data->dtls_sock, _client_psk_cb);
|
|
sock_dtls_set_rpk_cb(&_client_data->dtls_sock, _client_rpk_cb);
|
|
#endif /* MODULE_WAKAAMA_CLIENT_DTLS */
|
|
|
|
/* initiate LwM2M */
|
|
_client_data->lwm2m_ctx = lwm2m_init(_client_data);
|
|
if (!_client_data->lwm2m_ctx) {
|
|
DEBUG("[lwm2m_client_run] Failed to initiate LwM2M\n");
|
|
return NULL;
|
|
}
|
|
|
|
res = lwm2m_configure(_client_data->lwm2m_ctx, CONFIG_LWM2M_DEVICE_NAME, NULL,
|
|
CONFIG_LWM2M_ALT_PATH, obj_numof, obj_list);
|
|
if (res) {
|
|
DEBUG("[lwm2m_client_run] Failed to configure LwM2M\n");
|
|
return NULL;
|
|
}
|
|
|
|
#if IS_USED(MODULE_WAKAAMA_CLIENT_DTLS)
|
|
lwm2m_client_refresh_dtls_credentials();
|
|
#endif
|
|
|
|
sock_udp_event_init(&_client_data->sock, EVENT_PRIO_MEDIUM, _udp_event_handler, NULL);
|
|
|
|
#if IS_USED(MODULE_WAKAAMA_CLIENT_DTLS)
|
|
sock_dtls_event_init(&_client_data->dtls_sock, EVENT_PRIO_MEDIUM, _dtls_event_handler, NULL);
|
|
#endif
|
|
|
|
/* periodic event to tick the wakaama state machine */
|
|
event_timeout_init(&_lwm2m_step_event_timeout, EVENT_PRIO_MEDIUM, &_lwm2m_step_event);
|
|
event_timeout_set(&_lwm2m_step_event_timeout, LWM2M_CLIENT_MIN_REFRESH_TIME);
|
|
|
|
return _client_data->lwm2m_ctx;
|
|
}
|
|
|
|
static void _handle_packet_from_remote(const sock_udp_ep_t *remote,
|
|
lwm2m_client_connection_type_t type, uint8_t *buffer,
|
|
size_t len)
|
|
{
|
|
lwm2m_client_connection_t *conn;
|
|
|
|
DEBUG("[lwm2m:client] finding connection\n");
|
|
conn = lwm2m_client_connection_find(_client_data->conn_list, remote, type);
|
|
|
|
if (conn) {
|
|
DEBUG("[lwm2m:client] handle packet (%i bytes)\n", (int)len);
|
|
int result = lwm2m_connection_handle_packet(conn, buffer, len, _client_data);
|
|
if (0 != result) {
|
|
DEBUG("[lwm2m:client] error handling message %i\n", result);
|
|
}
|
|
}
|
|
else {
|
|
DEBUG("[lwm2m:client] couldn't find incoming connection\n");
|
|
}
|
|
}
|
|
|
|
static void _udp_event_handler(sock_udp_t *sock, sock_async_flags_t type, void *arg)
|
|
{
|
|
(void) arg;
|
|
|
|
sock_udp_ep_t remote;
|
|
|
|
if (type & SOCK_ASYNC_MSG_RECV) {
|
|
uint8_t rcv_buf[LWM2M_CLIENT_RCV_BUFFER_SIZE];
|
|
ssize_t rcv_len = sock_udp_recv(sock, rcv_buf, sizeof(rcv_buf), 0, &remote);
|
|
if (rcv_len <= 0) {
|
|
DEBUG("[lwm2m:client] UDP receive failure: %i\n", (int)rcv_len);
|
|
return;
|
|
}
|
|
|
|
_handle_packet_from_remote(&remote, LWM2M_CLIENT_CONN_UDP, rcv_buf, rcv_len);
|
|
}
|
|
}
|
|
|
|
#if IS_USED(MODULE_WAKAAMA_CLIENT_DTLS)
|
|
static void _dtls_event_handler(sock_dtls_t *sock, sock_async_flags_t type, void *arg)
|
|
{
|
|
(void) arg;
|
|
|
|
sock_udp_ep_t remote;
|
|
sock_dtls_session_t dtls_remote;
|
|
uint8_t rcv_buf[LWM2M_CLIENT_RCV_BUFFER_SIZE];
|
|
|
|
if (type & SOCK_ASYNC_MSG_RECV) {
|
|
ssize_t rcv_len = sock_dtls_recv(sock, &dtls_remote, rcv_buf, sizeof(rcv_buf), 0);
|
|
if (rcv_len <= 0) {
|
|
DEBUG("[lwm2m:client] DTLS receive failure: %i\n", (int)rcv_len);
|
|
return;
|
|
}
|
|
|
|
sock_dtls_session_get_udp_ep(&dtls_remote, &remote);
|
|
|
|
_handle_packet_from_remote(&remote, LWM2M_CLIENT_CONN_DTLS, rcv_buf, rcv_len);
|
|
}
|
|
}
|
|
#endif /* MODULE_WAKAAMA_CLIENT_DTLS */
|
|
|
|
static void _lwm2m_step_cb(event_t *arg)
|
|
{
|
|
(void) arg;
|
|
time_t next_step = LWM2M_CLIENT_MIN_REFRESH_TIME;
|
|
|
|
/* check if we need to reboot */
|
|
if (lwm2m_device_reboot_requested()) {
|
|
DEBUG("[lwm2m:client] reboot requested, rebooting ...\n");
|
|
pm_reboot();
|
|
}
|
|
|
|
/* perform step on the LwM2M FSM */
|
|
lwm2m_step(_client_data->lwm2m_ctx, &next_step);
|
|
DEBUG("[lwm2m:client] state: ");
|
|
switch (_client_data->lwm2m_ctx->state) {
|
|
case STATE_INITIAL:
|
|
DEBUG("STATE_INITIAL\n");
|
|
break;
|
|
case STATE_BOOTSTRAP_REQUIRED:
|
|
DEBUG("STATE_BOOTSTRAP_REQUIRED\n");
|
|
break;
|
|
case STATE_BOOTSTRAPPING:
|
|
DEBUG("STATE_BOOTSTRAPPING\n");
|
|
break;
|
|
case STATE_REGISTER_REQUIRED:
|
|
DEBUG("STATE_REGISTER_REQUIRED\n");
|
|
break;
|
|
case STATE_REGISTERING:
|
|
DEBUG("STATE_REGISTERING\n");
|
|
break;
|
|
case STATE_READY:
|
|
DEBUG("STATE_READY\n");
|
|
if (next_step > LWM2M_CLIENT_MIN_REFRESH_TIME) {
|
|
next_step = LWM2M_CLIENT_MIN_REFRESH_TIME;
|
|
}
|
|
break;
|
|
default:
|
|
DEBUG("Unknown...\n");
|
|
break;
|
|
}
|
|
|
|
/* program next step */
|
|
event_timeout_set(&_lwm2m_step_event_timeout, next_step * US_PER_SEC);
|
|
}
|
|
|
|
#if IS_USED(MODULE_WAKAAMA_CLIENT_DTLS)
|
|
void lwm2m_client_add_credential(credman_tag_t tag)
|
|
{
|
|
if (!_client_data) {
|
|
return;
|
|
}
|
|
|
|
const credman_tag_t *creds;
|
|
size_t creds_len = sock_dtls_get_credentials(&_client_data->dtls_sock, &creds);
|
|
|
|
DEBUG("[lwm2m:client] trying to add credential with tag %" PRIu16 "\n", tag);
|
|
for (unsigned i = 0; i < creds_len; i++) {
|
|
if (creds[i] == tag) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
sock_dtls_add_credential(&_client_data->dtls_sock, tag);
|
|
DEBUG("[lwm2m:client] added\n");
|
|
}
|
|
|
|
void lwm2m_client_remove_credential(credman_tag_t tag)
|
|
{
|
|
DEBUG("[lwm2m:client] removing credential with tag %" PRIu16 "\n", tag);
|
|
sock_dtls_remove_credential(&_client_data->dtls_sock, tag);
|
|
}
|
|
|
|
void lwm2m_client_refresh_dtls_credentials(void)
|
|
{
|
|
if (!_client_data) {
|
|
return;
|
|
}
|
|
|
|
DEBUG("[lwm2m:client:refresh_cred] refreshing DTLS credentials\n");
|
|
|
|
lwm2m_object_t *sec = lwm2m_get_object_by_id(_client_data, LWM2M_SECURITY_OBJECT_ID);
|
|
if (!sec) {
|
|
DEBUG("[lwm2m:client:refresh_cred] no security object found\n");
|
|
return;
|
|
}
|
|
|
|
/* prepare query */
|
|
lwm2m_uri_t query_uri;
|
|
LWM2M_URI_RESET(&query_uri);
|
|
query_uri.objectId = LWM2M_SECURITY_OBJECT_ID;
|
|
|
|
lwm2m_list_t *instance = sec->instanceList;
|
|
int64_t val;
|
|
|
|
/* check all registered security object instances */
|
|
do {
|
|
/* get the security mode */
|
|
query_uri.instanceId = instance->id;
|
|
query_uri.resourceId = LWM2M_SECURITY_SECURITY_ID;
|
|
|
|
int res = lwm2m_get_int(_client_data, &query_uri, &val);
|
|
if (res < 0) {
|
|
DEBUG("[lwm2m:client:refresh_cred] could not get security mode of %" PRIu16 "\n",
|
|
instance->id);
|
|
continue;
|
|
}
|
|
|
|
if (val == LWM2M_SECURITY_MODE_PRE_SHARED_KEY ||
|
|
val == LWM2M_SECURITY_MODE_RAW_PUBLIC_KEY) {
|
|
credman_tag_t tag = lwm2m_object_security_get_credential(instance->id);
|
|
if (tag != CREDMAN_TAG_EMPTY) {
|
|
lwm2m_client_add_credential(tag);
|
|
}
|
|
}
|
|
} while ((instance = instance->next) != NULL);
|
|
}
|
|
#endif /* MODULE_WAKAAMA_CLIENT_DTLS */
|