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