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