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_observation
16 #define LOG_LEVEL CONFIG_LWM2M_LOG_LEVEL
17
18 #include <zephyr/logging/log.h>
19 LOG_MODULE_REGISTER(LOG_MODULE_NAME);
20
21 #include "lwm2m_engine.h"
22 #include "lwm2m_object.h"
23 #include "lwm2m_util.h"
24 #include "lwm2m_rd_client.h"
25
26 #include <ctype.h>
27 #include <errno.h>
28 #include <stddef.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32
33 #include <zephyr/init.h>
34 #include <zephyr/net/http/parser_url.h>
35 #include <zephyr/net/lwm2m.h>
36 #include <zephyr/net/net_ip.h>
37 #include <zephyr/net/socket.h>
38 #include <zephyr/sys/printk.h>
39 #include <zephyr/types.h>
40 #include "lwm2m_obj_server.h"
41
42 #if defined(CONFIG_LWM2M_RW_SENML_JSON_SUPPORT)
43 #include "lwm2m_rw_senml_json.h"
44 #endif
45 #ifdef CONFIG_LWM2M_RW_JSON_SUPPORT
46 #include "lwm2m_rw_json.h"
47 #endif
48 #ifdef CONFIG_LWM2M_RW_CBOR_SUPPORT
49 #include "lwm2m_rw_cbor.h"
50 #endif
51 #ifdef CONFIG_LWM2M_RW_SENML_CBOR_SUPPORT
52 #include "lwm2m_rw_senml_cbor.h"
53 #endif
54
55 #define OBSERVE_COUNTER_START 0U
56
57 #if defined(CONFIG_COAP_EXTENDED_OPTIONS_LEN)
58 #define COAP_OPTION_BUF_LEN (CONFIG_COAP_EXTENDED_OPTIONS_LEN_VALUE + 1)
59 #else
60 #define COAP_OPTION_BUF_LEN 13
61 #endif
62
63 /* Resources */
64 static sys_slist_t obs_obj_path_list;
65
66 static struct observe_node observe_node_data[CONFIG_LWM2M_ENGINE_MAX_OBSERVER];
67
68 /* External resources */
69 struct lwm2m_ctx **lwm2m_sock_ctx(void);
70
71 int lwm2m_sock_nfds(void);
72 /* Resource wrappers */
lwm2m_obs_obj_path_list(void)73 sys_slist_t *lwm2m_obs_obj_path_list(void) { return &obs_obj_path_list; }
74
75 struct notification_attrs {
76 /* use to determine which value is set */
77 double gt;
78 double lt;
79 double st;
80 int32_t pmin;
81 int32_t pmax;
82 uint8_t flags;
83 };
84
85 /* write-attribute related definitions */
86 static const uint8_t LWM2M_ATTR_LEN[] = {4, 4, 2, 2, 2};
87
88 static struct lwm2m_attr write_attr_pool[CONFIG_LWM2M_NUM_ATTR];
89
90 /* Forward declarations */
91
92 void lwm2m_engine_free_list(sys_slist_t *path_list, sys_slist_t *free_list);
93
94 struct lwm2m_obj_path_list *lwm2m_engine_get_from_list(sys_slist_t *path_list);
95
lwm2m_attr_to_str(uint8_t type)96 const char *const lwm2m_attr_to_str(uint8_t type)
97 {
98 switch (type) {
99 case LWM2M_ATTR_PMIN:
100 return "pmin";
101 case LWM2M_ATTR_PMAX:
102 return "pmax";
103 case LWM2M_ATTR_GT:
104 return "gt";
105 case LWM2M_ATTR_LT:
106 return "lt";
107 case LWM2M_ATTR_STEP:
108 return "st";
109 default:
110 return "unknown";
111 }
112 }
113
update_attrs(void * ref,struct notification_attrs * out)114 static int update_attrs(void *ref, struct notification_attrs *out)
115 {
116 int i;
117
118 for (i = 0; i < CONFIG_LWM2M_NUM_ATTR; i++) {
119 if (ref != write_attr_pool[i].ref) {
120 continue;
121 }
122
123 switch (write_attr_pool[i].type) {
124 case LWM2M_ATTR_PMIN:
125 out->pmin = write_attr_pool[i].int_val;
126 break;
127 case LWM2M_ATTR_PMAX:
128 out->pmax = write_attr_pool[i].int_val;
129 break;
130 case LWM2M_ATTR_LT:
131 out->lt = write_attr_pool[i].float_val;
132 break;
133 case LWM2M_ATTR_GT:
134 out->gt = write_attr_pool[i].float_val;
135 break;
136 case LWM2M_ATTR_STEP:
137 out->st = write_attr_pool[i].float_val;
138 break;
139 default:
140 LOG_ERR("Unrecognized attr: %d", write_attr_pool[i].type);
141 return -EINVAL;
142 }
143
144 /* mark as set */
145 out->flags |= BIT(write_attr_pool[i].type);
146 }
147
148 return 0;
149 }
150
clear_attrs(void * ref)151 void clear_attrs(void *ref)
152 {
153 int i;
154
155 for (i = 0; i < CONFIG_LWM2M_NUM_ATTR; i++) {
156 if (ref == write_attr_pool[i].ref) {
157 (void)memset(&write_attr_pool[i], 0, sizeof(write_attr_pool[i]));
158 }
159 }
160 }
161
lwm2m_observer_path_compare(const struct lwm2m_obj_path * o_p,const struct lwm2m_obj_path * p)162 static bool lwm2m_observer_path_compare(const struct lwm2m_obj_path *o_p,
163 const struct lwm2m_obj_path *p)
164 {
165 /* check obj id matched or not */
166 if (p->obj_id != o_p->obj_id) {
167 return false;
168 }
169
170 if (o_p->level >= LWM2M_PATH_LEVEL_OBJECT_INST) {
171 if (p->level >= LWM2M_PATH_LEVEL_OBJECT_INST &&
172 p->obj_inst_id != o_p->obj_inst_id) {
173 return false;
174 }
175 }
176
177 if (o_p->level >= LWM2M_PATH_LEVEL_RESOURCE) {
178 if (p->level >= LWM2M_PATH_LEVEL_RESOURCE && p->res_id != o_p->res_id) {
179 return false;
180 }
181 }
182
183 if (IS_ENABLED(CONFIG_LWM2M_VERSION_1_1) && o_p->level == LWM2M_PATH_LEVEL_RESOURCE_INST) {
184 if (p->level == LWM2M_PATH_LEVEL_RESOURCE_INST &&
185 p->res_inst_id != o_p->res_inst_id) {
186 return false;
187 }
188 }
189
190 return true;
191 }
192
lwm2m_notify_observer_list(sys_slist_t * path_list,const struct lwm2m_obj_path * path)193 static bool lwm2m_notify_observer_list(sys_slist_t *path_list, const struct lwm2m_obj_path *path)
194 {
195 struct lwm2m_obj_path_list *o_p;
196
197 SYS_SLIST_FOR_EACH_CONTAINER(path_list, o_p, node) {
198 if (lwm2m_observer_path_compare(&o_p->path, path)) {
199 return true;
200 }
201 }
202
203 return false;
204 }
205
lwm2m_notify_observer(uint16_t obj_id,uint16_t obj_inst_id,uint16_t res_id)206 int lwm2m_notify_observer(uint16_t obj_id, uint16_t obj_inst_id, uint16_t res_id)
207 {
208 struct lwm2m_obj_path path;
209
210 path.level = LWM2M_PATH_LEVEL_RESOURCE;
211 path.obj_id = obj_id;
212 path.obj_inst_id = obj_inst_id;
213 path.res_id = res_id;
214
215 return lwm2m_notify_observer_path(&path);
216 }
217
engine_observe_get_attributes(const struct lwm2m_obj_path * path,struct notification_attrs * attrs,uint16_t srv_obj_inst)218 static int engine_observe_get_attributes(const struct lwm2m_obj_path *path,
219 struct notification_attrs *attrs, uint16_t srv_obj_inst)
220 {
221 struct lwm2m_engine_obj *obj;
222 struct lwm2m_engine_obj_field *obj_field = NULL;
223 struct lwm2m_engine_obj_inst *obj_inst = NULL;
224 struct lwm2m_engine_res_inst *res_inst = NULL;
225 int ret, i;
226
227 /* defaults from server object */
228 attrs->pmin = lwm2m_server_get_pmin(srv_obj_inst);
229 attrs->pmax = lwm2m_server_get_pmax(srv_obj_inst);
230 attrs->flags = BIT(LWM2M_ATTR_PMIN) | BIT(LWM2M_ATTR_PMAX);
231
232 /* check if object exists */
233 obj = get_engine_obj(path->obj_id);
234 if (!obj) {
235 LOG_ERR("unable to find obj: %u", path->obj_id);
236 return -ENOENT;
237 }
238
239 ret = update_attrs(obj, attrs);
240 if (ret < 0) {
241 return ret;
242 }
243
244 /* check if object instance exists */
245 if (path->level >= LWM2M_PATH_LEVEL_OBJECT_INST) {
246 obj_inst = get_engine_obj_inst(path->obj_id, path->obj_inst_id);
247 if (!obj_inst) {
248 attrs->pmax = 0;
249 attrs->pmin = 0;
250 return 0;
251 }
252
253 ret = update_attrs(obj_inst, attrs);
254 if (ret < 0) {
255 return ret;
256 }
257 }
258
259 /* check if resource exists */
260 if (path->level >= LWM2M_PATH_LEVEL_RESOURCE) {
261 for (i = 0; i < obj_inst->resource_count; i++) {
262 if (obj_inst->resources[i].res_id == path->res_id) {
263 break;
264 }
265 }
266
267 if (i == obj_inst->resource_count) {
268 LOG_ERR("unable to find res_id: %u/%u/%u", path->obj_id, path->obj_inst_id,
269 path->res_id);
270 return -ENOENT;
271 }
272
273 /* load object field data */
274 obj_field = lwm2m_get_engine_obj_field(obj, obj_inst->resources[i].res_id);
275 if (!obj_field) {
276 LOG_ERR("unable to find obj_field: %u/%u/%u", path->obj_id,
277 path->obj_inst_id, path->res_id);
278 return -ENOENT;
279 }
280
281 /* check for READ permission on matching resource */
282 if (!LWM2M_HAS_PERM(obj_field, LWM2M_PERM_R)) {
283 return -EPERM;
284 }
285
286 ret = update_attrs(&obj_inst->resources[i], attrs);
287 if (ret < 0) {
288 return ret;
289 }
290 }
291
292 /* check if resource instance exists */
293 if (IS_ENABLED(CONFIG_LWM2M_VERSION_1_1) && path->level == LWM2M_PATH_LEVEL_RESOURCE_INST) {
294 ret = path_to_objs(path, NULL, NULL, NULL, &res_inst);
295 if (ret < 0) {
296 return ret;
297 }
298
299 if (res_inst == NULL) {
300 return -ENOENT;
301 }
302
303 ret = update_attrs(res_inst, attrs);
304 if (ret < 0) {
305 return ret;
306 }
307 }
308
309 attrs->pmax = (attrs->pmax >= attrs->pmin) ? (uint32_t)attrs->pmax : 0UL;
310
311 return 0;
312 }
313
engine_observe_attribute_list_get(sys_slist_t * path_list,struct notification_attrs * nattrs,uint16_t server_obj_inst)314 int engine_observe_attribute_list_get(sys_slist_t *path_list, struct notification_attrs *nattrs,
315 uint16_t server_obj_inst)
316 {
317 struct lwm2m_obj_path_list *o_p;
318 /* Temporary compare values */
319 int32_t pmin = 0;
320 int32_t pmax = 0;
321 int ret;
322
323 SYS_SLIST_FOR_EACH_CONTAINER(path_list, o_p, node) {
324 nattrs->pmin = 0;
325 nattrs->pmax = 0;
326
327 ret = engine_observe_get_attributes(&o_p->path, nattrs, server_obj_inst);
328 if (ret < 0) {
329 return ret;
330 }
331
332 if (nattrs->pmin) {
333 if (pmin == 0) {
334 pmin = nattrs->pmin;
335 } else {
336 pmin = MIN(pmin, nattrs->pmin);
337 }
338 }
339
340 if (nattrs->pmax) {
341 if (pmax == 0) {
342 pmax = nattrs->pmax;
343 } else {
344 pmax = MIN(pmax, nattrs->pmax);
345 }
346 }
347 }
348
349 nattrs->pmin = pmin;
350 nattrs->pmax = pmax;
351 return 0;
352 }
353
lwm2m_notify_observer_path(const struct lwm2m_obj_path * path)354 int lwm2m_notify_observer_path(const struct lwm2m_obj_path *path)
355 {
356 struct observe_node *obs;
357 struct notification_attrs nattrs = {0};
358 int64_t timestamp;
359 int ret = 0;
360 int i;
361 struct lwm2m_ctx **sock_ctx = lwm2m_sock_ctx();
362
363 if (path->level < LWM2M_PATH_LEVEL_OBJECT) {
364 return 0;
365 }
366
367 /* look for observers which match our resource */
368 for (i = 0; i < lwm2m_sock_nfds(); ++i) {
369 SYS_SLIST_FOR_EACH_CONTAINER(&sock_ctx[i]->observer, obs, node) {
370 if (lwm2m_notify_observer_list(&obs->path_list, path)) {
371 /* update the event time for this observer */
372 ret = engine_observe_attribute_list_get(&obs->path_list, &nattrs,
373 sock_ctx[i]->srv_obj_inst);
374 if (ret < 0) {
375 return ret;
376 }
377
378 if (nattrs.pmin) {
379 timestamp =
380 obs->last_timestamp + MSEC_PER_SEC * nattrs.pmin;
381 } else {
382 /* Trig immediately */
383 timestamp = k_uptime_get();
384 }
385
386 if (!obs->event_timestamp || obs->event_timestamp > timestamp) {
387 obs->resource_update = true;
388 obs->event_timestamp = timestamp;
389 }
390
391 LOG_DBG("NOTIFY EVENT %u/%u/%u", path->obj_id, path->obj_inst_id,
392 path->res_id);
393 ret++;
394 lwm2m_engine_wake_up();
395 }
396 }
397 }
398
399 return ret;
400 }
401
engine_allocate_observer(sys_slist_t * path_list,bool composite)402 static struct observe_node *engine_allocate_observer(sys_slist_t *path_list, bool composite)
403 {
404 int i;
405 struct lwm2m_obj_path_list *entry, *tmp;
406 struct observe_node *obs = NULL;
407
408 /* find an unused observer index node */
409 for (i = 0; i < CONFIG_LWM2M_ENGINE_MAX_OBSERVER; i++) {
410 if (!observe_node_data[i].tkl) {
411
412 obs = &observe_node_data[i];
413 break;
414 }
415 }
416
417 if (!obs) {
418 return NULL;
419 }
420
421 sys_slist_init(&obs->path_list);
422 obs->composite = composite;
423
424 /* Allocate and copy path */
425 SYS_SLIST_FOR_EACH_CONTAINER(path_list, tmp, node) {
426 /* Allocate path entry */
427 entry = lwm2m_engine_get_from_list(&obs_obj_path_list);
428 if (!entry) {
429 /* Free list */
430 lwm2m_engine_free_list(&obs->path_list, &obs_obj_path_list);
431 return NULL;
432 }
433
434 /* copy the values and add it to the list */
435 memcpy(&entry->path, &tmp->path, sizeof(tmp->path));
436 /* Add to last by keeping already sorted order */
437 sys_slist_append(&obs->path_list, &entry->node);
438 }
439
440 return obs;
441 }
442
engine_observe_node_init(struct observe_node * obs,const uint8_t * token,struct lwm2m_ctx * ctx,uint8_t tkl,uint16_t format,int32_t att_pmax)443 static void engine_observe_node_init(struct observe_node *obs, const uint8_t *token,
444 struct lwm2m_ctx *ctx, uint8_t tkl, uint16_t format,
445 int32_t att_pmax)
446 {
447 struct lwm2m_obj_path_list *tmp;
448
449 memcpy(obs->token, token, tkl);
450 obs->tkl = tkl;
451
452 obs->last_timestamp = k_uptime_get();
453 if (att_pmax) {
454 obs->event_timestamp = obs->last_timestamp + MSEC_PER_SEC * att_pmax;
455 } else {
456 obs->event_timestamp = 0;
457 }
458 obs->resource_update = false;
459 obs->active_notify = NULL;
460 obs->format = format;
461 obs->counter = OBSERVE_COUNTER_START;
462 sys_slist_append(&ctx->observer, &obs->node);
463
464 SYS_SLIST_FOR_EACH_CONTAINER(&obs->path_list, tmp, node) {
465 LOG_DBG("OBSERVER ADDED %u/%u/%u/%u(%u)", tmp->path.obj_id, tmp->path.obj_inst_id,
466 tmp->path.res_id, tmp->path.res_inst_id, tmp->path.level);
467
468 if (ctx->observe_cb) {
469 ctx->observe_cb(LWM2M_OBSERVE_EVENT_OBSERVER_ADDED, &tmp->path, ctx);
470 }
471 }
472
473 LOG_DBG("token:'%s' addr:%s", sprint_token(token, tkl),
474 lwm2m_sprint_ip_addr(&ctx->remote_addr));
475 }
476
remove_observer_path_from_list(struct lwm2m_ctx * ctx,struct observe_node * obs,struct lwm2m_obj_path_list * o_p,sys_snode_t * prev_node)477 static void remove_observer_path_from_list(struct lwm2m_ctx *ctx, struct observe_node *obs,
478 struct lwm2m_obj_path_list *o_p, sys_snode_t *prev_node)
479 {
480 char buf[LWM2M_MAX_PATH_STR_SIZE];
481
482 LOG_DBG("Removing observer %p for path %s", obs, lwm2m_path_log_buf(buf, &o_p->path));
483 if (ctx->observe_cb) {
484 ctx->observe_cb(LWM2M_OBSERVE_EVENT_OBSERVER_REMOVED, &o_p->path, NULL);
485 }
486 /* Remove from the list and add to free list */
487 sys_slist_remove(&obs->path_list, prev_node, &o_p->node);
488 sys_slist_append(&obs_obj_path_list, &o_p->node);
489 }
490
engine_observe_single_path_id_remove(struct lwm2m_ctx * ctx,struct observe_node * obs,uint16_t obj_id,int32_t obj_inst_id)491 static void engine_observe_single_path_id_remove(struct lwm2m_ctx *ctx, struct observe_node *obs,
492 uint16_t obj_id, int32_t obj_inst_id)
493 {
494 struct lwm2m_obj_path_list *o_p, *tmp;
495 sys_snode_t *prev_node = NULL;
496
497 SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&obs->path_list, o_p, tmp, node) {
498 if (o_p->path.obj_id != obj_id && o_p->path.obj_inst_id) {
499 prev_node = &o_p->node;
500 continue;
501 }
502
503 if (obj_inst_id == -1 || o_p->path.obj_inst_id == obj_inst_id) {
504 remove_observer_path_from_list(ctx, obs, o_p, prev_node);
505 } else {
506 prev_node = &o_p->node;
507 }
508 }
509 }
510
engine_compare_obs_path_list(sys_slist_t * obs_path_list,sys_slist_t * path_list,int list_length)511 static bool engine_compare_obs_path_list(sys_slist_t *obs_path_list, sys_slist_t *path_list,
512 int list_length)
513 {
514 sys_snode_t *obs_ptr, *comp_ptr;
515 struct lwm2m_obj_path_list *obs_path, *comp_path;
516
517 obs_ptr = sys_slist_peek_head(obs_path_list);
518 comp_ptr = sys_slist_peek_head(path_list);
519 while (list_length--) {
520 obs_path = (struct lwm2m_obj_path_list *)obs_ptr;
521 comp_path = (struct lwm2m_obj_path_list *)comp_ptr;
522 if (memcmp(&obs_path->path, &comp_path->path, sizeof(struct lwm2m_obj_path))) {
523 return false;
524 }
525 /* Read Next Info from list entry*/
526 obs_ptr = sys_slist_peek_next_no_check(obs_ptr);
527 comp_ptr = sys_slist_peek_next_no_check(comp_ptr);
528 }
529
530 return true;
531 }
532
engine_path_list_size(sys_slist_t * lwm2m_path_list)533 static int engine_path_list_size(sys_slist_t *lwm2m_path_list)
534 {
535 int list_size = 0;
536 struct lwm2m_obj_path_list *entry;
537
538 SYS_SLIST_FOR_EACH_CONTAINER(lwm2m_path_list, entry, node) {
539 list_size++;
540 }
541 return list_size;
542 }
543
engine_observe_node_discover(sys_slist_t * observe_node_list,sys_snode_t ** prev_node,sys_slist_t * lwm2m_path_list,const uint8_t * token,uint8_t tkl)544 struct observe_node *engine_observe_node_discover(sys_slist_t *observe_node_list,
545 sys_snode_t **prev_node,
546 sys_slist_t *lwm2m_path_list,
547 const uint8_t *token, uint8_t tkl)
548 {
549 struct observe_node *obs;
550 int obs_list_size, path_list_size = 0;
551
552 if (lwm2m_path_list) {
553 path_list_size = engine_path_list_size(lwm2m_path_list);
554 }
555
556 *prev_node = NULL;
557
558 SYS_SLIST_FOR_EACH_CONTAINER(observe_node_list, obs, node) {
559
560 if (lwm2m_path_list) {
561 /* Validate Path for discovery */
562 obs_list_size = engine_path_list_size(&obs->path_list);
563
564 if (obs_list_size != path_list_size) {
565 *prev_node = &obs->node;
566 continue;
567 }
568
569 if (!engine_compare_obs_path_list(&obs->path_list, lwm2m_path_list,
570 obs_list_size)) {
571 *prev_node = &obs->node;
572 continue;
573 }
574 }
575
576 if (token && memcmp(obs->token, token, tkl)) {
577 /* Token not match */
578 *prev_node = &obs->node;
579 continue;
580 }
581 return obs;
582 }
583 return NULL;
584 }
585
engine_add_observer(struct lwm2m_message * msg,const uint8_t * token,uint8_t tkl,uint16_t format)586 static int engine_add_observer(struct lwm2m_message *msg, const uint8_t *token, uint8_t tkl,
587 uint16_t format)
588 {
589 struct observe_node *obs;
590 struct notification_attrs attrs;
591 struct lwm2m_obj_path_list obs_path_list_buf;
592 sys_slist_t lwm2m_path_list;
593 sys_snode_t *prev_node = NULL;
594 int ret;
595
596 if (!msg || !msg->ctx) {
597 LOG_ERR("valid lwm2m message is required");
598 return -EINVAL;
599 }
600
601 if (!token || (tkl == 0U || tkl > MAX_TOKEN_LEN)) {
602 LOG_ERR("token(%p) and token length(%u) must be valid.", token, tkl);
603 return -EINVAL;
604 }
605
606 /* Create 1 entry linked list for message path */
607 memcpy(&obs_path_list_buf.path, &msg->path, sizeof(struct lwm2m_obj_path));
608 sys_slist_init(&lwm2m_path_list);
609 sys_slist_append(&lwm2m_path_list, &obs_path_list_buf.node);
610
611 obs = engine_observe_node_discover(&msg->ctx->observer, &prev_node, &lwm2m_path_list, NULL,
612 0);
613 if (obs) {
614 memcpy(obs->token, token, tkl);
615 obs->tkl = tkl;
616
617 /* Cancel ongoing notification */
618 if (obs->active_notify != NULL) {
619 lwm2m_reset_message(obs->active_notify, true);
620 obs->active_notify = NULL;
621 }
622
623 LOG_DBG("OBSERVER DUPLICATE %u/%u/%u(%u) [%s]", msg->path.obj_id,
624 msg->path.obj_inst_id, msg->path.res_id, msg->path.level,
625 lwm2m_sprint_ip_addr(&msg->ctx->remote_addr));
626
627 return 0;
628 }
629
630 /* Read attributes and allocate new entry */
631 ret = engine_observe_get_attributes(&msg->path, &attrs, msg->ctx->srv_obj_inst);
632 if (ret < 0) {
633 return ret;
634 }
635
636 obs = engine_allocate_observer(&lwm2m_path_list, false);
637 if (!obs) {
638 return -ENOMEM;
639 }
640
641 engine_observe_node_init(obs, token, msg->ctx, tkl, format, attrs.pmax);
642 return 0;
643 }
644
do_composite_observe_read_path_op(struct lwm2m_message * msg,uint16_t content_format,sys_slist_t * lwm2m_path_list,sys_slist_t * lwm2m_path_free_list)645 int do_composite_observe_read_path_op(struct lwm2m_message *msg, uint16_t content_format,
646 sys_slist_t *lwm2m_path_list,
647 sys_slist_t *lwm2m_path_free_list)
648 {
649 switch (content_format) {
650 #if defined(CONFIG_LWM2M_RW_SENML_JSON_SUPPORT)
651 case LWM2M_FORMAT_APP_SEML_JSON:
652 return do_composite_observe_parse_path_senml_json(msg, lwm2m_path_list,
653 lwm2m_path_free_list);
654 #endif
655
656 #if defined(CONFIG_LWM2M_RW_SENML_CBOR_SUPPORT)
657 case LWM2M_FORMAT_APP_SENML_CBOR:
658 return do_composite_observe_parse_path_senml_cbor(msg, lwm2m_path_list,
659 lwm2m_path_free_list);
660 #endif
661
662 default:
663 LOG_ERR("Unsupported content-format: %u", content_format);
664 return -ENOMSG;
665 }
666 }
667
engine_add_composite_observer(struct lwm2m_message * msg,const uint8_t * token,uint8_t tkl,uint16_t format)668 static int engine_add_composite_observer(struct lwm2m_message *msg, const uint8_t *token,
669 uint8_t tkl, uint16_t format)
670 {
671 struct observe_node *obs;
672 struct notification_attrs attrs;
673 struct lwm2m_obj_path_list lwm2m_path_list_buf[CONFIG_LWM2M_COMPOSITE_PATH_LIST_SIZE];
674 sys_slist_t lwm2m_path_list;
675 sys_slist_t lwm2m_path_free_list;
676 sys_snode_t *prev_node = NULL;
677 int ret;
678
679 if (!msg || !msg->ctx) {
680 LOG_ERR("valid lwm2m message is required");
681 return -EINVAL;
682 }
683
684 if (!token || (tkl == 0U || tkl > MAX_TOKEN_LEN)) {
685 LOG_ERR("token(%p) and token length(%u) must be valid.", token, tkl);
686 return -EINVAL;
687 }
688
689 /* Init list */
690 lwm2m_engine_path_list_init(&lwm2m_path_list, &lwm2m_path_free_list, lwm2m_path_list_buf,
691 CONFIG_LWM2M_COMPOSITE_PATH_LIST_SIZE);
692
693 /* Read attributes and allocate new entry */
694 ret = do_composite_observe_read_path_op(msg, format, &lwm2m_path_list,
695 &lwm2m_path_free_list);
696 if (ret < 0) {
697 return ret;
698 }
699
700 obs = engine_observe_node_discover(&msg->ctx->observer, &prev_node, &lwm2m_path_list, NULL,
701 0);
702 if (obs) {
703 memcpy(obs->token, token, tkl);
704 obs->tkl = tkl;
705
706 /* Cancel ongoing notification */
707 if (obs->active_notify != NULL) {
708 lwm2m_reset_message(obs->active_notify, true);
709 obs->active_notify = NULL;
710 }
711
712 LOG_DBG("OBSERVER Composite DUPLICATE [%s]",
713 lwm2m_sprint_ip_addr(&msg->ctx->remote_addr));
714
715 return do_composite_read_op_for_parsed_list(msg, format, &lwm2m_path_list);
716 }
717
718 ret = engine_observe_attribute_list_get(&lwm2m_path_list, &attrs, msg->ctx->srv_obj_inst);
719 if (ret < 0) {
720 return ret;
721 }
722
723 obs = engine_allocate_observer(&lwm2m_path_list, true);
724 if (!obs) {
725 return -ENOMEM;
726 }
727 engine_observe_node_init(obs, token, msg->ctx, tkl, format, attrs.pmax);
728 return do_composite_read_op_for_parsed_list(msg, format, &lwm2m_path_list);
729 }
730
remove_observer_from_list(struct lwm2m_ctx * ctx,sys_snode_t * prev_node,struct observe_node * obs)731 void remove_observer_from_list(struct lwm2m_ctx *ctx, sys_snode_t *prev_node,
732 struct observe_node *obs)
733 {
734 struct lwm2m_obj_path_list *o_p, *tmp;
735
736 SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&obs->path_list, o_p, tmp, node) {
737 remove_observer_path_from_list(ctx, obs, o_p, NULL);
738 }
739 sys_slist_remove(&ctx->observer, prev_node, &obs->node);
740 (void)memset(obs, 0, sizeof(*obs));
741 }
742
engine_remove_observer_by_token(struct lwm2m_ctx * ctx,const uint8_t * token,uint8_t tkl)743 int engine_remove_observer_by_token(struct lwm2m_ctx *ctx, const uint8_t *token, uint8_t tkl)
744 {
745 struct observe_node *obs;
746 sys_snode_t *prev_node = NULL;
747
748 if (!token || (tkl == 0U || tkl > MAX_TOKEN_LEN)) {
749 LOG_ERR("token(%p) and token length(%u) must be valid.", token, tkl);
750 return -EINVAL;
751 }
752
753 obs = engine_observe_node_discover(&ctx->observer, &prev_node, NULL, token, tkl);
754 if (!obs) {
755 return -ENOENT;
756 }
757
758 remove_observer_from_list(ctx, prev_node, obs);
759
760 LOG_DBG("observer '%s' removed", sprint_token(token, tkl));
761
762 return 0;
763 }
764
engine_remove_composite_observer(struct lwm2m_message * msg,const uint8_t * token,uint8_t tkl,uint16_t format)765 static int engine_remove_composite_observer(struct lwm2m_message *msg, const uint8_t *token,
766 uint8_t tkl, uint16_t format)
767 {
768 struct observe_node *obs;
769 sys_snode_t *prev_node = NULL;
770 struct lwm2m_obj_path_list lwm2m_path_list_buf[CONFIG_LWM2M_COMPOSITE_PATH_LIST_SIZE];
771 sys_slist_t lwm2m_path_list;
772 sys_slist_t lwm2m_path_free_list;
773 int ret;
774
775 if (!token || (tkl == 0U || tkl > MAX_TOKEN_LEN)) {
776 LOG_ERR("token(%p) and token length(%u) must be valid.", token, tkl);
777 return -EINVAL;
778 }
779
780 /* Init list */
781 lwm2m_engine_path_list_init(&lwm2m_path_list, &lwm2m_path_free_list, lwm2m_path_list_buf,
782 CONFIG_LWM2M_COMPOSITE_PATH_LIST_SIZE);
783
784 ret = do_composite_observe_read_path_op(msg, format, &lwm2m_path_list,
785 &lwm2m_path_free_list);
786 if (ret < 0) {
787 return ret;
788 }
789
790 obs = engine_observe_node_discover(&msg->ctx->observer, &prev_node, &lwm2m_path_list, token,
791 tkl);
792 if (!obs) {
793 return -ENOENT;
794 }
795
796 remove_observer_from_list(msg->ctx, prev_node, obs);
797
798 LOG_DBG("observer '%s' removed", sprint_token(token, tkl));
799
800 return do_composite_read_op_for_parsed_list(msg, format, &lwm2m_path_list);
801 }
802
803 #if defined(CONFIG_LOG)
lwm2m_path_log_buf(char * buf,struct lwm2m_obj_path * path)804 char *lwm2m_path_log_buf(char *buf, struct lwm2m_obj_path *path)
805 {
806 size_t cur;
807
808 if (!path) {
809 sprintf(buf, "/");
810 return buf;
811 }
812
813 cur = sprintf(buf, "%u", path->obj_id);
814
815 if (path->level > 1) {
816 cur += sprintf(buf + cur, "/%u", path->obj_inst_id);
817 }
818 if (path->level > 2) {
819 cur += sprintf(buf + cur, "/%u", path->res_id);
820 }
821 if (path->level > 3) {
822 cur += sprintf(buf + cur, "/%u", path->res_inst_id);
823 }
824
825 return buf;
826 }
827 #endif /* CONFIG_LOG */
828
829 #if defined(CONFIG_LWM2M_CANCEL_OBSERVE_BY_PATH)
engine_remove_observer_by_path(struct lwm2m_ctx * ctx,struct lwm2m_obj_path * path)830 static int engine_remove_observer_by_path(struct lwm2m_ctx *ctx, struct lwm2m_obj_path *path)
831 {
832 char buf[LWM2M_MAX_PATH_STR_SIZE];
833 struct observe_node *obs;
834 struct lwm2m_obj_path_list obs_path_list_buf;
835 sys_slist_t lwm2m_path_list;
836 sys_snode_t *prev_node = NULL;
837
838 /* Create 1 entry linked list for message path */
839 memcpy(&obs_path_list_buf.path, path, sizeof(struct lwm2m_obj_path));
840 sys_slist_init(&lwm2m_path_list);
841 sys_slist_append(&lwm2m_path_list, &obs_path_list_buf.node);
842
843 obs = engine_observe_node_discover(&ctx->observer, &prev_node, &lwm2m_path_list, NULL, 0);
844 if (!obs) {
845 return -ENOENT;
846 }
847
848 LOG_INF("Removing observer for path %s", lwm2m_path_log_buf(buf, path));
849
850 remove_observer_from_list(ctx, prev_node, obs);
851
852 return 0;
853 }
854 #endif /* CONFIG_LWM2M_CANCEL_OBSERVE_BY_PATH */
855
engine_remove_observer_by_id(uint16_t obj_id,int32_t obj_inst_id)856 void engine_remove_observer_by_id(uint16_t obj_id, int32_t obj_inst_id)
857 {
858 struct observe_node *obs, *tmp;
859 sys_snode_t *prev_node = NULL;
860 int i;
861 struct lwm2m_ctx **sock_ctx = lwm2m_sock_ctx();
862
863 /* remove observer instances accordingly */
864 for (i = 0; i < lwm2m_sock_nfds(); ++i) {
865 SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&sock_ctx[i]->observer, obs, tmp, node) {
866 engine_observe_single_path_id_remove(sock_ctx[i], obs, obj_id, obj_inst_id);
867
868 if (sys_slist_is_empty(&obs->path_list)) {
869 remove_observer_from_list(sock_ctx[i], prev_node, obs);
870 } else {
871 prev_node = &obs->node;
872 }
873 }
874 }
875 }
876
lwm2m_update_or_allocate_attribute(void * ref,uint8_t type,void * data)877 static int lwm2m_update_or_allocate_attribute(void *ref, uint8_t type, void *data)
878 {
879 struct lwm2m_attr *attr;
880 int i;
881
882 /* find matching attributes */
883 for (i = 0; i < CONFIG_LWM2M_NUM_ATTR; i++) {
884 if (ref != write_attr_pool[i].ref || write_attr_pool[i].type != type) {
885 continue;
886 }
887
888 attr = write_attr_pool + i;
889 type = attr->type;
890
891 if (type <= LWM2M_ATTR_PMAX) {
892 attr->int_val = *(int32_t *)data;
893 LOG_DBG("Update %s to %d", lwm2m_attr_to_str(type), attr->int_val);
894 } else {
895 attr->float_val = *(double *)data;
896 LOG_DBG("Update %s to %f", lwm2m_attr_to_str(type), attr->float_val);
897 }
898 return 0;
899 }
900
901 /* add attribute to obj/obj_inst/res/res_inst */
902 /* grab an entry for newly added attribute */
903 for (i = 0; i < CONFIG_LWM2M_NUM_ATTR; i++) {
904 if (!write_attr_pool[i].ref) {
905 break;
906 }
907 }
908
909 if (i == CONFIG_LWM2M_NUM_ATTR) {
910 return -ENOMEM;
911 }
912
913 attr = write_attr_pool + i;
914 attr->type = type;
915 attr->ref = ref;
916
917 if (type <= LWM2M_ATTR_PMAX) {
918 attr->int_val = *(int32_t *)data;
919 LOG_DBG("Add %s to %d", lwm2m_attr_to_str(type), attr->int_val);
920 } else {
921 attr->float_val = *(double *)data;
922 LOG_DBG("Add %s to %f", lwm2m_attr_to_str(type), attr->float_val);
923 }
924 return 0;
925 }
926
lwm2m_engine_get_attr_name(const struct lwm2m_attr * attr)927 const char *lwm2m_engine_get_attr_name(const struct lwm2m_attr *attr)
928 {
929 if (attr->type >= NR_LWM2M_ATTR) {
930 return NULL;
931 }
932
933 return lwm2m_attr_to_str(attr->type);
934 }
935
lwm2m_engine_observer_timestamp_update(sys_slist_t * observer,const struct lwm2m_obj_path * path,uint16_t srv_obj_inst)936 static int lwm2m_engine_observer_timestamp_update(sys_slist_t *observer,
937 const struct lwm2m_obj_path *path,
938 uint16_t srv_obj_inst)
939 {
940 struct observe_node *obs;
941 struct notification_attrs nattrs = {0};
942 int ret;
943 int64_t timestamp;
944
945 /* update observe_node accordingly */
946 SYS_SLIST_FOR_EACH_CONTAINER(observer, obs, node) {
947 if (obs->resource_update) {
948 /* Resource Update on going skip this*/
949 continue;
950 }
951 /* Compare Observation node path to updated one */
952 if (!lwm2m_notify_observer_list(&obs->path_list, path)) {
953 continue;
954 }
955
956 /* Read Attributes after validation Path */
957 ret = engine_observe_attribute_list_get(&obs->path_list, &nattrs, srv_obj_inst);
958 if (ret < 0) {
959 return ret;
960 }
961
962 /* Update based on by PMax */
963 if (nattrs.pmax) {
964 /* Update Current */
965 timestamp = obs->last_timestamp + MSEC_PER_SEC * nattrs.pmax;
966 } else {
967 /* Disable Automatic Notify */
968 timestamp = 0;
969 }
970 obs->event_timestamp = timestamp;
971
972 (void)memset(&nattrs, 0, sizeof(nattrs));
973 }
974 return 0;
975 }
976
977 /* input / output selection */
978
lwm2m_get_path_reference_ptr(struct lwm2m_engine_obj * obj,const struct lwm2m_obj_path * path,void ** ref)979 int lwm2m_get_path_reference_ptr(struct lwm2m_engine_obj *obj, const struct lwm2m_obj_path *path,
980 void **ref)
981 {
982 struct lwm2m_engine_obj_inst *obj_inst;
983 struct lwm2m_engine_res *res;
984 struct lwm2m_engine_res_inst *res_inst;
985 int ret;
986
987 if (!obj) {
988 /* Discover Object */
989 obj = get_engine_obj(path->obj_id);
990 if (!obj) {
991 /* No matching object found - ignore request */
992 return -ENOENT;
993 }
994 }
995
996 if (path->level == LWM2M_PATH_LEVEL_OBJECT) {
997 *ref = obj;
998 } else if (path->level == LWM2M_PATH_LEVEL_OBJECT_INST) {
999 obj_inst = get_engine_obj_inst(path->obj_id, path->obj_inst_id);
1000 if (!obj_inst) {
1001 return -ENOENT;
1002 }
1003
1004 *ref = obj_inst;
1005 } else if (path->level == LWM2M_PATH_LEVEL_RESOURCE) {
1006 ret = path_to_objs(path, NULL, NULL, &res, NULL);
1007 if (ret < 0) {
1008 return ret;
1009 }
1010
1011 *ref = res;
1012 } else if (IS_ENABLED(CONFIG_LWM2M_VERSION_1_1) &&
1013 path->level == LWM2M_PATH_LEVEL_RESOURCE_INST) {
1014
1015 ret = path_to_objs(path, NULL, NULL, NULL, &res_inst);
1016 if (ret < 0) {
1017 return ret;
1018 }
1019
1020 *ref = res_inst;
1021 } else {
1022 /* bad request */
1023 return -EEXIST;
1024 }
1025
1026 return 0;
1027 }
1028
lwm2m_update_observer_min_period(struct lwm2m_ctx * client_ctx,const struct lwm2m_obj_path * path,uint32_t period_s)1029 int lwm2m_update_observer_min_period(struct lwm2m_ctx *client_ctx,
1030 const struct lwm2m_obj_path *path, uint32_t period_s)
1031 {
1032 int ret;
1033 struct notification_attrs nattrs = {0};
1034 struct notification_attrs attrs = {0};
1035 void *ref;
1036
1037 /* Read Reference pointer to attribute */
1038 ret = lwm2m_get_path_reference_ptr(NULL, path, &ref);
1039 if (ret < 0) {
1040 return ret;
1041 }
1042 /* retrieve existing attributes */
1043 ret = update_attrs(ref, &nattrs);
1044 if (ret < 0) {
1045 return ret;
1046 }
1047
1048 if (nattrs.flags & BIT(LWM2M_ATTR_PMIN) && nattrs.pmin == period_s) {
1049 /* No need to change */
1050 return 0;
1051 }
1052
1053 /* Read Pmin & Pmax values for path */
1054 ret = engine_observe_get_attributes(path, &attrs, client_ctx->srv_obj_inst);
1055 if (ret < 0) {
1056 return ret;
1057 }
1058
1059 if (period_s && attrs.pmax && attrs.pmax < period_s) {
1060 LOG_DBG("New pmin (%d) > pmax (%d)", period_s, attrs.pmax);
1061 return -EEXIST;
1062 }
1063
1064 return lwm2m_update_or_allocate_attribute(ref, LWM2M_ATTR_PMIN, &period_s);
1065 }
1066
lwm2m_update_observer_max_period(struct lwm2m_ctx * client_ctx,const struct lwm2m_obj_path * path,uint32_t period_s)1067 int lwm2m_update_observer_max_period(struct lwm2m_ctx *client_ctx,
1068 const struct lwm2m_obj_path *path, uint32_t period_s)
1069 {
1070 int ret;
1071 void *ref;
1072 struct notification_attrs nattrs = {0};
1073 struct notification_attrs attrs = {0};
1074
1075 /* Read Reference pointer to attribute */
1076 ret = lwm2m_get_path_reference_ptr(NULL, path, &ref);
1077 if (ret < 0) {
1078 return ret;
1079 }
1080 /* retrieve existing attributes */
1081 ret = update_attrs(ref, &nattrs);
1082 if (ret < 0) {
1083 return ret;
1084 }
1085
1086 if (nattrs.flags & BIT(LWM2M_ATTR_PMAX) && nattrs.pmax == period_s) {
1087 /* No need to change */
1088 return 0;
1089 }
1090
1091 /* Read Pmin & Pmax values for path */
1092 ret = engine_observe_get_attributes(path, &attrs, client_ctx->srv_obj_inst);
1093 if (ret < 0) {
1094 return ret;
1095 }
1096
1097 if (period_s && attrs.pmin > period_s) {
1098 LOG_DBG("pmin (%d) > new pmax (%d)", attrs.pmin, period_s);
1099 return -EEXIST;
1100 }
1101
1102 /* Update or allocate new */
1103 ret = lwm2m_update_or_allocate_attribute(ref, LWM2M_ATTR_PMAX, &period_s);
1104 if (ret < 0) {
1105 return ret;
1106 }
1107
1108 /* Update Observer timestamp */
1109 return lwm2m_engine_observer_timestamp_update(&client_ctx->observer, path,
1110 client_ctx->srv_obj_inst);
1111 }
1112
lwm2m_engine_get_next_attr(const void * ref,struct lwm2m_attr * prev)1113 struct lwm2m_attr *lwm2m_engine_get_next_attr(const void *ref, struct lwm2m_attr *prev)
1114 {
1115 struct lwm2m_attr *iter = (prev == NULL) ? write_attr_pool : prev + 1;
1116 struct lwm2m_attr *result = NULL;
1117
1118 if (!PART_OF_ARRAY(write_attr_pool, iter)) {
1119 return NULL;
1120 }
1121
1122 while (iter < &write_attr_pool[ARRAY_SIZE(write_attr_pool)]) {
1123 if (ref == iter->ref) {
1124 result = iter;
1125 break;
1126 }
1127
1128 ++iter;
1129 }
1130
1131 return result;
1132 }
1133
lwm2m_write_attr_handler(struct lwm2m_engine_obj * obj,struct lwm2m_message * msg)1134 int lwm2m_write_attr_handler(struct lwm2m_engine_obj *obj, struct lwm2m_message *msg)
1135 {
1136 bool update_observe_node = false;
1137 char opt_buf[COAP_OPTION_BUF_LEN];
1138 int nr_opt, i, ret = 0;
1139 struct coap_option options[NR_LWM2M_ATTR];
1140 struct lwm2m_attr *attr;
1141 struct notification_attrs nattrs = {0};
1142 uint8_t type = 0U;
1143 void *nattr_ptrs[NR_LWM2M_ATTR] = {&nattrs.pmin, &nattrs.pmax, &nattrs.gt, &nattrs.lt,
1144 &nattrs.st};
1145 void *ref;
1146
1147 if (!obj || !msg) {
1148 return -EINVAL;
1149 }
1150
1151 /* do not expose security obj */
1152 if (obj->obj_id == LWM2M_OBJECT_SECURITY_ID) {
1153 return -ENOENT;
1154 }
1155
1156 nr_opt = coap_find_options(msg->in.in_cpkt, COAP_OPTION_URI_QUERY, options, NR_LWM2M_ATTR);
1157 if (nr_opt <= 0) {
1158 LOG_ERR("No attribute found!");
1159 /* translate as bad request */
1160 return -EEXIST;
1161 }
1162
1163 /* get lwm2m_attr slist */
1164 ret = lwm2m_get_path_reference_ptr(obj, &msg->path, &ref);
1165 if (ret < 0) {
1166 return ret;
1167 }
1168
1169 /* retrieve existing attributes */
1170 ret = update_attrs(ref, &nattrs);
1171 if (ret < 0) {
1172 return ret;
1173 }
1174
1175 /* loop through options to parse attribute */
1176 for (i = 0; i < nr_opt; i++) {
1177 int limit = MIN(options[i].len, 5), plen = 0, vlen;
1178 struct lwm2m_attr val = {0};
1179
1180 type = 0U;
1181
1182 /* search for '=' */
1183 while (plen < limit && options[i].value[plen] != '=') {
1184 plen += 1;
1185 }
1186
1187 /* either length = 2(gt/lt/st) or = 4(pmin/pmax) */
1188 if (plen != 2 && plen != 4) {
1189 continue;
1190 }
1191
1192 /* matching attribute name */
1193 for (type = 0U; type < NR_LWM2M_ATTR; type++) {
1194 if (LWM2M_ATTR_LEN[type] == plen &&
1195 !memcmp(options[i].value, lwm2m_attr_to_str(type),
1196 LWM2M_ATTR_LEN[type])) {
1197 break;
1198 }
1199 }
1200
1201 /* unrecognized attribute */
1202 if (type == NR_LWM2M_ATTR) {
1203 continue;
1204 }
1205
1206 /* unset attribute when no value's given */
1207 if (options[i].len == plen) {
1208 nattrs.flags &= ~BIT(type);
1209
1210 (void)memset(nattr_ptrs[type], 0,
1211 type <= LWM2M_ATTR_PMAX ? sizeof(int32_t) : sizeof(double));
1212 continue;
1213 }
1214
1215 /* gt/lt/st cannot be assigned to obj/obj_inst unless unset */
1216 if (plen == 2 && msg->path.level <= 2U) {
1217 return -EEXIST;
1218 }
1219
1220 vlen = options[i].len - plen - 1;
1221 memcpy(opt_buf, options[i].value + plen + 1, vlen);
1222 opt_buf[vlen] = '\0';
1223
1224 /* convert value to integer or float */
1225 if (plen == 4) {
1226 char *end;
1227 long v;
1228
1229 /* pmin/pmax: integer (sec 5.1.2)
1230 * however, negative is non-sense
1231 */
1232 errno = 0;
1233 v = strtol(opt_buf, &end, 10);
1234 if (errno || *end || v < 0) {
1235 ret = -EINVAL;
1236 }
1237
1238 val.int_val = v;
1239 } else {
1240 /* gt/lt/st: type float */
1241 ret = lwm2m_atof(opt_buf, &val.float_val);
1242 }
1243
1244 if (ret < 0) {
1245 LOG_ERR("invalid attr[%s] value", lwm2m_attr_to_str(type));
1246 /* bad request */
1247 return -EEXIST;
1248 }
1249
1250 if (type <= LWM2M_ATTR_PMAX) {
1251 *(int32_t *)nattr_ptrs[type] = val.int_val;
1252 } else {
1253 memcpy(nattr_ptrs[type], &val.float_val, sizeof(val.float_val));
1254 }
1255
1256 nattrs.flags |= BIT(type);
1257 }
1258
1259 if (((nattrs.flags & (BIT(LWM2M_ATTR_PMIN) | BIT(LWM2M_ATTR_PMAX))) ==
1260 (BIT(LWM2M_ATTR_PMIN) | BIT(LWM2M_ATTR_PMAX))) &&
1261 nattrs.pmin > nattrs.pmax) {
1262 LOG_DBG("pmin (%d) > pmax (%d)", nattrs.pmin, nattrs.pmax);
1263 return -EEXIST;
1264 }
1265
1266 if ((nattrs.flags & (BIT(LWM2M_ATTR_LT) | BIT(LWM2M_ATTR_GT))) ==
1267 (BIT(LWM2M_ATTR_LT) | BIT(LWM2M_ATTR_GT))) {
1268 if (nattrs.lt > nattrs.gt) {
1269 LOG_DBG("lt > gt");
1270 return -EEXIST;
1271 }
1272
1273 if (nattrs.flags & BIT(LWM2M_ATTR_STEP)) {
1274 if (nattrs.lt + 2 * nattrs.st > nattrs.gt) {
1275 LOG_DBG("lt + 2*st > gt");
1276 return -EEXIST;
1277 }
1278 }
1279 }
1280
1281 /* find matching attributes */
1282 for (i = 0; i < CONFIG_LWM2M_NUM_ATTR; i++) {
1283 if (ref != write_attr_pool[i].ref) {
1284 continue;
1285 }
1286
1287 attr = write_attr_pool + i;
1288 type = attr->type;
1289
1290 if (!(BIT(type) & nattrs.flags)) {
1291 LOG_DBG("Unset attr %s", lwm2m_attr_to_str(type));
1292 (void)memset(attr, 0, sizeof(*attr));
1293
1294 if (type <= LWM2M_ATTR_PMAX) {
1295 update_observe_node = true;
1296 }
1297
1298 continue;
1299 }
1300
1301 nattrs.flags &= ~BIT(type);
1302
1303 if (type <= LWM2M_ATTR_PMAX) {
1304 if (attr->int_val == *(int32_t *)nattr_ptrs[type]) {
1305 continue;
1306 }
1307
1308 attr->int_val = *(int32_t *)nattr_ptrs[type];
1309 update_observe_node = true;
1310
1311 LOG_DBG("Update %s to %d", lwm2m_attr_to_str(type), attr->int_val);
1312 } else {
1313 if (attr->float_val == *(double *)nattr_ptrs[type]) {
1314 continue;
1315 }
1316
1317 attr->float_val = *(double *)nattr_ptrs[type];
1318
1319 LOG_DBG("Update %s to %f", lwm2m_attr_to_str(type), attr->float_val);
1320 }
1321 }
1322
1323 /* add attribute to obj/obj_inst/res/res_inst */
1324 for (type = 0U; nattrs.flags && type < NR_LWM2M_ATTR; type++) {
1325 if (!(BIT(type) & nattrs.flags)) {
1326 continue;
1327 }
1328
1329 /* grab an entry for newly added attribute */
1330 for (i = 0; i < CONFIG_LWM2M_NUM_ATTR; i++) {
1331 if (!write_attr_pool[i].ref) {
1332 break;
1333 }
1334 }
1335
1336 if (i == CONFIG_LWM2M_NUM_ATTR) {
1337 return -ENOMEM;
1338 }
1339
1340 attr = write_attr_pool + i;
1341 attr->type = type;
1342 attr->ref = ref;
1343
1344 if (type <= LWM2M_ATTR_PMAX) {
1345 attr->int_val = *(int32_t *)nattr_ptrs[type];
1346 update_observe_node = true;
1347
1348 LOG_DBG("Add %s to %d", lwm2m_attr_to_str(type), attr->int_val);
1349 } else {
1350 attr->float_val = *(double *)nattr_ptrs[type];
1351
1352 LOG_DBG("Add %s to %f", lwm2m_attr_to_str(type), attr->float_val);
1353 }
1354
1355 nattrs.flags &= ~BIT(type);
1356 }
1357
1358 /* check only pmin/pmax */
1359 if (!update_observe_node) {
1360 return 0;
1361 }
1362
1363 lwm2m_engine_observer_timestamp_update(&msg->ctx->observer, &msg->path,
1364 msg->ctx->srv_obj_inst);
1365
1366 return 0;
1367 }
1368
lwm2m_path_is_observed(const struct lwm2m_obj_path * path)1369 bool lwm2m_path_is_observed(const struct lwm2m_obj_path *path)
1370 {
1371 int i;
1372 struct observe_node *obs;
1373 struct lwm2m_ctx **sock_ctx = lwm2m_sock_ctx();
1374
1375 for (i = 0; i < lwm2m_sock_nfds(); ++i) {
1376 SYS_SLIST_FOR_EACH_CONTAINER(&sock_ctx[i]->observer, obs, node) {
1377
1378 if (lwm2m_notify_observer_list(&obs->path_list, path)) {
1379 return true;
1380 }
1381 }
1382 }
1383 return false;
1384 }
1385
lwm2m_engine_observation_handler(struct lwm2m_message * msg,int observe,uint16_t accept,bool composite)1386 int lwm2m_engine_observation_handler(struct lwm2m_message *msg, int observe, uint16_t accept,
1387 bool composite)
1388 {
1389 int r;
1390
1391 if (observe == 0) {
1392 /* add new observer */
1393 r = coap_append_option_int(msg->out.out_cpkt, COAP_OPTION_OBSERVE,
1394 OBSERVE_COUNTER_START);
1395 if (r < 0) {
1396 LOG_ERR("OBSERVE option error: %d", r);
1397 return r;
1398 }
1399
1400 if (composite) {
1401 r = engine_add_composite_observer(msg, msg->token, msg->tkl, accept);
1402 } else {
1403 r = engine_add_observer(msg, msg->token, msg->tkl, accept);
1404 }
1405
1406 if (r < 0) {
1407 LOG_ERR("add OBSERVE error: %d", r);
1408 }
1409 } else if (observe == 1) {
1410 /* remove observer */
1411 if (composite) {
1412 r = engine_remove_composite_observer(msg, msg->token, msg->tkl, accept);
1413 } else {
1414 r = engine_remove_observer_by_token(msg->ctx, msg->token, msg->tkl);
1415 if (r < 0) {
1416 #if defined(CONFIG_LWM2M_CANCEL_OBSERVE_BY_PATH)
1417 r = engine_remove_observer_by_path(msg->ctx, &msg->path);
1418 if (r < 0)
1419 #endif /* CONFIG_LWM2M_CANCEL_OBSERVE_BY_PATH */
1420 {
1421 LOG_ERR("remove observe error: %d", r);
1422 r = 0;
1423 }
1424 }
1425 }
1426
1427 } else {
1428 r = -EINVAL;
1429 }
1430 return r;
1431 }
1432
engine_observe_shedule_next_event(struct observe_node * obs,uint16_t srv_obj_inst,const int64_t timestamp)1433 int64_t engine_observe_shedule_next_event(struct observe_node *obs, uint16_t srv_obj_inst,
1434 const int64_t timestamp)
1435 {
1436 struct notification_attrs attrs;
1437 int64_t t_s = 0;
1438 int ret;
1439
1440 ret = engine_observe_attribute_list_get(&obs->path_list, &attrs, srv_obj_inst);
1441 if (ret < 0) {
1442 return 0;
1443 }
1444
1445 if (attrs.pmax) {
1446 t_s = timestamp + MSEC_PER_SEC * attrs.pmax;
1447 }
1448
1449 return t_s;
1450 }
1451
lwm2m_engine_get_from_list(sys_slist_t * path_list)1452 struct lwm2m_obj_path_list *lwm2m_engine_get_from_list(sys_slist_t *path_list)
1453 {
1454 sys_snode_t *path_node = sys_slist_get(path_list);
1455 struct lwm2m_obj_path_list *entry;
1456
1457 if (!path_node) {
1458 return NULL;
1459 }
1460
1461 entry = SYS_SLIST_CONTAINER(path_node, entry, node);
1462 if (entry) {
1463 memset(entry, 0, sizeof(struct lwm2m_obj_path_list));
1464 }
1465 return entry;
1466 }
1467
lwm2m_engine_free_list(sys_slist_t * path_list,sys_slist_t * free_list)1468 void lwm2m_engine_free_list(sys_slist_t *path_list, sys_slist_t *free_list)
1469 {
1470 sys_snode_t *node;
1471
1472 while (NULL != (node = sys_slist_get(path_list))) {
1473 /* Add to free list */
1474 sys_slist_append(free_list, node);
1475 }
1476 }
1477
lwm2m_engine_path_list_init(sys_slist_t * lwm2m_path_list,sys_slist_t * lwm2m_free_list,struct lwm2m_obj_path_list path_object_buf[],uint8_t path_object_size)1478 void lwm2m_engine_path_list_init(sys_slist_t *lwm2m_path_list, sys_slist_t *lwm2m_free_list,
1479 struct lwm2m_obj_path_list path_object_buf[],
1480 uint8_t path_object_size)
1481 {
1482 /* Init list */
1483 sys_slist_init(lwm2m_path_list);
1484 sys_slist_init(lwm2m_free_list);
1485
1486 /* Put buffer elements to free list */
1487 for (int i = 0; i < path_object_size; i++) {
1488 sys_slist_append(lwm2m_free_list, &path_object_buf[i].node);
1489 }
1490 }
1491
lwm2m_engine_add_path_to_list(sys_slist_t * lwm2m_path_list,sys_slist_t * lwm2m_free_list,const struct lwm2m_obj_path * path)1492 int lwm2m_engine_add_path_to_list(sys_slist_t *lwm2m_path_list, sys_slist_t *lwm2m_free_list,
1493 const struct lwm2m_obj_path *path)
1494 {
1495 struct lwm2m_obj_path_list *prev = NULL;
1496 struct lwm2m_obj_path_list *entry;
1497 struct lwm2m_obj_path_list *new_entry;
1498 bool add_before_current = false;
1499
1500 if (path->level == LWM2M_PATH_LEVEL_NONE) {
1501 /* Clear the list if we are adding the root path which includes all */
1502 lwm2m_engine_free_list(lwm2m_path_list, lwm2m_free_list);
1503 }
1504
1505 /* Check is it at list already here */
1506 new_entry = lwm2m_engine_get_from_list(lwm2m_free_list);
1507 if (!new_entry) {
1508 return -1;
1509 }
1510
1511 new_entry->path = *path;
1512 if (!sys_slist_is_empty(lwm2m_path_list)) {
1513
1514 /* Keep list Ordered by Object ID / Object instance / resource ID /
1515 * Resource Instance ID
1516 */
1517 SYS_SLIST_FOR_EACH_CONTAINER(lwm2m_path_list, entry, node) {
1518 if (entry->path.level == LWM2M_PATH_LEVEL_NONE ||
1519 lwm2m_obj_path_equal(&entry->path, &new_entry->path)) {
1520 /* Already Root request at list or current path is at list */
1521 sys_slist_append(lwm2m_free_list, &new_entry->node);
1522 return 0;
1523 }
1524
1525 /*
1526 * algorithm assumes that list is already properly sorted and that
1527 * there are no duplicates. general idea:
1528 * - if at any level up to new entry's path level, IDs below the level
1529 * match and the ID of the new entry at that level is smaller,
1530 * insert before.
1531 * - if all IDs of the new entry match the existing entry, insert before.
1532 * Because of sorting and no duplicates, the existing entry must have
1533 * higher path level and come after the new entry.
1534 */
1535 switch (new_entry->path.level) {
1536 case LWM2M_PATH_LEVEL_OBJECT:
1537 add_before_current = new_entry->path.obj_id <= entry->path.obj_id;
1538 break;
1539
1540 case LWM2M_PATH_LEVEL_OBJECT_INST:
1541 add_before_current =
1542 (new_entry->path.obj_id < entry->path.obj_id) ||
1543
1544 (entry->path.level >= LWM2M_PATH_LEVEL_OBJECT_INST &&
1545 entry->path.obj_id == new_entry->path.obj_id &&
1546 new_entry->path.obj_inst_id <= entry->path.obj_inst_id);
1547 break;
1548
1549 case LWM2M_PATH_LEVEL_RESOURCE:
1550 add_before_current =
1551 (new_entry->path.obj_id < entry->path.obj_id) ||
1552
1553 (entry->path.level >= LWM2M_PATH_LEVEL_OBJECT_INST &&
1554 entry->path.obj_id == new_entry->path.obj_id &&
1555 new_entry->path.obj_inst_id < entry->path.obj_inst_id) ||
1556
1557 (entry->path.level >= LWM2M_PATH_LEVEL_RESOURCE &&
1558 entry->path.obj_id == new_entry->path.obj_id &&
1559 entry->path.obj_inst_id == new_entry->path.obj_inst_id &&
1560 new_entry->path.res_id <= entry->path.res_id);
1561 break;
1562
1563 case LWM2M_PATH_LEVEL_RESOURCE_INST:
1564 add_before_current =
1565 (new_entry->path.obj_id < entry->path.obj_id) ||
1566
1567 (entry->path.level >= LWM2M_PATH_LEVEL_OBJECT_INST &&
1568 entry->path.obj_id == new_entry->path.obj_id &&
1569 new_entry->path.obj_inst_id < entry->path.obj_inst_id) ||
1570
1571 (entry->path.level >= LWM2M_PATH_LEVEL_RESOURCE &&
1572 entry->path.obj_id == new_entry->path.obj_id &&
1573 entry->path.obj_inst_id == new_entry->path.obj_inst_id &&
1574 new_entry->path.res_id < entry->path.res_id) ||
1575
1576 (entry->path.level >= LWM2M_PATH_LEVEL_RESOURCE_INST &&
1577 entry->path.obj_id == new_entry->path.obj_id &&
1578 entry->path.obj_inst_id == new_entry->path.obj_inst_id &&
1579 entry->path.res_id == new_entry->path.res_id &&
1580 new_entry->path.res_inst_id <= entry->path.res_inst_id);
1581 break;
1582 }
1583
1584 if (add_before_current) {
1585 if (prev) {
1586 sys_slist_insert(lwm2m_path_list, &prev->node,
1587 &new_entry->node);
1588 } else {
1589 sys_slist_prepend(lwm2m_path_list, &new_entry->node);
1590 }
1591 return 0;
1592 }
1593 prev = entry;
1594 }
1595 }
1596
1597 /* Add First or new tail entry */
1598 sys_slist_append(lwm2m_path_list, &new_entry->node);
1599 return 0;
1600 }
1601
lwm2m_engine_clear_duplicate_path(sys_slist_t * lwm2m_path_list,sys_slist_t * lwm2m_free_list)1602 void lwm2m_engine_clear_duplicate_path(sys_slist_t *lwm2m_path_list, sys_slist_t *lwm2m_free_list)
1603 {
1604 struct lwm2m_obj_path_list *prev = NULL;
1605 struct lwm2m_obj_path_list *entry, *tmp;
1606 bool remove_entry;
1607
1608 if (sys_slist_is_empty(lwm2m_path_list)) {
1609 return;
1610 }
1611
1612 /* Keep list Ordered but remove if shorter path is similar */
1613 SYS_SLIST_FOR_EACH_CONTAINER_SAFE(lwm2m_path_list, entry, tmp, node) {
1614
1615 if (prev && prev->path.level < entry->path.level) {
1616 if (prev->path.level == LWM2M_PATH_LEVEL_OBJECT &&
1617 entry->path.obj_id == prev->path.obj_id) {
1618 remove_entry = true;
1619 } else if (prev->path.level == LWM2M_PATH_LEVEL_OBJECT_INST &&
1620 entry->path.obj_id == prev->path.obj_id &&
1621 entry->path.obj_inst_id == prev->path.obj_inst_id) {
1622 /* Remove current from the list */
1623 remove_entry = true;
1624 } else if (prev->path.level == LWM2M_PATH_LEVEL_RESOURCE &&
1625 entry->path.obj_id == prev->path.obj_id &&
1626 entry->path.obj_inst_id == prev->path.obj_inst_id &&
1627 entry->path.res_id == prev->path.res_id) {
1628 /* Remove current from the list */
1629 remove_entry = true;
1630 } else {
1631 remove_entry = false;
1632 }
1633
1634 if (remove_entry) {
1635 /* Remove Current entry */
1636 sys_slist_remove(lwm2m_path_list, &prev->node, &entry->node);
1637 sys_slist_append(lwm2m_free_list, &entry->node);
1638 } else {
1639 prev = entry;
1640 }
1641 } else {
1642 prev = entry;
1643 }
1644 }
1645 }
1646