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