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