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 <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 <init.h>
57 #include <sys/printk.h>
58
59 #include "lwm2m_object.h"
60 #include "lwm2m_engine.h"
61 #include "lwm2m_rw_link_format.h"
62
63 #define LWM2M_RD_CLIENT_URI "rd"
64
65 #define SECONDS_TO_UPDATE_EARLY CONFIG_LWM2M_SECONDS_TO_UPDATE_EARLY
66 #define STATE_MACHINE_UPDATE_INTERVAL_MS 500
67
68 #define CLIENT_EP_LEN CONFIG_LWM2M_RD_CLIENT_ENDPOINT_NAME_MAX_LENGTH
69
70 /* Up to 3 characters + NULL */
71 #define CLIENT_BINDING_LEN sizeof("UQS")
72
73 /* The states for the RD client state machine */
74 /*
75 * When node is unregistered it ends up in UNREGISTERED
76 * and this is going to be there until use X or Y kicks it
77 * back into INIT again
78 */
79 enum sm_engine_state {
80 ENGINE_IDLE,
81 ENGINE_INIT,
82 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
83 ENGINE_DO_BOOTSTRAP_REG,
84 ENGINE_BOOTSTRAP_REG_SENT,
85 ENGINE_BOOTSTRAP_REG_DONE,
86 ENGINE_BOOTSTRAP_TRANS_DONE,
87 #endif
88 ENGINE_DO_REGISTRATION,
89 ENGINE_REGISTRATION_SENT,
90 ENGINE_REGISTRATION_DONE,
91 ENGINE_REGISTRATION_DONE_RX_OFF,
92 ENGINE_UPDATE_SENT,
93 ENGINE_DEREGISTER,
94 ENGINE_DEREGISTER_SENT,
95 ENGINE_DEREGISTERED,
96 ENGINE_NETWORK_ERROR,
97 };
98
99 struct lwm2m_rd_client_info {
100 struct k_mutex mutex;
101
102 uint32_t lifetime;
103 struct lwm2m_ctx *ctx;
104 uint8_t engine_state;
105 uint8_t retries;
106 uint8_t retry_delay;
107
108 int64_t last_update;
109 int64_t last_tx;
110
111 char ep_name[CLIENT_EP_LEN];
112 char server_ep[CLIENT_EP_LEN];
113
114 lwm2m_ctx_event_cb_t event_cb;
115
116 bool use_bootstrap : 1;
117 bool trigger_update : 1;
118 bool update_objects : 1;
119 } client;
120
121 /* Allocate some data for queries and updates. Make sure it's large enough to
122 * hold the largest query string, which in most cases will be the endpoint
123 * string. In other case, 32 bytes are enough to encode any other query string
124 * documented in the LwM2M specification.
125 */
126 static char query_buffer[MAX(32, sizeof("ep=") + CLIENT_EP_LEN)];
127
engine_update_tx_time(void)128 void engine_update_tx_time(void)
129 {
130 client.last_tx = k_uptime_get();
131 }
132
set_sm_state(uint8_t sm_state)133 static void set_sm_state(uint8_t sm_state)
134 {
135 enum lwm2m_rd_client_event event = LWM2M_RD_CLIENT_EVENT_NONE;
136
137 /* Determine if a callback to the app is needed */
138 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
139 if (sm_state == ENGINE_BOOTSTRAP_REG_DONE) {
140 event = LWM2M_RD_CLIENT_EVENT_BOOTSTRAP_REG_COMPLETE;
141 } else if (client.engine_state == ENGINE_BOOTSTRAP_TRANS_DONE &&
142 sm_state == ENGINE_DO_REGISTRATION) {
143 event = LWM2M_RD_CLIENT_EVENT_BOOTSTRAP_TRANSFER_COMPLETE;
144 } else
145 #endif
146 if (client.engine_state == ENGINE_UPDATE_SENT &&
147 sm_state == ENGINE_REGISTRATION_DONE) {
148 event = LWM2M_RD_CLIENT_EVENT_REG_UPDATE_COMPLETE;
149 } else if (sm_state == ENGINE_REGISTRATION_DONE) {
150 event = LWM2M_RD_CLIENT_EVENT_REGISTRATION_COMPLETE;
151 } else if (sm_state == ENGINE_REGISTRATION_DONE_RX_OFF) {
152 event = LWM2M_RD_CLIENT_EVENT_QUEUE_MODE_RX_OFF;
153 } else if ((sm_state == ENGINE_INIT ||
154 sm_state == ENGINE_DEREGISTERED) &&
155 (client.engine_state >= ENGINE_DO_REGISTRATION &&
156 client.engine_state <= ENGINE_DEREGISTER_SENT)) {
157 event = LWM2M_RD_CLIENT_EVENT_DISCONNECT;
158 } else if (sm_state == ENGINE_NETWORK_ERROR) {
159 client.retry_delay = 1 << client.retries;
160 client.retries++;
161 if (client.retries > CONFIG_LWM2M_RD_CLIENT_MAX_RETRIES) {
162 client.retries = 0;
163 event = LWM2M_RD_CLIENT_EVENT_NETWORK_ERROR;
164 }
165 }
166
167 /* TODO: add locking? */
168 client.engine_state = sm_state;
169
170 if (event > LWM2M_RD_CLIENT_EVENT_NONE && client.event_cb) {
171 client.event_cb(client.ctx, event);
172 }
173 }
174
sm_is_registered(void)175 static bool sm_is_registered(void)
176 {
177 return (client.engine_state >= ENGINE_REGISTRATION_DONE &&
178 client.engine_state <= ENGINE_DEREGISTER_SENT);
179 }
180
get_sm_state(void)181 static uint8_t get_sm_state(void)
182 {
183 /* TODO: add locking? */
184 return client.engine_state;
185 }
186
sm_handle_timeout_state(struct lwm2m_message * msg,enum sm_engine_state sm_state)187 static void sm_handle_timeout_state(struct lwm2m_message *msg,
188 enum sm_engine_state sm_state)
189 {
190 enum lwm2m_rd_client_event event = LWM2M_RD_CLIENT_EVENT_NONE;
191
192 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
193 if (client.engine_state == ENGINE_BOOTSTRAP_REG_SENT) {
194 event = LWM2M_RD_CLIENT_EVENT_BOOTSTRAP_REG_FAILURE;
195 } else
196 #endif
197 {
198 if (client.engine_state == ENGINE_REGISTRATION_SENT) {
199 event = LWM2M_RD_CLIENT_EVENT_REGISTRATION_FAILURE;
200 } else if (client.engine_state == ENGINE_UPDATE_SENT) {
201 event = LWM2M_RD_CLIENT_EVENT_REG_UPDATE_FAILURE;
202 } else if (client.engine_state == ENGINE_DEREGISTER_SENT) {
203 event = LWM2M_RD_CLIENT_EVENT_DEREGISTER_FAILURE;
204 } else {
205 /* TODO: unknown timeout state */
206 }
207 }
208
209 set_sm_state(sm_state);
210
211 if (event > LWM2M_RD_CLIENT_EVENT_NONE && client.event_cb) {
212 client.event_cb(client.ctx, event);
213 }
214 }
215
sm_handle_failure_state(enum sm_engine_state sm_state)216 static void sm_handle_failure_state(enum sm_engine_state sm_state)
217 {
218 enum lwm2m_rd_client_event event = LWM2M_RD_CLIENT_EVENT_NONE;
219
220 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
221 if (client.engine_state == ENGINE_BOOTSTRAP_REG_SENT) {
222 event = LWM2M_RD_CLIENT_EVENT_BOOTSTRAP_REG_FAILURE;
223 } else
224 #endif
225 if (client.engine_state == ENGINE_REGISTRATION_SENT) {
226 event = LWM2M_RD_CLIENT_EVENT_REGISTRATION_FAILURE;
227 } else if (client.engine_state == ENGINE_UPDATE_SENT) {
228 event = LWM2M_RD_CLIENT_EVENT_REG_UPDATE_FAILURE;
229 } else if (client.engine_state == ENGINE_DEREGISTER_SENT) {
230 event = LWM2M_RD_CLIENT_EVENT_DEREGISTER_FAILURE;
231 }
232
233 set_sm_state(sm_state);
234
235 if (event > LWM2M_RD_CLIENT_EVENT_NONE && client.event_cb) {
236 client.event_cb(client.ctx, event);
237 }
238 }
239
240 /* force state machine restart */
socket_fault_cb(int error)241 static void socket_fault_cb(int error)
242 {
243 LOG_ERR("RD Client socket error: %d", error);
244
245 lwm2m_engine_context_close(client.ctx);
246
247 client.ctx->sec_obj_inst = -1;
248
249 /* Jump directly to the registration phase. In case there is no valid
250 * security object for the LWM2M server, it will fall back to the
251 * bootstrap procedure.
252 */
253 set_sm_state(ENGINE_DO_REGISTRATION);
254 }
255
256 /* force re-update with remote peer */
engine_trigger_update(bool update_objects)257 void engine_trigger_update(bool update_objects)
258 {
259 if (client.engine_state < ENGINE_REGISTRATION_SENT ||
260 client.engine_state > ENGINE_UPDATE_SENT) {
261 return;
262 }
263
264 /* TODO: add locking? */
265 client.trigger_update = true;
266
267 if (update_objects) {
268 client.update_objects = true;
269 }
270 }
271
code2str(uint8_t code)272 static inline const char *code2str(uint8_t code)
273 {
274 switch (code) {
275 case COAP_RESPONSE_CODE_BAD_REQUEST:
276 return "Bad Request";
277 case COAP_RESPONSE_CODE_FORBIDDEN:
278 return "Forbidden";
279 case COAP_RESPONSE_CODE_NOT_FOUND:
280 return "Not Found";
281 case COAP_RESPONSE_CODE_PRECONDITION_FAILED:
282 return "Precondition Failed";
283 default:
284 break;
285 }
286
287 return "Unknown";
288 }
289
290 /* state machine reply callbacks */
291
292 #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)293 static int do_bootstrap_reply_cb(const struct coap_packet *response,
294 struct coap_reply *reply,
295 const struct sockaddr *from)
296 {
297 uint8_t code;
298
299 code = coap_header_get_code(response);
300 LOG_DBG("Bootstrap callback (code:%u.%u)",
301 COAP_RESPONSE_CODE_CLASS(code),
302 COAP_RESPONSE_CODE_DETAIL(code));
303
304 if (code == COAP_RESPONSE_CODE_CHANGED) {
305 LOG_INF("Bootstrap registration done!");
306 set_sm_state(ENGINE_BOOTSTRAP_REG_DONE);
307 return 0;
308 }
309
310 LOG_ERR("Failed with code %u.%u (%s). Not Retrying.",
311 COAP_RESPONSE_CODE_CLASS(code), COAP_RESPONSE_CODE_DETAIL(code),
312 code2str(code));
313
314 lwm2m_engine_context_close(client.ctx);
315 sm_handle_failure_state(ENGINE_IDLE);
316
317 return 0;
318 }
319
do_bootstrap_reg_timeout_cb(struct lwm2m_message * msg)320 static void do_bootstrap_reg_timeout_cb(struct lwm2m_message *msg)
321 {
322 LOG_WRN("Bootstrap Timeout");
323
324 /* TODO:
325 * Look for the "next" BOOTSTRAP server entry in our security info
326 */
327
328 /* Restart from scratch */
329 sm_handle_timeout_state(msg, ENGINE_INIT);
330 }
331 #endif
332
do_registration_reply_cb(const struct coap_packet * response,struct coap_reply * reply,const struct sockaddr * from)333 static int do_registration_reply_cb(const struct coap_packet *response,
334 struct coap_reply *reply,
335 const struct sockaddr *from)
336 {
337 struct coap_option options[2];
338 uint8_t code;
339 int ret;
340
341 code = coap_header_get_code(response);
342 LOG_DBG("Registration callback (code:%u.%u)",
343 COAP_RESPONSE_CODE_CLASS(code),
344 COAP_RESPONSE_CODE_DETAIL(code));
345
346 /* check state and possibly set registration to done */
347 if (code == COAP_RESPONSE_CODE_CREATED) {
348 ret = coap_find_options(response, COAP_OPTION_LOCATION_PATH,
349 options, 2);
350 if (ret < 2) {
351 LOG_ERR("Unexpected endpoint data returned.");
352 return -EINVAL;
353 }
354
355 /* option[0] should be "rd" */
356
357 if (options[1].len + 1 > sizeof(client.server_ep)) {
358 LOG_ERR("Unexpected length of query: "
359 "%u (expected %zu)\n",
360 options[1].len,
361 sizeof(client.server_ep));
362 return -EINVAL;
363 }
364
365 memcpy(client.server_ep, options[1].value,
366 options[1].len);
367 client.server_ep[options[1].len] = '\0';
368 set_sm_state(ENGINE_REGISTRATION_DONE);
369 LOG_INF("Registration Done (EP='%s')",
370 log_strdup(client.server_ep));
371
372 return 0;
373 }
374
375 LOG_ERR("Failed with code %u.%u (%s). Not Retrying.",
376 COAP_RESPONSE_CODE_CLASS(code), COAP_RESPONSE_CODE_DETAIL(code),
377 code2str(code));
378
379 lwm2m_engine_context_close(client.ctx);
380 sm_handle_failure_state(ENGINE_IDLE);
381
382 return 0;
383 }
384
do_registration_timeout_cb(struct lwm2m_message * msg)385 static void do_registration_timeout_cb(struct lwm2m_message *msg)
386 {
387 LOG_WRN("Registration Timeout");
388
389 /* Restart from scratch */
390 sm_handle_timeout_state(msg, ENGINE_INIT);
391 }
392
do_update_reply_cb(const struct coap_packet * response,struct coap_reply * reply,const struct sockaddr * from)393 static int do_update_reply_cb(const struct coap_packet *response,
394 struct coap_reply *reply,
395 const struct sockaddr *from)
396 {
397 uint8_t code;
398
399 code = coap_header_get_code(response);
400 LOG_INF("Update callback (code:%u.%u)",
401 COAP_RESPONSE_CODE_CLASS(code),
402 COAP_RESPONSE_CODE_DETAIL(code));
403
404 /* If NOT_FOUND just continue on */
405 if ((code == COAP_RESPONSE_CODE_CHANGED) ||
406 (code == COAP_RESPONSE_CODE_CREATED)) {
407 set_sm_state(ENGINE_REGISTRATION_DONE);
408 LOG_INF("Update Done");
409 return 0;
410 }
411
412 LOG_ERR("Failed with code %u.%u (%s). Retrying registration.",
413 COAP_RESPONSE_CODE_CLASS(code), COAP_RESPONSE_CODE_DETAIL(code),
414 code2str(code));
415
416 sm_handle_failure_state(ENGINE_DO_REGISTRATION);
417
418 return 0;
419 }
420
do_update_timeout_cb(struct lwm2m_message * msg)421 static void do_update_timeout_cb(struct lwm2m_message *msg)
422 {
423 LOG_WRN("Registration Update Timeout");
424
425 /* Re-do registration */
426 sm_handle_timeout_state(msg, ENGINE_DO_REGISTRATION);
427 }
428
do_deregister_reply_cb(const struct coap_packet * response,struct coap_reply * reply,const struct sockaddr * from)429 static int do_deregister_reply_cb(const struct coap_packet *response,
430 struct coap_reply *reply,
431 const struct sockaddr *from)
432 {
433 uint8_t code;
434
435 code = coap_header_get_code(response);
436 LOG_DBG("Deregister callback (code:%u.%u)",
437 COAP_RESPONSE_CODE_CLASS(code),
438 COAP_RESPONSE_CODE_DETAIL(code));
439
440 if (code == COAP_RESPONSE_CODE_DELETED) {
441 LOG_INF("Deregistration success");
442 lwm2m_engine_context_close(client.ctx);
443 set_sm_state(ENGINE_DEREGISTERED);
444 return 0;
445 }
446
447 LOG_ERR("Failed with code %u.%u (%s). Not Retrying",
448 COAP_RESPONSE_CODE_CLASS(code), COAP_RESPONSE_CODE_DETAIL(code),
449 code2str(code));
450
451 lwm2m_engine_context_close(client.ctx);
452 sm_handle_failure_state(ENGINE_IDLE);
453
454 return 0;
455 }
456
do_deregister_timeout_cb(struct lwm2m_message * msg)457 static void do_deregister_timeout_cb(struct lwm2m_message *msg)
458 {
459 LOG_WRN("De-Registration Timeout");
460
461 /* Abort de-registration and start from scratch */
462 sm_handle_timeout_state(msg, ENGINE_INIT);
463 }
464
sm_bootstrap_verify(bool bootstrap_server,int sec_obj_inst)465 static bool sm_bootstrap_verify(bool bootstrap_server, int sec_obj_inst)
466 {
467 char pathstr[MAX_RESOURCE_LEN];
468 bool bootstrap;
469 int ret;
470
471 snprintk(pathstr, sizeof(pathstr), "0/%d/1", sec_obj_inst);
472 ret = lwm2m_engine_get_bool(pathstr, &bootstrap);
473 if (ret < 0) {
474 LOG_WRN("Failed to check bootstrap, err %d", ret);
475 return false;
476 }
477
478 if (bootstrap == bootstrap_server) {
479 return true;
480 } else {
481 return false;
482 }
483 }
484
sm_update_lifetime(int srv_obj_inst,uint32_t * lifetime)485 static bool sm_update_lifetime(int srv_obj_inst, uint32_t *lifetime)
486 {
487 char pathstr[MAX_RESOURCE_LEN];
488 uint32_t new_lifetime;
489
490 snprintk(pathstr, sizeof(pathstr), "1/%d/1", srv_obj_inst);
491 if (lwm2m_engine_get_u32(pathstr, &new_lifetime) < 0) {
492 new_lifetime = CONFIG_LWM2M_ENGINE_DEFAULT_LIFETIME;
493 LOG_INF("Using default lifetime: %u", new_lifetime);
494 }
495
496 if (new_lifetime != *lifetime) {
497 *lifetime = new_lifetime;
498 return true;
499 }
500
501 return false;
502 }
503
sm_select_server_inst(int sec_obj_inst,int * srv_obj_inst,uint32_t * lifetime)504 static int sm_select_server_inst(int sec_obj_inst, int *srv_obj_inst,
505 uint32_t *lifetime)
506 {
507 char pathstr[MAX_RESOURCE_LEN];
508 uint16_t server_id;
509 int ret, obj_inst_id;
510
511 snprintk(pathstr, sizeof(pathstr), "0/%d/10", sec_obj_inst);
512 ret = lwm2m_engine_get_u16(pathstr, &server_id);
513 if (ret < 0) {
514 LOG_WRN("Failed to obtain Short Server ID, err %d", ret);
515 return -EINVAL;
516 }
517
518 obj_inst_id = lwm2m_server_short_id_to_inst(server_id);
519 if (obj_inst_id < 0) {
520 LOG_WRN("Failed to obtain Server Object instance, err %d",
521 obj_inst_id);
522 return -EINVAL;
523 }
524
525 *srv_obj_inst = obj_inst_id;
526
527 return 0;
528 }
529
sm_select_security_inst(bool bootstrap_server,int * sec_obj_inst)530 static int sm_select_security_inst(bool bootstrap_server, int *sec_obj_inst)
531 {
532 int i, obj_inst_id = -1;
533
534 /* lookup existing index */
535 i = lwm2m_security_inst_id_to_index(*sec_obj_inst);
536 if (i >= 0 && sm_bootstrap_verify(bootstrap_server, *sec_obj_inst)) {
537 return 0;
538 }
539
540 *sec_obj_inst = -1;
541
542 /* Iterate over all instances to find the correct one. */
543 for (i = 0; i < CONFIG_LWM2M_SECURITY_INSTANCE_COUNT; i++) {
544 obj_inst_id = lwm2m_security_index_to_inst_id(i);
545 if (obj_inst_id < 0) {
546 LOG_WRN("Failed to get inst id for %d", i);
547 continue;
548 }
549
550 if (sm_bootstrap_verify(bootstrap_server, obj_inst_id)) {
551 *sec_obj_inst = obj_inst_id;
552 return 0;
553 }
554 }
555
556 LOG_WRN("sec_obj_inst: No matching servers found.");
557
558 return -ENOENT;
559 }
560
561 /* state machine step functions */
562
sm_do_init(void)563 static int sm_do_init(void)
564 {
565 client.ctx->sec_obj_inst = -1;
566 client.ctx->srv_obj_inst = -1;
567 client.trigger_update = false;
568 client.lifetime = 0U;
569 client.retries = 0U;
570
571 /* Do bootstrap or registration */
572 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
573 if (client.use_bootstrap) {
574 set_sm_state(ENGINE_DO_BOOTSTRAP_REG);
575 } else {
576 set_sm_state(ENGINE_DO_REGISTRATION);
577 }
578 #else
579 set_sm_state(ENGINE_DO_REGISTRATION);
580 #endif
581 return 0;
582 }
583
584 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
sm_send_bootstrap_registration(void)585 static int sm_send_bootstrap_registration(void)
586 {
587 struct lwm2m_message *msg;
588 int ret;
589
590 msg = lwm2m_get_message(client.ctx);
591 if (!msg) {
592 LOG_ERR("Unable to get a lwm2m message!");
593 return -ENOMEM;
594 }
595
596 msg->type = COAP_TYPE_CON;
597 msg->code = COAP_METHOD_POST;
598 msg->mid = coap_next_id();
599 msg->tkl = LWM2M_MSG_TOKEN_GENERATE_NEW;
600 msg->reply_cb = do_bootstrap_reply_cb;
601 msg->message_timeout_cb = do_bootstrap_reg_timeout_cb;
602
603 ret = lwm2m_init_message(msg);
604 if (ret) {
605 goto cleanup;
606 }
607
608 /* TODO: handle return error */
609 coap_packet_append_option(&msg->cpkt, COAP_OPTION_URI_PATH,
610 "bs", strlen("bs"));
611
612 snprintk(query_buffer, sizeof(query_buffer) - 1, "ep=%s",
613 client.ep_name);
614 /* TODO: handle return error */
615 coap_packet_append_option(&msg->cpkt, COAP_OPTION_URI_QUERY,
616 query_buffer, strlen(query_buffer));
617
618 /* log the bootstrap attempt */
619 LOG_DBG("Register ID with bootstrap server as '%s'",
620 log_strdup(query_buffer));
621
622 lwm2m_send_message_async(msg);
623
624 return 0;
625
626 cleanup:
627 lwm2m_reset_message(msg, true);
628 return ret;
629 }
630
sm_do_bootstrap_reg(void)631 static int sm_do_bootstrap_reg(void)
632 {
633 int ret;
634
635 /* clear out existing connection data */
636 if (client.ctx->sock_fd > -1) {
637 lwm2m_engine_context_close(client.ctx);
638 }
639
640 client.ctx->bootstrap_mode = true;
641 ret = sm_select_security_inst(client.ctx->bootstrap_mode,
642 &client.ctx->sec_obj_inst);
643 if (ret < 0) {
644 /* no bootstrap server found, let's move to registration */
645 LOG_WRN("Bootstrap server not found! Try normal registration.");
646 set_sm_state(ENGINE_DO_REGISTRATION);
647 return ret;
648 }
649
650 LOG_INF("Bootstrap started with endpoint '%s' with client lifetime %d",
651 log_strdup(client.ep_name), client.lifetime);
652
653 ret = lwm2m_engine_start(client.ctx);
654 if (ret < 0) {
655 LOG_ERR("Cannot init LWM2M engine (%d)", ret);
656 set_sm_state(ENGINE_NETWORK_ERROR);
657 return ret;
658 }
659
660 ret = sm_send_bootstrap_registration();
661 if (!ret) {
662 set_sm_state(ENGINE_BOOTSTRAP_REG_SENT);
663 } else {
664 LOG_ERR("Bootstrap registration err: %d", ret);
665 lwm2m_engine_context_close(client.ctx);
666 set_sm_state(ENGINE_NETWORK_ERROR);
667 }
668
669 return ret;
670 }
671
engine_bootstrap_finish(void)672 void engine_bootstrap_finish(void)
673 {
674 LOG_INF("Bootstrap data transfer done!");
675 set_sm_state(ENGINE_BOOTSTRAP_TRANS_DONE);
676 }
677
sm_bootstrap_trans_done(void)678 static int sm_bootstrap_trans_done(void)
679 {
680 /* close down context resources */
681 lwm2m_engine_context_close(client.ctx);
682
683 /* reset security object instance */
684 client.ctx->sec_obj_inst = -1;
685
686 set_sm_state(ENGINE_DO_REGISTRATION);
687
688 return 0;
689 }
690 #endif
691
sm_send_registration(bool send_obj_support_data,coap_reply_t reply_cb,lwm2m_message_timeout_cb_t timeout_cb)692 static int sm_send_registration(bool send_obj_support_data,
693 coap_reply_t reply_cb,
694 lwm2m_message_timeout_cb_t timeout_cb)
695 {
696 struct lwm2m_message *msg;
697 int ret;
698 char binding[CLIENT_BINDING_LEN];
699
700 msg = lwm2m_get_message(client.ctx);
701 if (!msg) {
702 LOG_ERR("Unable to get a lwm2m message!");
703 return -ENOMEM;
704 }
705
706 /* remember the last reg time */
707 client.last_update = k_uptime_get();
708
709 msg->type = COAP_TYPE_CON;
710 msg->code = COAP_METHOD_POST;
711 msg->mid = coap_next_id();
712 msg->tkl = LWM2M_MSG_TOKEN_GENERATE_NEW;
713 msg->reply_cb = reply_cb;
714 msg->message_timeout_cb = timeout_cb;
715
716 ret = lwm2m_init_message(msg);
717 if (ret) {
718 goto cleanup;
719 }
720
721 ret = coap_packet_append_option(&msg->cpkt, COAP_OPTION_URI_PATH,
722 LWM2M_RD_CLIENT_URI,
723 strlen(LWM2M_RD_CLIENT_URI));
724 if (ret < 0) {
725 goto cleanup;
726 }
727
728 if (sm_is_registered()) {
729 ret = coap_packet_append_option(
730 &msg->cpkt, COAP_OPTION_URI_PATH,
731 client.server_ep, strlen(client.server_ep));
732 if (ret < 0) {
733 goto cleanup;
734 }
735 }
736
737 if (send_obj_support_data) {
738 ret = coap_append_option_int(
739 &msg->cpkt, COAP_OPTION_CONTENT_FORMAT,
740 LWM2M_FORMAT_APP_LINK_FORMAT);
741 if (ret < 0) {
742 goto cleanup;
743 }
744 }
745
746 if (!sm_is_registered()) {
747 snprintk(query_buffer, sizeof(query_buffer) - 1,
748 "lwm2m=%s", LWM2M_PROTOCOL_VERSION_STRING);
749 ret = coap_packet_append_option(
750 &msg->cpkt, COAP_OPTION_URI_QUERY,
751 query_buffer, strlen(query_buffer));
752 if (ret < 0) {
753 goto cleanup;
754 }
755
756 snprintk(query_buffer, sizeof(query_buffer) - 1,
757 "ep=%s", client.ep_name);
758 ret = coap_packet_append_option(
759 &msg->cpkt, COAP_OPTION_URI_QUERY,
760 query_buffer, strlen(query_buffer));
761 if (ret < 0) {
762 goto cleanup;
763 }
764 }
765
766 /* Send lifetime only if changed or on initial registration.*/
767 if (sm_update_lifetime(client.ctx->srv_obj_inst, &client.lifetime) ||
768 !sm_is_registered()) {
769 snprintk(query_buffer, sizeof(query_buffer) - 1,
770 "lt=%d", client.lifetime);
771 ret = coap_packet_append_option(
772 &msg->cpkt, COAP_OPTION_URI_QUERY,
773 query_buffer, strlen(query_buffer));
774 if (ret < 0) {
775 goto cleanup;
776 }
777 }
778
779 lwm2m_engine_get_binding(binding);
780 /* UDP is a default binding, no need to add option if UDP is used. */
781 if ((!sm_is_registered() && strcmp(binding, "U") != 0)) {
782 snprintk(query_buffer, sizeof(query_buffer) - 1,
783 "b=%s", binding);
784
785 ret = coap_packet_append_option(
786 &msg->cpkt, COAP_OPTION_URI_QUERY,
787 query_buffer, strlen(query_buffer));
788 if (ret < 0) {
789 goto cleanup;
790 }
791 }
792
793 if (send_obj_support_data) {
794 ret = coap_packet_append_payload_marker(&msg->cpkt);
795 if (ret < 0) {
796 goto cleanup;
797 }
798
799 msg->out.out_cpkt = &msg->cpkt;
800 msg->out.writer = &link_format_writer;
801
802 ret = do_register_op_link_format(msg);
803 if (ret < 0) {
804 goto cleanup;
805 }
806 }
807
808 lwm2m_send_message_async(msg);
809
810 /* log the registration attempt */
811 LOG_DBG("registration sent [%s]",
812 log_strdup(lwm2m_sprint_ip_addr(&client.ctx->remote_addr)));
813
814 return 0;
815
816 cleanup:
817 lwm2m_reset_message(msg, true);
818 return ret;
819 }
820
sm_do_registration(void)821 static int sm_do_registration(void)
822 {
823 int ret = 0;
824
825 /* clear out existing connection data */
826 if (client.ctx->sock_fd > -1) {
827 lwm2m_engine_context_close(client.ctx);
828 }
829
830 client.ctx->bootstrap_mode = false;
831 ret = sm_select_security_inst(client.ctx->bootstrap_mode,
832 &client.ctx->sec_obj_inst);
833 if (ret < 0) {
834 LOG_ERR("Unable to find a valid security instance.");
835 set_sm_state(ENGINE_INIT);
836 return -EINVAL;
837 }
838
839 ret = sm_select_server_inst(client.ctx->sec_obj_inst,
840 &client.ctx->srv_obj_inst,
841 &client.lifetime);
842 if (ret < 0) {
843 LOG_ERR("Unable to find a valid server instance.");
844 set_sm_state(ENGINE_INIT);
845 return -EINVAL;
846 }
847
848 LOG_INF("RD Client started with endpoint '%s' with client lifetime %d",
849 log_strdup(client.ep_name), client.lifetime);
850
851 ret = lwm2m_engine_start(client.ctx);
852 if (ret < 0) {
853 LOG_ERR("Cannot init LWM2M engine (%d)", ret);
854 set_sm_state(ENGINE_NETWORK_ERROR);
855 return ret;
856 }
857
858 ret = sm_send_registration(true,
859 do_registration_reply_cb,
860 do_registration_timeout_cb);
861 if (!ret) {
862 set_sm_state(ENGINE_REGISTRATION_SENT);
863 } else {
864 LOG_ERR("Registration err: %d", ret);
865 lwm2m_engine_context_close(client.ctx);
866 set_sm_state(ENGINE_NETWORK_ERROR);
867 }
868
869 return ret;
870 }
871
sm_registration_done(void)872 static int sm_registration_done(void)
873 {
874 int ret = 0;
875 bool update_objects;
876
877 /*
878 * check for lifetime seconds - SECONDS_TO_UPDATE_EARLY
879 * so that we can update early and avoid lifetime timeout
880 */
881 if (sm_is_registered() &&
882 (client.trigger_update ||
883 ((client.lifetime - SECONDS_TO_UPDATE_EARLY) <=
884 (k_uptime_get() - client.last_update) / 1000))) {
885 update_objects = client.update_objects;
886 client.trigger_update = false;
887 client.update_objects = false;
888
889 ret = sm_send_registration(update_objects,
890 do_update_reply_cb,
891 do_update_timeout_cb);
892 if (!ret) {
893 set_sm_state(ENGINE_UPDATE_SENT);
894 } else {
895 LOG_ERR("Registration update err: %d", ret);
896 lwm2m_engine_context_close(client.ctx);
897 /* perform full registration */
898 set_sm_state(ENGINE_DO_REGISTRATION);
899 }
900 }
901
902 if (IS_ENABLED(CONFIG_LWM2M_QUEUE_MODE_ENABLED) &&
903 (client.engine_state != ENGINE_REGISTRATION_DONE_RX_OFF) &&
904 (((k_uptime_get() - client.last_tx) / 1000) >=
905 CONFIG_LWM2M_QUEUE_MODE_UPTIME)) {
906 set_sm_state(ENGINE_REGISTRATION_DONE_RX_OFF);
907 }
908
909 return ret;
910 }
911
sm_do_deregister(void)912 static int sm_do_deregister(void)
913 {
914 struct lwm2m_message *msg;
915 int ret;
916
917 msg = lwm2m_get_message(client.ctx);
918 if (!msg) {
919 LOG_ERR("Unable to get a lwm2m message!");
920 return -ENOMEM;
921 }
922
923 msg->type = COAP_TYPE_CON;
924 msg->code = COAP_METHOD_DELETE;
925 msg->mid = coap_next_id();
926 msg->tkl = LWM2M_MSG_TOKEN_GENERATE_NEW;
927 msg->reply_cb = do_deregister_reply_cb;
928 msg->message_timeout_cb = do_deregister_timeout_cb;
929
930 ret = lwm2m_init_message(msg);
931 if (ret) {
932 goto cleanup;
933 }
934
935 ret = coap_packet_append_option(&msg->cpkt, COAP_OPTION_URI_PATH,
936 LWM2M_RD_CLIENT_URI,
937 strlen(LWM2M_RD_CLIENT_URI));
938 if (ret < 0) {
939 LOG_ERR("Failed to encode URI path option (err:%d).", ret);
940 goto cleanup;
941 }
942
943 /* include server endpoint in URI PATH */
944 ret = coap_packet_append_option(&msg->cpkt, COAP_OPTION_URI_PATH,
945 client.server_ep,
946 strlen(client.server_ep));
947 if (ret < 0) {
948 LOG_ERR("Failed to encode URI path option (err:%d).", ret);
949 goto cleanup;
950 }
951
952 LOG_INF("Deregister from '%s'", log_strdup(client.server_ep));
953
954 lwm2m_send_message_async(msg);
955
956 set_sm_state(ENGINE_DEREGISTER_SENT);
957 return 0;
958
959 cleanup:
960 lwm2m_reset_message(msg, true);
961 lwm2m_engine_context_close(client.ctx);
962 return ret;
963 }
964
sm_do_network_error(void)965 static void sm_do_network_error(void)
966 {
967 if (--client.retry_delay > 0) {
968 return;
969 }
970
971 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
972 if (client.ctx->bootstrap_mode) {
973 set_sm_state(ENGINE_DO_BOOTSTRAP_REG);
974 return;
975 }
976 #endif
977
978 set_sm_state(ENGINE_DO_REGISTRATION);
979 }
980
lwm2m_rd_client_service(struct k_work * work)981 static void lwm2m_rd_client_service(struct k_work *work)
982 {
983 k_mutex_lock(&client.mutex, K_FOREVER);
984
985 if (client.ctx) {
986 switch (get_sm_state()) {
987 case ENGINE_IDLE:
988 break;
989
990 case ENGINE_INIT:
991 sm_do_init();
992 break;
993
994 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
995 case ENGINE_DO_BOOTSTRAP_REG:
996 sm_do_bootstrap_reg();
997 break;
998
999 case ENGINE_BOOTSTRAP_REG_SENT:
1000 /* wait for bootstrap registration done */
1001 break;
1002
1003 case ENGINE_BOOTSTRAP_REG_DONE:
1004 /* wait for transfer done */
1005 break;
1006
1007 case ENGINE_BOOTSTRAP_TRANS_DONE:
1008 sm_bootstrap_trans_done();
1009 break;
1010 #endif
1011
1012 case ENGINE_DO_REGISTRATION:
1013 sm_do_registration();
1014 break;
1015
1016 case ENGINE_REGISTRATION_SENT:
1017 /* wait registration to be done or timeout */
1018 break;
1019
1020 case ENGINE_REGISTRATION_DONE:
1021 case ENGINE_REGISTRATION_DONE_RX_OFF:
1022 sm_registration_done();
1023 break;
1024
1025 case ENGINE_UPDATE_SENT:
1026 /* wait update to be done or abort */
1027 break;
1028
1029 case ENGINE_DEREGISTER:
1030 sm_do_deregister();
1031 break;
1032
1033 case ENGINE_DEREGISTER_SENT:
1034 /* wait for deregister to be done or reset */
1035 break;
1036
1037 case ENGINE_DEREGISTERED:
1038 set_sm_state(ENGINE_IDLE);
1039 break;
1040
1041 case ENGINE_NETWORK_ERROR:
1042 sm_do_network_error();
1043 break;
1044
1045 default:
1046 LOG_ERR("Unhandled state: %d", get_sm_state());
1047
1048 }
1049 }
1050
1051 k_mutex_unlock(&client.mutex);
1052 }
1053
lwm2m_rd_client_start(struct lwm2m_ctx * client_ctx,const char * ep_name,uint32_t flags,lwm2m_ctx_event_cb_t event_cb)1054 void lwm2m_rd_client_start(struct lwm2m_ctx *client_ctx, const char *ep_name,
1055 uint32_t flags, lwm2m_ctx_event_cb_t event_cb)
1056 {
1057 k_mutex_lock(&client.mutex, K_FOREVER);
1058
1059 if (!IS_ENABLED(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP) &&
1060 (flags & LWM2M_RD_CLIENT_FLAG_BOOTSTRAP)) {
1061 LOG_ERR("Bootstrap support is disabled. Please enable "
1062 "CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP.");
1063
1064 k_mutex_unlock(&client.mutex);
1065 return;
1066 }
1067
1068 client.ctx = client_ctx;
1069 client.ctx->sock_fd = -1;
1070 client.ctx->fault_cb = socket_fault_cb;
1071 client.event_cb = event_cb;
1072 client.use_bootstrap = flags & LWM2M_RD_CLIENT_FLAG_BOOTSTRAP;
1073
1074 set_sm_state(ENGINE_INIT);
1075 strncpy(client.ep_name, ep_name, CLIENT_EP_LEN - 1);
1076 client.ep_name[CLIENT_EP_LEN - 1] = '\0';
1077 LOG_INF("Start LWM2M Client: %s", log_strdup(client.ep_name));
1078
1079 k_mutex_unlock(&client.mutex);
1080 }
1081
lwm2m_rd_client_stop(struct lwm2m_ctx * client_ctx,lwm2m_ctx_event_cb_t event_cb)1082 void lwm2m_rd_client_stop(struct lwm2m_ctx *client_ctx,
1083 lwm2m_ctx_event_cb_t event_cb)
1084 {
1085 k_mutex_lock(&client.mutex, K_FOREVER);
1086
1087 client.ctx = client_ctx;
1088 client.event_cb = event_cb;
1089
1090 if (sm_is_registered()) {
1091 set_sm_state(ENGINE_DEREGISTER);
1092 } else {
1093 if (client.ctx->sock_fd > -1) {
1094 lwm2m_engine_context_close(client.ctx);
1095 }
1096 set_sm_state(ENGINE_IDLE);
1097 }
1098
1099 LOG_INF("Stop LWM2M Client: %s", log_strdup(client.ep_name));
1100
1101 k_mutex_unlock(&client.mutex);
1102 }
1103
lwm2m_rd_client_update(void)1104 void lwm2m_rd_client_update(void)
1105 {
1106 engine_trigger_update(false);
1107 }
1108
lwm2m_rd_client_init(const struct device * dev)1109 static int lwm2m_rd_client_init(const struct device *dev)
1110 {
1111 k_mutex_init(&client.mutex);
1112
1113 return lwm2m_engine_add_service(lwm2m_rd_client_service,
1114 STATE_MACHINE_UPDATE_INTERVAL_MS);
1115
1116 }
1117
1118 SYS_INIT(lwm2m_rd_client_init, APPLICATION,
1119 CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
1120