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