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