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