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