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