1 /*
2 * Copyright (c) 2017 Linaro Limited
3 * Copyright (c) 2017-2019 Foundries.io
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 /*
9 * Copyright (c) 2015, Yanzi Networks AB.
10 * All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. Neither the name of the copyright holder nor the names of its
21 * contributors may be used to endorse or promote products derived
22 * from this software without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
27 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
28 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
33 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
35 * OF THE POSSIBILITY OF SUCH DAMAGE.
36 */
37
38 /*
39 * Original authors:
40 * Joakim Eriksson <joakime@sics.se>
41 * Niclas Finne <nfi@sics.se>
42 * Joel Hoglund <joel@sics.se>
43 */
44
45 #define LOG_MODULE_NAME net_lwm2m_rd_client
46 #define LOG_LEVEL CONFIG_LWM2M_LOG_LEVEL
47
48 #include <zephyr/logging/log.h>
49 LOG_MODULE_REGISTER(LOG_MODULE_NAME);
50
51 #include <zephyr/types.h>
52 #include <stddef.h>
53 #include <stdio.h>
54 #include <string.h>
55 #include <errno.h>
56 #include <zephyr/init.h>
57 #include <zephyr/sys/printk.h>
58 #include <zephyr/net/socket.h>
59
60 #include "lwm2m_object.h"
61 #include "lwm2m_engine.h"
62 #include "lwm2m_rd_client.h"
63 #include "lwm2m_rw_link_format.h"
64 #include "lwm2m_util.h"
65
66 #define LWM2M_RD_CLIENT_URI "rd"
67 #define SECONDS_TO_UPDATE_EARLY CONFIG_LWM2M_SECONDS_TO_UPDATE_EARLY
68 #define CLIENT_EP_LEN CONFIG_LWM2M_RD_CLIENT_ENDPOINT_NAME_MAX_LENGTH
69 #define CLIENT_BINDING_LEN sizeof("UQ")
70 #define CLIENT_QUEUE_LEN sizeof("Q")
71 #define DELAY_BEFORE_CLOSING (1 * MSEC_PER_SEC)
72 #define DELAY_FOR_ACK 100U
73 #define EXCHANGE_LIFETIME 247U
74
75 static void sm_handle_registration_update_failure(void);
76 static int sm_send_registration_msg(void);
77 static bool sm_is_suspended(void);
78 static void lwm2m_rd_client_service(struct k_work *work);
79 static int64_t calc_next_event(void);
80 static void set_sm_state_delayed(uint8_t sm_state, int64_t delay_ms);
81 static void set_sm_state(uint8_t sm_state);
82
83 /* The states for the RD client state machine */
84 /*
85 * When node is unregistered it ends up in UNREGISTERED
86 * and this is going to be there until use X or Y kicks it
87 * back into INIT again
88 */
89 enum sm_engine_state {
90 ENGINE_IDLE,
91 ENGINE_INIT,
92 ENGINE_DO_BOOTSTRAP_REG,
93 ENGINE_BOOTSTRAP_REG_SENT,
94 ENGINE_BOOTSTRAP_REG_DONE,
95 ENGINE_BOOTSTRAP_TRANS_DONE,
96 ENGINE_DO_REGISTRATION,
97 ENGINE_SEND_REGISTRATION,
98 ENGINE_REGISTRATION_SENT,
99 ENGINE_REGISTRATION_DONE,
100 ENGINE_REGISTRATION_DONE_RX_OFF,
101 ENGINE_UPDATE_REGISTRATION,
102 ENGINE_UPDATE_SENT,
103 ENGINE_SUSPENDED,
104 ENGINE_DEREGISTER,
105 ENGINE_DEREGISTER_SENT,
106 ENGINE_DEREGISTERED,
107 ENGINE_NETWORK_ERROR,
108 };
109
110 struct lwm2m_rd_client_info {
111 struct k_mutex mutex;
112 struct lwm2m_message rd_message;
113 struct lwm2m_ctx *ctx;
114 uint32_t lifetime;
115 uint8_t engine_state;
116 uint8_t retries;
117 uint8_t retry_delay;
118
119 int64_t last_update;
120 int64_t last_tx;
121 int64_t next_event;
122 int64_t last_state_change;
123
124 char ep_name[CLIENT_EP_LEN];
125 char server_ep[CLIENT_EP_LEN];
126
127 bool use_bootstrap : 1;
128
129 bool trigger_update : 1;
130 bool update_objects : 1;
131 bool close_socket : 1;
132 } client;
133
134 /* Allocate some data for queries and updates. Make sure it's large enough to
135 * hold the largest query string, which in most cases will be the endpoint
136 * string. In other case, 32 bytes are enough to encode any other query string
137 * documented in the LwM2M specification.
138 */
139 static char query_buffer[MAX(32, sizeof("ep=") + CLIENT_EP_LEN)];
140 static enum sm_engine_state suspended_client_state;
141
rd_get_message(void)142 static struct lwm2m_message *rd_get_message(void)
143 {
144 if (client.rd_message.ctx) {
145 /* Free old message */
146 lwm2m_reset_message(&client.rd_message, true);
147 }
148
149 client.rd_message.ctx = client.ctx;
150 return &client.rd_message;
151
152 }
153
rd_client_message_free(void)154 static void rd_client_message_free(void)
155 {
156 lwm2m_reset_message(lwm2m_get_ongoing_rd_msg(), true);
157 }
158
159
lwm2m_get_ongoing_rd_msg(void)160 struct lwm2m_message *lwm2m_get_ongoing_rd_msg(void)
161 {
162 if (!client.ctx || !client.rd_message.ctx) {
163 return NULL;
164 }
165 return &client.rd_message;
166 }
167
engine_update_tx_time(void)168 void engine_update_tx_time(void)
169 {
170 client.last_tx = k_uptime_get();
171 }
172
next_event_at(int64_t timestamp)173 static void next_event_at(int64_t timestamp)
174 {
175 client.next_event = timestamp;
176 (void)lwm2m_engine_call_at(lwm2m_rd_client_service, timestamp);
177 }
178
set_sm_state_delayed(uint8_t sm_state,int64_t delay_ms)179 static void set_sm_state_delayed(uint8_t sm_state, int64_t delay_ms)
180 {
181 k_mutex_lock(&client.mutex, K_FOREVER);
182 enum lwm2m_rd_client_event event = LWM2M_RD_CLIENT_EVENT_NONE;
183
184 /* Determine if a callback to the app is needed */
185 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
186 if (sm_state == ENGINE_BOOTSTRAP_REG_DONE) {
187 event = LWM2M_RD_CLIENT_EVENT_BOOTSTRAP_REG_COMPLETE;
188 } else if (client.engine_state == ENGINE_BOOTSTRAP_TRANS_DONE &&
189 sm_state == ENGINE_DO_REGISTRATION) {
190 event = LWM2M_RD_CLIENT_EVENT_BOOTSTRAP_TRANSFER_COMPLETE;
191 } else
192 #endif
193 if (client.engine_state == ENGINE_UPDATE_SENT &&
194 (sm_state == ENGINE_REGISTRATION_DONE ||
195 sm_state == ENGINE_REGISTRATION_DONE_RX_OFF)) {
196 lwm2m_push_queued_buffers(client.ctx);
197 event = LWM2M_RD_CLIENT_EVENT_REG_UPDATE_COMPLETE;
198 } else if (sm_state == ENGINE_REGISTRATION_DONE) {
199 lwm2m_push_queued_buffers(client.ctx);
200 event = LWM2M_RD_CLIENT_EVENT_REGISTRATION_COMPLETE;
201 } else if (sm_state == ENGINE_REGISTRATION_DONE_RX_OFF) {
202 event = LWM2M_RD_CLIENT_EVENT_QUEUE_MODE_RX_OFF;
203 } else if ((sm_state == ENGINE_INIT ||
204 sm_state == ENGINE_DEREGISTERED) &&
205 (client.engine_state >= ENGINE_DO_REGISTRATION &&
206 client.engine_state <= ENGINE_DEREGISTER_SENT)) {
207 event = LWM2M_RD_CLIENT_EVENT_DISCONNECT;
208 } else if (sm_state == ENGINE_NETWORK_ERROR) {
209 lwm2m_socket_close(client.ctx);
210 client.retry_delay = 1 << client.retries;
211 client.retries++;
212 if (client.retries > CONFIG_LWM2M_RD_CLIENT_MAX_RETRIES) {
213 client.retries = 0;
214 event = LWM2M_RD_CLIENT_EVENT_NETWORK_ERROR;
215 }
216 } else if (sm_state == ENGINE_UPDATE_REGISTRATION) {
217 event = LWM2M_RD_CLIENT_EVENT_REG_UPDATE;
218 } else if (sm_state == ENGINE_DEREGISTER) {
219 event = LWM2M_RD_CLIENT_EVENT_DEREGISTER;
220 }
221
222 if (sm_is_suspended()) {
223 /* Just change the state where we are going to resume next */
224 suspended_client_state = sm_state;
225 } else {
226 client.engine_state = sm_state;
227 }
228
229 if (event > LWM2M_RD_CLIENT_EVENT_NONE && client.ctx->event_cb) {
230 client.ctx->event_cb(client.ctx, event);
231 }
232
233 /* Suspend socket after Event callback */
234 if (event == LWM2M_RD_CLIENT_EVENT_QUEUE_MODE_RX_OFF) {
235 if (IS_ENABLED(CONFIG_LWM2M_RD_CLIENT_SUSPEND_SOCKET_AT_IDLE) ||
236 IS_ENABLED(CONFIG_LWM2M_RD_CLIENT_STOP_POLLING_AT_IDLE)) {
237 lwm2m_socket_suspend(client.ctx);
238 } else if (IS_ENABLED(CONFIG_LWM2M_RD_CLIENT_CLOSE_SOCKET_AT_IDLE)) {
239 lwm2m_close_socket(client.ctx);
240 }
241 }
242 client.last_state_change = k_uptime_get();
243 next_event_at(k_uptime_get() + delay_ms);
244 k_mutex_unlock(&client.mutex);
245 }
246
set_sm_state(uint8_t sm_state)247 static void set_sm_state(uint8_t sm_state)
248 {
249 set_sm_state_delayed(sm_state, 0);
250 }
251
sm_is_bootstrap(void)252 static bool sm_is_bootstrap(void)
253 {
254 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
255 k_mutex_lock(&client.mutex, K_FOREVER);
256 bool is_bootstrap = (client.engine_state >= ENGINE_DO_BOOTSTRAP_REG &&
257 client.engine_state <= ENGINE_BOOTSTRAP_TRANS_DONE);
258 k_mutex_unlock(&client.mutex);
259 return is_bootstrap;
260 #else
261 return false;
262 #endif
263 }
264
sm_is_registered(void)265 static bool sm_is_registered(void)
266 {
267 k_mutex_lock(&client.mutex, K_FOREVER);
268 bool registered = (client.engine_state >= ENGINE_REGISTRATION_DONE &&
269 client.engine_state <= ENGINE_DEREGISTER_SENT);
270
271 k_mutex_unlock(&client.mutex);
272 return registered;
273 }
274
sm_is_suspended(void)275 static bool sm_is_suspended(void)
276 {
277 k_mutex_lock(&client.mutex, K_FOREVER);
278 bool suspended = (client.engine_state == ENGINE_SUSPENDED);
279
280 k_mutex_unlock(&client.mutex);
281 return suspended;
282 }
283
284
get_sm_state(void)285 static uint8_t get_sm_state(void)
286 {
287 k_mutex_lock(&client.mutex, K_FOREVER);
288 uint8_t state = client.engine_state;
289
290 k_mutex_unlock(&client.mutex);
291 return state;
292 }
293
sm_handle_timeout_state(struct lwm2m_message * msg,enum sm_engine_state sm_state)294 static void sm_handle_timeout_state(struct lwm2m_message *msg,
295 enum sm_engine_state sm_state)
296 {
297 k_mutex_lock(&client.mutex, K_FOREVER);
298 enum lwm2m_rd_client_event event = LWM2M_RD_CLIENT_EVENT_NONE;
299
300 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
301 if (client.engine_state == ENGINE_BOOTSTRAP_REG_SENT) {
302 event = LWM2M_RD_CLIENT_EVENT_BOOTSTRAP_REG_FAILURE;
303 } else
304 #endif
305 {
306 if (client.engine_state == ENGINE_REGISTRATION_SENT) {
307 event = LWM2M_RD_CLIENT_EVENT_REG_TIMEOUT;
308 } else if (client.engine_state == ENGINE_UPDATE_SENT) {
309 event = LWM2M_RD_CLIENT_EVENT_REG_TIMEOUT;
310 } else if (client.engine_state == ENGINE_DEREGISTER_SENT) {
311 event = LWM2M_RD_CLIENT_EVENT_DEREGISTER_FAILURE;
312 } else {
313 /* TODO: unknown timeout state */
314 }
315 }
316
317 set_sm_state(sm_state);
318
319 if (event > LWM2M_RD_CLIENT_EVENT_NONE && client.ctx->event_cb) {
320 client.ctx->event_cb(client.ctx, event);
321 }
322 k_mutex_unlock(&client.mutex);
323 }
324
sm_handle_failure_state(enum sm_engine_state sm_state)325 static void sm_handle_failure_state(enum sm_engine_state sm_state)
326 {
327 k_mutex_lock(&client.mutex, K_FOREVER);
328 enum lwm2m_rd_client_event event = LWM2M_RD_CLIENT_EVENT_NONE;
329
330 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
331 if (client.engine_state == ENGINE_BOOTSTRAP_REG_SENT) {
332 event = LWM2M_RD_CLIENT_EVENT_BOOTSTRAP_REG_FAILURE;
333 } else
334 #endif
335 if (client.engine_state == ENGINE_REGISTRATION_SENT) {
336 event = LWM2M_RD_CLIENT_EVENT_REGISTRATION_FAILURE;
337 } else if (client.engine_state == ENGINE_UPDATE_SENT) {
338 sm_handle_registration_update_failure();
339 k_mutex_unlock(&client.mutex);
340 return;
341 } else if (client.engine_state == ENGINE_DEREGISTER_SENT) {
342 event = LWM2M_RD_CLIENT_EVENT_DEREGISTER_FAILURE;
343 }
344
345 lwm2m_engine_stop(client.ctx);
346 set_sm_state(sm_state);
347
348 if (event > LWM2M_RD_CLIENT_EVENT_NONE && client.ctx->event_cb) {
349 client.ctx->event_cb(client.ctx, event);
350 }
351 k_mutex_unlock(&client.mutex);
352 }
353
354 /* force state machine restart */
socket_fault_cb(int error)355 static void socket_fault_cb(int error)
356 {
357 LOG_ERR("RD Client socket error: %d", error);
358 lwm2m_socket_close(client.ctx);
359
360 if (IS_ENABLED(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP) && sm_is_bootstrap()) {
361 client.ctx->sec_obj_inst = -1;
362 /* force full registration */
363 client.last_update = 0;
364
365 if (get_sm_state() == ENGINE_BOOTSTRAP_TRANS_DONE) {
366 /* Ignore the error, some servers close the connection immediately
367 * after receiving Ack to Bootstrap-Finish command.
368 */
369 return;
370 }
371 }
372
373 /* Network error state causes engine to re-register,
374 * so only trigger that state if we are not stopping the
375 * engine.
376 */
377 if (client.engine_state > ENGINE_IDLE &&
378 client.engine_state < ENGINE_SUSPENDED) {
379 set_sm_state(ENGINE_NETWORK_ERROR);
380 } else if (client.engine_state != ENGINE_SUSPENDED) {
381 sm_handle_failure_state(ENGINE_IDLE);
382 }
383 }
384
385 /* force re-update with remote peer */
engine_trigger_update(bool update_objects)386 void engine_trigger_update(bool update_objects)
387 {
388 k_mutex_lock(&client.mutex, K_FOREVER);
389 if (client.engine_state < ENGINE_REGISTRATION_SENT ||
390 client.engine_state > ENGINE_UPDATE_SENT) {
391 k_mutex_unlock(&client.mutex);
392 return;
393 }
394
395 client.trigger_update = true;
396 /* short delay for Ack, then trigger an update */
397 next_event_at(k_uptime_get() + DELAY_FOR_ACK);
398
399 if (update_objects) {
400 client.update_objects = true;
401 }
402 k_mutex_unlock(&client.mutex);
403 }
404
code2str(uint8_t code)405 static inline const char *code2str(uint8_t code)
406 {
407 switch (code) {
408 case COAP_RESPONSE_CODE_BAD_REQUEST:
409 return "Bad Request";
410 case COAP_RESPONSE_CODE_FORBIDDEN:
411 return "Forbidden";
412 case COAP_RESPONSE_CODE_NOT_FOUND:
413 return "Not Found";
414 case COAP_RESPONSE_CODE_PRECONDITION_FAILED:
415 return "Precondition Failed";
416 default:
417 break;
418 }
419
420 return "Unknown";
421 }
422
423 /* state machine reply callbacks */
424
425 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
do_bootstrap_reply_cb(const struct coap_packet * response,struct coap_reply * reply,const struct sockaddr * from)426 static int do_bootstrap_reply_cb(const struct coap_packet *response,
427 struct coap_reply *reply,
428 const struct sockaddr *from)
429 {
430 uint8_t code;
431
432 code = coap_header_get_code(response);
433 LOG_DBG("Bootstrap callback (code:%u.%u)",
434 COAP_RESPONSE_CODE_CLASS(code),
435 COAP_RESPONSE_CODE_DETAIL(code));
436
437 if (code == COAP_RESPONSE_CODE_CHANGED) {
438 LOG_INF("Bootstrap registration done!");
439 set_sm_state(ENGINE_BOOTSTRAP_REG_DONE);
440 return 0;
441 }
442
443 LOG_ERR("Failed with code %u.%u (%s). Not Retrying.",
444 COAP_RESPONSE_CODE_CLASS(code), COAP_RESPONSE_CODE_DETAIL(code),
445 code2str(code));
446
447 sm_handle_failure_state(ENGINE_IDLE);
448
449 return 0;
450 }
451
do_bootstrap_reg_timeout_cb(struct lwm2m_message * msg)452 static void do_bootstrap_reg_timeout_cb(struct lwm2m_message *msg)
453 {
454 LOG_WRN("Bootstrap Timeout");
455
456 /* TODO:
457 * Look for the "next" BOOTSTRAP server entry in our security info
458 */
459
460 /* Restart from scratch */
461 sm_handle_timeout_state(msg, ENGINE_INIT);
462 }
463 #endif
464
engine_trigger_bootstrap(void)465 int engine_trigger_bootstrap(void)
466 {
467 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
468 k_mutex_lock(&client.mutex, K_FOREVER);
469
470 if (client.use_bootstrap) {
471 /* Bootstrap is not possible to trig */
472 LOG_WRN("Bootstrap process ongoing");
473 k_mutex_unlock(&client.mutex);
474 return -EPERM;
475 }
476 LOG_INF("Server Initiated Bootstrap");
477 /* Free ongoing possible message */
478 rd_client_message_free();
479 client.use_bootstrap = true;
480 client.trigger_update = false;
481 set_sm_state_delayed(ENGINE_INIT, DELAY_BEFORE_CLOSING);
482 k_mutex_unlock(&client.mutex);
483 return 0;
484 #else
485 return -EPERM;
486 #endif
487 }
do_registration_reply_cb(const struct coap_packet * response,struct coap_reply * reply,const struct sockaddr * from)488 static int do_registration_reply_cb(const struct coap_packet *response,
489 struct coap_reply *reply,
490 const struct sockaddr *from)
491 {
492 struct coap_option options[2];
493 uint8_t code;
494 int ret = -EINVAL;
495
496 code = coap_header_get_code(response);
497 LOG_DBG("Registration callback (code:%u.%u)",
498 COAP_RESPONSE_CODE_CLASS(code),
499 COAP_RESPONSE_CODE_DETAIL(code));
500
501 /* check state and possibly set registration to done */
502 if (code == COAP_RESPONSE_CODE_CREATED) {
503 ret = coap_find_options(response, COAP_OPTION_LOCATION_PATH,
504 options, 2);
505 if (ret < 2) {
506 LOG_ERR("Unexpected endpoint data returned. ret = %d", ret);
507 ret = -EINVAL;
508 goto fail;
509 }
510
511 /* option[0] should be "rd" */
512
513 if (options[1].len + 1 > sizeof(client.server_ep)) {
514 LOG_ERR("Unexpected length of query: "
515 "%u (expected %zu)\n",
516 options[1].len,
517 sizeof(client.server_ep));
518 ret = -EINVAL;
519 goto fail;
520 }
521
522 /* remember the last reg time */
523 client.last_update = k_uptime_get();
524
525 memcpy(client.server_ep, options[1].value,
526 options[1].len);
527 client.server_ep[options[1].len] = '\0';
528 set_sm_state(ENGINE_REGISTRATION_DONE);
529 LOG_INF("Registration Done (EP='%s')",
530 client.server_ep);
531
532 return 0;
533 }
534
535 LOG_ERR("Failed with code %u.%u (%s). Not Retrying.",
536 COAP_RESPONSE_CODE_CLASS(code), COAP_RESPONSE_CODE_DETAIL(code),
537 code2str(code));
538 fail:
539 sm_handle_failure_state(ENGINE_IDLE);
540
541 return ret;
542 }
543
do_registration_timeout_cb(struct lwm2m_message * msg)544 static void do_registration_timeout_cb(struct lwm2m_message *msg)
545 {
546 LOG_WRN("Registration Timeout");
547
548 /* Restart from scratch */
549 sm_handle_timeout_state(msg, ENGINE_INIT);
550 }
551
do_update_reply_cb(const struct coap_packet * response,struct coap_reply * reply,const struct sockaddr * from)552 static int do_update_reply_cb(const struct coap_packet *response,
553 struct coap_reply *reply,
554 const struct sockaddr *from)
555 {
556 uint8_t code;
557
558 code = coap_header_get_code(response);
559 LOG_INF("Update callback (code:%u.%u)",
560 COAP_RESPONSE_CODE_CLASS(code),
561 COAP_RESPONSE_CODE_DETAIL(code));
562
563 /* If NOT_FOUND just continue on */
564 if ((code == COAP_RESPONSE_CODE_CHANGED) ||
565 (code == COAP_RESPONSE_CODE_CREATED)) {
566 /* remember the last reg time */
567 client.last_update = k_uptime_get();
568 set_sm_state(ENGINE_REGISTRATION_DONE);
569 LOG_INF("Update Done");
570 return 0;
571 }
572
573 LOG_ERR("Failed with code %u.%u (%s). Retrying registration.",
574 COAP_RESPONSE_CODE_CLASS(code), COAP_RESPONSE_CODE_DETAIL(code),
575 code2str(code));
576
577 sm_handle_failure_state(ENGINE_DO_REGISTRATION);
578
579 return 0;
580 }
581
do_update_timeout_cb(struct lwm2m_message * msg)582 static void do_update_timeout_cb(struct lwm2m_message *msg)
583 {
584 LOG_WRN("Registration Update Timeout");
585
586 if (client.ctx->sock_fd > -1) {
587 client.close_socket = true;
588 }
589 /* Re-do registration */
590 sm_handle_timeout_state(msg, ENGINE_DO_REGISTRATION);
591 }
592
do_deregister_reply_cb(const struct coap_packet * response,struct coap_reply * reply,const struct sockaddr * from)593 static int do_deregister_reply_cb(const struct coap_packet *response,
594 struct coap_reply *reply,
595 const struct sockaddr *from)
596 {
597 uint8_t code;
598
599 code = coap_header_get_code(response);
600 LOG_DBG("Deregister callback (code:%u.%u)",
601 COAP_RESPONSE_CODE_CLASS(code),
602 COAP_RESPONSE_CODE_DETAIL(code));
603
604 if (code == COAP_RESPONSE_CODE_DELETED) {
605 LOG_INF("Deregistration success");
606 set_sm_state(ENGINE_DEREGISTERED);
607 return 0;
608 }
609
610 LOG_ERR("Failed with code %u.%u (%s). Not Retrying",
611 COAP_RESPONSE_CODE_CLASS(code), COAP_RESPONSE_CODE_DETAIL(code),
612 code2str(code));
613
614 sm_handle_failure_state(ENGINE_IDLE);
615
616 return 0;
617 }
618
do_deregister_timeout_cb(struct lwm2m_message * msg)619 static void do_deregister_timeout_cb(struct lwm2m_message *msg)
620 {
621 LOG_WRN("De-Registration Timeout");
622
623 sm_handle_timeout_state(msg, ENGINE_IDLE);
624 }
625
sm_bootstrap_verify(bool bootstrap_server,int sec_obj_inst)626 static bool sm_bootstrap_verify(bool bootstrap_server, int sec_obj_inst)
627 {
628 bool bootstrap;
629 int ret;
630
631 ret = lwm2m_get_bool(&LWM2M_OBJ(0, sec_obj_inst, 1), &bootstrap);
632 if (ret < 0) {
633 LOG_WRN("Failed to check bootstrap, err %d", ret);
634 return false;
635 }
636
637 if (bootstrap == bootstrap_server) {
638 return true;
639 } else {
640 return false;
641 }
642 }
643
sm_update_lifetime(int srv_obj_inst,uint32_t * lifetime)644 static bool sm_update_lifetime(int srv_obj_inst, uint32_t *lifetime)
645 {
646 uint32_t new_lifetime;
647
648 if (lwm2m_get_u32(&LWM2M_OBJ(1, srv_obj_inst, 1), &new_lifetime) < 0) {
649 new_lifetime = CONFIG_LWM2M_ENGINE_DEFAULT_LIFETIME;
650 LOG_INF("Using default lifetime: %u", new_lifetime);
651 }
652
653 if (new_lifetime < CONFIG_LWM2M_ENGINE_DEFAULT_LIFETIME) {
654 new_lifetime = CONFIG_LWM2M_ENGINE_DEFAULT_LIFETIME;
655 lwm2m_set_u32(&LWM2M_OBJ(1, srv_obj_inst, 1), new_lifetime);
656 LOG_INF("Overwrite a server lifetime with default");
657 }
658
659 if (new_lifetime != *lifetime) {
660 *lifetime = new_lifetime;
661 return true;
662 }
663
664 return false;
665 }
666
sm_select_server_inst(int sec_obj_inst,int * srv_obj_inst,uint32_t * lifetime)667 static int sm_select_server_inst(int sec_obj_inst, int *srv_obj_inst,
668 uint32_t *lifetime)
669 {
670 uint16_t server_id;
671 int ret, obj_inst_id;
672
673 ret = lwm2m_get_u16(&LWM2M_OBJ(0, sec_obj_inst, 10), &server_id);
674 if (ret < 0) {
675 LOG_WRN("Failed to obtain Short Server ID, err %d", ret);
676 return -EINVAL;
677 }
678
679 obj_inst_id = lwm2m_server_short_id_to_inst(server_id);
680 if (obj_inst_id < 0) {
681 LOG_WRN("Failed to obtain Server Object instance, err %d",
682 obj_inst_id);
683 return -EINVAL;
684 }
685
686 sm_update_lifetime(obj_inst_id, lifetime);
687 *srv_obj_inst = obj_inst_id;
688
689 return 0;
690 }
691
sm_select_security_inst(bool bootstrap_server,int * sec_obj_inst)692 static int sm_select_security_inst(bool bootstrap_server, int *sec_obj_inst)
693 {
694 int i, obj_inst_id = -1;
695
696 /* lookup existing index */
697 i = lwm2m_security_inst_id_to_index(*sec_obj_inst);
698 if (i >= 0 && sm_bootstrap_verify(bootstrap_server, *sec_obj_inst)) {
699 return 0;
700 }
701
702 *sec_obj_inst = -1;
703
704 /* Iterate over all instances to find the correct one. */
705 for (i = 0; i < CONFIG_LWM2M_SECURITY_INSTANCE_COUNT; i++) {
706 obj_inst_id = lwm2m_security_index_to_inst_id(i);
707 if (obj_inst_id < 0) {
708 LOG_WRN("Failed to get inst id for %d", i);
709 continue;
710 }
711
712 if (sm_bootstrap_verify(bootstrap_server, obj_inst_id)) {
713 *sec_obj_inst = obj_inst_id;
714 return 0;
715 }
716 }
717
718 LOG_WRN("sec_obj_inst: No matching servers found.");
719
720 return -ENOENT;
721 }
722
723 /* state machine step functions */
724
sm_do_init(void)725 static int sm_do_init(void)
726 {
727 lwm2m_engine_stop(client.ctx);
728 client.ctx->sec_obj_inst = -1;
729 client.ctx->srv_obj_inst = -1;
730 client.trigger_update = false;
731 client.lifetime = 0U;
732 client.retries = 0U;
733 client.last_update = 0U;
734 client.close_socket = false;
735
736 /* Do bootstrap or registration */
737 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
738 if (client.use_bootstrap) {
739 set_sm_state(ENGINE_DO_BOOTSTRAP_REG);
740 } else {
741 set_sm_state(ENGINE_DO_REGISTRATION);
742 }
743 #else
744 set_sm_state(ENGINE_DO_REGISTRATION);
745 #endif
746 return 0;
747 }
748
749 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
sm_send_bootstrap_registration(void)750 static int sm_send_bootstrap_registration(void)
751 {
752 struct lwm2m_message *msg;
753 int ret;
754
755 msg = rd_get_message();
756 if (!msg) {
757 LOG_ERR("Unable to get a lwm2m message!");
758 return -ENOMEM;
759 }
760
761 msg->type = COAP_TYPE_CON;
762 msg->code = COAP_METHOD_POST;
763 msg->mid = coap_next_id();
764 msg->tkl = LWM2M_MSG_TOKEN_GENERATE_NEW;
765 msg->reply_cb = do_bootstrap_reply_cb;
766 msg->message_timeout_cb = do_bootstrap_reg_timeout_cb;
767
768 ret = lwm2m_init_message(msg);
769 if (ret) {
770 goto cleanup;
771 }
772
773 /* TODO: handle return error */
774 coap_packet_append_option(&msg->cpkt, COAP_OPTION_URI_PATH,
775 "bs", strlen("bs"));
776
777 snprintk(query_buffer, sizeof(query_buffer) - 1, "ep=%s",
778 client.ep_name);
779 /* TODO: handle return error */
780 coap_packet_append_option(&msg->cpkt, COAP_OPTION_URI_QUERY,
781 query_buffer, strlen(query_buffer));
782
783
784 if (IS_ENABLED(CONFIG_LWM2M_VERSION_1_1)) {
785 int pct = LWM2M_FORMAT_OMA_TLV;
786
787 if (IS_ENABLED(CONFIG_LWM2M_RW_SENML_CBOR_SUPPORT)) {
788 pct = LWM2M_FORMAT_APP_SENML_CBOR;
789 } else if (IS_ENABLED(CONFIG_LWM2M_RW_SENML_JSON_SUPPORT)) {
790 pct = LWM2M_FORMAT_APP_SEML_JSON;
791 }
792
793 snprintk(query_buffer, sizeof(query_buffer) - 1, "pct=%d", pct);
794
795 coap_packet_append_option(&msg->cpkt, COAP_OPTION_URI_QUERY,
796 query_buffer, strlen(query_buffer));
797 }
798
799 /* log the bootstrap attempt */
800 LOG_DBG("Register ID with bootstrap server as '%s'",
801 query_buffer);
802
803 lwm2m_send_message_async(msg);
804
805 return 0;
806
807 cleanup:
808 lwm2m_reset_message(msg, true);
809 return ret;
810 }
811
sm_do_bootstrap_reg(void)812 static int sm_do_bootstrap_reg(void)
813 {
814 int ret;
815
816 /* clear out existing connection data */
817 if (client.ctx->sock_fd > -1) {
818 lwm2m_engine_stop(client.ctx);
819 }
820
821 client.ctx->bootstrap_mode = true;
822 ret = sm_select_security_inst(client.ctx->bootstrap_mode,
823 &client.ctx->sec_obj_inst);
824 if (ret < 0) {
825 /* no bootstrap server found, let's move to registration */
826 LOG_WRN("Bootstrap server not found! Try normal registration.");
827 set_sm_state(ENGINE_DO_REGISTRATION);
828 return ret;
829 }
830
831 LOG_INF("Bootstrap started with endpoint '%s' with client lifetime %d",
832 client.ep_name, client.lifetime);
833
834 ret = lwm2m_engine_start(client.ctx);
835 if (ret < 0) {
836 LOG_ERR("Cannot init LWM2M engine (%d)", ret);
837 set_sm_state(ENGINE_NETWORK_ERROR);
838 return ret;
839 }
840
841 ret = sm_send_bootstrap_registration();
842 if (!ret) {
843 set_sm_state(ENGINE_BOOTSTRAP_REG_SENT);
844 } else {
845 LOG_ERR("Bootstrap registration err: %d", ret);
846 set_sm_state(ENGINE_NETWORK_ERROR);
847 }
848
849 return ret;
850 }
851
engine_bootstrap_finish(void)852 void engine_bootstrap_finish(void)
853 {
854 LOG_INF("Bootstrap data transfer done!");
855 /* Delay the state transition, so engine have some time to send ACK
856 * before we close the socket
857 */
858 set_sm_state_delayed(ENGINE_BOOTSTRAP_TRANS_DONE, DELAY_BEFORE_CLOSING);
859 }
860
sm_bootstrap_trans_done(void)861 static int sm_bootstrap_trans_done(void)
862 {
863 /* close down context resources */
864 lwm2m_engine_stop(client.ctx);
865
866 /* reset security object instance */
867 client.ctx->sec_obj_inst = -1;
868 client.use_bootstrap = false;
869
870 set_sm_state(ENGINE_DO_REGISTRATION);
871
872 return 0;
873 }
874 #endif
875
sm_send_registration(bool send_obj_support_data,coap_reply_t reply_cb,lwm2m_message_timeout_cb_t timeout_cb)876 static int sm_send_registration(bool send_obj_support_data,
877 coap_reply_t reply_cb,
878 lwm2m_message_timeout_cb_t timeout_cb)
879 {
880 struct lwm2m_message *msg;
881 int ret;
882 char binding[CLIENT_BINDING_LEN];
883 char queue[CLIENT_QUEUE_LEN];
884
885 msg = rd_get_message();
886 if (!msg) {
887 LOG_ERR("Unable to get a lwm2m message!");
888 return -ENOMEM;
889 }
890
891 msg->type = COAP_TYPE_CON;
892 msg->code = COAP_METHOD_POST;
893 msg->mid = coap_next_id();
894 msg->tkl = LWM2M_MSG_TOKEN_GENERATE_NEW;
895 msg->reply_cb = reply_cb;
896 msg->message_timeout_cb = timeout_cb;
897
898 ret = lwm2m_init_message(msg);
899 if (ret) {
900 goto cleanup;
901 }
902
903 ret = coap_packet_append_option(&msg->cpkt, COAP_OPTION_URI_PATH,
904 LWM2M_RD_CLIENT_URI,
905 strlen(LWM2M_RD_CLIENT_URI));
906 if (ret < 0) {
907 goto cleanup;
908 }
909
910 if (sm_is_registered()) {
911 ret = coap_packet_append_option(
912 &msg->cpkt, COAP_OPTION_URI_PATH,
913 client.server_ep, strlen(client.server_ep));
914 if (ret < 0) {
915 goto cleanup;
916 }
917 }
918
919 if (send_obj_support_data) {
920 ret = coap_append_option_int(
921 &msg->cpkt, COAP_OPTION_CONTENT_FORMAT,
922 LWM2M_FORMAT_APP_LINK_FORMAT);
923 if (ret < 0) {
924 goto cleanup;
925 }
926 }
927
928 if (!sm_is_registered()) {
929 snprintk(query_buffer, sizeof(query_buffer) - 1,
930 "lwm2m=%s", LWM2M_PROTOCOL_VERSION_STRING);
931 ret = coap_packet_append_option(
932 &msg->cpkt, COAP_OPTION_URI_QUERY,
933 query_buffer, strlen(query_buffer));
934 if (ret < 0) {
935 goto cleanup;
936 }
937
938 snprintk(query_buffer, sizeof(query_buffer) - 1,
939 "ep=%s", client.ep_name);
940 ret = coap_packet_append_option(
941 &msg->cpkt, COAP_OPTION_URI_QUERY,
942 query_buffer, strlen(query_buffer));
943 if (ret < 0) {
944 goto cleanup;
945 }
946 }
947
948 /* Send lifetime only if changed or on initial registration.*/
949 if (sm_update_lifetime(client.ctx->srv_obj_inst, &client.lifetime) ||
950 !sm_is_registered()) {
951 snprintk(query_buffer, sizeof(query_buffer) - 1,
952 "lt=%d", client.lifetime);
953 ret = coap_packet_append_option(
954 &msg->cpkt, COAP_OPTION_URI_QUERY,
955 query_buffer, strlen(query_buffer));
956 if (ret < 0) {
957 goto cleanup;
958 }
959 }
960
961 lwm2m_engine_get_binding(binding);
962 lwm2m_engine_get_queue_mode(queue);
963 /* UDP is a default binding, no need to add option if UDP without queue is used. */
964 if ((!sm_is_registered() && (strcmp(binding, "U") != 0 || strcmp(queue, "Q") == 0))) {
965 snprintk(query_buffer, sizeof(query_buffer) - 1,
966 "b=%s", binding);
967
968 ret = coap_packet_append_option(
969 &msg->cpkt, COAP_OPTION_URI_QUERY,
970 query_buffer, strlen(query_buffer));
971 if (ret < 0) {
972 goto cleanup;
973 }
974
975 #if CONFIG_LWM2M_VERSION_1_1
976 /* In LwM2M 1.1, queue mode is a separate parameter */
977 uint16_t len = strlen(queue);
978
979 if (len) {
980 ret = coap_packet_append_option(
981 &msg->cpkt, COAP_OPTION_URI_QUERY,
982 queue, len);
983 if (ret < 0) {
984 goto cleanup;
985 }
986 }
987 #endif
988 }
989
990 if (send_obj_support_data) {
991 ret = coap_packet_append_payload_marker(&msg->cpkt);
992 if (ret < 0) {
993 goto cleanup;
994 }
995
996 msg->out.out_cpkt = &msg->cpkt;
997 msg->out.writer = &link_format_writer;
998
999 ret = do_register_op_link_format(msg);
1000 if (ret < 0) {
1001 goto cleanup;
1002 }
1003 }
1004
1005 lwm2m_send_message_async(msg);
1006
1007 /* log the registration attempt */
1008 LOG_DBG("registration sent [%s]",
1009 lwm2m_sprint_ip_addr(&client.ctx->remote_addr));
1010
1011 return 0;
1012
1013 cleanup:
1014 LOG_ERR("error %d when sending registration message", ret);
1015 lwm2m_reset_message(msg, true);
1016 return ret;
1017 }
1018
sm_handle_registration_update_failure(void)1019 static void sm_handle_registration_update_failure(void)
1020 {
1021 k_mutex_lock(&client.mutex, K_FOREVER);
1022 LOG_WRN("Registration Update fail -> trigger full registration");
1023 lwm2m_engine_context_close(client.ctx);
1024 set_sm_state(ENGINE_SEND_REGISTRATION);
1025 k_mutex_unlock(&client.mutex);
1026 }
1027
sm_send_registration_msg(void)1028 static int sm_send_registration_msg(void)
1029 {
1030 int ret;
1031
1032 ret = sm_send_registration(true,
1033 do_registration_reply_cb,
1034 do_registration_timeout_cb);
1035 if (!ret) {
1036 set_sm_state(ENGINE_REGISTRATION_SENT);
1037 } else {
1038 LOG_ERR("Registration err: %d", ret);
1039 set_sm_state(ENGINE_NETWORK_ERROR);
1040 }
1041
1042 return ret;
1043 }
1044
sm_do_registration(void)1045 static int sm_do_registration(void)
1046 {
1047 int ret = 0;
1048
1049 if (client.ctx->connection_suspended) {
1050 if (lwm2m_engine_connection_resume(client.ctx)) {
1051 lwm2m_engine_context_close(client.ctx);
1052 /* perform full registration */
1053 set_sm_state(ENGINE_DO_REGISTRATION);
1054 return 0;
1055 }
1056
1057 } else {
1058 /* clear out existing connection data */
1059 if (client.ctx->sock_fd > -1) {
1060 if (client.close_socket) {
1061 /* Clear old socket connection */
1062 client.close_socket = false;
1063 lwm2m_engine_stop(client.ctx);
1064 } else {
1065 lwm2m_engine_context_close(client.ctx);
1066 }
1067 }
1068
1069 client.last_update = 0;
1070
1071 client.ctx->bootstrap_mode = false;
1072 ret = sm_select_security_inst(client.ctx->bootstrap_mode,
1073 &client.ctx->sec_obj_inst);
1074 if (ret < 0) {
1075 LOG_ERR("Unable to find a valid security instance.");
1076 set_sm_state(ENGINE_INIT);
1077 return -EINVAL;
1078 }
1079
1080 ret = sm_select_server_inst(client.ctx->sec_obj_inst,
1081 &client.ctx->srv_obj_inst,
1082 &client.lifetime);
1083 if (ret < 0) {
1084 LOG_ERR("Unable to find a valid server instance.");
1085 set_sm_state(ENGINE_INIT);
1086 return -EINVAL;
1087 }
1088
1089 LOG_INF("RD Client started with endpoint '%s' with client lifetime %d",
1090 client.ep_name, client.lifetime);
1091
1092 ret = lwm2m_engine_start(client.ctx);
1093 if (ret < 0) {
1094 LOG_ERR("Cannot init LWM2M engine (%d)", ret);
1095 set_sm_state(ENGINE_NETWORK_ERROR);
1096 return ret;
1097 }
1098 }
1099
1100 ret = sm_send_registration_msg();
1101
1102 return ret;
1103 }
1104
next_update(void)1105 static int64_t next_update(void)
1106 {
1107 /*
1108 * check for lifetime seconds - SECONDS_TO_UPDATE_EARLY
1109 * so that we can update early and avoid lifetime timeout
1110 */
1111 return client.last_update + (client.lifetime - SECONDS_TO_UPDATE_EARLY) * MSEC_PER_SEC;
1112 }
1113
next_rx_off(void)1114 static int64_t next_rx_off(void)
1115 {
1116 if (IS_ENABLED(CONFIG_LWM2M_QUEUE_MODE_ENABLED)) {
1117 return client.last_tx + CONFIG_LWM2M_QUEUE_MODE_UPTIME * MSEC_PER_SEC;
1118 } else {
1119 return next_update();
1120 }
1121 }
1122
1123 /** Return timestamp to next even whether it is RX_OFF or update event */
calc_next_event(void)1124 static int64_t calc_next_event(void)
1125 {
1126 return Z_MIN(next_update(), next_rx_off());
1127 }
1128
sm_registration_done(void)1129 static void sm_registration_done(void)
1130 {
1131 k_mutex_lock(&client.mutex, K_FOREVER);
1132
1133 int64_t now = k_uptime_get();
1134
1135 if (sm_is_registered() &&
1136 (client.trigger_update ||
1137 now >= next_update())) {
1138 set_sm_state(ENGINE_UPDATE_REGISTRATION);
1139 } else if (IS_ENABLED(CONFIG_LWM2M_QUEUE_MODE_ENABLED) &&
1140 (client.engine_state != ENGINE_REGISTRATION_DONE_RX_OFF) &&
1141 (now >= next_rx_off())) {
1142 set_sm_state(ENGINE_REGISTRATION_DONE_RX_OFF);
1143 next_event_at(next_update());
1144 } else {
1145 next_event_at(calc_next_event());
1146 }
1147 k_mutex_unlock(&client.mutex);
1148 }
1149
update_registration(void)1150 static int update_registration(void)
1151 {
1152 int ret;
1153 bool update_objects;
1154
1155 update_objects = client.update_objects;
1156 client.trigger_update = false;
1157 client.update_objects = false;
1158
1159 ret = lwm2m_engine_connection_resume(client.ctx);
1160 if (ret) {
1161 return ret;
1162 }
1163
1164 ret = sm_send_registration(update_objects,
1165 do_update_reply_cb,
1166 do_update_timeout_cb);
1167 if (ret) {
1168 LOG_ERR("Registration update err: %d", ret);
1169 return ret;
1170 }
1171
1172 return 0;
1173 }
1174
sm_update_registration(void)1175 static int sm_update_registration(void)
1176 {
1177 int ret;
1178
1179 ret = update_registration();
1180 if (ret) {
1181 LOG_ERR("Failed to update registration. Falling back to full registration");
1182
1183 lwm2m_engine_stop(client.ctx);
1184 /* perform full registration */
1185 set_sm_state(ENGINE_DO_REGISTRATION);
1186 return ret;
1187 }
1188
1189 set_sm_state(ENGINE_UPDATE_SENT);
1190
1191 return 0;
1192 }
1193
sm_do_deregister(void)1194 static int sm_do_deregister(void)
1195 {
1196 struct lwm2m_message *msg;
1197 int ret;
1198
1199 if (lwm2m_engine_connection_resume(client.ctx)) {
1200 lwm2m_engine_context_close(client.ctx);
1201 /* Connection failed, enter directly to deregistered state */
1202 set_sm_state(ENGINE_DEREGISTERED);
1203 return 0;
1204 }
1205
1206 msg = rd_get_message();
1207 if (!msg) {
1208 LOG_ERR("Unable to get a lwm2m message!");
1209 ret = -ENOMEM;
1210 goto close_ctx;
1211 }
1212
1213 msg->type = COAP_TYPE_CON;
1214 msg->code = COAP_METHOD_DELETE;
1215 msg->mid = coap_next_id();
1216 msg->tkl = LWM2M_MSG_TOKEN_GENERATE_NEW;
1217 msg->reply_cb = do_deregister_reply_cb;
1218 msg->message_timeout_cb = do_deregister_timeout_cb;
1219
1220 ret = lwm2m_init_message(msg);
1221 if (ret) {
1222 goto cleanup;
1223 }
1224
1225 ret = coap_packet_append_option(&msg->cpkt, COAP_OPTION_URI_PATH,
1226 LWM2M_RD_CLIENT_URI,
1227 strlen(LWM2M_RD_CLIENT_URI));
1228 if (ret < 0) {
1229 LOG_ERR("Failed to encode URI path option (err:%d).", ret);
1230 goto cleanup;
1231 }
1232
1233 /* include server endpoint in URI PATH */
1234 ret = coap_packet_append_option(&msg->cpkt, COAP_OPTION_URI_PATH,
1235 client.server_ep,
1236 strlen(client.server_ep));
1237 if (ret < 0) {
1238 LOG_ERR("Failed to encode URI path option (err:%d).", ret);
1239 goto cleanup;
1240 }
1241
1242 LOG_INF("Deregister from '%s'", client.server_ep);
1243
1244 lwm2m_send_message_async(msg);
1245
1246 set_sm_state(ENGINE_DEREGISTER_SENT);
1247 return 0;
1248
1249 cleanup:
1250 lwm2m_reset_message(msg, true);
1251 close_ctx:
1252 lwm2m_engine_stop(client.ctx);
1253 set_sm_state(ENGINE_DEREGISTERED);
1254 return ret;
1255 }
1256
sm_do_network_error(void)1257 static void sm_do_network_error(void)
1258 {
1259 int err;
1260
1261 if (client.retry_delay) {
1262 client.retry_delay = 0;
1263 next_event_at(k_uptime_get() + client.retry_delay * MSEC_PER_SEC);
1264 return;
1265 }
1266
1267 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
1268 if (client.ctx->bootstrap_mode) {
1269 lwm2m_engine_context_close(client.ctx);
1270 set_sm_state(ENGINE_DO_BOOTSTRAP_REG);
1271 return;
1272 }
1273 #endif
1274
1275 if (!client.last_update ||
1276 (k_uptime_get() - client.last_update) / MSEC_PER_SEC > client.lifetime) {
1277 /* do full registration as there is no active registration or lifetime exceeded */
1278 lwm2m_engine_context_close(client.ctx);
1279 set_sm_state(ENGINE_DO_REGISTRATION);
1280 return;
1281 }
1282
1283 err = lwm2m_socket_start(client.ctx);
1284 if (err) {
1285 LOG_ERR("Failed to start socket %d", err);
1286 /*
1287 * keep this state until lifetime/retry count exceeds. Renew
1288 * sm state to set retry_delay etc ...
1289 */
1290 set_sm_state(ENGINE_NETWORK_ERROR);
1291 return;
1292 }
1293
1294 set_sm_state(ENGINE_UPDATE_REGISTRATION);
1295 }
1296
lwm2m_rd_client_service(struct k_work * work)1297 static void lwm2m_rd_client_service(struct k_work *work)
1298 {
1299 k_mutex_lock(&client.mutex, K_FOREVER);
1300
1301 int64_t timeout = 0;
1302
1303 if (client.ctx) {
1304 LOG_DBG("State: %d", get_sm_state());
1305 client.next_event = INT64_MAX;
1306 switch (get_sm_state()) {
1307 case ENGINE_IDLE:
1308 if (client.ctx->sock_fd > -1) {
1309 lwm2m_engine_stop(client.ctx);
1310 }
1311 rd_client_message_free();
1312 break;
1313
1314 case ENGINE_INIT:
1315 sm_do_init();
1316 break;
1317
1318 case ENGINE_SUSPENDED:
1319 break;
1320
1321 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
1322 case ENGINE_DO_BOOTSTRAP_REG:
1323 sm_do_bootstrap_reg();
1324 break;
1325
1326 case ENGINE_BOOTSTRAP_REG_SENT:
1327 /* wait for bootstrap registration done */
1328 timeout = EXCHANGE_LIFETIME;
1329 break;
1330
1331 case ENGINE_BOOTSTRAP_REG_DONE:
1332 /* wait for transfer done */
1333 timeout = EXCHANGE_LIFETIME;
1334 break;
1335
1336 case ENGINE_BOOTSTRAP_TRANS_DONE:
1337 sm_bootstrap_trans_done();
1338 break;
1339 #endif
1340
1341 case ENGINE_DO_REGISTRATION:
1342 sm_do_registration();
1343 break;
1344
1345 case ENGINE_SEND_REGISTRATION:
1346 sm_send_registration_msg();
1347 break;
1348
1349 case ENGINE_REGISTRATION_SENT:
1350 /* wait registration to be done or timeout */
1351 timeout = EXCHANGE_LIFETIME;
1352 break;
1353
1354 case ENGINE_REGISTRATION_DONE:
1355 case ENGINE_REGISTRATION_DONE_RX_OFF:
1356 sm_registration_done();
1357 break;
1358
1359 case ENGINE_UPDATE_REGISTRATION:
1360 sm_update_registration();
1361 break;
1362
1363 case ENGINE_UPDATE_SENT:
1364 /* wait update to be done or abort */
1365 timeout = EXCHANGE_LIFETIME;
1366 break;
1367
1368 case ENGINE_DEREGISTER:
1369 sm_do_deregister();
1370 break;
1371
1372 case ENGINE_DEREGISTER_SENT:
1373 /* wait for deregister to be done or reset */
1374 timeout = EXCHANGE_LIFETIME;
1375 break;
1376
1377 case ENGINE_DEREGISTERED:
1378 lwm2m_engine_stop(client.ctx);
1379 set_sm_state(ENGINE_IDLE);
1380 break;
1381
1382 case ENGINE_NETWORK_ERROR:
1383 sm_do_network_error();
1384 break;
1385
1386 default:
1387 LOG_ERR("Unhandled state: %d", get_sm_state());
1388
1389 }
1390
1391 if (timeout) {
1392 int64_t end = client.last_state_change + timeout * MSEC_PER_SEC;
1393
1394 if (end < k_uptime_get()) {
1395 LOG_DBG("State machine have timed out");
1396 sm_handle_timeout_state(NULL, ENGINE_INIT);
1397 } else if (client.next_event > end) {
1398 next_event_at(end);
1399 }
1400 }
1401 }
1402
1403 k_mutex_unlock(&client.mutex);
1404 }
1405
lwm2m_rd_client_start(struct lwm2m_ctx * client_ctx,const char * ep_name,uint32_t flags,lwm2m_ctx_event_cb_t event_cb,lwm2m_observe_cb_t observe_cb)1406 int lwm2m_rd_client_start(struct lwm2m_ctx *client_ctx, const char *ep_name,
1407 uint32_t flags, lwm2m_ctx_event_cb_t event_cb,
1408 lwm2m_observe_cb_t observe_cb)
1409 {
1410 k_mutex_lock(&client.mutex, K_FOREVER);
1411
1412 if (!IS_ENABLED(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP) &&
1413 (flags & LWM2M_RD_CLIENT_FLAG_BOOTSTRAP)) {
1414 LOG_ERR("Bootstrap support is disabled. Please enable "
1415 "CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP.");
1416
1417 k_mutex_unlock(&client.mutex);
1418 return -ENOTSUP;
1419 }
1420
1421 /* Check client idle state or socket is still active */
1422
1423 if (client.ctx && (client.engine_state != ENGINE_IDLE || client.ctx->sock_fd != -1)) {
1424 LOG_WRN("Client is already running. state %d ", client.engine_state);
1425 k_mutex_unlock(&client.mutex);
1426 return -EINPROGRESS;
1427 }
1428
1429 /* Init Context */
1430 lwm2m_engine_context_init(client_ctx);
1431
1432 client.ctx = client_ctx;
1433 client.ctx->sock_fd = -1;
1434 client.ctx->fault_cb = socket_fault_cb;
1435 client.ctx->observe_cb = observe_cb;
1436 client.ctx->event_cb = event_cb;
1437 client.use_bootstrap = flags & LWM2M_RD_CLIENT_FLAG_BOOTSTRAP;
1438
1439 set_sm_state(ENGINE_INIT);
1440 strncpy(client.ep_name, ep_name, CLIENT_EP_LEN - 1);
1441 client.ep_name[CLIENT_EP_LEN - 1] = '\0';
1442 LOG_INF("Start LWM2M Client: %s", client.ep_name);
1443
1444 next_event_at(0);
1445
1446 k_mutex_unlock(&client.mutex);
1447
1448 return 0;
1449 }
1450
lwm2m_rd_client_stop(struct lwm2m_ctx * client_ctx,lwm2m_ctx_event_cb_t event_cb,bool deregister)1451 int lwm2m_rd_client_stop(struct lwm2m_ctx *client_ctx,
1452 lwm2m_ctx_event_cb_t event_cb, bool deregister)
1453 {
1454 k_mutex_lock(&client.mutex, K_FOREVER);
1455
1456 if (client.ctx != client_ctx) {
1457 k_mutex_unlock(&client.mutex);
1458 LOG_WRN("Cannot stop. Wrong context");
1459 return -EPERM;
1460 }
1461
1462 client.ctx->event_cb = event_cb;
1463 rd_client_message_free();
1464
1465 if (sm_is_registered() && deregister) {
1466 set_sm_state(ENGINE_DEREGISTER);
1467 } else {
1468 set_sm_state(ENGINE_DEREGISTERED);
1469 }
1470
1471 LOG_INF("Stop LWM2M Client: %s", client.ep_name);
1472
1473 k_mutex_unlock(&client.mutex);
1474
1475
1476 return 0;
1477 }
1478
lwm2m_rd_client_pause(void)1479 int lwm2m_rd_client_pause(void)
1480 {
1481 enum lwm2m_rd_client_event event = LWM2M_RD_CLIENT_EVENT_ENGINE_SUSPENDED;
1482 LOG_DBG("lwm2m_rd_client_pause()");
1483
1484 k_mutex_lock(&client.mutex, K_FOREVER);
1485
1486 if (!client.ctx) {
1487 k_mutex_unlock(&client.mutex);
1488 LOG_ERR("Cannot pause. No context");
1489 return -EPERM;
1490 } else if (sm_is_suspended()) {
1491 k_mutex_unlock(&client.mutex);
1492 LOG_ERR("LwM2M client already suspended");
1493 return 0;
1494 }
1495
1496 LOG_INF("Suspend client");
1497 if (client.ctx->event_cb) {
1498 client.ctx->event_cb(client.ctx, event);
1499 }
1500
1501 /* Suspend or close the socket */
1502 if (IS_ENABLED(CONFIG_LWM2M_RD_CLIENT_CLOSE_SOCKET_AT_IDLE)) {
1503 lwm2m_close_socket(client.ctx);
1504 } else {
1505 lwm2m_socket_suspend(client.ctx);
1506 }
1507
1508 suspended_client_state = get_sm_state();
1509 set_sm_state(ENGINE_SUSPENDED);
1510
1511 k_mutex_unlock(&client.mutex);
1512
1513 return 0;
1514 }
1515
lwm2m_rd_client_resume(void)1516 int lwm2m_rd_client_resume(void)
1517 {
1518 k_mutex_lock(&client.mutex, K_FOREVER);
1519
1520 if (!client.ctx || !lwm2m_rd_client_is_suspended(client.ctx)) {
1521 k_mutex_unlock(&client.mutex);
1522 LOG_WRN("Cannot resume, state is not suspended");
1523 return -EPERM;
1524 }
1525
1526 LOG_INF("Resume Client state");
1527
1528 if (suspended_client_state == ENGINE_UPDATE_SENT) {
1529 /* Set back to Registration done and trigger an update */
1530 suspended_client_state = ENGINE_REGISTRATION_DONE;
1531 }
1532 /* Clear Possible pending RD Client message */
1533 rd_client_message_free();
1534
1535 client.engine_state = suspended_client_state;
1536
1537 /* Do we need to resume the bootstrap? */
1538 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
1539 if (sm_is_bootstrap()) {
1540 client.engine_state = ENGINE_DO_BOOTSTRAP_REG;
1541 }
1542 #endif
1543 /* Or do we resume into registration state */
1544 if (client.engine_state >= ENGINE_DO_REGISTRATION &&
1545 client.engine_state <= ENGINE_SUSPENDED) {
1546 if (!client.last_update ||
1547 (client.lifetime <= (k_uptime_get() - client.last_update) / MSEC_PER_SEC)) {
1548 /* No lifetime left, register again */
1549 client.engine_state = ENGINE_DO_REGISTRATION;
1550 } else {
1551 /* Resume similarly like from QUEUE mode */
1552 client.engine_state = ENGINE_REGISTRATION_DONE_RX_OFF;
1553 lwm2m_rd_client_connection_resume(client.ctx);
1554 }
1555 }
1556
1557 next_event_at(0);
1558 k_mutex_unlock(&client.mutex);
1559
1560 return 0;
1561 }
1562
lwm2m_rd_client_update(void)1563 void lwm2m_rd_client_update(void)
1564 {
1565 engine_trigger_update(false);
1566 }
1567
lwm2m_rd_client_ctx(void)1568 struct lwm2m_ctx *lwm2m_rd_client_ctx(void)
1569 {
1570 return client.ctx;
1571 }
1572
lwm2m_rd_client_connection_resume(struct lwm2m_ctx * client_ctx)1573 int lwm2m_rd_client_connection_resume(struct lwm2m_ctx *client_ctx)
1574 {
1575 if (client.ctx != client_ctx) {
1576 return -EPERM;
1577 }
1578
1579 if (client.engine_state == ENGINE_REGISTRATION_DONE_RX_OFF) {
1580 /*
1581 * Switch state to triggering a proper registration message
1582 * If the socket stays open (Connection ID or no-sec), or we have TLS session cache,
1583 * we can trigger the update, otherwise fall back to full registration.
1584 */
1585 if ((IS_ENABLED(CONFIG_LWM2M_RD_CLIENT_SUSPEND_SOCKET_AT_IDLE) &&
1586 IS_ENABLED(CONFIG_LWM2M_TLS_SESSION_CACHING)) ||
1587 (IS_ENABLED(CONFIG_LWM2M_RD_CLIENT_STOP_POLLING_AT_IDLE) ||
1588 IS_ENABLED(CONFIG_LWM2M_RD_CLIENT_LISTEN_AT_IDLE)) ||
1589 !IS_ENABLED(CONFIG_LWM2M_DTLS_SUPPORT)) {
1590 client.engine_state = ENGINE_REGISTRATION_DONE;
1591 client.trigger_update = true;
1592 } else {
1593 client.engine_state = ENGINE_DO_REGISTRATION;
1594 }
1595 next_event_at(0);
1596 }
1597
1598 return 0;
1599 }
1600
lwm2m_rd_client_timeout(struct lwm2m_ctx * client_ctx)1601 int lwm2m_rd_client_timeout(struct lwm2m_ctx *client_ctx)
1602 {
1603 if (client.ctx != client_ctx) {
1604 return -EPERM;
1605 }
1606
1607 if (!sm_is_registered()) {
1608 return 0;
1609 }
1610 k_mutex_lock(&client.mutex, K_FOREVER);
1611 LOG_WRN("Confirmable Timeout -> Re-connect and register");
1612 set_sm_state(ENGINE_DO_REGISTRATION);
1613 next_event_at(0);
1614 k_mutex_unlock(&client.mutex);
1615 return 0;
1616 }
1617
lwm2m_rd_client_is_registred(struct lwm2m_ctx * client_ctx)1618 bool lwm2m_rd_client_is_registred(struct lwm2m_ctx *client_ctx)
1619 {
1620 if (client.ctx != client_ctx || !sm_is_registered()) {
1621 return false;
1622 }
1623
1624 return true;
1625 }
lwm2m_rd_client_is_suspended(struct lwm2m_ctx * client_ctx)1626 bool lwm2m_rd_client_is_suspended(struct lwm2m_ctx *client_ctx)
1627 {
1628 if (client.ctx != client_ctx || !sm_is_suspended()) {
1629 return false;
1630 }
1631
1632 return true;
1633 }
1634
1635
lwm2m_rd_client_init(void)1636 int lwm2m_rd_client_init(void)
1637 {
1638 client.ctx = NULL;
1639 client.rd_message.ctx = NULL;
1640 client.engine_state = ENGINE_IDLE;
1641 k_mutex_init(&client.mutex);
1642
1643 return 0;
1644 }
1645
sys_lwm2m_rd_client_init(void)1646 static int sys_lwm2m_rd_client_init(void)
1647 {
1648 return lwm2m_rd_client_init();
1649 }
1650
1651
1652 SYS_INIT(sys_lwm2m_rd_client_init, APPLICATION,
1653 CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
1654