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