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 
66 #define LWM2M_RD_CLIENT_URI "rd"
67 
68 #define SECONDS_TO_UPDATE_EARLY	CONFIG_LWM2M_SECONDS_TO_UPDATE_EARLY
69 #define STATE_MACHINE_UPDATE_INTERVAL_MS 500
70 
71 #define CLIENT_EP_LEN		CONFIG_LWM2M_RD_CLIENT_ENDPOINT_NAME_MAX_LENGTH
72 
73 #define CLIENT_BINDING_LEN sizeof("UQ")
74 #define CLIENT_QUEUE_LEN sizeof("Q")
75 
76 static void sm_handle_registration_update_failure(void);
77 static int sm_send_registration_msg(void);
78 static bool sm_is_suspended(void);
79 
80 /* The states for the RD client state machine */
81 /*
82  * When node is unregistered it ends up in UNREGISTERED
83  * and this is going to be there until use X or Y kicks it
84  * back into INIT again
85  */
86 enum sm_engine_state {
87 	ENGINE_IDLE,
88 	ENGINE_INIT,
89 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
90 	ENGINE_DO_BOOTSTRAP_REG,
91 	ENGINE_BOOTSTRAP_REG_SENT,
92 	ENGINE_BOOTSTRAP_REG_DONE,
93 	ENGINE_BOOTSTRAP_TRANS_DONE,
94 #endif
95 	ENGINE_DO_REGISTRATION,
96 	ENGINE_SEND_REGISTRATION,
97 	ENGINE_REGISTRATION_SENT,
98 	ENGINE_REGISTRATION_DONE,
99 	ENGINE_REGISTRATION_DONE_RX_OFF,
100 	ENGINE_UPDATE_REGISTRATION,
101 	ENGINE_UPDATE_SENT,
102 	ENGINE_SUSPENDED,
103 	ENGINE_DEREGISTER,
104 	ENGINE_DEREGISTER_SENT,
105 	ENGINE_DEREGISTERED,
106 	ENGINE_NETWORK_ERROR,
107 };
108 
109 struct lwm2m_rd_client_info {
110 	struct k_mutex mutex;
111 	struct lwm2m_message rd_message;
112 	struct lwm2m_ctx *ctx;
113 	uint32_t lifetime;
114 	uint8_t engine_state;
115 	uint8_t retries;
116 	uint8_t retry_delay;
117 
118 	int64_t last_update;
119 	int64_t last_tx;
120 
121 	char ep_name[CLIENT_EP_LEN];
122 	char server_ep[CLIENT_EP_LEN];
123 
124 	bool use_bootstrap : 1;
125 
126 	bool trigger_update : 1;
127 	bool update_objects : 1;
128 	bool close_socket : 1;
129 } client;
130 
131 /* Allocate some data for queries and updates. Make sure it's large enough to
132  * hold the largest query string, which in most cases will be the endpoint
133  * string. In other case, 32 bytes are enough to encode any other query string
134  * documented in the LwM2M specification.
135  */
136 static char query_buffer[MAX(32, sizeof("ep=") + CLIENT_EP_LEN)];
137 static enum sm_engine_state suspended_client_state;
138 
rd_get_message(void)139 static struct lwm2m_message *rd_get_message(void)
140 {
141 	if (client.rd_message.ctx) {
142 		/* Free old message */
143 		lwm2m_reset_message(&client.rd_message, true);
144 	}
145 
146 	client.rd_message.ctx = client.ctx;
147 	return &client.rd_message;
148 
149 }
150 
rd_client_message_free(void)151 static void rd_client_message_free(void)
152 {
153 	lwm2m_reset_message(lwm2m_get_ongoing_rd_msg(), true);
154 }
155 
156 
lwm2m_get_ongoing_rd_msg(void)157 struct lwm2m_message *lwm2m_get_ongoing_rd_msg(void)
158 {
159 	if (!client.ctx || !client.rd_message.ctx) {
160 		return NULL;
161 	}
162 	return &client.rd_message;
163 }
164 
engine_update_tx_time(void)165 void engine_update_tx_time(void)
166 {
167 	client.last_tx = k_uptime_get();
168 }
169 
set_sm_state(uint8_t sm_state)170 static void set_sm_state(uint8_t sm_state)
171 {
172 	k_mutex_lock(&client.mutex, K_FOREVER);
173 	enum lwm2m_rd_client_event event = LWM2M_RD_CLIENT_EVENT_NONE;
174 
175 	/* Determine if a callback to the app is needed */
176 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
177 	if (sm_state == ENGINE_BOOTSTRAP_REG_DONE) {
178 		event = LWM2M_RD_CLIENT_EVENT_BOOTSTRAP_REG_COMPLETE;
179 	} else if (client.engine_state == ENGINE_BOOTSTRAP_TRANS_DONE &&
180 		   sm_state == ENGINE_DO_REGISTRATION) {
181 		event = LWM2M_RD_CLIENT_EVENT_BOOTSTRAP_TRANSFER_COMPLETE;
182 	} else
183 #endif
184 	if (client.engine_state == ENGINE_UPDATE_SENT &&
185 	    (sm_state == ENGINE_REGISTRATION_DONE ||
186 	     sm_state == ENGINE_REGISTRATION_DONE_RX_OFF)) {
187 		lwm2m_push_queued_buffers(client.ctx);
188 		event = LWM2M_RD_CLIENT_EVENT_REG_UPDATE_COMPLETE;
189 	} else if (sm_state == ENGINE_REGISTRATION_DONE) {
190 		lwm2m_push_queued_buffers(client.ctx);
191 		event = LWM2M_RD_CLIENT_EVENT_REGISTRATION_COMPLETE;
192 	} else if (sm_state == ENGINE_REGISTRATION_DONE_RX_OFF) {
193 		event = LWM2M_RD_CLIENT_EVENT_QUEUE_MODE_RX_OFF;
194 	} else if ((sm_state == ENGINE_INIT ||
195 		    sm_state == ENGINE_DEREGISTERED) &&
196 		   (client.engine_state >= ENGINE_DO_REGISTRATION &&
197 		    client.engine_state <= ENGINE_DEREGISTER_SENT)) {
198 		event = LWM2M_RD_CLIENT_EVENT_DISCONNECT;
199 	} else if (sm_state == ENGINE_NETWORK_ERROR) {
200 		lwm2m_socket_close(client.ctx);
201 		client.retry_delay = 1 << client.retries;
202 		client.retries++;
203 		if (client.retries > CONFIG_LWM2M_RD_CLIENT_MAX_RETRIES) {
204 			client.retries = 0;
205 			event = LWM2M_RD_CLIENT_EVENT_NETWORK_ERROR;
206 		}
207 	} else if (sm_state == ENGINE_UPDATE_REGISTRATION) {
208 		event = LWM2M_RD_CLIENT_EVENT_REG_UPDATE;
209 	}
210 
211 	if (sm_is_suspended()) {
212 		/* Just change the state where we are going to resume next */
213 		suspended_client_state = sm_state;
214 	} else {
215 		client.engine_state = sm_state;
216 	}
217 
218 	if (event > LWM2M_RD_CLIENT_EVENT_NONE && client.ctx->event_cb) {
219 		client.ctx->event_cb(client.ctx, event);
220 	}
221 
222 	/* Suspend socket after Event callback */
223 	if (event == LWM2M_RD_CLIENT_EVENT_QUEUE_MODE_RX_OFF) {
224 		if (IS_ENABLED(CONFIG_LWM2M_RD_CLIENT_SUSPEND_SOCKET_AT_IDLE) ||
225 		    IS_ENABLED(CONFIG_LWM2M_RD_CLIENT_STOP_POLLING_AT_IDLE)) {
226 			lwm2m_socket_suspend(client.ctx);
227 		} else if (IS_ENABLED(CONFIG_LWM2M_RD_CLIENT_CLOSE_SOCKET_AT_IDLE)) {
228 			lwm2m_close_socket(client.ctx);
229 		}
230 	}
231 	k_mutex_unlock(&client.mutex);
232 }
233 
sm_is_bootstrap(void)234 static bool sm_is_bootstrap(void)
235 {
236 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
237 	k_mutex_lock(&client.mutex, K_FOREVER);
238 	bool is_bootstrap = (client.engine_state >= ENGINE_DO_BOOTSTRAP_REG &&
239 		client.engine_state <= ENGINE_BOOTSTRAP_TRANS_DONE);
240 	k_mutex_unlock(&client.mutex);
241 	return is_bootstrap;
242 #else
243 	return false;
244 #endif
245 }
246 
sm_is_registered(void)247 static bool sm_is_registered(void)
248 {
249 	k_mutex_lock(&client.mutex, K_FOREVER);
250 	bool registered = (client.engine_state >= ENGINE_REGISTRATION_DONE &&
251 			   client.engine_state <= ENGINE_DEREGISTER_SENT);
252 
253 	k_mutex_unlock(&client.mutex);
254 	return registered;
255 }
256 
sm_is_suspended(void)257 static bool sm_is_suspended(void)
258 {
259 	k_mutex_lock(&client.mutex, K_FOREVER);
260 	bool suspended = (client.engine_state == ENGINE_SUSPENDED);
261 
262 	k_mutex_unlock(&client.mutex);
263 	return suspended;
264 }
265 
266 
get_sm_state(void)267 static uint8_t get_sm_state(void)
268 {
269 	k_mutex_lock(&client.mutex, K_FOREVER);
270 	uint8_t state = client.engine_state;
271 
272 	k_mutex_unlock(&client.mutex);
273 	return state;
274 }
275 
sm_handle_timeout_state(struct lwm2m_message * msg,enum sm_engine_state sm_state)276 static void sm_handle_timeout_state(struct lwm2m_message *msg,
277 				    enum sm_engine_state sm_state)
278 {
279 	k_mutex_lock(&client.mutex, K_FOREVER);
280 	enum lwm2m_rd_client_event event = LWM2M_RD_CLIENT_EVENT_NONE;
281 
282 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
283 	if (client.engine_state == ENGINE_BOOTSTRAP_REG_SENT) {
284 		event = LWM2M_RD_CLIENT_EVENT_BOOTSTRAP_REG_FAILURE;
285 	} else
286 #endif
287 	{
288 		if (client.engine_state == ENGINE_REGISTRATION_SENT) {
289 			event = LWM2M_RD_CLIENT_EVENT_REG_TIMEOUT;
290 		} else if (client.engine_state == ENGINE_UPDATE_SENT) {
291 			event = LWM2M_RD_CLIENT_EVENT_REG_TIMEOUT;
292 		} else if (client.engine_state == ENGINE_DEREGISTER_SENT) {
293 			event = LWM2M_RD_CLIENT_EVENT_DEREGISTER_FAILURE;
294 		} else {
295 			/* TODO: unknown timeout state */
296 		}
297 	}
298 
299 	set_sm_state(sm_state);
300 
301 	if (event > LWM2M_RD_CLIENT_EVENT_NONE && client.ctx->event_cb) {
302 		client.ctx->event_cb(client.ctx, event);
303 	}
304 	k_mutex_unlock(&client.mutex);
305 }
306 
sm_handle_failure_state(enum sm_engine_state sm_state)307 static void sm_handle_failure_state(enum sm_engine_state sm_state)
308 {
309 	k_mutex_lock(&client.mutex, K_FOREVER);
310 	enum lwm2m_rd_client_event event = LWM2M_RD_CLIENT_EVENT_NONE;
311 
312 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
313 	if (client.engine_state == ENGINE_BOOTSTRAP_REG_SENT) {
314 		event = LWM2M_RD_CLIENT_EVENT_BOOTSTRAP_REG_FAILURE;
315 	} else
316 #endif
317 	if (client.engine_state == ENGINE_REGISTRATION_SENT) {
318 		event = LWM2M_RD_CLIENT_EVENT_REGISTRATION_FAILURE;
319 	} else if (client.engine_state == ENGINE_UPDATE_SENT) {
320 		sm_handle_registration_update_failure();
321 		k_mutex_unlock(&client.mutex);
322 		return;
323 	} else if (client.engine_state == ENGINE_DEREGISTER_SENT) {
324 		event = LWM2M_RD_CLIENT_EVENT_DEREGISTER_FAILURE;
325 	}
326 
327 	lwm2m_engine_stop(client.ctx);
328 	set_sm_state(sm_state);
329 
330 	if (event > LWM2M_RD_CLIENT_EVENT_NONE && client.ctx->event_cb) {
331 		client.ctx->event_cb(client.ctx, event);
332 	}
333 	k_mutex_unlock(&client.mutex);
334 }
335 
336 /* force state machine restart */
socket_fault_cb(int error)337 static void socket_fault_cb(int error)
338 {
339 	LOG_ERR("RD Client socket error: %d", error);
340 
341 	if (sm_is_bootstrap()) {
342 		client.ctx->sec_obj_inst = -1;
343 		/* force full registration */
344 		client.last_update = 0;
345 	}
346 
347 	lwm2m_socket_close(client.ctx);
348 
349 	/* Network error state causes engine to re-register,
350 	 * so only trigger that state if we are not stopping the
351 	 * engine.
352 	 */
353 	if (client.engine_state > ENGINE_IDLE &&
354 		client.engine_state < ENGINE_SUSPENDED) {
355 		set_sm_state(ENGINE_NETWORK_ERROR);
356 	} else if (client.engine_state != ENGINE_SUSPENDED) {
357 		sm_handle_failure_state(ENGINE_IDLE);
358 	}
359 }
360 
361 /* force re-update with remote peer */
engine_trigger_update(bool update_objects)362 void engine_trigger_update(bool update_objects)
363 {
364 	k_mutex_lock(&client.mutex, K_FOREVER);
365 	if (client.engine_state < ENGINE_REGISTRATION_SENT ||
366 	    client.engine_state > ENGINE_UPDATE_SENT) {
367 		k_mutex_unlock(&client.mutex);
368 		return;
369 	}
370 
371 	client.trigger_update = true;
372 
373 	if (update_objects) {
374 		client.update_objects = true;
375 	}
376 	k_mutex_unlock(&client.mutex);
377 }
378 
code2str(uint8_t code)379 static inline const char *code2str(uint8_t code)
380 {
381 	switch (code) {
382 	case COAP_RESPONSE_CODE_BAD_REQUEST:
383 		return "Bad Request";
384 	case COAP_RESPONSE_CODE_FORBIDDEN:
385 		return "Forbidden";
386 	case COAP_RESPONSE_CODE_NOT_FOUND:
387 		return "Not Found";
388 	case COAP_RESPONSE_CODE_PRECONDITION_FAILED:
389 		return "Precondition Failed";
390 	default:
391 		break;
392 	}
393 
394 	return "Unknown";
395 }
396 
397 /* state machine reply callbacks */
398 
399 #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)400 static int do_bootstrap_reply_cb(const struct coap_packet *response,
401 				 struct coap_reply *reply,
402 				 const struct sockaddr *from)
403 {
404 	uint8_t code;
405 
406 	code = coap_header_get_code(response);
407 	LOG_DBG("Bootstrap callback (code:%u.%u)",
408 		COAP_RESPONSE_CODE_CLASS(code),
409 		COAP_RESPONSE_CODE_DETAIL(code));
410 
411 	if (code == COAP_RESPONSE_CODE_CHANGED) {
412 		LOG_INF("Bootstrap registration done!");
413 		set_sm_state(ENGINE_BOOTSTRAP_REG_DONE);
414 		return 0;
415 	}
416 
417 	LOG_ERR("Failed with code %u.%u (%s). Not Retrying.",
418 		COAP_RESPONSE_CODE_CLASS(code), COAP_RESPONSE_CODE_DETAIL(code),
419 		code2str(code));
420 
421 	sm_handle_failure_state(ENGINE_IDLE);
422 
423 	return 0;
424 }
425 
do_bootstrap_reg_timeout_cb(struct lwm2m_message * msg)426 static void do_bootstrap_reg_timeout_cb(struct lwm2m_message *msg)
427 {
428 	LOG_WRN("Bootstrap Timeout");
429 
430 	/* TODO:
431 	 * Look for the "next" BOOTSTRAP server entry in our security info
432 	 */
433 
434 	/* Restart from scratch */
435 	sm_handle_timeout_state(msg, ENGINE_INIT);
436 }
437 #endif
438 
engine_trigger_bootstrap(void)439 int engine_trigger_bootstrap(void)
440 {
441 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
442 	k_mutex_lock(&client.mutex, K_FOREVER);
443 
444 	if (client.use_bootstrap) {
445 		/* Bootstrap is not possible to trig */
446 		LOG_WRN("Bootstrap process ongoing");
447 		k_mutex_unlock(&client.mutex);
448 		return -EPERM;
449 	}
450 	LOG_INF("Server Initiated Bootstrap");
451 	/* Free ongoing possible message */
452 	rd_client_message_free();
453 	client.use_bootstrap = true;
454 	client.trigger_update = false;
455 	client.engine_state = ENGINE_INIT;
456 	k_mutex_unlock(&client.mutex);
457 	return 0;
458 #else
459 	return -EPERM;
460 #endif
461 }
do_registration_reply_cb(const struct coap_packet * response,struct coap_reply * reply,const struct sockaddr * from)462 static int do_registration_reply_cb(const struct coap_packet *response,
463 				    struct coap_reply *reply,
464 				    const struct sockaddr *from)
465 {
466 	struct coap_option options[2];
467 	uint8_t code;
468 	int ret = -EINVAL;
469 
470 	code = coap_header_get_code(response);
471 	LOG_DBG("Registration callback (code:%u.%u)",
472 		COAP_RESPONSE_CODE_CLASS(code),
473 		COAP_RESPONSE_CODE_DETAIL(code));
474 
475 	/* check state and possibly set registration to done */
476 	if (code == COAP_RESPONSE_CODE_CREATED) {
477 		ret = coap_find_options(response, COAP_OPTION_LOCATION_PATH,
478 					options, 2);
479 		if (ret < 2) {
480 			LOG_ERR("Unexpected endpoint data returned. ret = %d", ret);
481 			ret = -EINVAL;
482 			goto fail;
483 		}
484 
485 		/* option[0] should be "rd" */
486 
487 		if (options[1].len + 1 > sizeof(client.server_ep)) {
488 			LOG_ERR("Unexpected length of query: "
489 				    "%u (expected %zu)\n",
490 				    options[1].len,
491 				    sizeof(client.server_ep));
492 			ret = -EINVAL;
493 			goto fail;
494 		}
495 
496 		/* remember the last reg time */
497 		client.last_update = k_uptime_get();
498 
499 		memcpy(client.server_ep, options[1].value,
500 		       options[1].len);
501 		client.server_ep[options[1].len] = '\0';
502 		set_sm_state(ENGINE_REGISTRATION_DONE);
503 		LOG_INF("Registration Done (EP='%s')",
504 			client.server_ep);
505 
506 		return 0;
507 	} else if (code == COAP_RESPONSE_CODE_CONTINUE) {
508 #if defined(CONFIG_LWM2M_COAP_BLOCK_TRANSFER)
509 		return 0;
510 #else
511 		LOG_ERR("Response code CONTINUE not supported");
512 #endif
513 	}
514 
515 	LOG_ERR("Failed with code %u.%u (%s). Not Retrying.",
516 		COAP_RESPONSE_CODE_CLASS(code), COAP_RESPONSE_CODE_DETAIL(code),
517 		code2str(code));
518 fail:
519 	sm_handle_failure_state(ENGINE_IDLE);
520 
521 	return ret;
522 }
523 
do_registration_timeout_cb(struct lwm2m_message * msg)524 static void do_registration_timeout_cb(struct lwm2m_message *msg)
525 {
526 	LOG_WRN("Registration Timeout");
527 
528 	/* Restart from scratch */
529 	sm_handle_timeout_state(msg, ENGINE_INIT);
530 }
531 
do_update_reply_cb(const struct coap_packet * response,struct coap_reply * reply,const struct sockaddr * from)532 static int do_update_reply_cb(const struct coap_packet *response,
533 			      struct coap_reply *reply,
534 			      const struct sockaddr *from)
535 {
536 	uint8_t code;
537 
538 	code = coap_header_get_code(response);
539 	LOG_INF("Update callback (code:%u.%u)",
540 		COAP_RESPONSE_CODE_CLASS(code),
541 		COAP_RESPONSE_CODE_DETAIL(code));
542 
543 	/* If NOT_FOUND just continue on */
544 	if ((code == COAP_RESPONSE_CODE_CHANGED) ||
545 	    (code == COAP_RESPONSE_CODE_CREATED)) {
546 		/* remember the last reg time */
547 		client.last_update = k_uptime_get();
548 		set_sm_state(ENGINE_REGISTRATION_DONE);
549 		LOG_INF("Update Done");
550 		return 0;
551 	}
552 
553 	LOG_ERR("Failed with code %u.%u (%s). Retrying registration.",
554 		COAP_RESPONSE_CODE_CLASS(code), COAP_RESPONSE_CODE_DETAIL(code),
555 		code2str(code));
556 
557 	sm_handle_failure_state(ENGINE_DO_REGISTRATION);
558 
559 	return 0;
560 }
561 
do_update_timeout_cb(struct lwm2m_message * msg)562 static void do_update_timeout_cb(struct lwm2m_message *msg)
563 {
564 	LOG_WRN("Registration Update Timeout");
565 
566 	if (client.ctx->sock_fd > -1) {
567 		client.close_socket = true;
568 	}
569 	/* Re-do registration */
570 	sm_handle_timeout_state(msg, ENGINE_DO_REGISTRATION);
571 }
572 
do_deregister_reply_cb(const struct coap_packet * response,struct coap_reply * reply,const struct sockaddr * from)573 static int do_deregister_reply_cb(const struct coap_packet *response,
574 				  struct coap_reply *reply,
575 				  const struct sockaddr *from)
576 {
577 	uint8_t code;
578 
579 	code = coap_header_get_code(response);
580 	LOG_DBG("Deregister callback (code:%u.%u)",
581 		COAP_RESPONSE_CODE_CLASS(code),
582 		COAP_RESPONSE_CODE_DETAIL(code));
583 
584 	if (code == COAP_RESPONSE_CODE_DELETED) {
585 		LOG_INF("Deregistration success");
586 		set_sm_state(ENGINE_DEREGISTERED);
587 		return 0;
588 	}
589 
590 	LOG_ERR("Failed with code %u.%u (%s). Not Retrying",
591 		COAP_RESPONSE_CODE_CLASS(code), COAP_RESPONSE_CODE_DETAIL(code),
592 		code2str(code));
593 
594 	sm_handle_failure_state(ENGINE_IDLE);
595 
596 	return 0;
597 }
598 
do_deregister_timeout_cb(struct lwm2m_message * msg)599 static void do_deregister_timeout_cb(struct lwm2m_message *msg)
600 {
601 	LOG_WRN("De-Registration Timeout");
602 
603 	sm_handle_timeout_state(msg, ENGINE_IDLE);
604 }
605 
sm_bootstrap_verify(bool bootstrap_server,int sec_obj_inst)606 static bool sm_bootstrap_verify(bool bootstrap_server, int sec_obj_inst)
607 {
608 	bool bootstrap;
609 	int ret;
610 
611 	ret = lwm2m_get_bool(&LWM2M_OBJ(0, sec_obj_inst, 1), &bootstrap);
612 	if (ret < 0) {
613 		LOG_WRN("Failed to check bootstrap, err %d", ret);
614 		return false;
615 	}
616 
617 	if (bootstrap == bootstrap_server) {
618 		return true;
619 	} else {
620 		return false;
621 	}
622 }
623 
sm_update_lifetime(int srv_obj_inst,uint32_t * lifetime)624 static bool sm_update_lifetime(int srv_obj_inst, uint32_t *lifetime)
625 {
626 	uint32_t new_lifetime;
627 
628 	if (lwm2m_get_u32(&LWM2M_OBJ(1, srv_obj_inst, 1), &new_lifetime) < 0) {
629 		new_lifetime = CONFIG_LWM2M_ENGINE_DEFAULT_LIFETIME;
630 		LOG_INF("Using default lifetime: %u", new_lifetime);
631 	}
632 
633 	if (new_lifetime < CONFIG_LWM2M_ENGINE_DEFAULT_LIFETIME) {
634 		new_lifetime = CONFIG_LWM2M_ENGINE_DEFAULT_LIFETIME;
635 		lwm2m_set_u32(&LWM2M_OBJ(1, srv_obj_inst, 1), new_lifetime);
636 		LOG_INF("Overwrite a server lifetime with default");
637 	}
638 
639 	if (new_lifetime != *lifetime) {
640 		*lifetime = new_lifetime;
641 		return true;
642 	}
643 
644 	return false;
645 }
646 
sm_select_server_inst(int sec_obj_inst,int * srv_obj_inst,uint32_t * lifetime)647 static int sm_select_server_inst(int sec_obj_inst, int *srv_obj_inst,
648 				 uint32_t *lifetime)
649 {
650 	uint16_t server_id;
651 	int ret, obj_inst_id;
652 
653 	ret = lwm2m_get_u16(&LWM2M_OBJ(0, sec_obj_inst, 10), &server_id);
654 	if (ret < 0) {
655 		LOG_WRN("Failed to obtain Short Server ID, err %d", ret);
656 		return -EINVAL;
657 	}
658 
659 	obj_inst_id = lwm2m_server_short_id_to_inst(server_id);
660 	if (obj_inst_id < 0) {
661 		LOG_WRN("Failed to obtain Server Object instance, err %d",
662 			obj_inst_id);
663 		return -EINVAL;
664 	}
665 
666 	sm_update_lifetime(obj_inst_id, lifetime);
667 	*srv_obj_inst = obj_inst_id;
668 
669 	return 0;
670 }
671 
sm_select_security_inst(bool bootstrap_server,int * sec_obj_inst)672 static int sm_select_security_inst(bool bootstrap_server, int *sec_obj_inst)
673 {
674 	int i, obj_inst_id = -1;
675 
676 	/* lookup existing index */
677 	i = lwm2m_security_inst_id_to_index(*sec_obj_inst);
678 	if (i >= 0 && sm_bootstrap_verify(bootstrap_server, *sec_obj_inst)) {
679 		return 0;
680 	}
681 
682 	*sec_obj_inst = -1;
683 
684 	/* Iterate over all instances to find the correct one. */
685 	for (i = 0; i < CONFIG_LWM2M_SECURITY_INSTANCE_COUNT; i++) {
686 		obj_inst_id = lwm2m_security_index_to_inst_id(i);
687 		if (obj_inst_id < 0) {
688 			LOG_WRN("Failed to get inst id for %d", i);
689 			continue;
690 		}
691 
692 		if (sm_bootstrap_verify(bootstrap_server, obj_inst_id)) {
693 			*sec_obj_inst = obj_inst_id;
694 			return 0;
695 		}
696 	}
697 
698 	LOG_WRN("sec_obj_inst: No matching servers found.");
699 
700 	return -ENOENT;
701 }
702 
703 /* state machine step functions */
704 
sm_do_init(void)705 static int sm_do_init(void)
706 {
707 	lwm2m_engine_stop(client.ctx);
708 	client.ctx->sec_obj_inst = -1;
709 	client.ctx->srv_obj_inst = -1;
710 	client.trigger_update = false;
711 	client.lifetime = 0U;
712 	client.retries = 0U;
713 	client.last_update = 0U;
714 	client.close_socket = false;
715 
716 	/* Do bootstrap or registration */
717 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
718 	if (client.use_bootstrap) {
719 		set_sm_state(ENGINE_DO_BOOTSTRAP_REG);
720 	} else {
721 		set_sm_state(ENGINE_DO_REGISTRATION);
722 	}
723 #else
724 	set_sm_state(ENGINE_DO_REGISTRATION);
725 #endif
726 	return 0;
727 }
728 
729 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
sm_send_bootstrap_registration(void)730 static int sm_send_bootstrap_registration(void)
731 {
732 	struct lwm2m_message *msg;
733 	int ret;
734 
735 	msg = rd_get_message();
736 	if (!msg) {
737 		LOG_ERR("Unable to get a lwm2m message!");
738 		return -ENOMEM;
739 	}
740 
741 	msg->type = COAP_TYPE_CON;
742 	msg->code = COAP_METHOD_POST;
743 	msg->mid = coap_next_id();
744 	msg->tkl = LWM2M_MSG_TOKEN_GENERATE_NEW;
745 	msg->reply_cb = do_bootstrap_reply_cb;
746 	msg->message_timeout_cb = do_bootstrap_reg_timeout_cb;
747 
748 	ret = lwm2m_init_message(msg);
749 	if (ret) {
750 		goto cleanup;
751 	}
752 
753 	/* TODO: handle return error */
754 	coap_packet_append_option(&msg->cpkt, COAP_OPTION_URI_PATH,
755 				  "bs", strlen("bs"));
756 
757 	snprintk(query_buffer, sizeof(query_buffer) - 1, "ep=%s",
758 		 client.ep_name);
759 	/* TODO: handle return error */
760 	coap_packet_append_option(&msg->cpkt, COAP_OPTION_URI_QUERY,
761 				  query_buffer, strlen(query_buffer));
762 
763 
764 	if (IS_ENABLED(CONFIG_LWM2M_VERSION_1_1)) {
765 		int pct = LWM2M_FORMAT_OMA_TLV;
766 
767 		if (IS_ENABLED(CONFIG_LWM2M_RW_SENML_CBOR_SUPPORT)) {
768 			pct = LWM2M_FORMAT_APP_SENML_CBOR;
769 		} else if (IS_ENABLED(CONFIG_LWM2M_RW_SENML_JSON_SUPPORT)) {
770 			pct = LWM2M_FORMAT_APP_SEML_JSON;
771 		}
772 
773 		snprintk(query_buffer, sizeof(query_buffer) - 1, "pct=%d", pct);
774 
775 		coap_packet_append_option(&msg->cpkt, COAP_OPTION_URI_QUERY,
776 				  query_buffer, strlen(query_buffer));
777 	}
778 
779 	/* log the bootstrap attempt */
780 	LOG_DBG("Register ID with bootstrap server as '%s'",
781 		query_buffer);
782 
783 	lwm2m_send_message_async(msg);
784 
785 	return 0;
786 
787 cleanup:
788 	lwm2m_reset_message(msg, true);
789 	return ret;
790 }
791 
sm_do_bootstrap_reg(void)792 static int sm_do_bootstrap_reg(void)
793 {
794 	int ret;
795 
796 	/* clear out existing connection data */
797 	if (client.ctx->sock_fd > -1) {
798 		lwm2m_engine_stop(client.ctx);
799 	}
800 
801 	client.ctx->bootstrap_mode = true;
802 	ret = sm_select_security_inst(client.ctx->bootstrap_mode,
803 				      &client.ctx->sec_obj_inst);
804 	if (ret < 0) {
805 		/* no bootstrap server found, let's move to registration */
806 		LOG_WRN("Bootstrap server not found! Try normal registration.");
807 		set_sm_state(ENGINE_DO_REGISTRATION);
808 		return ret;
809 	}
810 
811 	LOG_INF("Bootstrap started with endpoint '%s' with client lifetime %d",
812 		client.ep_name, client.lifetime);
813 
814 	ret = lwm2m_engine_start(client.ctx);
815 	if (ret < 0) {
816 		LOG_ERR("Cannot init LWM2M engine (%d)", ret);
817 		set_sm_state(ENGINE_NETWORK_ERROR);
818 		return ret;
819 	}
820 
821 	ret = sm_send_bootstrap_registration();
822 	if (!ret) {
823 		set_sm_state(ENGINE_BOOTSTRAP_REG_SENT);
824 	} else {
825 		LOG_ERR("Bootstrap registration err: %d", ret);
826 		set_sm_state(ENGINE_NETWORK_ERROR);
827 	}
828 
829 	return ret;
830 }
831 
engine_bootstrap_finish(void)832 void engine_bootstrap_finish(void)
833 {
834 	LOG_INF("Bootstrap data transfer done!");
835 	set_sm_state(ENGINE_BOOTSTRAP_TRANS_DONE);
836 }
837 
sm_bootstrap_trans_done(void)838 static int sm_bootstrap_trans_done(void)
839 {
840 	/* close down context resources */
841 	lwm2m_engine_stop(client.ctx);
842 
843 	/* reset security object instance */
844 	client.ctx->sec_obj_inst = -1;
845 	client.use_bootstrap = false;
846 
847 	set_sm_state(ENGINE_DO_REGISTRATION);
848 
849 	return 0;
850 }
851 #endif
852 
sm_send_registration(bool send_obj_support_data,coap_reply_t reply_cb,lwm2m_message_timeout_cb_t timeout_cb)853 static int sm_send_registration(bool send_obj_support_data,
854 				coap_reply_t reply_cb,
855 				lwm2m_message_timeout_cb_t timeout_cb)
856 {
857 	struct lwm2m_message *msg;
858 	int ret;
859 	char binding[CLIENT_BINDING_LEN];
860 	char queue[CLIENT_QUEUE_LEN];
861 
862 	msg = rd_get_message();
863 	if (!msg) {
864 		LOG_ERR("Unable to get a lwm2m message!");
865 		return -ENOMEM;
866 	}
867 
868 	msg->type = COAP_TYPE_CON;
869 	msg->code = COAP_METHOD_POST;
870 	msg->mid = coap_next_id();
871 	msg->tkl = LWM2M_MSG_TOKEN_GENERATE_NEW;
872 	msg->reply_cb = reply_cb;
873 	msg->message_timeout_cb = timeout_cb;
874 
875 	ret = lwm2m_init_message(msg);
876 	if (ret) {
877 		goto cleanup;
878 	}
879 
880 	ret = coap_packet_append_option(&msg->cpkt, COAP_OPTION_URI_PATH,
881 					LWM2M_RD_CLIENT_URI,
882 					strlen(LWM2M_RD_CLIENT_URI));
883 	if (ret < 0) {
884 		goto cleanup;
885 	}
886 
887 	if (sm_is_registered()) {
888 		ret = coap_packet_append_option(
889 			&msg->cpkt, COAP_OPTION_URI_PATH,
890 			client.server_ep, strlen(client.server_ep));
891 		if (ret < 0) {
892 			goto cleanup;
893 		}
894 	}
895 
896 	if (send_obj_support_data) {
897 		ret = coap_append_option_int(
898 			&msg->cpkt, COAP_OPTION_CONTENT_FORMAT,
899 			LWM2M_FORMAT_APP_LINK_FORMAT);
900 		if (ret < 0) {
901 			goto cleanup;
902 		}
903 	}
904 
905 	if (!sm_is_registered()) {
906 		snprintk(query_buffer, sizeof(query_buffer) - 1,
907 			"lwm2m=%s", LWM2M_PROTOCOL_VERSION_STRING);
908 		ret = coap_packet_append_option(
909 			&msg->cpkt, COAP_OPTION_URI_QUERY,
910 			query_buffer, strlen(query_buffer));
911 		if (ret < 0) {
912 			goto cleanup;
913 		}
914 
915 		snprintk(query_buffer, sizeof(query_buffer) - 1,
916 			 "ep=%s", client.ep_name);
917 		ret = coap_packet_append_option(
918 			&msg->cpkt, COAP_OPTION_URI_QUERY,
919 			query_buffer, strlen(query_buffer));
920 		if (ret < 0) {
921 			goto cleanup;
922 		}
923 	}
924 
925 	/* Send lifetime only if changed or on initial registration.*/
926 	if (sm_update_lifetime(client.ctx->srv_obj_inst, &client.lifetime) ||
927 	    !sm_is_registered()) {
928 		snprintk(query_buffer, sizeof(query_buffer) - 1,
929 			 "lt=%d", client.lifetime);
930 		ret = coap_packet_append_option(
931 			&msg->cpkt, COAP_OPTION_URI_QUERY,
932 			query_buffer, strlen(query_buffer));
933 		if (ret < 0) {
934 			goto cleanup;
935 		}
936 	}
937 
938 	lwm2m_engine_get_binding(binding);
939 	lwm2m_engine_get_queue_mode(queue);
940 	/* UDP is a default binding, no need to add option if UDP without queue is used. */
941 	if ((!sm_is_registered() && (strcmp(binding, "U") != 0 || strcmp(queue, "Q") == 0))) {
942 		snprintk(query_buffer, sizeof(query_buffer) - 1,
943 			 "b=%s", binding);
944 
945 		ret = coap_packet_append_option(
946 			&msg->cpkt, COAP_OPTION_URI_QUERY,
947 			query_buffer, strlen(query_buffer));
948 		if (ret < 0) {
949 			goto cleanup;
950 		}
951 
952 #if CONFIG_LWM2M_VERSION_1_1
953 		/* In LwM2M 1.1, queue mode is a separate parameter */
954 		uint16_t len = strlen(queue);
955 
956 		if (len) {
957 			ret = coap_packet_append_option(
958 				&msg->cpkt, COAP_OPTION_URI_QUERY,
959 				queue, len);
960 			if (ret < 0) {
961 				goto cleanup;
962 			}
963 		}
964 #endif
965 	}
966 
967 	if (send_obj_support_data) {
968 		ret = coap_packet_append_payload_marker(&msg->cpkt);
969 		if (ret < 0) {
970 			goto cleanup;
971 		}
972 
973 		msg->out.out_cpkt = &msg->cpkt;
974 		msg->out.writer = &link_format_writer;
975 
976 		ret = do_register_op_link_format(msg);
977 		if (ret < 0) {
978 			goto cleanup;
979 		}
980 	}
981 
982 	lwm2m_send_message_async(msg);
983 
984 	/* log the registration attempt */
985 	LOG_DBG("registration sent [%s]",
986 		lwm2m_sprint_ip_addr(&client.ctx->remote_addr));
987 
988 	return 0;
989 
990 cleanup:
991 	LOG_ERR("error %d when sending registration message", ret);
992 	lwm2m_reset_message(msg, true);
993 	return ret;
994 }
995 
sm_handle_registration_update_failure(void)996 static void sm_handle_registration_update_failure(void)
997 {
998 	k_mutex_lock(&client.mutex, K_FOREVER);
999 	LOG_WRN("Registration Update fail -> trigger full registration");
1000 	client.engine_state = ENGINE_SEND_REGISTRATION;
1001 	lwm2m_engine_context_close(client.ctx);
1002 	k_mutex_unlock(&client.mutex);
1003 }
1004 
sm_send_registration_msg(void)1005 static int sm_send_registration_msg(void)
1006 {
1007 	int ret;
1008 
1009 	ret = sm_send_registration(true,
1010 				   do_registration_reply_cb,
1011 				   do_registration_timeout_cb);
1012 	if (!ret) {
1013 		set_sm_state(ENGINE_REGISTRATION_SENT);
1014 	} else {
1015 		LOG_ERR("Registration err: %d", ret);
1016 		set_sm_state(ENGINE_NETWORK_ERROR);
1017 	}
1018 
1019 	return ret;
1020 }
1021 
sm_do_registration(void)1022 static int sm_do_registration(void)
1023 {
1024 	int ret = 0;
1025 
1026 	if (client.ctx->connection_suspended) {
1027 		if (lwm2m_engine_connection_resume(client.ctx)) {
1028 			lwm2m_engine_context_close(client.ctx);
1029 			/* perform full registration */
1030 			set_sm_state(ENGINE_DO_REGISTRATION);
1031 			return 0;
1032 		}
1033 
1034 	} else {
1035 		/* clear out existing connection data */
1036 		if (client.ctx->sock_fd > -1) {
1037 			if (client.close_socket) {
1038 				/* Clear old socket connection */
1039 				client.close_socket = false;
1040 				lwm2m_engine_stop(client.ctx);
1041 			} else {
1042 				lwm2m_engine_context_close(client.ctx);
1043 			}
1044 		}
1045 
1046 		client.last_update = 0;
1047 
1048 		client.ctx->bootstrap_mode = false;
1049 		ret = sm_select_security_inst(client.ctx->bootstrap_mode,
1050 					      &client.ctx->sec_obj_inst);
1051 		if (ret < 0) {
1052 			LOG_ERR("Unable to find a valid security instance.");
1053 			set_sm_state(ENGINE_INIT);
1054 			return -EINVAL;
1055 		}
1056 
1057 		ret = sm_select_server_inst(client.ctx->sec_obj_inst,
1058 					    &client.ctx->srv_obj_inst,
1059 					    &client.lifetime);
1060 		if (ret < 0) {
1061 			LOG_ERR("Unable to find a valid server instance.");
1062 			set_sm_state(ENGINE_INIT);
1063 			return -EINVAL;
1064 		}
1065 
1066 		LOG_INF("RD Client started with endpoint '%s' with client lifetime %d",
1067 			client.ep_name, client.lifetime);
1068 
1069 		ret = lwm2m_engine_start(client.ctx);
1070 		if (ret < 0) {
1071 			LOG_ERR("Cannot init LWM2M engine (%d)", ret);
1072 			set_sm_state(ENGINE_NETWORK_ERROR);
1073 			return ret;
1074 		}
1075 	}
1076 
1077 	ret = sm_send_registration_msg();
1078 
1079 	return ret;
1080 }
1081 
sm_registration_done(void)1082 static int sm_registration_done(void)
1083 {
1084 	k_mutex_lock(&client.mutex, K_FOREVER);
1085 	int ret = 0;
1086 
1087 	/*
1088 	 * check for lifetime seconds - SECONDS_TO_UPDATE_EARLY
1089 	 * so that we can update early and avoid lifetime timeout
1090 	 */
1091 	if (sm_is_registered() &&
1092 	    (client.trigger_update ||
1093 	     ((client.lifetime - SECONDS_TO_UPDATE_EARLY) <=
1094 	      (k_uptime_get() - client.last_update) / 1000))) {
1095 		set_sm_state(ENGINE_UPDATE_REGISTRATION);
1096 	} else if (IS_ENABLED(CONFIG_LWM2M_QUEUE_MODE_ENABLED) &&
1097 	    (client.engine_state != ENGINE_REGISTRATION_DONE_RX_OFF) &&
1098 	    (((k_uptime_get() - client.last_tx) / 1000) >=
1099 	     CONFIG_LWM2M_QUEUE_MODE_UPTIME)) {
1100 		set_sm_state(ENGINE_REGISTRATION_DONE_RX_OFF);
1101 	}
1102 	k_mutex_unlock(&client.mutex);
1103 	return ret;
1104 }
1105 
update_registration(void)1106 static int update_registration(void)
1107 {
1108 	int ret;
1109 	bool update_objects;
1110 
1111 	update_objects = client.update_objects;
1112 	client.trigger_update = false;
1113 	client.update_objects = false;
1114 
1115 	ret = lwm2m_engine_connection_resume(client.ctx);
1116 	if (ret) {
1117 		return ret;
1118 	}
1119 
1120 	ret = sm_send_registration(update_objects,
1121 				   do_update_reply_cb,
1122 				   do_update_timeout_cb);
1123 	if (ret) {
1124 		LOG_ERR("Registration update err: %d", ret);
1125 		return ret;
1126 	}
1127 
1128 	return 0;
1129 }
1130 
sm_update_registration(void)1131 static int sm_update_registration(void)
1132 {
1133 	int ret;
1134 
1135 	ret = update_registration();
1136 	if (ret) {
1137 		LOG_ERR("Failed to update registration. Falling back to full registration");
1138 
1139 		lwm2m_engine_stop(client.ctx);
1140 		/* perform full registration */
1141 		set_sm_state(ENGINE_DO_REGISTRATION);
1142 		return ret;
1143 	}
1144 
1145 	set_sm_state(ENGINE_UPDATE_SENT);
1146 
1147 	return 0;
1148 }
1149 
sm_do_deregister(void)1150 static int sm_do_deregister(void)
1151 {
1152 	struct lwm2m_message *msg;
1153 	int ret;
1154 
1155 	if (lwm2m_engine_connection_resume(client.ctx)) {
1156 		lwm2m_engine_context_close(client.ctx);
1157 		/* Connection failed, enter directly to deregistered state */
1158 		set_sm_state(ENGINE_DEREGISTERED);
1159 		return 0;
1160 	}
1161 
1162 	msg = rd_get_message();
1163 	if (!msg) {
1164 		LOG_ERR("Unable to get a lwm2m message!");
1165 		ret = -ENOMEM;
1166 		goto close_ctx;
1167 	}
1168 
1169 	msg->type = COAP_TYPE_CON;
1170 	msg->code = COAP_METHOD_DELETE;
1171 	msg->mid = coap_next_id();
1172 	msg->tkl = LWM2M_MSG_TOKEN_GENERATE_NEW;
1173 	msg->reply_cb = do_deregister_reply_cb;
1174 	msg->message_timeout_cb = do_deregister_timeout_cb;
1175 
1176 	ret = lwm2m_init_message(msg);
1177 	if (ret) {
1178 		goto cleanup;
1179 	}
1180 
1181 	ret = coap_packet_append_option(&msg->cpkt, COAP_OPTION_URI_PATH,
1182 					LWM2M_RD_CLIENT_URI,
1183 					strlen(LWM2M_RD_CLIENT_URI));
1184 	if (ret < 0) {
1185 		LOG_ERR("Failed to encode URI path option (err:%d).", ret);
1186 		goto cleanup;
1187 	}
1188 
1189 	/* include server endpoint in URI PATH */
1190 	ret = coap_packet_append_option(&msg->cpkt, COAP_OPTION_URI_PATH,
1191 					client.server_ep,
1192 					strlen(client.server_ep));
1193 	if (ret < 0) {
1194 		LOG_ERR("Failed to encode URI path option (err:%d).", ret);
1195 		goto cleanup;
1196 	}
1197 
1198 	LOG_INF("Deregister from '%s'", client.server_ep);
1199 
1200 	lwm2m_send_message_async(msg);
1201 
1202 	set_sm_state(ENGINE_DEREGISTER_SENT);
1203 	return 0;
1204 
1205 cleanup:
1206 	lwm2m_reset_message(msg, true);
1207 close_ctx:
1208 	lwm2m_engine_stop(client.ctx);
1209 	set_sm_state(ENGINE_DEREGISTERED);
1210 	return ret;
1211 }
1212 
sm_do_network_error(void)1213 static void sm_do_network_error(void)
1214 {
1215 	int err;
1216 
1217 	if (--client.retry_delay > 0) {
1218 		return;
1219 	}
1220 
1221 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
1222 	if (client.ctx->bootstrap_mode) {
1223 		lwm2m_engine_context_close(client.ctx);
1224 		set_sm_state(ENGINE_DO_BOOTSTRAP_REG);
1225 		return;
1226 	}
1227 #endif
1228 
1229 	if (!client.last_update || (k_uptime_get() - client.last_update) / 1000 > client.lifetime) {
1230 		/* do full registration as there is no active registration or lifetime exceeded */
1231 		lwm2m_engine_context_close(client.ctx);
1232 		set_sm_state(ENGINE_DO_REGISTRATION);
1233 		return;
1234 	}
1235 
1236 	err = lwm2m_socket_start(client.ctx);
1237 	if (err) {
1238 		LOG_ERR("Failed to start socket %d", err);
1239 		/*
1240 		 * keep this state until lifetime/retry count exceeds. Renew
1241 		 * sm state to set retry_delay etc ...
1242 		 */
1243 		set_sm_state(ENGINE_NETWORK_ERROR);
1244 		return;
1245 	}
1246 
1247 	set_sm_state(ENGINE_UPDATE_REGISTRATION);
1248 }
1249 
lwm2m_rd_client_service(struct k_work * work)1250 static void lwm2m_rd_client_service(struct k_work *work)
1251 {
1252 	k_mutex_lock(&client.mutex, K_FOREVER);
1253 
1254 	if (client.ctx) {
1255 		switch (get_sm_state()) {
1256 		case ENGINE_IDLE:
1257 			if (client.ctx->sock_fd > -1) {
1258 				lwm2m_engine_stop(client.ctx);
1259 			}
1260 			rd_client_message_free();
1261 			break;
1262 
1263 		case ENGINE_INIT:
1264 			sm_do_init();
1265 			break;
1266 
1267 		case ENGINE_SUSPENDED:
1268 			break;
1269 
1270 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
1271 		case ENGINE_DO_BOOTSTRAP_REG:
1272 			sm_do_bootstrap_reg();
1273 			break;
1274 
1275 		case ENGINE_BOOTSTRAP_REG_SENT:
1276 			/* wait for bootstrap registration done */
1277 			break;
1278 
1279 		case ENGINE_BOOTSTRAP_REG_DONE:
1280 			/* wait for transfer done */
1281 			break;
1282 
1283 		case ENGINE_BOOTSTRAP_TRANS_DONE:
1284 			sm_bootstrap_trans_done();
1285 			break;
1286 #endif
1287 
1288 		case ENGINE_DO_REGISTRATION:
1289 			sm_do_registration();
1290 			break;
1291 
1292 		case ENGINE_SEND_REGISTRATION:
1293 			sm_send_registration_msg();
1294 			break;
1295 
1296 		case ENGINE_REGISTRATION_SENT:
1297 			/* wait registration to be done or timeout */
1298 			break;
1299 
1300 		case ENGINE_REGISTRATION_DONE:
1301 		case ENGINE_REGISTRATION_DONE_RX_OFF:
1302 			sm_registration_done();
1303 			break;
1304 
1305 		case ENGINE_UPDATE_REGISTRATION:
1306 			sm_update_registration();
1307 			break;
1308 
1309 		case ENGINE_UPDATE_SENT:
1310 			/* wait update to be done or abort */
1311 			break;
1312 
1313 		case ENGINE_DEREGISTER:
1314 			sm_do_deregister();
1315 			break;
1316 
1317 		case ENGINE_DEREGISTER_SENT:
1318 			/* wait for deregister to be done or reset */
1319 			break;
1320 
1321 		case ENGINE_DEREGISTERED:
1322 			lwm2m_engine_stop(client.ctx);
1323 			set_sm_state(ENGINE_IDLE);
1324 			break;
1325 
1326 		case ENGINE_NETWORK_ERROR:
1327 			sm_do_network_error();
1328 			break;
1329 
1330 		default:
1331 			LOG_ERR("Unhandled state: %d", get_sm_state());
1332 
1333 		}
1334 	}
1335 
1336 	k_mutex_unlock(&client.mutex);
1337 }
1338 
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)1339 int lwm2m_rd_client_start(struct lwm2m_ctx *client_ctx, const char *ep_name,
1340 			   uint32_t flags, lwm2m_ctx_event_cb_t event_cb,
1341 			   lwm2m_observe_cb_t observe_cb)
1342 {
1343 	k_mutex_lock(&client.mutex, K_FOREVER);
1344 
1345 	if (!IS_ENABLED(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP) &&
1346 	    (flags & LWM2M_RD_CLIENT_FLAG_BOOTSTRAP)) {
1347 		LOG_ERR("Bootstrap support is disabled. Please enable "
1348 			"CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP.");
1349 
1350 		k_mutex_unlock(&client.mutex);
1351 		return -ENOTSUP;
1352 	}
1353 
1354 	/* Check client idle state or socket is still active */
1355 
1356 	if (client.ctx && (client.engine_state != ENGINE_IDLE || client.ctx->sock_fd != -1)) {
1357 		LOG_WRN("Client is already running. state %d ", client.engine_state);
1358 		k_mutex_unlock(&client.mutex);
1359 		return -EINPROGRESS;
1360 	}
1361 
1362 	/* Init Context */
1363 	lwm2m_engine_context_init(client_ctx);
1364 
1365 	client.ctx = client_ctx;
1366 	client.ctx->sock_fd = -1;
1367 	client.ctx->fault_cb = socket_fault_cb;
1368 	client.ctx->observe_cb = observe_cb;
1369 	client.ctx->event_cb = event_cb;
1370 	client.use_bootstrap = flags & LWM2M_RD_CLIENT_FLAG_BOOTSTRAP;
1371 
1372 	set_sm_state(ENGINE_INIT);
1373 	strncpy(client.ep_name, ep_name, CLIENT_EP_LEN - 1);
1374 	client.ep_name[CLIENT_EP_LEN - 1] = '\0';
1375 	LOG_INF("Start LWM2M Client: %s", client.ep_name);
1376 
1377 	k_mutex_unlock(&client.mutex);
1378 	return 0;
1379 }
1380 
lwm2m_rd_client_stop(struct lwm2m_ctx * client_ctx,lwm2m_ctx_event_cb_t event_cb,bool deregister)1381 int lwm2m_rd_client_stop(struct lwm2m_ctx *client_ctx,
1382 			   lwm2m_ctx_event_cb_t event_cb, bool deregister)
1383 {
1384 	k_mutex_lock(&client.mutex, K_FOREVER);
1385 
1386 	if (client.ctx != client_ctx) {
1387 		k_mutex_unlock(&client.mutex);
1388 		LOG_WRN("Cannot stop. Wrong context");
1389 		return -EPERM;
1390 	}
1391 
1392 	client.ctx->event_cb = event_cb;
1393 	rd_client_message_free();
1394 
1395 	if (sm_is_registered() && deregister) {
1396 		set_sm_state(ENGINE_DEREGISTER);
1397 	} else {
1398 		set_sm_state(ENGINE_DEREGISTERED);
1399 	}
1400 
1401 	LOG_INF("Stop LWM2M Client: %s", client.ep_name);
1402 
1403 	k_mutex_unlock(&client.mutex);
1404 
1405 	return 0;
1406 }
1407 
lwm2m_rd_client_pause(void)1408 int lwm2m_rd_client_pause(void)
1409 {
1410 	enum lwm2m_rd_client_event event = LWM2M_RD_CLIENT_EVENT_ENGINE_SUSPENDED;
1411 
1412 	k_mutex_lock(&client.mutex, K_FOREVER);
1413 
1414 	if (!client.ctx) {
1415 		k_mutex_unlock(&client.mutex);
1416 		LOG_ERR("Cannot pause. No context");
1417 		return -EPERM;
1418 	} else if (client.engine_state == ENGINE_SUSPENDED) {
1419 		k_mutex_unlock(&client.mutex);
1420 		LOG_ERR("LwM2M client already suspended");
1421 		return 0;
1422 	}
1423 
1424 	LOG_INF("Suspend client");
1425 	if (!client.ctx->connection_suspended && client.ctx->event_cb) {
1426 		client.ctx->event_cb(client.ctx, event);
1427 	}
1428 
1429 	suspended_client_state = get_sm_state();
1430 	client.engine_state = ENGINE_SUSPENDED;
1431 
1432 	k_mutex_unlock(&client.mutex);
1433 
1434 	return 0;
1435 }
1436 
lwm2m_rd_client_resume(void)1437 int lwm2m_rd_client_resume(void)
1438 {
1439 	int ret;
1440 
1441 	k_mutex_lock(&client.mutex, K_FOREVER);
1442 
1443 	if (!client.ctx) {
1444 		k_mutex_unlock(&client.mutex);
1445 		LOG_WRN("Cannot resume. No context");
1446 		return -EPERM;
1447 	}
1448 
1449 	if (client.engine_state != ENGINE_SUSPENDED) {
1450 		k_mutex_unlock(&client.mutex);
1451 		LOG_WRN("Cannot resume state is not Suspended");
1452 		return -EPERM;
1453 	}
1454 
1455 	LOG_INF("Resume Client state");
1456 	lwm2m_close_socket(client.ctx);
1457 	if (suspended_client_state == ENGINE_UPDATE_SENT) {
1458 		/* Set back to Registration done for enable trigger Update */
1459 		suspended_client_state = ENGINE_REGISTRATION_DONE;
1460 	}
1461 	/* Clear Possible pending RD Client message */
1462 	rd_client_message_free();
1463 
1464 	client.engine_state = suspended_client_state;
1465 
1466 	if (!client.last_update ||
1467 	    (client.lifetime <= (k_uptime_get() - client.last_update) / 1000)) {
1468 		client.engine_state = ENGINE_DO_REGISTRATION;
1469 	} else {
1470 		lwm2m_rd_client_connection_resume(client.ctx);
1471 		client.trigger_update = true;
1472 	}
1473 
1474 	ret = lwm2m_open_socket(client.ctx);
1475 	if (ret) {
1476 		LOG_ERR("Socket Open Fail");
1477 		client.engine_state = ENGINE_INIT;
1478 	}
1479 
1480 	k_mutex_unlock(&client.mutex);
1481 
1482 	return 0;
1483 }
1484 
lwm2m_rd_client_update(void)1485 void lwm2m_rd_client_update(void)
1486 {
1487 	engine_trigger_update(false);
1488 }
1489 
lwm2m_rd_client_ctx(void)1490 struct lwm2m_ctx *lwm2m_rd_client_ctx(void)
1491 {
1492 	return client.ctx;
1493 }
1494 
lwm2m_rd_client_connection_resume(struct lwm2m_ctx * client_ctx)1495 int lwm2m_rd_client_connection_resume(struct lwm2m_ctx *client_ctx)
1496 {
1497 	if (client.ctx != client_ctx) {
1498 		return -EPERM;
1499 	}
1500 
1501 	if (client.engine_state == ENGINE_REGISTRATION_DONE_RX_OFF) {
1502 #ifdef CONFIG_LWM2M_DTLS_SUPPORT
1503 		/*
1504 		 * Switch state for triggering a proper registration message
1505 		 * if CONFIG_LWM2M_TLS_SESSION_CACHING is false we force full
1506 		 * registration after Fully DTLS handshake
1507 		 */
1508 		if (IS_ENABLED(CONFIG_LWM2M_TLS_SESSION_CACHING)) {
1509 			client.engine_state = ENGINE_REGISTRATION_DONE;
1510 			client.trigger_update = true;
1511 		} else {
1512 			client.engine_state = ENGINE_DO_REGISTRATION;
1513 		}
1514 #else
1515 		client.engine_state = ENGINE_REGISTRATION_DONE;
1516 		client.trigger_update = true;
1517 #endif
1518 	}
1519 
1520 	return 0;
1521 }
1522 
lwm2m_rd_client_timeout(struct lwm2m_ctx * client_ctx)1523 int lwm2m_rd_client_timeout(struct lwm2m_ctx *client_ctx)
1524 {
1525 	if (client.ctx != client_ctx) {
1526 		return -EPERM;
1527 	}
1528 
1529 	if (!sm_is_registered()) {
1530 		return 0;
1531 	}
1532 	k_mutex_lock(&client.mutex, K_FOREVER);
1533 	LOG_WRN("Confirmable Timeout -> Re-connect and register");
1534 	client.engine_state = ENGINE_DO_REGISTRATION;
1535 	k_mutex_unlock(&client.mutex);
1536 	return 0;
1537 }
1538 
lwm2m_rd_client_is_registred(struct lwm2m_ctx * client_ctx)1539 bool lwm2m_rd_client_is_registred(struct lwm2m_ctx *client_ctx)
1540 {
1541 	if (client.ctx != client_ctx || !sm_is_registered()) {
1542 		return false;
1543 	}
1544 
1545 	return true;
1546 }
lwm2m_rd_client_is_suspended(struct lwm2m_ctx * client_ctx)1547 bool lwm2m_rd_client_is_suspended(struct lwm2m_ctx *client_ctx)
1548 {
1549 	if (client.ctx != client_ctx || !sm_is_suspended()) {
1550 		return false;
1551 	}
1552 
1553 	return true;
1554 }
1555 
1556 
lwm2m_rd_client_init(void)1557 int lwm2m_rd_client_init(void)
1558 {
1559 	client.ctx = NULL;
1560 	client.rd_message.ctx = NULL;
1561 	client.engine_state = ENGINE_IDLE;
1562 	k_mutex_init(&client.mutex);
1563 
1564 	return lwm2m_engine_add_service(lwm2m_rd_client_service,
1565 					STATE_MACHINE_UPDATE_INTERVAL_MS);
1566 
1567 }
1568 
sys_lwm2m_rd_client_init(void)1569 static int sys_lwm2m_rd_client_init(void)
1570 {
1571 	return lwm2m_rd_client_init();
1572 }
1573 
1574 
1575 SYS_INIT(sys_lwm2m_rd_client_init, APPLICATION,
1576 	 CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
1577