From f20eeebbd12b91918fbf9393f4d0289209e70920 Mon Sep 17 00:00:00 2001 From: Juha Heiskanen Date: Fri, 8 Apr 2022 05:20:55 -0700 Subject: [PATCH] net: lwm2m: DTLS session cache enable and queue mode update Enabled DTLS session cache for support session resume. Fixed LwM2M queue mode for close connection and reconnect automatically. Re-connect will do Registration update before it send queued data. Session resume is helping a case when NAT change address and cause less network traffic. Signed-off-by: Juha Heiskanen --- include/zephyr/net/lwm2m.h | 16 +++ subsys/net/lib/lwm2m/Kconfig | 5 + subsys/net/lib/lwm2m/lwm2m_engine.c | 166 ++++++++++++++++++++++--- subsys/net/lib/lwm2m/lwm2m_engine.h | 7 ++ subsys/net/lib/lwm2m/lwm2m_rd_client.c | 47 +++++++ subsys/net/lib/lwm2m/lwm2m_rd_client.h | 4 +- 6 files changed, 230 insertions(+), 15 deletions(-) diff --git a/include/zephyr/net/lwm2m.h b/include/zephyr/net/lwm2m.h index 25bad21286d..6108cf7bc47 100644 --- a/include/zephyr/net/lwm2m.h +++ b/include/zephyr/net/lwm2m.h @@ -117,6 +117,9 @@ struct lwm2m_ctx { struct coap_pending pendings[CONFIG_LWM2M_ENGINE_MAX_PENDING]; struct coap_reply replies[CONFIG_LWM2M_ENGINE_MAX_REPLIES]; sys_slist_t pending_sends; +#if defined(CONFIG_LWM2M_QUEUE_MODE_ENABLED) + sys_slist_t queued_messages; +#endif sys_slist_t observer; /** A pointer to currently processed request, for internal LwM2M engine @@ -151,6 +154,19 @@ struct lwm2m_ctx { */ bool use_dtls; +#if defined(CONFIG_LWM2M_QUEUE_MODE_ENABLED) + /** + * Flag to indicate that the socket connection is suspended. + * With queue mode, this will tell if there is a need to reconnect. + */ + bool connection_suspended; + + /** + * Flag to indicate that the client is buffering Notifications and Send messages. + * True value buffer Notifications and Send messages. + */ + bool buffer_client_messages; +#endif /** Current index of Security Object used for server credentials */ int sec_obj_inst; diff --git a/subsys/net/lib/lwm2m/Kconfig b/subsys/net/lib/lwm2m/Kconfig index eecc59e1ec0..f2a4901390b 100644 --- a/subsys/net/lib/lwm2m/Kconfig +++ b/subsys/net/lib/lwm2m/Kconfig @@ -192,6 +192,11 @@ config LWM2M_QUEUE_MODE_UPTIME defaults to 93 seconds, see RFC 7252), it does not forbid other values though. +config LWM2M_TLS_SESSION_CACHING + bool "TLS session caching" + help + Enabling this only when feature is supported in TLS library. + config LWM2M_RD_CLIENT_SUPPORT bool "support for LWM2M client bootstrap/registration state machine" default y diff --git a/subsys/net/lib/lwm2m/lwm2m_engine.c b/subsys/net/lib/lwm2m/lwm2m_engine.c index 352921f6520..4b29072ba52 100644 --- a/subsys/net/lib/lwm2m/lwm2m_engine.c +++ b/subsys/net/lib/lwm2m/lwm2m_engine.c @@ -1338,6 +1338,9 @@ void lwm2m_reset_message(struct lwm2m_message *msg, bool release) if (msg->ctx) { sys_slist_find_and_remove(&msg->ctx->pending_sends, &msg->node); +#if defined(CONFIG_LWM2M_QUEUE_MODE_ENABLED) + sys_slist_find_and_remove(&msg->ctx->queued_messages, &msg->node); +#endif } if (release) { @@ -1421,15 +1424,85 @@ cleanup: return r; } +#if defined(CONFIG_LWM2M_QUEUE_MODE_ENABLED) +int lwm2m_engine_connection_resume(struct lwm2m_ctx *client_ctx) +{ +#ifdef CONFIG_LWM2M_DTLS_SUPPORT + if (!client_ctx->use_dtls) { + return 0; + } + + if (client_ctx->connection_suspended) { + client_ctx->connection_suspended = false; + LOG_DBG("Resume suspended connection"); + return lwm2m_socket_start(client_ctx); + } +#endif + return 0; +} +#endif + + +#if defined(CONFIG_LWM2M_QUEUE_MODE_ENABLED) +int lwm2m_push_queued_buffers(struct lwm2m_ctx *client_ctx) +{ + client_ctx->buffer_client_messages = false; + while (!sys_slist_is_empty(&client_ctx->queued_messages)) { + sys_snode_t *msg_node = sys_slist_get(&client_ctx->queued_messages); + struct lwm2m_message *msg; + + if (!msg_node) { + break; + } + msg = SYS_SLIST_CONTAINER(msg_node, msg, node); + sys_slist_append(&msg->ctx->pending_sends, &msg->node); + } + return 0; +} +#endif + int lwm2m_send_message_async(struct lwm2m_message *msg) { +#if defined(CONFIG_LWM2M_QUEUE_MODE_ENABLED) && defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT) + int ret; + + ret = lwm2m_rd_client_connection_resume(msg->ctx); + if (ret) { + lwm2m_reset_message(msg, true); + return ret; + } +#endif sys_slist_append(&msg->ctx->pending_sends, &msg->node); if (IS_ENABLED(CONFIG_LWM2M_RD_CLIENT_SUPPORT) && IS_ENABLED(CONFIG_LWM2M_QUEUE_MODE_ENABLED)) { engine_update_tx_time(); } + return 0; +} +int lwm2m_information_interface_send(struct lwm2m_message *msg) +{ +#if defined(CONFIG_LWM2M_QUEUE_MODE_ENABLED) && defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT) + int ret; + + ret = lwm2m_rd_client_connection_resume(msg->ctx); + if (ret) { + lwm2m_reset_message(msg, true); + return ret; + } + + if (msg->ctx->buffer_client_messages) { + sys_slist_append(&msg->ctx->queued_messages, &msg->node); + return 0; + } +#endif + sys_slist_append(&msg->ctx->pending_sends, &msg->node); + + if (IS_ENABLED(CONFIG_LWM2M_RD_CLIENT_SUPPORT) && + IS_ENABLED(CONFIG_LWM2M_QUEUE_MODE_ENABLED)) { + engine_update_tx_time(); + } return 0; } @@ -5250,7 +5323,7 @@ static int generate_notify_message(struct lwm2m_ctx *ctx, goto cleanup; } - lwm2m_send_message_async(msg); + lwm2m_information_interface_send(msg); LOG_DBG("NOTIFY MSG: SENT"); return 0; @@ -5344,6 +5417,44 @@ static int32_t lwm2m_engine_service(const int64_t timestamp) timestamp); } +#if defined(CONFIG_LWM2M_QUEUE_MODE_ENABLED) + +int lwm2m_engine_close_socket_connection(struct lwm2m_ctx *client_ctx) +{ + int ret = 0; + /* Enable Queue mode buffer store */ + client_ctx->buffer_client_messages = true; + +#ifdef CONFIG_LWM2M_DTLS_SUPPORT + if (!client_ctx->use_dtls) { + return 0; + } + + if (client_ctx->sock_fd >= 0) { + ret = close(client_ctx->sock_fd); + if (ret) { + LOG_ERR("Failed to close socket: %d", errno); + ret = -errno; + return ret; + } + client_ctx->sock_fd = -1; + client_ctx->connection_suspended = true; + } + + /* Open socket again that Observation and re-send functionality works */ + client_ctx->sock_fd = + socket(client_ctx->remote_addr.sa_family, SOCK_DGRAM, IPPROTO_DTLS_1_2); + + if (client_ctx->sock_fd < 0) { + LOG_ERR("Failed to create socket: %d", errno); + return -errno; + } +#endif + + return ret; +} +#endif + int lwm2m_engine_context_close(struct lwm2m_ctx *client_ctx) { int sock_fd = client_ctx->sock_fd; @@ -5369,7 +5480,10 @@ int lwm2m_engine_context_close(struct lwm2m_ctx *client_ctx) CONFIG_LWM2M_ENGINE_MAX_PENDING); coap_replies_clear(client_ctx->replies, CONFIG_LWM2M_ENGINE_MAX_REPLIES); - +#if defined(CONFIG_LWM2M_QUEUE_MODE_ENABLED) + client_ctx->connection_suspended = false; + client_ctx->buffer_client_messages = true; +#endif lwm2m_socket_del(client_ctx); client_ctx->sock_fd = -1; if (sock_fd >= 0) { @@ -5383,6 +5497,11 @@ void lwm2m_engine_context_init(struct lwm2m_ctx *client_ctx) { sys_slist_init(&client_ctx->pending_sends); sys_slist_init(&client_ctx->observer); +#if defined(CONFIG_LWM2M_QUEUE_MODE_ENABLED) + client_ctx->buffer_client_messages = true; + client_ctx->connection_suspended = false; + sys_slist_init(&client_ctx->queued_messages); +#endif } /* LwM2M Socket Integration */ @@ -5633,6 +5752,7 @@ int lwm2m_socket_start(struct lwm2m_ctx *client_ctx) socklen_t addr_len; int flags; int ret; + bool allocate_socket = false; #if defined(CONFIG_LWM2M_DTLS_SUPPORT) uint8_t tmp; @@ -5653,15 +5773,20 @@ int lwm2m_socket_start(struct lwm2m_ctx *client_ctx) return ret; } } - - if (client_ctx->use_dtls) { - client_ctx->sock_fd = socket(client_ctx->remote_addr.sa_family, - SOCK_DGRAM, IPPROTO_DTLS_1_2); - } else #endif /* CONFIG_LWM2M_DTLS_SUPPORT */ - { - client_ctx->sock_fd = socket(client_ctx->remote_addr.sa_family, - SOCK_DGRAM, IPPROTO_UDP); + + if (client_ctx->sock_fd < 0) { + allocate_socket = true; +#if defined(CONFIG_LWM2M_DTLS_SUPPORT) + if (client_ctx->use_dtls) { + client_ctx->sock_fd = socket(client_ctx->remote_addr.sa_family, SOCK_DGRAM, + IPPROTO_DTLS_1_2); + } else +#endif /* CONFIG_LWM2M_DTLS_SUPPORT */ + { + client_ctx->sock_fd = + socket(client_ctx->remote_addr.sa_family, SOCK_DGRAM, IPPROTO_UDP); + } } if (client_ctx->sock_fd < 0) { @@ -5683,6 +5808,18 @@ int lwm2m_socket_start(struct lwm2m_ctx *client_ctx) goto error; } + if (IS_ENABLED(CONFIG_LWM2M_TLS_SESSION_CACHING)) { + int session_cache = TLS_SESSION_CACHE_ENABLED; + + ret = setsockopt(client_ctx->sock_fd, SOL_TLS, TLS_SESSION_CACHE, + &session_cache, sizeof(session_cache)); + if (ret < 0) { + ret = -errno; + LOG_ERR("Failed to set TLS_SESSION_CACHE option: %d", errno); + goto error; + } + } + if (client_ctx->hostname_verify && (client_ctx->desthostname != NULL)) { /** store character at len position */ tmp = client_ctx->desthostname[client_ctx->desthostnamelen]; @@ -5734,9 +5871,10 @@ int lwm2m_socket_start(struct lwm2m_ctx *client_ctx) } LOG_INF("Connected, sock id %d", client_ctx->sock_fd); - - return lwm2m_socket_add(client_ctx); - + if (allocate_socket) { + return lwm2m_socket_add(client_ctx); + } + return 0; error: lwm2m_engine_context_close(client_ctx); return ret; @@ -6334,7 +6472,7 @@ int lwm2m_engine_send(struct lwm2m_ctx *ctx, char const *path_list[], uint8_t pa goto cleanup; } LOG_INF("Send op to server (/dp)"); - lwm2m_send_message_async(msg); + lwm2m_information_interface_send(msg); return 0; cleanup: diff --git a/subsys/net/lib/lwm2m/lwm2m_engine.h b/subsys/net/lib/lwm2m/lwm2m_engine.h index cb31e97d244..d5d54666189 100644 --- a/subsys/net/lib/lwm2m/lwm2m_engine.h +++ b/subsys/net/lib/lwm2m/lwm2m_engine.h @@ -123,6 +123,8 @@ struct lwm2m_message *lwm2m_get_message(struct lwm2m_ctx *client_ctx); void lwm2m_reset_message(struct lwm2m_message *msg, bool release); int lwm2m_init_message(struct lwm2m_message *msg); int lwm2m_send_message_async(struct lwm2m_message *msg); +/* Notification and Send operation */ +int lwm2m_information_interface_send(struct lwm2m_message *msg); int lwm2m_send_empty_ack(struct lwm2m_ctx *client_ctx, uint16_t mid); int lwm2m_register_payload_handler(struct lwm2m_message *msg); @@ -192,6 +194,11 @@ const char *lwm2m_engine_get_attr_name(const struct lwm2m_attr *attr); int lwm2m_socket_add(struct lwm2m_ctx *ctx); void lwm2m_socket_del(struct lwm2m_ctx *ctx); int lwm2m_socket_start(struct lwm2m_ctx *client_ctx); +#if defined(CONFIG_LWM2M_QUEUE_MODE_ENABLED) +int lwm2m_engine_close_socket_connection(struct lwm2m_ctx *client_ctx); +int lwm2m_engine_connection_resume(struct lwm2m_ctx *client_ctx); +int lwm2m_push_queued_buffers(struct lwm2m_ctx *client_ctx); +#endif int lwm2m_parse_peerinfo(char *url, struct lwm2m_ctx *client_ctx, bool is_firmware_uri); #endif /* LWM2M_ENGINE_H */ diff --git a/subsys/net/lib/lwm2m/lwm2m_rd_client.c b/subsys/net/lib/lwm2m/lwm2m_rd_client.c index 06d04cdd088..7acfa7f8d47 100644 --- a/subsys/net/lib/lwm2m/lwm2m_rd_client.c +++ b/subsys/net/lib/lwm2m/lwm2m_rd_client.c @@ -146,11 +146,20 @@ static void set_sm_state(uint8_t sm_state) if (client.engine_state == ENGINE_UPDATE_SENT && (sm_state == ENGINE_REGISTRATION_DONE || sm_state == ENGINE_REGISTRATION_DONE_RX_OFF)) { +#if defined(CONFIG_LWM2M_QUEUE_MODE_ENABLED) + lwm2m_push_queued_buffers(client.ctx); +#endif event = LWM2M_RD_CLIENT_EVENT_REG_UPDATE_COMPLETE; } else if (sm_state == ENGINE_REGISTRATION_DONE) { +#if defined(CONFIG_LWM2M_QUEUE_MODE_ENABLED) + lwm2m_push_queued_buffers(client.ctx); +#endif event = LWM2M_RD_CLIENT_EVENT_REGISTRATION_COMPLETE; } else if (sm_state == ENGINE_REGISTRATION_DONE_RX_OFF) { event = LWM2M_RD_CLIENT_EVENT_QUEUE_MODE_RX_OFF; +#if defined(CONFIG_LWM2M_QUEUE_MODE_ENABLED) + lwm2m_engine_close_socket_connection(client.ctx); +#endif } else if ((sm_state == ENGINE_INIT || sm_state == ENGINE_DEREGISTERED) && (client.engine_state >= ENGINE_DO_REGISTRATION && @@ -919,6 +928,15 @@ static int sm_registration_done(void) update_objects = client.update_objects; client.trigger_update = false; client.update_objects = false; +#if defined(CONFIG_LWM2M_QUEUE_MODE_ENABLED) + ret = lwm2m_engine_connection_resume(client.ctx); + if (ret) { + lwm2m_engine_context_close(client.ctx); + /* perform full registration */ + set_sm_state(ENGINE_DO_REGISTRATION); + return ret; + } +#endif ret = sm_send_registration(update_objects, do_update_reply_cb, @@ -1154,6 +1172,35 @@ struct lwm2m_ctx *lwm2m_rd_client_ctx(void) return client.ctx; } +#if defined(CONFIG_LWM2M_QUEUE_MODE_ENABLED) +int lwm2m_rd_client_connection_resume(struct lwm2m_ctx *client_ctx) +{ + if (client.ctx != client_ctx) { + return -EPERM; + } + + if (client.engine_state == ENGINE_REGISTRATION_DONE_RX_OFF) { +#ifdef CONFIG_LWM2M_DTLS_SUPPORT + /* + * Switch state for triggering a proper registration message + * if CONFIG_LWM2M_TLS_SESSION_CACHING is false we force full + * registration after Fully DTLS handshake + */ + if (IS_ENABLED(CONFIG_LWM2M_TLS_SESSION_CACHING)) { + client.engine_state = ENGINE_REGISTRATION_DONE; + } else { + client.engine_state = ENGINE_DO_REGISTRATION; + } +#else + client.engine_state = ENGINE_REGISTRATION_DONE; +#endif + client.trigger_update = true; + } + + return 0; +} +#endif + static int lwm2m_rd_client_init(const struct device *dev) { k_mutex_init(&client.mutex); diff --git a/subsys/net/lib/lwm2m/lwm2m_rd_client.h b/subsys/net/lib/lwm2m/lwm2m_rd_client.h index f3bdee05a36..8a7ca1209f8 100644 --- a/subsys/net/lib/lwm2m/lwm2m_rd_client.h +++ b/subsys/net/lib/lwm2m/lwm2m_rd_client.h @@ -43,7 +43,9 @@ int engine_trigger_bootstrap(void); #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP) void engine_bootstrap_finish(void); #endif - +#if defined(CONFIG_LWM2M_QUEUE_MODE_ENABLED) +int lwm2m_rd_client_connection_resume(struct lwm2m_ctx *client_ctx); +#endif void engine_update_tx_time(void); #endif /* LWM2M_RD_CLIENT_H */