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