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