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