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