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