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_message_handling
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 <ctype.h>
22 #include <errno.h>
23 #include <stddef.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 #include <zephyr/init.h>
29 #include <zephyr/net/http/parser_url.h>
30 #include <zephyr/net/lwm2m.h>
31 #include <zephyr/net/lwm2m_path.h>
32 #include <zephyr/net/net_ip.h>
33 #include <zephyr/net/socket.h>
34 #include <zephyr/sys/printk.h>
35 #include <zephyr/types.h>
36 #include <zephyr/sys/hash_function.h>
37
38 #if defined(CONFIG_LWM2M_DTLS_SUPPORT)
39 #include <zephyr/net/tls_credentials.h>
40 #endif
41 #if defined(CONFIG_DNS_RESOLVER)
42 #include <zephyr/net/dns_resolve.h>
43 #endif
44
45 #include "lwm2m_engine.h"
46 #include "lwm2m_object.h"
47 #include "lwm2m_obj_access_control.h"
48 #include "lwm2m_obj_server.h"
49 #include "lwm2m_obj_gateway.h"
50 #include "lwm2m_rw_link_format.h"
51 #include "lwm2m_rw_oma_tlv.h"
52 #include "lwm2m_rw_plain_text.h"
53 #include "lwm2m_rw_opaque.h"
54 #include "lwm2m_util.h"
55 #include "lwm2m_rd_client.h"
56 #if defined(CONFIG_LWM2M_RW_SENML_JSON_SUPPORT)
57 #include "lwm2m_rw_senml_json.h"
58 #endif
59 #ifdef CONFIG_LWM2M_RW_JSON_SUPPORT
60 #include "lwm2m_rw_json.h"
61 #endif
62 #ifdef CONFIG_LWM2M_RW_CBOR_SUPPORT
63 #include "lwm2m_rw_cbor.h"
64 #endif
65 #ifdef CONFIG_LWM2M_RW_SENML_CBOR_SUPPORT
66 #include "lwm2m_rw_senml_cbor.h"
67 #endif
68
69 /* TODO: figure out what's correct value */
70 #define TIMEOUT_BLOCKWISE_TRANSFER_MS (MSEC_PER_SEC * 30)
71
72 #define LWM2M_DP_CLIENT_URI "dp"
73
74 #define OUTPUT_CONTEXT_IN_USE_MARK (enum coap_block_size)(-1)
75
76 #ifdef CONFIG_ZTEST
77 #define STATIC
78 #else
79 #define STATIC static
80 #endif
81
82 /* Resources */
83
84 /* Shared set of in-flight LwM2M messages */
85 static struct lwm2m_message messages[CONFIG_LWM2M_ENGINE_MAX_MESSAGES];
86 static struct lwm2m_block_context block1_contexts[NUM_BLOCK1_CONTEXT];
87 static struct lwm2m_message *ongoing_block2_tx;
88
89 #if defined(CONFIG_LWM2M_COAP_BLOCK_TRANSFER)
90 /* we need 1 more buffer as the payload is encoded in that buffer first even if
91 * block transfer is not required for the message.
92 */
93 #define ENCODE_BUFFER_POOL_SIZE (CONFIG_LWM2M_NUM_OUTPUT_BLOCK_CONTEXT + 1)
94 /* buffers for serializing big message bodies */
95 K_MEM_SLAB_DEFINE_STATIC(body_encode_buffer_slab, CONFIG_LWM2M_COAP_ENCODE_BUFFER_SIZE,
96 ENCODE_BUFFER_POOL_SIZE, 4);
97 #endif
98
99 /* External resources */
100 sys_slist_t *lwm2m_engine_obj_list(void);
101
102 sys_slist_t *lwm2m_engine_obj_inst_list(void);
103
104 static int handle_request(struct coap_packet *request, struct lwm2m_message *msg);
105 #if defined(CONFIG_LWM2M_COAP_BLOCK_TRANSFER)
106 STATIC int build_msg_block_for_send(struct lwm2m_message *msg, uint16_t block_num,
107 enum coap_block_size block_size);
108 struct coap_block_context *lwm2m_output_block_context(void);
109 #endif
110
111 /* block-wise transfer functions */
112
lwm2m_default_block_size(void)113 enum coap_block_size lwm2m_default_block_size(void)
114 {
115 return coap_bytes_to_block_size(CONFIG_LWM2M_COAP_BLOCK_SIZE);
116 }
117
lwm2m_clear_block_contexts(void)118 void lwm2m_clear_block_contexts(void)
119 {
120 (void)memset(block1_contexts, 0, sizeof(block1_contexts));
121 }
122
init_block_ctx(const struct lwm2m_obj_path * path,struct lwm2m_block_context ** ctx)123 static int init_block_ctx(const struct lwm2m_obj_path *path, struct lwm2m_block_context **ctx)
124 {
125 int i;
126 int64_t timestamp;
127
128 if (!path) {
129 LOG_ERR("Null block ctx path");
130 return -EFAULT;
131 }
132
133 *ctx = NULL;
134 timestamp = k_uptime_get();
135 for (i = 0; i < NUM_BLOCK1_CONTEXT; i++) {
136 if (block1_contexts[i].path.level == 0U) {
137 *ctx = &block1_contexts[i];
138 break;
139 }
140
141 if (timestamp - block1_contexts[i].timestamp >
142 TIMEOUT_BLOCKWISE_TRANSFER_MS) {
143 *ctx = &block1_contexts[i];
144 /* TODO: notify application for block
145 * transfer timeout
146 */
147 break;
148 }
149 }
150
151 if (*ctx == NULL) {
152 LOG_ERR("Cannot find free block context");
153 return -ENOMEM;
154 }
155
156 memcpy(&(*ctx)->path, path, sizeof(struct lwm2m_obj_path));
157 coap_block_transfer_init(&(*ctx)->ctx, lwm2m_default_block_size(), 0);
158 (*ctx)->timestamp = timestamp;
159 (*ctx)->expected = 0;
160 (*ctx)->last_block = false;
161 memset(&(*ctx)->opaque, 0, sizeof((*ctx)->opaque));
162
163 return 0;
164 }
165
get_block_ctx(const struct lwm2m_obj_path * path,struct lwm2m_block_context ** ctx)166 static int get_block_ctx(const struct lwm2m_obj_path *path, struct lwm2m_block_context **ctx)
167 {
168 int i;
169
170 if (!path) {
171 LOG_ERR("Null block ctx path");
172 return -EFAULT;
173 }
174
175 *ctx = NULL;
176
177 for (i = 0; i < NUM_BLOCK1_CONTEXT; i++) {
178 if (memcmp(path, &block1_contexts[i].path,
179 sizeof(struct lwm2m_obj_path)) == 0) {
180 *ctx = &block1_contexts[i];
181 /* refresh timestamp */
182 (*ctx)->timestamp = k_uptime_get();
183 break;
184 }
185 }
186
187 if (*ctx == NULL) {
188 return -ENOENT;
189 }
190
191 return 0;
192 }
193
free_block_ctx(struct lwm2m_block_context * ctx)194 static void free_block_ctx(struct lwm2m_block_context *ctx)
195 {
196 if (ctx == NULL) {
197 return;
198 }
199
200 memset(&ctx->path, 0, sizeof(struct lwm2m_obj_path));
201 }
202
203 #if defined(CONFIG_LWM2M_COAP_BLOCK_TRANSFER)
request_output_block_ctx(struct coap_block_context ** ctx)204 STATIC int request_output_block_ctx(struct coap_block_context **ctx)
205 {
206 *ctx = NULL;
207 int i;
208
209 for (i = 0; i < NUM_OUTPUT_BLOCK_CONTEXT; i++) {
210 if (lwm2m_output_block_context()[i].block_size == 0) {
211 *ctx = &lwm2m_output_block_context()[i];
212 (*ctx)->block_size = OUTPUT_CONTEXT_IN_USE_MARK;
213 return 0;
214 }
215 }
216
217 return -ENOMEM;
218 }
219
release_output_block_ctx(struct coap_block_context ** ctx)220 STATIC void release_output_block_ctx(struct coap_block_context **ctx)
221 {
222 int i;
223
224 if (ctx == NULL) {
225 return;
226 }
227
228 for (i = 0; i < NUM_OUTPUT_BLOCK_CONTEXT; i++) {
229 if (&lwm2m_output_block_context()[i] == *ctx) {
230 lwm2m_output_block_context()[i].block_size = 0;
231 *ctx = NULL;
232 }
233 }
234 }
235
236
log_buffer_usage(void)237 static inline void log_buffer_usage(void)
238 {
239 #if defined(CONFIG_LWM2M_LOG_ENCODE_BUFFER_ALLOCATIONS)
240 LOG_PRINTK("body_encode_buffer_slab: free: %u, allocated: %u, max. allocated: %u\n",
241 k_mem_slab_num_free_get(&body_encode_buffer_slab),
242 k_mem_slab_num_used_get(&body_encode_buffer_slab),
243 k_mem_slab_max_used_get(&body_encode_buffer_slab));
244 #endif
245 }
246
request_body_encode_buffer(uint8_t ** buffer)247 static inline int request_body_encode_buffer(uint8_t **buffer)
248 {
249 int r;
250
251 r = k_mem_slab_alloc(&body_encode_buffer_slab, (void **)buffer, K_NO_WAIT);
252 log_buffer_usage();
253 return r;
254 }
255
release_body_encode_buffer(uint8_t ** buffer)256 static inline void release_body_encode_buffer(uint8_t **buffer)
257 {
258 if (buffer && *buffer) {
259 k_mem_slab_free(&body_encode_buffer_slab, (void *)*buffer);
260 log_buffer_usage();
261 }
262 }
263
build_msg_block_for_send(struct lwm2m_message * msg,uint16_t block_num,enum coap_block_size block_size)264 STATIC int build_msg_block_for_send(struct lwm2m_message *msg, uint16_t block_num,
265 enum coap_block_size block_size)
266 {
267 int ret;
268 uint16_t payload_size;
269 const uint16_t block_size_bytes = coap_block_size_to_bytes(block_size);
270 uint16_t complete_payload_len;
271 const uint8_t *complete_payload =
272 coap_packet_get_payload(&msg->body_encode_buffer, &complete_payload_len);
273 uint8_t token[COAP_TOKEN_MAX_LEN];
274 uint8_t tkl;
275
276 NET_ASSERT(msg->msg_data == msg->cpkt.data,
277 "big data buffer should not be in use for writing message");
278
279 if (block_num * block_size_bytes >= complete_payload_len) {
280 return -EINVAL;
281 }
282
283 if (block_num == 0) {
284 /* Copy the header only for first block.
285 * For following blocks a new one is generated.
286 */
287 ret = buf_append(CPKT_BUF_WRITE(&msg->cpkt), msg->body_encode_buffer.data,
288 msg->body_encode_buffer.hdr_len);
289 if (ret < 0) {
290 return ret;
291 }
292 msg->cpkt.hdr_len = msg->body_encode_buffer.hdr_len;
293 } else {
294 /* Keep user data between blocks */
295 void *user_data = msg->reply ? msg->reply->user_data : NULL;
296
297 /* reuse message for next block. Copy token from the new query to allow
298 * CoAP clients to use new token for every query of ongoing transaction
299 */
300 lwm2m_reset_message(msg, false);
301 if (msg->type == COAP_TYPE_ACK) {
302 msg->mid = coap_header_get_id(msg->in.in_cpkt);
303 tkl = coap_header_get_token(msg->in.in_cpkt, token);
304 } else {
305 msg->mid = coap_next_id();
306 tkl = LWM2M_MSG_TOKEN_GENERATE_NEW;
307 }
308 msg->token = token;
309 msg->tkl = tkl;
310 ret = lwm2m_init_message(msg);
311 if (ret < 0) {
312 lwm2m_reset_message(msg, true);
313 LOG_ERR("Unable to init lwm2m message for next block!");
314 return ret;
315 }
316 if (msg->reply) {
317 msg->reply->user_data = user_data;
318 }
319 }
320
321 /* copy the options */
322 ret = buf_append(CPKT_BUF_WRITE(&msg->cpkt),
323 msg->body_encode_buffer.data + msg->body_encode_buffer.hdr_len,
324 msg->body_encode_buffer.opt_len);
325 if (ret < 0) {
326 return ret;
327 }
328 msg->cpkt.opt_len = msg->body_encode_buffer.opt_len;
329
330 msg->cpkt.delta = msg->body_encode_buffer.delta;
331
332 if (block_num == 0) {
333 ret = request_output_block_ctx(&msg->out.block_ctx);
334 if (ret < 0) {
335 LOG_ERR("coap packet init error: no output block context available");
336 return ret;
337 }
338 ret = coap_block_transfer_init(msg->out.block_ctx, block_size,
339 complete_payload_len);
340 if (ret < 0) {
341 return ret;
342 }
343 if (msg->type == COAP_TYPE_ACK) {
344 ongoing_block2_tx = msg;
345 }
346 msg->block_send = true;
347 } else {
348 /* update block context */
349 msg->out.block_ctx->current = block_num * block_size_bytes;
350 msg->out.block_ctx->block_size = block_size;
351 }
352
353 ret = coap_append_descriptive_block_option(&msg->cpkt, msg->out.block_ctx);
354 if (ret < 0) {
355 return ret;
356 }
357
358 ret = coap_packet_append_payload_marker(&msg->cpkt);
359 if (ret < 0) {
360 return ret;
361 }
362
363 payload_size = MIN(complete_payload_len - block_num * block_size_bytes, block_size_bytes);
364 ret = buf_append(CPKT_BUF_WRITE(&msg->cpkt),
365 complete_payload + (block_num * block_size_bytes), payload_size);
366 if (ret < 0) {
367 return ret;
368 }
369
370 return 0;
371 }
372
prepare_msg_for_send(struct lwm2m_message * msg)373 STATIC int prepare_msg_for_send(struct lwm2m_message *msg)
374 {
375 int ret;
376 uint16_t len;
377 const uint8_t *payload;
378
379 /* save the big buffer for later use (splitting blocks) */
380 msg->body_encode_buffer = msg->cpkt;
381
382 /* set the default (small) buffer for sending blocks */
383 msg->cpkt.data = msg->msg_data;
384 msg->cpkt.offset = 0;
385 msg->cpkt.max_len = MAX_PACKET_SIZE;
386
387 payload = coap_packet_get_payload(&msg->body_encode_buffer, &len);
388 if (len <= CONFIG_LWM2M_COAP_MAX_MSG_SIZE) {
389
390 /* copy the packet */
391 ret = buf_append(CPKT_BUF_WRITE(&msg->cpkt), msg->body_encode_buffer.data,
392 msg->body_encode_buffer.offset);
393 if (ret != 0) {
394 return ret;
395 }
396
397 msg->cpkt.hdr_len = msg->body_encode_buffer.hdr_len;
398 msg->cpkt.opt_len = msg->body_encode_buffer.opt_len;
399
400 /* clear big buffer */
401 release_body_encode_buffer(&msg->body_encode_buffer.data);
402 msg->body_encode_buffer.data = NULL;
403
404 NET_ASSERT(msg->out.block_ctx == NULL, "Expecting to have no context to release");
405 } else {
406 /* Before splitting the content, append Etag option to protect the integrity of
407 * the payload.
408 */
409 if (IS_ENABLED(CONFIG_SYS_HASH_FUNC32)) {
410 uint32_t hash = sys_hash32(payload, len);
411
412 coap_packet_append_option(&msg->body_encode_buffer, COAP_OPTION_ETAG,
413 (const uint8_t *)&hash, sizeof(hash));
414 }
415
416 ret = build_msg_block_for_send(msg, 0, lwm2m_default_block_size());
417 if (ret != 0) {
418 return ret;
419 }
420 }
421
422 return 0;
423 }
424
425 #endif
426
lwm2m_engine_context_close(struct lwm2m_ctx * client_ctx)427 void lwm2m_engine_context_close(struct lwm2m_ctx *client_ctx)
428 {
429 struct lwm2m_message *msg;
430 sys_snode_t *obs_node;
431 struct observe_node *obs;
432 size_t i;
433
434 /* Remove observes for this context */
435 while (!sys_slist_is_empty(&client_ctx->observer)) {
436 obs_node = sys_slist_get_not_empty(&client_ctx->observer);
437 obs = SYS_SLIST_CONTAINER(obs_node, obs, node);
438 remove_observer_from_list(client_ctx, NULL, obs);
439 }
440
441 for (i = 0, msg = messages; i < ARRAY_SIZE(messages); i++, msg++) {
442 if (msg->ctx == client_ctx) {
443 lwm2m_reset_message(msg, true);
444 }
445 }
446
447 coap_pendings_clear(client_ctx->pendings, ARRAY_SIZE(client_ctx->pendings));
448 coap_replies_clear(client_ctx->replies, ARRAY_SIZE(client_ctx->replies));
449
450
451 client_ctx->connection_suspended = false;
452 #if defined(CONFIG_LWM2M_QUEUE_MODE_ENABLED)
453 client_ctx->buffer_client_messages = true;
454 #endif
455 }
456
lwm2m_engine_context_init(struct lwm2m_ctx * client_ctx)457 void lwm2m_engine_context_init(struct lwm2m_ctx *client_ctx)
458 {
459 sys_slist_init(&client_ctx->pending_sends);
460 sys_slist_init(&client_ctx->observer);
461 client_ctx->connection_suspended = false;
462 #if defined(CONFIG_LWM2M_QUEUE_MODE_ENABLED)
463 client_ctx->buffer_client_messages = true;
464 sys_slist_init(&client_ctx->queued_messages);
465 #endif
466 }
467 /* utility functions */
468
coap_options_to_path(struct coap_option * opt,int options_count,struct lwm2m_obj_path * path)469 int coap_options_to_path(struct coap_option *opt, int options_count,
470 struct lwm2m_obj_path *path)
471 {
472 uint16_t len,
473 *id[4] = {&path->obj_id, &path->obj_inst_id, &path->res_id, &path->res_inst_id};
474
475 path->level = options_count;
476
477 for (int i = 0; i < options_count; i++) {
478 *id[i] = lwm2m_atou16(opt[i].value, opt[i].len, &len);
479 if (len == 0U || opt[i].len != len) {
480 path->level = i;
481 break;
482 }
483 }
484
485 return options_count == path->level ? 0 : -EINVAL;
486 }
487
find_msg(struct coap_pending * pending,struct coap_reply * reply)488 struct lwm2m_message *find_msg(struct coap_pending *pending, struct coap_reply *reply)
489 {
490 size_t i;
491 struct lwm2m_message *msg;
492
493 if (!pending && !reply) {
494 return NULL;
495 }
496
497 msg = lwm2m_get_ongoing_rd_msg();
498 if (msg) {
499 if (pending != NULL && msg->pending == pending) {
500 return msg;
501 }
502
503 if (reply != NULL && msg->reply == reply) {
504 return msg;
505 }
506 }
507
508 for (i = 0; i < CONFIG_LWM2M_ENGINE_MAX_MESSAGES; i++) {
509 if (pending != NULL && messages[i].ctx && messages[i].pending == pending) {
510 return &messages[i];
511 }
512
513 if (reply != NULL && messages[i].ctx && messages[i].reply == reply) {
514 return &messages[i];
515 }
516 }
517
518 return NULL;
519 }
520
lwm2m_get_message(struct lwm2m_ctx * client_ctx)521 struct lwm2m_message *lwm2m_get_message(struct lwm2m_ctx *client_ctx)
522 {
523 size_t i;
524
525 for (i = 0; i < CONFIG_LWM2M_ENGINE_MAX_MESSAGES; i++) {
526 if (!messages[i].ctx) {
527 messages[i].ctx = client_ctx;
528 return &messages[i];
529 }
530 }
531
532 return NULL;
533 }
534
lm2m_message_clear_allocations(struct lwm2m_message * msg)535 void lm2m_message_clear_allocations(struct lwm2m_message *msg)
536 {
537 if (msg->pending) {
538 coap_pending_clear(msg->pending);
539 msg->pending = NULL;
540 }
541
542 if (msg->reply) {
543 /* make sure we want to clear the reply */
544 coap_reply_clear(msg->reply);
545 msg->reply = NULL;
546 }
547 }
548
lwm2m_reset_message(struct lwm2m_message * msg,bool release)549 void lwm2m_reset_message(struct lwm2m_message *msg, bool release)
550 {
551 if (!msg) {
552 return;
553 }
554
555 lm2m_message_clear_allocations(msg);
556
557 if (msg->ctx) {
558 sys_slist_find_and_remove(&msg->ctx->pending_sends, &msg->node);
559 #if defined(CONFIG_LWM2M_QUEUE_MODE_ENABLED)
560 sys_slist_find_and_remove(&msg->ctx->queued_messages, &msg->node);
561 #endif
562 }
563
564 if (release) {
565 #if defined(CONFIG_LWM2M_COAP_BLOCK_TRANSFER)
566 release_output_block_ctx(&msg->out.block_ctx);
567 release_body_encode_buffer(&msg->body_encode_buffer.data);
568 #endif
569 (void)memset(msg, 0, sizeof(*msg));
570 } else {
571 msg->message_timeout_cb = NULL;
572 (void)memset(&msg->cpkt, 0, sizeof(msg->cpkt));
573 #if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT)
574 msg->cache_info = NULL;
575 #endif
576 }
577 }
578
lwm2m_init_message(struct lwm2m_message * msg)579 int lwm2m_init_message(struct lwm2m_message *msg)
580 {
581 uint8_t tokenlen = 0U;
582 uint8_t *token = NULL;
583 uint8_t *body_data;
584 uint16_t body_data_max_len;
585 int r = 0;
586
587 if (!msg || !msg->ctx) {
588 LOG_ERR("LwM2M message is invalid.");
589 return -EINVAL;
590 }
591
592 if (msg->tkl == LWM2M_MSG_TOKEN_GENERATE_NEW) {
593 tokenlen = 8U;
594 token = coap_next_token();
595 } else if (msg->token && msg->tkl != 0) {
596 tokenlen = msg->tkl;
597 token = msg->token;
598 }
599
600 lm2m_message_clear_allocations(msg);
601 #if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT)
602 msg->cache_info = NULL;
603 #endif
604 #if defined(CONFIG_LWM2M_COAP_BLOCK_TRANSFER)
605 if (msg->body_encode_buffer.data == NULL) {
606 /* Get new big buffer for serializing the message */
607 r = request_body_encode_buffer(&body_data);
608 if (r < 0) {
609 LOG_ERR("coap packet init error: no msg buffer available");
610 goto cleanup;
611 }
612 /* in case of failure the buffer is released with this pointer */
613 msg->body_encode_buffer.data = body_data;
614 body_data_max_len = CONFIG_LWM2M_COAP_ENCODE_BUFFER_SIZE;
615 } else {
616 /* We have already a big buffer. The message is reused for each block. */
617 body_data = msg->msg_data;
618 body_data_max_len = sizeof(msg->msg_data);
619 }
620 #else
621 body_data = msg->msg_data;
622 body_data_max_len = sizeof(msg->msg_data);
623 #endif
624 r = coap_packet_init(&msg->cpkt, body_data, body_data_max_len, COAP_VERSION_1, msg->type,
625 tokenlen, token, msg->code, msg->mid);
626 if (r < 0) {
627 LOG_ERR("coap packet init error (err:%d)", r);
628 goto cleanup;
629 }
630
631 /* only TYPE_CON messages need pending tracking / reply handling */
632 if (msg->type != COAP_TYPE_CON) {
633 return 0;
634 }
635
636 msg->pending = coap_pending_next_unused(msg->ctx->pendings, ARRAY_SIZE(msg->ctx->pendings));
637 if (!msg->pending) {
638 LOG_ERR("Unable to find a free pending to track "
639 "retransmissions.");
640 r = -ENOMEM;
641 goto cleanup;
642 }
643
644 r = coap_pending_init(msg->pending, &msg->cpkt, &msg->ctx->remote_addr, NULL);
645 if (r < 0) {
646 LOG_ERR("Unable to initialize a pending "
647 "retransmission (err:%d).",
648 r);
649 goto cleanup;
650 }
651
652 if (msg->reply_cb) {
653 msg->reply =
654 coap_reply_next_unused(msg->ctx->replies, ARRAY_SIZE(msg->ctx->replies));
655 if (!msg->reply) {
656 LOG_ERR("No resources for waiting for replies.");
657 r = -ENOMEM;
658 goto cleanup;
659 }
660
661 coap_reply_clear(msg->reply);
662 coap_reply_init(msg->reply, &msg->cpkt);
663 msg->reply->reply = msg->reply_cb;
664 }
665
666 return 0;
667
668 cleanup:
669 lwm2m_reset_message(msg, true);
670
671 return r;
672 }
673
lwm2m_send_message_async(struct lwm2m_message * msg)674 int lwm2m_send_message_async(struct lwm2m_message *msg)
675 {
676 #if defined(CONFIG_LWM2M_COAP_BLOCK_TRANSFER)
677
678 /* check if body encode buffer is in use => packet is not yet prepared for send */
679 if (msg->body_encode_buffer.data == msg->cpkt.data) {
680 int ret = prepare_msg_for_send(msg);
681
682 if (ret) {
683 lwm2m_reset_message(msg, true);
684 return ret;
685 }
686 }
687 #endif
688 if (IS_ENABLED(CONFIG_LWM2M_QUEUE_MODE_ENABLED)) {
689 int ret = lwm2m_rd_client_connection_resume(msg->ctx);
690
691 if (ret && ret != -EPERM) {
692 lwm2m_reset_message(msg, true);
693 return ret;
694 }
695 }
696 sys_slist_append(&msg->ctx->pending_sends, &msg->node);
697
698 if (IS_ENABLED(CONFIG_LWM2M_QUEUE_MODE_ENABLED)) {
699 engine_update_tx_time();
700 }
701 lwm2m_engine_wake_up();
702 return 0;
703 }
704
lwm2m_information_interface_send(struct lwm2m_message * msg)705 int lwm2m_information_interface_send(struct lwm2m_message *msg)
706 {
707 #if defined(CONFIG_LWM2M_QUEUE_MODE_ENABLED)
708 int ret;
709
710 ret = lwm2m_rd_client_connection_resume(msg->ctx);
711 if (ret) {
712 lwm2m_reset_message(msg, true);
713 return ret;
714 }
715
716 if (IS_ENABLED(CONFIG_LWM2M_QUEUE_MODE_NO_MSG_BUFFERING)) {
717 sys_slist_append(&msg->ctx->pending_sends, &msg->node);
718 lwm2m_engine_wake_up();
719 lwm2m_engine_connection_resume(msg->ctx);
720 return 0;
721 }
722
723 if (msg->ctx->buffer_client_messages) {
724 sys_slist_append(&msg->ctx->queued_messages, &msg->node);
725 lwm2m_engine_wake_up();
726 return 0;
727 }
728 #endif
729
730 return lwm2m_send_message_async(msg);
731 }
732
lwm2m_send_empty_ack(struct lwm2m_ctx * client_ctx,uint16_t mid)733 int lwm2m_send_empty_ack(struct lwm2m_ctx *client_ctx, uint16_t mid)
734 {
735 struct lwm2m_message *msg;
736 int ret;
737
738 msg = lwm2m_get_message(client_ctx);
739 if (!msg) {
740 LOG_ERR("Unable to get a lwm2m message!");
741 return -ENOMEM;
742 }
743
744 msg->type = COAP_TYPE_ACK;
745 msg->code = COAP_CODE_EMPTY;
746 msg->mid = mid;
747
748 ret = lwm2m_init_message(msg);
749 if (ret) {
750 goto cleanup;
751 }
752
753 ret = zsock_send(client_ctx->sock_fd, msg->cpkt.data, msg->cpkt.offset, 0);
754
755 if (ret < 0) {
756 LOG_ERR("Failed to send packet, err %d", errno);
757 ret = -errno;
758 }
759
760 cleanup:
761 lwm2m_reset_message(msg, true);
762 return ret;
763 }
764
lwm2m_acknowledge(struct lwm2m_ctx * client_ctx)765 void lwm2m_acknowledge(struct lwm2m_ctx *client_ctx)
766 {
767 struct lwm2m_message *request;
768
769 if (client_ctx == NULL || client_ctx->processed_req == NULL) {
770 return;
771 }
772
773 request = (struct lwm2m_message *)client_ctx->processed_req;
774
775 if (request->acknowledged) {
776 return;
777 }
778
779 if (lwm2m_send_empty_ack(client_ctx, request->mid) < 0) {
780 return;
781 }
782
783 request->acknowledged = true;
784 }
785
lwm2m_register_payload_handler(struct lwm2m_message * msg)786 int lwm2m_register_payload_handler(struct lwm2m_message *msg)
787 {
788 struct lwm2m_engine_obj *obj;
789 struct lwm2m_engine_obj_inst *obj_inst;
790 int ret;
791 sys_slist_t *engine_obj_list = lwm2m_engine_obj_list();
792 sys_slist_t *engine_obj_inst_list = lwm2m_engine_obj_inst_list();
793
794 ret = engine_put_begin(&msg->out, NULL);
795 if (ret < 0) {
796 return ret;
797 }
798
799 SYS_SLIST_FOR_EACH_CONTAINER(engine_obj_list, obj, node) {
800 /* Security obj MUST NOT be part of registration message */
801 if (obj->obj_id == LWM2M_OBJECT_SECURITY_ID) {
802 continue;
803 }
804
805 /* Only report <OBJ_ID> when no instance available or it's
806 * needed to report object version.
807 */
808 if (obj->instance_count == 0U || lwm2m_engine_shall_report_obj_version(obj)) {
809 ret = engine_put_corelink(&msg->out, &LWM2M_OBJ(obj->obj_id));
810 if (ret < 0) {
811 return ret;
812 }
813
814 if (obj->instance_count == 0U) {
815 continue;
816 }
817 }
818
819 SYS_SLIST_FOR_EACH_CONTAINER(engine_obj_inst_list, obj_inst, node) {
820 if (obj_inst->obj->obj_id == obj->obj_id) {
821 ret = engine_put_corelink(
822 &msg->out,
823 &LWM2M_OBJ(obj_inst->obj->obj_id, obj_inst->obj_inst_id));
824 if (ret < 0) {
825 return ret;
826 }
827 }
828 }
829 }
830
831 return 0;
832 }
833
select_writer(struct lwm2m_output_context * out,uint16_t accept)834 static int select_writer(struct lwm2m_output_context *out, uint16_t accept)
835 {
836 switch (accept) {
837
838 case LWM2M_FORMAT_APP_LINK_FORMAT:
839 out->writer = &link_format_writer;
840 break;
841
842 case LWM2M_FORMAT_APP_OCTET_STREAM:
843 out->writer = &opaque_writer;
844 break;
845
846 case LWM2M_FORMAT_PLAIN_TEXT:
847 case LWM2M_FORMAT_OMA_PLAIN_TEXT:
848 out->writer = &plain_text_writer;
849 break;
850
851 #ifdef CONFIG_LWM2M_RW_OMA_TLV_SUPPORT
852 case LWM2M_FORMAT_OMA_TLV:
853 case LWM2M_FORMAT_OMA_OLD_TLV:
854 out->writer = &oma_tlv_writer;
855 break;
856 #endif
857
858 #ifdef CONFIG_LWM2M_RW_JSON_SUPPORT
859 case LWM2M_FORMAT_OMA_JSON:
860 case LWM2M_FORMAT_OMA_OLD_JSON:
861 out->writer = &json_writer;
862 break;
863 #endif
864
865 #if defined(CONFIG_LWM2M_RW_SENML_JSON_SUPPORT)
866 case LWM2M_FORMAT_APP_SEML_JSON:
867 out->writer = &senml_json_writer;
868 break;
869 #endif
870
871 #ifdef CONFIG_LWM2M_RW_CBOR_SUPPORT
872 case LWM2M_FORMAT_APP_CBOR:
873 out->writer = &cbor_writer;
874 break;
875 #endif
876
877 #ifdef CONFIG_LWM2M_RW_SENML_CBOR_SUPPORT
878 case LWM2M_FORMAT_APP_SENML_CBOR:
879 out->writer = &senml_cbor_writer;
880 break;
881 #endif
882
883 default:
884 LOG_WRN("Unknown content type %u", accept);
885 return -ECANCELED;
886 }
887
888 return 0;
889 }
890
select_reader(struct lwm2m_input_context * in,uint16_t format)891 static int select_reader(struct lwm2m_input_context *in, uint16_t format)
892 {
893 switch (format) {
894
895 case LWM2M_FORMAT_APP_OCTET_STREAM:
896 in->reader = &opaque_reader;
897 break;
898
899 case LWM2M_FORMAT_PLAIN_TEXT:
900 case LWM2M_FORMAT_OMA_PLAIN_TEXT:
901 in->reader = &plain_text_reader;
902 break;
903
904 #ifdef CONFIG_LWM2M_RW_OMA_TLV_SUPPORT
905 case LWM2M_FORMAT_OMA_TLV:
906 case LWM2M_FORMAT_OMA_OLD_TLV:
907 in->reader = &oma_tlv_reader;
908 break;
909 #endif
910
911 #ifdef CONFIG_LWM2M_RW_JSON_SUPPORT
912 case LWM2M_FORMAT_OMA_JSON:
913 case LWM2M_FORMAT_OMA_OLD_JSON:
914 in->reader = &json_reader;
915 break;
916 #endif
917
918 #if defined(CONFIG_LWM2M_RW_SENML_JSON_SUPPORT)
919 case LWM2M_FORMAT_APP_SEML_JSON:
920 in->reader = &senml_json_reader;
921 break;
922 #endif
923
924 #ifdef CONFIG_LWM2M_RW_CBOR_SUPPORT
925 case LWM2M_FORMAT_APP_CBOR:
926 in->reader = &cbor_reader;
927 break;
928 #endif
929
930 #ifdef CONFIG_LWM2M_RW_SENML_CBOR_SUPPORT
931 case LWM2M_FORMAT_APP_SENML_CBOR:
932 in->reader = &senml_cbor_reader;
933 break;
934 #endif
935 default:
936 LOG_WRN("Unknown content type %u", format);
937 return -ENOMSG;
938 }
939
940 return 0;
941 }
942
943 /* generic data handlers */
lwm2m_write_handler_opaque(struct lwm2m_engine_obj_inst * obj_inst,struct lwm2m_engine_res * res,struct lwm2m_engine_res_inst * res_inst,struct lwm2m_message * msg,void * data_ptr,size_t data_len)944 static int lwm2m_write_handler_opaque(struct lwm2m_engine_obj_inst *obj_inst,
945 struct lwm2m_engine_res *res,
946 struct lwm2m_engine_res_inst *res_inst,
947 struct lwm2m_message *msg, void *data_ptr, size_t data_len)
948 {
949 int len = 1;
950 bool last_pkt_block = false;
951 int ret = 0;
952 bool last_block = true;
953 struct lwm2m_opaque_context opaque_ctx = {0};
954 void *write_buf;
955 size_t write_buf_len;
956
957 if (msg->in.block_ctx != NULL) {
958 last_block = msg->in.block_ctx->last_block;
959
960 /* Restore the opaque context from the block context, if used. */
961 opaque_ctx = msg->in.block_ctx->opaque;
962 }
963
964 #if CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0
965 /* In case validation callback is present, write data to the temporary
966 * buffer first, for validation. Otherwise, write to the data buffer
967 * directly.
968 */
969 if (res->validate_cb) {
970 write_buf = msg->ctx->validate_buf;
971 write_buf_len = sizeof(msg->ctx->validate_buf);
972 } else
973 #endif /* CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0 */
974 {
975 write_buf = data_ptr;
976 write_buf_len = data_len;
977 }
978
979 while (!last_pkt_block && len > 0) {
980 len = engine_get_opaque(&msg->in, write_buf, MIN(data_len, write_buf_len),
981 &opaque_ctx, &last_pkt_block);
982 if (len <= 0) {
983 return len;
984 }
985
986 #if CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0
987 if (res->validate_cb) {
988 ret = res->validate_cb(obj_inst->obj_inst_id, res->res_id,
989 res_inst->res_inst_id, write_buf, len,
990 last_pkt_block && last_block, opaque_ctx.len,
991 msg->in.block_ctx->ctx.current);
992 if (ret < 0) {
993 /* -EEXIST will generate Bad Request LWM2M response. */
994 return -EEXIST;
995 }
996
997 memcpy(data_ptr, write_buf, len);
998 }
999 #endif /* CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0 */
1000
1001 if (res->post_write_cb) {
1002 ret = res->post_write_cb(
1003 obj_inst->obj_inst_id, res->res_id, res_inst->res_inst_id, data_ptr,
1004 len, last_pkt_block && last_block, opaque_ctx.len,
1005 (msg->in.block_ctx ? msg->in.block_ctx->ctx.current : 0));
1006 if (ret < 0) {
1007 return ret;
1008 }
1009 }
1010 if (msg->in.block_ctx && !last_pkt_block) {
1011 msg->in.block_ctx->ctx.current += len;
1012 }
1013 }
1014
1015 if (msg->in.block_ctx != NULL) {
1016 msg->in.block_ctx->opaque = opaque_ctx;
1017 }
1018
1019 return opaque_ctx.len;
1020 }
1021 /* This function is exposed for the content format writers */
lwm2m_write_handler(struct lwm2m_engine_obj_inst * obj_inst,struct lwm2m_engine_res * res,struct lwm2m_engine_res_inst * res_inst,struct lwm2m_engine_obj_field * obj_field,struct lwm2m_message * msg)1022 int lwm2m_write_handler(struct lwm2m_engine_obj_inst *obj_inst, struct lwm2m_engine_res *res,
1023 struct lwm2m_engine_res_inst *res_inst,
1024 struct lwm2m_engine_obj_field *obj_field, struct lwm2m_message *msg)
1025 {
1026 void *data_ptr = NULL;
1027 size_t data_len = 0;
1028 size_t len = 0;
1029 size_t total_size = 0;
1030 int64_t temp64 = 0;
1031 int32_t temp32 = 0;
1032 time_t temp_time = 0;
1033 int ret = 0;
1034 bool last_block = true;
1035 void *write_buf;
1036 size_t write_buf_len;
1037 size_t offset = 0;
1038
1039 if (!obj_inst || !res || !res_inst || !obj_field || !msg) {
1040 return -EINVAL;
1041 }
1042
1043 if (LWM2M_HAS_RES_FLAG(res_inst, LWM2M_RES_DATA_FLAG_RO)) {
1044 return -EACCES;
1045 }
1046
1047 /* setup initial data elements */
1048 data_ptr = res_inst->data_ptr;
1049 data_len = res_inst->max_data_len;
1050
1051 /* allow user to override data elements via callback */
1052 if (res->pre_write_cb) {
1053 data_ptr = res->pre_write_cb(obj_inst->obj_inst_id, res->res_id,
1054 res_inst->res_inst_id, &data_len);
1055 }
1056
1057 if (msg->in.block_ctx != NULL) {
1058 /* Get block_ctx for total_size (might be zero) */
1059 total_size = msg->in.block_ctx->ctx.total_size;
1060 offset = msg->in.block_ctx->ctx.current;
1061
1062 LOG_DBG("BLOCK1: total:%zu current:%zu"
1063 " last:%u",
1064 msg->in.block_ctx->ctx.total_size, msg->in.block_ctx->ctx.current,
1065 msg->in.block_ctx->last_block);
1066 }
1067
1068 /* Only when post_write callback is set, we allow larger content than our
1069 * buffer sizes. The post-write callback handles assembling of the data
1070 */
1071 if (!res->post_write_cb) {
1072 if ((offset > 0 && offset >= data_len) || total_size > data_len) {
1073 return -ENOMEM;
1074 }
1075 data_len -= offset;
1076 data_ptr = (uint8_t *)data_ptr + offset;
1077 }
1078
1079 #if CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0
1080 /* In case validation callback is present, write data to the temporary
1081 * buffer first, for validation. Otherwise, write to the data buffer
1082 * directly.
1083 */
1084 if (res->validate_cb) {
1085 write_buf = msg->ctx->validate_buf;
1086 write_buf_len = sizeof(msg->ctx->validate_buf);
1087 } else
1088 #endif /* CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0 */
1089 {
1090 write_buf = data_ptr;
1091 write_buf_len = data_len;
1092 }
1093
1094 if (data_ptr && data_len > 0) {
1095 switch (obj_field->data_type) {
1096
1097 case LWM2M_RES_TYPE_OPAQUE:
1098 ret = lwm2m_write_handler_opaque(obj_inst, res, res_inst, msg, data_ptr,
1099 data_len);
1100 len = ret;
1101 break;
1102
1103 case LWM2M_RES_TYPE_STRING:
1104 ret = engine_get_string(&msg->in, write_buf, write_buf_len);
1105 if (ret < 0) {
1106 break;
1107 }
1108
1109 len = strlen((char *)write_buf) + 1;
1110 break;
1111
1112 case LWM2M_RES_TYPE_TIME:
1113 ret = engine_get_time(&msg->in, &temp_time);
1114 if (ret < 0) {
1115 break;
1116 }
1117
1118 if (write_buf_len == sizeof(time_t)) {
1119 *(time_t *)write_buf = temp_time;
1120 len = sizeof(time_t);
1121 } else if (write_buf_len == sizeof(uint32_t)) {
1122 *(uint32_t *)write_buf = (uint32_t)temp_time;
1123 len = sizeof(uint32_t);
1124 } else {
1125 LOG_ERR("Time resource buf len not supported %zu", write_buf_len);
1126 ret = -EINVAL;
1127 }
1128
1129 break;
1130
1131 case LWM2M_RES_TYPE_U32:
1132 ret = engine_get_s64(&msg->in, &temp64);
1133 if (ret < 0) {
1134 break;
1135 }
1136
1137 *(uint32_t *)write_buf = temp64;
1138 len = 4;
1139 break;
1140
1141 case LWM2M_RES_TYPE_U16:
1142 ret = engine_get_s32(&msg->in, &temp32);
1143 if (ret < 0) {
1144 break;
1145 }
1146
1147 *(uint16_t *)write_buf = temp32;
1148 len = 2;
1149 break;
1150
1151 case LWM2M_RES_TYPE_U8:
1152 ret = engine_get_s32(&msg->in, &temp32);
1153 if (ret < 0) {
1154 break;
1155 }
1156
1157 *(uint8_t *)write_buf = temp32;
1158 len = 1;
1159 break;
1160
1161 case LWM2M_RES_TYPE_S64:
1162 ret = engine_get_s64(&msg->in, (int64_t *)write_buf);
1163 len = 8;
1164 break;
1165
1166 case LWM2M_RES_TYPE_S32:
1167 ret = engine_get_s32(&msg->in, (int32_t *)write_buf);
1168 len = 4;
1169 break;
1170
1171 case LWM2M_RES_TYPE_S16:
1172 ret = engine_get_s32(&msg->in, &temp32);
1173 if (ret < 0) {
1174 break;
1175 }
1176
1177 *(int16_t *)write_buf = temp32;
1178 len = 2;
1179 break;
1180
1181 case LWM2M_RES_TYPE_S8:
1182 ret = engine_get_s32(&msg->in, &temp32);
1183 if (ret < 0) {
1184 break;
1185 }
1186
1187 *(int8_t *)write_buf = temp32;
1188 len = 1;
1189 break;
1190
1191 case LWM2M_RES_TYPE_BOOL:
1192 ret = engine_get_bool(&msg->in, (bool *)write_buf);
1193 len = 1;
1194 break;
1195
1196 case LWM2M_RES_TYPE_FLOAT:
1197 ret = engine_get_float(&msg->in, (double *)write_buf);
1198 len = sizeof(double);
1199 break;
1200
1201 case LWM2M_RES_TYPE_OBJLNK:
1202 ret = engine_get_objlnk(&msg->in, (struct lwm2m_objlnk *)write_buf);
1203 len = sizeof(struct lwm2m_objlnk);
1204 break;
1205
1206 default:
1207 LOG_ERR("unknown obj data_type %d", obj_field->data_type);
1208 return -EINVAL;
1209 }
1210
1211 if (ret < 0) {
1212 return ret;
1213 }
1214 } else {
1215 return -ENOENT;
1216 }
1217
1218 if (obj_field->data_type != LWM2M_RES_TYPE_OPAQUE) {
1219
1220 #if CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0
1221 if (res->validate_cb) {
1222 ret = res->validate_cb(obj_inst->obj_inst_id, res->res_id,
1223 res_inst->res_inst_id, write_buf, len, last_block,
1224 total_size, offset);
1225 if (ret < 0) {
1226 /* -EEXIST will generate Bad Request LWM2M response. */
1227 return -EEXIST;
1228 }
1229
1230 if (len > data_len) {
1231 LOG_ERR("Received data won't fit into provided "
1232 "buffer");
1233 return -ENOMEM;
1234 }
1235
1236 if (obj_field->data_type == LWM2M_RES_TYPE_STRING) {
1237 strncpy(data_ptr, write_buf, data_len);
1238 } else {
1239 memcpy(data_ptr, write_buf, len);
1240 }
1241 }
1242 #endif /* CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0 */
1243
1244 if (res->post_write_cb) {
1245 ret = res->post_write_cb(obj_inst->obj_inst_id, res->res_id,
1246 res_inst->res_inst_id, data_ptr, len, last_block,
1247 total_size, offset);
1248 }
1249 }
1250
1251 res_inst->data_len = len;
1252
1253 if (LWM2M_HAS_PERM(obj_field, LWM2M_PERM_R)) {
1254 lwm2m_notify_observer_path(&msg->path);
1255 }
1256
1257 return ret;
1258 }
1259
lwm2m_read_resource_data(struct lwm2m_message * msg,void * data_ptr,size_t data_len,uint8_t data_type)1260 static int lwm2m_read_resource_data(struct lwm2m_message *msg, void *data_ptr, size_t data_len,
1261 uint8_t data_type)
1262 {
1263 int ret;
1264
1265 switch (data_type) {
1266
1267 case LWM2M_RES_TYPE_OPAQUE:
1268 ret = engine_put_opaque(&msg->out, &msg->path, (uint8_t *)data_ptr, data_len);
1269 break;
1270
1271 case LWM2M_RES_TYPE_STRING:
1272 if (data_len) {
1273 data_len -= 1; /* Remove the '\0' */
1274 }
1275 ret = engine_put_string(&msg->out, &msg->path, (uint8_t *)data_ptr, data_len);
1276 break;
1277
1278 case LWM2M_RES_TYPE_U32:
1279 ret = engine_put_s64(&msg->out, &msg->path, (int64_t) *(uint32_t *)data_ptr);
1280 break;
1281
1282 case LWM2M_RES_TYPE_U16:
1283 ret = engine_put_s32(&msg->out, &msg->path, (int32_t) *(uint16_t *)data_ptr);
1284 break;
1285
1286 case LWM2M_RES_TYPE_U8:
1287 ret = engine_put_s16(&msg->out, &msg->path, (int16_t) *(uint8_t *)data_ptr);
1288 break;
1289
1290 case LWM2M_RES_TYPE_S64:
1291 ret = engine_put_s64(&msg->out, &msg->path, *(int64_t *)data_ptr);
1292 break;
1293
1294 case LWM2M_RES_TYPE_S32:
1295 ret = engine_put_s32(&msg->out, &msg->path, *(int32_t *)data_ptr);
1296 break;
1297
1298 case LWM2M_RES_TYPE_S16:
1299 ret = engine_put_s16(&msg->out, &msg->path, *(int16_t *)data_ptr);
1300 break;
1301
1302 case LWM2M_RES_TYPE_S8:
1303 ret = engine_put_s8(&msg->out, &msg->path, *(int8_t *)data_ptr);
1304 break;
1305
1306 case LWM2M_RES_TYPE_TIME:
1307 if (data_len == sizeof(time_t)) {
1308 ret = engine_put_time(&msg->out, &msg->path, *(time_t *)data_ptr);
1309 } else if (data_len == sizeof(uint32_t)) {
1310 ret = engine_put_time(&msg->out, &msg->path,
1311 (time_t) *((uint32_t *)data_ptr));
1312 } else {
1313 LOG_ERR("Resource time length not supported %zu", data_len);
1314 ret = -EINVAL;
1315 }
1316
1317 break;
1318
1319 case LWM2M_RES_TYPE_BOOL:
1320 ret = engine_put_bool(&msg->out, &msg->path, *(bool *)data_ptr);
1321 break;
1322
1323 case LWM2M_RES_TYPE_FLOAT:
1324 ret = engine_put_float(&msg->out, &msg->path, (double *)data_ptr);
1325 break;
1326
1327 case LWM2M_RES_TYPE_OBJLNK:
1328 ret = engine_put_objlnk(&msg->out, &msg->path, (struct lwm2m_objlnk *)data_ptr);
1329 break;
1330
1331 default:
1332 LOG_ERR("unknown obj data_type %d", data_type);
1333 ret = -EINVAL;
1334 }
1335
1336 return ret;
1337 }
1338
lwm2m_read_cached_data(struct lwm2m_message * msg,struct lwm2m_time_series_resource * cached_data,uint8_t data_type)1339 static int lwm2m_read_cached_data(struct lwm2m_message *msg,
1340 struct lwm2m_time_series_resource *cached_data, uint8_t data_type)
1341 {
1342 #if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT)
1343 int ret;
1344 struct lwm2m_time_series_elem buf;
1345 struct lwm2m_cache_read_entry *read_info;
1346 size_t length = lwm2m_cache_size(cached_data);
1347
1348 LOG_DBG("Read cached data size %u", length);
1349
1350 if (msg->cache_info) {
1351 read_info = &msg->cache_info->read_info[msg->cache_info->entry_size];
1352 /* Store original timeseries ring buffer get states for failure handling */
1353 read_info->cache_data = cached_data;
1354 read_info->original_get_base = cached_data->rb.get_base;
1355 read_info->original_get_head = cached_data->rb.get_head;
1356 read_info->original_get_tail = cached_data->rb.get_tail;
1357 msg->cache_info->entry_size++;
1358 if (msg->cache_info->entry_limit) {
1359 length = MIN(length, msg->cache_info->entry_limit);
1360 LOG_DBG("Limited number of read %d", length);
1361 }
1362 }
1363
1364 for (size_t i = 0; i < length; i++) {
1365
1366 if (!lwm2m_cache_read(cached_data, &buf)) {
1367 LOG_ERR("Read operation fail");
1368 return -ENOMEM;
1369 }
1370
1371 ret = engine_put_timestamp(&msg->out, buf.t);
1372 if (ret) {
1373 return ret;
1374 }
1375
1376 switch (data_type) {
1377
1378 case LWM2M_RES_TYPE_U32:
1379 ret = engine_put_s64(&msg->out, &msg->path, (int64_t)buf.u32);
1380 break;
1381
1382 case LWM2M_RES_TYPE_U16:
1383 ret = engine_put_s32(&msg->out, &msg->path, (int32_t)buf.u16);
1384 break;
1385
1386 case LWM2M_RES_TYPE_U8:
1387 ret = engine_put_s16(&msg->out, &msg->path, (int16_t)buf.u8);
1388 break;
1389
1390 case LWM2M_RES_TYPE_S64:
1391 ret = engine_put_s64(&msg->out, &msg->path, buf.i64);
1392 break;
1393
1394 case LWM2M_RES_TYPE_S32:
1395 ret = engine_put_s32(&msg->out, &msg->path, buf.i32);
1396 break;
1397
1398 case LWM2M_RES_TYPE_S16:
1399 ret = engine_put_s16(&msg->out, &msg->path, buf.i16);
1400 break;
1401
1402 case LWM2M_RES_TYPE_S8:
1403 ret = engine_put_s8(&msg->out, &msg->path, buf.i8);
1404 break;
1405
1406 case LWM2M_RES_TYPE_BOOL:
1407 ret = engine_put_bool(&msg->out, &msg->path, buf.b);
1408 break;
1409
1410 case LWM2M_RES_TYPE_TIME:
1411 ret = engine_put_time(&msg->out, &msg->path, buf.time);
1412 break;
1413
1414 default:
1415 ret = engine_put_float(&msg->out, &msg->path, &buf.f);
1416 break;
1417
1418 }
1419
1420 /* Validate that we really read some data */
1421 if (ret < 0) {
1422 LOG_ERR("Read operation fail");
1423 return -ENOMEM;
1424 }
1425 }
1426
1427 return 0;
1428 #else
1429 return -ENOTSUP;
1430 #endif
1431 }
1432
lwm2m_accept_timeseries_read(struct lwm2m_message * msg,struct lwm2m_time_series_resource * cached_data)1433 static bool lwm2m_accept_timeseries_read(struct lwm2m_message *msg,
1434 struct lwm2m_time_series_resource *cached_data)
1435 {
1436 #if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT)
1437 if (cached_data && msg->cache_info && lwm2m_cache_size(cached_data) &&
1438 msg->out.writer->put_data_timestamp) {
1439 return true;
1440 }
1441 #endif
1442 return false;
1443 }
1444
lwm2m_read_handler(struct lwm2m_engine_obj_inst * obj_inst,struct lwm2m_engine_res * res,struct lwm2m_engine_obj_field * obj_field,struct lwm2m_message * msg)1445 static int lwm2m_read_handler(struct lwm2m_engine_obj_inst *obj_inst, struct lwm2m_engine_res *res,
1446 struct lwm2m_engine_obj_field *obj_field, struct lwm2m_message *msg)
1447 {
1448 int i, loop_max = 1, found_values = 0;
1449 uint16_t res_inst_id_tmp = 0U;
1450 void *data_ptr = NULL;
1451 struct lwm2m_time_series_resource *cached_data = NULL;
1452 size_t data_len = 0;
1453 struct lwm2m_obj_path temp_path;
1454 int ret = 0;
1455
1456 if (!obj_inst || !res || !obj_field || !msg) {
1457 return -EINVAL;
1458 }
1459 temp_path.obj_id = obj_inst->obj->obj_id;
1460
1461 temp_path.obj_inst_id = obj_inst->obj_inst_id;
1462 temp_path.res_id = obj_field->res_id;
1463 temp_path.level = LWM2M_PATH_LEVEL_RESOURCE;
1464
1465 loop_max = res->res_inst_count;
1466 if (res->multi_res_inst) {
1467 /* search for valid resource instances */
1468 for (i = 0; i < loop_max; i++) {
1469 if (res->res_instances[i].res_inst_id != RES_INSTANCE_NOT_CREATED) {
1470 found_values = 1;
1471 break;
1472 }
1473 }
1474
1475 if (!found_values) {
1476 return -ENOENT;
1477 }
1478
1479 ret = engine_put_begin_ri(&msg->out, &msg->path);
1480 if (ret < 0) {
1481 return ret;
1482 }
1483
1484 res_inst_id_tmp = msg->path.res_inst_id;
1485 }
1486
1487 for (i = 0; i < loop_max; i++) {
1488 if (res->res_instances[i].res_inst_id == RES_INSTANCE_NOT_CREATED) {
1489 continue;
1490 }
1491
1492 if (IS_ENABLED(CONFIG_LWM2M_VERSION_1_1) &&
1493 msg->path.level == LWM2M_PATH_LEVEL_RESOURCE_INST &&
1494 msg->path.res_inst_id != res->res_instances[i].res_inst_id) {
1495 continue;
1496 }
1497
1498 if (res->res_inst_count > 1) {
1499 msg->path.res_inst_id = res->res_instances[i].res_inst_id;
1500 }
1501 if (res->multi_res_inst) {
1502 temp_path.res_inst_id = res->res_instances[i].res_inst_id;
1503 temp_path.level = LWM2M_PATH_LEVEL_RESOURCE_INST;
1504 }
1505
1506 cached_data = lwm2m_cache_entry_get_by_object(&temp_path);
1507
1508 if (lwm2m_accept_timeseries_read(msg, cached_data)) {
1509 /* Content Format Writer have to support timestamp write */
1510 ret = lwm2m_read_cached_data(msg, cached_data, obj_field->data_type);
1511 } else {
1512 /* setup initial data elements */
1513 data_ptr = res->res_instances[i].data_ptr;
1514 data_len = res->res_instances[i].data_len;
1515
1516 /* allow user to override data elements via callback */
1517 if (res->read_cb) {
1518 data_ptr =
1519 res->read_cb(obj_inst->obj_inst_id, res->res_id,
1520 res->res_instances[i].res_inst_id, &data_len);
1521 }
1522
1523 if (!data_ptr && data_len) {
1524 return -ENOENT;
1525 }
1526
1527 if (!data_len) {
1528 if (obj_field->data_type != LWM2M_RES_TYPE_OPAQUE &&
1529 obj_field->data_type != LWM2M_RES_TYPE_STRING) {
1530 return -ENOENT;
1531 }
1532 /* Only opaque and string types can be empty, and when
1533 * empty, we should not give pointer to potentially uninitialized
1534 * data to a content formatter. Give pointer to empty string
1535 * instead.
1536 */
1537 data_ptr = "";
1538 }
1539 ret = lwm2m_read_resource_data(msg, data_ptr, data_len,
1540 obj_field->data_type);
1541 }
1542
1543 /* Validate that we really read some data */
1544 if (ret < 0) {
1545 LOG_ERR("Read operation fail");
1546 return -ENOMEM;
1547 }
1548 }
1549
1550 if (res->multi_res_inst) {
1551 ret = engine_put_end_ri(&msg->out, &msg->path);
1552 if (ret < 0) {
1553 return ret;
1554 }
1555
1556 msg->path.res_inst_id = res_inst_id_tmp;
1557 }
1558
1559 return 0;
1560 }
1561
lwm2m_delete_handler(struct lwm2m_message * msg)1562 static int lwm2m_delete_handler(struct lwm2m_message *msg)
1563 {
1564 int ret;
1565
1566 if (!msg) {
1567 return -EINVAL;
1568 }
1569
1570 /* Device management interface is not allowed to delete Security and
1571 * Device objects instances.
1572 */
1573 if (msg->path.obj_id == LWM2M_OBJECT_SECURITY_ID ||
1574 msg->path.obj_id == LWM2M_OBJECT_DEVICE_ID) {
1575 return -EPERM;
1576 }
1577
1578 ret = lwm2m_delete_obj_inst(msg->path.obj_id, msg->path.obj_inst_id);
1579 if (ret < 0) {
1580 return ret;
1581 }
1582
1583 if (!msg->ctx->bootstrap_mode) {
1584 engine_trigger_update(true);
1585 }
1586
1587 return 0;
1588 }
1589
do_read_op(struct lwm2m_message * msg,uint16_t content_format)1590 static int do_read_op(struct lwm2m_message *msg, uint16_t content_format)
1591 {
1592 switch (content_format) {
1593
1594 case LWM2M_FORMAT_APP_OCTET_STREAM:
1595 return do_read_op_opaque(msg, content_format);
1596
1597 case LWM2M_FORMAT_PLAIN_TEXT:
1598 case LWM2M_FORMAT_OMA_PLAIN_TEXT:
1599 return do_read_op_plain_text(msg, content_format);
1600
1601 #if defined(CONFIG_LWM2M_RW_OMA_TLV_SUPPORT)
1602 case LWM2M_FORMAT_OMA_TLV:
1603 case LWM2M_FORMAT_OMA_OLD_TLV:
1604 return do_read_op_tlv(msg, content_format);
1605 #endif
1606
1607 #if defined(CONFIG_LWM2M_RW_JSON_SUPPORT)
1608 case LWM2M_FORMAT_OMA_JSON:
1609 case LWM2M_FORMAT_OMA_OLD_JSON:
1610 return do_read_op_json(msg, content_format);
1611 #endif
1612
1613 #if defined(CONFIG_LWM2M_RW_SENML_JSON_SUPPORT)
1614 case LWM2M_FORMAT_APP_SEML_JSON:
1615 return do_read_op_senml_json(msg);
1616 #endif
1617
1618 #if defined(CONFIG_LWM2M_RW_CBOR_SUPPORT)
1619 case LWM2M_FORMAT_APP_CBOR:
1620 return do_read_op_cbor(msg);
1621 #endif
1622
1623 #if defined(CONFIG_LWM2M_RW_SENML_CBOR_SUPPORT)
1624 case LWM2M_FORMAT_APP_SENML_CBOR:
1625 return do_read_op_senml_cbor(msg);
1626 #endif
1627
1628 default:
1629 LOG_ERR("Unsupported content-format: %u", content_format);
1630 return -ENOMSG;
1631 }
1632 }
1633
do_composite_read_op(struct lwm2m_message * msg,uint16_t content_format)1634 static int do_composite_read_op(struct lwm2m_message *msg, uint16_t content_format)
1635 {
1636 switch (content_format) {
1637
1638 #if defined(CONFIG_LWM2M_RW_SENML_JSON_SUPPORT)
1639 case LWM2M_FORMAT_APP_SEML_JSON:
1640 return do_composite_read_op_senml_json(msg);
1641 #endif
1642
1643 #if defined(CONFIG_LWM2M_RW_SENML_CBOR_SUPPORT)
1644 case LWM2M_FORMAT_APP_SENML_CBOR:
1645 return do_composite_read_op_senml_cbor(msg);
1646 #endif
1647
1648 default:
1649 LOG_ERR("Unsupported content-format: %u", content_format);
1650 return -ENOMSG;
1651 }
1652 }
1653
lwm2m_perform_read_object_instance(struct lwm2m_message * msg,struct lwm2m_engine_obj_inst * obj_inst,uint8_t * num_read)1654 static int lwm2m_perform_read_object_instance(struct lwm2m_message *msg,
1655 struct lwm2m_engine_obj_inst *obj_inst,
1656 uint8_t *num_read)
1657 {
1658 struct lwm2m_engine_res *res = NULL;
1659 struct lwm2m_engine_obj_field *obj_field;
1660 int ret = 0;
1661
1662 while (obj_inst) {
1663 if (!obj_inst->resources || obj_inst->resource_count == 0U) {
1664 goto move_forward;
1665 }
1666
1667 /* update the obj_inst_id as we move through the instances */
1668 msg->path.obj_inst_id = obj_inst->obj_inst_id;
1669
1670 ret = engine_put_begin_oi(&msg->out, &msg->path);
1671 if (ret < 0) {
1672 return ret;
1673 }
1674
1675 for (int index = 0; index < obj_inst->resource_count; index++) {
1676 if (msg->path.level > LWM2M_PATH_LEVEL_OBJECT_INST &&
1677 msg->path.res_id != obj_inst->resources[index].res_id) {
1678 continue;
1679 }
1680
1681 res = &obj_inst->resources[index];
1682 msg->path.res_id = res->res_id;
1683 obj_field = lwm2m_get_engine_obj_field(obj_inst->obj, res->res_id);
1684 if (!obj_field) {
1685 ret = -ENOENT;
1686 } else if (!LWM2M_HAS_PERM(obj_field, LWM2M_PERM_R)) {
1687 ret = -EPERM;
1688 } else {
1689 /* start resource formatting */
1690 ret = engine_put_begin_r(&msg->out, &msg->path);
1691 if (ret < 0) {
1692 return ret;
1693 }
1694
1695 /* perform read operation on this resource */
1696 ret = lwm2m_read_handler(obj_inst, res, obj_field, msg);
1697 if (ret == -ENOMEM) {
1698 /* No point continuing if there's no
1699 * memory left in a message.
1700 */
1701 return ret;
1702 } else if (ret < 0) {
1703 /* ignore errors unless single read */
1704 if (msg->path.level > LWM2M_PATH_LEVEL_OBJECT_INST &&
1705 !LWM2M_HAS_PERM(obj_field, BIT(LWM2M_FLAG_OPTIONAL))) {
1706 LOG_ERR("READ OP: %d", ret);
1707 }
1708 } else {
1709 *num_read += 1U;
1710 }
1711
1712 /* end resource formatting */
1713 ret = engine_put_end_r(&msg->out, &msg->path);
1714 if (ret < 0) {
1715 return ret;
1716 }
1717 }
1718
1719 /* on single read break if errors */
1720 if (ret < 0 && msg->path.level > LWM2M_PATH_LEVEL_OBJECT_INST) {
1721 break;
1722 }
1723 }
1724
1725 move_forward:
1726 ret = engine_put_end_oi(&msg->out, &msg->path);
1727 if (ret < 0) {
1728 return ret;
1729 }
1730
1731 if (msg->path.level <= LWM2M_PATH_LEVEL_OBJECT) {
1732 /* advance to the next object instance */
1733 obj_inst = next_engine_obj_inst(msg->path.obj_id, obj_inst->obj_inst_id);
1734 } else {
1735 obj_inst = NULL;
1736 }
1737 }
1738
1739 return ret;
1740 }
1741
lwm2m_perform_read_op(struct lwm2m_message * msg,uint16_t content_format)1742 int lwm2m_perform_read_op(struct lwm2m_message *msg, uint16_t content_format)
1743 {
1744 struct lwm2m_engine_obj_inst *obj_inst = NULL;
1745 struct lwm2m_obj_path temp_path;
1746 int ret = 0;
1747 uint8_t num_read = 0U;
1748
1749 if (msg->path.level >= LWM2M_PATH_LEVEL_OBJECT_INST) {
1750 obj_inst = get_engine_obj_inst(msg->path.obj_id, msg->path.obj_inst_id);
1751 if (!obj_inst) {
1752 /* When Object instance is indicated error have to be reported */
1753 return -ENOENT;
1754 }
1755 } else if (msg->path.level == LWM2M_PATH_LEVEL_OBJECT) {
1756 /* find first obj_inst with path's obj_id.
1757 * Path level 1 can accept NULL. It define empty payload to response.
1758 */
1759 obj_inst = next_engine_obj_inst(msg->path.obj_id, -1);
1760 }
1761
1762 /* set output content-format */
1763 ret = coap_append_option_int(msg->out.out_cpkt, COAP_OPTION_CONTENT_FORMAT, content_format);
1764 if (ret < 0) {
1765 LOG_ERR("Error setting response content-format: %d", ret);
1766 return ret;
1767 }
1768
1769 ret = coap_packet_append_payload_marker(msg->out.out_cpkt);
1770 if (ret < 0) {
1771 LOG_ERR("Error appending payload marker: %d", ret);
1772 return ret;
1773 }
1774
1775 /* store original path values so we can change them during processing */
1776 memcpy(&temp_path, &msg->path, sizeof(temp_path));
1777
1778 if (engine_put_begin(&msg->out, &msg->path) < 0) {
1779 return -ENOMEM;
1780 }
1781
1782 ret = lwm2m_perform_read_object_instance(msg, obj_inst, &num_read);
1783 if (ret < 0) {
1784 return ret;
1785 }
1786
1787 if (engine_put_end(&msg->out, &msg->path) < 0) {
1788 return -ENOMEM;
1789 }
1790
1791 /* restore original path values */
1792 memcpy(&msg->path, &temp_path, sizeof(temp_path));
1793
1794 /* did not read anything even if we should have - on single item */
1795 if (ret == 0 && num_read == 0U) {
1796 if (msg->path.level == LWM2M_PATH_LEVEL_RESOURCE) {
1797 return -ENOENT;
1798 }
1799
1800 if (IS_ENABLED(CONFIG_LWM2M_VERSION_1_1) &&
1801 msg->path.level == LWM2M_PATH_LEVEL_RESOURCE_INST) {
1802 return -ENOENT;
1803 }
1804 }
1805
1806 return ret;
1807 }
1808
lwm2m_discover_add_res(struct lwm2m_message * msg,struct lwm2m_engine_obj_inst * obj_inst,struct lwm2m_engine_res * res)1809 static int lwm2m_discover_add_res(struct lwm2m_message *msg, struct lwm2m_engine_obj_inst *obj_inst,
1810 struct lwm2m_engine_res *res)
1811 {
1812 int ret;
1813
1814 ret = engine_put_corelink(
1815 &msg->out, &LWM2M_OBJ(obj_inst->obj->obj_id, obj_inst->obj_inst_id, res->res_id));
1816 if (ret < 0) {
1817 return ret;
1818 }
1819
1820 /* Report resource instances, if applicable. */
1821 if (IS_ENABLED(CONFIG_LWM2M_VERSION_1_1) && msg->path.level == LWM2M_PATH_LEVEL_RESOURCE &&
1822 res->multi_res_inst) {
1823 for (int i = 0; i < res->res_inst_count; i++) {
1824 struct lwm2m_engine_res_inst *res_inst = &res->res_instances[i];
1825
1826 if (res_inst->res_inst_id == RES_INSTANCE_NOT_CREATED) {
1827 continue;
1828 }
1829
1830 ret = engine_put_corelink(
1831 &msg->out, &LWM2M_OBJ(obj_inst->obj->obj_id, obj_inst->obj_inst_id,
1832 res->res_id, res_inst->res_inst_id));
1833 if (ret < 0) {
1834 return ret;
1835 }
1836 }
1837 }
1838
1839 return 0;
1840 }
1841
lwm2m_discover_handler(struct lwm2m_message * msg,bool is_bootstrap)1842 int lwm2m_discover_handler(struct lwm2m_message *msg, bool is_bootstrap)
1843 {
1844 struct lwm2m_engine_obj *obj;
1845 struct lwm2m_engine_obj_inst *obj_inst;
1846 int ret;
1847 bool reported = false;
1848 sys_slist_t *engine_obj_list = lwm2m_engine_obj_list();
1849 sys_slist_t *engine_obj_inst_list = lwm2m_engine_obj_inst_list();
1850
1851 /* Object ID is required in Device Management Discovery (5.4.2). */
1852 if (!is_bootstrap && (msg->path.level == LWM2M_PATH_LEVEL_NONE ||
1853 msg->path.obj_id == LWM2M_OBJECT_SECURITY_ID)) {
1854 return -EPERM;
1855 }
1856
1857 /* Bootstrap discovery allows to specify at most Object ID. */
1858 if (is_bootstrap && msg->path.level > LWM2M_PATH_LEVEL_OBJECT) {
1859 return -EPERM;
1860 }
1861
1862 /* set output content-format */
1863 ret = coap_append_option_int(msg->out.out_cpkt, COAP_OPTION_CONTENT_FORMAT,
1864 LWM2M_FORMAT_APP_LINK_FORMAT);
1865 if (ret < 0) {
1866 LOG_ERR("Error setting response content-format: %d", ret);
1867 return ret;
1868 }
1869
1870 ret = coap_packet_append_payload_marker(msg->out.out_cpkt);
1871 if (ret < 0) {
1872 return ret;
1873 }
1874
1875 /*
1876 * Add required prefix for bootstrap discovery (5.2.7.3).
1877 * For device management discovery, `engine_put_begin()` adds nothing.
1878 */
1879 ret = engine_put_begin(&msg->out, &msg->path);
1880 if (ret < 0) {
1881 return ret;
1882 }
1883
1884 SYS_SLIST_FOR_EACH_CONTAINER(engine_obj_list, obj, node) {
1885 /* Skip unrelated objects */
1886 if (msg->path.level > 0 && msg->path.obj_id != obj->obj_id) {
1887 continue;
1888 }
1889
1890 /* For bootstrap discover, only report object ID when no
1891 * instance is available or it's needed to report object
1892 * version.
1893 * For device management discovery, only report object ID with
1894 * attributes if object ID (alone) was provided.
1895 */
1896 if ((is_bootstrap &&
1897 (obj->instance_count == 0U || lwm2m_engine_shall_report_obj_version(obj))) ||
1898 (!is_bootstrap && msg->path.level == LWM2M_PATH_LEVEL_OBJECT)) {
1899 ret = engine_put_corelink(&msg->out, &LWM2M_OBJ(obj->obj_id));
1900 if (ret < 0) {
1901 return ret;
1902 }
1903
1904 reported = true;
1905
1906 if (obj->instance_count == 0U) {
1907 continue;
1908 }
1909 }
1910
1911 SYS_SLIST_FOR_EACH_CONTAINER(engine_obj_inst_list, obj_inst, node) {
1912 if (obj_inst->obj->obj_id != obj->obj_id) {
1913 continue;
1914 }
1915
1916 /* Skip unrelated object instance. */
1917 if (msg->path.level > LWM2M_PATH_LEVEL_OBJECT &&
1918 msg->path.obj_inst_id != obj_inst->obj_inst_id) {
1919 continue;
1920 }
1921
1922 /* Report object instances only if no Resource ID is
1923 * provided.
1924 */
1925 if (msg->path.level <= LWM2M_PATH_LEVEL_OBJECT_INST) {
1926 ret = engine_put_corelink(
1927 &msg->out,
1928 &LWM2M_OBJ(obj_inst->obj->obj_id, obj_inst->obj_inst_id));
1929 if (ret < 0) {
1930 return ret;
1931 }
1932
1933 reported = true;
1934 }
1935
1936 /* Do not report resources in bootstrap discovery. */
1937 if (is_bootstrap) {
1938 continue;
1939 }
1940
1941 for (int i = 0; i < obj_inst->resource_count; i++) {
1942 /* Skip unrelated resources. */
1943 if (msg->path.level == LWM2M_PATH_LEVEL_RESOURCE &&
1944 msg->path.res_id != obj_inst->resources[i].res_id) {
1945 continue;
1946 }
1947
1948 ret = lwm2m_discover_add_res(msg, obj_inst,
1949 &obj_inst->resources[i]);
1950 if (ret < 0) {
1951 return ret;
1952 }
1953
1954 reported = true;
1955 }
1956 }
1957 }
1958
1959 return reported ? 0 : -ENOENT;
1960 }
1961
do_discover_op(struct lwm2m_message * msg,uint16_t content_format)1962 static int do_discover_op(struct lwm2m_message *msg, uint16_t content_format)
1963 {
1964 switch (content_format) {
1965 case LWM2M_FORMAT_APP_LINK_FORMAT:
1966 return do_discover_op_link_format(msg, msg->ctx->bootstrap_mode);
1967
1968 default:
1969 LOG_ERR("Unsupported format: %u", content_format);
1970 return -ENOMSG;
1971 }
1972 }
1973
do_write_op(struct lwm2m_message * msg,uint16_t format)1974 static int do_write_op(struct lwm2m_message *msg, uint16_t format)
1975 {
1976 int r;
1977
1978 switch (format) {
1979
1980 case LWM2M_FORMAT_APP_OCTET_STREAM:
1981 r = do_write_op_opaque(msg);
1982 break;
1983
1984 case LWM2M_FORMAT_PLAIN_TEXT:
1985 case LWM2M_FORMAT_OMA_PLAIN_TEXT:
1986 r = do_write_op_plain_text(msg);
1987 break;
1988
1989 #ifdef CONFIG_LWM2M_RW_OMA_TLV_SUPPORT
1990 case LWM2M_FORMAT_OMA_TLV:
1991 case LWM2M_FORMAT_OMA_OLD_TLV:
1992 r = do_write_op_tlv(msg);
1993 break;
1994 #endif
1995
1996 #ifdef CONFIG_LWM2M_RW_JSON_SUPPORT
1997 case LWM2M_FORMAT_OMA_JSON:
1998 case LWM2M_FORMAT_OMA_OLD_JSON:
1999 r = do_write_op_json(msg);
2000 break;
2001 #endif
2002
2003 #if defined(CONFIG_LWM2M_RW_SENML_JSON_SUPPORT)
2004 case LWM2M_FORMAT_APP_SEML_JSON:
2005 r = do_write_op_senml_json(msg);
2006 break;
2007 #endif
2008
2009 #ifdef CONFIG_LWM2M_RW_CBOR_SUPPORT
2010 case LWM2M_FORMAT_APP_CBOR:
2011 r = do_write_op_cbor(msg);
2012 break;
2013 #endif
2014
2015 #ifdef CONFIG_LWM2M_RW_SENML_CBOR_SUPPORT
2016 case LWM2M_FORMAT_APP_SENML_CBOR:
2017 r = do_write_op_senml_cbor(msg);
2018 break;
2019 #endif
2020
2021 default:
2022 LOG_ERR("Unsupported format: %u", format);
2023 r = -ENOMSG;
2024 break;
2025 }
2026
2027 return r;
2028 }
2029
parse_write_op(struct lwm2m_message * msg,uint16_t format)2030 static int parse_write_op(struct lwm2m_message *msg, uint16_t format)
2031 {
2032 int block_opt, block_num;
2033 struct lwm2m_block_context *block_ctx = NULL;
2034 enum coap_block_size block_size;
2035 bool last_block = false;
2036 int r;
2037 uint16_t payload_len = 0U;
2038 const uint8_t *payload_start;
2039
2040 /* setup incoming data */
2041 payload_start = coap_packet_get_payload(msg->in.in_cpkt, &payload_len);
2042 if (payload_len > 0) {
2043 msg->in.offset = payload_start - msg->in.in_cpkt->data;
2044 } else {
2045 msg->in.offset = msg->in.in_cpkt->offset;
2046 }
2047
2048 /* Check for block transfer */
2049 block_opt = coap_get_option_int(msg->in.in_cpkt, COAP_OPTION_BLOCK1);
2050 if (block_opt > 0) {
2051 last_block = !GET_MORE(block_opt);
2052
2053 /* RFC7252: 4.6. Message Size */
2054 block_size = GET_BLOCK_SIZE(block_opt);
2055 if (!last_block && coap_block_size_to_bytes(block_size) > payload_len) {
2056 LOG_DBG("Trailing payload is discarded!");
2057 return -EFBIG;
2058 }
2059
2060 block_num = GET_BLOCK_NUM(block_opt);
2061
2062 /*
2063 * RFC7959: 2.5. Using the Block1 Option
2064 * If we've received first block, replace old context (if any) with a new one.
2065 */
2066 r = get_block_ctx(&msg->path, &block_ctx);
2067 if (block_num == 0) {
2068 /* free block context for previous incomplete transfer */
2069 free_block_ctx(block_ctx);
2070
2071 r = init_block_ctx(&msg->path, &block_ctx);
2072 /* If we have already parsed the packet, we can handle the block size
2073 * given by the server.
2074 */
2075 block_ctx->ctx.block_size = block_size;
2076 }
2077
2078 if (r < 0) {
2079 LOG_ERR("Cannot find block context");
2080 return r;
2081 }
2082
2083 msg->in.block_ctx = block_ctx;
2084
2085 if (block_num < block_ctx->expected) {
2086 LOG_WRN("Block already handled %d, expected %d", block_num,
2087 block_ctx->expected);
2088 (void)coap_header_set_code(msg->out.out_cpkt, COAP_RESPONSE_CODE_CONTINUE);
2089 /* Respond with the original Block1 header, original Ack might have been
2090 * lost, and this is a retry. We don't know the original response, but
2091 * since it is handled, just assume we can continue.
2092 */
2093 (void)coap_append_option_int(msg->out.out_cpkt, COAP_OPTION_BLOCK1,
2094 block_opt);
2095 return 0;
2096 }
2097 if (block_num > block_ctx->expected) {
2098 LOG_WRN("Block out of order %d, expected %d", block_num,
2099 block_ctx->expected);
2100 r = -EFAULT;
2101 return r;
2102 }
2103 r = coap_update_from_block(msg->in.in_cpkt, &block_ctx->ctx);
2104 if (r < 0) {
2105 LOG_ERR("Error from block update: %d", r);
2106 return r;
2107 }
2108
2109 block_ctx->last_block = last_block;
2110 block_ctx->expected++;
2111 }
2112
2113 r = do_write_op(msg, format);
2114
2115 /* Handle blockwise 1 (Part 2): Append BLOCK1 option / free context */
2116 if (block_ctx) {
2117 if (r >= 0) {
2118 /* Add block1 option to response.
2119 * As RFC7959 Section-2.3, More flag is off, because we have already
2120 * written the data.
2121 */
2122 r = coap_append_block1_option(msg->out.out_cpkt, &block_ctx->ctx);
2123 if (r < 0) {
2124 /* report as internal server error */
2125 LOG_DBG("Fail adding block1 option: %d", r);
2126 r = -EINVAL;
2127 }
2128 if (!last_block) {
2129 r = coap_header_set_code(msg->out.out_cpkt,
2130 COAP_RESPONSE_CODE_CONTINUE);
2131 if (r < 0) {
2132 LOG_DBG("Failed to modify response code");
2133 r = -EINVAL;
2134 }
2135 }
2136 }
2137 if (r < 0 || last_block) {
2138 /* Free context when finished or when there is error */
2139 free_block_ctx(block_ctx);
2140 }
2141 }
2142
2143 return r;
2144 }
2145
do_composite_write_op(struct lwm2m_message * msg,uint16_t format)2146 static int do_composite_write_op(struct lwm2m_message *msg, uint16_t format)
2147 {
2148 uint16_t payload_len = 0U;
2149 const uint8_t *payload_start;
2150
2151 /* setup incoming data */
2152 payload_start = coap_packet_get_payload(msg->in.in_cpkt, &payload_len);
2153 if (payload_len > 0) {
2154 msg->in.offset = payload_start - msg->in.in_cpkt->data;
2155 } else {
2156 msg->in.offset = msg->in.in_cpkt->offset;
2157 }
2158
2159 if (coap_get_option_int(msg->in.in_cpkt, COAP_OPTION_BLOCK1) >= 0) {
2160 return -ENOTSUP;
2161 }
2162
2163 switch (format) {
2164 #if defined(CONFIG_LWM2M_RW_SENML_JSON_SUPPORT)
2165 case LWM2M_FORMAT_APP_SEML_JSON:
2166 return do_write_op_senml_json(msg);
2167 #endif
2168
2169 #if defined(CONFIG_LWM2M_RW_SENML_CBOR_SUPPORT)
2170 case LWM2M_FORMAT_APP_SENML_CBOR:
2171 return do_write_op_senml_cbor(msg);
2172 #endif
2173
2174 default:
2175 LOG_ERR("Unsupported format: %u", format);
2176 return -ENOMSG;
2177 }
2178 }
2179
lwm2m_engine_path_included(uint8_t code,bool bootstrap_mode)2180 static bool lwm2m_engine_path_included(uint8_t code, bool bootstrap_mode)
2181 {
2182 switch (code & COAP_REQUEST_MASK) {
2183 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
2184 case COAP_METHOD_DELETE:
2185 case COAP_METHOD_GET:
2186 if (bootstrap_mode) {
2187 return false;
2188 }
2189 break;
2190 #endif
2191 case COAP_METHOD_FETCH:
2192 /* Composite Read operation */
2193 case COAP_METHOD_IPATCH:
2194 /* Composite write operation */
2195 return false;
2196 default:
2197 break;
2198 }
2199 return true;
2200 }
2201
lwm2m_engine_default_content_format(uint16_t * accept_format)2202 static int lwm2m_engine_default_content_format(uint16_t *accept_format)
2203 {
2204 if (IS_ENABLED(CONFIG_LWM2M_VERSION_1_1)) {
2205 /* Select content format use SenML CBOR when it possible */
2206 if (IS_ENABLED(CONFIG_LWM2M_RW_SENML_CBOR_SUPPORT)) {
2207 LOG_DBG("No accept option given. Assume SenML CBOR.");
2208 *accept_format = LWM2M_FORMAT_APP_SENML_CBOR;
2209 } else if (IS_ENABLED(CONFIG_LWM2M_RW_SENML_JSON_SUPPORT)) {
2210 LOG_DBG("No accept option given. Assume SenML Json.");
2211 *accept_format = LWM2M_FORMAT_APP_SEML_JSON;
2212 } else if (IS_ENABLED(CONFIG_LWM2M_RW_CBOR_SUPPORT)) {
2213 LOG_DBG("No accept option given. Assume CBOR.");
2214 *accept_format = LWM2M_FORMAT_APP_CBOR;
2215 } else {
2216 LOG_ERR("CBOR, SenML CBOR or SenML JSON is not supported");
2217 return -ENOTSUP;
2218 }
2219 } else if (IS_ENABLED(CONFIG_LWM2M_RW_OMA_TLV_SUPPORT)) {
2220 LOG_DBG("No accept option given. Assume OMA TLV.");
2221 *accept_format = LWM2M_FORMAT_OMA_TLV;
2222 } else {
2223 LOG_ERR("No default content format is set");
2224 return -ENOTSUP;
2225 }
2226
2227 return 0;
2228 }
2229
lwm2m_exec_handler(struct lwm2m_message * msg)2230 static int lwm2m_exec_handler(struct lwm2m_message *msg)
2231 {
2232 struct lwm2m_engine_obj_inst *obj_inst;
2233 struct lwm2m_engine_res *res = NULL;
2234 int ret;
2235 uint8_t *args;
2236 uint16_t args_len;
2237
2238 if (!msg) {
2239 return -EINVAL;
2240 }
2241
2242 ret = path_to_objs(&msg->path, &obj_inst, NULL, &res, NULL);
2243 if (ret < 0) {
2244 return ret;
2245 }
2246
2247 args = (uint8_t *)coap_packet_get_payload(msg->in.in_cpkt, &args_len);
2248
2249 if (res->execute_cb) {
2250 return res->execute_cb(obj_inst->obj_inst_id, args, args_len);
2251 }
2252
2253 /* TODO: something else to handle for execute? */
2254 return -ENOENT;
2255 }
2256
handle_request(struct coap_packet * request,struct lwm2m_message * msg)2257 static int handle_request(struct coap_packet *request, struct lwm2m_message *msg)
2258 {
2259 int r;
2260 uint8_t code;
2261 struct coap_option options[4];
2262 struct lwm2m_engine_obj *obj = NULL;
2263 uint8_t token[8];
2264 uint8_t tkl = 0U;
2265 uint16_t format = LWM2M_FORMAT_NONE, accept;
2266 int observe = -1; /* default to -1, 0 = ENABLE, 1 = DISABLE */
2267
2268 /* set CoAP request / message */
2269 msg->in.in_cpkt = request;
2270 msg->out.out_cpkt = &msg->cpkt;
2271
2272 /* set default reader/writer */
2273 msg->in.reader = &plain_text_reader;
2274 msg->out.writer = &plain_text_writer;
2275
2276 code = coap_header_get_code(msg->in.in_cpkt);
2277
2278 /* setup response token */
2279 tkl = coap_header_get_token(msg->in.in_cpkt, token);
2280 if (tkl) {
2281 msg->tkl = tkl;
2282 msg->token = token;
2283 }
2284
2285 if (IS_ENABLED(CONFIG_LWM2M_GATEWAY_OBJ_SUPPORT)) {
2286 r = lwm2m_gw_handle_req(msg);
2287 if (r == 0) {
2288 return 0;
2289 } else if (r != -ENOENT) {
2290 goto error;
2291 }
2292 }
2293
2294 /* parse the URL path into components */
2295 r = coap_find_options(msg->in.in_cpkt, COAP_OPTION_URI_PATH, options, ARRAY_SIZE(options));
2296 if (r < 0) {
2297 goto error;
2298 }
2299
2300 /* Treat empty URI path option as is there were no option - this will be
2301 * represented as a level "zero" in the path structure.
2302 */
2303 if (r == 1 && options[0].len == 0) {
2304 r = 0;
2305 }
2306
2307 if (r == 0 && lwm2m_engine_path_included(code, msg->ctx->bootstrap_mode)) {
2308 /* No URI path or empty URI path option - allowed only during
2309 * bootstrap or CoAP Fetch or iPATCH.
2310 */
2311
2312 r = -EPERM;
2313 goto error;
2314 }
2315
2316 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
2317 /* check for bootstrap-finish */
2318 if ((code & COAP_REQUEST_MASK) == COAP_METHOD_POST && r == 1 &&
2319 strncmp(options[0].value, "bs", options[0].len) == 0) {
2320 engine_bootstrap_finish();
2321
2322 msg->code = COAP_RESPONSE_CODE_CHANGED;
2323
2324 r = lwm2m_init_message(msg);
2325 if (r < 0) {
2326 goto error;
2327 }
2328
2329 return 0;
2330 }
2331 #endif
2332
2333 r = coap_options_to_path(options, r, &msg->path);
2334 if (r < 0) {
2335 r = -ENOENT;
2336 goto error;
2337 }
2338
2339 /* read Content Format / setup in.reader */
2340 r = coap_find_options(msg->in.in_cpkt, COAP_OPTION_CONTENT_FORMAT, options, 1);
2341 if (r > 0) {
2342 format = coap_option_value_to_int(&options[0]);
2343 r = select_reader(&msg->in, format);
2344 if (r < 0) {
2345 goto error;
2346 }
2347 }
2348
2349 /* read Accept / setup out.writer */
2350 r = coap_find_options(msg->in.in_cpkt, COAP_OPTION_ACCEPT, options, 1);
2351 if (r > 0) {
2352 accept = coap_option_value_to_int(&options[0]);
2353 } else {
2354 /* Select Default based LWM2M_VERSION */
2355 r = lwm2m_engine_default_content_format(&accept);
2356 if (r) {
2357 goto error;
2358 }
2359 }
2360
2361 r = select_writer(&msg->out, accept);
2362 if (r < 0) {
2363 goto error;
2364 }
2365
2366 /* Do Only Object find if path have been parsed */
2367 if (lwm2m_engine_path_included(code, msg->ctx->bootstrap_mode)) {
2368 if (!(msg->ctx->bootstrap_mode && msg->path.level == LWM2M_PATH_LEVEL_NONE)) {
2369 /* find registered obj */
2370 obj = get_engine_obj(msg->path.obj_id);
2371 if (!obj) {
2372 /* No matching object found - ignore request */
2373 r = -ENOENT;
2374 goto error;
2375 }
2376 }
2377 }
2378
2379 /* set the operation */
2380 switch (code & COAP_REQUEST_MASK) {
2381
2382 case COAP_METHOD_GET:
2383 /*
2384 * LwM2M V1_0_1-20170704-A, table 25,
2385 * Discover: CoAP GET + accept=LWM2M_FORMAT_APP_LINK_FORMAT
2386 */
2387 if (accept == LWM2M_FORMAT_APP_LINK_FORMAT) {
2388 msg->operation = LWM2M_OP_DISCOVER;
2389 accept = LWM2M_FORMAT_APP_LINK_FORMAT;
2390 } else {
2391 msg->operation = LWM2M_OP_READ;
2392 }
2393
2394 /* check for observe */
2395 observe = coap_get_option_int(msg->in.in_cpkt, COAP_OPTION_OBSERVE);
2396 msg->code = COAP_RESPONSE_CODE_CONTENT;
2397 break;
2398
2399 case COAP_METHOD_FETCH:
2400 msg->operation = LWM2M_OP_READ;
2401 /* check for observe */
2402 observe = coap_get_option_int(msg->in.in_cpkt, COAP_OPTION_OBSERVE);
2403 msg->code = COAP_RESPONSE_CODE_CONTENT;
2404 break;
2405
2406 case COAP_METHOD_IPATCH:
2407 msg->operation = LWM2M_OP_WRITE;
2408 msg->code = COAP_RESPONSE_CODE_CHANGED;
2409 break;
2410
2411 case COAP_METHOD_POST:
2412 if (msg->path.level == 1U) {
2413 /* create an object instance */
2414 msg->operation = LWM2M_OP_CREATE;
2415 msg->code = COAP_RESPONSE_CODE_CREATED;
2416 } else if (msg->path.level == 2U) {
2417 /* write values to an object instance */
2418 msg->operation = LWM2M_OP_WRITE;
2419 msg->code = COAP_RESPONSE_CODE_CHANGED;
2420 } else {
2421 msg->operation = LWM2M_OP_EXECUTE;
2422 msg->code = COAP_RESPONSE_CODE_CHANGED;
2423 }
2424
2425 break;
2426
2427 case COAP_METHOD_PUT:
2428 /* write attributes if content-format is absent */
2429 if (format == LWM2M_FORMAT_NONE) {
2430 msg->operation = LWM2M_OP_WRITE_ATTR;
2431 } else {
2432 msg->operation = LWM2M_OP_WRITE;
2433 }
2434
2435 msg->code = COAP_RESPONSE_CODE_CHANGED;
2436 break;
2437
2438 case COAP_METHOD_DELETE:
2439 msg->operation = LWM2M_OP_DELETE;
2440 msg->code = COAP_RESPONSE_CODE_DELETED;
2441 break;
2442
2443 default:
2444 break;
2445 }
2446
2447 /* render CoAP packet header */
2448 r = lwm2m_init_message(msg);
2449 if (r < 0) {
2450 goto error;
2451 }
2452
2453 #if defined(CONFIG_LWM2M_ACCESS_CONTROL_ENABLE)
2454 r = access_control_check_access(msg->path.obj_id, msg->path.obj_inst_id,
2455 msg->ctx->srv_obj_inst, msg->operation,
2456 msg->ctx->bootstrap_mode);
2457 if (r < 0) {
2458 LOG_ERR("Access denied - Server obj %u does not have proper access to "
2459 "resource",
2460 msg->ctx->srv_obj_inst);
2461 goto error;
2462 }
2463 #endif
2464 if (msg->path.level > LWM2M_PATH_LEVEL_NONE &&
2465 msg->path.obj_id == LWM2M_OBJECT_SECURITY_ID && !msg->ctx->bootstrap_mode) {
2466 r = -EACCES;
2467 goto error;
2468 }
2469
2470 switch (msg->operation) {
2471
2472 case LWM2M_OP_READ:
2473 if (observe >= 0) {
2474 /* Validate That Token is valid for Observation */
2475 if (!msg->token) {
2476 LOG_ERR("OBSERVE request missing token");
2477 r = -EINVAL;
2478 goto error;
2479 }
2480
2481 if ((code & COAP_REQUEST_MASK) == COAP_METHOD_GET) {
2482 /* Normal Observation Request or Cancel */
2483 r = lwm2m_engine_observation_handler(msg, observe, accept,
2484 false);
2485 if (r < 0) {
2486 goto error;
2487 }
2488
2489 r = do_read_op(msg, accept);
2490 } else {
2491 /* Composite Observation request & cancel handler */
2492 r = lwm2m_engine_observation_handler(msg, observe, accept,
2493 true);
2494 if (r < 0) {
2495 goto error;
2496 }
2497 }
2498 } else {
2499 if ((code & COAP_REQUEST_MASK) == COAP_METHOD_GET) {
2500 r = do_read_op(msg, accept);
2501 } else {
2502 r = do_composite_read_op(msg, accept);
2503 }
2504 }
2505 break;
2506
2507 case LWM2M_OP_DISCOVER:
2508 r = do_discover_op(msg, accept);
2509 break;
2510
2511 case LWM2M_OP_WRITE:
2512 case LWM2M_OP_CREATE:
2513 if ((code & COAP_REQUEST_MASK) == COAP_METHOD_IPATCH) {
2514 /* iPATCH is for Composite purpose */
2515 r = do_composite_write_op(msg, format);
2516 } else {
2517 /* Single resource write Operation */
2518 r = parse_write_op(msg, format);
2519 }
2520 #if defined(CONFIG_LWM2M_ACCESS_CONTROL_ENABLE)
2521 if (msg->operation == LWM2M_OP_CREATE && r >= 0) {
2522 access_control_add(msg->path.obj_id, msg->path.obj_inst_id,
2523 msg->ctx->srv_obj_inst);
2524 }
2525 #endif
2526 break;
2527
2528 case LWM2M_OP_WRITE_ATTR:
2529 r = lwm2m_write_attr_handler(obj, msg);
2530 break;
2531
2532 case LWM2M_OP_EXECUTE:
2533 r = lwm2m_exec_handler(msg);
2534 break;
2535
2536 case LWM2M_OP_DELETE:
2537 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
2538 if (msg->ctx->bootstrap_mode) {
2539 r = bootstrap_delete(msg);
2540 break;
2541 }
2542 #endif
2543 r = lwm2m_delete_handler(msg);
2544 break;
2545
2546 default:
2547 LOG_ERR("Unknown operation: %u", msg->operation);
2548 r = -EINVAL;
2549 }
2550
2551 if (r < 0) {
2552 goto error;
2553 }
2554
2555 return 0;
2556
2557 error:
2558 lwm2m_reset_message(msg, false);
2559 if (r == -ENOENT) {
2560 msg->code = COAP_RESPONSE_CODE_NOT_FOUND;
2561 } else if (r == -EPERM) {
2562 msg->code = COAP_RESPONSE_CODE_NOT_ALLOWED;
2563 } else if (r == -EEXIST) {
2564 msg->code = COAP_RESPONSE_CODE_BAD_REQUEST;
2565 } else if (r == -EFAULT) {
2566 msg->code = COAP_RESPONSE_CODE_INCOMPLETE;
2567 } else if (r == -EFBIG) {
2568 msg->code = COAP_RESPONSE_CODE_REQUEST_TOO_LARGE;
2569 } else if (r == -ENOTSUP) {
2570 msg->code = COAP_RESPONSE_CODE_NOT_IMPLEMENTED;
2571 } else if (r == -ENOMSG) {
2572 msg->code = COAP_RESPONSE_CODE_UNSUPPORTED_CONTENT_FORMAT;
2573 } else if (r == -EACCES) {
2574 msg->code = COAP_RESPONSE_CODE_UNAUTHORIZED;
2575 } else if (r == -ECANCELED) {
2576 msg->code = COAP_RESPONSE_CODE_NOT_ACCEPTABLE;
2577 } else {
2578 /* Failed to handle the request */
2579 msg->code = COAP_RESPONSE_CODE_INTERNAL_ERROR;
2580 }
2581
2582 r = lwm2m_init_message(msg);
2583 if (r < 0) {
2584 LOG_ERR("Error recreating message: %d", r);
2585 }
2586
2587 return 0;
2588 }
2589
lwm2m_response_promote_to_con(struct lwm2m_message * msg)2590 static int lwm2m_response_promote_to_con(struct lwm2m_message *msg)
2591 {
2592 int ret;
2593
2594 msg->type = COAP_TYPE_CON;
2595 msg->mid = coap_next_id();
2596
2597 /* Since the response CoAP packet is already generated at this point,
2598 * tweak the specific fields manually:
2599 * - CoAP message type (byte 0, bits 2 and 3)
2600 * - CoAP message id (bytes 2 and 3)
2601 */
2602 msg->cpkt.data[0] &= ~(0x3 << 4);
2603 msg->cpkt.data[0] |= (msg->type & 0x3) << 4;
2604 msg->cpkt.data[2] = msg->mid >> 8;
2605 msg->cpkt.data[3] = (uint8_t)msg->mid;
2606
2607 if (msg->pending) {
2608 coap_pending_clear(msg->pending);
2609 }
2610
2611 /* Add the packet to the pending list. */
2612 msg->pending = coap_pending_next_unused(msg->ctx->pendings, ARRAY_SIZE(msg->ctx->pendings));
2613 if (!msg->pending) {
2614 LOG_ERR("Unable to find a free pending to track "
2615 "retransmissions.");
2616 return -ENOMEM;
2617 }
2618
2619 ret = coap_pending_init(msg->pending, &msg->cpkt, &msg->ctx->remote_addr, NULL);
2620 if (ret < 0) {
2621 LOG_ERR("Unable to initialize a pending "
2622 "retransmission (err:%d).",
2623 ret);
2624 }
2625
2626 return ret;
2627 }
2628
find_ongoing_block2_tx(void)2629 static struct lwm2m_message *find_ongoing_block2_tx(void)
2630 {
2631 /* TODO: I could try to check if there is Request-Tags attached, and then match queries
2632 * for those, but currently popular LwM2M servers don't attach those tags, so in reality
2633 * I have no way of properly matching query with BLOCK2 option to a previous query.
2634 * Therefore we can only support one ongoing BLOCK2 transfer and assume all BLOCK2 requests
2635 * are part of currently ongoing one.
2636 */
2637 return ongoing_block2_tx;
2638 }
2639
clear_ongoing_block2_tx(void)2640 static void clear_ongoing_block2_tx(void)
2641 {
2642 if (ongoing_block2_tx) {
2643 LOG_DBG("clear");
2644 lwm2m_reset_message(ongoing_block2_tx, true);
2645 ongoing_block2_tx = NULL;
2646 }
2647 }
2648
handle_ongoing_block2_tx(struct lwm2m_message * msg,struct coap_packet * cpkt)2649 static void handle_ongoing_block2_tx(struct lwm2m_message *msg, struct coap_packet *cpkt)
2650 {
2651 #if defined(CONFIG_LWM2M_COAP_BLOCK_TRANSFER)
2652 int r;
2653 bool more;
2654 uint32_t block;
2655 enum coap_block_size block_size;
2656
2657 r = coap_get_block2_option(cpkt, &more, &block);
2658 if (r < 0) {
2659 LOG_ERR("Failed to parse BLOCK2");
2660 return;
2661 }
2662
2663 block_size = coap_bytes_to_block_size(r);
2664 msg->in.in_cpkt = cpkt;
2665
2666 r = build_msg_block_for_send(msg, block, block_size);
2667 if (r < 0) {
2668 clear_ongoing_block2_tx();
2669 LOG_ERR("Unable to build next block of lwm2m message! r=%d", r);
2670 return;
2671 }
2672
2673 r = lwm2m_send_message_async(msg);
2674 if (r < 0) {
2675 clear_ongoing_block2_tx();
2676 LOG_ERR("Unable to send next block of lwm2m message!");
2677 return;
2678 }
2679 #endif
2680 }
2681
lwm2m_udp_receive(struct lwm2m_ctx * client_ctx,uint8_t * buf,uint16_t buf_len,struct sockaddr * from_addr)2682 void lwm2m_udp_receive(struct lwm2m_ctx *client_ctx, uint8_t *buf, uint16_t buf_len,
2683 struct sockaddr *from_addr)
2684 {
2685 struct lwm2m_message *msg = NULL;
2686 struct coap_pending *pending;
2687 struct coap_reply *reply;
2688 struct coap_packet response;
2689 int r;
2690 #if defined(CONFIG_LWM2M_COAP_BLOCK_TRANSFER)
2691 bool more_blocks = false;
2692 uint32_t block_num;
2693 uint32_t last_block_num;
2694 #endif
2695 bool has_block2;
2696
2697 r = coap_packet_parse(&response, buf, buf_len, NULL, 0);
2698 if (r < 0) {
2699 LOG_ERR("Invalid data received (err:%d)", r);
2700 return;
2701 }
2702
2703 has_block2 = coap_get_option_int(&response, COAP_OPTION_BLOCK2) > 0 ? true : false;
2704 pending = coap_pending_received(&response, client_ctx->pendings,
2705 ARRAY_SIZE(client_ctx->pendings));
2706 if (pending && coap_header_get_type(&response) == COAP_TYPE_ACK) {
2707 msg = find_msg(pending, NULL);
2708 if (msg == NULL) {
2709 LOG_DBG("Orphaned pending %p.", pending);
2710 coap_pending_clear(pending);
2711 return;
2712 }
2713
2714 msg->acknowledged = true;
2715
2716 if (msg->reply == NULL) {
2717 /* No response expected, release the message. */
2718 lwm2m_reset_message(msg, true);
2719 return;
2720 }
2721
2722 /* If the original message was a request and an empty
2723 * ACK was received, expect separate response later.
2724 */
2725 if ((msg->code >= COAP_METHOD_GET) && (msg->code <= COAP_METHOD_DELETE) &&
2726 (coap_header_get_code(&response) == COAP_CODE_EMPTY)) {
2727 LOG_DBG("Empty ACK, expect separate response.");
2728 return;
2729 }
2730 }
2731
2732 reply = coap_response_received(&response, from_addr, client_ctx->replies,
2733 ARRAY_SIZE(client_ctx->replies));
2734 if (reply) {
2735 msg = find_msg(NULL, reply);
2736
2737 if (coap_header_get_type(&response) == COAP_TYPE_CON) {
2738 r = lwm2m_send_empty_ack(client_ctx, coap_header_get_id(&response));
2739 if (r < 0) {
2740 LOG_ERR("Error transmitting ACK");
2741 }
2742 }
2743
2744 #if defined(CONFIG_LWM2M_COAP_BLOCK_TRANSFER)
2745 if (coap_header_get_code(&response) == COAP_RESPONSE_CODE_CONTINUE) {
2746
2747 r = coap_get_block1_option(&response, &more_blocks, &block_num);
2748 if (r < 0) {
2749 LOG_ERR("Missing block1 option in response with continue");
2750 return;
2751 }
2752
2753 enum coap_block_size block_size = coap_bytes_to_block_size(r);
2754
2755 if (r != CONFIG_LWM2M_COAP_BLOCK_SIZE) {
2756 LOG_WRN("Server requests different block size: ignore");
2757 }
2758
2759 if (!more_blocks) {
2760 lwm2m_reset_message(msg, true);
2761 LOG_ERR("Missing more flag in response with continue");
2762 return;
2763 }
2764
2765 last_block_num = msg->out.block_ctx->current /
2766 coap_block_size_to_bytes(block_size);
2767 if (last_block_num > block_num) {
2768 LOG_INF("Block already sent: ignore");
2769 return;
2770 } else if (last_block_num < block_num) {
2771 LOG_WRN("Requested block out of order");
2772 return;
2773 }
2774
2775 r = build_msg_block_for_send(msg, block_num + 1, block_size);
2776 if (r < 0) {
2777 lwm2m_reset_message(msg, true);
2778 LOG_ERR("Unable to build next block of lwm2m message!");
2779 return;
2780 }
2781
2782 r = lwm2m_send_message_async(msg);
2783 if (r < 0) {
2784 lwm2m_reset_message(msg, true);
2785 LOG_ERR("Unable to send next block of lwm2m message!");
2786 return;
2787 }
2788
2789 /* skip release as message was reused for new block */
2790 LOG_DBG("Block # %d sent", block_num + 1);
2791 return;
2792 }
2793 #endif
2794
2795 /* skip release if reply->user_data has error condition */
2796 if (reply && reply->user_data == (void *)COAP_REPLY_STATUS_ERROR) {
2797 /* reset reply->user_data for next time */
2798 reply->user_data = (void *)COAP_REPLY_STATUS_NONE;
2799 LOG_DBG("reply %p NOT removed", reply);
2800 return;
2801 }
2802
2803 /* free up msg resources */
2804 if (msg) {
2805 lwm2m_reset_message(msg, true);
2806 }
2807
2808 LOG_DBG("reply %p handled and removed", reply);
2809 return;
2810 }
2811
2812 if (coap_header_get_type(&response) == COAP_TYPE_CON) {
2813 if (has_block2 && IS_ENABLED(CONFIG_LWM2M_COAP_BLOCK_TRANSFER)) {
2814 msg = find_ongoing_block2_tx();
2815 if (msg) {
2816 return handle_ongoing_block2_tx(msg, &response);
2817 }
2818 return;
2819 }
2820
2821 /* Clear out existing Block2 transfers when new requests come */
2822 clear_ongoing_block2_tx();
2823
2824 msg = lwm2m_get_message(client_ctx);
2825 if (!msg) {
2826 LOG_ERR("Unable to get a lwm2m message!");
2827 return;
2828 }
2829
2830 /* Create a response message if we reach this point */
2831 msg->type = COAP_TYPE_ACK;
2832 msg->code = coap_header_get_code(&response);
2833 msg->mid = coap_header_get_id(&response);
2834 /* skip token generation by default */
2835 msg->tkl = 0;
2836
2837 client_ctx->processed_req = msg;
2838
2839 lwm2m_registry_lock();
2840 /* process the response to this request */
2841 r = handle_request(&response, msg);
2842 lwm2m_registry_unlock();
2843 if (r < 0) {
2844 return;
2845 }
2846
2847 if (msg->acknowledged) {
2848 r = lwm2m_response_promote_to_con(msg);
2849 if (r < 0) {
2850 LOG_ERR("Failed to promote response to CON: %d", r);
2851 lwm2m_reset_message(msg, true);
2852 return;
2853 }
2854 }
2855
2856 client_ctx->processed_req = NULL;
2857 lwm2m_send_message_async(msg);
2858 } else {
2859 LOG_DBG("No handler for response");
2860 }
2861 }
2862
notify_message_timeout_cb(struct lwm2m_message * msg)2863 static void notify_message_timeout_cb(struct lwm2m_message *msg)
2864 {
2865 if (msg->ctx != NULL) {
2866 struct observe_node *obs;
2867 struct lwm2m_ctx *client_ctx = msg->ctx;
2868 sys_snode_t *prev_node = NULL;
2869
2870 obs = engine_observe_node_discover(&client_ctx->observer, &prev_node, NULL,
2871 msg->token, msg->tkl);
2872
2873 if (obs) {
2874 obs->active_notify = NULL;
2875 if (client_ctx->observe_cb) {
2876 client_ctx->observe_cb(LWM2M_OBSERVE_EVENT_NOTIFY_TIMEOUT,
2877 &msg->path, msg->reply->user_data);
2878 }
2879
2880 lwm2m_rd_client_timeout(client_ctx);
2881 }
2882 }
2883
2884 LOG_ERR("Notify Message Timed Out : %p", msg);
2885 }
2886
lwm2m_read_first_path_ptr(sys_slist_t * lwm2m_path_list)2887 static struct lwm2m_obj_path *lwm2m_read_first_path_ptr(sys_slist_t *lwm2m_path_list)
2888 {
2889 struct lwm2m_obj_path_list *entry;
2890
2891 entry = (struct lwm2m_obj_path_list *)sys_slist_peek_head(lwm2m_path_list);
2892 return &entry->path;
2893 }
2894
notify_cached_pending_data_trig(struct observe_node * obs)2895 static void notify_cached_pending_data_trig(struct observe_node *obs)
2896 {
2897 #if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT)
2898 struct lwm2m_time_series_resource *cached_data;
2899 struct lwm2m_obj_path_list *entry;
2900
2901 SYS_SLIST_FOR_EACH_CONTAINER(&obs->path_list, entry, node) {
2902 cached_data = lwm2m_cache_entry_get_by_object(&entry->path);
2903 if (!cached_data || lwm2m_cache_size(cached_data) == 0) {
2904 continue;
2905 }
2906
2907 /* Trig next send by iMin */
2908 lwm2m_notify_observer_path(&entry->path);
2909 }
2910 #endif
2911 }
2912
notify_message_reply_cb(const struct coap_packet * response,struct coap_reply * reply,const struct sockaddr * from)2913 static int notify_message_reply_cb(const struct coap_packet *response, struct coap_reply *reply,
2914 const struct sockaddr *from)
2915 {
2916 int ret = 0;
2917 uint8_t type, code;
2918 struct lwm2m_message *msg;
2919 struct observe_node *obs;
2920 sys_snode_t *prev_node = NULL;
2921
2922 type = coap_header_get_type(response);
2923 code = coap_header_get_code(response);
2924
2925 LOG_DBG("NOTIFY ACK type:%u code:%d.%d reply_token:'%s'", type,
2926 COAP_RESPONSE_CODE_CLASS(code), COAP_RESPONSE_CODE_DETAIL(code),
2927 sprint_token(reply->token, reply->tkl));
2928
2929 msg = find_msg(NULL, reply);
2930
2931 /* remove observer on COAP_TYPE_RESET */
2932 if (type == COAP_TYPE_RESET) {
2933 if (reply->tkl > 0) {
2934 ret = engine_remove_observer_by_token(msg->ctx, reply->token, reply->tkl);
2935 if (ret) {
2936 LOG_ERR("remove observe error: %d", ret);
2937 }
2938 } else {
2939 LOG_ERR("notify reply missing token -- ignored.");
2940 }
2941 } else {
2942 obs = engine_observe_node_discover(&msg->ctx->observer, &prev_node, NULL,
2943 reply->token, reply->tkl);
2944
2945 if (obs) {
2946 obs->active_notify = NULL;
2947 if (msg->ctx->observe_cb) {
2948 msg->ctx->observe_cb(LWM2M_OBSERVE_EVENT_NOTIFY_ACK,
2949 lwm2m_read_first_path_ptr(&obs->path_list),
2950 reply->user_data);
2951 }
2952 notify_cached_pending_data_trig(obs);
2953 }
2954 }
2955
2956 return 0;
2957 }
2958
do_send_op(struct lwm2m_message * msg,uint16_t content_format,sys_slist_t * lwm2m_path_list)2959 static int do_send_op(struct lwm2m_message *msg, uint16_t content_format,
2960 sys_slist_t *lwm2m_path_list)
2961 {
2962 switch (content_format) {
2963 #if defined(CONFIG_LWM2M_RW_SENML_JSON_SUPPORT)
2964 case LWM2M_FORMAT_APP_SEML_JSON:
2965 return do_send_op_senml_json(msg, lwm2m_path_list);
2966 #endif
2967
2968 #if defined(CONFIG_LWM2M_RW_SENML_CBOR_SUPPORT)
2969 case LWM2M_FORMAT_APP_SENML_CBOR:
2970 return do_send_op_senml_cbor(msg, lwm2m_path_list);
2971 #endif
2972
2973 default:
2974 LOG_ERR("Unsupported content-format for /dp: %u", content_format);
2975 return -ENOMSG;
2976 }
2977 }
2978
lwm2m_timeseries_data_rebuild(struct lwm2m_message * msg,int error_code)2979 static bool lwm2m_timeseries_data_rebuild(struct lwm2m_message *msg, int error_code)
2980 {
2981 #if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT)
2982 struct lwm2m_cache_read_info *cache_temp;
2983
2984 if (error_code != -ENOMEM) {
2985 return false;
2986 }
2987
2988 cache_temp = msg->cache_info;
2989
2990 if (!cache_temp || !cache_temp->entry_size) {
2991 return false;
2992 }
2993
2994 /* Put Ring buffer back to original */
2995 for (int i = 0; i < cache_temp->entry_size; i++) {
2996 cache_temp->read_info[i].cache_data->rb.get_head =
2997 cache_temp->read_info[i].original_get_head;
2998 cache_temp->read_info[i].cache_data->rb.get_tail =
2999 cache_temp->read_info[i].original_get_tail;
3000 cache_temp->read_info[i].cache_data->rb.get_base =
3001 cache_temp->read_info[i].original_get_base;
3002 }
3003
3004 if (cache_temp->entry_limit) {
3005 /* Limited number of message build fail also */
3006 return false;
3007 }
3008
3009 /* Limit re-build entry count */
3010 cache_temp->entry_limit = LWM2M_LIMITED_TIMESERIES_RESOURCE_COUNT / cache_temp->entry_size;
3011 cache_temp->entry_size = 0;
3012
3013 lwm2m_reset_message(msg, false);
3014 LOG_INF("Try re-buildbuild again with limited cache size %d", cache_temp->entry_limit);
3015 return true;
3016 #else
3017 return false;
3018 #endif
3019 }
3020
generate_notify_message(struct lwm2m_ctx * ctx,struct observe_node * obs,void * user_data)3021 int generate_notify_message(struct lwm2m_ctx *ctx, struct observe_node *obs, void *user_data)
3022 {
3023 struct lwm2m_message *msg;
3024 struct lwm2m_engine_obj_inst *obj_inst;
3025 struct lwm2m_obj_path *path;
3026 int ret = 0;
3027 #if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT)
3028 struct lwm2m_cache_read_info cache_temp_info;
3029
3030 cache_temp_info.entry_size = 0;
3031 cache_temp_info.entry_limit = 0;
3032 #endif
3033
3034 msg = lwm2m_get_message(ctx);
3035 if (!msg) {
3036 LOG_ERR("Unable to get a lwm2m message!");
3037 return -ENOMEM;
3038 }
3039 msg_init:
3040
3041 if (!obs->composite) {
3042 path = lwm2m_read_first_path_ptr(&obs->path_list);
3043 if (!path) {
3044 LOG_ERR("Observation node not include path");
3045 ret = -EINVAL;
3046 goto cleanup;
3047 }
3048 /* copy path */
3049 memcpy(&msg->path, path, sizeof(struct lwm2m_obj_path));
3050 LOG_DBG("[%s] NOTIFY MSG START: %u/%u/%u(%u) token:'%s' [%s] %lld",
3051 obs->resource_update ? "MANUAL" : "AUTO", path->obj_id, path->obj_inst_id,
3052 path->res_id, path->level, sprint_token(obs->token, obs->tkl),
3053 lwm2m_sprint_ip_addr(&ctx->remote_addr), (long long)k_uptime_get());
3054
3055 obj_inst = get_engine_obj_inst(path->obj_id, path->obj_inst_id);
3056 if (!obj_inst) {
3057 LOG_ERR("unable to get engine obj for %u/%u", path->obj_id,
3058 path->obj_inst_id);
3059 ret = -EINVAL;
3060 goto cleanup;
3061 }
3062 } else {
3063 LOG_DBG("[%s] NOTIFY MSG START: (Composite)) token:'%s' [%s] %lld",
3064 obs->resource_update ? "MANUAL" : "AUTO",
3065 sprint_token(obs->token, obs->tkl), lwm2m_sprint_ip_addr(&ctx->remote_addr),
3066 (long long)k_uptime_get());
3067 }
3068
3069 msg->operation = LWM2M_OP_READ;
3070 msg->type = COAP_TYPE_CON;
3071 msg->code = COAP_RESPONSE_CODE_CONTENT;
3072 msg->mid = coap_next_id();
3073 msg->token = obs->token;
3074 msg->tkl = obs->tkl;
3075 msg->reply_cb = notify_message_reply_cb;
3076 msg->message_timeout_cb = notify_message_timeout_cb;
3077 msg->out.out_cpkt = &msg->cpkt;
3078
3079 ret = lwm2m_init_message(msg);
3080 if (ret < 0) {
3081 LOG_ERR("Unable to init lwm2m message! (err: %d)", ret);
3082 goto cleanup;
3083 }
3084 #if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT)
3085 msg->cache_info = &cache_temp_info;
3086 #endif
3087
3088 /* lwm2m_init_message() cleans the coap reply fields, so we assign our data here */
3089 msg->reply->user_data = user_data;
3090
3091 /* each notification should increment the obs counter */
3092 obs->counter++;
3093 ret = coap_append_option_int(&msg->cpkt, COAP_OPTION_OBSERVE, obs->counter);
3094 if (ret < 0) {
3095 LOG_ERR("OBSERVE option error: %d", ret);
3096 goto cleanup;
3097 }
3098
3099 /* set the output writer */
3100 select_writer(&msg->out, obs->format);
3101 if (obs->composite) {
3102 /* Use do send which actually do Composite read operation */
3103 ret = do_send_op(msg, obs->format, &obs->path_list);
3104 } else {
3105 ret = do_read_op(msg, obs->format);
3106 }
3107
3108 if (ret < 0) {
3109 if (lwm2m_timeseries_data_rebuild(msg, ret)) {
3110 /* Message Build fail by ENOMEM and data include timeseries data.
3111 * Try rebuild message again by limiting timeseries data entry lengths.
3112 */
3113 goto msg_init;
3114 }
3115 LOG_ERR("error in multi-format read (err:%d)", ret);
3116 goto cleanup;
3117 }
3118
3119 obs->active_notify = msg;
3120 obs->resource_update = false;
3121 lwm2m_information_interface_send(msg);
3122 #if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT)
3123 msg->cache_info = NULL;
3124 #endif
3125
3126 LOG_DBG("NOTIFY MSG: SENT");
3127 return 0;
3128
3129 cleanup:
3130 lwm2m_reset_message(msg, true);
3131 return ret;
3132 }
3133
lwm2m_perform_composite_read_root(struct lwm2m_message * msg,uint8_t * num_read)3134 static int lwm2m_perform_composite_read_root(struct lwm2m_message *msg, uint8_t *num_read)
3135 {
3136 int ret;
3137 struct lwm2m_engine_obj *obj;
3138 struct lwm2m_engine_obj_inst *obj_inst;
3139 sys_slist_t *engine_obj_list = lwm2m_engine_obj_list();
3140
3141 SYS_SLIST_FOR_EACH_CONTAINER(engine_obj_list, obj, node) {
3142 /* Security obj MUST NOT be part of registration message */
3143 if (obj->obj_id == LWM2M_OBJECT_SECURITY_ID) {
3144 continue;
3145 }
3146
3147 msg->path.level = 1;
3148 msg->path.obj_id = obj->obj_id;
3149
3150 obj_inst = next_engine_obj_inst(msg->path.obj_id, -1);
3151
3152 if (!obj_inst) {
3153 continue;
3154 }
3155
3156 ret = lwm2m_perform_read_object_instance(msg, obj_inst, num_read);
3157 if (ret == -ENOMEM) {
3158 return ret;
3159 }
3160 }
3161 return 0;
3162 }
3163
lwm2m_perform_composite_read_op(struct lwm2m_message * msg,uint16_t content_format,sys_slist_t * lwm2m_path_list)3164 int lwm2m_perform_composite_read_op(struct lwm2m_message *msg, uint16_t content_format,
3165 sys_slist_t *lwm2m_path_list)
3166 {
3167 struct lwm2m_engine_obj_inst *obj_inst = NULL;
3168 struct lwm2m_obj_path_list *entry;
3169 int ret = 0;
3170 uint8_t num_read = 0U;
3171
3172 /* set output content-format */
3173 ret = coap_append_option_int(msg->out.out_cpkt, COAP_OPTION_CONTENT_FORMAT, content_format);
3174 if (ret < 0) {
3175 LOG_ERR("Error setting response content-format: %d", ret);
3176 return ret;
3177 }
3178
3179 ret = coap_packet_append_payload_marker(msg->out.out_cpkt);
3180 if (ret < 0) {
3181 LOG_ERR("Error appending payload marker: %d", ret);
3182 return ret;
3183 }
3184
3185 /* Add object start mark */
3186 engine_put_begin(&msg->out, &msg->path);
3187
3188 /* Read resource from path */
3189 SYS_SLIST_FOR_EACH_CONTAINER(lwm2m_path_list, entry, node) {
3190 /* Copy path to message path */
3191 memcpy(&msg->path, &entry->path, sizeof(struct lwm2m_obj_path));
3192
3193 if (msg->path.level >= LWM2M_PATH_LEVEL_OBJECT_INST) {
3194 obj_inst = get_engine_obj_inst(msg->path.obj_id, msg->path.obj_inst_id);
3195 } else if (msg->path.level == LWM2M_PATH_LEVEL_OBJECT) {
3196 /* find first obj_inst with path's obj_id */
3197 obj_inst = next_engine_obj_inst(msg->path.obj_id, -1);
3198 } else {
3199 /* Read root Path */
3200 ret = lwm2m_perform_composite_read_root(msg, &num_read);
3201 if (ret == -ENOMEM) {
3202 LOG_ERR("Supported message size is too small for read root");
3203 return ret;
3204 }
3205 break;
3206 }
3207
3208 if (!obj_inst) {
3209 continue;
3210 }
3211
3212 ret = lwm2m_perform_read_object_instance(msg, obj_inst, &num_read);
3213 if (ret == -ENOMEM) {
3214 return ret;
3215 }
3216 }
3217 /* did not read anything even if we should have - on single item */
3218 if (num_read == 0U) {
3219 return -ENOENT;
3220 }
3221
3222 /* Add object end mark */
3223 if (engine_put_end(&msg->out, &msg->path) < 0) {
3224 return -ENOMEM;
3225 }
3226
3227 return 0;
3228 }
3229
lwm2m_parse_peerinfo(char * url,struct lwm2m_ctx * client_ctx,bool is_firmware_uri)3230 int lwm2m_parse_peerinfo(char *url, struct lwm2m_ctx *client_ctx, bool is_firmware_uri)
3231 {
3232 struct http_parser_url parser;
3233 #if defined(CONFIG_LWM2M_DNS_SUPPORT)
3234 struct zsock_addrinfo *res, hints = {0};
3235 #endif
3236 int ret;
3237 uint16_t off, len;
3238 uint8_t tmp;
3239
3240 LOG_DBG("Parse url: %s", url);
3241
3242 http_parser_url_init(&parser);
3243 ret = http_parser_parse_url(url, strlen(url), 0, &parser);
3244 if (ret < 0) {
3245 LOG_ERR("Invalid url: %s", url);
3246 return -ENOTSUP;
3247 }
3248
3249 off = parser.field_data[UF_SCHEMA].off;
3250 len = parser.field_data[UF_SCHEMA].len;
3251
3252 /* check for supported protocol */
3253 if (strncmp(url + off, "coaps", len) != 0) {
3254 return -EPROTONOSUPPORT;
3255 }
3256
3257 /* check for DTLS requirement */
3258 client_ctx->use_dtls = false;
3259 if (len == 5U && strncmp(url + off, "coaps", len) == 0) {
3260 #if defined(CONFIG_LWM2M_DTLS_SUPPORT)
3261 client_ctx->use_dtls = true;
3262 #else
3263 return -EPROTONOSUPPORT;
3264 #endif /* CONFIG_LWM2M_DTLS_SUPPORT */
3265 }
3266
3267 if (!(parser.field_set & (1 << UF_PORT))) {
3268 if (is_firmware_uri && client_ctx->use_dtls) {
3269 /* Set to default coaps firmware update port */
3270 parser.port = CONFIG_LWM2M_FIRMWARE_PORT_SECURE;
3271 } else if (is_firmware_uri) {
3272 /* Set to default coap firmware update port */
3273 parser.port = CONFIG_LWM2M_FIRMWARE_PORT_NONSECURE;
3274 } else {
3275 /* Set to default LwM2M server port */
3276 parser.port = CONFIG_LWM2M_PEER_PORT;
3277 }
3278 }
3279
3280 off = parser.field_data[UF_HOST].off;
3281 len = parser.field_data[UF_HOST].len;
3282
3283 /* truncate host portion */
3284 tmp = url[off + len];
3285 url[off + len] = '\0';
3286
3287 /* initialize remote_addr */
3288 (void)memset(&client_ctx->remote_addr, 0, sizeof(client_ctx->remote_addr));
3289
3290 /* try and set IP address directly */
3291 client_ctx->remote_addr.sa_family = AF_INET6;
3292 ret = net_addr_pton(AF_INET6, url + off,
3293 &((struct sockaddr_in6 *)&client_ctx->remote_addr)->sin6_addr);
3294 /* Try to parse again using AF_INET */
3295 if (ret < 0) {
3296 client_ctx->remote_addr.sa_family = AF_INET;
3297 ret = net_addr_pton(AF_INET, url + off,
3298 &((struct sockaddr_in *)&client_ctx->remote_addr)->sin_addr);
3299 }
3300
3301 if (ret < 0) {
3302 #if defined(CONFIG_LWM2M_DNS_SUPPORT)
3303 #if defined(CONFIG_NET_IPV6) && defined(CONFIG_NET_IPV4)
3304 hints.ai_family = AF_UNSPEC;
3305 #elif defined(CONFIG_NET_IPV6)
3306 hints.ai_family = AF_INET6;
3307 #elif defined(CONFIG_NET_IPV4)
3308 hints.ai_family = AF_INET;
3309 #else
3310 hints.ai_family = AF_UNSPEC;
3311 #endif /* defined(CONFIG_NET_IPV6) && defined(CONFIG_NET_IPV4) */
3312 hints.ai_socktype = SOCK_DGRAM;
3313 ret = zsock_getaddrinfo(url + off, NULL, &hints, &res);
3314 if (ret != 0) {
3315 LOG_ERR("Unable to resolve address");
3316 /* DNS error codes don't align with normal errors */
3317 ret = -ENOENT;
3318 goto cleanup;
3319 }
3320
3321 memcpy(&client_ctx->remote_addr, res->ai_addr, sizeof(client_ctx->remote_addr));
3322 client_ctx->remote_addr.sa_family = res->ai_family;
3323 zsock_freeaddrinfo(res);
3324 #if defined(CONFIG_LWM2M_DTLS_SUPPORT)
3325 /** copy url pointer to be used in socket */
3326 client_ctx->desthostname = url + off;
3327 client_ctx->desthostnamelen = len;
3328 client_ctx->hostname_verify = true;
3329 #endif
3330
3331 #else
3332 goto cleanup;
3333 #endif /* CONFIG_LWM2M_DNS_SUPPORT */
3334 }
3335
3336 /* set port */
3337 if (client_ctx->remote_addr.sa_family == AF_INET6) {
3338 net_sin6(&client_ctx->remote_addr)->sin6_port = htons(parser.port);
3339 } else if (client_ctx->remote_addr.sa_family == AF_INET) {
3340 net_sin(&client_ctx->remote_addr)->sin_port = htons(parser.port);
3341 } else {
3342 ret = -EPROTONOSUPPORT;
3343 }
3344
3345 cleanup:
3346 /* restore host separator */
3347 url[off + len] = tmp;
3348 return ret;
3349 }
3350
do_composite_read_op_for_parsed_list(struct lwm2m_message * msg,uint16_t content_format,sys_slist_t * path_list)3351 int do_composite_read_op_for_parsed_list(struct lwm2m_message *msg, uint16_t content_format,
3352 sys_slist_t *path_list)
3353 {
3354 struct lwm2m_obj_path_list *entry;
3355
3356 /* Check access rights */
3357 SYS_SLIST_FOR_EACH_CONTAINER(path_list, entry, node) {
3358 if (entry->path.level > LWM2M_PATH_LEVEL_NONE &&
3359 entry->path.obj_id == LWM2M_OBJECT_SECURITY_ID && !msg->ctx->bootstrap_mode) {
3360 return -EACCES;
3361 }
3362 }
3363
3364 switch (content_format) {
3365
3366 #if defined(CONFIG_LWM2M_RW_SENML_JSON_SUPPORT)
3367 case LWM2M_FORMAT_APP_SEML_JSON:
3368 return do_composite_read_op_for_parsed_list_senml_json(msg, path_list);
3369 #endif
3370
3371 #if defined(CONFIG_LWM2M_RW_SENML_CBOR_SUPPORT)
3372 case LWM2M_FORMAT_APP_SENML_CBOR:
3373 return do_composite_read_op_for_parsed_path_senml_cbor(msg, path_list);
3374 #endif
3375
3376 default:
3377 LOG_ERR("Unsupported content-format: %u", content_format);
3378 return -ENOMSG;
3379 }
3380 }
3381
3382 #if defined(CONFIG_LWM2M_SERVER_OBJECT_VERSION_1_1)
do_send_reply_cb(const struct coap_packet * response,struct coap_reply * reply,const struct sockaddr * from)3383 static int do_send_reply_cb(const struct coap_packet *response, struct coap_reply *reply,
3384 const struct sockaddr *from)
3385 {
3386 uint8_t code;
3387 struct lwm2m_message *msg = (struct lwm2m_message *)reply->user_data;
3388
3389 code = coap_header_get_code(response);
3390 LOG_DBG("Send callback (code:%u.%u)", COAP_RESPONSE_CODE_CLASS(code),
3391 COAP_RESPONSE_CODE_DETAIL(code));
3392
3393 if (code == COAP_RESPONSE_CODE_CHANGED) {
3394 LOG_INF("Send done!");
3395 if (msg && msg->send_status_cb) {
3396 msg->send_status_cb(LWM2M_SEND_STATUS_SUCCESS);
3397 }
3398 return 0;
3399 }
3400
3401 LOG_ERR("Failed with code %u.%u. Not Retrying.", COAP_RESPONSE_CODE_CLASS(code),
3402 COAP_RESPONSE_CODE_DETAIL(code));
3403
3404 if (msg && msg->send_status_cb) {
3405 msg->send_status_cb(LWM2M_SEND_STATUS_FAILURE);
3406 }
3407
3408 return 0;
3409 }
3410
do_send_timeout_cb(struct lwm2m_message * msg)3411 static void do_send_timeout_cb(struct lwm2m_message *msg)
3412 {
3413 if (msg->send_status_cb) {
3414 msg->send_status_cb(LWM2M_SEND_STATUS_TIMEOUT);
3415 }
3416 LOG_WRN("Send Timeout");
3417 lwm2m_rd_client_timeout(msg->ctx);
3418 }
3419 #endif
3420
3421 #if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT)
init_next_pending_timeseries_data(struct lwm2m_cache_read_info * cache_temp,sys_slist_t * lwm2m_path_list,sys_slist_t * lwm2m_path_free_list)3422 static bool init_next_pending_timeseries_data(struct lwm2m_cache_read_info *cache_temp,
3423 sys_slist_t *lwm2m_path_list,
3424 sys_slist_t *lwm2m_path_free_list)
3425 {
3426 uint32_t bytes_available = 0;
3427
3428 /* Check do we have still pending data to send */
3429 for (int i = 0; i < cache_temp->entry_size; i++) {
3430 if (ring_buf_is_empty(&cache_temp->read_info[i].cache_data->rb)) {
3431 /* Skip Empty cached buffers */
3432 continue;
3433 }
3434
3435 /* Add to linked list */
3436 if (lwm2m_engine_add_path_to_list(lwm2m_path_list, lwm2m_path_free_list,
3437 &cache_temp->read_info[i].cache_data->path)) {
3438 return false;
3439 }
3440
3441 bytes_available += ring_buf_size_get(&cache_temp->read_info[i].cache_data->rb);
3442 }
3443
3444 if (bytes_available == 0) {
3445 return false;
3446 }
3447
3448 LOG_INF("Allocate a new message for pending data %u", bytes_available);
3449 cache_temp->entry_size = 0;
3450 cache_temp->entry_limit = 0;
3451 return true;
3452 }
3453 #endif
3454
lwm2m_send_cb(struct lwm2m_ctx * ctx,const struct lwm2m_obj_path path_list[],uint8_t path_list_size,lwm2m_send_cb_t reply_cb)3455 int lwm2m_send_cb(struct lwm2m_ctx *ctx, const struct lwm2m_obj_path path_list[],
3456 uint8_t path_list_size, lwm2m_send_cb_t reply_cb)
3457 {
3458 #if defined(CONFIG_LWM2M_SERVER_OBJECT_VERSION_1_1)
3459 struct lwm2m_message *msg;
3460 int ret;
3461 uint16_t content_format;
3462
3463 /* Path list buffer */
3464 struct lwm2m_obj_path_list lwm2m_path_list_buf[CONFIG_LWM2M_COMPOSITE_PATH_LIST_SIZE];
3465 sys_slist_t lwm2m_path_list;
3466 sys_slist_t lwm2m_path_free_list;
3467 #if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT)
3468 struct lwm2m_cache_read_info cache_temp_info;
3469
3470 cache_temp_info.entry_size = 0;
3471 cache_temp_info.entry_limit = 0;
3472 #endif
3473
3474 /* Validate Connection */
3475 if (!lwm2m_rd_client_is_registred(ctx)) {
3476 return -EPERM;
3477 }
3478
3479 if (lwm2m_server_get_mute_send(ctx->srv_obj_inst)) {
3480 LOG_WRN("Send operation is muted by server");
3481 return -EPERM;
3482 }
3483
3484 /* Init list */
3485 lwm2m_engine_path_list_init(&lwm2m_path_list, &lwm2m_path_free_list, lwm2m_path_list_buf,
3486 CONFIG_LWM2M_COMPOSITE_PATH_LIST_SIZE);
3487
3488 if (path_list_size > CONFIG_LWM2M_COMPOSITE_PATH_LIST_SIZE) {
3489 return -E2BIG;
3490 }
3491
3492 if (IS_ENABLED(CONFIG_LWM2M_RW_SENML_CBOR_SUPPORT)) {
3493 content_format = LWM2M_FORMAT_APP_SENML_CBOR;
3494 } else if (IS_ENABLED(CONFIG_LWM2M_RW_SENML_JSON_SUPPORT)) {
3495 content_format = LWM2M_FORMAT_APP_SEML_JSON;
3496 } else {
3497 LOG_WRN("SenML CBOR or JSON is not supported");
3498 return -ENOTSUP;
3499 }
3500
3501 /* Parse Path to internal used object path format */
3502 for (int i = 0; i < path_list_size; i++) {
3503 /* Add to linked list */
3504 if (lwm2m_engine_add_path_to_list(&lwm2m_path_list, &lwm2m_path_free_list,
3505 &path_list[i])) {
3506 return -1;
3507 }
3508 }
3509 /* Clear path which are part are part of recursive path /1 will include /1/0/1 */
3510 lwm2m_engine_clear_duplicate_path(&lwm2m_path_list, &lwm2m_path_free_list);
3511 lwm2m_registry_lock();
3512 #if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT)
3513 msg_alloc:
3514 #endif
3515 /* Allocate Message buffer */
3516 msg = lwm2m_get_message(ctx);
3517 if (!msg) {
3518 lwm2m_registry_unlock();
3519 LOG_ERR("Unable to get a lwm2m message!");
3520 return -ENOMEM;
3521 }
3522
3523 msg_init:
3524
3525 msg->type = COAP_TYPE_CON;
3526 msg->reply_cb = do_send_reply_cb;
3527 msg->message_timeout_cb = do_send_timeout_cb;
3528 msg->code = COAP_METHOD_POST;
3529 msg->mid = coap_next_id();
3530 msg->tkl = LWM2M_MSG_TOKEN_GENERATE_NEW;
3531 msg->out.out_cpkt = &msg->cpkt;
3532
3533 ret = lwm2m_init_message(msg);
3534 if (ret) {
3535 goto cleanup;
3536 }
3537 #if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT)
3538 msg->cache_info = &cache_temp_info;
3539 #endif
3540
3541 /* Register user callback if defined for confirmation */
3542 if (reply_cb) {
3543 msg->reply->user_data = msg;
3544 msg->send_status_cb = reply_cb;
3545 }
3546
3547 ret = select_writer(&msg->out, content_format);
3548 if (ret) {
3549 goto cleanup;
3550 }
3551
3552 ret = coap_packet_append_option(&msg->cpkt, COAP_OPTION_URI_PATH, LWM2M_DP_CLIENT_URI,
3553 strlen(LWM2M_DP_CLIENT_URI));
3554 if (ret < 0) {
3555 goto cleanup;
3556 }
3557
3558 /* Write requested path data */
3559 ret = do_send_op(msg, content_format, &lwm2m_path_list);
3560 if (ret < 0) {
3561 if (lwm2m_timeseries_data_rebuild(msg, ret)) {
3562 /* Message Build fail by ENOMEM and data include timeseries data.
3563 * Try rebuild message again by limiting timeseries data entry lengths.
3564 */
3565 goto msg_init;
3566 }
3567
3568 LOG_ERR("Send (err:%d)", ret);
3569 goto cleanup;
3570 }
3571 #if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT)
3572 msg->cache_info = NULL;
3573 #endif
3574 LOG_INF("Send op to server (/dp)");
3575 lwm2m_information_interface_send(msg);
3576
3577 #if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT)
3578 if (cache_temp_info.entry_size) {
3579 /* Init Path list for continuous message allocation */
3580 lwm2m_engine_path_list_init(&lwm2m_path_list, &lwm2m_path_free_list,
3581 lwm2m_path_list_buf,
3582 CONFIG_LWM2M_COMPOSITE_PATH_LIST_SIZE);
3583
3584 if (init_next_pending_timeseries_data(&cache_temp_info, &lwm2m_path_list,
3585 &lwm2m_path_free_list)) {
3586 goto msg_alloc;
3587 }
3588 }
3589 #endif
3590 lwm2m_registry_unlock();
3591 return 0;
3592 cleanup:
3593 lwm2m_registry_unlock();
3594 lwm2m_reset_message(msg, true);
3595 return ret;
3596 #else
3597 LOG_WRN("LwM2M send is only supported for CONFIG_LWM2M_SERVER_OBJECT_VERSION_1_1");
3598 return -ENOTSUP;
3599 #endif
3600 }
3601