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