1 /*
2 * Copyright (c) 2017 Linaro Limited
3 * Copyright (c) 2018-2019 Foundries.io
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 /*
9 * Uses some original concepts by:
10 * Joakim Eriksson <joakime@sics.se>
11 * Niclas Finne <nfi@sics.se>
12 * Joel Hoglund <joel@sics.se>
13 */
14
15 #define LOG_MODULE_NAME net_lwm2m_engine
16 #define LOG_LEVEL CONFIG_LWM2M_LOG_LEVEL
17
18 #include <logging/log.h>
19 LOG_MODULE_REGISTER(LOG_MODULE_NAME);
20
21 #include <fcntl.h>
22 #include <zephyr/types.h>
23 #include <stddef.h>
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <ctype.h>
28 #include <errno.h>
29 #include <init.h>
30 #include <sys/printk.h>
31 #include <net/net_ip.h>
32 #include <net/http_parser_url.h>
33 #include <net/socket.h>
34 #if defined(CONFIG_LWM2M_DTLS_SUPPORT)
35 #include <net/tls_credentials.h>
36 #endif
37 #if defined(CONFIG_DNS_RESOLVER)
38 #include <net/dns_resolve.h>
39 #endif
40
41 #include "lwm2m_object.h"
42 #include "lwm2m_engine.h"
43 #include "lwm2m_rw_link_format.h"
44 #include "lwm2m_rw_plain_text.h"
45 #include "lwm2m_rw_oma_tlv.h"
46 #include "lwm2m_util.h"
47 #ifdef CONFIG_LWM2M_RW_JSON_SUPPORT
48 #include "lwm2m_rw_json.h"
49 #endif
50 #ifdef CONFIG_LWM2M_RD_CLIENT_SUPPORT
51 #include "lwm2m_rd_client.h"
52 #endif
53
54 #if IS_ENABLED(CONFIG_NET_TC_THREAD_COOPERATIVE)
55 /* Lowest priority cooperative thread */
56 #define THREAD_PRIORITY K_PRIO_COOP(CONFIG_NUM_COOP_PRIORITIES - 1)
57 #else
58 #define THREAD_PRIORITY K_PRIO_PREEMPT(CONFIG_NUM_PREEMPT_PRIORITIES - 1)
59 #endif
60
61 #define ENGINE_UPDATE_INTERVAL_MS 500
62 #define OBSERVE_COUNTER_START 0U
63
64 #if defined(CONFIG_COAP_EXTENDED_OPTIONS_LEN)
65 #define COAP_OPTION_BUF_LEN (CONFIG_COAP_EXTENDED_OPTIONS_LEN_VALUE + 1)
66 #else
67 #define COAP_OPTION_BUF_LEN 13
68 #endif
69
70 #define MAX_TOKEN_LEN 8
71
72 #define LWM2M_MAX_PATH_STR_LEN sizeof("65535/65535/65535/65535")
73
74 struct observe_node {
75 sys_snode_t node;
76 struct lwm2m_obj_path path;
77 uint8_t token[MAX_TOKEN_LEN];
78 int64_t event_timestamp;
79 int64_t last_timestamp;
80 uint32_t min_period_sec;
81 uint32_t max_period_sec;
82 uint32_t counter;
83 uint16_t format;
84 uint8_t tkl;
85 };
86
87 struct notification_attrs {
88 /* use to determine which value is set */
89 float32_value_t gt;
90 float32_value_t lt;
91 float32_value_t st;
92 int32_t pmin;
93 int32_t pmax;
94 uint8_t flags;
95 };
96
97 static struct observe_node observe_node_data[CONFIG_LWM2M_ENGINE_MAX_OBSERVER];
98
99 #define MAX_PERIODIC_SERVICE 10
100
101 struct service_node {
102 sys_snode_t node;
103 k_work_handler_t service_work;
104 uint32_t min_call_period; /* ms */
105 uint64_t last_timestamp; /* ms */
106 };
107
108 static struct service_node service_node_data[MAX_PERIODIC_SERVICE];
109
110 static sys_slist_t engine_obj_list;
111 static sys_slist_t engine_obj_inst_list;
112 static sys_slist_t engine_service_list;
113
114 static K_KERNEL_STACK_DEFINE(engine_thread_stack,
115 CONFIG_LWM2M_ENGINE_STACK_SIZE);
116 static struct k_thread engine_thread_data;
117
118 #define MAX_POLL_FD CONFIG_NET_SOCKETS_POLL_MAX
119
120 static struct lwm2m_ctx *sock_ctx[MAX_POLL_FD];
121 static struct pollfd sock_fds[MAX_POLL_FD];
122 static int sock_nfds;
123
124 #define NUM_BLOCK1_CONTEXT CONFIG_LWM2M_NUM_BLOCK1_CONTEXT
125
126 /* TODO: figure out what's correct value */
127 #define TIMEOUT_BLOCKWISE_TRANSFER_MS (MSEC_PER_SEC * 30)
128
129 static struct lwm2m_block_context block1_contexts[NUM_BLOCK1_CONTEXT];
130
131 /* write-attribute related definitons */
132 static const char * const LWM2M_ATTR_STR[] = { "pmin", "pmax",
133 "gt", "lt", "st" };
134 static const uint8_t LWM2M_ATTR_LEN[] = { 4, 4, 2, 2, 2 };
135
136 static struct lwm2m_attr write_attr_pool[CONFIG_LWM2M_NUM_ATTR];
137
138 static struct lwm2m_engine_obj *get_engine_obj(int obj_id);
139 static struct lwm2m_engine_obj_inst *get_engine_obj_inst(int obj_id,
140 int obj_inst_id);
141
142 /* Shared set of in-flight LwM2M messages */
143 static struct lwm2m_message messages[CONFIG_LWM2M_ENGINE_MAX_MESSAGES];
144
145 /* for debugging: to print IP addresses */
lwm2m_sprint_ip_addr(const struct sockaddr * addr)146 char *lwm2m_sprint_ip_addr(const struct sockaddr *addr)
147 {
148 static char buf[NET_IPV6_ADDR_LEN];
149
150 if (addr->sa_family == AF_INET6) {
151 return net_addr_ntop(AF_INET6, &net_sin6(addr)->sin6_addr,
152 buf, sizeof(buf));
153 }
154
155 if (addr->sa_family == AF_INET) {
156 return net_addr_ntop(AF_INET, &net_sin(addr)->sin_addr,
157 buf, sizeof(buf));
158 }
159
160 LOG_ERR("Unknown IP address family:%d", addr->sa_family);
161 strcpy(buf, "unk");
162 return buf;
163 }
164
to_hex_digit(uint8_t digit)165 static uint8_t to_hex_digit(uint8_t digit)
166 {
167 if (digit >= 10U) {
168 return digit - 10U + 'a';
169 }
170
171 return digit + '0';
172 }
173
sprint_token(const uint8_t * token,uint8_t tkl)174 static char *sprint_token(const uint8_t *token, uint8_t tkl)
175 {
176 static char buf[32];
177 char *ptr = buf;
178
179 if (token && tkl != 0) {
180 int i;
181
182 tkl = MIN(tkl, sizeof(buf) / 2 - 1);
183
184 for (i = 0; i < tkl; i++) {
185 *ptr++ = to_hex_digit(token[i] >> 4);
186 *ptr++ = to_hex_digit(token[i] & 0x0F);
187 }
188
189 *ptr = '\0';
190 } else {
191 strcpy(buf, "[no-token]");
192 }
193
194 return buf;
195 }
196
197 /* block-wise transfer functions */
198
lwm2m_default_block_size(void)199 enum coap_block_size lwm2m_default_block_size(void)
200 {
201 switch (CONFIG_LWM2M_COAP_BLOCK_SIZE) {
202 case 16:
203 return COAP_BLOCK_16;
204 case 32:
205 return COAP_BLOCK_32;
206 case 64:
207 return COAP_BLOCK_64;
208 case 128:
209 return COAP_BLOCK_128;
210 case 256:
211 return COAP_BLOCK_256;
212 case 512:
213 return COAP_BLOCK_512;
214 case 1024:
215 return COAP_BLOCK_1024;
216 }
217
218 return COAP_BLOCK_256;
219 }
220
init_block_ctx(const uint8_t * token,uint8_t tkl,struct lwm2m_block_context ** ctx)221 static int init_block_ctx(const uint8_t *token, uint8_t tkl,
222 struct lwm2m_block_context **ctx)
223 {
224 int i;
225 int64_t timestamp;
226
227 *ctx = NULL;
228 timestamp = k_uptime_get();
229 for (i = 0; i < NUM_BLOCK1_CONTEXT; i++) {
230 if (block1_contexts[i].tkl == 0U) {
231 *ctx = &block1_contexts[i];
232 break;
233 }
234
235 if (timestamp - block1_contexts[i].timestamp >
236 TIMEOUT_BLOCKWISE_TRANSFER_MS) {
237 *ctx = &block1_contexts[i];
238 /* TODO: notify application for block
239 * transfer timeout
240 */
241 break;
242 }
243 }
244
245 if (*ctx == NULL) {
246 LOG_ERR("Cannot find free block context");
247 return -ENOMEM;
248 }
249
250 (*ctx)->tkl = tkl;
251 memcpy((*ctx)->token, token, tkl);
252 coap_block_transfer_init(&(*ctx)->ctx, lwm2m_default_block_size(), 0);
253 (*ctx)->timestamp = timestamp;
254 (*ctx)->expected = 0;
255 (*ctx)->last_block = false;
256 memset(&(*ctx)->opaque, 0, sizeof((*ctx)->opaque));
257
258 return 0;
259 }
260
get_block_ctx(const uint8_t * token,uint8_t tkl,struct lwm2m_block_context ** ctx)261 static int get_block_ctx(const uint8_t *token, uint8_t tkl,
262 struct lwm2m_block_context **ctx)
263 {
264 int i;
265
266 *ctx = NULL;
267
268 for (i = 0; i < NUM_BLOCK1_CONTEXT; i++) {
269 if (block1_contexts[i].tkl == tkl &&
270 memcmp(token, block1_contexts[i].token, tkl) == 0) {
271 *ctx = &block1_contexts[i];
272 /* refresh timestamp */
273 (*ctx)->timestamp = k_uptime_get();
274 break;
275 }
276 }
277
278 if (*ctx == NULL) {
279 return -ENOENT;
280 }
281
282 return 0;
283 }
284
free_block_ctx(struct lwm2m_block_context * ctx)285 static void free_block_ctx(struct lwm2m_block_context *ctx)
286 {
287 if (ctx == NULL) {
288 return;
289 }
290
291 ctx->tkl = 0U;
292 }
293
294 /* observer functions */
295
update_attrs(void * ref,struct notification_attrs * out)296 static int update_attrs(void *ref, struct notification_attrs *out)
297 {
298 int i;
299
300 for (i = 0; i < CONFIG_LWM2M_NUM_ATTR; i++) {
301 if (ref != write_attr_pool[i].ref) {
302 continue;
303 }
304
305 switch (write_attr_pool[i].type) {
306 case LWM2M_ATTR_PMIN:
307 out->pmin = write_attr_pool[i].int_val;
308 break;
309 case LWM2M_ATTR_PMAX:
310 out->pmax = write_attr_pool[i].int_val;
311 break;
312 case LWM2M_ATTR_LT:
313 out->lt = write_attr_pool[i].float_val;
314 break;
315 case LWM2M_ATTR_GT:
316 out->gt = write_attr_pool[i].float_val;
317 break;
318 case LWM2M_ATTR_STEP:
319 out->st = write_attr_pool[i].float_val;
320 break;
321 default:
322 LOG_ERR("Unrecognize attr: %d",
323 write_attr_pool[i].type);
324 return -EINVAL;
325 }
326
327 /* mark as set */
328 out->flags |= BIT(write_attr_pool[i].type);
329 }
330
331 return 0;
332 }
333
clear_attrs(void * ref)334 static void clear_attrs(void *ref)
335 {
336 int i;
337
338 for (i = 0; i < CONFIG_LWM2M_NUM_ATTR; i++) {
339 if (ref == write_attr_pool[i].ref) {
340 (void)memset(&write_attr_pool[i], 0,
341 sizeof(write_attr_pool[i]));
342 }
343 }
344 }
345
lwm2m_notify_observer(uint16_t obj_id,uint16_t obj_inst_id,uint16_t res_id)346 int lwm2m_notify_observer(uint16_t obj_id, uint16_t obj_inst_id, uint16_t res_id)
347 {
348 struct observe_node *obs;
349 int ret = 0;
350 int i;
351
352 /* look for observers which match our resource */
353 for (i = 0; i < sock_nfds; ++i) {
354 SYS_SLIST_FOR_EACH_CONTAINER(&sock_ctx[i]->observer, obs, node) {
355 if (obs->path.obj_id == obj_id &&
356 obs->path.obj_inst_id == obj_inst_id &&
357 (obs->path.level < 3 ||
358 obs->path.res_id == res_id)) {
359 /* update the event time for this observer */
360 obs->event_timestamp = k_uptime_get();
361
362 LOG_DBG("NOTIFY EVENT %u/%u/%u",
363 obj_id, obj_inst_id, res_id);
364
365 ret++;
366 }
367 }
368 }
369
370 return ret;
371 }
372
lwm2m_notify_observer_path(struct lwm2m_obj_path * path)373 int lwm2m_notify_observer_path(struct lwm2m_obj_path *path)
374 {
375 return lwm2m_notify_observer(path->obj_id, path->obj_inst_id,
376 path->res_id);
377 }
378
engine_add_observer(struct lwm2m_message * msg,const uint8_t * token,uint8_t tkl,uint16_t format)379 static int engine_add_observer(struct lwm2m_message *msg,
380 const uint8_t *token, uint8_t tkl,
381 uint16_t format)
382 {
383 struct lwm2m_engine_obj *obj = NULL;
384 struct lwm2m_engine_obj_field *obj_field = NULL;
385 struct lwm2m_engine_obj_inst *obj_inst = NULL;
386 struct observe_node *obs;
387 struct notification_attrs attrs = {
388 .flags = BIT(LWM2M_ATTR_PMIN) | BIT(LWM2M_ATTR_PMAX),
389 };
390 int i, ret;
391
392 if (!msg || !msg->ctx) {
393 LOG_ERR("valid lwm2m message is required");
394 return -EINVAL;
395 }
396
397 if (!token || (tkl == 0U || tkl > MAX_TOKEN_LEN)) {
398 LOG_ERR("token(%p) and token length(%u) must be valid.",
399 token, tkl);
400 return -EINVAL;
401 }
402
403 /* defaults from server object */
404 attrs.pmin = lwm2m_server_get_pmin(msg->ctx->srv_obj_inst);
405 attrs.pmax = lwm2m_server_get_pmax(msg->ctx->srv_obj_inst);
406
407 /* TODO: observe dup checking */
408
409 /* make sure this observer doesn't exist already */
410 SYS_SLIST_FOR_EACH_CONTAINER(&msg->ctx->observer, obs, node) {
411 /* TODO: distinguish server object */
412 if (memcmp(&obs->path, &msg->path, sizeof(msg->path)) == 0) {
413 /* quietly update the token information */
414 memcpy(obs->token, token, tkl);
415 obs->tkl = tkl;
416
417 LOG_DBG("OBSERVER DUPLICATE %u/%u/%u(%u) [%s]",
418 msg->path.obj_id, msg->path.obj_inst_id,
419 msg->path.res_id, msg->path.level,
420 log_strdup(
421 lwm2m_sprint_ip_addr(&msg->ctx->remote_addr)));
422
423 return 0;
424 }
425 }
426
427 /* check if object exists */
428 obj = get_engine_obj(msg->path.obj_id);
429 if (!obj) {
430 LOG_ERR("unable to find obj: %u", msg->path.obj_id);
431 return -ENOENT;
432 }
433
434 ret = update_attrs(obj, &attrs);
435 if (ret < 0) {
436 return ret;
437 }
438
439 /* check if object instance exists */
440 if (msg->path.level >= 2U) {
441 obj_inst = get_engine_obj_inst(msg->path.obj_id,
442 msg->path.obj_inst_id);
443 if (!obj_inst) {
444 LOG_ERR("unable to find obj_inst: %u/%u",
445 msg->path.obj_id, msg->path.obj_inst_id);
446 return -ENOENT;
447 }
448
449 ret = update_attrs(obj_inst, &attrs);
450 if (ret < 0) {
451 return ret;
452 }
453 }
454
455 /* check if resource exists */
456 if (msg->path.level >= 3U) {
457 for (i = 0; i < obj_inst->resource_count; i++) {
458 if (obj_inst->resources[i].res_id == msg->path.res_id) {
459 break;
460 }
461 }
462
463 if (i == obj_inst->resource_count) {
464 LOG_ERR("unable to find res_id: %u/%u/%u",
465 msg->path.obj_id, msg->path.obj_inst_id,
466 msg->path.res_id);
467 return -ENOENT;
468 }
469
470 /* load object field data */
471 obj_field = lwm2m_get_engine_obj_field(obj,
472 obj_inst->resources[i].res_id);
473 if (!obj_field) {
474 LOG_ERR("unable to find obj_field: %u/%u/%u",
475 msg->path.obj_id, msg->path.obj_inst_id,
476 msg->path.res_id);
477 return -ENOENT;
478 }
479
480 /* check for READ permission on matching resource */
481 if (!LWM2M_HAS_PERM(obj_field, LWM2M_PERM_R)) {
482 return -EPERM;
483 }
484
485 ret = update_attrs(&obj_inst->resources[i], &attrs);
486 if (ret < 0) {
487 return ret;
488 }
489 }
490
491 /* find an unused observer index node */
492 for (i = 0; i < CONFIG_LWM2M_ENGINE_MAX_OBSERVER; i++) {
493 if (!observe_node_data[i].tkl) {
494 break;
495 }
496 }
497
498 /* couldn't find an index */
499 if (i == CONFIG_LWM2M_ENGINE_MAX_OBSERVER) {
500 return -ENOMEM;
501 }
502
503 /* copy the values and add it to the list */
504 memcpy(&observe_node_data[i].path, &msg->path, sizeof(msg->path));
505 memcpy(observe_node_data[i].token, token, tkl);
506 observe_node_data[i].tkl = tkl;
507 observe_node_data[i].last_timestamp = k_uptime_get();
508 observe_node_data[i].event_timestamp =
509 observe_node_data[i].last_timestamp;
510 observe_node_data[i].min_period_sec = attrs.pmin;
511 observe_node_data[i].max_period_sec = (attrs.pmax > 0) ? MAX(attrs.pmax, attrs.pmin)
512 : attrs.pmax;
513 observe_node_data[i].format = format;
514 observe_node_data[i].counter = OBSERVE_COUNTER_START;
515 sys_slist_append(&msg->ctx->observer,
516 &observe_node_data[i].node);
517
518 LOG_DBG("OBSERVER ADDED %u/%u/%u(%u) token:'%s' addr:%s",
519 msg->path.obj_id, msg->path.obj_inst_id,
520 msg->path.res_id, msg->path.level,
521 log_strdup(sprint_token(token, tkl)),
522 log_strdup(lwm2m_sprint_ip_addr(&msg->ctx->remote_addr)));
523
524 return 0;
525 }
526
engine_remove_observer(struct lwm2m_ctx * ctx,const uint8_t * token,uint8_t tkl)527 static int engine_remove_observer(struct lwm2m_ctx *ctx, const uint8_t *token, uint8_t tkl)
528 {
529 struct observe_node *obs, *found_obj = NULL;
530 sys_snode_t *prev_node = NULL;
531
532 if (!token || (tkl == 0U || tkl > MAX_TOKEN_LEN)) {
533 LOG_ERR("token(%p) and token length(%u) must be valid.",
534 token, tkl);
535 return -EINVAL;
536 }
537
538 /* find the node index */
539 SYS_SLIST_FOR_EACH_CONTAINER(&ctx->observer, obs, node) {
540 if (memcmp(obs->token, token, tkl) == 0) {
541 found_obj = obs;
542 break;
543 }
544
545 prev_node = &obs->node;
546 }
547
548 if (!found_obj) {
549 return -ENOENT;
550 }
551
552 sys_slist_remove(&ctx->observer, prev_node, &found_obj->node);
553 (void)memset(found_obj, 0, sizeof(*found_obj));
554
555 LOG_DBG("observer '%s' removed", log_strdup(sprint_token(token, tkl)));
556
557 return 0;
558 }
559
560 #if defined(CONFIG_LOG)
lwm2m_path_log_strdup(char * buf,struct lwm2m_obj_path * path)561 char *lwm2m_path_log_strdup(char *buf, struct lwm2m_obj_path *path)
562 {
563 size_t cur = sprintf(buf, "%u", path->obj_id);
564
565 if (path->level > 1) {
566 cur += sprintf(buf + cur, "/%u", path->obj_inst_id);
567 }
568 if (path->level > 2) {
569 cur += sprintf(buf + cur, "/%u", path->res_id);
570 }
571 if (path->level > 3) {
572 cur += sprintf(buf + cur, "/%u", path->res_inst_id);
573 }
574
575 return log_strdup(buf);
576 }
577 #endif /* CONFIG_LOG */
578
579 #if defined(CONFIG_LWM2M_CANCEL_OBSERVE_BY_PATH)
engine_remove_observer_by_path(struct lwm2m_ctx * ctx,struct lwm2m_obj_path * path)580 static int engine_remove_observer_by_path(struct lwm2m_ctx *ctx,
581 struct lwm2m_obj_path *path)
582 {
583 char buf[LWM2M_MAX_PATH_STR_LEN];
584 struct observe_node *obs, *found_obj = NULL;
585 sys_snode_t *prev_node = NULL;
586
587 /* find the node index */
588 SYS_SLIST_FOR_EACH_CONTAINER(&ctx->observer, obs, node) {
589 if (memcmp(path, &obs->path, sizeof(*path)) == 0) {
590 found_obj = obs;
591 break;
592 }
593
594 prev_node = &obs->node;
595 }
596
597 if (!found_obj) {
598 return -ENOENT;
599 }
600
601 LOG_INF("Removing observer for path %s",
602 lwm2m_path_log_strdup(buf, path));
603 sys_slist_remove(&ctx->observer, prev_node, &found_obj->node);
604 (void)memset(found_obj, 0, sizeof(*found_obj));
605
606 return 0;
607 }
608 #endif /* CONFIG_LWM2M_CANCEL_OBSERVE_BY_PATH */
609
engine_remove_observer_by_id(uint16_t obj_id,int32_t obj_inst_id)610 static void engine_remove_observer_by_id(uint16_t obj_id, int32_t obj_inst_id)
611 {
612 struct observe_node *obs, *tmp;
613 sys_snode_t *prev_node = NULL;
614 int i;
615
616 /* remove observer instances accordingly */
617 for (i = 0; i < sock_nfds; ++i) {
618 SYS_SLIST_FOR_EACH_CONTAINER_SAFE(
619 &sock_ctx[i]->observer, obs, tmp, node) {
620 if (!(obj_id == obs->path.obj_id &&
621 obj_inst_id == obs->path.obj_inst_id)) {
622 prev_node = &obs->node;
623 continue;
624 }
625
626 sys_slist_remove(&sock_ctx[i]->observer, prev_node, &obs->node);
627 (void)memset(obs, 0, sizeof(*obs));
628 }
629 }
630 }
631
632 /* engine object */
633
lwm2m_register_obj(struct lwm2m_engine_obj * obj)634 void lwm2m_register_obj(struct lwm2m_engine_obj *obj)
635 {
636 sys_slist_append(&engine_obj_list, &obj->node);
637 }
638
lwm2m_unregister_obj(struct lwm2m_engine_obj * obj)639 void lwm2m_unregister_obj(struct lwm2m_engine_obj *obj)
640 {
641 engine_remove_observer_by_id(obj->obj_id, -1);
642 sys_slist_find_and_remove(&engine_obj_list, &obj->node);
643 }
644
get_engine_obj(int obj_id)645 static struct lwm2m_engine_obj *get_engine_obj(int obj_id)
646 {
647 struct lwm2m_engine_obj *obj;
648
649 SYS_SLIST_FOR_EACH_CONTAINER(&engine_obj_list, obj, node) {
650 if (obj->obj_id == obj_id) {
651 return obj;
652 }
653 }
654
655 return NULL;
656 }
657
658 struct lwm2m_engine_obj_field *
lwm2m_get_engine_obj_field(struct lwm2m_engine_obj * obj,int res_id)659 lwm2m_get_engine_obj_field(struct lwm2m_engine_obj *obj, int res_id)
660 {
661 int i;
662
663 if (obj && obj->fields && obj->field_count > 0) {
664 for (i = 0; i < obj->field_count; i++) {
665 if (obj->fields[i].res_id == res_id) {
666 return &obj->fields[i];
667 }
668 }
669 }
670
671 return NULL;
672 }
673
674 /* engine object instance */
675
engine_register_obj_inst(struct lwm2m_engine_obj_inst * obj_inst)676 static void engine_register_obj_inst(struct lwm2m_engine_obj_inst *obj_inst)
677 {
678 sys_slist_append(&engine_obj_inst_list, &obj_inst->node);
679 }
680
engine_unregister_obj_inst(struct lwm2m_engine_obj_inst * obj_inst)681 static void engine_unregister_obj_inst(struct lwm2m_engine_obj_inst *obj_inst)
682 {
683 engine_remove_observer_by_id(
684 obj_inst->obj->obj_id, obj_inst->obj_inst_id);
685 sys_slist_find_and_remove(&engine_obj_inst_list, &obj_inst->node);
686 }
687
get_engine_obj_inst(int obj_id,int obj_inst_id)688 static struct lwm2m_engine_obj_inst *get_engine_obj_inst(int obj_id,
689 int obj_inst_id)
690 {
691 struct lwm2m_engine_obj_inst *obj_inst;
692
693 SYS_SLIST_FOR_EACH_CONTAINER(&engine_obj_inst_list, obj_inst,
694 node) {
695 if (obj_inst->obj->obj_id == obj_id &&
696 obj_inst->obj_inst_id == obj_inst_id) {
697 return obj_inst;
698 }
699 }
700
701 return NULL;
702 }
703
704 static struct lwm2m_engine_obj_inst *
next_engine_obj_inst(int obj_id,int obj_inst_id)705 next_engine_obj_inst(int obj_id, int obj_inst_id)
706 {
707 struct lwm2m_engine_obj_inst *obj_inst, *next = NULL;
708
709 SYS_SLIST_FOR_EACH_CONTAINER(&engine_obj_inst_list, obj_inst,
710 node) {
711 if (obj_inst->obj->obj_id == obj_id &&
712 obj_inst->obj_inst_id > obj_inst_id &&
713 (!next || next->obj_inst_id > obj_inst->obj_inst_id)) {
714 next = obj_inst;
715 }
716 }
717
718 return next;
719 }
720
lwm2m_create_obj_inst(uint16_t obj_id,uint16_t obj_inst_id,struct lwm2m_engine_obj_inst ** obj_inst)721 int lwm2m_create_obj_inst(uint16_t obj_id, uint16_t obj_inst_id,
722 struct lwm2m_engine_obj_inst **obj_inst)
723 {
724 struct lwm2m_engine_obj *obj;
725 int ret;
726
727 *obj_inst = NULL;
728 obj = get_engine_obj(obj_id);
729 if (!obj) {
730 LOG_ERR("unable to find obj: %u", obj_id);
731 return -ENOENT;
732 }
733
734 if (!obj->create_cb) {
735 LOG_ERR("obj %u has no create_cb", obj_id);
736 return -EINVAL;
737 }
738
739 if (obj->instance_count + 1 > obj->max_instance_count) {
740 LOG_ERR("no more instances available for obj %u", obj_id);
741 return -ENOMEM;
742 }
743
744 *obj_inst = obj->create_cb(obj_inst_id);
745 if (!*obj_inst) {
746 LOG_ERR("unable to create obj %u instance %u",
747 obj_id, obj_inst_id);
748 /*
749 * Already checked for instance count total.
750 * This can only be an error if the object instance exists.
751 */
752 return -EEXIST;
753 }
754
755 obj->instance_count++;
756 (*obj_inst)->obj = obj;
757 (*obj_inst)->obj_inst_id = obj_inst_id;
758 engine_register_obj_inst(*obj_inst);
759
760 if (obj->user_create_cb) {
761 ret = obj->user_create_cb(obj_inst_id);
762 if (ret < 0) {
763 LOG_ERR("Error in user obj create %u/%u: %d",
764 obj_id, obj_inst_id, ret);
765 lwm2m_delete_obj_inst(obj_id, obj_inst_id);
766 return ret;
767 }
768 }
769
770 return 0;
771 }
772
lwm2m_delete_obj_inst(uint16_t obj_id,uint16_t obj_inst_id)773 int lwm2m_delete_obj_inst(uint16_t obj_id, uint16_t obj_inst_id)
774 {
775 int i, ret = 0;
776 struct lwm2m_engine_obj *obj;
777 struct lwm2m_engine_obj_inst *obj_inst;
778
779 obj = get_engine_obj(obj_id);
780 if (!obj) {
781 return -ENOENT;
782 }
783
784 obj_inst = get_engine_obj_inst(obj_id, obj_inst_id);
785 if (!obj_inst) {
786 return -ENOENT;
787 }
788
789 if (obj->user_delete_cb) {
790 ret = obj->user_delete_cb(obj_inst_id);
791 if (ret < 0) {
792 LOG_ERR("Error in user obj delete %u/%u: %d",
793 obj_id, obj_inst_id, ret);
794 /* don't return error */
795 }
796 }
797
798 engine_unregister_obj_inst(obj_inst);
799 obj->instance_count--;
800
801 if (obj->delete_cb) {
802 ret = obj->delete_cb(obj_inst_id);
803 }
804
805 /* reset obj_inst and res_inst data structure */
806 for (i = 0; i < obj_inst->resource_count; i++) {
807 clear_attrs(&obj_inst->resources[i]);
808 (void)memset(obj_inst->resources + i, 0,
809 sizeof(struct lwm2m_engine_res));
810 }
811
812 clear_attrs(obj_inst);
813 (void)memset(obj_inst, 0, sizeof(struct lwm2m_engine_obj_inst));
814 return ret;
815 }
816
817 /* utility functions */
818
atou16(uint8_t * buf,uint16_t buflen,uint16_t * len)819 static uint16_t atou16(uint8_t *buf, uint16_t buflen, uint16_t *len)
820 {
821 uint16_t val = 0U;
822 uint16_t pos = 0U;
823
824 /* we should get a value first - consume all numbers */
825 while (pos < buflen && isdigit(buf[pos])) {
826 val = val * 10U + (buf[pos] - '0');
827 pos++;
828 }
829
830 *len = pos;
831 return val;
832 }
833
coap_options_to_path(struct coap_option * opt,int options_count,struct lwm2m_obj_path * path)834 static int coap_options_to_path(struct coap_option *opt, int options_count,
835 struct lwm2m_obj_path *path)
836 {
837 uint16_t len, *id[4] = { &path->obj_id, &path->obj_inst_id,
838 &path->res_id, &path->res_inst_id };
839
840 path->level = options_count;
841
842 for (int i = 0; i < options_count; i++) {
843 *id[i] = atou16(opt[i].value, opt[i].len, &len);
844 if (len == 0U || opt[i].len != len) {
845 path->level = i;
846 break;
847 }
848 }
849
850 return options_count == path->level ? 0 : -EINVAL;
851 }
852
find_msg(struct coap_pending * pending,struct coap_reply * reply)853 static struct lwm2m_message *find_msg(struct coap_pending *pending,
854 struct coap_reply *reply)
855 {
856 size_t i;
857
858 if (!pending && !reply) {
859 return NULL;
860 }
861
862 for (i = 0; i < CONFIG_LWM2M_ENGINE_MAX_MESSAGES; i++) {
863 if (pending != NULL && messages[i].ctx &&
864 messages[i].pending == pending) {
865 return &messages[i];
866 }
867
868 if (reply != NULL && messages[i].ctx &&
869 messages[i].reply == reply) {
870 return &messages[i];
871 }
872 }
873
874 return NULL;
875 }
876
lwm2m_get_message(struct lwm2m_ctx * client_ctx)877 struct lwm2m_message *lwm2m_get_message(struct lwm2m_ctx *client_ctx)
878 {
879 size_t i;
880
881 for (i = 0; i < CONFIG_LWM2M_ENGINE_MAX_MESSAGES; i++) {
882 if (!messages[i].ctx) {
883 messages[i].ctx = client_ctx;
884 return &messages[i];
885 }
886 }
887
888 return NULL;
889 }
890
lwm2m_reset_message(struct lwm2m_message * msg,bool release)891 void lwm2m_reset_message(struct lwm2m_message *msg, bool release)
892 {
893 if (!msg) {
894 return;
895 }
896
897 if (msg->pending) {
898 coap_pending_clear(msg->pending);
899 }
900
901 if (msg->reply) {
902 /* make sure we want to clear the reply */
903 coap_reply_clear(msg->reply);
904 }
905
906 if (release) {
907 (void)memset(msg, 0, sizeof(*msg));
908 } else {
909 msg->message_timeout_cb = NULL;
910 (void)memset(&msg->cpkt, 0, sizeof(msg->cpkt));
911 }
912 }
913
lwm2m_init_message(struct lwm2m_message * msg)914 int lwm2m_init_message(struct lwm2m_message *msg)
915 {
916 uint8_t tokenlen = 0U;
917 uint8_t *token = NULL;
918 int r = 0;
919
920 if (!msg || !msg->ctx) {
921 LOG_ERR("LwM2M message is invalid.");
922 return -EINVAL;
923 }
924
925 if (msg->tkl == LWM2M_MSG_TOKEN_GENERATE_NEW) {
926 tokenlen = 8U;
927 token = coap_next_token();
928 } else if (msg->token && msg->tkl != 0) {
929 tokenlen = msg->tkl;
930 token = msg->token;
931 }
932
933 r = coap_packet_init(&msg->cpkt, msg->msg_data, sizeof(msg->msg_data),
934 COAP_VERSION_1, msg->type, tokenlen, token,
935 msg->code, msg->mid);
936 if (r < 0) {
937 LOG_ERR("coap packet init error (err:%d)", r);
938 goto cleanup;
939 }
940
941 /* only TYPE_CON messages need pending tracking / reply handling */
942 if (msg->type != COAP_TYPE_CON) {
943 return 0;
944 }
945
946 msg->pending = coap_pending_next_unused(
947 msg->ctx->pendings,
948 CONFIG_LWM2M_ENGINE_MAX_PENDING);
949 if (!msg->pending) {
950 LOG_ERR("Unable to find a free pending to track "
951 "retransmissions.");
952 r = -ENOMEM;
953 goto cleanup;
954 }
955
956 r = coap_pending_init(msg->pending, &msg->cpkt, &msg->ctx->remote_addr,
957 COAP_DEFAULT_MAX_RETRANSMIT);
958 if (r < 0) {
959 LOG_ERR("Unable to initialize a pending "
960 "retransmission (err:%d).", r);
961 goto cleanup;
962 }
963
964 if (msg->reply_cb) {
965 msg->reply = coap_reply_next_unused(
966 msg->ctx->replies,
967 CONFIG_LWM2M_ENGINE_MAX_REPLIES);
968 if (!msg->reply) {
969 LOG_ERR("No resources for waiting for replies.");
970 r = -ENOMEM;
971 goto cleanup;
972 }
973
974 coap_reply_clear(msg->reply);
975 coap_reply_init(msg->reply, &msg->cpkt);
976 msg->reply->reply = msg->reply_cb;
977 }
978
979 return 0;
980
981 cleanup:
982 lwm2m_reset_message(msg, true);
983
984 return r;
985 }
986
lwm2m_send_message_async(struct lwm2m_message * msg)987 int lwm2m_send_message_async(struct lwm2m_message *msg)
988 {
989 sys_slist_append(&msg->ctx->pending_sends, &msg->node);
990 return 0;
991 }
992
lwm2m_send_message(struct lwm2m_message * msg)993 static int lwm2m_send_message(struct lwm2m_message *msg)
994 {
995 int rc;
996
997 if (!msg || !msg->ctx) {
998 LOG_ERR("LwM2M message is invalid.");
999 return -EINVAL;
1000 }
1001
1002 if (msg->type == COAP_TYPE_CON) {
1003 coap_pending_cycle(msg->pending);
1004 }
1005
1006 rc = send(msg->ctx->sock_fd, msg->cpkt.data, msg->cpkt.offset, 0);
1007
1008 if (rc < 0) {
1009 LOG_ERR("Failed to send packet, err %d", errno);
1010 if (msg->type != COAP_TYPE_CON) {
1011 lwm2m_reset_message(msg, true);
1012 }
1013
1014 return -errno;
1015 }
1016
1017 if (msg->type != COAP_TYPE_CON) {
1018 lwm2m_reset_message(msg, true);
1019 }
1020
1021 if (IS_ENABLED(CONFIG_LWM2M_RD_CLIENT_SUPPORT) &&
1022 IS_ENABLED(CONFIG_LWM2M_QUEUE_MODE_ENABLED)) {
1023 engine_update_tx_time();
1024 }
1025
1026 return 0;
1027 }
1028
lwm2m_send_empty_ack(struct lwm2m_ctx * client_ctx,uint16_t mid)1029 int lwm2m_send_empty_ack(struct lwm2m_ctx *client_ctx, uint16_t mid)
1030 {
1031 struct lwm2m_message *msg;
1032 int ret;
1033
1034 msg = lwm2m_get_message(client_ctx);
1035 if (!msg) {
1036 LOG_ERR("Unable to get a lwm2m message!");
1037 return -ENOMEM;
1038 }
1039
1040 msg->type = COAP_TYPE_ACK;
1041 msg->code = COAP_CODE_EMPTY;
1042 msg->mid = mid;
1043
1044 ret = lwm2m_init_message(msg);
1045 if (ret) {
1046 goto cleanup;
1047 }
1048
1049 lwm2m_send_message_async(msg);
1050
1051 return 0;
1052
1053 cleanup:
1054 lwm2m_reset_message(msg, true);
1055 return ret;
1056 }
1057
lwm2m_acknowledge(struct lwm2m_ctx * client_ctx)1058 void lwm2m_acknowledge(struct lwm2m_ctx *client_ctx)
1059 {
1060 struct lwm2m_message *request;
1061
1062 if (client_ctx == NULL || client_ctx->processed_req == NULL) {
1063 return;
1064 }
1065
1066 request = (struct lwm2m_message *)client_ctx->processed_req;
1067
1068 if (request->acknowledged) {
1069 return;
1070 }
1071
1072 if (lwm2m_send_empty_ack(client_ctx, request->mid) < 0) {
1073 return;
1074 }
1075
1076 request->acknowledged = true;
1077 }
1078
lwm2m_register_payload_handler(struct lwm2m_message * msg)1079 int lwm2m_register_payload_handler(struct lwm2m_message *msg)
1080 {
1081 struct lwm2m_engine_obj *obj;
1082 struct lwm2m_engine_obj_inst *obj_inst;
1083 int ret;
1084
1085 engine_put_begin(&msg->out, NULL);
1086
1087 SYS_SLIST_FOR_EACH_CONTAINER(&engine_obj_list, obj, node) {
1088 /* Security obj MUST NOT be part of registration message */
1089 if (obj->obj_id == LWM2M_OBJECT_SECURITY_ID) {
1090 continue;
1091 }
1092
1093 /* Only report <OBJ_ID> when no instance available or it's
1094 * needed to report object version.
1095 */
1096 if (obj->instance_count == 0U ||
1097 lwm2m_engine_shall_report_obj_version(obj)) {
1098 struct lwm2m_obj_path path = {
1099 .obj_id = obj->obj_id,
1100 .level = LWM2M_PATH_LEVEL_OBJECT,
1101 };
1102
1103 ret = engine_put_corelink(&msg->out, &path);
1104 if (ret < 0) {
1105 return ret;
1106 }
1107
1108 if (obj->instance_count == 0U) {
1109 continue;
1110 }
1111 }
1112
1113 SYS_SLIST_FOR_EACH_CONTAINER(&engine_obj_inst_list,
1114 obj_inst, node) {
1115 if (obj_inst->obj->obj_id == obj->obj_id) {
1116 struct lwm2m_obj_path path = {
1117 .obj_id = obj_inst->obj->obj_id,
1118 .obj_inst_id = obj_inst->obj_inst_id,
1119 .level = LWM2M_PATH_LEVEL_OBJECT_INST,
1120 };
1121
1122 ret = engine_put_corelink(&msg->out, &path);
1123 if (ret < 0) {
1124 return ret;
1125 }
1126 }
1127 }
1128 }
1129
1130 return 0;
1131 }
1132
1133 /* input / output selection */
1134
select_writer(struct lwm2m_output_context * out,uint16_t accept)1135 static int select_writer(struct lwm2m_output_context *out, uint16_t accept)
1136 {
1137 switch (accept) {
1138
1139 case LWM2M_FORMAT_APP_LINK_FORMAT:
1140 out->writer = &link_format_writer;
1141 break;
1142
1143 case LWM2M_FORMAT_PLAIN_TEXT:
1144 case LWM2M_FORMAT_OMA_PLAIN_TEXT:
1145 out->writer = &plain_text_writer;
1146 break;
1147
1148 case LWM2M_FORMAT_OMA_TLV:
1149 case LWM2M_FORMAT_OMA_OLD_TLV:
1150 out->writer = &oma_tlv_writer;
1151 break;
1152
1153 #ifdef CONFIG_LWM2M_RW_JSON_SUPPORT
1154 case LWM2M_FORMAT_OMA_JSON:
1155 case LWM2M_FORMAT_OMA_OLD_JSON:
1156 out->writer = &json_writer;
1157 break;
1158 #endif
1159
1160 default:
1161 LOG_WRN("Unknown content type %u", accept);
1162 return -ENOMSG;
1163
1164 }
1165
1166 return 0;
1167 }
1168
select_reader(struct lwm2m_input_context * in,uint16_t format)1169 static int select_reader(struct lwm2m_input_context *in, uint16_t format)
1170 {
1171 switch (format) {
1172
1173 case LWM2M_FORMAT_APP_OCTET_STREAM:
1174 case LWM2M_FORMAT_PLAIN_TEXT:
1175 case LWM2M_FORMAT_OMA_PLAIN_TEXT:
1176 in->reader = &plain_text_reader;
1177 break;
1178
1179 case LWM2M_FORMAT_OMA_TLV:
1180 case LWM2M_FORMAT_OMA_OLD_TLV:
1181 in->reader = &oma_tlv_reader;
1182 break;
1183
1184 #ifdef CONFIG_LWM2M_RW_JSON_SUPPORT
1185 case LWM2M_FORMAT_OMA_JSON:
1186 case LWM2M_FORMAT_OMA_OLD_JSON:
1187 in->reader = &json_reader;
1188 break;
1189 #endif
1190
1191 default:
1192 LOG_WRN("Unknown content type %u", format);
1193 return -ENOMSG;
1194 }
1195
1196 return 0;
1197 }
1198
1199 /* user data setter functions */
1200
string_to_path(char * pathstr,struct lwm2m_obj_path * path,char delim)1201 static int string_to_path(char *pathstr, struct lwm2m_obj_path *path,
1202 char delim)
1203 {
1204 uint16_t value, len;
1205 int i, tokstart = -1, toklen;
1206 int end_index = strlen(pathstr) - 1;
1207
1208 (void)memset(path, 0, sizeof(*path));
1209 for (i = 0; i <= end_index; i++) {
1210 /* search for first numeric */
1211 if (tokstart == -1) {
1212 if (!isdigit((unsigned char)pathstr[i])) {
1213 continue;
1214 }
1215
1216 tokstart = i;
1217 }
1218
1219 /* find delimiter char or end of string */
1220 if (pathstr[i] == delim || i == end_index) {
1221 toklen = i - tokstart + 1;
1222
1223 /* don't process delimiter char */
1224 if (pathstr[i] == delim) {
1225 toklen--;
1226 }
1227
1228 if (toklen <= 0) {
1229 continue;
1230 }
1231
1232 value = atou16(&pathstr[tokstart], toklen, &len);
1233 switch (path->level) {
1234
1235 case 0:
1236 path->obj_id = value;
1237 break;
1238
1239 case 1:
1240 path->obj_inst_id = value;
1241 break;
1242
1243 case 2:
1244 path->res_id = value;
1245 break;
1246
1247 case 3:
1248 path->res_inst_id = value;
1249 break;
1250
1251 default:
1252 LOG_ERR("invalid level (%d)", path->level);
1253 return -EINVAL;
1254
1255 }
1256
1257 /* increase the path level for each token found */
1258 path->level++;
1259 tokstart = -1;
1260 }
1261 }
1262
1263 return 0;
1264 }
1265
path_to_objs(const struct lwm2m_obj_path * path,struct lwm2m_engine_obj_inst ** obj_inst,struct lwm2m_engine_obj_field ** obj_field,struct lwm2m_engine_res ** res,struct lwm2m_engine_res_inst ** res_inst)1266 static int path_to_objs(const struct lwm2m_obj_path *path,
1267 struct lwm2m_engine_obj_inst **obj_inst,
1268 struct lwm2m_engine_obj_field **obj_field,
1269 struct lwm2m_engine_res **res,
1270 struct lwm2m_engine_res_inst **res_inst)
1271 {
1272 struct lwm2m_engine_obj_inst *oi;
1273 struct lwm2m_engine_obj_field *of;
1274 struct lwm2m_engine_res *r = NULL;
1275 struct lwm2m_engine_res_inst *ri = NULL;
1276 int i;
1277
1278 if (!path) {
1279 return -EINVAL;
1280 }
1281
1282 oi = get_engine_obj_inst(path->obj_id, path->obj_inst_id);
1283 if (!oi) {
1284 LOG_ERR("obj instance %d/%d not found",
1285 path->obj_id, path->obj_inst_id);
1286 return -ENOENT;
1287 }
1288
1289 if (!oi->resources || oi->resource_count == 0U) {
1290 LOG_ERR("obj instance has no resources");
1291 return -EINVAL;
1292 }
1293
1294 of = lwm2m_get_engine_obj_field(oi->obj, path->res_id);
1295 if (!of) {
1296 LOG_ERR("obj field %d not found", path->res_id);
1297 return -ENOENT;
1298 }
1299
1300 for (i = 0; i < oi->resource_count; i++) {
1301 if (oi->resources[i].res_id == path->res_id) {
1302 r = &oi->resources[i];
1303 break;
1304 }
1305 }
1306
1307 if (!r) {
1308 LOG_ERR("resource %d not found", path->res_id);
1309 return -ENOENT;
1310 }
1311
1312 for (i = 0; i < r->res_inst_count; i++) {
1313 if (r->res_instances[i].res_inst_id == path->res_inst_id) {
1314 ri = &r->res_instances[i];
1315 break;
1316 }
1317 }
1318
1319 /* specifically don't complain about missing resource instance */
1320
1321 if (obj_inst) {
1322 *obj_inst = oi;
1323 }
1324
1325 if (obj_field) {
1326 *obj_field = of;
1327 }
1328
1329 if (res) {
1330 *res = r;
1331 }
1332
1333 if (ri && res_inst) {
1334 *res_inst = ri;
1335 }
1336
1337 return 0;
1338 }
1339
lwm2m_engine_get_next_attr(const void * ref,struct lwm2m_attr * prev)1340 struct lwm2m_attr *lwm2m_engine_get_next_attr(const void *ref,
1341 struct lwm2m_attr *prev)
1342 {
1343 struct lwm2m_attr *iter = (prev == NULL) ? write_attr_pool : prev + 1;
1344 struct lwm2m_attr *result = NULL;
1345
1346 if (!PART_OF_ARRAY(write_attr_pool, iter)) {
1347 return NULL;
1348 }
1349
1350 while (iter < &write_attr_pool[ARRAY_SIZE(write_attr_pool)]) {
1351 if (ref == iter->ref) {
1352 result = iter;
1353 break;
1354 }
1355
1356 ++iter;
1357 }
1358
1359 return result;
1360 }
1361
lwm2m_engine_get_attr_name(const struct lwm2m_attr * attr)1362 const char *lwm2m_engine_get_attr_name(const struct lwm2m_attr *attr)
1363 {
1364 if (attr->type >= NR_LWM2M_ATTR) {
1365 return NULL;
1366 }
1367
1368 return LWM2M_ATTR_STR[attr->type];
1369 }
1370
lwm2m_engine_create_obj_inst(char * pathstr)1371 int lwm2m_engine_create_obj_inst(char *pathstr)
1372 {
1373 struct lwm2m_obj_path path;
1374 struct lwm2m_engine_obj_inst *obj_inst;
1375 int ret = 0;
1376
1377 LOG_DBG("path:%s", log_strdup(pathstr));
1378
1379 /* translate path -> path_obj */
1380 ret = string_to_path(pathstr, &path, '/');
1381 if (ret < 0) {
1382 return ret;
1383 }
1384
1385 if (path.level != 2U) {
1386 LOG_ERR("path must have 2 parts");
1387 return -EINVAL;
1388 }
1389
1390 ret = lwm2m_create_obj_inst(path.obj_id, path.obj_inst_id, &obj_inst);
1391 if (ret < 0) {
1392 return ret;
1393 }
1394
1395 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT)
1396 engine_trigger_update(true);
1397 #endif
1398
1399 return 0;
1400 }
1401
lwm2m_engine_delete_obj_inst(char * pathstr)1402 int lwm2m_engine_delete_obj_inst(char *pathstr)
1403 {
1404 struct lwm2m_obj_path path;
1405 int ret = 0;
1406
1407 LOG_DBG("path: %s", log_strdup(pathstr));
1408
1409 /* translate path -> path_obj */
1410 ret = string_to_path(pathstr, &path, '/');
1411 if (ret < 0) {
1412 return ret;
1413 }
1414
1415 if (path.level != 2U) {
1416 LOG_ERR("path must have 2 parts");
1417 return -EINVAL;
1418 }
1419
1420 ret = lwm2m_delete_obj_inst(path.obj_id, path.obj_inst_id);
1421 if (ret < 0) {
1422 return ret;
1423 }
1424
1425 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT)
1426 engine_trigger_update(true);
1427 #endif
1428
1429 return 0;
1430 }
1431
1432
lwm2m_engine_set_res_data(char * pathstr,void * data_ptr,uint16_t data_len,uint8_t data_flags)1433 int lwm2m_engine_set_res_data(char *pathstr, void *data_ptr, uint16_t data_len,
1434 uint8_t data_flags)
1435 {
1436 struct lwm2m_obj_path path;
1437 struct lwm2m_engine_res_inst *res_inst = NULL;
1438 int ret = 0;
1439
1440 /* translate path -> path_obj */
1441 ret = string_to_path(pathstr, &path, '/');
1442 if (ret < 0) {
1443 return ret;
1444 }
1445
1446 if (path.level < 3) {
1447 LOG_ERR("path must have at least 3 parts");
1448 return -EINVAL;
1449 }
1450
1451 /* look up resource obj */
1452 ret = path_to_objs(&path, NULL, NULL, NULL, &res_inst);
1453 if (ret < 0) {
1454 return ret;
1455 }
1456
1457 if (!res_inst) {
1458 LOG_ERR("res instance %d not found", path.res_inst_id);
1459 return -ENOENT;
1460 }
1461
1462 /* assign data elements */
1463 res_inst->data_ptr = data_ptr;
1464 res_inst->data_len = data_len;
1465 res_inst->max_data_len = data_len;
1466 res_inst->data_flags = data_flags;
1467
1468 return ret;
1469 }
1470
lwm2m_engine_set(char * pathstr,void * value,uint16_t len)1471 static int lwm2m_engine_set(char *pathstr, void *value, uint16_t len)
1472 {
1473 struct lwm2m_obj_path path;
1474 struct lwm2m_engine_obj_inst *obj_inst;
1475 struct lwm2m_engine_obj_field *obj_field;
1476 struct lwm2m_engine_res *res = NULL;
1477 struct lwm2m_engine_res_inst *res_inst = NULL;
1478 void *data_ptr = NULL;
1479 size_t max_data_len = 0;
1480 int ret = 0;
1481 bool changed = false;
1482
1483 LOG_DBG("path:%s, value:%p, len:%d", log_strdup(pathstr), value, len);
1484
1485 /* translate path -> path_obj */
1486 ret = string_to_path(pathstr, &path, '/');
1487 if (ret < 0) {
1488 return ret;
1489 }
1490
1491 if (path.level < 3) {
1492 LOG_ERR("path must have at least 3 parts");
1493 return -EINVAL;
1494 }
1495
1496 /* look up resource obj */
1497 ret = path_to_objs(&path, &obj_inst, &obj_field, &res, &res_inst);
1498 if (ret < 0) {
1499 return ret;
1500 }
1501
1502 if (!res_inst) {
1503 LOG_ERR("res instance %d not found", path.res_inst_id);
1504 return -ENOENT;
1505 }
1506
1507 if (LWM2M_HAS_RES_FLAG(res_inst, LWM2M_RES_DATA_FLAG_RO)) {
1508 LOG_ERR("res instance data pointer is read-only "
1509 "[%u/%u/%u/%u:%u]", path.obj_id, path.obj_inst_id,
1510 path.res_id, path.res_inst_id, path.level);
1511 return -EACCES;
1512 }
1513
1514 /* setup initial data elements */
1515 data_ptr = res_inst->data_ptr;
1516 max_data_len = res_inst->max_data_len;
1517
1518 /* allow user to override data elements via callback */
1519 if (res->pre_write_cb) {
1520 data_ptr = res->pre_write_cb(obj_inst->obj_inst_id,
1521 res->res_id, res_inst->res_inst_id,
1522 &max_data_len);
1523 }
1524
1525 if (!data_ptr) {
1526 LOG_ERR("res instance data pointer is NULL [%u/%u/%u/%u:%u]",
1527 path.obj_id, path.obj_inst_id, path.res_id,
1528 path.res_inst_id, path.level);
1529 return -EINVAL;
1530 }
1531
1532 /* check length (note: we add 1 to string length for NULL pad) */
1533 if (len > max_data_len -
1534 (obj_field->data_type == LWM2M_RES_TYPE_STRING ? 1 : 0)) {
1535 LOG_ERR("length %u is too long for res instance %d data",
1536 len, path.res_id);
1537 return -ENOMEM;
1538 }
1539
1540 if (memcmp(data_ptr, value, len) != 0) {
1541 changed = true;
1542 }
1543
1544 #if CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0
1545 if (res->validate_cb) {
1546 ret = res->validate_cb(obj_inst->obj_inst_id, res->res_id,
1547 res_inst->res_inst_id, value,
1548 len, false, 0);
1549 if (ret < 0) {
1550 return -EINVAL;
1551 }
1552 }
1553 #endif /* CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0 */
1554
1555 switch (obj_field->data_type) {
1556
1557 case LWM2M_RES_TYPE_OPAQUE:
1558 memcpy((uint8_t *)data_ptr, value, len);
1559 break;
1560
1561 case LWM2M_RES_TYPE_STRING:
1562 memcpy((uint8_t *)data_ptr, value, len);
1563 ((uint8_t *)data_ptr)[len] = '\0';
1564 break;
1565
1566 case LWM2M_RES_TYPE_U32:
1567 case LWM2M_RES_TYPE_TIME:
1568 *((uint32_t *)data_ptr) = *(uint32_t *)value;
1569 break;
1570
1571 case LWM2M_RES_TYPE_U16:
1572 *((uint16_t *)data_ptr) = *(uint16_t *)value;
1573 break;
1574
1575 case LWM2M_RES_TYPE_U8:
1576 *((uint8_t *)data_ptr) = *(uint8_t *)value;
1577 break;
1578
1579 case LWM2M_RES_TYPE_S64:
1580 *((int64_t *)data_ptr) = *(int64_t *)value;
1581 break;
1582
1583 case LWM2M_RES_TYPE_S32:
1584 *((int32_t *)data_ptr) = *(int32_t *)value;
1585 break;
1586
1587 case LWM2M_RES_TYPE_S16:
1588 *((int16_t *)data_ptr) = *(int16_t *)value;
1589 break;
1590
1591 case LWM2M_RES_TYPE_S8:
1592 *((int8_t *)data_ptr) = *(int8_t *)value;
1593 break;
1594
1595 case LWM2M_RES_TYPE_BOOL:
1596 *((bool *)data_ptr) = *(bool *)value;
1597 break;
1598
1599 case LWM2M_RES_TYPE_FLOAT:
1600 ((float32_value_t *)data_ptr)->val1 =
1601 ((float32_value_t *)value)->val1;
1602 ((float32_value_t *)data_ptr)->val2 =
1603 ((float32_value_t *)value)->val2;
1604 break;
1605
1606 case LWM2M_RES_TYPE_OBJLNK:
1607 *((struct lwm2m_objlnk *)data_ptr) =
1608 *(struct lwm2m_objlnk *)value;
1609 break;
1610
1611 default:
1612 LOG_ERR("unknown obj data_type %d", obj_field->data_type);
1613 return -EINVAL;
1614
1615 }
1616
1617 res_inst->data_len = len;
1618
1619 if (res->post_write_cb) {
1620 ret = res->post_write_cb(obj_inst->obj_inst_id,
1621 res->res_id, res_inst->res_inst_id,
1622 data_ptr, len, false, 0);
1623 }
1624
1625 if (changed && LWM2M_HAS_PERM(obj_field, LWM2M_PERM_R)) {
1626 NOTIFY_OBSERVER_PATH(&path);
1627 }
1628
1629 return ret;
1630 }
1631
lwm2m_engine_set_opaque(char * pathstr,char * data_ptr,uint16_t data_len)1632 int lwm2m_engine_set_opaque(char *pathstr, char *data_ptr, uint16_t data_len)
1633 {
1634 return lwm2m_engine_set(pathstr, data_ptr, data_len);
1635 }
1636
lwm2m_engine_set_string(char * pathstr,char * data_ptr)1637 int lwm2m_engine_set_string(char *pathstr, char *data_ptr)
1638 {
1639 return lwm2m_engine_set(pathstr, data_ptr, strlen(data_ptr));
1640 }
1641
lwm2m_engine_set_u8(char * pathstr,uint8_t value)1642 int lwm2m_engine_set_u8(char *pathstr, uint8_t value)
1643 {
1644 return lwm2m_engine_set(pathstr, &value, 1);
1645 }
1646
lwm2m_engine_set_u16(char * pathstr,uint16_t value)1647 int lwm2m_engine_set_u16(char *pathstr, uint16_t value)
1648 {
1649 return lwm2m_engine_set(pathstr, &value, 2);
1650 }
1651
lwm2m_engine_set_u32(char * pathstr,uint32_t value)1652 int lwm2m_engine_set_u32(char *pathstr, uint32_t value)
1653 {
1654 return lwm2m_engine_set(pathstr, &value, 4);
1655 }
1656
lwm2m_engine_set_u64(char * pathstr,uint64_t value)1657 int lwm2m_engine_set_u64(char *pathstr, uint64_t value)
1658 {
1659 return lwm2m_engine_set(pathstr, &value, 8);
1660 }
1661
lwm2m_engine_set_s8(char * pathstr,int8_t value)1662 int lwm2m_engine_set_s8(char *pathstr, int8_t value)
1663 {
1664 return lwm2m_engine_set(pathstr, &value, 1);
1665 }
1666
lwm2m_engine_set_s16(char * pathstr,int16_t value)1667 int lwm2m_engine_set_s16(char *pathstr, int16_t value)
1668 {
1669 return lwm2m_engine_set(pathstr, &value, 2);
1670 }
1671
lwm2m_engine_set_s32(char * pathstr,int32_t value)1672 int lwm2m_engine_set_s32(char *pathstr, int32_t value)
1673 {
1674 return lwm2m_engine_set(pathstr, &value, 4);
1675 }
1676
lwm2m_engine_set_s64(char * pathstr,int64_t value)1677 int lwm2m_engine_set_s64(char *pathstr, int64_t value)
1678 {
1679 return lwm2m_engine_set(pathstr, &value, 8);
1680 }
1681
lwm2m_engine_set_bool(char * pathstr,bool value)1682 int lwm2m_engine_set_bool(char *pathstr, bool value)
1683 {
1684 uint8_t temp = (value != 0 ? 1 : 0);
1685
1686 return lwm2m_engine_set(pathstr, &temp, 1);
1687 }
1688
lwm2m_engine_set_float32(char * pathstr,float32_value_t * value)1689 int lwm2m_engine_set_float32(char *pathstr, float32_value_t *value)
1690 {
1691 return lwm2m_engine_set(pathstr, value, sizeof(float32_value_t));
1692 }
1693
lwm2m_engine_set_objlnk(char * pathstr,struct lwm2m_objlnk * value)1694 int lwm2m_engine_set_objlnk(char *pathstr, struct lwm2m_objlnk *value)
1695 {
1696 return lwm2m_engine_set(pathstr, value, sizeof(struct lwm2m_objlnk));
1697 }
1698
1699 /* user data getter functions */
1700
lwm2m_engine_get_res_data(char * pathstr,void ** data_ptr,uint16_t * data_len,uint8_t * data_flags)1701 int lwm2m_engine_get_res_data(char *pathstr, void **data_ptr, uint16_t *data_len,
1702 uint8_t *data_flags)
1703 {
1704 struct lwm2m_obj_path path;
1705 struct lwm2m_engine_res_inst *res_inst = NULL;
1706 int ret = 0;
1707
1708 /* translate path -> path_obj */
1709 ret = string_to_path(pathstr, &path, '/');
1710 if (ret < 0) {
1711 return ret;
1712 }
1713
1714 if (path.level < 3) {
1715 LOG_ERR("path must have at least 3 parts");
1716 return -EINVAL;
1717 }
1718
1719 /* look up resource obj */
1720 ret = path_to_objs(&path, NULL, NULL, NULL, &res_inst);
1721 if (ret < 0) {
1722 return ret;
1723 }
1724
1725 if (!res_inst) {
1726 LOG_ERR("res instance %d not found", path.res_inst_id);
1727 return -ENOENT;
1728 }
1729
1730 *data_ptr = res_inst->data_ptr;
1731 *data_len = res_inst->data_len;
1732 *data_flags = res_inst->data_flags;
1733
1734 return 0;
1735 }
1736
lwm2m_engine_get(char * pathstr,void * buf,uint16_t buflen)1737 static int lwm2m_engine_get(char *pathstr, void *buf, uint16_t buflen)
1738 {
1739 int ret = 0;
1740 struct lwm2m_obj_path path;
1741 struct lwm2m_engine_obj_inst *obj_inst;
1742 struct lwm2m_engine_obj_field *obj_field;
1743 struct lwm2m_engine_res *res = NULL;
1744 struct lwm2m_engine_res_inst *res_inst = NULL;
1745 void *data_ptr = NULL;
1746 size_t data_len = 0;
1747
1748 LOG_DBG("path:%s, buf:%p, buflen:%d", log_strdup(pathstr), buf, buflen);
1749
1750 /* translate path -> path_obj */
1751 ret = string_to_path(pathstr, &path, '/');
1752 if (ret < 0) {
1753 return ret;
1754 }
1755
1756 if (path.level < 3) {
1757 LOG_ERR("path must have at least 3 parts");
1758 return -EINVAL;
1759 }
1760
1761 /* look up resource obj */
1762 ret = path_to_objs(&path, &obj_inst, &obj_field, &res, &res_inst);
1763 if (ret < 0) {
1764 return ret;
1765 }
1766
1767 if (!res_inst) {
1768 LOG_ERR("res instance %d not found", path.res_inst_id);
1769 return -ENOENT;
1770 }
1771
1772 /* setup initial data elements */
1773 data_ptr = res_inst->data_ptr;
1774 data_len = res_inst->data_len;
1775
1776 /* allow user to override data elements via callback */
1777 if (res->read_cb) {
1778 data_ptr = res->read_cb(obj_inst->obj_inst_id,
1779 res->res_id, res_inst->res_inst_id,
1780 &data_len);
1781 }
1782
1783 /* TODO: handle data_len > buflen case */
1784
1785 if (data_ptr && data_len > 0) {
1786 switch (obj_field->data_type) {
1787
1788 case LWM2M_RES_TYPE_OPAQUE:
1789 if (data_len > buflen) {
1790 return -ENOMEM;
1791 }
1792
1793 memcpy(buf, data_ptr, data_len);
1794 break;
1795
1796 case LWM2M_RES_TYPE_STRING:
1797 strncpy((uint8_t *)buf, (uint8_t *)data_ptr, buflen);
1798 break;
1799
1800 case LWM2M_RES_TYPE_U32:
1801 case LWM2M_RES_TYPE_TIME:
1802 *(uint32_t *)buf = *(uint32_t *)data_ptr;
1803 break;
1804
1805 case LWM2M_RES_TYPE_U16:
1806 *(uint16_t *)buf = *(uint16_t *)data_ptr;
1807 break;
1808
1809 case LWM2M_RES_TYPE_U8:
1810 *(uint8_t *)buf = *(uint8_t *)data_ptr;
1811 break;
1812
1813 case LWM2M_RES_TYPE_S64:
1814 *(int64_t *)buf = *(int64_t *)data_ptr;
1815 break;
1816
1817 case LWM2M_RES_TYPE_S32:
1818 *(int32_t *)buf = *(int32_t *)data_ptr;
1819 break;
1820
1821 case LWM2M_RES_TYPE_S16:
1822 *(int16_t *)buf = *(int16_t *)data_ptr;
1823 break;
1824
1825 case LWM2M_RES_TYPE_S8:
1826 *(int8_t *)buf = *(int8_t *)data_ptr;
1827 break;
1828
1829 case LWM2M_RES_TYPE_BOOL:
1830 *(bool *)buf = *(bool *)data_ptr;
1831 break;
1832
1833 case LWM2M_RES_TYPE_FLOAT:
1834 ((float32_value_t *)buf)->val1 =
1835 ((float32_value_t *)data_ptr)->val1;
1836 ((float32_value_t *)buf)->val2 =
1837 ((float32_value_t *)data_ptr)->val2;
1838 break;
1839
1840 case LWM2M_RES_TYPE_OBJLNK:
1841 *(struct lwm2m_objlnk *)buf =
1842 *(struct lwm2m_objlnk *)data_ptr;
1843 break;
1844
1845 default:
1846 LOG_ERR("unknown obj data_type %d",
1847 obj_field->data_type);
1848 return -EINVAL;
1849
1850 }
1851 }
1852
1853 return 0;
1854 }
1855
lwm2m_engine_get_opaque(char * pathstr,void * buf,uint16_t buflen)1856 int lwm2m_engine_get_opaque(char *pathstr, void *buf, uint16_t buflen)
1857 {
1858 return lwm2m_engine_get(pathstr, buf, buflen);
1859 }
1860
lwm2m_engine_get_string(char * pathstr,void * buf,uint16_t buflen)1861 int lwm2m_engine_get_string(char *pathstr, void *buf, uint16_t buflen)
1862 {
1863 return lwm2m_engine_get(pathstr, buf, buflen);
1864 }
1865
lwm2m_engine_get_u8(char * pathstr,uint8_t * value)1866 int lwm2m_engine_get_u8(char *pathstr, uint8_t *value)
1867 {
1868 return lwm2m_engine_get(pathstr, value, 1);
1869 }
1870
lwm2m_engine_get_u16(char * pathstr,uint16_t * value)1871 int lwm2m_engine_get_u16(char *pathstr, uint16_t *value)
1872 {
1873 return lwm2m_engine_get(pathstr, value, 2);
1874 }
1875
lwm2m_engine_get_u32(char * pathstr,uint32_t * value)1876 int lwm2m_engine_get_u32(char *pathstr, uint32_t *value)
1877 {
1878 return lwm2m_engine_get(pathstr, value, 4);
1879 }
1880
lwm2m_engine_get_u64(char * pathstr,uint64_t * value)1881 int lwm2m_engine_get_u64(char *pathstr, uint64_t *value)
1882 {
1883 return lwm2m_engine_get(pathstr, value, 8);
1884 }
1885
lwm2m_engine_get_s8(char * pathstr,int8_t * value)1886 int lwm2m_engine_get_s8(char *pathstr, int8_t *value)
1887 {
1888 return lwm2m_engine_get(pathstr, value, 1);
1889 }
1890
lwm2m_engine_get_s16(char * pathstr,int16_t * value)1891 int lwm2m_engine_get_s16(char *pathstr, int16_t *value)
1892 {
1893 return lwm2m_engine_get(pathstr, value, 2);
1894 }
1895
lwm2m_engine_get_s32(char * pathstr,int32_t * value)1896 int lwm2m_engine_get_s32(char *pathstr, int32_t *value)
1897 {
1898 return lwm2m_engine_get(pathstr, value, 4);
1899 }
1900
lwm2m_engine_get_s64(char * pathstr,int64_t * value)1901 int lwm2m_engine_get_s64(char *pathstr, int64_t *value)
1902 {
1903 return lwm2m_engine_get(pathstr, value, 8);
1904 }
1905
lwm2m_engine_get_bool(char * pathstr,bool * value)1906 int lwm2m_engine_get_bool(char *pathstr, bool *value)
1907 {
1908 int ret = 0;
1909 int8_t temp = 0;
1910
1911 ret = lwm2m_engine_get_s8(pathstr, &temp);
1912 if (!ret) {
1913 *value = temp != 0;
1914 }
1915
1916 return ret;
1917 }
1918
lwm2m_engine_get_float32(char * pathstr,float32_value_t * buf)1919 int lwm2m_engine_get_float32(char *pathstr, float32_value_t *buf)
1920 {
1921 return lwm2m_engine_get(pathstr, buf, sizeof(float32_value_t));
1922 }
1923
lwm2m_engine_get_objlnk(char * pathstr,struct lwm2m_objlnk * buf)1924 int lwm2m_engine_get_objlnk(char *pathstr, struct lwm2m_objlnk *buf)
1925 {
1926 return lwm2m_engine_get(pathstr, buf, sizeof(struct lwm2m_objlnk));
1927 }
1928
lwm2m_engine_get_resource(char * pathstr,struct lwm2m_engine_res ** res)1929 int lwm2m_engine_get_resource(char *pathstr, struct lwm2m_engine_res **res)
1930 {
1931 int ret;
1932 struct lwm2m_obj_path path;
1933
1934 ret = string_to_path(pathstr, &path, '/');
1935 if (ret < 0) {
1936 return ret;
1937 }
1938
1939 if (path.level < 3) {
1940 LOG_ERR("path must have 3 parts");
1941 return -EINVAL;
1942 }
1943
1944 return path_to_objs(&path, NULL, NULL, res, NULL);
1945 }
1946
lwm2m_engine_update_observer_min_period(char * pathstr,uint32_t period_s)1947 int lwm2m_engine_update_observer_min_period(char *pathstr, uint32_t period_s)
1948 {
1949 int i, ret;
1950 struct lwm2m_obj_path path;
1951
1952 ret = string_to_path(pathstr, &path, '/');
1953 if (ret < 0) {
1954 return ret;
1955 }
1956
1957 for (i = 0; i < CONFIG_LWM2M_ENGINE_MAX_OBSERVER; i++) {
1958 if (observe_node_data[i].path.level == path.level &&
1959 observe_node_data[i].path.obj_id == path.obj_id &&
1960 (path.level >= 2 ?
1961 observe_node_data[i].path.obj_inst_id == path.obj_inst_id : true) &&
1962 (path.level >= 3 ?
1963 observe_node_data[i].path.res_id == path.res_id : true)) {
1964
1965 observe_node_data[i].min_period_sec = period_s;
1966 return 0;
1967 }
1968 }
1969
1970 return -ENOENT;
1971 }
1972
lwm2m_engine_update_observer_max_period(char * pathstr,uint32_t period_s)1973 int lwm2m_engine_update_observer_max_period(char *pathstr, uint32_t period_s)
1974 {
1975 int i, ret;
1976 struct lwm2m_obj_path path;
1977
1978 ret = string_to_path(pathstr, &path, '/');
1979 if (ret < 0) {
1980 return ret;
1981 }
1982
1983 for (i = 0; i < CONFIG_LWM2M_ENGINE_MAX_OBSERVER; i++) {
1984 if (observe_node_data[i].path.level == path.level &&
1985 observe_node_data[i].path.obj_id == path.obj_id &&
1986 (path.level >= 2 ?
1987 observe_node_data[i].path.obj_inst_id == path.obj_inst_id : true) &&
1988 (path.level >= 3 ?
1989 observe_node_data[i].path.res_id == path.res_id : true)) {
1990
1991 observe_node_data[i].max_period_sec = period_s;
1992 return 0;
1993 }
1994 }
1995
1996 return -ENOENT;
1997 }
1998
lwm2m_engine_get_binding(char * binding)1999 void lwm2m_engine_get_binding(char *binding)
2000 {
2001 if (IS_ENABLED(CONFIG_LWM2M_QUEUE_MODE_ENABLED)) {
2002 strcpy(binding, "UQ");
2003 } else {
2004 /* Defaults to UDP. */
2005 strcpy(binding, "U");
2006 }
2007 }
2008
lwm2m_engine_create_res_inst(char * pathstr)2009 int lwm2m_engine_create_res_inst(char *pathstr)
2010 {
2011 int ret, i;
2012 struct lwm2m_engine_res *res = NULL;
2013 struct lwm2m_engine_res_inst *res_inst = NULL;
2014 struct lwm2m_obj_path path;
2015
2016 ret = string_to_path(pathstr, &path, '/');
2017 if (ret < 0) {
2018 return ret;
2019 }
2020
2021 if (path.level < 4) {
2022 LOG_ERR("path must have 4 parts");
2023 return -EINVAL;
2024 }
2025
2026 ret = path_to_objs(&path, NULL, NULL, &res, &res_inst);
2027 if (ret < 0) {
2028 return ret;
2029 }
2030
2031 if (!res) {
2032 LOG_ERR("resource %u not found", path.res_id);
2033 return -ENOENT;
2034 }
2035
2036 if (res_inst && res_inst->res_inst_id != RES_INSTANCE_NOT_CREATED) {
2037 LOG_ERR("res instance %u already exists", path.res_inst_id);
2038 return -EINVAL;
2039 }
2040
2041 if (!res->res_instances || res->res_inst_count == 0) {
2042 LOG_ERR("no available res instances");
2043 return -ENOMEM;
2044 }
2045
2046 for (i = 0; i < res->res_inst_count; i++) {
2047 if (res->res_instances[i].res_inst_id ==
2048 RES_INSTANCE_NOT_CREATED) {
2049 break;
2050 }
2051 }
2052
2053 if (i >= res->res_inst_count) {
2054 LOG_ERR("no available res instances");
2055 return -ENOMEM;
2056 }
2057
2058 res->res_instances[i].res_inst_id = path.res_inst_id;
2059 return 0;
2060 }
2061
lwm2m_engine_delete_res_inst(char * pathstr)2062 int lwm2m_engine_delete_res_inst(char *pathstr)
2063 {
2064 int ret;
2065 struct lwm2m_engine_res_inst *res_inst = NULL;
2066 struct lwm2m_obj_path path;
2067
2068 ret = string_to_path(pathstr, &path, '/');
2069 if (ret < 0) {
2070 return ret;
2071 }
2072
2073 if (path.level < 4) {
2074 LOG_ERR("path must have 4 parts");
2075 return -EINVAL;
2076 }
2077
2078 ret = path_to_objs(&path, NULL, NULL, NULL, &res_inst);
2079 if (ret < 0) {
2080 return ret;
2081 }
2082
2083 if (!res_inst) {
2084 LOG_ERR("res instance %u not found", path.res_inst_id);
2085 return -ENOENT;
2086 }
2087
2088 res_inst->data_ptr = NULL;
2089 res_inst->max_data_len = 0U;
2090 res_inst->data_len = 0U;
2091 res_inst->res_inst_id = RES_INSTANCE_NOT_CREATED;
2092
2093 return 0;
2094 }
2095
lwm2m_engine_register_read_callback(char * pathstr,lwm2m_engine_get_data_cb_t cb)2096 int lwm2m_engine_register_read_callback(char *pathstr,
2097 lwm2m_engine_get_data_cb_t cb)
2098 {
2099 int ret;
2100 struct lwm2m_engine_res *res = NULL;
2101
2102 ret = lwm2m_engine_get_resource(pathstr, &res);
2103 if (ret < 0) {
2104 return ret;
2105 }
2106
2107 res->read_cb = cb;
2108 return 0;
2109 }
2110
lwm2m_engine_register_pre_write_callback(char * pathstr,lwm2m_engine_get_data_cb_t cb)2111 int lwm2m_engine_register_pre_write_callback(char *pathstr,
2112 lwm2m_engine_get_data_cb_t cb)
2113 {
2114 int ret;
2115 struct lwm2m_engine_res *res = NULL;
2116
2117 ret = lwm2m_engine_get_resource(pathstr, &res);
2118 if (ret < 0) {
2119 return ret;
2120 }
2121
2122 res->pre_write_cb = cb;
2123 return 0;
2124 }
2125
lwm2m_engine_register_validate_callback(char * pathstr,lwm2m_engine_set_data_cb_t cb)2126 int lwm2m_engine_register_validate_callback(char *pathstr,
2127 lwm2m_engine_set_data_cb_t cb)
2128 {
2129 #if CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0
2130 int ret;
2131 struct lwm2m_engine_res *res = NULL;
2132
2133 ret = lwm2m_engine_get_resource(pathstr, &res);
2134 if (ret < 0) {
2135 return ret;
2136 }
2137
2138 res->validate_cb = cb;
2139 return 0;
2140 #else
2141 ARG_UNUSED(pathstr);
2142 ARG_UNUSED(cb);
2143
2144 LOG_ERR("Validation disabled. Set "
2145 "CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0 to "
2146 "enable validation support.");
2147 return -ENOTSUP;
2148 #endif /* CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0 */
2149 }
2150
lwm2m_engine_register_post_write_callback(char * pathstr,lwm2m_engine_set_data_cb_t cb)2151 int lwm2m_engine_register_post_write_callback(char *pathstr,
2152 lwm2m_engine_set_data_cb_t cb)
2153 {
2154 int ret;
2155 struct lwm2m_engine_res *res = NULL;
2156
2157 ret = lwm2m_engine_get_resource(pathstr, &res);
2158 if (ret < 0) {
2159 return ret;
2160 }
2161
2162 res->post_write_cb = cb;
2163 return 0;
2164 }
2165
lwm2m_engine_register_exec_callback(char * pathstr,lwm2m_engine_execute_cb_t cb)2166 int lwm2m_engine_register_exec_callback(char *pathstr,
2167 lwm2m_engine_execute_cb_t cb)
2168 {
2169 int ret;
2170 struct lwm2m_engine_res *res = NULL;
2171
2172 ret = lwm2m_engine_get_resource(pathstr, &res);
2173 if (ret < 0) {
2174 return ret;
2175 }
2176
2177 res->execute_cb = cb;
2178 return 0;
2179 }
2180
lwm2m_engine_register_create_callback(uint16_t obj_id,lwm2m_engine_user_cb_t cb)2181 int lwm2m_engine_register_create_callback(uint16_t obj_id,
2182 lwm2m_engine_user_cb_t cb)
2183 {
2184 struct lwm2m_engine_obj *obj = NULL;
2185
2186 obj = get_engine_obj(obj_id);
2187 if (!obj) {
2188 LOG_ERR("unable to find obj: %u", obj_id);
2189 return -ENOENT;
2190 }
2191
2192 obj->user_create_cb = cb;
2193 return 0;
2194 }
2195
lwm2m_engine_register_delete_callback(uint16_t obj_id,lwm2m_engine_user_cb_t cb)2196 int lwm2m_engine_register_delete_callback(uint16_t obj_id,
2197 lwm2m_engine_user_cb_t cb)
2198 {
2199 struct lwm2m_engine_obj *obj = NULL;
2200
2201 obj = get_engine_obj(obj_id);
2202 if (!obj) {
2203 LOG_ERR("unable to find obj: %u", obj_id);
2204 return -ENOENT;
2205 }
2206
2207 obj->user_delete_cb = cb;
2208 return 0;
2209 }
2210
2211 /* generic data handlers */
2212
lwm2m_read_handler(struct lwm2m_engine_obj_inst * obj_inst,struct lwm2m_engine_res * res,struct lwm2m_engine_obj_field * obj_field,struct lwm2m_message * msg)2213 static int lwm2m_read_handler(struct lwm2m_engine_obj_inst *obj_inst,
2214 struct lwm2m_engine_res *res,
2215 struct lwm2m_engine_obj_field *obj_field,
2216 struct lwm2m_message *msg)
2217 {
2218 int i, loop_max = 1, found_values = 0;
2219 uint16_t res_inst_id_tmp = 0U;
2220 void *data_ptr = NULL;
2221 size_t data_len = 0;
2222
2223 if (!obj_inst || !res || !obj_field || !msg) {
2224 return -EINVAL;
2225 }
2226
2227 loop_max = res->res_inst_count;
2228 if (res->multi_res_inst) {
2229 /* search for valid resource instances */
2230 for (i = 0; i < loop_max; i++) {
2231 if (res->res_instances[i].res_inst_id !=
2232 RES_INSTANCE_NOT_CREATED) {
2233 found_values = 1;
2234 break;
2235 }
2236 }
2237
2238 if (!found_values) {
2239 return -ENOENT;
2240 }
2241
2242 engine_put_begin_ri(&msg->out, &msg->path);
2243 res_inst_id_tmp = msg->path.res_inst_id;
2244 }
2245
2246 for (i = 0; i < loop_max; i++) {
2247 if (res->res_instances[i].res_inst_id ==
2248 RES_INSTANCE_NOT_CREATED) {
2249 continue;
2250 }
2251
2252 if (res->res_inst_count > 1) {
2253 msg->path.res_inst_id =
2254 res->res_instances[i].res_inst_id;
2255 }
2256
2257 /* setup initial data elements */
2258 data_ptr = res->res_instances[i].data_ptr;
2259 data_len = res->res_instances[i].data_len;
2260
2261 /* allow user to override data elements via callback */
2262 if (res->read_cb) {
2263 data_ptr = res->read_cb(obj_inst->obj_inst_id,
2264 res->res_id,
2265 res->res_instances[i].res_inst_id,
2266 &data_len);
2267 }
2268
2269 if (!data_ptr || data_len == 0) {
2270 return -ENOENT;
2271 }
2272
2273 switch (obj_field->data_type) {
2274
2275 case LWM2M_RES_TYPE_OPAQUE:
2276 engine_put_opaque(&msg->out, &msg->path,
2277 (uint8_t *)data_ptr,
2278 data_len);
2279 break;
2280
2281 case LWM2M_RES_TYPE_STRING:
2282 engine_put_string(&msg->out, &msg->path,
2283 (uint8_t *)data_ptr,
2284 strlen((uint8_t *)data_ptr));
2285 break;
2286
2287 case LWM2M_RES_TYPE_U32:
2288 case LWM2M_RES_TYPE_TIME:
2289 engine_put_s64(&msg->out, &msg->path,
2290 (int64_t)*(uint32_t *)data_ptr);
2291 break;
2292
2293 case LWM2M_RES_TYPE_U16:
2294 engine_put_s32(&msg->out, &msg->path,
2295 (int32_t)*(uint16_t *)data_ptr);
2296 break;
2297
2298 case LWM2M_RES_TYPE_U8:
2299 engine_put_s16(&msg->out, &msg->path,
2300 (int16_t)*(uint8_t *)data_ptr);
2301 break;
2302
2303 case LWM2M_RES_TYPE_S64:
2304 engine_put_s64(&msg->out, &msg->path,
2305 *(int64_t *)data_ptr);
2306 break;
2307
2308 case LWM2M_RES_TYPE_S32:
2309 engine_put_s32(&msg->out, &msg->path,
2310 *(int32_t *)data_ptr);
2311 break;
2312
2313 case LWM2M_RES_TYPE_S16:
2314 engine_put_s16(&msg->out, &msg->path,
2315 *(int16_t *)data_ptr);
2316 break;
2317
2318 case LWM2M_RES_TYPE_S8:
2319 engine_put_s8(&msg->out, &msg->path,
2320 *(int8_t *)data_ptr);
2321 break;
2322
2323 case LWM2M_RES_TYPE_BOOL:
2324 engine_put_bool(&msg->out, &msg->path,
2325 *(bool *)data_ptr);
2326 break;
2327
2328 case LWM2M_RES_TYPE_FLOAT:
2329 engine_put_float32fix(&msg->out, &msg->path,
2330 (float32_value_t *)data_ptr);
2331 break;
2332
2333 case LWM2M_RES_TYPE_OBJLNK:
2334 engine_put_objlnk(&msg->out, &msg->path,
2335 (struct lwm2m_objlnk *)data_ptr);
2336 break;
2337
2338 default:
2339 LOG_ERR("unknown obj data_type %d",
2340 obj_field->data_type);
2341 return -EINVAL;
2342
2343 }
2344 }
2345
2346 if (res->multi_res_inst) {
2347 engine_put_end_ri(&msg->out, &msg->path);
2348 msg->path.res_inst_id = res_inst_id_tmp;
2349 }
2350
2351 return 0;
2352 }
2353
lwm2m_engine_get_opaque_more(struct lwm2m_input_context * in,uint8_t * buf,size_t buflen,struct lwm2m_opaque_context * opaque,bool * last_block)2354 size_t lwm2m_engine_get_opaque_more(struct lwm2m_input_context *in,
2355 uint8_t *buf, size_t buflen,
2356 struct lwm2m_opaque_context *opaque,
2357 bool *last_block)
2358 {
2359 uint32_t in_len = opaque->remaining;
2360 uint16_t remaining = in->in_cpkt->max_len - in->offset;
2361
2362 if (in_len > buflen) {
2363 in_len = buflen;
2364 }
2365
2366 if (in_len > remaining) {
2367 in_len = remaining;
2368 }
2369
2370 opaque->remaining -= in_len;
2371 remaining -= in_len;
2372 if (opaque->remaining == 0U || remaining == 0) {
2373 *last_block = true;
2374 }
2375
2376 if (buf_read(buf, in_len, CPKT_BUF_READ(in->in_cpkt),
2377 &in->offset) < 0) {
2378 *last_block = true;
2379 return 0;
2380 }
2381
2382 return (size_t)in_len;
2383 }
2384
lwm2m_write_handler_opaque(struct lwm2m_engine_obj_inst * obj_inst,struct lwm2m_engine_res * res,struct lwm2m_engine_res_inst * res_inst,struct lwm2m_message * msg,void * data_ptr,size_t data_len)2385 static int lwm2m_write_handler_opaque(struct lwm2m_engine_obj_inst *obj_inst,
2386 struct lwm2m_engine_res *res,
2387 struct lwm2m_engine_res_inst *res_inst,
2388 struct lwm2m_message *msg,
2389 void *data_ptr, size_t data_len)
2390 {
2391 size_t len = 1;
2392 bool last_pkt_block = false;
2393 int ret = 0;
2394 bool last_block = true;
2395 struct lwm2m_opaque_context opaque_ctx = { 0 };
2396 void *write_buf;
2397 size_t write_buf_len;
2398
2399 if (msg->in.block_ctx != NULL) {
2400 last_block = msg->in.block_ctx->last_block;
2401
2402 /* Restore the opaque context from the block context, if used.
2403 */
2404 opaque_ctx = msg->in.block_ctx->opaque;
2405 }
2406
2407 #if CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0
2408 /* In case validation callback is present, write data to the temporary
2409 * buffer first, for validation. Otherwise, write to the data buffer
2410 * directly.
2411 */
2412 if (res->validate_cb) {
2413 write_buf = msg->ctx->validate_buf;
2414 write_buf_len = sizeof(msg->ctx->validate_buf);
2415 } else
2416 #endif /* CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0 */
2417 {
2418 write_buf = data_ptr;
2419 write_buf_len = data_len;
2420 }
2421
2422 while (!last_pkt_block && len > 0) {
2423 len = engine_get_opaque(&msg->in, write_buf,
2424 MIN(data_len, write_buf_len),
2425 &opaque_ctx, &last_pkt_block);
2426 if (len == 0) {
2427 /* ignore empty content and continue */
2428 return 0;
2429 }
2430
2431 #if CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0
2432 if (res->validate_cb) {
2433 ret = res->validate_cb(
2434 obj_inst->obj_inst_id, res->res_id,
2435 res_inst->res_inst_id, write_buf, len,
2436 last_pkt_block && last_block, opaque_ctx.len);
2437 if (ret < 0) {
2438 /* -EEXIST will generate Bad Request LWM2M response. */
2439 return -EEXIST;
2440 }
2441
2442 memcpy(data_ptr, write_buf, len);
2443 }
2444 #endif /* CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0 */
2445
2446 if (res->post_write_cb) {
2447 ret = res->post_write_cb(
2448 obj_inst->obj_inst_id, res->res_id,
2449 res_inst->res_inst_id, data_ptr, len,
2450 last_pkt_block && last_block, opaque_ctx.len);
2451 if (ret < 0) {
2452 return ret;
2453 }
2454 }
2455 }
2456
2457 if (msg->in.block_ctx != NULL) {
2458 msg->in.block_ctx->opaque = opaque_ctx;
2459 }
2460
2461 return opaque_ctx.len;
2462 }
2463
2464 /* This function is exposed for the content format writers */
lwm2m_write_handler(struct lwm2m_engine_obj_inst * obj_inst,struct lwm2m_engine_res * res,struct lwm2m_engine_res_inst * res_inst,struct lwm2m_engine_obj_field * obj_field,struct lwm2m_message * msg)2465 int lwm2m_write_handler(struct lwm2m_engine_obj_inst *obj_inst,
2466 struct lwm2m_engine_res *res,
2467 struct lwm2m_engine_res_inst *res_inst,
2468 struct lwm2m_engine_obj_field *obj_field,
2469 struct lwm2m_message *msg)
2470 {
2471 void *data_ptr = NULL;
2472 size_t data_len = 0;
2473 size_t len = 0;
2474 size_t total_size = 0;
2475 int64_t temp64 = 0;
2476 int32_t temp32 = 0;
2477 int ret = 0;
2478 bool last_block = true;
2479 void *write_buf;
2480 size_t write_buf_len;
2481
2482 if (!obj_inst || !res || !res_inst || !obj_field || !msg) {
2483 return -EINVAL;
2484 }
2485
2486 if (LWM2M_HAS_RES_FLAG(res_inst, LWM2M_RES_DATA_FLAG_RO)) {
2487 return -EACCES;
2488 }
2489
2490 /* setup initial data elements */
2491 data_ptr = res_inst->data_ptr;
2492 data_len = res_inst->max_data_len;
2493
2494 /* allow user to override data elements via callback */
2495 if (res->pre_write_cb) {
2496 data_ptr = res->pre_write_cb(obj_inst->obj_inst_id,
2497 res->res_id, res_inst->res_inst_id,
2498 &data_len);
2499 }
2500
2501 if (res->post_write_cb
2502 #if CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0
2503 || res->validate_cb
2504 #endif
2505 ) {
2506 if (msg->in.block_ctx != NULL) {
2507 /* Get block_ctx for total_size (might be zero) */
2508 total_size = msg->in.block_ctx->ctx.total_size;
2509 LOG_DBG("BLOCK1: total:%zu current:%zu"
2510 " last:%u",
2511 msg->in.block_ctx->ctx.total_size,
2512 msg->in.block_ctx->ctx.current,
2513 msg->in.block_ctx->last_block);
2514 }
2515 }
2516
2517 #if CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0
2518 /* In case validation callback is present, write data to the temporary
2519 * buffer first, for validation. Otherwise, write to the data buffer
2520 * directly.
2521 */
2522 if (res->validate_cb) {
2523 write_buf = msg->ctx->validate_buf;
2524 write_buf_len = sizeof(msg->ctx->validate_buf);
2525 } else
2526 #endif /* CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0 */
2527 {
2528 write_buf = data_ptr;
2529 write_buf_len = data_len;
2530 }
2531
2532 if (data_ptr && data_len > 0) {
2533 switch (obj_field->data_type) {
2534
2535 case LWM2M_RES_TYPE_OPAQUE:
2536 ret = lwm2m_write_handler_opaque(obj_inst, res,
2537 res_inst, msg,
2538 data_ptr, data_len);
2539 if (ret < 0) {
2540 return ret;
2541 }
2542
2543 len = ret;
2544 break;
2545
2546 case LWM2M_RES_TYPE_STRING:
2547 engine_get_string(&msg->in, write_buf, write_buf_len);
2548 len = strlen((char *)write_buf);
2549 break;
2550
2551 case LWM2M_RES_TYPE_U32:
2552 case LWM2M_RES_TYPE_TIME:
2553 engine_get_s64(&msg->in, &temp64);
2554 *(uint32_t *)write_buf = temp64;
2555 len = 4;
2556 break;
2557
2558 case LWM2M_RES_TYPE_U16:
2559 engine_get_s32(&msg->in, &temp32);
2560 *(uint16_t *)write_buf = temp32;
2561 len = 2;
2562 break;
2563
2564 case LWM2M_RES_TYPE_U8:
2565 engine_get_s32(&msg->in, &temp32);
2566 *(uint8_t *)write_buf = temp32;
2567 len = 1;
2568 break;
2569
2570 case LWM2M_RES_TYPE_S64:
2571 engine_get_s64(&msg->in, (int64_t *)write_buf);
2572 len = 8;
2573 break;
2574
2575 case LWM2M_RES_TYPE_S32:
2576 engine_get_s32(&msg->in, (int32_t *)write_buf);
2577 len = 4;
2578 break;
2579
2580 case LWM2M_RES_TYPE_S16:
2581 engine_get_s32(&msg->in, &temp32);
2582 *(int16_t *)write_buf = temp32;
2583 len = 2;
2584 break;
2585
2586 case LWM2M_RES_TYPE_S8:
2587 engine_get_s32(&msg->in, &temp32);
2588 *(int8_t *)write_buf = temp32;
2589 len = 1;
2590 break;
2591
2592 case LWM2M_RES_TYPE_BOOL:
2593 engine_get_bool(&msg->in, (bool *)write_buf);
2594 len = 1;
2595 break;
2596
2597 case LWM2M_RES_TYPE_FLOAT:
2598 engine_get_float32fix(&msg->in,
2599 (float32_value_t *)write_buf);
2600 len = sizeof(float32_value_t);
2601 break;
2602
2603 case LWM2M_RES_TYPE_OBJLNK:
2604 engine_get_objlnk(&msg->in,
2605 (struct lwm2m_objlnk *)write_buf);
2606 len = sizeof(struct lwm2m_objlnk);
2607 break;
2608
2609 default:
2610 LOG_ERR("unknown obj data_type %d",
2611 obj_field->data_type);
2612 return -EINVAL;
2613
2614 }
2615 } else {
2616 return -ENOENT;
2617 }
2618
2619 if (obj_field->data_type != LWM2M_RES_TYPE_OPAQUE) {
2620 #if CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0
2621 if (res->validate_cb) {
2622 ret = res->validate_cb(
2623 obj_inst->obj_inst_id, res->res_id,
2624 res_inst->res_inst_id, write_buf, len,
2625 last_block, total_size);
2626 if (ret < 0) {
2627 /* -EEXIST will generate Bad Request LWM2M response. */
2628 return -EEXIST;
2629 }
2630
2631 if (len > data_len) {
2632 LOG_ERR("Received data won't fit into provided "
2633 "bufffer");
2634 return -ENOMEM;
2635 }
2636
2637 if (obj_field->data_type == LWM2M_RES_TYPE_STRING) {
2638 strncpy(data_ptr, write_buf, data_len);
2639 } else {
2640 memcpy(data_ptr, write_buf, len);
2641 }
2642 }
2643 #endif /* CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0 */
2644
2645 if (res->post_write_cb) {
2646 ret = res->post_write_cb(
2647 obj_inst->obj_inst_id, res->res_id,
2648 res_inst->res_inst_id, data_ptr, len,
2649 last_block, total_size);
2650 }
2651 }
2652
2653 res_inst->data_len = len;
2654
2655 if (LWM2M_HAS_PERM(obj_field, LWM2M_PERM_R)) {
2656 NOTIFY_OBSERVER_PATH(&msg->path);
2657 }
2658
2659 return ret;
2660 }
2661
lwm2m_write_attr_handler(struct lwm2m_engine_obj * obj,struct lwm2m_message * msg)2662 static int lwm2m_write_attr_handler(struct lwm2m_engine_obj *obj,
2663 struct lwm2m_message *msg)
2664 {
2665 bool update_observe_node = false;
2666 char opt_buf[COAP_OPTION_BUF_LEN];
2667 int nr_opt, i, ret = 0;
2668 struct coap_option options[NR_LWM2M_ATTR];
2669 struct lwm2m_engine_obj_inst *obj_inst = NULL;
2670 struct lwm2m_engine_res *res = NULL;
2671 struct lwm2m_attr *attr;
2672 struct notification_attrs nattrs = { 0 };
2673 struct observe_node *obs;
2674 uint8_t type = 0U;
2675 void *nattr_ptrs[NR_LWM2M_ATTR] = {
2676 &nattrs.pmin, &nattrs.pmax, &nattrs.gt, &nattrs.lt, &nattrs.st
2677 };
2678 void *ref;
2679
2680 if (!obj || !msg) {
2681 return -EINVAL;
2682 }
2683
2684 /* do not expose security obj */
2685 if (obj->obj_id == LWM2M_OBJECT_SECURITY_ID) {
2686 return -ENOENT;
2687 }
2688
2689 nr_opt = coap_find_options(msg->in.in_cpkt, COAP_OPTION_URI_QUERY,
2690 options, NR_LWM2M_ATTR);
2691 if (nr_opt <= 0) {
2692 LOG_ERR("No attribute found!");
2693 /* translate as bad request */
2694 return -EEXIST;
2695 }
2696
2697 /* get lwm2m_attr slist */
2698 if (msg->path.level == 3U) {
2699 ret = path_to_objs(&msg->path, NULL, NULL, &res, NULL);
2700 if (ret < 0) {
2701 return ret;
2702 }
2703
2704 ref = res;
2705 } else if (msg->path.level == 1U) {
2706 ref = obj;
2707 } else if (msg->path.level == 2U) {
2708 obj_inst = get_engine_obj_inst(msg->path.obj_id,
2709 msg->path.obj_inst_id);
2710 if (!obj_inst) {
2711 return -ENOENT;
2712 }
2713
2714 ref = obj_inst;
2715 } else {
2716 /* bad request */
2717 return -EEXIST;
2718 }
2719
2720 /* retrieve existing attributes */
2721 ret = update_attrs(ref, &nattrs);
2722 if (ret < 0) {
2723 return ret;
2724 }
2725
2726 /* loop through options to parse attribute */
2727 for (i = 0; i < nr_opt; i++) {
2728 int limit = MIN(options[i].len, 5), plen = 0, vlen;
2729 float32_value_t val = { 0 };
2730 type = 0U;
2731
2732 /* search for '=' */
2733 while (plen < limit && options[i].value[plen] != '=') {
2734 plen += 1;
2735 }
2736
2737 /* either length = 2(gt/lt/st) or = 4(pmin/pmax) */
2738 if (plen != 2 && plen != 4) {
2739 continue;
2740 }
2741
2742 /* matching attribute name */
2743 for (type = 0U; type < NR_LWM2M_ATTR; type++) {
2744 if (LWM2M_ATTR_LEN[type] == plen &&
2745 !memcmp(options[i].value, LWM2M_ATTR_STR[type],
2746 LWM2M_ATTR_LEN[type])) {
2747 break;
2748 }
2749 }
2750
2751 /* unrecognized attribute */
2752 if (type == NR_LWM2M_ATTR) {
2753 continue;
2754 }
2755
2756 /* unset attribute when no value's given */
2757 if (options[i].len == plen) {
2758 nattrs.flags &= ~BIT(type);
2759
2760 (void)memset(nattr_ptrs[type], 0,
2761 type <= LWM2M_ATTR_PMAX ? sizeof(int32_t) :
2762 sizeof(float32_value_t));
2763 continue;
2764 }
2765
2766 /* gt/lt/st cannot be assigned to obj/obj_inst unless unset */
2767 if (plen == 2 && msg->path.level <= 2U) {
2768 return -EEXIST;
2769 }
2770
2771 vlen = options[i].len - plen - 1;
2772 memcpy(opt_buf, options[i].value + plen + 1, vlen);
2773 opt_buf[vlen] = '\0';
2774
2775 /* convert value to integer or float */
2776 if (plen == 4) {
2777 char *end;
2778 long int v;
2779
2780 /* pmin/pmax: integer (sec 5.1.2)
2781 * however, negative is non-sense
2782 */
2783 errno = 0;
2784 v = strtol(opt_buf, &end, 10);
2785 if (errno || *end || v < 0) {
2786 ret = -EINVAL;
2787 }
2788
2789 val.val1 = v;
2790 } else {
2791 /* gt/lt/st: type float */
2792 ret = lwm2m_atof32(opt_buf, &val);
2793 }
2794
2795 if (ret < 0) {
2796 LOG_ERR("invalid attr[%s] value",
2797 log_strdup(LWM2M_ATTR_STR[type]));
2798 /* bad request */
2799 return -EEXIST;
2800 }
2801
2802 if (type <= LWM2M_ATTR_PMAX) {
2803 *(int32_t *)nattr_ptrs[type] = val.val1;
2804 } else {
2805 memcpy(nattr_ptrs[type], &val, sizeof(float32_value_t));
2806 }
2807
2808 nattrs.flags |= BIT(type);
2809 }
2810
2811 if ((nattrs.flags & (BIT(LWM2M_ATTR_PMIN) | BIT(LWM2M_ATTR_PMAX))) &&
2812 nattrs.pmin > nattrs.pmax) {
2813 LOG_DBG("pmin (%d) > pmax (%d)", nattrs.pmin, nattrs.pmax);
2814 return -EEXIST;
2815 }
2816
2817 if (nattrs.flags & (BIT(LWM2M_ATTR_LT) | BIT(LWM2M_ATTR_GT))) {
2818 if (!((nattrs.lt.val1 < nattrs.gt.val1) ||
2819 (nattrs.lt.val2 < nattrs.gt.val2))) {
2820 LOG_DBG("lt > gt");
2821 return -EEXIST;
2822 }
2823
2824 if (nattrs.flags & BIT(LWM2M_ATTR_STEP)) {
2825 int32_t st1 = nattrs.st.val1 * 2 +
2826 nattrs.st.val2 * 2 / 1000000;
2827 int32_t st2 = nattrs.st.val2 * 2 % 1000000;
2828 if (!(((nattrs.lt.val1 + st1) < nattrs.gt.val1) ||
2829 ((nattrs.lt.val2 + st2) < nattrs.gt.val2))) {
2830 LOG_DBG("lt + 2*st > gt");
2831 return -EEXIST;
2832 }
2833 }
2834 }
2835
2836 /* find matching attributes */
2837 for (i = 0; i < CONFIG_LWM2M_NUM_ATTR; i++) {
2838 if (ref != write_attr_pool[i].ref) {
2839 continue;
2840 }
2841
2842 attr = write_attr_pool + i;
2843 type = attr->type;
2844
2845 if (!(BIT(type) & nattrs.flags)) {
2846 LOG_DBG("Unset attr %s",
2847 log_strdup(LWM2M_ATTR_STR[type]));
2848 (void)memset(attr, 0, sizeof(*attr));
2849
2850 if (type <= LWM2M_ATTR_PMAX) {
2851 update_observe_node = true;
2852 }
2853
2854 continue;
2855 }
2856
2857 nattrs.flags &= ~BIT(type);
2858
2859 if (type <= LWM2M_ATTR_PMAX) {
2860 if (attr->int_val == *(int32_t *)nattr_ptrs[type]) {
2861 continue;
2862 }
2863
2864 attr->int_val = *(int32_t *)nattr_ptrs[type];
2865 update_observe_node = true;
2866 } else {
2867 if (!memcmp(&attr->float_val, nattr_ptrs[type],
2868 sizeof(float32_value_t))) {
2869 continue;
2870 }
2871
2872 memcpy(&attr->float_val, nattr_ptrs[type],
2873 sizeof(float32_value_t));
2874 }
2875
2876 LOG_DBG("Update %s to %d.%06d",
2877 log_strdup(LWM2M_ATTR_STR[type]),
2878 attr->float_val.val1, attr->float_val.val2);
2879 }
2880
2881 /* add attribute to obj/obj_inst/res */
2882 for (type = 0U; nattrs.flags && type < NR_LWM2M_ATTR; type++) {
2883 if (!(BIT(type) & nattrs.flags)) {
2884 continue;
2885 }
2886
2887 /* grab an entry for newly added attribute */
2888 for (i = 0; i < CONFIG_LWM2M_NUM_ATTR; i++) {
2889 if (!write_attr_pool[i].ref) {
2890 break;
2891 }
2892 }
2893
2894 if (i == CONFIG_LWM2M_NUM_ATTR) {
2895 return -ENOMEM;
2896 }
2897
2898 attr = write_attr_pool + i;
2899 attr->type = type;
2900 attr->ref = ref;
2901
2902 if (type <= LWM2M_ATTR_PMAX) {
2903 attr->int_val = *(int32_t *)nattr_ptrs[type];
2904 update_observe_node = true;
2905 } else {
2906 memcpy(&attr->float_val, nattr_ptrs[type],
2907 sizeof(float32_value_t));
2908 }
2909
2910 nattrs.flags &= ~BIT(type);
2911 LOG_DBG("Add %s to %d.%06d", log_strdup(LWM2M_ATTR_STR[type]),
2912 attr->float_val.val1, attr->float_val.val2);
2913 }
2914
2915 /* check only pmin/pmax */
2916 if (!update_observe_node) {
2917 return 0;
2918 }
2919
2920 /* update observe_node accordingly */
2921 SYS_SLIST_FOR_EACH_CONTAINER(&msg->ctx->observer, obs, node) {
2922 /* updated path is deeper than obs node, skip */
2923 if (msg->path.level > obs->path.level) {
2924 continue;
2925 }
2926
2927 /* check obj id matched or not */
2928 if (msg->path.obj_id != obs->path.obj_id) {
2929 continue;
2930 }
2931
2932 /* defaults from server object */
2933 nattrs.pmin = lwm2m_server_get_pmin(msg->ctx->srv_obj_inst);
2934 nattrs.pmax = lwm2m_server_get_pmax(msg->ctx->srv_obj_inst);
2935
2936 ret = update_attrs(obj, &nattrs);
2937 if (ret < 0) {
2938 return ret;
2939 }
2940
2941 if (obs->path.level > 1) {
2942 if (msg->path.level > 1 &&
2943 msg->path.obj_inst_id != obs->path.obj_inst_id) {
2944 continue;
2945 }
2946
2947 /* get obj_inst */
2948 if (!obj_inst || obj_inst->obj_inst_id !=
2949 obs->path.obj_inst_id) {
2950 obj_inst = get_engine_obj_inst(
2951 obs->path.obj_id,
2952 obs->path.obj_inst_id);
2953 if (!obj_inst) {
2954 return -ENOENT;
2955 }
2956 }
2957
2958 ret = update_attrs(obj_inst, &nattrs);
2959 if (ret < 0) {
2960 return ret;
2961 }
2962 }
2963
2964 if (obs->path.level > 2) {
2965 if (msg->path.level > 2 &&
2966 msg->path.res_id != obs->path.res_id) {
2967 continue;
2968 }
2969
2970 if (!res || res->res_id != obs->path.res_id) {
2971 ret = path_to_objs(&obs->path, NULL, NULL,
2972 &res, NULL);
2973 if (ret < 0) {
2974 return ret;
2975 }
2976 }
2977
2978 ret = update_attrs(res, &nattrs);
2979 if (ret < 0) {
2980 return ret;
2981 }
2982 }
2983
2984 LOG_DBG("%d/%d/%d(%d) updated from %d/%d to %u/%u",
2985 obs->path.obj_id, obs->path.obj_inst_id,
2986 obs->path.res_id, obs->path.level,
2987 obs->min_period_sec, obs->max_period_sec,
2988 nattrs.pmin, MAX(nattrs.pmin, nattrs.pmax));
2989 obs->min_period_sec = (uint32_t)nattrs.pmin;
2990 obs->max_period_sec = (uint32_t)MAX(nattrs.pmin, nattrs.pmax);
2991 (void)memset(&nattrs, 0, sizeof(nattrs));
2992 }
2993
2994 return 0;
2995 }
2996
lwm2m_exec_handler(struct lwm2m_message * msg)2997 static int lwm2m_exec_handler(struct lwm2m_message *msg)
2998 {
2999 struct lwm2m_engine_obj_inst *obj_inst;
3000 struct lwm2m_engine_res *res = NULL;
3001 int ret;
3002 uint8_t *args;
3003 uint16_t args_len;
3004
3005 if (!msg) {
3006 return -EINVAL;
3007 }
3008
3009 ret = path_to_objs(&msg->path, &obj_inst, NULL, &res, NULL);
3010 if (ret < 0) {
3011 return ret;
3012 }
3013
3014 args = (uint8_t *)coap_packet_get_payload(msg->in.in_cpkt, &args_len);
3015
3016 if (res->execute_cb) {
3017 return res->execute_cb(obj_inst->obj_inst_id, args, args_len);
3018 }
3019
3020 /* TODO: something else to handle for execute? */
3021 return -ENOENT;
3022 }
3023
lwm2m_delete_handler(struct lwm2m_message * msg)3024 static int lwm2m_delete_handler(struct lwm2m_message *msg)
3025 {
3026 int ret;
3027
3028 if (!msg) {
3029 return -EINVAL;
3030 }
3031
3032 /* Device management interface is not allowed to delete Security and
3033 * Device objects instances.
3034 */
3035 if (msg->path.obj_id == LWM2M_OBJECT_SECURITY_ID ||
3036 msg->path.obj_id == LWM2M_OBJECT_DEVICE_ID) {
3037 return -EPERM;
3038 }
3039
3040 ret = lwm2m_delete_obj_inst(msg->path.obj_id, msg->path.obj_inst_id);
3041 if (ret < 0) {
3042 return ret;
3043 }
3044
3045 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT)
3046 if (!msg->ctx->bootstrap_mode) {
3047 engine_trigger_update(true);
3048 }
3049 #endif
3050
3051 return 0;
3052 }
3053
do_read_op(struct lwm2m_message * msg,uint16_t content_format)3054 static int do_read_op(struct lwm2m_message *msg, uint16_t content_format)
3055 {
3056 switch (content_format) {
3057
3058 case LWM2M_FORMAT_APP_OCTET_STREAM:
3059 case LWM2M_FORMAT_PLAIN_TEXT:
3060 case LWM2M_FORMAT_OMA_PLAIN_TEXT:
3061 return do_read_op_plain_text(msg, content_format);
3062
3063 case LWM2M_FORMAT_OMA_TLV:
3064 case LWM2M_FORMAT_OMA_OLD_TLV:
3065 return do_read_op_tlv(msg, content_format);
3066
3067 #if defined(CONFIG_LWM2M_RW_JSON_SUPPORT)
3068 case LWM2M_FORMAT_OMA_JSON:
3069 case LWM2M_FORMAT_OMA_OLD_JSON:
3070 return do_read_op_json(msg, content_format);
3071 #endif
3072
3073 default:
3074 LOG_ERR("Unsupported content-format: %u", content_format);
3075 return -ENOMSG;
3076
3077 }
3078 }
3079
lwm2m_perform_read_op(struct lwm2m_message * msg,uint16_t content_format)3080 int lwm2m_perform_read_op(struct lwm2m_message *msg, uint16_t content_format)
3081 {
3082 struct lwm2m_engine_obj_inst *obj_inst = NULL;
3083 struct lwm2m_engine_res *res = NULL;
3084 struct lwm2m_engine_obj_field *obj_field;
3085 struct lwm2m_obj_path temp_path;
3086 int ret = 0, index;
3087 uint8_t num_read = 0U;
3088
3089 if (msg->path.level >= 2U) {
3090 obj_inst = get_engine_obj_inst(msg->path.obj_id,
3091 msg->path.obj_inst_id);
3092 } else if (msg->path.level == 1U) {
3093 /* find first obj_inst with path's obj_id */
3094 obj_inst = next_engine_obj_inst(msg->path.obj_id, -1);
3095 }
3096
3097 if (!obj_inst) {
3098 return -ENOENT;
3099 }
3100
3101 /* set output content-format */
3102 ret = coap_append_option_int(msg->out.out_cpkt,
3103 COAP_OPTION_CONTENT_FORMAT,
3104 content_format);
3105 if (ret < 0) {
3106 LOG_ERR("Error setting response content-format: %d", ret);
3107 return ret;
3108 }
3109
3110 ret = coap_packet_append_payload_marker(msg->out.out_cpkt);
3111 if (ret < 0) {
3112 LOG_ERR("Error appending payload marker: %d", ret);
3113 return ret;
3114 }
3115
3116 /* store original path values so we can change them during processing */
3117 memcpy(&temp_path, &msg->path, sizeof(temp_path));
3118 engine_put_begin(&msg->out, &msg->path);
3119
3120 while (obj_inst) {
3121 if (!obj_inst->resources || obj_inst->resource_count == 0U) {
3122 goto move_forward;
3123 }
3124
3125 /* update the obj_inst_id as we move through the instances */
3126 msg->path.obj_inst_id = obj_inst->obj_inst_id;
3127
3128 if (msg->path.level <= 1U) {
3129 /* start instance formatting */
3130 engine_put_begin_oi(&msg->out, &msg->path);
3131 }
3132
3133 for (index = 0; index < obj_inst->resource_count; index++) {
3134 if (msg->path.level > 2 &&
3135 msg->path.res_id !=
3136 obj_inst->resources[index].res_id) {
3137 continue;
3138 }
3139
3140 res = &obj_inst->resources[index];
3141
3142 /*
3143 * On an entire object instance, we need to set path's
3144 * res_id for lwm2m_read_handler to read this specific
3145 * resource.
3146 */
3147 msg->path.res_id = res->res_id;
3148 obj_field = lwm2m_get_engine_obj_field(obj_inst->obj,
3149 res->res_id);
3150 if (!obj_field) {
3151 ret = -ENOENT;
3152 } else if (!LWM2M_HAS_PERM(obj_field, LWM2M_PERM_R)) {
3153 ret = -EPERM;
3154 } else {
3155 /* start resource formatting */
3156 engine_put_begin_r(&msg->out, &msg->path);
3157
3158 /* perform read operation on this resource */
3159 ret = lwm2m_read_handler(obj_inst, res,
3160 obj_field, msg);
3161 if (ret < 0) {
3162 /* ignore errors unless single read */
3163 if (msg->path.level > 2 &&
3164 !LWM2M_HAS_PERM(obj_field,
3165 BIT(LWM2M_FLAG_OPTIONAL))) {
3166 LOG_ERR("READ OP: %d", ret);
3167 }
3168 } else {
3169 num_read += 1U;
3170 }
3171
3172 /* end resource formatting */
3173 engine_put_end_r(&msg->out, &msg->path);
3174 }
3175
3176 /* on single read break if errors */
3177 if (ret < 0 && msg->path.level > 2) {
3178 break;
3179 }
3180
3181 /* when reading multiple resources ignore return code */
3182 ret = 0;
3183 }
3184
3185 move_forward:
3186 if (msg->path.level <= 1U) {
3187 /* end instance formatting */
3188 engine_put_end_oi(&msg->out, &msg->path);
3189 }
3190
3191 if (msg->path.level <= 1U) {
3192 /* advance to the next object instance */
3193 obj_inst = next_engine_obj_inst(msg->path.obj_id,
3194 obj_inst->obj_inst_id);
3195 } else {
3196 obj_inst = NULL;
3197 }
3198 }
3199
3200 engine_put_end(&msg->out, &msg->path);
3201
3202 /* restore original path values */
3203 memcpy(&msg->path, &temp_path, sizeof(temp_path));
3204
3205 /* did not read anything even if we should have - on single item */
3206 if (ret == 0 && num_read == 0U && msg->path.level == 3U) {
3207 return -ENOENT;
3208 }
3209
3210 return ret;
3211 }
3212
lwm2m_discover_handler(struct lwm2m_message * msg,bool is_bootstrap)3213 int lwm2m_discover_handler(struct lwm2m_message *msg, bool is_bootstrap)
3214 {
3215 struct lwm2m_engine_obj *obj;
3216 struct lwm2m_engine_obj_inst *obj_inst;
3217 int ret;
3218 bool reported = false;
3219
3220 /* Object ID is required in Device Management Discovery (5.4.2). */
3221 if (!is_bootstrap &&
3222 (msg->path.level == LWM2M_PATH_LEVEL_NONE ||
3223 msg->path.obj_id == LWM2M_OBJECT_SECURITY_ID)) {
3224 return -EPERM;
3225 }
3226
3227 /* Bootstrap discovery allows to specify at most Object ID. */
3228 if (is_bootstrap && msg->path.level > LWM2M_PATH_LEVEL_OBJECT) {
3229 return -EPERM;
3230 }
3231
3232 /* set output content-format */
3233 ret = coap_append_option_int(msg->out.out_cpkt,
3234 COAP_OPTION_CONTENT_FORMAT,
3235 LWM2M_FORMAT_APP_LINK_FORMAT);
3236 if (ret < 0) {
3237 LOG_ERR("Error setting response content-format: %d", ret);
3238 return ret;
3239 }
3240
3241 ret = coap_packet_append_payload_marker(msg->out.out_cpkt);
3242 if (ret < 0) {
3243 return ret;
3244 }
3245
3246 /*
3247 * Add required prefix for bootstrap discovery (5.2.7.3).
3248 * For device management discovery, `engine_put_begin()` adds nothing.
3249 */
3250 engine_put_begin(&msg->out, &msg->path);
3251
3252 SYS_SLIST_FOR_EACH_CONTAINER(&engine_obj_list, obj, node) {
3253 /* Skip unrelated objects */
3254 if (msg->path.level > 0 && msg->path.obj_id != obj->obj_id) {
3255 continue;
3256 }
3257
3258 /* For bootstrap discover, only report object ID when no
3259 * instance is available or it's needed to report object
3260 * version.
3261 * For device management discovery, only report object ID with
3262 * attributes if object ID (alone) was provided.
3263 */
3264 if ((is_bootstrap && (obj->instance_count == 0U ||
3265 lwm2m_engine_shall_report_obj_version(obj))) ||
3266 (!is_bootstrap && msg->path.level == LWM2M_PATH_LEVEL_OBJECT)) {
3267 struct lwm2m_obj_path path = {
3268 .obj_id = obj->obj_id,
3269 .level = LWM2M_PATH_LEVEL_OBJECT,
3270 };
3271
3272 ret = engine_put_corelink(&msg->out, &path);
3273 if (ret < 0) {
3274 return ret;
3275 }
3276
3277 reported = true;
3278
3279 if (obj->instance_count == 0U) {
3280 continue;
3281 }
3282 }
3283
3284 SYS_SLIST_FOR_EACH_CONTAINER(&engine_obj_inst_list,
3285 obj_inst, node) {
3286 if (obj_inst->obj->obj_id != obj->obj_id) {
3287 continue;
3288 }
3289
3290 /* Skip unrelated object instance. */
3291 if (msg->path.level > LWM2M_PATH_LEVEL_OBJECT &&
3292 msg->path.obj_inst_id != obj_inst->obj_inst_id) {
3293 continue;
3294 }
3295
3296 /* Report object instances only if no Resource ID is
3297 * provided.
3298 */
3299 if (msg->path.level <= LWM2M_PATH_LEVEL_OBJECT_INST) {
3300 struct lwm2m_obj_path path = {
3301 .obj_id = obj_inst->obj->obj_id,
3302 .obj_inst_id = obj_inst->obj_inst_id,
3303 .level = LWM2M_PATH_LEVEL_OBJECT_INST,
3304 };
3305
3306 ret = engine_put_corelink(&msg->out, &path);
3307 if (ret < 0) {
3308 return ret;
3309 }
3310
3311 reported = true;
3312 }
3313
3314 /* Do not report resources in bootstrap discovery. */
3315 if (is_bootstrap) {
3316 continue;
3317 }
3318
3319 for (int i = 0; i < obj_inst->resource_count; i++) {
3320 /* Skip unrelated resources. */
3321 if (msg->path.level == LWM2M_PATH_LEVEL_RESOURCE &&
3322 msg->path.res_id != obj_inst->resources[i].res_id) {
3323 continue;
3324 }
3325
3326 struct lwm2m_obj_path path = {
3327 .obj_id = obj_inst->obj->obj_id,
3328 .obj_inst_id = obj_inst->obj_inst_id,
3329 .res_id = obj_inst->resources[i].res_id,
3330 .level = LWM2M_PATH_LEVEL_RESOURCE,
3331 };
3332
3333 ret = engine_put_corelink(&msg->out, &path);
3334 if (ret < 0) {
3335 return ret;
3336 }
3337
3338 reported = true;
3339 }
3340 }
3341 }
3342
3343 return reported ? 0 : -ENOENT;
3344 }
3345
do_discover_op(struct lwm2m_message * msg,uint16_t content_format)3346 static int do_discover_op(struct lwm2m_message *msg, uint16_t content_format)
3347 {
3348 switch (content_format) {
3349 case LWM2M_FORMAT_APP_LINK_FORMAT:
3350 return do_discover_op_link_format(
3351 msg, msg->ctx->bootstrap_mode);
3352
3353 default:
3354 LOG_ERR("Unsupported format: %u", content_format);
3355 return -ENOMSG;
3356 }
3357 }
3358
lwm2m_get_or_create_engine_obj(struct lwm2m_message * msg,struct lwm2m_engine_obj_inst ** obj_inst,uint8_t * created)3359 int lwm2m_get_or_create_engine_obj(struct lwm2m_message *msg,
3360 struct lwm2m_engine_obj_inst **obj_inst,
3361 uint8_t *created)
3362 {
3363 int ret = 0;
3364
3365 if (created) {
3366 *created = 0U;
3367 }
3368
3369 *obj_inst = get_engine_obj_inst(msg->path.obj_id,
3370 msg->path.obj_inst_id);
3371 if (!*obj_inst) {
3372 ret = lwm2m_create_obj_inst(msg->path.obj_id,
3373 msg->path.obj_inst_id,
3374 obj_inst);
3375 if (ret < 0) {
3376 return ret;
3377 }
3378
3379 /* set created flag to one */
3380 if (created) {
3381 *created = 1U;
3382 }
3383
3384 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT)
3385 if (!msg->ctx->bootstrap_mode) {
3386 engine_trigger_update(true);
3387 }
3388 #endif
3389 }
3390
3391 return ret;
3392 }
3393
lwm2m_engine_get_obj(const struct lwm2m_obj_path * path)3394 struct lwm2m_engine_obj *lwm2m_engine_get_obj(
3395 const struct lwm2m_obj_path *path)
3396 {
3397 if (path->level < LWM2M_PATH_LEVEL_OBJECT) {
3398 return NULL;
3399 }
3400
3401 return get_engine_obj(path->obj_id);
3402 }
3403
lwm2m_engine_get_obj_inst(const struct lwm2m_obj_path * path)3404 struct lwm2m_engine_obj_inst *lwm2m_engine_get_obj_inst(
3405 const struct lwm2m_obj_path *path)
3406 {
3407 if (path->level < LWM2M_PATH_LEVEL_OBJECT_INST) {
3408 return NULL;
3409 }
3410
3411 return get_engine_obj_inst(path->obj_id, path->obj_inst_id);
3412 }
3413
lwm2m_engine_get_res(const struct lwm2m_obj_path * path)3414 struct lwm2m_engine_res *lwm2m_engine_get_res(
3415 const struct lwm2m_obj_path *path)
3416 {
3417 struct lwm2m_engine_res *res = NULL;
3418 int ret;
3419
3420 if (path->level < LWM2M_PATH_LEVEL_RESOURCE) {
3421 return NULL;
3422 }
3423
3424 ret = path_to_objs(path, NULL, NULL, &res, NULL);
3425 if (ret < 0) {
3426 return NULL;
3427 }
3428
3429 return res;
3430 }
3431
lwm2m_engine_shall_report_obj_version(const struct lwm2m_engine_obj * obj)3432 bool lwm2m_engine_shall_report_obj_version(const struct lwm2m_engine_obj *obj)
3433 {
3434 if (obj->is_core) {
3435 return obj->version_major != LWM2M_PROTOCOL_VERSION_MAJOR ||
3436 obj->version_minor != LWM2M_PROTOCOL_VERSION_MINOR;
3437 }
3438
3439 return obj->version_major != 1 || obj->version_minor != 0;
3440 }
3441
do_write_op(struct lwm2m_message * msg,uint16_t format)3442 static int do_write_op(struct lwm2m_message *msg,
3443 uint16_t format)
3444 {
3445 switch (format) {
3446
3447 case LWM2M_FORMAT_APP_OCTET_STREAM:
3448 case LWM2M_FORMAT_PLAIN_TEXT:
3449 case LWM2M_FORMAT_OMA_PLAIN_TEXT:
3450 return do_write_op_plain_text(msg);
3451
3452 case LWM2M_FORMAT_OMA_TLV:
3453 case LWM2M_FORMAT_OMA_OLD_TLV:
3454 return do_write_op_tlv(msg);
3455
3456 #ifdef CONFIG_LWM2M_RW_JSON_SUPPORT
3457 case LWM2M_FORMAT_OMA_JSON:
3458 case LWM2M_FORMAT_OMA_OLD_JSON:
3459 return do_write_op_json(msg);
3460 #endif
3461
3462 default:
3463 LOG_ERR("Unsupported format: %u", format);
3464 return -ENOMSG;
3465
3466 }
3467 }
3468
3469 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
bootstrap_delete_allowed(int obj_id,int obj_inst_id)3470 static bool bootstrap_delete_allowed(int obj_id, int obj_inst_id)
3471 {
3472 char pathstr[MAX_RESOURCE_LEN];
3473 bool bootstrap_server;
3474 int ret;
3475
3476 if (obj_id == LWM2M_OBJECT_SECURITY_ID) {
3477 snprintk(pathstr, sizeof(pathstr), "%d/%d/1",
3478 LWM2M_OBJECT_SECURITY_ID, obj_inst_id);
3479 ret = lwm2m_engine_get_bool(pathstr, &bootstrap_server);
3480 if (ret < 0) {
3481 return false;
3482 }
3483
3484 if (bootstrap_server) {
3485 return false;
3486 }
3487 }
3488
3489 if (obj_id == LWM2M_OBJECT_DEVICE_ID) {
3490 return false;
3491 }
3492
3493 return true;
3494 }
3495
3496
bootstrap_delete(struct lwm2m_message * msg)3497 static int bootstrap_delete(struct lwm2m_message *msg)
3498 {
3499 struct lwm2m_engine_obj_inst *obj_inst, *tmp;
3500 int ret = 0;
3501
3502 if (msg->path.level > 2) {
3503 return -EPERM;
3504 }
3505
3506 if (msg->path.level == 2) {
3507 if (!bootstrap_delete_allowed(msg->path.obj_id,
3508 msg->path.obj_inst_id)) {
3509 return -EPERM;
3510 }
3511
3512 return lwm2m_delete_obj_inst(msg->path.obj_id,
3513 msg->path.obj_inst_id);
3514 }
3515
3516 /* DELETE all instances of a specific object or all object instances if
3517 * not specified, excluding the following exceptions (according to the
3518 * LwM2M specification v1.0.2, ch 5.2.7.5):
3519 * - LwM2M Bootstrap-Server Account (Bootstrap Security object, ID 0)
3520 * - Device object (ID 3)
3521 */
3522 SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&engine_obj_inst_list,
3523 obj_inst, tmp, node) {
3524 if (msg->path.level == 1 &&
3525 obj_inst->obj->obj_id != msg->path.obj_id) {
3526 continue;
3527 }
3528
3529 if (!bootstrap_delete_allowed(obj_inst->obj->obj_id,
3530 obj_inst->obj_inst_id)) {
3531 continue;
3532 }
3533
3534 ret = lwm2m_delete_obj_inst(obj_inst->obj->obj_id,
3535 obj_inst->obj_inst_id);
3536 if (ret < 0) {
3537 return ret;
3538 }
3539 }
3540
3541 return ret;
3542 }
3543 #endif
3544
handle_request(struct coap_packet * request,struct lwm2m_message * msg)3545 static int handle_request(struct coap_packet *request,
3546 struct lwm2m_message *msg)
3547 {
3548 int r;
3549 uint8_t code;
3550 struct coap_option options[4];
3551 struct lwm2m_engine_obj *obj = NULL;
3552 uint8_t token[8];
3553 uint8_t tkl = 0U;
3554 uint16_t format = LWM2M_FORMAT_NONE, accept;
3555 int observe = -1; /* default to -1, 0 = ENABLE, 1 = DISABLE */
3556 int block_opt, block_num;
3557 struct lwm2m_block_context *block_ctx = NULL;
3558 enum coap_block_size block_size;
3559 uint16_t payload_len = 0U;
3560 bool last_block = false;
3561 bool ignore = false;
3562 const uint8_t *payload_start;
3563
3564 /* set CoAP request / message */
3565 msg->in.in_cpkt = request;
3566 msg->out.out_cpkt = &msg->cpkt;
3567
3568 /* set default reader/writer */
3569 msg->in.reader = &plain_text_reader;
3570 msg->out.writer = &plain_text_writer;
3571
3572 code = coap_header_get_code(msg->in.in_cpkt);
3573
3574 /* setup response token */
3575 tkl = coap_header_get_token(msg->in.in_cpkt, token);
3576 if (tkl) {
3577 msg->tkl = tkl;
3578 msg->token = token;
3579 }
3580
3581 /* parse the URL path into components */
3582 r = coap_find_options(msg->in.in_cpkt, COAP_OPTION_URI_PATH, options,
3583 ARRAY_SIZE(options));
3584 if (r < 0) {
3585 goto error;
3586 }
3587
3588 /* Treat empty URI path option as is there were no option - this will be
3589 * represented as a level "zero" in the path structure.
3590 */
3591 if (r == 1 && options[0].len == 0) {
3592 r = 0;
3593 }
3594
3595 if (r == 0) {
3596 /* No URI path or empty URI path option - allowed only during
3597 * bootstrap.
3598 */
3599 switch (code & COAP_REQUEST_MASK) {
3600 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
3601 case COAP_METHOD_DELETE:
3602 case COAP_METHOD_GET:
3603 if (msg->ctx->bootstrap_mode) {
3604 break;
3605 }
3606
3607 r = -EPERM;
3608 goto error;
3609 #endif
3610 default:
3611 r = -EPERM;
3612 goto error;
3613 }
3614 }
3615
3616 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
3617 /* check for bootstrap-finish */
3618 if ((code & COAP_REQUEST_MASK) == COAP_METHOD_POST && r == 1 &&
3619 strncmp(options[0].value, "bs", options[0].len) == 0) {
3620 engine_bootstrap_finish();
3621
3622 msg->code = COAP_RESPONSE_CODE_CHANGED;
3623
3624 r = lwm2m_init_message(msg);
3625 if (r < 0) {
3626 goto error;
3627 }
3628
3629 return 0;
3630 } else
3631 #endif
3632 {
3633 r = coap_options_to_path(options, r, &msg->path);
3634 if (r < 0) {
3635 r = -ENOENT;
3636 goto error;
3637 }
3638 }
3639
3640 /* read Content Format / setup in.reader */
3641 r = coap_find_options(msg->in.in_cpkt, COAP_OPTION_CONTENT_FORMAT,
3642 options, 1);
3643 if (r > 0) {
3644 format = coap_option_value_to_int(&options[0]);
3645 r = select_reader(&msg->in, format);
3646 if (r < 0) {
3647 goto error;
3648 }
3649 }
3650
3651 /* read Accept / setup out.writer */
3652 r = coap_find_options(msg->in.in_cpkt, COAP_OPTION_ACCEPT, options, 1);
3653 if (r > 0) {
3654 accept = coap_option_value_to_int(&options[0]);
3655 } else {
3656 LOG_DBG("No accept option given. Assume OMA TLV.");
3657 accept = LWM2M_FORMAT_OMA_TLV;
3658 }
3659
3660 r = select_writer(&msg->out, accept);
3661 if (r < 0) {
3662 goto error;
3663 }
3664
3665 if (!(msg->ctx->bootstrap_mode && msg->path.level == 0)) {
3666 /* find registered obj */
3667 obj = get_engine_obj(msg->path.obj_id);
3668 if (!obj) {
3669 /* No matching object found - ignore request */
3670 r = -ENOENT;
3671 goto error;
3672 }
3673 }
3674
3675 /* set the operation */
3676 switch (code & COAP_REQUEST_MASK) {
3677
3678 case COAP_METHOD_GET:
3679 /*
3680 * LwM2M V1_0_1-20170704-A, table 25,
3681 * Discover: CoAP GET + accept=LWM2M_FORMAT_APP_LINK_FORMAT
3682 */
3683 if (accept == LWM2M_FORMAT_APP_LINK_FORMAT) {
3684 msg->operation = LWM2M_OP_DISCOVER;
3685 accept = LWM2M_FORMAT_APP_LINK_FORMAT;
3686 } else {
3687 msg->operation = LWM2M_OP_READ;
3688 }
3689
3690 /* check for observe */
3691 observe = coap_get_option_int(msg->in.in_cpkt,
3692 COAP_OPTION_OBSERVE);
3693 msg->code = COAP_RESPONSE_CODE_CONTENT;
3694 break;
3695
3696 case COAP_METHOD_POST:
3697 if (msg->path.level == 1U) {
3698 /* create an object instance */
3699 msg->operation = LWM2M_OP_CREATE;
3700 msg->code = COAP_RESPONSE_CODE_CREATED;
3701 } else if (msg->path.level == 2U) {
3702 /* write values to an object instance */
3703 msg->operation = LWM2M_OP_WRITE;
3704 msg->code = COAP_RESPONSE_CODE_CHANGED;
3705 } else {
3706 msg->operation = LWM2M_OP_EXECUTE;
3707 msg->code = COAP_RESPONSE_CODE_CHANGED;
3708 }
3709
3710 break;
3711
3712 case COAP_METHOD_PUT:
3713 /* write attributes if content-format is absent */
3714 if (format == LWM2M_FORMAT_NONE) {
3715 msg->operation = LWM2M_OP_WRITE_ATTR;
3716 } else {
3717 msg->operation = LWM2M_OP_WRITE;
3718 }
3719
3720 msg->code = COAP_RESPONSE_CODE_CHANGED;
3721 break;
3722
3723 case COAP_METHOD_DELETE:
3724 msg->operation = LWM2M_OP_DELETE;
3725 msg->code = COAP_RESPONSE_CODE_DELETED;
3726 break;
3727
3728 default:
3729 break;
3730 }
3731
3732 /* setup incoming data */
3733 payload_start = coap_packet_get_payload(msg->in.in_cpkt, &payload_len);
3734 if (payload_len > 0) {
3735 msg->in.offset = payload_start - msg->in.in_cpkt->data;
3736 } else {
3737 msg->in.offset = msg->in.in_cpkt->offset;
3738 }
3739
3740 /* Check for block transfer */
3741
3742 block_opt = coap_get_option_int(msg->in.in_cpkt, COAP_OPTION_BLOCK1);
3743 if (block_opt > 0) {
3744 last_block = !GET_MORE(block_opt);
3745
3746 /* RFC7252: 4.6. Message Size */
3747 block_size = GET_BLOCK_SIZE(block_opt);
3748 if (!last_block &&
3749 coap_block_size_to_bytes(block_size) > payload_len) {
3750 LOG_DBG("Trailing payload is discarded!");
3751 r = -EFBIG;
3752 goto error;
3753 }
3754
3755 block_num = GET_BLOCK_NUM(block_opt);
3756
3757 /* Try to retrieve existing block context. If one not exists,
3758 * and we've received first block, allocate new context.
3759 */
3760 r = get_block_ctx(token, tkl, &block_ctx);
3761 if (r < 0 && block_num == 0) {
3762 r = init_block_ctx(token, tkl, &block_ctx);
3763 }
3764
3765 if (r < 0) {
3766 LOG_ERR("Cannot find block context");
3767 goto error;
3768 }
3769
3770 msg->in.block_ctx = block_ctx;
3771
3772 if (block_num < block_ctx->expected) {
3773 LOG_WRN("Block already handled %d, expected %d",
3774 block_num, block_ctx->expected);
3775 ignore = true;
3776 } else if (block_num > block_ctx->expected) {
3777 LOG_WRN("Block out of order %d, expected %d",
3778 block_num, block_ctx->expected);
3779 r = -EFAULT;
3780 goto error;
3781 } else {
3782 r = coap_update_from_block(msg->in.in_cpkt, &block_ctx->ctx);
3783 if (r < 0) {
3784 LOG_ERR("Error from block update: %d", r);
3785 goto error;
3786 }
3787
3788 block_ctx->last_block = last_block;
3789
3790 /* Initial block sent by the server might be larger than
3791 * our block size therefore it is needed to take this
3792 * into account when calculating next expected block
3793 * number.
3794 */
3795 block_ctx->expected += GET_BLOCK_SIZE(block_opt) -
3796 block_ctx->ctx.block_size + 1;
3797 }
3798
3799 /* Handle blockwise 1 (Part 1): Set response code */
3800 if (!last_block) {
3801 msg->code = COAP_RESPONSE_CODE_CONTINUE;
3802 }
3803 }
3804
3805 /* render CoAP packet header */
3806 r = lwm2m_init_message(msg);
3807 if (r < 0) {
3808 goto error;
3809 }
3810
3811 if (!ignore) {
3812
3813 switch (msg->operation) {
3814
3815 case LWM2M_OP_READ:
3816 if (observe == 0) {
3817 /* add new observer */
3818 if (msg->token) {
3819 r = coap_append_option_int(
3820 msg->out.out_cpkt,
3821 COAP_OPTION_OBSERVE,
3822 OBSERVE_COUNTER_START);
3823 if (r < 0) {
3824 LOG_ERR("OBSERVE option error: %d", r);
3825 goto error;
3826 }
3827
3828 r = engine_add_observer(msg, token, tkl,
3829 accept);
3830 if (r < 0) {
3831 LOG_ERR("add OBSERVE error: %d", r);
3832 goto error;
3833 }
3834 } else {
3835 LOG_ERR("OBSERVE request missing token");
3836 r = -EINVAL;
3837 goto error;
3838 }
3839 } else if (observe == 1) {
3840 /* remove observer */
3841 r = engine_remove_observer(msg->ctx, token, tkl);
3842 if (r < 0) {
3843 #if defined(CONFIG_LWM2M_CANCEL_OBSERVE_BY_PATH)
3844 r = engine_remove_observer_by_path(msg->ctx,
3845 &msg->path);
3846 if (r < 0)
3847 #endif /* CONFIG_LWM2M_CANCEL_OBSERVE_BY_PATH */
3848 {
3849 LOG_ERR("remove observe error: %d", r);
3850 }
3851 }
3852 }
3853
3854 r = do_read_op(msg, accept);
3855 break;
3856
3857 case LWM2M_OP_DISCOVER:
3858 r = do_discover_op(msg, accept);
3859 break;
3860
3861 case LWM2M_OP_WRITE:
3862 case LWM2M_OP_CREATE:
3863 r = do_write_op(msg, format);
3864 break;
3865
3866 case LWM2M_OP_WRITE_ATTR:
3867 r = lwm2m_write_attr_handler(obj, msg);
3868 break;
3869
3870 case LWM2M_OP_EXECUTE:
3871 r = lwm2m_exec_handler(msg);
3872 break;
3873
3874 case LWM2M_OP_DELETE:
3875 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
3876 if (msg->ctx->bootstrap_mode) {
3877 r = bootstrap_delete(msg);
3878 break;
3879 }
3880 #endif
3881 r = lwm2m_delete_handler(msg);
3882 break;
3883
3884 default:
3885 LOG_ERR("Unknown operation: %u", msg->operation);
3886 r = -EINVAL;
3887 }
3888
3889 if (r < 0) {
3890 goto error;
3891 }
3892 }
3893
3894 /* Handle blockwise 1 (Part 2): Append BLOCK1 option / free context */
3895 if (block_ctx) {
3896 if (!last_block) {
3897 /* More to come, ack with correspond block # */
3898 r = coap_append_block1_option(msg->out.out_cpkt,
3899 &block_ctx->ctx);
3900 if (r < 0) {
3901 /* report as internal server error */
3902 LOG_ERR("Fail adding block1 option: %d", r);
3903 r = -EINVAL;
3904 goto error;
3905 }
3906 } else {
3907 /* Free context when finished */
3908 free_block_ctx(block_ctx);
3909 }
3910 }
3911
3912 return 0;
3913
3914 error:
3915 lwm2m_reset_message(msg, false);
3916 if (r == -ENOENT) {
3917 msg->code = COAP_RESPONSE_CODE_NOT_FOUND;
3918 } else if (r == -EPERM) {
3919 msg->code = COAP_RESPONSE_CODE_NOT_ALLOWED;
3920 } else if (r == -EEXIST) {
3921 msg->code = COAP_RESPONSE_CODE_BAD_REQUEST;
3922 } else if (r == -EFAULT) {
3923 msg->code = COAP_RESPONSE_CODE_INCOMPLETE;
3924 } else if (r == -EFBIG) {
3925 msg->code = COAP_RESPONSE_CODE_REQUEST_TOO_LARGE;
3926 } else if (r == -ENOTSUP) {
3927 msg->code = COAP_RESPONSE_CODE_NOT_IMPLEMENTED;
3928 } else if (r == -ENOMSG) {
3929 msg->code = COAP_RESPONSE_CODE_UNSUPPORTED_CONTENT_FORMAT;
3930 } else {
3931 /* Failed to handle the request */
3932 msg->code = COAP_RESPONSE_CODE_INTERNAL_ERROR;
3933 }
3934
3935 r = lwm2m_init_message(msg);
3936 if (r < 0) {
3937 LOG_ERR("Error recreating message: %d", r);
3938 }
3939
3940 /* Free block context when error happened */
3941 free_block_ctx(block_ctx);
3942
3943 return 0;
3944 }
3945
lwm2m_response_promote_to_con(struct lwm2m_message * msg)3946 static int lwm2m_response_promote_to_con(struct lwm2m_message *msg)
3947 {
3948 int ret;
3949
3950 msg->type = COAP_TYPE_CON;
3951 msg->mid = coap_next_id();
3952
3953 /* Since the response CoAP packet is already generated at this point,
3954 * tweak the specific fields manually:
3955 * - CoAP message type (byte 0, bits 2 and 3)
3956 * - CoAP message id (bytes 2 and 3)
3957 */
3958 msg->cpkt.data[0] &= ~(0x3 << 4);
3959 msg->cpkt.data[0] |= (msg->type & 0x3) << 4;
3960 msg->cpkt.data[2] = msg->mid >> 8;
3961 msg->cpkt.data[3] = (uint8_t) msg->mid;
3962
3963 /* Add the packet to the pending list. */
3964 msg->pending = coap_pending_next_unused(
3965 msg->ctx->pendings,
3966 CONFIG_LWM2M_ENGINE_MAX_PENDING);
3967 if (!msg->pending) {
3968 LOG_ERR("Unable to find a free pending to track "
3969 "retransmissions.");
3970 return -ENOMEM;
3971 }
3972
3973 ret = coap_pending_init(msg->pending, &msg->cpkt,
3974 &msg->ctx->remote_addr,
3975 COAP_DEFAULT_MAX_RETRANSMIT);
3976 if (ret < 0) {
3977 LOG_ERR("Unable to initialize a pending "
3978 "retransmission (err:%d).", ret);
3979 }
3980
3981 return ret;
3982 }
3983
lwm2m_udp_receive(struct lwm2m_ctx * client_ctx,uint8_t * buf,uint16_t buf_len,struct sockaddr * from_addr,udp_request_handler_cb_t udp_request_handler)3984 static void lwm2m_udp_receive(struct lwm2m_ctx *client_ctx,
3985 uint8_t *buf, uint16_t buf_len,
3986 struct sockaddr *from_addr,
3987 udp_request_handler_cb_t udp_request_handler)
3988 {
3989 struct lwm2m_message *msg = NULL;
3990 struct coap_pending *pending;
3991 struct coap_reply *reply;
3992 struct coap_packet response;
3993 int r;
3994 uint8_t token[8];
3995 uint8_t tkl;
3996
3997 r = coap_packet_parse(&response, buf, buf_len, NULL, 0);
3998 if (r < 0) {
3999 LOG_ERR("Invalid data received (err:%d)", r);
4000 return;
4001 }
4002
4003 tkl = coap_header_get_token(&response, token);
4004 pending = coap_pending_received(&response, client_ctx->pendings,
4005 CONFIG_LWM2M_ENGINE_MAX_PENDING);
4006 if (pending && coap_header_get_type(&response) == COAP_TYPE_ACK) {
4007 msg = find_msg(pending, NULL);
4008 if (msg == NULL) {
4009 LOG_DBG("Orphaned pending %p.", pending);
4010 return;
4011 }
4012
4013 msg->acknowledged = true;
4014
4015 if (msg->reply == NULL) {
4016 /* No response expected, release the message. */
4017 lwm2m_reset_message(msg, true);
4018 return;
4019 }
4020
4021 /* If the original message was a request and an empty
4022 * ACK was received, expect separate response later.
4023 */
4024 if ((msg->code >= COAP_METHOD_GET) &&
4025 (msg->code <= COAP_METHOD_DELETE) &&
4026 (coap_header_get_code(&response) == COAP_CODE_EMPTY)) {
4027 LOG_DBG("Empty ACK, expect separate response.");
4028 return;
4029 }
4030 }
4031
4032 LOG_DBG("checking for reply from [%s]",
4033 log_strdup(lwm2m_sprint_ip_addr(from_addr)));
4034 reply = coap_response_received(&response, from_addr,
4035 client_ctx->replies,
4036 CONFIG_LWM2M_ENGINE_MAX_REPLIES);
4037 if (reply) {
4038 msg = find_msg(NULL, reply);
4039
4040 if (coap_header_get_type(&response) == COAP_TYPE_CON) {
4041 r = lwm2m_send_empty_ack(client_ctx,
4042 coap_header_get_id(&response));
4043 if (r < 0) {
4044 LOG_ERR("Error transmitting ACK");
4045 }
4046 }
4047
4048 /* skip release if reply->user_data has error condition */
4049 if (reply && reply->user_data != COAP_REPLY_STATUS_NONE) {
4050 /* reset reply->user_data for next time */
4051 reply->user_data = (void *)COAP_REPLY_STATUS_NONE;
4052 LOG_DBG("reply %p NOT removed", reply);
4053 return;
4054 }
4055
4056 /* free up msg resources */
4057 if (msg) {
4058 lwm2m_reset_message(msg, true);
4059 }
4060
4061 LOG_DBG("reply %p handled and removed", reply);
4062 return;
4063 }
4064
4065 /*
4066 * If no normal response handler is found, then this is
4067 * a new request coming from the server. Let's look
4068 * at registered objects to find a handler.
4069 */
4070 if (udp_request_handler &&
4071 coap_header_get_type(&response) == COAP_TYPE_CON) {
4072 msg = lwm2m_get_message(client_ctx);
4073 if (!msg) {
4074 LOG_ERR("Unable to get a lwm2m message!");
4075 return;
4076 }
4077
4078 /* Create a response message if we reach this point */
4079 msg->type = COAP_TYPE_ACK;
4080 msg->code = coap_header_get_code(&response);
4081 msg->mid = coap_header_get_id(&response);
4082 /* skip token generation by default */
4083 msg->tkl = 0;
4084
4085 client_ctx->processed_req = msg;
4086
4087 /* process the response to this request */
4088 r = udp_request_handler(&response, msg);
4089 if (r < 0) {
4090 return;
4091 }
4092
4093 if (msg->acknowledged) {
4094 r = lwm2m_response_promote_to_con(msg);
4095 if (r < 0) {
4096 LOG_ERR("Failed to promote reponse to CON: %d",
4097 r);
4098 lwm2m_reset_message(msg, true);
4099 return;
4100 }
4101 }
4102
4103 client_ctx->processed_req = NULL;
4104 lwm2m_send_message_async(msg);
4105 } else {
4106 LOG_DBG("No handler for response");
4107 }
4108 }
4109
4110 /* returns ms until the next retransmission is due, or INT32_MAX
4111 * if no retransmissions are necessary
4112 */
retransmit_request(struct lwm2m_ctx * client_ctx,const uint32_t timestamp)4113 static int32_t retransmit_request(struct lwm2m_ctx *client_ctx,
4114 const uint32_t timestamp)
4115 {
4116 struct lwm2m_message *msg;
4117 struct coap_pending *p;
4118 int32_t remaining, next_retransmission = INT32_MAX;
4119 int i;
4120
4121 for (i = 0, p = client_ctx->pendings;
4122 i < CONFIG_LWM2M_ENGINE_MAX_PENDING; i++, p++) {
4123 if (!p->timeout) {
4124 continue;
4125 }
4126
4127 remaining = p->t0 + p->timeout - timestamp;
4128 if (remaining < 0) {
4129 msg = find_msg(p, NULL);
4130 if (!msg) {
4131 LOG_ERR("pending has no valid LwM2M message!");
4132 coap_pending_clear(p);
4133 continue;
4134 }
4135 if (!p->retries) {
4136 /* pending request has expired */
4137 if (msg->message_timeout_cb) {
4138 msg->message_timeout_cb(msg);
4139 }
4140 lwm2m_reset_message(msg, true);
4141 continue;
4142 }
4143 if (msg->acknowledged) {
4144 /* No need to retransmit, just keep the timer running to
4145 * timeout in case no response arrives.
4146 */
4147 coap_pending_cycle(p);
4148 continue;
4149 }
4150
4151 lwm2m_send_message_async(msg);
4152 break;
4153 }
4154 if (remaining < next_retransmission) {
4155 next_retransmission = remaining;
4156 }
4157 }
4158
4159 return next_retransmission;
4160 }
4161
notify_message_timeout_cb(struct lwm2m_message * msg)4162 static void notify_message_timeout_cb(struct lwm2m_message *msg)
4163 {
4164 if (msg->ctx != NULL) {
4165 struct lwm2m_ctx *client_ctx = msg->ctx;
4166
4167 if (client_ctx->notify_timeout_cb != NULL) {
4168 client_ctx->notify_timeout_cb();
4169 }
4170 }
4171
4172 LOG_ERR("Notify Message Timed Out : %p", msg);
4173 }
4174
notify_message_reply_cb(const struct coap_packet * response,struct coap_reply * reply,const struct sockaddr * from)4175 static int notify_message_reply_cb(const struct coap_packet *response,
4176 struct coap_reply *reply,
4177 const struct sockaddr *from)
4178 {
4179 int ret = 0;
4180 uint8_t type, code;
4181 struct lwm2m_message *msg;
4182
4183 type = coap_header_get_type(response);
4184 code = coap_header_get_code(response);
4185
4186 LOG_DBG("NOTIFY ACK type:%u code:%d.%d reply_token:'%s'",
4187 type,
4188 COAP_RESPONSE_CODE_CLASS(code),
4189 COAP_RESPONSE_CODE_DETAIL(code),
4190 log_strdup(sprint_token(reply->token, reply->tkl)));
4191
4192 /* remove observer on COAP_TYPE_RESET */
4193 if (type == COAP_TYPE_RESET) {
4194 if (reply->tkl > 0) {
4195 msg = find_msg(NULL, reply);
4196 ret = engine_remove_observer(msg->ctx, reply->token, reply->tkl);
4197 if (ret) {
4198 LOG_ERR("remove observe error: %d", ret);
4199 }
4200 } else {
4201 LOG_ERR("notify reply missing token -- ignored.");
4202 }
4203 }
4204
4205 return 0;
4206 }
4207
generate_notify_message(struct lwm2m_ctx * ctx,struct observe_node * obs,bool manual_trigger)4208 static int generate_notify_message(struct lwm2m_ctx *ctx,
4209 struct observe_node *obs,
4210 bool manual_trigger)
4211 {
4212 struct lwm2m_message *msg;
4213 struct lwm2m_engine_obj_inst *obj_inst;
4214 int ret = 0;
4215
4216 msg = lwm2m_get_message(ctx);
4217 if (!msg) {
4218 LOG_ERR("Unable to get a lwm2m message!");
4219 return -ENOMEM;
4220 }
4221
4222 /* copy path */
4223 memcpy(&msg->path, &obs->path, sizeof(struct lwm2m_obj_path));
4224 msg->operation = LWM2M_OP_READ;
4225
4226 LOG_DBG("[%s] NOTIFY MSG START: %u/%u/%u(%u) token:'%s' [%s] %lld",
4227 manual_trigger ? "MANUAL" : "AUTO",
4228 obs->path.obj_id,
4229 obs->path.obj_inst_id,
4230 obs->path.res_id,
4231 obs->path.level,
4232 log_strdup(sprint_token(obs->token, obs->tkl)),
4233 log_strdup(lwm2m_sprint_ip_addr(&ctx->remote_addr)),
4234 k_uptime_get());
4235
4236 obj_inst = get_engine_obj_inst(obs->path.obj_id,
4237 obs->path.obj_inst_id);
4238 if (!obj_inst) {
4239 LOG_ERR("unable to get engine obj for %u/%u",
4240 obs->path.obj_id,
4241 obs->path.obj_inst_id);
4242 ret = -EINVAL;
4243 goto cleanup;
4244 }
4245
4246 msg->type = COAP_TYPE_CON;
4247 msg->code = COAP_RESPONSE_CODE_CONTENT;
4248 msg->mid = coap_next_id();
4249 msg->token = obs->token;
4250 msg->tkl = obs->tkl;
4251 msg->reply_cb = notify_message_reply_cb;
4252 msg->message_timeout_cb = notify_message_timeout_cb;
4253 msg->out.out_cpkt = &msg->cpkt;
4254
4255 ret = lwm2m_init_message(msg);
4256 if (ret < 0) {
4257 LOG_ERR("Unable to init lwm2m message! (err: %d)", ret);
4258 goto cleanup;
4259 }
4260
4261 /* each notification should increment the obs counter */
4262 obs->counter++;
4263 ret = coap_append_option_int(&msg->cpkt, COAP_OPTION_OBSERVE,
4264 obs->counter);
4265 if (ret < 0) {
4266 LOG_ERR("OBSERVE option error: %d", ret);
4267 goto cleanup;
4268 }
4269
4270 /* set the output writer */
4271 select_writer(&msg->out, obs->format);
4272
4273 ret = do_read_op(msg, obs->format);
4274 if (ret < 0) {
4275 LOG_ERR("error in multi-format read (err:%d)", ret);
4276 goto cleanup;
4277 }
4278
4279 lwm2m_send_message_async(msg);
4280
4281 LOG_DBG("NOTIFY MSG: SENT");
4282 return 0;
4283
4284 cleanup:
4285 lwm2m_reset_message(msg, true);
4286 return ret;
4287 }
4288
engine_next_service_timeout_ms(uint32_t max_timeout,const int64_t timestamp)4289 static int32_t engine_next_service_timeout_ms(uint32_t max_timeout,
4290 const int64_t timestamp)
4291 {
4292 struct service_node *srv;
4293 uint64_t time_left_ms;
4294 uint32_t timeout = max_timeout;
4295
4296 SYS_SLIST_FOR_EACH_CONTAINER(&engine_service_list, srv, node) {
4297 time_left_ms = srv->last_timestamp + srv->min_call_period;
4298
4299 /* service is due */
4300 if (time_left_ms < timestamp) {
4301 return 0;
4302 }
4303
4304 /* service timeout is less than the current timeout */
4305 time_left_ms -= timestamp;
4306 if (time_left_ms < timeout) {
4307 timeout = time_left_ms;
4308 }
4309 }
4310
4311 return timeout;
4312 }
4313
lwm2m_engine_add_service(k_work_handler_t service,uint32_t period_ms)4314 int lwm2m_engine_add_service(k_work_handler_t service, uint32_t period_ms)
4315 {
4316 int i;
4317
4318 /* find an unused service index node */
4319 for (i = 0; i < MAX_PERIODIC_SERVICE; i++) {
4320 if (!service_node_data[i].service_work) {
4321 break;
4322 }
4323 }
4324
4325 if (i == MAX_PERIODIC_SERVICE) {
4326 return -ENOMEM;
4327 }
4328
4329 service_node_data[i].service_work = service;
4330 service_node_data[i].min_call_period = period_ms;
4331 service_node_data[i].last_timestamp = 0U;
4332
4333 sys_slist_append(&engine_service_list,
4334 &service_node_data[i].node);
4335
4336 return 0;
4337 }
4338
lwm2m_engine_update_service_period(k_work_handler_t service,uint32_t period_ms)4339 int lwm2m_engine_update_service_period(k_work_handler_t service, uint32_t period_ms)
4340 {
4341 int i = 0;
4342
4343 for (i = 0; i < MAX_PERIODIC_SERVICE; i++) {
4344 if (service_node_data[i].service_work == service) {
4345 service_node_data[i].min_call_period = period_ms;
4346 return 0;
4347 }
4348 }
4349
4350 return -ENOENT;
4351 }
4352
lwm2m_engine_service(const int64_t timestamp)4353 static int32_t lwm2m_engine_service(const int64_t timestamp)
4354 {
4355 struct service_node *srv;
4356 int64_t service_due_timestamp;
4357
4358 SYS_SLIST_FOR_EACH_CONTAINER(&engine_service_list, srv, node) {
4359 service_due_timestamp = srv->last_timestamp +
4360 srv->min_call_period;
4361 /* service is due */
4362 if (timestamp >= service_due_timestamp) {
4363 srv->last_timestamp = k_uptime_get();
4364 srv->service_work(NULL);
4365 }
4366 }
4367
4368 /* calculate how long to sleep till the next service */
4369 return engine_next_service_timeout_ms(ENGINE_UPDATE_INTERVAL_MS,
4370 timestamp);
4371 }
4372
lwm2m_engine_context_close(struct lwm2m_ctx * client_ctx)4373 int lwm2m_engine_context_close(struct lwm2m_ctx *client_ctx)
4374 {
4375 int sock_fd = client_ctx->sock_fd;
4376 struct lwm2m_message *msg;
4377 sys_snode_t *obs_node;
4378 struct observe_node *obs;
4379 size_t i;
4380
4381 /* Remove observes for this context */
4382 while (!sys_slist_is_empty(&client_ctx->observer)) {
4383 obs_node = sys_slist_get_not_empty(&client_ctx->observer);
4384 obs = SYS_SLIST_CONTAINER(obs_node, obs, node);
4385 (void)memset(obs, 0, sizeof(*obs));
4386 }
4387
4388 for (i = 0, msg = messages; i < ARRAY_SIZE(messages); i++, msg++) {
4389 if (msg->ctx == client_ctx) {
4390 lwm2m_reset_message(msg, true);
4391 }
4392 }
4393
4394 coap_pendings_clear(client_ctx->pendings,
4395 CONFIG_LWM2M_ENGINE_MAX_PENDING);
4396 coap_replies_clear(client_ctx->replies,
4397 CONFIG_LWM2M_ENGINE_MAX_REPLIES);
4398
4399 lwm2m_socket_del(client_ctx);
4400 client_ctx->sock_fd = -1;
4401 if (sock_fd >= 0) {
4402 return close(sock_fd);
4403 } else {
4404 return 0;
4405 }
4406 }
4407
lwm2m_engine_context_init(struct lwm2m_ctx * client_ctx)4408 void lwm2m_engine_context_init(struct lwm2m_ctx *client_ctx)
4409 {
4410 sys_slist_init(&client_ctx->pending_sends);
4411 sys_slist_init(&client_ctx->observer);
4412 }
4413
4414 /* LwM2M Socket Integration */
4415
lwm2m_socket_add(struct lwm2m_ctx * ctx)4416 int lwm2m_socket_add(struct lwm2m_ctx *ctx)
4417 {
4418 if (sock_nfds >= MAX_POLL_FD) {
4419 return -ENOMEM;
4420 }
4421
4422 sock_ctx[sock_nfds] = ctx;
4423 sock_fds[sock_nfds].fd = ctx->sock_fd;
4424 sock_fds[sock_nfds].events = POLLIN;
4425 sock_nfds++;
4426
4427 return 0;
4428 }
4429
lwm2m_socket_del(struct lwm2m_ctx * ctx)4430 void lwm2m_socket_del(struct lwm2m_ctx *ctx)
4431 {
4432 for (int i = 0; i < sock_nfds; i++) {
4433 if (sock_ctx[i] != ctx) {
4434 continue;
4435 }
4436
4437 sock_nfds--;
4438
4439 /* If not last, overwrite the entry with the last one. */
4440 if (i < sock_nfds) {
4441 sock_ctx[i] = sock_ctx[sock_nfds];
4442 sock_fds[i].fd = sock_fds[sock_nfds].fd;
4443 sock_fds[i].events = sock_fds[sock_nfds].events;
4444 }
4445
4446 /* Remove the last entry. */
4447 sock_ctx[sock_nfds] = NULL;
4448 sock_fds[sock_nfds].fd = -1;
4449 break;
4450 }
4451 }
4452
manual_notify_is_due(const struct observe_node * obs,const int64_t timestamp)4453 static bool manual_notify_is_due(const struct observe_node *obs,
4454 const int64_t timestamp)
4455 {
4456 const bool has_min_period = obs->min_period_sec != 0;
4457
4458 return obs->event_timestamp > obs->last_timestamp &&
4459 (!has_min_period || timestamp > obs->last_timestamp +
4460 MSEC_PER_SEC * obs->min_period_sec);
4461 }
4462
automatic_notify_is_due(const struct observe_node * obs,const int64_t timestamp)4463 static bool automatic_notify_is_due(const struct observe_node *obs,
4464 const int64_t timestamp)
4465 {
4466 const bool has_max_period = obs->max_period_sec != 0;
4467
4468 return has_max_period && (timestamp > obs->last_timestamp +
4469 MSEC_PER_SEC * obs->max_period_sec);
4470 }
4471
check_notifications(struct lwm2m_ctx * ctx,const int64_t timestamp)4472 static void check_notifications(struct lwm2m_ctx *ctx,
4473 const int64_t timestamp)
4474 {
4475 struct observe_node *obs;
4476 int rc;
4477 bool manual_notify, automatic_notify;
4478
4479 SYS_SLIST_FOR_EACH_CONTAINER(&ctx->observer, obs, node) {
4480 manual_notify = manual_notify_is_due(obs, timestamp);
4481 automatic_notify = automatic_notify_is_due(obs, timestamp);
4482 if (!manual_notify && !automatic_notify) {
4483 continue;
4484 }
4485 rc = generate_notify_message(ctx, obs, manual_notify);
4486 if (rc == -ENOMEM) {
4487 /* no memory/messages available, retry later */
4488 return;
4489 }
4490 obs->last_timestamp = timestamp;
4491 if (!rc) {
4492 /* create at most one notification */
4493 return;
4494 }
4495 }
4496 }
4497
socket_recv_message(struct lwm2m_ctx * client_ctx)4498 static int socket_recv_message(struct lwm2m_ctx *client_ctx)
4499 {
4500 static uint8_t in_buf[NET_IPV6_MTU];
4501 socklen_t from_addr_len;
4502 ssize_t len;
4503 static struct sockaddr from_addr;
4504
4505 from_addr_len = sizeof(from_addr);
4506 len = recvfrom(client_ctx->sock_fd, in_buf, sizeof(in_buf) - 1,
4507 0, &from_addr, &from_addr_len);
4508
4509 if (len < 0) {
4510 if (errno == EAGAIN || errno == EWOULDBLOCK) {
4511 return -errno;
4512 }
4513
4514 LOG_ERR("Error reading response: %d", errno);
4515 if (client_ctx->fault_cb != NULL) {
4516 client_ctx->fault_cb(errno);
4517 }
4518 return -errno;
4519 }
4520
4521 if (len == 0) {
4522 LOG_ERR("Zero length recv");
4523 return 0;
4524 }
4525
4526 in_buf[len] = 0U;
4527 lwm2m_udp_receive(client_ctx, in_buf, len, &from_addr, handle_request);
4528
4529 return 0;
4530 }
4531
socket_send_message(struct lwm2m_ctx * client_ctx)4532 static int socket_send_message(struct lwm2m_ctx *client_ctx)
4533 {
4534 sys_snode_t *msg_node = sys_slist_get(&client_ctx->pending_sends);
4535 struct lwm2m_message *msg;
4536
4537 if (!msg_node) {
4538 return 0;
4539 }
4540 msg = SYS_SLIST_CONTAINER(msg_node, msg, node);
4541 return lwm2m_send_message(msg);
4542 }
4543
socket_reset_pollfd_events(void)4544 static void socket_reset_pollfd_events(void)
4545 {
4546 for (int i = 0; i < sock_nfds; ++i) {
4547 sock_fds[i].events = POLLIN
4548 | (sys_slist_is_empty(&sock_ctx[i]->pending_sends) ? 0 : POLLOUT);
4549 sock_fds[i].revents = 0;
4550 }
4551 }
4552
4553 /* LwM2M main work loop */
socket_loop(void)4554 static void socket_loop(void)
4555 {
4556 int i, rc;
4557 int64_t timestamp;
4558 int32_t timeout, next_retransmit;
4559
4560 while (1) {
4561 timestamp = k_uptime_get();
4562 timeout = lwm2m_engine_service(timestamp);
4563
4564 /* wait for sockets */
4565 if (sock_nfds < 1) {
4566 k_msleep(timeout);
4567 continue;
4568 }
4569
4570 for (i = 0; i < sock_nfds; ++i) {
4571 if (sys_slist_is_empty(&sock_ctx[i]->pending_sends)) {
4572 next_retransmit = retransmit_request(sock_ctx[i], timestamp);
4573 if (next_retransmit < timeout) {
4574 timeout = next_retransmit;
4575 }
4576 }
4577 if (sys_slist_is_empty(&sock_ctx[i]->pending_sends)) {
4578 check_notifications(sock_ctx[i], timestamp);
4579 }
4580 }
4581
4582 socket_reset_pollfd_events();
4583
4584 /*
4585 * FIXME: Currently we timeout and restart poll in case fds
4586 * were modified.
4587 */
4588 rc = poll(sock_fds, sock_nfds, timeout);
4589 if (rc < 0) {
4590 LOG_ERR("Error in poll:%d", errno);
4591 errno = 0;
4592 k_msleep(ENGINE_UPDATE_INTERVAL_MS);
4593 continue;
4594 }
4595
4596 for (i = 0; i < sock_nfds; i++) {
4597 if ((sock_fds[i].revents & POLLERR) ||
4598 (sock_fds[i].revents & POLLNVAL) ||
4599 (sock_fds[i].revents & POLLHUP)) {
4600 LOG_ERR("Poll reported a socket error, %02x.",
4601 sock_fds[i].revents);
4602 if (sock_ctx[i] != NULL &&
4603 sock_ctx[i]->fault_cb != NULL) {
4604 sock_ctx[i]->fault_cb(EIO);
4605 }
4606 continue;
4607 }
4608
4609 if (sock_fds[i].revents & POLLIN) {
4610 while (sock_ctx[i]) {
4611 rc = socket_recv_message(sock_ctx[i]);
4612 if (rc) {
4613 break;
4614 }
4615 }
4616 }
4617
4618 if (sock_fds[i].revents & POLLOUT) {
4619 socket_send_message(sock_ctx[i]);
4620 }
4621 }
4622 }
4623 }
4624
4625 #if defined(CONFIG_LWM2M_DTLS_SUPPORT)
load_tls_credential(struct lwm2m_ctx * client_ctx,uint16_t res_id,enum tls_credential_type type)4626 static int load_tls_credential(struct lwm2m_ctx *client_ctx, uint16_t res_id,
4627 enum tls_credential_type type)
4628 {
4629 int ret = 0;
4630 void *cred = NULL;
4631 uint16_t cred_len;
4632 uint8_t cred_flags;
4633 char pathstr[MAX_RESOURCE_LEN];
4634
4635 /* ignore error value */
4636 tls_credential_delete(client_ctx->tls_tag, type);
4637
4638 snprintk(pathstr, sizeof(pathstr), "0/%d/%u", client_ctx->sec_obj_inst,
4639 res_id);
4640
4641 ret = lwm2m_engine_get_res_data(pathstr, &cred, &cred_len, &cred_flags);
4642 if (ret < 0) {
4643 LOG_ERR("Unable to get resource data for '%s'",
4644 log_strdup(pathstr));
4645 return ret;
4646 }
4647
4648 ret = tls_credential_add(client_ctx->tls_tag, type, cred, cred_len);
4649 if (ret < 0) {
4650 LOG_ERR("Error setting cred tag %d type %d: Error %d",
4651 client_ctx->tls_tag, type, ret);
4652 }
4653
4654 return ret;
4655 }
4656 #endif /* CONFIG_LWM2M_DTLS_SUPPORT */
4657
lwm2m_socket_start(struct lwm2m_ctx * client_ctx)4658 int lwm2m_socket_start(struct lwm2m_ctx *client_ctx)
4659 {
4660 int flags;
4661 #if defined(CONFIG_LWM2M_DTLS_SUPPORT)
4662 int ret;
4663
4664 if (client_ctx->load_credentials) {
4665 ret = client_ctx->load_credentials(client_ctx);
4666 if (ret < 0) {
4667 return ret;
4668 }
4669 } else {
4670 ret = load_tls_credential(client_ctx, 3, TLS_CREDENTIAL_PSK_ID);
4671 if (ret < 0) {
4672 return ret;
4673 }
4674
4675 ret = load_tls_credential(client_ctx, 5, TLS_CREDENTIAL_PSK);
4676 if (ret < 0) {
4677 return ret;
4678 }
4679 }
4680
4681 if (client_ctx->use_dtls) {
4682 client_ctx->sock_fd = socket(client_ctx->remote_addr.sa_family,
4683 SOCK_DGRAM, IPPROTO_DTLS_1_2);
4684 } else
4685 #endif /* CONFIG_LWM2M_DTLS_SUPPORT */
4686 {
4687 client_ctx->sock_fd = socket(client_ctx->remote_addr.sa_family,
4688 SOCK_DGRAM, IPPROTO_UDP);
4689 }
4690
4691 if (client_ctx->sock_fd < 0) {
4692 LOG_ERR("Failed to create socket: %d", errno);
4693 return -errno;
4694 }
4695
4696 #if defined(CONFIG_LWM2M_DTLS_SUPPORT)
4697 if (client_ctx->use_dtls) {
4698 sec_tag_t tls_tag_list[] = {
4699 client_ctx->tls_tag,
4700 };
4701
4702 ret = setsockopt(client_ctx->sock_fd, SOL_TLS, TLS_SEC_TAG_LIST,
4703 tls_tag_list, sizeof(tls_tag_list));
4704 if (ret < 0) {
4705 LOG_ERR("Failed to set TLS_SEC_TAG_LIST option: %d",
4706 errno);
4707 lwm2m_engine_context_close(client_ctx);
4708 return -errno;
4709 }
4710 }
4711 #endif /* CONFIG_LWM2M_DTLS_SUPPORT */
4712
4713 if (connect(client_ctx->sock_fd, &client_ctx->remote_addr,
4714 NET_SOCKADDR_MAX_SIZE) < 0) {
4715 LOG_ERR("Cannot connect UDP (-%d)", errno);
4716 lwm2m_engine_context_close(client_ctx);
4717 return -errno;
4718 }
4719
4720 flags = fcntl(client_ctx->sock_fd, F_GETFL, 0);
4721 if (flags == -1) {
4722 return -errno;
4723 }
4724 fcntl(client_ctx->sock_fd, F_SETFL, flags | O_NONBLOCK);
4725
4726 return lwm2m_socket_add(client_ctx);
4727 }
4728
lwm2m_parse_peerinfo(char * url,struct sockaddr * addr,bool * use_dtls)4729 int lwm2m_parse_peerinfo(char *url, struct sockaddr *addr, bool *use_dtls)
4730 {
4731 struct http_parser_url parser;
4732 #if defined(CONFIG_LWM2M_DNS_SUPPORT)
4733 struct addrinfo *res, hints = { 0 };
4734 #endif
4735 int ret;
4736 uint16_t off, len;
4737 uint8_t tmp;
4738
4739 LOG_DBG("Parse url: %s", log_strdup(url));
4740
4741 http_parser_url_init(&parser);
4742 ret = http_parser_parse_url(url, strlen(url), 0, &parser);
4743 if (ret < 0) {
4744 LOG_ERR("Invalid url: %s", log_strdup(url));
4745 return -ENOTSUP;
4746 }
4747
4748 off = parser.field_data[UF_SCHEMA].off;
4749 len = parser.field_data[UF_SCHEMA].len;
4750
4751 /* check for supported protocol */
4752 if (strncmp(url + off, "coaps", len) != 0) {
4753 return -EPROTONOSUPPORT;
4754 }
4755
4756 /* check for DTLS requirement */
4757 *use_dtls = false;
4758 if (len == 5U && strncmp(url + off, "coaps", len) == 0) {
4759 #if defined(CONFIG_LWM2M_DTLS_SUPPORT)
4760 *use_dtls = true;
4761 #else
4762 return -EPROTONOSUPPORT;
4763 #endif /* CONFIG_LWM2M_DTLS_SUPPORT */
4764 }
4765
4766 if (!(parser.field_set & (1 << UF_PORT))) {
4767 /* Set to default port of CoAP */
4768 parser.port = CONFIG_LWM2M_PEER_PORT;
4769 }
4770
4771 off = parser.field_data[UF_HOST].off;
4772 len = parser.field_data[UF_HOST].len;
4773
4774 /* truncate host portion */
4775 tmp = url[off + len];
4776 url[off + len] = '\0';
4777
4778 /* initialize addr */
4779 (void)memset(addr, 0, sizeof(*addr));
4780
4781 /* try and set IP address directly */
4782 addr->sa_family = AF_INET6;
4783 ret = net_addr_pton(AF_INET6, url + off,
4784 &((struct sockaddr_in6 *)addr)->sin6_addr);
4785 /* Try to parse again using AF_INET */
4786 if (ret < 0) {
4787 addr->sa_family = AF_INET;
4788 ret = net_addr_pton(AF_INET, url + off,
4789 &((struct sockaddr_in *)addr)->sin_addr);
4790 }
4791
4792 if (ret < 0) {
4793 #if defined(CONFIG_LWM2M_DNS_SUPPORT)
4794 #if defined(CONFIG_NET_IPV6) && defined(CONFIG_NET_IPV4)
4795 hints.ai_family = AF_UNSPEC;
4796 #elif defined(CONFIG_NET_IPV6)
4797 hints.ai_family = AF_INET6;
4798 #elif defined(CONFIG_NET_IPV4)
4799 hints.ai_family = AF_INET;
4800 #else
4801 hints.ai_family = AF_UNSPEC;
4802 #endif /* defined(CONFIG_NET_IPV6) && defined(CONFIG_NET_IPV4) */
4803 hints.ai_socktype = SOCK_DGRAM;
4804 ret = getaddrinfo(url + off, NULL, &hints, &res);
4805 if (ret != 0) {
4806 LOG_ERR("Unable to resolve address");
4807 /* DNS error codes don't align with normal errors */
4808 ret = -ENOENT;
4809 goto cleanup;
4810 }
4811
4812 memcpy(addr, res->ai_addr, sizeof(*addr));
4813 addr->sa_family = res->ai_family;
4814 freeaddrinfo(res);
4815 #else
4816 goto cleanup;
4817 #endif /* CONFIG_LWM2M_DNS_SUPPORT */
4818 }
4819
4820 /* set port */
4821 if (addr->sa_family == AF_INET6) {
4822 net_sin6(addr)->sin6_port = htons(parser.port);
4823 } else if (addr->sa_family == AF_INET) {
4824 net_sin(addr)->sin_port = htons(parser.port);
4825 } else {
4826 ret = -EPROTONOSUPPORT;
4827 }
4828
4829 cleanup:
4830 /* restore host separator */
4831 url[off + len] = tmp;
4832 return ret;
4833 }
4834
lwm2m_engine_start(struct lwm2m_ctx * client_ctx)4835 int lwm2m_engine_start(struct lwm2m_ctx *client_ctx)
4836 {
4837 char pathstr[MAX_RESOURCE_LEN];
4838 char *url;
4839 uint16_t url_len;
4840 uint8_t url_data_flags;
4841 int ret = 0U;
4842
4843 /* get the server URL */
4844 snprintk(pathstr, sizeof(pathstr), "0/%d/0", client_ctx->sec_obj_inst);
4845 ret = lwm2m_engine_get_res_data(pathstr, (void **)&url, &url_len,
4846 &url_data_flags);
4847 if (ret < 0) {
4848 return ret;
4849 }
4850
4851 url[url_len] = '\0';
4852 ret = lwm2m_parse_peerinfo(url, &client_ctx->remote_addr,
4853 &client_ctx->use_dtls);
4854 if (ret < 0) {
4855 return ret;
4856 }
4857
4858 lwm2m_engine_context_init(client_ctx);
4859 return lwm2m_socket_start(client_ctx);
4860 }
4861
lwm2m_engine_init(const struct device * dev)4862 static int lwm2m_engine_init(const struct device *dev)
4863 {
4864 (void)memset(block1_contexts, 0, sizeof(block1_contexts));
4865
4866 /* start sock receive thread */
4867 k_thread_create(&engine_thread_data, &engine_thread_stack[0],
4868 K_KERNEL_STACK_SIZEOF(engine_thread_stack),
4869 (k_thread_entry_t)socket_loop, NULL, NULL, NULL,
4870 THREAD_PRIORITY, 0, K_NO_WAIT);
4871 k_thread_name_set(&engine_thread_data, "lwm2m-sock-recv");
4872 LOG_DBG("LWM2M engine socket receive thread started");
4873
4874 return 0;
4875 }
4876
4877 SYS_INIT(lwm2m_engine_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
4878