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 * Copyright (c) 2015, Yanzi Networks AB.
10 * All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. Neither the name of the copyright holder nor the names of its
21 * contributors may be used to endorse or promote products derived
22 * from this software without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
27 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
28 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
33 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
35 * OF THE POSSIBILITY OF SUCH DAMAGE.
36 */
37
38 /*
39 * Original Authors:
40 * Joakim Eriksson <joakime@sics.se>
41 * Niclas Finne <nfi@sics.se>
42 */
43
44 /*
45 * Zephyr Contribution by Michael Scott <michael.scott@linaro.org>
46 * - Zephyr code style changes / code cleanup
47 * - Move to Zephyr APIs where possible
48 * - Convert to Zephyr int/uint types
49 * - Remove engine dependency (replace with writer context)
50 * - Add write int64 function
51 */
52
53 /*
54 * TODO:
55 * - Lots of byte-order API clean up
56 * - Var / parameter type cleanup
57 * - Replace magic #'s with defines
58 */
59
60 #define LOG_MODULE_NAME net_lwm2m_oma_tlv
61 #define LOG_LEVEL CONFIG_LWM2M_LOG_LEVEL
62
63 #include <zephyr/logging/log.h>
64 LOG_MODULE_REGISTER(LOG_MODULE_NAME);
65
66 #include <string.h>
67 #include <stdint.h>
68 #include <zephyr/sys/byteorder.h>
69
70 #include "lwm2m_rw_oma_tlv.h"
71 #include "lwm2m_engine.h"
72 #include "lwm2m_rd_client.h"
73 #include "lwm2m_util.h"
74
75 enum {
76 OMA_TLV_TYPE_OBJECT_INSTANCE = 0,
77 OMA_TLV_TYPE_RESOURCE_INSTANCE = 1,
78 OMA_TLV_TYPE_MULTI_RESOURCE = 2,
79 OMA_TLV_TYPE_RESOURCE = 3
80 };
81
82 struct oma_tlv {
83 uint8_t type;
84 uint16_t id; /* can be 8-bit or 16-bit when serialized */
85 uint32_t length;
86 };
87
get_len_type(const struct oma_tlv * tlv)88 static uint8_t get_len_type(const struct oma_tlv *tlv)
89 {
90 if (tlv->length < 8) {
91 return 0;
92 } else if (tlv->length < 0x100) {
93 return 1;
94 } else if (tlv->length < 0x10000) {
95 return 2;
96 }
97
98 return 3;
99 }
100
tlv_calc_type(uint8_t flags)101 static uint8_t tlv_calc_type(uint8_t flags)
102 {
103 return flags & WRITER_RESOURCE_INSTANCE ?
104 OMA_TLV_TYPE_RESOURCE_INSTANCE : OMA_TLV_TYPE_RESOURCE;
105 }
106
tlv_calc_id(uint8_t flags,struct lwm2m_obj_path * path)107 static uint16_t tlv_calc_id(uint8_t flags, struct lwm2m_obj_path *path)
108 {
109 return flags & WRITER_RESOURCE_INSTANCE ?
110 path->res_inst_id : path->res_id;
111 }
112
tlv_setup(struct oma_tlv * tlv,uint8_t type,uint16_t id,uint32_t buflen)113 static void tlv_setup(struct oma_tlv *tlv, uint8_t type, uint16_t id,
114 uint32_t buflen)
115 {
116 if (tlv) {
117 tlv->type = type;
118 tlv->id = id;
119 tlv->length = buflen;
120 }
121 }
122
oma_tlv_put_u8(struct lwm2m_output_context * out,uint8_t value,bool insert)123 static int oma_tlv_put_u8(struct lwm2m_output_context *out,
124 uint8_t value, bool insert)
125 {
126 struct tlv_out_formatter_data *fd;
127 int ret;
128
129 if (insert) {
130 fd = engine_get_out_user_data(out);
131 if (!fd) {
132 return -EINVAL;
133 }
134
135 ret = buf_insert(CPKT_BUF_WRITE(out->out_cpkt),
136 fd->mark_pos, &value, 1);
137 if (ret < 0) {
138 return ret;
139 }
140
141 fd->mark_pos++;
142 } else {
143 ret = buf_append(CPKT_BUF_WRITE(out->out_cpkt), &value, 1);
144 if (ret < 0) {
145 return ret;
146 }
147 }
148
149 return 0;
150 }
151
oma_tlv_put(const struct oma_tlv * tlv,struct lwm2m_output_context * out,uint8_t * value,bool insert)152 static int oma_tlv_put(const struct oma_tlv *tlv,
153 struct lwm2m_output_context *out, uint8_t *value,
154 bool insert)
155 {
156 size_t pos;
157 int ret, i;
158 uint8_t len_type, tmp;
159
160 /* len_type is the same as number of bytes required for length */
161 len_type = get_len_type(tlv);
162
163 /* first type byte in TLV header */
164 tmp = (tlv->type << 6) |
165 (tlv->id > 255 ? (1 << 5) : 0) |
166 (len_type << 3) |
167 (len_type == 0U ? tlv->length : 0);
168
169 ret = oma_tlv_put_u8(out, tmp, insert);
170 if (ret < 0) {
171 return ret;
172 }
173
174 pos = 1;
175
176 /* The ID */
177 if (tlv->id > 255) {
178 ret = oma_tlv_put_u8(out, (tlv->id >> 8) & 0xff, insert);
179 if (ret < 0) {
180 return ret;
181 }
182
183 pos++;
184 }
185
186 ret = oma_tlv_put_u8(out, tlv->id & 0xff, insert);
187 if (ret < 0) {
188 return ret;
189 }
190
191 pos++;
192
193 for (i = 2; i >= 0; i--) {
194 if (len_type > i) {
195 ret = oma_tlv_put_u8(out,
196 (tlv->length >> (i * 8)) & 0xff,
197 insert);
198 if (ret < 0) {
199 return ret;
200 }
201
202 pos++;
203 }
204 }
205
206 /* finally add the value */
207 if (value != NULL && tlv->length > 0 && !insert) {
208 ret = buf_append(CPKT_BUF_WRITE(out->out_cpkt),
209 value, tlv->length);
210 if (ret < 0) {
211 return ret;
212 }
213 }
214
215 return pos + tlv->length;
216 }
217
oma_tlv_get(struct oma_tlv * tlv,struct lwm2m_input_context * in,bool dont_advance)218 static int oma_tlv_get(struct oma_tlv *tlv, struct lwm2m_input_context *in,
219 bool dont_advance)
220 {
221 uint8_t len_type;
222 uint8_t len_pos = 1U;
223 size_t tlv_len;
224 uint16_t tmp_offset;
225 uint8_t buf[2];
226 int ret;
227
228 tmp_offset = in->offset;
229 ret = buf_read_u8(&buf[0], CPKT_BUF_READ(in->in_cpkt), &tmp_offset);
230 if (ret < 0) {
231 goto error;
232 }
233
234 tlv->type = (buf[0] >> 6) & 3;
235 len_type = (buf[0] >> 3) & 3;
236 len_pos = 1 + (((buf[0] & (1 << 5)) != 0U) ? 2 : 1);
237
238 ret = buf_read_u8(&buf[1], CPKT_BUF_READ(in->in_cpkt), &tmp_offset);
239 if (ret < 0) {
240 goto error;
241 }
242
243 tlv->id = buf[1];
244
245 /* if len_pos > 2 it means that there are more ID to read */
246 if (len_pos > 2) {
247 ret = buf_read_u8(&buf[1], CPKT_BUF_READ(in->in_cpkt),
248 &tmp_offset);
249 if (ret < 0) {
250 goto error;
251 }
252
253 tlv->id = (tlv->id << 8) + buf[1];
254 }
255
256 if (len_type == 0U) {
257 tlv_len = buf[0] & 7;
258 } else {
259 /* read the length */
260 tlv_len = 0;
261 while (len_type > 0) {
262 ret = buf_read_u8(&buf[1], CPKT_BUF_READ(in->in_cpkt),
263 &tmp_offset);
264 if (ret < 0) {
265 goto error;
266 }
267
268 len_pos++;
269 tlv_len = tlv_len << 8 | buf[1];
270 len_type--;
271 }
272 }
273
274 /* and read out the data??? */
275 tlv->length = tlv_len;
276
277 if (!dont_advance) {
278 in->offset = tmp_offset;
279 }
280
281 return len_pos + tlv_len;
282
283 error:
284 if (!dont_advance) {
285 in->offset = tmp_offset;
286 }
287
288 return ret;
289 }
290
put_begin_tlv(struct lwm2m_output_context * out,uint16_t * mark_pos,uint8_t * writer_flags,int writer_flag)291 static int put_begin_tlv(struct lwm2m_output_context *out, uint16_t *mark_pos,
292 uint8_t *writer_flags, int writer_flag)
293 {
294 /* set flags */
295 *writer_flags |= writer_flag;
296
297 /*
298 * store position for inserting TLV when we know the length
299 */
300 *mark_pos = out->out_cpkt->offset;
301
302 return 0;
303 }
304
put_end_tlv(struct lwm2m_output_context * out,uint16_t mark_pos,uint8_t * writer_flags,uint8_t writer_flag,int tlv_type,int tlv_id)305 static int put_end_tlv(struct lwm2m_output_context *out, uint16_t mark_pos,
306 uint8_t *writer_flags, uint8_t writer_flag, int tlv_type,
307 int tlv_id)
308 {
309 struct tlv_out_formatter_data *fd;
310 struct oma_tlv tlv;
311 int len;
312
313 fd = engine_get_out_user_data(out);
314 if (!fd) {
315 return -EINVAL;
316 }
317
318 *writer_flags &= ~writer_flag;
319
320 len = out->out_cpkt->offset - mark_pos;
321
322 /* use stored location */
323 fd->mark_pos = mark_pos;
324
325 /* set instance length */
326 tlv_setup(&tlv, tlv_type, tlv_id, len);
327 len = oma_tlv_put(&tlv, out, NULL, true) - tlv.length;
328
329 return len;
330 }
331
put_begin_oi(struct lwm2m_output_context * out,struct lwm2m_obj_path * path)332 static int put_begin_oi(struct lwm2m_output_context *out,
333 struct lwm2m_obj_path *path)
334 {
335 struct tlv_out_formatter_data *fd;
336
337 /* No need for oi level TLV constructs */
338 if (path->level > LWM2M_PATH_LEVEL_OBJECT) {
339 return 0;
340 }
341
342 fd = engine_get_out_user_data(out);
343 if (!fd) {
344 return -EINVAL;
345 }
346
347 return put_begin_tlv(out, &fd->mark_pos_oi, &fd->writer_flags, 0);
348 }
349
put_end_oi(struct lwm2m_output_context * out,struct lwm2m_obj_path * path)350 static int put_end_oi(struct lwm2m_output_context *out,
351 struct lwm2m_obj_path *path)
352 {
353 struct tlv_out_formatter_data *fd;
354
355 if (path->level > LWM2M_PATH_LEVEL_OBJECT) {
356 return 0;
357 }
358
359 fd = engine_get_out_user_data(out);
360 if (!fd) {
361 return -EINVAL;
362 }
363
364 return put_end_tlv(out, fd->mark_pos_oi, &fd->writer_flags, 0,
365 OMA_TLV_TYPE_OBJECT_INSTANCE, path->obj_inst_id);
366 }
367
put_begin_ri(struct lwm2m_output_context * out,struct lwm2m_obj_path * path)368 static int put_begin_ri(struct lwm2m_output_context *out,
369 struct lwm2m_obj_path *path)
370 {
371 struct tlv_out_formatter_data *fd;
372
373 fd = engine_get_out_user_data(out);
374 if (!fd) {
375 return -EINVAL;
376 }
377
378 return put_begin_tlv(out, &fd->mark_pos_ri, &fd->writer_flags,
379 WRITER_RESOURCE_INSTANCE);
380 }
381
put_end_ri(struct lwm2m_output_context * out,struct lwm2m_obj_path * path)382 static int put_end_ri(struct lwm2m_output_context *out,
383 struct lwm2m_obj_path *path)
384 {
385 struct tlv_out_formatter_data *fd;
386
387 fd = engine_get_out_user_data(out);
388 if (!fd) {
389 return -EINVAL;
390 }
391
392 /* Skip writing Multiple Resource TLV if path level is 4 */
393 if (IS_ENABLED(CONFIG_LWM2M_VERSION_1_1) &&
394 path->level == LWM2M_PATH_LEVEL_RESOURCE_INST) {
395 return 0;
396 }
397
398 return put_end_tlv(out, fd->mark_pos_ri, &fd->writer_flags,
399 WRITER_RESOURCE_INSTANCE,
400 OMA_TLV_TYPE_MULTI_RESOURCE, path->res_id);
401 }
402
put_s8(struct lwm2m_output_context * out,struct lwm2m_obj_path * path,int8_t value)403 static int put_s8(struct lwm2m_output_context *out, struct lwm2m_obj_path *path,
404 int8_t value)
405 {
406 struct tlv_out_formatter_data *fd;
407 int len;
408 struct oma_tlv tlv;
409
410 fd = engine_get_out_user_data(out);
411 if (!fd) {
412 return -EINVAL;
413 }
414
415 tlv_setup(&tlv, tlv_calc_type(fd->writer_flags),
416 tlv_calc_id(fd->writer_flags, path), sizeof(value));
417
418 len = oma_tlv_put(&tlv, out, (uint8_t *)&value, false);
419 return len;
420 }
421
put_s16(struct lwm2m_output_context * out,struct lwm2m_obj_path * path,int16_t value)422 static int put_s16(struct lwm2m_output_context *out,
423 struct lwm2m_obj_path *path, int16_t value)
424 {
425 struct tlv_out_formatter_data *fd;
426 int len;
427 struct oma_tlv tlv;
428 uint8_t net_value[sizeof(int16_t)];
429
430 if (INT8_MIN <= value && value <= INT8_MAX) {
431 return put_s8(out, path, (int8_t)value);
432 }
433
434 fd = engine_get_out_user_data(out);
435 if (!fd) {
436 return -EINVAL;
437 }
438
439 sys_put_be16(value, net_value);
440 tlv_setup(&tlv, tlv_calc_type(fd->writer_flags),
441 tlv_calc_id(fd->writer_flags, path), sizeof(net_value));
442
443 len = oma_tlv_put(&tlv, out, net_value, false);
444 return len;
445 }
446
put_s32(struct lwm2m_output_context * out,struct lwm2m_obj_path * path,int32_t value)447 static int put_s32(struct lwm2m_output_context *out,
448 struct lwm2m_obj_path *path, int32_t value)
449 {
450 struct tlv_out_formatter_data *fd;
451 int len;
452 struct oma_tlv tlv;
453 uint8_t net_value[sizeof(int32_t)];
454
455 if (INT16_MIN <= value && value <= INT16_MAX) {
456 return put_s16(out, path, (int16_t)value);
457 }
458
459 fd = engine_get_out_user_data(out);
460 if (!fd) {
461 return -EINVAL;
462 }
463
464 sys_put_be32(value, net_value);
465 tlv_setup(&tlv, tlv_calc_type(fd->writer_flags),
466 tlv_calc_id(fd->writer_flags, path), sizeof(net_value));
467
468 len = oma_tlv_put(&tlv, out, net_value, false);
469
470 return len;
471 }
472
put_s64(struct lwm2m_output_context * out,struct lwm2m_obj_path * path,int64_t value)473 static int put_s64(struct lwm2m_output_context *out,
474 struct lwm2m_obj_path *path, int64_t value)
475 {
476 struct tlv_out_formatter_data *fd;
477 int len;
478 struct oma_tlv tlv;
479 uint8_t net_value[sizeof(int64_t)];
480
481 if (INT32_MIN <= value && value <= INT32_MAX) {
482 return put_s32(out, path, (int32_t)value);
483 }
484
485 fd = engine_get_out_user_data(out);
486 if (!fd) {
487 return -EINVAL;
488 }
489
490 sys_put_be64(value, net_value);
491 tlv_setup(&tlv, tlv_calc_type(fd->writer_flags),
492 tlv_calc_id(fd->writer_flags, path), sizeof(net_value));
493
494 len = oma_tlv_put(&tlv, out, net_value, false);
495 return len;
496 }
497
498
put_time(struct lwm2m_output_context * out,struct lwm2m_obj_path * path,time_t value)499 static int put_time(struct lwm2m_output_context *out, struct lwm2m_obj_path *path, time_t value)
500 {
501 return put_s64(out, path, (int64_t)value);
502 }
503
put_string(struct lwm2m_output_context * out,struct lwm2m_obj_path * path,char * buf,size_t buflen)504 static int put_string(struct lwm2m_output_context *out,
505 struct lwm2m_obj_path *path, char *buf, size_t buflen)
506 {
507 struct tlv_out_formatter_data *fd;
508 int len;
509 struct oma_tlv tlv;
510
511 fd = engine_get_out_user_data(out);
512 if (!fd) {
513 return -EINVAL;
514 }
515
516 tlv_setup(&tlv, tlv_calc_type(fd->writer_flags),
517 tlv_calc_id(fd->writer_flags, path), (uint32_t)buflen);
518 len = oma_tlv_put(&tlv, out, (uint8_t *)buf, false);
519 return len;
520 }
521
put_float(struct lwm2m_output_context * out,struct lwm2m_obj_path * path,double * value)522 static int put_float(struct lwm2m_output_context *out,
523 struct lwm2m_obj_path *path, double *value)
524 {
525 struct tlv_out_formatter_data *fd;
526 int len;
527 struct oma_tlv tlv;
528 int ret;
529 uint8_t b64[8];
530
531 fd = engine_get_out_user_data(out);
532 if (!fd) {
533 return -EINVAL;
534 }
535
536 ret = lwm2m_float_to_b64(value, b64, sizeof(b64));
537 if (ret < 0) {
538 LOG_ERR("float32 conversion error: %d", ret);
539 return ret;
540 }
541
542 tlv_setup(&tlv, tlv_calc_type(fd->writer_flags),
543 tlv_calc_id(fd->writer_flags, path), sizeof(b64));
544 len = oma_tlv_put(&tlv, out, b64, false);
545 return len;
546 }
547
put_bool(struct lwm2m_output_context * out,struct lwm2m_obj_path * path,bool value)548 static int put_bool(struct lwm2m_output_context *out,
549 struct lwm2m_obj_path *path, bool value)
550 {
551 int8_t value_s8 = (value != 0 ? 1 : 0);
552
553 return put_s8(out, path, value_s8);
554 }
555
put_opaque(struct lwm2m_output_context * out,struct lwm2m_obj_path * path,char * buf,size_t buflen)556 static int put_opaque(struct lwm2m_output_context *out,
557 struct lwm2m_obj_path *path, char *buf, size_t buflen)
558 {
559 return put_string(out, path, buf, buflen);
560 }
561
put_objlnk(struct lwm2m_output_context * out,struct lwm2m_obj_path * path,struct lwm2m_objlnk * value)562 static int put_objlnk(struct lwm2m_output_context *out,
563 struct lwm2m_obj_path *path, struct lwm2m_objlnk *value)
564 {
565 struct tlv_out_formatter_data *fd;
566 struct oma_tlv tlv;
567 uint32_t net_value = sys_cpu_to_be32(
568 ((uint32_t)value->obj_id << 16) | value->obj_inst);
569
570 fd = engine_get_out_user_data(out);
571 if (!fd) {
572 return -EINVAL;
573 }
574
575 tlv_setup(&tlv, tlv_calc_type(fd->writer_flags),
576 tlv_calc_id(fd->writer_flags, path), sizeof(net_value));
577
578 return oma_tlv_put(&tlv, out, (uint8_t *)&net_value, false);
579 }
580
get_number(struct lwm2m_input_context * in,int64_t * value,uint8_t max_len)581 static int get_number(struct lwm2m_input_context *in, int64_t *value,
582 uint8_t max_len)
583 {
584 struct oma_tlv tlv;
585 int size;
586 uint8_t temp[sizeof(int64_t)];
587 int ret;
588
589 size = oma_tlv_get(&tlv, in, false);
590 if (size < 0) {
591 return size;
592 }
593
594 if (tlv.length > max_len) {
595 LOG_ERR("invalid length: %u", tlv.length);
596 return -ENOMEM;
597 }
598
599 ret = buf_read(temp, tlv.length, CPKT_BUF_READ(in->in_cpkt), &in->offset);
600 if (ret < 0) {
601 return ret;
602 }
603
604 switch (tlv.length) {
605 case 1:
606 *value = (int8_t)temp[0];
607 break;
608 case 2:
609 *value = (int16_t)sys_get_be16(temp);
610 break;
611 case 4:
612 *value = (int32_t)sys_get_be32(temp);
613 break;
614 case 8:
615 *value = (int64_t)sys_get_be64(temp);
616 break;
617 default:
618 LOG_ERR("invalid length: %u", tlv.length);
619 return -EBADMSG;
620 }
621
622 return size;
623 }
624
get_s64(struct lwm2m_input_context * in,int64_t * value)625 static int get_s64(struct lwm2m_input_context *in, int64_t *value)
626 {
627 return get_number(in, value, 8);
628 }
629
get_time(struct lwm2m_input_context * in,time_t * value)630 static int get_time(struct lwm2m_input_context *in, time_t *value)
631 {
632 int64_t temp64;
633 int ret;
634
635 ret = get_number(in, &temp64, 8);
636 *value = (time_t)temp64;
637
638 return ret;
639 }
640
get_s32(struct lwm2m_input_context * in,int32_t * value)641 static int get_s32(struct lwm2m_input_context *in, int32_t *value)
642 {
643 int64_t temp;
644 int size;
645
646 size = get_number(in, &temp, 4);
647 if (size < 0) {
648 return size;
649 }
650
651 *value = (int32_t)temp;
652
653 return size;
654 }
655
get_string(struct lwm2m_input_context * in,uint8_t * buf,size_t buflen)656 static int get_string(struct lwm2m_input_context *in, uint8_t *buf,
657 size_t buflen)
658 {
659 struct oma_tlv tlv;
660 int size;
661 int ret;
662
663 size = oma_tlv_get(&tlv, in, false);
664 if (size < 0) {
665 return size;
666 }
667
668 if (buflen <= tlv.length) {
669 return -ENOMEM;
670 }
671
672 ret = buf_read(buf, tlv.length, CPKT_BUF_READ(in->in_cpkt),
673 &in->offset);
674 if (ret < 0) {
675 return ret;
676 }
677
678 buf[tlv.length] = '\0';
679
680 return size;
681 }
682
683 /* convert float to fixpoint */
get_float(struct lwm2m_input_context * in,double * value)684 static int get_float(struct lwm2m_input_context *in, double *value)
685 {
686 struct oma_tlv tlv;
687 int size;
688 uint8_t buf[8];
689 int ret;
690
691 size = oma_tlv_get(&tlv, in, false);
692 if (size < 0) {
693 return size;
694 }
695
696 if (tlv.length != 4U && tlv.length != 8U) {
697 LOG_ERR("Invalid float length: %d", tlv.length);
698 return -EBADMSG;
699 }
700
701 /* read float in network byte order */
702 ret = buf_read(buf, tlv.length, CPKT_BUF_READ(in->in_cpkt),
703 &in->offset);
704 if (ret < 0) {
705 return ret;
706 }
707
708 if (tlv.length == 4U) {
709 ret = lwm2m_b32_to_float(buf, 4, value);
710 } else {
711 ret = lwm2m_b64_to_float(buf, 8, value);
712 }
713
714 if (ret < 0) {
715 LOG_ERR("binary%s conversion error: %d",
716 tlv.length == 4U ? "32" : "64", ret);
717 return ret;
718 }
719
720 return size;
721 }
722
get_bool(struct lwm2m_input_context * in,bool * value)723 static int get_bool(struct lwm2m_input_context *in, bool *value)
724 {
725 int64_t temp;
726 int size;
727
728 size = get_number(in, &temp, 2);
729 if (size < 0) {
730 return size;
731
732 }
733
734 *value = (temp != 0);
735
736 return size;
737 }
738
get_opaque(struct lwm2m_input_context * in,uint8_t * value,size_t buflen,struct lwm2m_opaque_context * opaque,bool * last_block)739 static int get_opaque(struct lwm2m_input_context *in, uint8_t *value,
740 size_t buflen, struct lwm2m_opaque_context *opaque,
741 bool *last_block)
742 {
743 struct oma_tlv tlv;
744 int size;
745
746 /* Get the TLV header only on first read. */
747 if (opaque->offset == 0) {
748 size = oma_tlv_get(&tlv, in, false);
749 if (size < 0) {
750 return size;
751 }
752
753 opaque->len = tlv.length;
754 }
755
756 return lwm2m_engine_get_opaque_more(in, value, buflen,
757 opaque, last_block);
758 }
759
get_objlnk(struct lwm2m_input_context * in,struct lwm2m_objlnk * value)760 static int get_objlnk(struct lwm2m_input_context *in,
761 struct lwm2m_objlnk *value)
762 {
763 int32_t value_s32;
764 int size;
765
766 size = get_s32(in, &value_s32);
767 if (size < 0) {
768 return size;
769 }
770
771 value->obj_id = (value_s32 >> 16) & 0xFFFF;
772 value->obj_inst = value_s32 & 0xFFFF;
773
774 return size;
775 }
776
777 const struct lwm2m_writer oma_tlv_writer = {
778 .put_begin_oi = put_begin_oi,
779 .put_end_oi = put_end_oi,
780 .put_begin_ri = put_begin_ri,
781 .put_end_ri = put_end_ri,
782 .put_s8 = put_s8,
783 .put_s16 = put_s16,
784 .put_s32 = put_s32,
785 .put_s64 = put_s64,
786 .put_string = put_string,
787 .put_float = put_float,
788 .put_time = put_time,
789 .put_bool = put_bool,
790 .put_opaque = put_opaque,
791 .put_objlnk = put_objlnk,
792 };
793
794 const struct lwm2m_reader oma_tlv_reader = {
795 .get_s32 = get_s32,
796 .get_s64 = get_s64,
797 .get_string = get_string,
798 .get_time = get_time,
799 .get_float = get_float,
800 .get_bool = get_bool,
801 .get_opaque = get_opaque,
802 .get_objlnk = get_objlnk,
803 };
804
do_read_op_tlv(struct lwm2m_message * msg,int content_format)805 int do_read_op_tlv(struct lwm2m_message *msg, int content_format)
806 {
807 struct tlv_out_formatter_data fd;
808 int ret;
809
810 (void)memset(&fd, 0, sizeof(fd));
811 engine_set_out_user_data(&msg->out, &fd);
812 ret = lwm2m_perform_read_op(msg, content_format);
813 engine_clear_out_user_data(&msg->out);
814 return ret;
815 }
816
do_write_op_tlv_dummy_read(struct lwm2m_message * msg)817 static int do_write_op_tlv_dummy_read(struct lwm2m_message *msg)
818 {
819 struct oma_tlv tlv;
820 uint8_t read_char;
821
822 oma_tlv_get(&tlv, &msg->in, false);
823 while (tlv.length--) {
824 if (buf_read_u8(&read_char, CPKT_BUF_READ(msg->in.in_cpkt),
825 &msg->in.offset) < 0) {
826 break;
827 }
828 }
829
830 return 0;
831 }
832
do_write_op_tlv_item(struct lwm2m_message * msg)833 static int do_write_op_tlv_item(struct lwm2m_message *msg)
834 {
835 struct lwm2m_engine_obj_inst *obj_inst = NULL;
836 struct lwm2m_engine_res *res = NULL;
837 struct lwm2m_engine_res_inst *res_inst = NULL;
838 struct lwm2m_engine_obj_field *obj_field = NULL;
839 uint8_t created = 0U;
840 int ret;
841
842 ret = lwm2m_get_or_create_engine_obj(msg, &obj_inst, &created);
843 if (ret < 0) {
844 goto error;
845 }
846
847 ret = lwm2m_engine_validate_write_access(msg, obj_inst, &obj_field);
848 if (ret < 0) {
849 goto error;
850 }
851
852 ret = lwm2m_engine_get_create_res_inst(&msg->path, &res, &res_inst);
853
854 if (ret < 0) {
855 /* if OPTIONAL and BOOTSTRAP-WRITE or CREATE use ENOTSUP */
856 if ((msg->ctx->bootstrap_mode ||
857 msg->operation == LWM2M_OP_CREATE) &&
858 LWM2M_HAS_PERM(obj_field, BIT(LWM2M_FLAG_OPTIONAL))) {
859 ret = -ENOTSUP;
860 goto error;
861 }
862
863 ret = -ENOENT;
864 goto error;
865 }
866
867 ret = lwm2m_write_handler(obj_inst, res, res_inst, obj_field, msg);
868 if (ret == -EACCES || ret == -ENOENT) {
869 /* if read-only or non-existent data buffer move on */
870 do_write_op_tlv_dummy_read(msg);
871 ret = 0;
872 }
873
874 return ret;
875
876 error:
877 do_write_op_tlv_dummy_read(msg);
878 return ret;
879 }
880
write_tlv_resource(struct lwm2m_message * msg,struct oma_tlv * tlv)881 static int write_tlv_resource(struct lwm2m_message *msg, struct oma_tlv *tlv)
882 {
883 int ret;
884
885 if (msg->in.block_ctx) {
886 msg->in.block_ctx->path.res_id = tlv->id;
887 }
888
889 msg->path.res_id = tlv->id;
890 msg->path.level = 3U;
891 ret = do_write_op_tlv_item(msg);
892
893 /*
894 * ignore errors for CREATE op
895 * for OP_CREATE and BOOTSTRAP WRITE: errors on
896 * optional resources are ignored (ENOTSUP)
897 */
898 if (ret < 0 &&
899 !((ret == -ENOTSUP) &&
900 (msg->ctx->bootstrap_mode ||
901 msg->operation == LWM2M_OP_CREATE))) {
902 return ret;
903 }
904
905 return 0;
906 }
907
908 #if defined(CONFIG_LWM2M_VERSION_1_1)
write_tlv_resource_instance(struct lwm2m_message * msg,struct oma_tlv * tlv)909 static int write_tlv_resource_instance(struct lwm2m_message *msg, struct oma_tlv *tlv)
910 {
911 int ret;
912
913 if (msg->in.block_ctx) {
914 msg->in.block_ctx->path.res_inst_id = tlv->id;
915 }
916
917 msg->path.res_inst_id = tlv->id;
918 msg->path.level = LWM2M_PATH_LEVEL_RESOURCE_INST;
919 ret = do_write_op_tlv_item(msg);
920
921 if (ret < 0) {
922 return ret;
923 }
924
925 return 0;
926 }
927 #endif
928
lwm2m_multi_resource_tlv_parse(struct lwm2m_message * msg,struct oma_tlv * multi_resource_tlv)929 static int lwm2m_multi_resource_tlv_parse(struct lwm2m_message *msg,
930 struct oma_tlv *multi_resource_tlv)
931 {
932 struct oma_tlv tlv_resource_instance;
933 int len2;
934 int pos = 0;
935 int ret;
936
937 if (msg->in.block_ctx) {
938 msg->in.block_ctx->path.res_id = multi_resource_tlv->id;
939 }
940
941 if (multi_resource_tlv->length == 0U) {
942 /* No data for resource instances, so create only a resource */
943 return write_tlv_resource(msg, multi_resource_tlv);
944 }
945
946 while (pos < multi_resource_tlv->length &&
947 (len2 = oma_tlv_get(&tlv_resource_instance, &msg->in, true))) {
948 if (tlv_resource_instance.type != OMA_TLV_TYPE_RESOURCE_INSTANCE) {
949 LOG_ERR("Multi resource id not supported %u %d", tlv_resource_instance.id,
950 tlv_resource_instance.length);
951 return -ENOTSUP;
952 }
953
954 msg->path.res_id = multi_resource_tlv->id;
955 msg->path.res_inst_id = tlv_resource_instance.id;
956 msg->path.level = LWM2M_PATH_LEVEL_RESOURCE_INST;
957 ret = do_write_op_tlv_item(msg);
958
959 /*
960 * Ignore errors on optional resources when doing
961 * BOOTSTRAP WRITE or CREATE operation.
962 */
963 if (ret < 0 && !((ret == -ENOTSUP) &&
964 (msg->ctx->bootstrap_mode || msg->operation == LWM2M_OP_CREATE))) {
965 return ret;
966 }
967
968 pos += len2;
969 }
970
971 return 0;
972 }
973
do_write_op_tlv(struct lwm2m_message * msg)974 int do_write_op_tlv(struct lwm2m_message *msg)
975 {
976 struct lwm2m_engine_obj_inst *obj_inst = NULL;
977 int len;
978 struct oma_tlv tlv;
979 int ret;
980
981 /* In case of block transfer, check if there are any fragments
982 * left from the previous resource (instance). If this is the
983 * case, proceed directly to processing the message -
984 * consecutive blocks from the same resource do not carry the
985 * TLV header.
986 */
987 if (msg->in.block_ctx != NULL && msg->in.block_ctx->ctx.current > 0) {
988 msg->path.res_id = msg->in.block_ctx->path.res_id;
989 msg->path.level = 3U;
990 ret = do_write_op_tlv_item(msg);
991 if (ret < 0) {
992 return ret;
993 }
994
995 }
996
997 while (true) {
998 /*
999 * This initial read of TLV data won't advance frag/offset.
1000 * We need tlv.type to determine how to proceed.
1001 */
1002 len = oma_tlv_get(&tlv, &msg->in, true);
1003 if (len < 0) {
1004 break;
1005 }
1006
1007 if (tlv.type == OMA_TLV_TYPE_OBJECT_INSTANCE) {
1008 struct oma_tlv tlv2;
1009 int len2;
1010 int pos = 0;
1011
1012 oma_tlv_get(&tlv, &msg->in, false);
1013 msg->path.obj_inst_id = tlv.id;
1014 if (tlv.length == 0U) {
1015 /* Create only - no data */
1016 ret = lwm2m_create_obj_inst(
1017 msg->path.obj_id,
1018 msg->path.obj_inst_id,
1019 &obj_inst);
1020 if (ret < 0) {
1021 return ret;
1022 }
1023
1024 if (!msg->ctx->bootstrap_mode) {
1025 engine_trigger_update(true);
1026 }
1027 }
1028
1029 while (pos < tlv.length &&
1030 (len2 = oma_tlv_get(&tlv2, &msg->in, true))) {
1031 if (tlv2.type == OMA_TLV_TYPE_RESOURCE) {
1032 ret = write_tlv_resource(msg, &tlv2);
1033 if (ret) {
1034 return ret;
1035 }
1036 } else if (tlv2.type == OMA_TLV_TYPE_MULTI_RESOURCE) {
1037 oma_tlv_get(&tlv2, &msg->in, false);
1038 ret = lwm2m_multi_resource_tlv_parse(msg, &tlv2);
1039 if (ret) {
1040 return ret;
1041 }
1042 } else {
1043 /* Skip Unsupported TLV type */
1044 return -ENOTSUP;
1045 }
1046
1047 pos += len2;
1048 }
1049 } else if (tlv.type == OMA_TLV_TYPE_RESOURCE) {
1050 if (msg->path.level < LWM2M_PATH_LEVEL_OBJECT_INST) {
1051 return -ENOTSUP;
1052 }
1053 ret = write_tlv_resource(msg, &tlv);
1054 if (ret) {
1055 return ret;
1056 }
1057 } else if (tlv.type == OMA_TLV_TYPE_MULTI_RESOURCE) {
1058 if (msg->path.level < LWM2M_PATH_LEVEL_OBJECT_INST) {
1059 return -ENOTSUP;
1060 }
1061 oma_tlv_get(&tlv, &msg->in, false);
1062 ret = lwm2m_multi_resource_tlv_parse(msg, &tlv);
1063 if (ret) {
1064 return ret;
1065 }
1066 #if defined(CONFIG_LWM2M_VERSION_1_1)
1067 } else if (tlv.type == OMA_TLV_TYPE_RESOURCE_INSTANCE) {
1068 if (msg->path.level < LWM2M_PATH_LEVEL_OBJECT_INST) {
1069 return -ENOTSUP;
1070 }
1071 ret = write_tlv_resource_instance(msg, &tlv);
1072 if (ret) {
1073 return ret;
1074 }
1075 #endif
1076 } else {
1077 return -ENOTSUP;
1078 }
1079 }
1080
1081 return 0;
1082 }
1083