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