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 ret = lwm2m_send_message_async(msg);
789 if (ret < 0) {
790 LOG_ERR("Failed to send bootstrap message (err: %d)", ret);
791 goto cleanup;
792 }
793
794 return 0;
795
796 cleanup:
797 lwm2m_reset_message(msg, true);
798 return ret;
799 }
800
sm_do_bootstrap_reg(void)801 static void sm_do_bootstrap_reg(void)
802 {
803 int ret;
804
805 /* clear out existing connection data */
806 if (client.ctx->sock_fd > -1) {
807 lwm2m_engine_stop(client.ctx);
808 }
809
810 client.ctx->bootstrap_mode = true;
811 ret = sm_next_bootstrap_inst(&client.ctx->sec_obj_inst);
812 if (ret < 0) {
813 set_sm_state(ENGINE_NETWORK_ERROR);
814 return;
815 }
816
817 LOG_INF("Bootstrap started with endpoint '%s' using security object %d",
818 client.ep_name, client.ctx->sec_obj_inst);
819
820 ret = lwm2m_engine_start(client.ctx);
821 if (ret < 0) {
822 LOG_ERR("Cannot init LWM2M engine (%d)", ret);
823 set_sm_state(ENGINE_NETWORK_ERROR);
824 return;
825 }
826
827 ret = sm_send_bootstrap_registration();
828 if (!ret) {
829 set_sm_state(ENGINE_BOOTSTRAP_REG_SENT);
830 } else {
831 LOG_ERR("Bootstrap registration err: %d", ret);
832 set_sm_state(ENGINE_NETWORK_ERROR);
833 }
834
835 return;
836 }
837
engine_bootstrap_finish(void)838 void engine_bootstrap_finish(void)
839 {
840 LOG_INF("Bootstrap data transfer done!");
841 /* Transition only if the client is bootstrapping, otherwise retransmissions of bootstrap
842 * finish may restart an already registered client.
843 * Delay the state transition, so engine have some time to send ACK before we close the
844 * socket.
845 */
846 if (get_sm_state() == ENGINE_BOOTSTRAP_REG_DONE) {
847 set_sm_state_delayed(ENGINE_BOOTSTRAP_TRANS_DONE, DELAY_BEFORE_CLOSING);
848 }
849 }
850
sm_bootstrap_trans_done(void)851 static int sm_bootstrap_trans_done(void)
852 {
853 /* close down context resources */
854 lwm2m_engine_stop(client.ctx);
855
856 /* reset security object instance */
857 client.ctx->sec_obj_inst = -1;
858 client.use_bootstrap = false;
859
860 /* reset server timestamps */
861 lwm2m_server_reset_timestamps();
862
863 set_sm_state(ENGINE_DO_REGISTRATION);
864
865 return 0;
866 }
867 #endif
868
sm_send_registration(bool send_obj_support_data,coap_reply_t reply_cb,lwm2m_message_timeout_cb_t timeout_cb)869 static int sm_send_registration(bool send_obj_support_data,
870 coap_reply_t reply_cb,
871 lwm2m_message_timeout_cb_t timeout_cb)
872 {
873 struct lwm2m_message *msg;
874 int ret;
875 char binding[CLIENT_BINDING_LEN];
876 char queue[CLIENT_QUEUE_LEN];
877
878 msg = rd_get_message();
879 if (!msg) {
880 LOG_ERR("Unable to get a lwm2m message!");
881 return -ENOMEM;
882 }
883
884 msg->type = COAP_TYPE_CON;
885 msg->code = COAP_METHOD_POST;
886 msg->mid = coap_next_id();
887 msg->tkl = LWM2M_MSG_TOKEN_GENERATE_NEW;
888 msg->reply_cb = reply_cb;
889 msg->message_timeout_cb = timeout_cb;
890
891 ret = lwm2m_init_message(msg);
892 if (ret) {
893 goto cleanup;
894 }
895
896 ret = coap_packet_append_option(&msg->cpkt, COAP_OPTION_URI_PATH,
897 LWM2M_RD_CLIENT_URI,
898 strlen(LWM2M_RD_CLIENT_URI));
899 if (ret < 0) {
900 goto cleanup;
901 }
902
903 if (sm_is_registered()) {
904 ret = coap_packet_append_option(
905 &msg->cpkt, COAP_OPTION_URI_PATH,
906 client.server_ep, strlen(client.server_ep));
907 if (ret < 0) {
908 goto cleanup;
909 }
910 }
911
912 if (send_obj_support_data) {
913 ret = coap_append_option_int(
914 &msg->cpkt, COAP_OPTION_CONTENT_FORMAT,
915 LWM2M_FORMAT_APP_LINK_FORMAT);
916 if (ret < 0) {
917 goto cleanup;
918 }
919 }
920
921 if (!sm_is_registered()) {
922 snprintk(query_buffer, sizeof(query_buffer) - 1,
923 "lwm2m=%s", LWM2M_PROTOCOL_VERSION_STRING);
924 ret = coap_packet_append_option(
925 &msg->cpkt, COAP_OPTION_URI_QUERY,
926 query_buffer, strlen(query_buffer));
927 if (ret < 0) {
928 goto cleanup;
929 }
930
931 snprintk(query_buffer, sizeof(query_buffer) - 1,
932 "ep=%s", client.ep_name);
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
941 /* Send lifetime only if changed or on initial registration.*/
942 if (sm_update_lifetime(client.ctx->srv_obj_inst, &client.lifetime) ||
943 !sm_is_registered()) {
944 snprintk(query_buffer, sizeof(query_buffer) - 1,
945 "lt=%d", client.lifetime);
946 ret = coap_packet_append_option(
947 &msg->cpkt, COAP_OPTION_URI_QUERY,
948 query_buffer, strlen(query_buffer));
949 if (ret < 0) {
950 goto cleanup;
951 }
952 }
953
954 lwm2m_engine_get_binding(binding);
955 lwm2m_engine_get_queue_mode(queue);
956 /* UDP is a default binding, no need to add option if UDP without queue is used. */
957 if ((!sm_is_registered() && (strcmp(binding, "U") != 0 || strcmp(queue, "Q") == 0))) {
958 snprintk(query_buffer, sizeof(query_buffer) - 1,
959 "b=%s", binding);
960
961 ret = coap_packet_append_option(
962 &msg->cpkt, COAP_OPTION_URI_QUERY,
963 query_buffer, strlen(query_buffer));
964 if (ret < 0) {
965 goto cleanup;
966 }
967
968 #if CONFIG_LWM2M_VERSION_1_1
969 /* In LwM2M 1.1, queue mode is a separate parameter */
970 uint16_t len = strlen(queue);
971
972 if (len) {
973 ret = coap_packet_append_option(
974 &msg->cpkt, COAP_OPTION_URI_QUERY,
975 queue, len);
976 if (ret < 0) {
977 goto cleanup;
978 }
979 }
980 #endif
981 }
982
983 if (send_obj_support_data) {
984 ret = coap_packet_append_payload_marker(&msg->cpkt);
985 if (ret < 0) {
986 goto cleanup;
987 }
988
989 msg->out.out_cpkt = &msg->cpkt;
990 msg->out.writer = &link_format_writer;
991
992 ret = do_register_op_link_format(msg);
993 if (ret < 0) {
994 goto cleanup;
995 }
996 }
997
998 ret = lwm2m_send_message_async(msg);
999 if (ret < 0) {
1000 goto cleanup;
1001 }
1002
1003 /* log the registration attempt */
1004 LOG_DBG("registration sent [%s]",
1005 lwm2m_sprint_ip_addr(&client.ctx->remote_addr));
1006
1007 return 0;
1008
1009 cleanup:
1010 LOG_ERR("error %d when sending registration message", ret);
1011 lwm2m_reset_message(msg, true);
1012 return ret;
1013 }
1014
sm_handle_registration_update_failure(void)1015 static void sm_handle_registration_update_failure(void)
1016 {
1017 k_mutex_lock(&client.mutex, K_FOREVER);
1018 LOG_WRN("Registration Update fail -> trigger full registration");
1019 lwm2m_engine_context_close(client.ctx);
1020 set_sm_state(ENGINE_SEND_REGISTRATION);
1021 k_mutex_unlock(&client.mutex);
1022 }
1023
sm_send_registration_msg(void)1024 static int sm_send_registration_msg(void)
1025 {
1026 int ret;
1027
1028 ret = sm_send_registration(true,
1029 do_registration_reply_cb,
1030 do_registration_timeout_cb);
1031 if (!ret) {
1032 set_sm_state(ENGINE_REGISTRATION_SENT);
1033 } else {
1034 LOG_ERR("Registration err: %d", ret);
1035 set_sm_state(ENGINE_NETWORK_ERROR);
1036 }
1037
1038 return ret;
1039 }
1040
sm_do_registration(void)1041 static void sm_do_registration(void)
1042 {
1043 uint16_t ssid;
1044 int ret = 0;
1045
1046 if (client.ctx->connection_suspended) {
1047 if (lwm2m_engine_connection_resume(client.ctx)) {
1048 lwm2m_engine_context_close(client.ctx);
1049 /* perform full registration */
1050 set_sm_state(ENGINE_DO_REGISTRATION);
1051 return;
1052 }
1053
1054 } else {
1055 bool select_srv = true;
1056 uint16_t srv = (uint16_t) client.ctx->srv_obj_inst;
1057
1058 client.last_update = 0;
1059 client.ctx->bootstrap_mode = false;
1060
1061 /* clear out existing connection data */
1062 if (client.ctx->sock_fd > -1) {
1063 if (client.close_socket) {
1064 /* Clear old socket connection */
1065 client.close_socket = false;
1066 lwm2m_engine_stop(client.ctx);
1067 } else {
1068 lwm2m_engine_context_close(client.ctx);
1069 /* Keep current connection, retry registration with same server */
1070 select_srv = false;
1071 }
1072 }
1073
1074 if (select_srv) {
1075 /* Select next one from the list, or fail */
1076 if (!lwm2m_server_select(&srv)) {
1077 LOG_ERR("Unable to find a valid server instance.");
1078 goto bootstrap_or_retry;
1079 }
1080
1081 client.ctx->srv_obj_inst = srv;
1082 sm_update_lifetime(srv, &client.lifetime);
1083
1084 ret = lwm2m_get_u16(&LWM2M_OBJ(1, client.ctx->srv_obj_inst, 0), &ssid);
1085 if (ret < 0) {
1086 LOG_ERR("Failed to read SSID");
1087 lwm2m_server_disable(srv, K_FOREVER);
1088 goto bootstrap_or_retry;
1089 }
1090
1091 ret = lwm2m_security_short_id_to_inst(ssid);
1092 if (ret < 0) {
1093 LOG_ERR("Unable to find a valid security instance.");
1094 lwm2m_server_disable(srv, K_FOREVER);
1095 goto bootstrap_or_retry;
1096 }
1097 client.ctx->sec_obj_inst = (uint16_t) ret;
1098 }
1099
1100 LOG_INF("RD Client started with endpoint '%s' with client lifetime %d using server "
1101 "object %d",
1102 client.ep_name, client.lifetime, client.ctx->srv_obj_inst);
1103
1104 ret = lwm2m_engine_start(client.ctx);
1105 if (ret < 0) {
1106 LOG_ERR("Cannot init LWM2M engine (%d)", ret);
1107 goto retry;
1108 }
1109 }
1110
1111 sm_send_registration_msg();
1112 return;
1113
1114 bootstrap_or_retry:
1115 if (!client.server_disabled && fallback_to_bootstrap()) {
1116 lwm2m_engine_stop(client.ctx);
1117 return;
1118 }
1119 retry:
1120 lwm2m_engine_stop(client.ctx);
1121 set_sm_state(ENGINE_NETWORK_ERROR);
1122 }
1123
next_update(void)1124 static int64_t next_update(void)
1125 {
1126 int64_t next;
1127 int64_t period = CONFIG_LWM2M_UPDATE_PERIOD;
1128 int64_t early = CONFIG_LWM2M_SECONDS_TO_UPDATE_EARLY;
1129
1130 if (period == 0) {
1131 period = client.lifetime;
1132 }
1133 if (early > client.lifetime) {
1134 early = client.lifetime;
1135 }
1136
1137 next = MIN(period, client.lifetime - early);
1138 next = MAX(next, MINIMUM_PERIOD);
1139
1140 return client.last_update + next * MSEC_PER_SEC;
1141 }
1142
next_rx_off(void)1143 static int64_t next_rx_off(void)
1144 {
1145 if (IS_ENABLED(CONFIG_LWM2M_QUEUE_MODE_ENABLED)) {
1146 return client.last_tx + CONFIG_LWM2M_QUEUE_MODE_UPTIME * MSEC_PER_SEC;
1147 } else {
1148 return next_update();
1149 }
1150 }
1151
1152 /** Return timestamp to next even whether it is RX_OFF or update event */
calc_next_event(void)1153 static int64_t calc_next_event(void)
1154 {
1155 return Z_MIN(next_update(), next_rx_off());
1156 }
1157
sm_registration_done(void)1158 static void sm_registration_done(void)
1159 {
1160 k_mutex_lock(&client.mutex, K_FOREVER);
1161
1162 int64_t now = k_uptime_get();
1163
1164 if (sm_is_registered() &&
1165 (client.trigger_update ||
1166 now >= next_update())) {
1167 set_sm_state_delayed(ENGINE_UPDATE_REGISTRATION, DELAY_FOR_ACK);
1168 } else if (IS_ENABLED(CONFIG_LWM2M_QUEUE_MODE_ENABLED) &&
1169 (client.engine_state != ENGINE_REGISTRATION_DONE_RX_OFF) &&
1170 (now >= next_rx_off())) {
1171 set_sm_state(ENGINE_REGISTRATION_DONE_RX_OFF);
1172 next_event_at(next_update());
1173 } else {
1174 next_event_at(calc_next_event());
1175 }
1176 k_mutex_unlock(&client.mutex);
1177 }
1178
update_registration(void)1179 static int update_registration(void)
1180 {
1181 int ret;
1182 bool update_objects;
1183
1184 update_objects = client.update_objects;
1185 client.trigger_update = false;
1186 client.update_objects = false;
1187
1188 ret = lwm2m_engine_connection_resume(client.ctx);
1189 if (ret) {
1190 return ret;
1191 }
1192
1193 ret = sm_send_registration(update_objects,
1194 do_update_reply_cb,
1195 do_update_timeout_cb);
1196 if (ret) {
1197 LOG_ERR("Registration update err: %d", ret);
1198 return ret;
1199 }
1200
1201 return 0;
1202 }
1203
sm_update_registration(void)1204 static int sm_update_registration(void)
1205 {
1206 int ret;
1207
1208 ret = update_registration();
1209 if (ret) {
1210 LOG_ERR("Failed to update registration. Falling back to full registration");
1211
1212 lwm2m_engine_stop(client.ctx);
1213 /* perform full registration */
1214 set_sm_state(ENGINE_DO_REGISTRATION);
1215 return ret;
1216 }
1217
1218 set_sm_state(ENGINE_UPDATE_SENT);
1219
1220 return 0;
1221 }
1222
sm_do_deregister(void)1223 static int sm_do_deregister(void)
1224 {
1225 struct lwm2m_message *msg;
1226 int ret;
1227
1228 if (lwm2m_engine_connection_resume(client.ctx)) {
1229 lwm2m_engine_context_close(client.ctx);
1230 /* Connection failed, enter directly to deregistered state */
1231 set_sm_state(ENGINE_DEREGISTERED);
1232 return 0;
1233 }
1234
1235 msg = rd_get_message();
1236 if (!msg) {
1237 LOG_ERR("Unable to get a lwm2m message!");
1238 ret = -ENOMEM;
1239 goto close_ctx;
1240 }
1241
1242 msg->type = COAP_TYPE_CON;
1243 msg->code = COAP_METHOD_DELETE;
1244 msg->mid = coap_next_id();
1245 msg->tkl = LWM2M_MSG_TOKEN_GENERATE_NEW;
1246 msg->reply_cb = do_deregister_reply_cb;
1247 msg->message_timeout_cb = do_deregister_timeout_cb;
1248
1249 ret = lwm2m_init_message(msg);
1250 if (ret) {
1251 goto cleanup;
1252 }
1253
1254 ret = coap_packet_append_option(&msg->cpkt, COAP_OPTION_URI_PATH,
1255 LWM2M_RD_CLIENT_URI,
1256 strlen(LWM2M_RD_CLIENT_URI));
1257 if (ret < 0) {
1258 LOG_ERR("Failed to encode URI path option (err:%d).", ret);
1259 goto cleanup;
1260 }
1261
1262 /* include server endpoint in URI PATH */
1263 ret = coap_packet_append_option(&msg->cpkt, COAP_OPTION_URI_PATH,
1264 client.server_ep,
1265 strlen(client.server_ep));
1266 if (ret < 0) {
1267 LOG_ERR("Failed to encode URI path option (err:%d).", ret);
1268 goto cleanup;
1269 }
1270
1271 LOG_INF("Deregister from '%s'", client.server_ep);
1272
1273 ret = lwm2m_send_message_async(msg);
1274 if (ret < 0) {
1275 LOG_ERR("Failed to send deregistration message (err:%d).", ret);
1276 goto cleanup;
1277 }
1278
1279 set_sm_state(ENGINE_DEREGISTER_SENT);
1280 return 0;
1281
1282 cleanup:
1283 lwm2m_reset_message(msg, true);
1284 close_ctx:
1285 lwm2m_engine_stop(client.ctx);
1286 set_sm_state(ENGINE_DEREGISTERED);
1287 return ret;
1288 }
1289
fallback_to_bootstrap(void)1290 static bool fallback_to_bootstrap(void)
1291 {
1292 if (IS_ENABLED(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)) {
1293 bool fallback = true;
1294
1295 (void)lwm2m_get_bool(&LWM2M_OBJ(LWM2M_OBJECT_SERVER_ID, client.ctx->srv_obj_inst,
1296 SERVER_BOOTSTRAP_ON_REGISTRATION_FAILURE_ID),
1297 &fallback);
1298 if (fallback) {
1299 client.use_bootstrap = true;
1300 set_sm_state(ENGINE_INIT);
1301 return true;
1302 }
1303 }
1304 return false;
1305 }
1306
sm_do_network_error(void)1307 static void sm_do_network_error(void)
1308 {
1309 int err;
1310
1311 LOG_ERR("sm_do_network_error, retries %d", client.retries);
1312
1313 lwm2m_socket_close(client.ctx);
1314
1315 if (client.retry_delay) {
1316 next_event_at(k_uptime_get() + client.retry_delay * MSEC_PER_SEC);
1317 client.retry_delay = 0;
1318 return;
1319 }
1320 client.retry_delay = 1 << client.retries;
1321 client.retries++;
1322
1323 /* Stop retrying and try fallback */
1324 if (client.retries > CONFIG_LWM2M_RD_CLIENT_MAX_RETRIES) {
1325 LOG_ERR("Network error, max retries reached (%d)", client.retries);
1326
1327 /* Disable current server for a period so lwm2m_server_select() does not pick it */
1328 if (client.ctx->srv_obj_inst > -1) {
1329 lwm2m_server_disable(client.ctx->srv_obj_inst, DISABLE_TIMEOUT);
1330 }
1331
1332 /* Are we in bootstrap? Try if we can fallback to some other BS server */
1333 if (client.ctx->bootstrap_mode &&
1334 IS_ENABLED(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)) {
1335 LOG_DBG("In bootstrap, try fallback srv");
1336 /* Do we have any other bootstrap server to back off to? */
1337 if (sm_next_bootstrap_inst(&client.ctx->sec_obj_inst) < 0) {
1338 /* No, we are out of options, stop engine */
1339 goto stop_engine;
1340 }
1341 set_sm_state(ENGINE_INIT);
1342 return;
1343 }
1344
1345 /* Try if there are other server to fall back to,
1346 * Only allow fallback to higher priority server (lower value, or lower id)
1347 * if we have successfully registered before.
1348 * This should block us from looping the same list again.
1349 * Instead we should fallback to bootstrap.
1350 */
1351 uint16_t srv;
1352
1353 if (lwm2m_server_select(&srv)) {
1354 uint8_t p1, p2;
1355
1356 p1 = lwm2m_server_get_prio(client.ctx->srv_obj_inst);
1357 p2 = lwm2m_server_get_prio(srv);
1358 if (p1 < p2 || client.last_update != 0) {
1359 set_sm_state(ENGINE_INIT);
1360 return;
1361 }
1362 }
1363
1364 /* If we have been disabled by some server, don't fall back to bootstrap */
1365 if (client.server_disabled) {
1366 set_sm_state(ENGINE_SERVER_DISABLED);
1367 return;
1368 }
1369
1370 if (fallback_to_bootstrap()) {
1371 return;
1372 }
1373 goto stop_engine;
1374 }
1375
1376 /* Retry bootstrap */
1377 if (IS_ENABLED(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)) {
1378 if (client.ctx->bootstrap_mode) {
1379 lwm2m_engine_context_close(client.ctx);
1380 /* If we don't have fallback BS server, retry with current one */
1381 if (sm_next_bootstrap_inst(&client.ctx->sec_obj_inst) < 0) {
1382 client.ctx->sec_obj_inst = -1;
1383 }
1384 set_sm_state(ENGINE_DO_BOOTSTRAP_REG);
1385 return;
1386 }
1387 }
1388
1389 if (!client.last_update ||
1390 (k_uptime_get() - client.last_update) / MSEC_PER_SEC > client.lifetime) {
1391 /* do full registration as there is no active registration or lifetime exceeded */
1392 /* Keep the same server until out of retry */
1393 set_sm_state(ENGINE_DO_REGISTRATION);
1394 return;
1395 }
1396
1397 /* Try if we can recover the DTLS session and try Update.
1398 * This might fallback into full registration on sm_handle_registration_update_failure().
1399 */
1400 err = lwm2m_socket_start(client.ctx);
1401 if (err) {
1402 LOG_ERR("Failed to start socket %d", err);
1403 /*
1404 * keep this state until lifetime/retry count exceeds. Renew
1405 * sm state to set retry_delay etc ...
1406 */
1407 set_sm_state(ENGINE_NETWORK_ERROR);
1408 return;
1409 }
1410 set_sm_state(ENGINE_UPDATE_REGISTRATION);
1411 return;
1412
1413 stop_engine:
1414
1415 /* We are out of options, stop engine */
1416 if (client.ctx->event_cb) {
1417 if (client.ctx->bootstrap_mode) {
1418 client.ctx->event_cb(client.ctx,
1419 LWM2M_RD_CLIENT_EVENT_BOOTSTRAP_REG_FAILURE);
1420 } else {
1421 client.ctx->event_cb(client.ctx, LWM2M_RD_CLIENT_EVENT_NETWORK_ERROR);
1422 }
1423 }
1424 set_sm_state(ENGINE_IDLE);
1425 }
1426
lwm2m_rd_client_service(struct k_work * work)1427 static void lwm2m_rd_client_service(struct k_work *work)
1428 {
1429 k_mutex_lock(&client.mutex, K_FOREVER);
1430
1431 int64_t timeout = 0;
1432
1433 if (client.ctx) {
1434 LOG_DBG("State: %d", get_sm_state());
1435 client.next_event = INT64_MAX;
1436 switch (get_sm_state()) {
1437 case ENGINE_IDLE:
1438 if (client.ctx->sock_fd > -1) {
1439 lwm2m_engine_stop(client.ctx);
1440 }
1441 rd_client_message_free();
1442 break;
1443
1444 case ENGINE_INIT:
1445 sm_do_init();
1446 break;
1447
1448 case ENGINE_SUSPENDED:
1449 break;
1450
1451 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
1452 case ENGINE_DO_BOOTSTRAP_REG:
1453 sm_do_bootstrap_reg();
1454 break;
1455
1456 case ENGINE_BOOTSTRAP_REG_SENT:
1457 /* wait for bootstrap registration done */
1458 timeout = EXCHANGE_LIFETIME;
1459 break;
1460
1461 case ENGINE_BOOTSTRAP_REG_DONE:
1462 /* wait for transfer done */
1463 timeout = EXCHANGE_LIFETIME;
1464 break;
1465
1466 case ENGINE_BOOTSTRAP_TRANS_DONE:
1467 sm_bootstrap_trans_done();
1468 break;
1469 #endif
1470
1471 case ENGINE_DO_REGISTRATION:
1472 sm_do_registration();
1473 break;
1474
1475 case ENGINE_SEND_REGISTRATION:
1476 sm_send_registration_msg();
1477 break;
1478
1479 case ENGINE_REGISTRATION_SENT:
1480 /* wait registration to be done or timeout */
1481 timeout = EXCHANGE_LIFETIME;
1482 break;
1483
1484 case ENGINE_REGISTRATION_DONE:
1485 case ENGINE_REGISTRATION_DONE_RX_OFF:
1486 sm_registration_done();
1487 break;
1488
1489 case ENGINE_UPDATE_REGISTRATION:
1490 sm_update_registration();
1491 break;
1492
1493 case ENGINE_UPDATE_SENT:
1494 /* wait update to be done or abort */
1495 timeout = EXCHANGE_LIFETIME;
1496 break;
1497
1498 case ENGINE_SERVER_DISABLED:
1499 if (lwm2m_server_select(NULL)) {
1500 set_sm_state(ENGINE_INIT);
1501 } else {
1502 /* wait for server to be enabled. */
1503 /*
1504 * TODO: Once engine is converted to use timepoint_t
1505 * this should calculate the next event from the previous server.
1506 */
1507 next_event_at(k_uptime_get() + SEC_PER_MIN * MSEC_PER_SEC);
1508 }
1509 break;
1510
1511 case ENGINE_DEREGISTER:
1512 sm_do_deregister();
1513 break;
1514
1515 case ENGINE_DEREGISTER_SENT:
1516 /* wait for deregister to be done or reset */
1517 timeout = EXCHANGE_LIFETIME;
1518 break;
1519
1520 case ENGINE_DEREGISTERED:
1521 lwm2m_engine_stop(client.ctx);
1522 if (client.server_disabled) {
1523 set_sm_state(ENGINE_SERVER_DISABLED);
1524 } else {
1525 set_sm_state(ENGINE_IDLE);
1526 }
1527 break;
1528
1529 case ENGINE_NETWORK_ERROR:
1530 sm_do_network_error();
1531 break;
1532
1533 default:
1534 LOG_ERR("Unhandled state: %d", get_sm_state());
1535
1536 }
1537
1538 if (timeout) {
1539 int64_t end = client.last_state_change + timeout * MSEC_PER_SEC;
1540
1541 if (end < k_uptime_get()) {
1542 LOG_DBG("State machine have timed out");
1543 sm_handle_timeout_state(ENGINE_INIT);
1544 } else if (client.next_event > end) {
1545 next_event_at(end);
1546 }
1547 }
1548 }
1549
1550 k_mutex_unlock(&client.mutex);
1551 }
1552
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)1553 int lwm2m_rd_client_start(struct lwm2m_ctx *client_ctx, const char *ep_name,
1554 uint32_t flags, lwm2m_ctx_event_cb_t event_cb,
1555 lwm2m_observe_cb_t observe_cb)
1556 {
1557 k_mutex_lock(&client.mutex, K_FOREVER);
1558
1559 if (!IS_ENABLED(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP) &&
1560 (flags & LWM2M_RD_CLIENT_FLAG_BOOTSTRAP)) {
1561 LOG_ERR("Bootstrap support is disabled. Please enable "
1562 "CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP.");
1563
1564 k_mutex_unlock(&client.mutex);
1565 return -ENOTSUP;
1566 }
1567
1568 /* Check client idle state or socket is still active */
1569
1570 if (client.ctx && (client.engine_state != ENGINE_IDLE || client.ctx->sock_fd != -1)) {
1571 LOG_WRN("Client is already running. state %d ", client.engine_state);
1572 k_mutex_unlock(&client.mutex);
1573 return -EINPROGRESS;
1574 }
1575
1576 /* Init Context */
1577 lwm2m_server_reset_timestamps();
1578 lwm2m_engine_context_init(client_ctx);
1579
1580 client.ctx = client_ctx;
1581 client.ctx->sock_fd = -1;
1582 client.ctx->fault_cb = socket_fault_cb;
1583 client.ctx->observe_cb = observe_cb;
1584 client.ctx->event_cb = event_cb;
1585 client.use_bootstrap = flags & LWM2M_RD_CLIENT_FLAG_BOOTSTRAP;
1586 client.ctx->srv_obj_inst = -1;
1587 client.ctx->sec_obj_inst = -1;
1588 client.retries = 0;
1589
1590 strncpy(client.ep_name, ep_name, CLIENT_EP_LEN - 1);
1591 client.ep_name[CLIENT_EP_LEN - 1] = '\0';
1592 LOG_INF("Start LWM2M Client: %s", client.ep_name);
1593
1594 set_sm_state(ENGINE_INIT);
1595
1596 k_mutex_unlock(&client.mutex);
1597
1598 return 0;
1599 }
1600
lwm2m_rd_client_stop(struct lwm2m_ctx * client_ctx,lwm2m_ctx_event_cb_t event_cb,bool deregister)1601 int lwm2m_rd_client_stop(struct lwm2m_ctx *client_ctx,
1602 lwm2m_ctx_event_cb_t event_cb, bool deregister)
1603 {
1604 k_mutex_lock(&client.mutex, K_FOREVER);
1605
1606 if (client.ctx != client_ctx) {
1607 k_mutex_unlock(&client.mutex);
1608 LOG_WRN("Cannot stop. Wrong context");
1609 return -EPERM;
1610 }
1611
1612 client.ctx->event_cb = event_cb;
1613 rd_client_message_free();
1614
1615 if (sm_is_registered() && deregister && !client.server_disabled) {
1616 set_sm_state(ENGINE_DEREGISTER);
1617 } else {
1618 client.server_disabled = false;
1619 set_sm_state(ENGINE_DEREGISTERED);
1620 }
1621
1622 LOG_INF("Stop LWM2M Client: %s", client.ep_name);
1623
1624 k_mutex_unlock(&client.mutex);
1625
1626
1627 return 0;
1628 }
1629
lwm2m_rd_client_pause(void)1630 int lwm2m_rd_client_pause(void)
1631 {
1632 enum lwm2m_rd_client_event event = LWM2M_RD_CLIENT_EVENT_ENGINE_SUSPENDED;
1633 LOG_DBG("lwm2m_rd_client_pause()");
1634
1635 k_mutex_lock(&client.mutex, K_FOREVER);
1636
1637 if (!client.ctx) {
1638 k_mutex_unlock(&client.mutex);
1639 LOG_ERR("Cannot pause. No context");
1640 return -EPERM;
1641 } else if (sm_is_suspended()) {
1642 k_mutex_unlock(&client.mutex);
1643 LOG_ERR("LwM2M client already suspended");
1644 return 0;
1645 }
1646
1647 LOG_INF("Suspend client");
1648 if (client.ctx->event_cb) {
1649 client.ctx->event_cb(client.ctx, event);
1650 }
1651
1652 /* Suspend or close the socket */
1653 if (IS_ENABLED(CONFIG_LWM2M_RD_CLIENT_CLOSE_SOCKET_AT_IDLE)) {
1654 lwm2m_close_socket(client.ctx);
1655 } else {
1656 lwm2m_socket_suspend(client.ctx);
1657 }
1658
1659 suspended_client_state = get_sm_state();
1660 set_sm_state(ENGINE_SUSPENDED);
1661
1662 k_mutex_unlock(&client.mutex);
1663
1664 return 0;
1665 }
1666
lwm2m_rd_client_resume(void)1667 int lwm2m_rd_client_resume(void)
1668 {
1669 k_mutex_lock(&client.mutex, K_FOREVER);
1670
1671 if (!client.ctx || !lwm2m_rd_client_is_suspended(client.ctx)) {
1672 k_mutex_unlock(&client.mutex);
1673 LOG_WRN("Cannot resume, state is not suspended");
1674 return -EPERM;
1675 }
1676
1677 LOG_INF("Resume Client state");
1678
1679 if (suspended_client_state == ENGINE_UPDATE_SENT) {
1680 /* Set back to Registration done and trigger an update */
1681 suspended_client_state = ENGINE_REGISTRATION_DONE;
1682 }
1683 /* Clear Possible pending RD Client message */
1684 rd_client_message_free();
1685
1686 client.engine_state = suspended_client_state;
1687
1688 /* Do we need to resume the bootstrap? */
1689 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
1690 if (sm_is_bootstrap()) {
1691 client.engine_state = ENGINE_DO_BOOTSTRAP_REG;
1692 }
1693 #endif
1694 /* Or do we resume into registration state */
1695 if (client.engine_state >= ENGINE_DO_REGISTRATION &&
1696 client.engine_state <= ENGINE_SERVER_DISABLED) {
1697 if (!client.last_update ||
1698 (client.lifetime <= (k_uptime_get() - client.last_update) / MSEC_PER_SEC)) {
1699 /* No lifetime left, register again */
1700 client.engine_state = ENGINE_DO_REGISTRATION;
1701 } else {
1702 /* Resume similarly like from QUEUE mode */
1703 client.engine_state = ENGINE_REGISTRATION_DONE_RX_OFF;
1704 lwm2m_rd_client_connection_resume(client.ctx);
1705 }
1706 }
1707
1708 next_event_at(0);
1709 k_mutex_unlock(&client.mutex);
1710
1711 return 0;
1712 }
1713
lwm2m_rd_client_server_disabled(uint16_t inst_id)1714 int lwm2m_rd_client_server_disabled(uint16_t inst_id)
1715 {
1716 if (client.ctx->srv_obj_inst != inst_id) {
1717 return -EPERM;
1718 }
1719
1720 k_mutex_lock(&client.mutex, K_FOREVER);
1721
1722 client.server_disabled = true;
1723
1724 if (sm_is_registered()) {
1725 LOG_INF("Server disabled, deregister");
1726 set_sm_state_delayed(ENGINE_DEREGISTER, DELAY_BEFORE_CLOSING);
1727 } else {
1728 LOG_INF("Server disabled");
1729 set_sm_state(ENGINE_DEREGISTERED);
1730 }
1731
1732 k_mutex_unlock(&client.mutex);
1733
1734 return 0;
1735 }
1736
lwm2m_rd_client_update(void)1737 void lwm2m_rd_client_update(void)
1738 {
1739 engine_trigger_update(false);
1740 }
1741
lwm2m_rd_client_ctx(void)1742 struct lwm2m_ctx *lwm2m_rd_client_ctx(void)
1743 {
1744 return client.ctx;
1745 }
1746
lwm2m_rd_client_set_ctx(struct lwm2m_ctx * ctx)1747 void lwm2m_rd_client_set_ctx(struct lwm2m_ctx *ctx)
1748 {
1749 client.ctx = ctx;
1750 }
1751
lwm2m_rd_client_connection_resume(struct lwm2m_ctx * client_ctx)1752 int lwm2m_rd_client_connection_resume(struct lwm2m_ctx *client_ctx)
1753 {
1754 if (client.ctx != client_ctx) {
1755 return -EPERM;
1756 }
1757
1758 if (client.engine_state == ENGINE_REGISTRATION_DONE_RX_OFF) {
1759 /*
1760 * Switch state to triggering a proper registration message
1761 * If the socket stays open (Connection ID or no-sec), or we have TLS session cache,
1762 * we can trigger the update, otherwise fall back to full registration.
1763 */
1764 if ((IS_ENABLED(CONFIG_LWM2M_RD_CLIENT_SUSPEND_SOCKET_AT_IDLE) &&
1765 IS_ENABLED(CONFIG_LWM2M_TLS_SESSION_CACHING)) ||
1766 (IS_ENABLED(CONFIG_LWM2M_RD_CLIENT_STOP_POLLING_AT_IDLE) ||
1767 IS_ENABLED(CONFIG_LWM2M_RD_CLIENT_LISTEN_AT_IDLE)) ||
1768 !IS_ENABLED(CONFIG_LWM2M_DTLS_SUPPORT)) {
1769 client.engine_state = ENGINE_REGISTRATION_DONE;
1770 if (IS_ENABLED(CONFIG_LWM2M_QUEUE_MODE_NO_MSG_BUFFERING)) {
1771 /* Force online for a short period */
1772 engine_update_tx_time();
1773 } else {
1774 client.trigger_update = true;
1775 }
1776 } else {
1777 client.engine_state = ENGINE_DO_REGISTRATION;
1778 }
1779 next_event_at(0);
1780 }
1781
1782 return 0;
1783 }
1784
lwm2m_rd_client_timeout(struct lwm2m_ctx * client_ctx)1785 int lwm2m_rd_client_timeout(struct lwm2m_ctx *client_ctx)
1786 {
1787 if (client.ctx != client_ctx) {
1788 return -EPERM;
1789 }
1790
1791 if (!sm_is_registered()) {
1792 return 0;
1793 }
1794 k_mutex_lock(&client.mutex, K_FOREVER);
1795 LOG_WRN("Confirmable Timeout -> Re-connect and register");
1796 set_sm_state(ENGINE_DO_REGISTRATION);
1797 next_event_at(0);
1798 k_mutex_unlock(&client.mutex);
1799 return 0;
1800 }
1801
lwm2m_rd_client_is_registred(struct lwm2m_ctx * client_ctx)1802 bool lwm2m_rd_client_is_registred(struct lwm2m_ctx *client_ctx)
1803 {
1804 if (client.ctx != client_ctx || !sm_is_registered()) {
1805 return false;
1806 }
1807
1808 return true;
1809 }
lwm2m_rd_client_is_suspended(struct lwm2m_ctx * client_ctx)1810 bool lwm2m_rd_client_is_suspended(struct lwm2m_ctx *client_ctx)
1811 {
1812 if (client.ctx != client_ctx || !sm_is_suspended()) {
1813 return false;
1814 }
1815
1816 return true;
1817 }
1818
1819
lwm2m_rd_client_init(void)1820 int lwm2m_rd_client_init(void)
1821 {
1822 client.ctx = NULL;
1823 client.rd_message.ctx = NULL;
1824 client.engine_state = ENGINE_IDLE;
1825 k_mutex_init(&client.mutex);
1826
1827 return 0;
1828 }
1829
sys_lwm2m_rd_client_init(void)1830 static int sys_lwm2m_rd_client_init(void)
1831 {
1832 return lwm2m_rd_client_init();
1833 }
1834
1835 LWM2M_ENGINE_INIT(sys_lwm2m_rd_client_init);
1836