1 /*
2 * Copyright (c) 2018 Intel Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <logging/log.h>
8 LOG_MODULE_REGISTER(net_coap, CONFIG_COAP_LOG_LEVEL);
9
10 #include <stdlib.h>
11 #include <stddef.h>
12 #include <string.h>
13 #include <stdbool.h>
14 #include <errno.h>
15 #include <random/rand32.h>
16 #include <sys/atomic.h>
17 #include <sys/util.h>
18
19 #include <zephyr/types.h>
20 #include <sys/byteorder.h>
21 #include <sys/math_extras.h>
22
23 #include <net/net_ip.h>
24 #include <net/net_core.h>
25 #include <net/coap.h>
26
27 /* Values as per RFC 7252, section-3.1.
28 *
29 * Option Delta/Length: 4-bit unsigned integer. A value between 0 and
30 * 12 indicates the Option Delta/Length. Three values are reserved for
31 * special constructs:
32 * 13: An 8-bit unsigned integer precedes the Option Value and indicates
33 * the Option Delta/Length minus 13.
34 * 14: A 16-bit unsigned integer in network byte order precedes the
35 * Option Value and indicates the Option Delta/Length minus 269.
36 * 15: Reserved for future use.
37 */
38 #define COAP_OPTION_NO_EXT 12 /* Option's Delta/Length without extended data */
39 #define COAP_OPTION_EXT_13 13
40 #define COAP_OPTION_EXT_14 14
41 #define COAP_OPTION_EXT_15 15
42 #define COAP_OPTION_EXT_269 269
43
44 /* CoAP Payload Marker */
45 #define COAP_MARKER 0xFF
46
47 #define BASIC_HEADER_SIZE 4
48
49 /* The CoAP message ID that is incremented each time coap_next_id() is called. */
50 static uint16_t message_id;
51
append_u8(struct coap_packet * cpkt,uint8_t data)52 static inline bool append_u8(struct coap_packet *cpkt, uint8_t data)
53 {
54 if (!cpkt) {
55 return false;
56 }
57
58 if (cpkt->max_len - cpkt->offset < 1) {
59 return false;
60 }
61
62 cpkt->data[cpkt->offset++] = data;
63
64 return true;
65 }
66
append_be16(struct coap_packet * cpkt,uint16_t data)67 static inline bool append_be16(struct coap_packet *cpkt, uint16_t data)
68 {
69 if (!cpkt) {
70 return false;
71 }
72
73 if (cpkt->max_len - cpkt->offset < 2) {
74 return false;
75 }
76
77 cpkt->data[cpkt->offset++] = data >> 8;
78 cpkt->data[cpkt->offset++] = (uint8_t) data;
79
80 return true;
81 }
82
append(struct coap_packet * cpkt,const uint8_t * data,uint16_t len)83 static inline bool append(struct coap_packet *cpkt, const uint8_t *data, uint16_t len)
84 {
85 if (!cpkt || !data) {
86 return false;
87 }
88
89 if (cpkt->max_len - cpkt->offset < len) {
90 return false;
91 }
92
93 memcpy(cpkt->data + cpkt->offset, data, len);
94 cpkt->offset += len;
95
96 return true;
97 }
98
coap_packet_init(struct coap_packet * cpkt,uint8_t * data,uint16_t max_len,uint8_t ver,uint8_t type,uint8_t token_len,const uint8_t * token,uint8_t code,uint16_t id)99 int coap_packet_init(struct coap_packet *cpkt, uint8_t *data, uint16_t max_len,
100 uint8_t ver, uint8_t type, uint8_t token_len,
101 const uint8_t *token, uint8_t code, uint16_t id)
102 {
103 uint8_t hdr;
104 bool res;
105
106 if (!cpkt || !data || !max_len) {
107 return -EINVAL;
108 }
109
110 memset(cpkt, 0, sizeof(*cpkt));
111
112 cpkt->data = data;
113 cpkt->offset = 0U;
114 cpkt->max_len = max_len;
115 cpkt->delta = 0U;
116
117 hdr = (ver & 0x3) << 6;
118 hdr |= (type & 0x3) << 4;
119 hdr |= token_len & 0xF;
120
121 res = append_u8(cpkt, hdr);
122 if (!res) {
123 return -EINVAL;
124 }
125
126 res = append_u8(cpkt, code);
127 if (!res) {
128 return -EINVAL;
129 }
130
131 res = append_be16(cpkt, id);
132 if (!res) {
133 return -EINVAL;
134 }
135
136 if (token && token_len) {
137 res = append(cpkt, token, token_len);
138 if (!res) {
139 return -EINVAL;
140 }
141 }
142
143 /* Header length : (version + type + tkl) + code + id + [token] */
144 cpkt->hdr_len = 1 + 1 + 2 + token_len;
145
146 return 0;
147 }
148
coap_ack_init(struct coap_packet * cpkt,const struct coap_packet * req,uint8_t * data,uint16_t max_len,uint8_t code)149 int coap_ack_init(struct coap_packet *cpkt, const struct coap_packet *req,
150 uint8_t *data, uint16_t max_len, uint8_t code)
151 {
152 uint16_t id;
153 uint8_t ver;
154 uint8_t tkl;
155 uint8_t token[COAP_TOKEN_MAX_LEN];
156
157 ver = coap_header_get_version(req);
158 id = coap_header_get_id(req);
159 tkl = code ? coap_header_get_token(req, token) : 0;
160
161 return coap_packet_init(cpkt, data, max_len, ver, COAP_TYPE_ACK, tkl,
162 token, code, id);
163 }
164
option_header_set_delta(uint8_t * opt,uint8_t delta)165 static void option_header_set_delta(uint8_t *opt, uint8_t delta)
166 {
167 *opt = (delta & 0xF) << 4;
168 }
169
option_header_set_len(uint8_t * opt,uint8_t len)170 static void option_header_set_len(uint8_t *opt, uint8_t len)
171 {
172 *opt |= (len & 0xF);
173 }
174
encode_extended_option(uint16_t num,uint8_t * opt,uint16_t * ext)175 static uint8_t encode_extended_option(uint16_t num, uint8_t *opt, uint16_t *ext)
176 {
177 if (num < COAP_OPTION_EXT_13) {
178 *opt = num;
179 *ext = 0U;
180
181 return 0;
182 } else if (num < COAP_OPTION_EXT_269) {
183 *opt = COAP_OPTION_EXT_13;
184 *ext = num - COAP_OPTION_EXT_13;
185
186 return 1;
187 }
188
189 *opt = COAP_OPTION_EXT_14;
190 *ext = num - COAP_OPTION_EXT_269;
191
192 return 2;
193 }
194
encode_option(struct coap_packet * cpkt,uint16_t code,const uint8_t * value,uint16_t len)195 static int encode_option(struct coap_packet *cpkt, uint16_t code,
196 const uint8_t *value, uint16_t len)
197 {
198 uint16_t delta_ext; /* Extended delta */
199 uint16_t len_ext; /* Extended length */
200 uint8_t opt; /* delta | len */
201 uint8_t opt_delta;
202 uint8_t opt_len;
203 uint8_t delta_size;
204 uint8_t len_size;
205 bool res;
206
207 delta_size = encode_extended_option(code, &opt_delta, &delta_ext);
208 len_size = encode_extended_option(len, &opt_len, &len_ext);
209
210 option_header_set_delta(&opt, opt_delta);
211 option_header_set_len(&opt, opt_len);
212
213 res = append_u8(cpkt, opt);
214 if (!res) {
215 return -EINVAL;
216 }
217
218 if (delta_size == 1U) {
219 res = append_u8(cpkt, (uint8_t)delta_ext);
220 if (!res) {
221 return -EINVAL;
222 }
223 } else if (delta_size == 2U) {
224 res = append_be16(cpkt, delta_ext);
225 if (!res) {
226 return -EINVAL;
227 }
228 }
229
230 if (len_size == 1U) {
231 res = append_u8(cpkt, (uint8_t)len_ext);
232 if (!res) {
233 return -EINVAL;
234 }
235 } else if (len_size == 2U) {
236 res = append_be16(cpkt, len_ext);
237 if (!res) {
238 return -EINVAL;
239 }
240 }
241
242 if (len && value) {
243 res = append(cpkt, value, len);
244 if (!res) {
245 return -EINVAL;
246 }
247 }
248
249 return (1 + delta_size + len_size + len);
250 }
251
252 /* TODO Add support for inserting options in proper place
253 * and modify other option's delta accordingly.
254 */
coap_packet_append_option(struct coap_packet * cpkt,uint16_t code,const uint8_t * value,uint16_t len)255 int coap_packet_append_option(struct coap_packet *cpkt, uint16_t code,
256 const uint8_t *value, uint16_t len)
257 {
258 int r;
259
260 if (!cpkt) {
261 return -EINVAL;
262 }
263
264 if (len && !value) {
265 return -EINVAL;
266 }
267
268 if (code < cpkt->delta) {
269 NET_ERR("Options should be in ascending order");
270 return -EINVAL;
271 }
272
273 /* Calculate delta, if this option is not the first one */
274 if (cpkt->opt_len) {
275 code = (code == cpkt->delta) ? 0 : code - cpkt->delta;
276 }
277
278 r = encode_option(cpkt, code, value, len);
279 if (r < 0) {
280 return -EINVAL;
281 }
282
283 cpkt->opt_len += r;
284 cpkt->delta += code;
285
286 return 0;
287 }
288
coap_append_option_int(struct coap_packet * cpkt,uint16_t code,unsigned int val)289 int coap_append_option_int(struct coap_packet *cpkt, uint16_t code,
290 unsigned int val)
291 {
292 uint8_t data[4], len;
293
294 if (val == 0U) {
295 data[0] = 0U;
296 len = 0U;
297 } else if (val < 0xFF) {
298 data[0] = (uint8_t) val;
299 len = 1U;
300 } else if (val < 0xFFFF) {
301 sys_put_be16(val, data);
302 len = 2U;
303 } else if (val < 0xFFFFFF) {
304 sys_put_be16(val, &data[1]);
305 data[0] = val >> 16;
306 len = 3U;
307 } else {
308 sys_put_be32(val, data);
309 len = 4U;
310 }
311
312 return coap_packet_append_option(cpkt, code, data, len);
313 }
314
coap_option_value_to_int(const struct coap_option * option)315 unsigned int coap_option_value_to_int(const struct coap_option *option)
316 {
317 switch (option->len) {
318 case 0:
319 return 0;
320 case 1:
321 return option->value[0];
322 case 2:
323 return (option->value[1] << 0) | (option->value[0] << 8);
324 case 3:
325 return (option->value[2] << 0) | (option->value[1] << 8) |
326 (option->value[0] << 16);
327 case 4:
328 return (option->value[3] << 0) | (option->value[2] << 8) |
329 (option->value[1] << 16) | (option->value[0] << 24);
330 default:
331 return 0;
332 }
333
334 return 0;
335 }
336
coap_packet_append_payload_marker(struct coap_packet * cpkt)337 int coap_packet_append_payload_marker(struct coap_packet *cpkt)
338 {
339 return append_u8(cpkt, COAP_MARKER) ? 0 : -EINVAL;
340 }
341
coap_packet_append_payload(struct coap_packet * cpkt,const uint8_t * payload,uint16_t payload_len)342 int coap_packet_append_payload(struct coap_packet *cpkt, const uint8_t *payload,
343 uint16_t payload_len)
344 {
345 return append(cpkt, payload, payload_len) ? 0 : -EINVAL;
346 }
347
coap_next_token(void)348 uint8_t *coap_next_token(void)
349 {
350 static uint8_t token[COAP_TOKEN_MAX_LEN];
351
352 sys_rand_get(token, COAP_TOKEN_MAX_LEN);
353
354 return token;
355 }
356
option_header_get_delta(uint8_t opt)357 static uint8_t option_header_get_delta(uint8_t opt)
358 {
359 return (opt & 0xF0) >> 4;
360 }
361
option_header_get_len(uint8_t opt)362 static uint8_t option_header_get_len(uint8_t opt)
363 {
364 return opt & 0x0F;
365 }
366
read_u8(uint8_t * data,uint16_t offset,uint16_t * pos,uint16_t max_len,uint8_t * value)367 static int read_u8(uint8_t *data, uint16_t offset, uint16_t *pos,
368 uint16_t max_len, uint8_t *value)
369 {
370 if (max_len - offset < 1) {
371 return -EINVAL;
372 }
373
374 *value = data[offset++];
375 *pos = offset;
376
377 return max_len - offset;
378 }
379
read_be16(uint8_t * data,uint16_t offset,uint16_t * pos,uint16_t max_len,uint16_t * value)380 static int read_be16(uint8_t *data, uint16_t offset, uint16_t *pos,
381 uint16_t max_len, uint16_t *value)
382 {
383 if (max_len - offset < 2) {
384 return -EINVAL;
385 }
386
387 *value = data[offset++] << 8;
388 *value |= data[offset++];
389 *pos = offset;
390
391 return max_len - offset;
392 }
393
read(uint8_t * data,uint16_t offset,uint16_t * pos,uint16_t max_len,uint16_t len,uint8_t * value)394 static int read(uint8_t *data, uint16_t offset, uint16_t *pos,
395 uint16_t max_len, uint16_t len, uint8_t *value)
396 {
397 if (max_len - offset < len) {
398 return -EINVAL;
399 }
400
401 memcpy(value, data + offset, len);
402 offset += len;
403 *pos = offset;
404
405 return max_len - offset;
406 }
407
decode_delta(uint8_t * data,uint16_t offset,uint16_t * pos,uint16_t max_len,uint16_t opt,uint16_t * opt_ext,uint16_t * hdr_len)408 static int decode_delta(uint8_t *data, uint16_t offset, uint16_t *pos, uint16_t max_len,
409 uint16_t opt, uint16_t *opt_ext, uint16_t *hdr_len)
410 {
411 int ret = 0;
412
413 if (opt == COAP_OPTION_EXT_13) {
414 uint8_t val;
415
416 *hdr_len = 1U;
417
418 ret = read_u8(data, offset, pos, max_len, &val);
419 if (ret < 0) {
420 return -EINVAL;
421 }
422
423 opt = val + COAP_OPTION_EXT_13;
424 } else if (opt == COAP_OPTION_EXT_14) {
425 uint16_t val;
426
427 *hdr_len = 2U;
428
429 ret = read_be16(data, offset, pos, max_len, &val);
430 if (ret < 0) {
431 return -EINVAL;
432 }
433
434 opt = val + COAP_OPTION_EXT_269;
435 } else if (opt == COAP_OPTION_EXT_15) {
436 return -EINVAL;
437 }
438
439 *opt_ext = opt;
440
441 return ret;
442 }
443
parse_option(uint8_t * data,uint16_t offset,uint16_t * pos,uint16_t max_len,uint16_t * opt_delta,uint16_t * opt_len,struct coap_option * option)444 static int parse_option(uint8_t *data, uint16_t offset, uint16_t *pos,
445 uint16_t max_len, uint16_t *opt_delta, uint16_t *opt_len,
446 struct coap_option *option)
447 {
448 uint16_t hdr_len;
449 uint16_t delta;
450 uint16_t len;
451 uint8_t opt;
452 int r;
453
454 r = read_u8(data, offset, pos, max_len, &opt);
455 if (r < 0) {
456 return r;
457 }
458
459 /* This indicates that options have ended */
460 if (opt == COAP_MARKER) {
461 /* packet w/ marker but no payload is malformed */
462 return r > 0 ? 0 : -EINVAL;
463 }
464
465 *opt_len += 1U;
466
467 delta = option_header_get_delta(opt);
468 len = option_header_get_len(opt);
469
470 /* r == 0 means no more data to read from fragment, but delta
471 * field shows that packet should contain more data, it must
472 * be a malformed packet.
473 */
474 if (r == 0 && delta > COAP_OPTION_NO_EXT) {
475 return -EINVAL;
476 }
477
478 if (delta > COAP_OPTION_NO_EXT) {
479 /* In case 'delta' doesn't fit the option fixed header. */
480 r = decode_delta(data, *pos, pos, max_len,
481 delta, &delta, &hdr_len);
482 if ((r < 0) || (r == 0 && len > COAP_OPTION_NO_EXT)) {
483 return -EINVAL;
484 }
485
486 if (u16_add_overflow(*opt_len, hdr_len, opt_len)) {
487 return -EINVAL;
488 }
489 }
490
491 if (len > COAP_OPTION_NO_EXT) {
492 /* In case 'len' doesn't fit the option fixed header. */
493 r = decode_delta(data, *pos, pos, max_len,
494 len, &len, &hdr_len);
495 if (r < 0) {
496 return -EINVAL;
497 }
498
499 if (u16_add_overflow(*opt_len, hdr_len, opt_len)) {
500 return -EINVAL;
501 }
502 }
503
504 if (u16_add_overflow(*opt_delta, delta, opt_delta) ||
505 u16_add_overflow(*opt_len, len, opt_len)) {
506 return -EINVAL;
507 }
508
509 if (r == 0 && len != 0U) {
510 /* r == 0 means no more data to read from fragment, but len
511 * field shows that packet should contain more data, it must
512 * be a malformed packet.
513 */
514 return -EINVAL;
515 }
516
517 if (option) {
518 /*
519 * Make sure the option data will fit into the value field of
520 * coap_option.
521 * NOTE: To expand the size of the value field set:
522 * CONFIG_COAP_EXTENDED_OPTIONS_LEN=y
523 * CONFIG_COAP_EXTENDED_OPTIONS_LEN_VALUE=<size>
524 */
525 if (len > sizeof(option->value)) {
526 NET_ERR("%u is > sizeof(coap_option->value)(%zu)!",
527 len, sizeof(option->value));
528 return -EINVAL;
529 }
530
531 option->delta = *opt_delta;
532 option->len = len;
533 r = read(data, *pos, pos, max_len, len, &option->value[0]);
534 if (r < 0) {
535 return -EINVAL;
536 }
537 } else {
538 if (u16_add_overflow(*pos, len, pos)) {
539 return -EINVAL;
540 }
541
542 r = max_len - *pos;
543 }
544
545 return r;
546 }
547
coap_packet_parse(struct coap_packet * cpkt,uint8_t * data,uint16_t len,struct coap_option * options,uint8_t opt_num)548 int coap_packet_parse(struct coap_packet *cpkt, uint8_t *data, uint16_t len,
549 struct coap_option *options, uint8_t opt_num)
550 {
551 uint16_t opt_len;
552 uint16_t offset;
553 uint16_t delta;
554 uint8_t num;
555 uint8_t tkl;
556 int ret;
557
558 if (!cpkt || !data) {
559 return -EINVAL;
560 }
561
562 if (len < BASIC_HEADER_SIZE) {
563 return -EINVAL;
564 }
565
566 if (options) {
567 memset(options, 0, opt_num * sizeof(struct coap_option));
568 }
569
570 cpkt->data = data;
571 cpkt->offset = len;
572 cpkt->max_len = len;
573 cpkt->opt_len = 0U;
574 cpkt->hdr_len = 0U;
575 cpkt->delta = 0U;
576
577 /* Token lengths 9-15 are reserved. */
578 tkl = cpkt->data[0] & 0x0f;
579 if (tkl > 8) {
580 return -EINVAL;
581 }
582
583 cpkt->hdr_len = BASIC_HEADER_SIZE + tkl;
584 if (cpkt->hdr_len > len) {
585 return -EINVAL;
586 }
587
588 if (cpkt->hdr_len == len) {
589 return 0;
590 }
591
592 offset = cpkt->hdr_len;
593 opt_len = 0U;
594 delta = 0U;
595 num = 0U;
596
597 while (1) {
598 struct coap_option *option;
599
600 option = num < opt_num ? &options[num++] : NULL;
601 ret = parse_option(cpkt->data, offset, &offset, cpkt->max_len,
602 &delta, &opt_len, option);
603 if (ret < 0) {
604 return ret;
605 } else if (ret == 0) {
606 break;
607 }
608 }
609
610 cpkt->opt_len = opt_len;
611 cpkt->delta = delta;
612
613 return 0;
614 }
615
coap_find_options(const struct coap_packet * cpkt,uint16_t code,struct coap_option * options,uint16_t veclen)616 int coap_find_options(const struct coap_packet *cpkt, uint16_t code,
617 struct coap_option *options, uint16_t veclen)
618 {
619 uint16_t opt_len;
620 uint16_t offset;
621 uint16_t delta;
622 uint8_t num;
623 int r;
624
625 /* Check if there are options to parse */
626 if (cpkt->hdr_len == cpkt->max_len) {
627 return 0;
628 }
629
630 offset = cpkt->hdr_len;
631 opt_len = 0U;
632 delta = 0U;
633 num = 0U;
634
635 while (delta <= code && num < veclen) {
636 r = parse_option(cpkt->data, offset, &offset,
637 cpkt->max_len, &delta, &opt_len,
638 &options[num]);
639 if (r < 0) {
640 return -EINVAL;
641 }
642
643 if (code == options[num].delta) {
644 num++;
645 }
646
647 if (r == 0) {
648 break;
649 }
650 }
651
652 return num;
653 }
654
coap_header_get_version(const struct coap_packet * cpkt)655 uint8_t coap_header_get_version(const struct coap_packet *cpkt)
656 {
657 if (!cpkt || !cpkt->data) {
658 return 0;
659 }
660
661 return (cpkt->data[0] & 0xC0) >> 6;
662 }
663
coap_header_get_type(const struct coap_packet * cpkt)664 uint8_t coap_header_get_type(const struct coap_packet *cpkt)
665 {
666 if (!cpkt || !cpkt->data) {
667 return 0;
668 }
669
670 return (cpkt->data[0] & 0x30) >> 4;
671 }
672
__coap_header_get_code(const struct coap_packet * cpkt)673 static uint8_t __coap_header_get_code(const struct coap_packet *cpkt)
674 {
675 if (!cpkt || !cpkt->data) {
676 return 0;
677 }
678
679 return cpkt->data[1];
680 }
681
coap_header_get_token(const struct coap_packet * cpkt,uint8_t * token)682 uint8_t coap_header_get_token(const struct coap_packet *cpkt, uint8_t *token)
683 {
684 uint8_t tkl;
685
686 if (!cpkt || !cpkt->data) {
687 return 0;
688 }
689
690 tkl = cpkt->data[0] & 0x0f;
691 if (tkl) {
692 memcpy(token, cpkt->data + BASIC_HEADER_SIZE, tkl);
693 }
694
695 return tkl;
696 }
697
coap_header_get_code(const struct coap_packet * cpkt)698 uint8_t coap_header_get_code(const struct coap_packet *cpkt)
699 {
700 uint8_t code = __coap_header_get_code(cpkt);
701
702 switch (code) {
703 /* Methods are encoded in the code field too */
704 case COAP_METHOD_GET:
705 case COAP_METHOD_POST:
706 case COAP_METHOD_PUT:
707 case COAP_METHOD_DELETE:
708
709 /* All the defined response codes */
710 case COAP_RESPONSE_CODE_OK:
711 case COAP_RESPONSE_CODE_CREATED:
712 case COAP_RESPONSE_CODE_DELETED:
713 case COAP_RESPONSE_CODE_VALID:
714 case COAP_RESPONSE_CODE_CHANGED:
715 case COAP_RESPONSE_CODE_CONTENT:
716 case COAP_RESPONSE_CODE_CONTINUE:
717 case COAP_RESPONSE_CODE_BAD_REQUEST:
718 case COAP_RESPONSE_CODE_UNAUTHORIZED:
719 case COAP_RESPONSE_CODE_BAD_OPTION:
720 case COAP_RESPONSE_CODE_FORBIDDEN:
721 case COAP_RESPONSE_CODE_NOT_FOUND:
722 case COAP_RESPONSE_CODE_NOT_ALLOWED:
723 case COAP_RESPONSE_CODE_NOT_ACCEPTABLE:
724 case COAP_RESPONSE_CODE_INCOMPLETE:
725 case COAP_RESPONSE_CODE_PRECONDITION_FAILED:
726 case COAP_RESPONSE_CODE_REQUEST_TOO_LARGE:
727 case COAP_RESPONSE_CODE_UNSUPPORTED_CONTENT_FORMAT:
728 case COAP_RESPONSE_CODE_INTERNAL_ERROR:
729 case COAP_RESPONSE_CODE_NOT_IMPLEMENTED:
730 case COAP_RESPONSE_CODE_BAD_GATEWAY:
731 case COAP_RESPONSE_CODE_SERVICE_UNAVAILABLE:
732 case COAP_RESPONSE_CODE_GATEWAY_TIMEOUT:
733 case COAP_RESPONSE_CODE_PROXYING_NOT_SUPPORTED:
734 case COAP_CODE_EMPTY:
735 return code;
736 default:
737 return COAP_CODE_EMPTY;
738 }
739 }
740
coap_header_get_id(const struct coap_packet * cpkt)741 uint16_t coap_header_get_id(const struct coap_packet *cpkt)
742 {
743 if (!cpkt || !cpkt->data) {
744 return 0;
745 }
746
747 return (cpkt->data[2] << 8) | cpkt->data[3];
748 }
749
coap_packet_get_payload(const struct coap_packet * cpkt,uint16_t * len)750 const uint8_t *coap_packet_get_payload(const struct coap_packet *cpkt, uint16_t *len)
751 {
752 int payload_len;
753
754 if (!cpkt || !len) {
755 return NULL;
756 }
757
758 payload_len = cpkt->offset - cpkt->hdr_len - cpkt->opt_len;
759 if (payload_len > 1) {
760 *len = payload_len - 1; /* subtract payload marker length */
761 } else {
762 *len = 0U;
763 }
764
765 return *len == 0 ? NULL :
766 cpkt->data + cpkt->hdr_len + cpkt->opt_len + 1;
767 }
768
uri_path_eq(const struct coap_packet * cpkt,const char * const * path,struct coap_option * options,uint8_t opt_num)769 static bool uri_path_eq(const struct coap_packet *cpkt,
770 const char * const *path,
771 struct coap_option *options,
772 uint8_t opt_num)
773 {
774 uint8_t i;
775 uint8_t j = 0U;
776
777 for (i = 0U; i < opt_num && path[j]; i++) {
778 if (options[i].delta != COAP_OPTION_URI_PATH) {
779 continue;
780 }
781
782 if (IS_ENABLED(CONFIG_COAP_URI_WILDCARD) && strlen(path[j]) == 1) {
783 if (*path[j] == '+') {
784 /* Single-level wildcard */
785 j++;
786 continue;
787 } else if (*path[j] == '#') {
788 /* Multi-level wildcard */
789 return true;
790 }
791 }
792
793 if (options[i].len != strlen(path[j])) {
794 return false;
795 }
796
797 if (memcmp(options[i].value, path[j], options[i].len)) {
798 return false;
799 }
800
801 j++;
802 }
803
804 if (path[j]) {
805 return false;
806 }
807
808 for (; i < opt_num; i++) {
809 if (options[i].delta == COAP_OPTION_URI_PATH) {
810 return false;
811 }
812 }
813
814 return true;
815 }
816
method_from_code(const struct coap_resource * resource,uint8_t code)817 static coap_method_t method_from_code(const struct coap_resource *resource,
818 uint8_t code)
819 {
820 switch (code) {
821 case COAP_METHOD_GET:
822 return resource->get;
823 case COAP_METHOD_POST:
824 return resource->post;
825 case COAP_METHOD_PUT:
826 return resource->put;
827 case COAP_METHOD_DELETE:
828 return resource->del;
829 default:
830 return NULL;
831 }
832 }
833
is_request(const struct coap_packet * cpkt)834 static bool is_request(const struct coap_packet *cpkt)
835 {
836 uint8_t code = coap_header_get_code(cpkt);
837
838 return !(code & ~COAP_REQUEST_MASK);
839 }
840
coap_handle_request(struct coap_packet * cpkt,struct coap_resource * resources,struct coap_option * options,uint8_t opt_num,struct sockaddr * addr,socklen_t addr_len)841 int coap_handle_request(struct coap_packet *cpkt,
842 struct coap_resource *resources,
843 struct coap_option *options,
844 uint8_t opt_num,
845 struct sockaddr *addr, socklen_t addr_len)
846 {
847 struct coap_resource *resource;
848
849 if (!is_request(cpkt)) {
850 return 0;
851 }
852
853 /* FIXME: deal with hierarchical resources */
854 for (resource = resources; resource && resource->path; resource++) {
855 coap_method_t method;
856 uint8_t code;
857
858 if (!uri_path_eq(cpkt, resource->path, options, opt_num)) {
859 continue;
860 }
861
862 code = coap_header_get_code(cpkt);
863 method = method_from_code(resource, code);
864 if (!method) {
865 return -EPERM;
866 }
867
868 return method(resource, cpkt, addr, addr_len);
869 }
870
871 NET_DBG("%d", __LINE__);
872 return -ENOENT;
873 }
874
coap_block_transfer_init(struct coap_block_context * ctx,enum coap_block_size block_size,size_t total_size)875 int coap_block_transfer_init(struct coap_block_context *ctx,
876 enum coap_block_size block_size,
877 size_t total_size)
878 {
879 ctx->block_size = block_size;
880 ctx->total_size = total_size;
881 ctx->current = 0;
882
883 return 0;
884 }
885
886 #define GET_BLOCK_SIZE(v) (((v) & 0x7))
887 #define GET_MORE(v) (!!((v) & 0x08))
888 #define GET_NUM(v) ((v) >> 4)
889
890 #define SET_BLOCK_SIZE(v, b) (v |= ((b) & 0x07))
891 #define SET_MORE(v, m) ((v) |= (m) ? 0x08 : 0x00)
892 #define SET_NUM(v, n) ((v) |= ((n) << 4))
893
coap_append_block1_option(struct coap_packet * cpkt,struct coap_block_context * ctx)894 int coap_append_block1_option(struct coap_packet *cpkt,
895 struct coap_block_context *ctx)
896 {
897 uint16_t bytes = coap_block_size_to_bytes(ctx->block_size);
898 unsigned int val = 0U;
899 int r;
900
901 if (is_request(cpkt)) {
902 SET_BLOCK_SIZE(val, ctx->block_size);
903 SET_MORE(val, ctx->current + bytes < ctx->total_size);
904 SET_NUM(val, ctx->current / bytes);
905 } else {
906 SET_BLOCK_SIZE(val, ctx->block_size);
907 SET_NUM(val, ctx->current / bytes);
908 }
909
910 r = coap_append_option_int(cpkt, COAP_OPTION_BLOCK1, val);
911
912 return r;
913 }
914
coap_append_block2_option(struct coap_packet * cpkt,struct coap_block_context * ctx)915 int coap_append_block2_option(struct coap_packet *cpkt,
916 struct coap_block_context *ctx)
917 {
918 int r, val = 0;
919 uint16_t bytes = coap_block_size_to_bytes(ctx->block_size);
920
921 if (is_request(cpkt)) {
922 SET_BLOCK_SIZE(val, ctx->block_size);
923 SET_NUM(val, ctx->current / bytes);
924 } else {
925 SET_BLOCK_SIZE(val, ctx->block_size);
926 SET_MORE(val, ctx->current + bytes < ctx->total_size);
927 SET_NUM(val, ctx->current / bytes);
928 }
929
930 r = coap_append_option_int(cpkt, COAP_OPTION_BLOCK2, val);
931
932 return r;
933 }
934
coap_append_size1_option(struct coap_packet * cpkt,struct coap_block_context * ctx)935 int coap_append_size1_option(struct coap_packet *cpkt,
936 struct coap_block_context *ctx)
937 {
938 return coap_append_option_int(cpkt, COAP_OPTION_SIZE1, ctx->total_size);
939 }
940
coap_append_size2_option(struct coap_packet * cpkt,struct coap_block_context * ctx)941 int coap_append_size2_option(struct coap_packet *cpkt,
942 struct coap_block_context *ctx)
943 {
944 return coap_append_option_int(cpkt, COAP_OPTION_SIZE2, ctx->total_size);
945 }
946
coap_get_option_int(const struct coap_packet * cpkt,uint16_t code)947 int coap_get_option_int(const struct coap_packet *cpkt, uint16_t code)
948 {
949 struct coap_option option = {};
950 unsigned int val;
951 int count = 1;
952
953 count = coap_find_options(cpkt, code, &option, count);
954 if (count <= 0) {
955 return -ENOENT;
956 }
957
958 val = coap_option_value_to_int(&option);
959
960 return val;
961 }
962
update_descriptive_block(struct coap_block_context * ctx,int block,int size)963 static int update_descriptive_block(struct coap_block_context *ctx,
964 int block, int size)
965 {
966 size_t new_current = GET_NUM(block) << (GET_BLOCK_SIZE(block) + 4);
967
968 if (block == -ENOENT) {
969 return 0;
970 }
971
972 if (size && ctx->total_size && ctx->total_size != size) {
973 return -EINVAL;
974 }
975
976 if (ctx->current > 0 && GET_BLOCK_SIZE(block) > ctx->block_size) {
977 return -EINVAL;
978 }
979
980 if (ctx->total_size && new_current > ctx->total_size) {
981 return -EINVAL;
982 }
983
984 if (size) {
985 ctx->total_size = size;
986 }
987 ctx->current = new_current;
988 ctx->block_size = MIN(GET_BLOCK_SIZE(block), ctx->block_size);
989
990 return 0;
991 }
992
update_control_block1(struct coap_block_context * ctx,int block,int size)993 static int update_control_block1(struct coap_block_context *ctx,
994 int block, int size)
995 {
996 size_t new_current = GET_NUM(block) << (GET_BLOCK_SIZE(block) + 4);
997
998 if (block == -ENOENT) {
999 return 0;
1000 }
1001
1002 if (new_current != ctx->current) {
1003 return -EINVAL;
1004 }
1005
1006 if (GET_BLOCK_SIZE(block) > ctx->block_size) {
1007 return -EINVAL;
1008 }
1009
1010 ctx->block_size = GET_BLOCK_SIZE(block);
1011 ctx->total_size = size;
1012
1013 return 0;
1014 }
1015
update_control_block2(struct coap_block_context * ctx,int block,int size)1016 static int update_control_block2(struct coap_block_context *ctx,
1017 int block, int size)
1018 {
1019 size_t new_current = GET_NUM(block) << (GET_BLOCK_SIZE(block) + 4);
1020
1021 if (block == -ENOENT) {
1022 return 0;
1023 }
1024
1025 if (GET_MORE(block)) {
1026 return -EINVAL;
1027 }
1028
1029 if (GET_NUM(block) > 0 && GET_BLOCK_SIZE(block) != ctx->block_size) {
1030 return -EINVAL;
1031 }
1032
1033 ctx->current = new_current;
1034 ctx->block_size = MIN(GET_BLOCK_SIZE(block), ctx->block_size);
1035
1036 return 0;
1037 }
1038
coap_update_from_block(const struct coap_packet * cpkt,struct coap_block_context * ctx)1039 int coap_update_from_block(const struct coap_packet *cpkt,
1040 struct coap_block_context *ctx)
1041 {
1042 int r, block1, block2, size1, size2;
1043
1044 block1 = coap_get_option_int(cpkt, COAP_OPTION_BLOCK1);
1045 block2 = coap_get_option_int(cpkt, COAP_OPTION_BLOCK2);
1046 size1 = coap_get_option_int(cpkt, COAP_OPTION_SIZE1);
1047 size2 = coap_get_option_int(cpkt, COAP_OPTION_SIZE2);
1048
1049 size1 = size1 == -ENOENT ? 0 : size1;
1050 size2 = size2 == -ENOENT ? 0 : size2;
1051
1052 if (is_request(cpkt)) {
1053 r = update_control_block2(ctx, block2, size2);
1054 if (r) {
1055 return r;
1056 }
1057
1058 return update_descriptive_block(ctx, block1, size1);
1059 }
1060
1061 r = update_control_block1(ctx, block1, size1);
1062 if (r) {
1063 return r;
1064 }
1065
1066 return update_descriptive_block(ctx, block2, size2);
1067 }
1068
coap_next_block(const struct coap_packet * cpkt,struct coap_block_context * ctx)1069 size_t coap_next_block(const struct coap_packet *cpkt,
1070 struct coap_block_context *ctx)
1071 {
1072 int block;
1073
1074 if (is_request(cpkt)) {
1075 block = coap_get_option_int(cpkt, COAP_OPTION_BLOCK1);
1076 } else {
1077 block = coap_get_option_int(cpkt, COAP_OPTION_BLOCK2);
1078 }
1079
1080 if (!GET_MORE(block)) {
1081 return 0;
1082 }
1083
1084 ctx->current += coap_block_size_to_bytes(ctx->block_size);
1085
1086 return ctx->current;
1087 }
1088
coap_pending_init(struct coap_pending * pending,const struct coap_packet * request,const struct sockaddr * addr,uint8_t retries)1089 int coap_pending_init(struct coap_pending *pending,
1090 const struct coap_packet *request,
1091 const struct sockaddr *addr,
1092 uint8_t retries)
1093 {
1094 memset(pending, 0, sizeof(*pending));
1095
1096 pending->id = coap_header_get_id(request);
1097
1098 memcpy(&pending->addr, addr, sizeof(*addr));
1099
1100 pending->data = request->data;
1101 pending->len = request->offset;
1102 pending->t0 = k_uptime_get_32();
1103 pending->retries = retries;
1104
1105 return 0;
1106 }
1107
coap_pending_next_unused(struct coap_pending * pendings,size_t len)1108 struct coap_pending *coap_pending_next_unused(
1109 struct coap_pending *pendings, size_t len)
1110 {
1111 struct coap_pending *p;
1112 size_t i;
1113
1114 for (i = 0, p = pendings; i < len; i++, p++) {
1115 if (p->timeout == 0) {
1116 return p;
1117 }
1118 }
1119
1120 return NULL;
1121 }
1122
coap_reply_next_unused(struct coap_reply * replies,size_t len)1123 struct coap_reply *coap_reply_next_unused(
1124 struct coap_reply *replies, size_t len)
1125 {
1126 struct coap_reply *r;
1127 size_t i;
1128
1129 for (i = 0, r = replies; i < len; i++, r++) {
1130 if (!r->reply) {
1131 return r;
1132 }
1133 }
1134
1135 return NULL;
1136 }
1137
is_addr_unspecified(const struct sockaddr * addr)1138 static inline bool is_addr_unspecified(const struct sockaddr *addr)
1139 {
1140 if (addr->sa_family == AF_UNSPEC) {
1141 return true;
1142 }
1143
1144 if (addr->sa_family == AF_INET6) {
1145 return net_ipv6_is_addr_unspecified(
1146 &(net_sin6(addr)->sin6_addr));
1147 } else if (addr->sa_family == AF_INET) {
1148 return net_sin(addr)->sin_addr.s4_addr32[0] == 0U;
1149 }
1150
1151 return false;
1152 }
1153
coap_observer_next_unused(struct coap_observer * observers,size_t len)1154 struct coap_observer *coap_observer_next_unused(
1155 struct coap_observer *observers, size_t len)
1156 {
1157 struct coap_observer *o;
1158 size_t i;
1159
1160 for (i = 0, o = observers; i < len; i++, o++) {
1161 if (is_addr_unspecified(&o->addr)) {
1162 return o;
1163 }
1164 }
1165
1166 return NULL;
1167 }
1168
coap_pending_received(const struct coap_packet * response,struct coap_pending * pendings,size_t len)1169 struct coap_pending *coap_pending_received(
1170 const struct coap_packet *response,
1171 struct coap_pending *pendings, size_t len)
1172 {
1173 struct coap_pending *p;
1174 uint16_t resp_id = coap_header_get_id(response);
1175 size_t i;
1176
1177 for (i = 0, p = pendings; i < len; i++, p++) {
1178 if (!p->timeout) {
1179 continue;
1180 }
1181
1182 if (resp_id != p->id) {
1183 continue;
1184 }
1185
1186 return p;
1187 }
1188
1189 return NULL;
1190 }
1191
coap_pending_next_to_expire(struct coap_pending * pendings,size_t len)1192 struct coap_pending *coap_pending_next_to_expire(
1193 struct coap_pending *pendings, size_t len)
1194 {
1195 struct coap_pending *p, *found = NULL;
1196 size_t i;
1197 uint32_t expiry, min_expiry;
1198
1199 for (i = 0, p = pendings; i < len; i++, p++) {
1200 if (!p->timeout) {
1201 continue;
1202 }
1203
1204 expiry = p->t0 + p->timeout;
1205
1206 if (!found || (int32_t)(expiry - min_expiry) < 0) {
1207 min_expiry = expiry;
1208 found = p;
1209 }
1210 }
1211
1212 return found;
1213 }
1214
init_ack_timeout(void)1215 static uint32_t init_ack_timeout(void)
1216 {
1217 #if defined(CONFIG_COAP_RANDOMIZE_ACK_TIMEOUT)
1218 const uint32_t max_ack = CONFIG_COAP_INIT_ACK_TIMEOUT_MS *
1219 COAP_DEFAULT_ACK_RANDOM_FACTOR;
1220 const uint32_t min_ack = CONFIG_COAP_INIT_ACK_TIMEOUT_MS;
1221
1222 /* Randomly generated initial ACK timeout
1223 * ACK_TIMEOUT < INIT_ACK_TIMEOUT < ACK_TIMEOUT * ACK_RANDOM_FACTOR
1224 * Ref: https://tools.ietf.org/html/rfc7252#section-4.8
1225 */
1226 return min_ack + (sys_rand32_get() % (max_ack - min_ack));
1227 #else
1228 return CONFIG_COAP_INIT_ACK_TIMEOUT_MS;
1229 #endif /* defined(CONFIG_COAP_RANDOMIZE_ACK_TIMEOUT) */
1230 }
1231
coap_pending_cycle(struct coap_pending * pending)1232 bool coap_pending_cycle(struct coap_pending *pending)
1233 {
1234 if (pending->timeout == 0) {
1235 /* Initial transmission. */
1236 pending->timeout = init_ack_timeout();
1237
1238 return true;
1239 }
1240
1241 if (pending->retries == 0) {
1242 return false;
1243 }
1244
1245 pending->t0 += pending->timeout;
1246 pending->timeout = pending->timeout << 1;
1247 pending->retries--;
1248
1249 return true;
1250 }
1251
coap_pending_clear(struct coap_pending * pending)1252 void coap_pending_clear(struct coap_pending *pending)
1253 {
1254 pending->timeout = 0;
1255 pending->data = NULL;
1256 }
1257
coap_pendings_clear(struct coap_pending * pendings,size_t len)1258 void coap_pendings_clear(struct coap_pending *pendings, size_t len)
1259 {
1260 struct coap_pending *p;
1261 size_t i;
1262
1263 for (i = 0, p = pendings; i < len; i++, p++) {
1264 coap_pending_clear(p);
1265 }
1266 }
1267
1268 /* Reordering according to RFC7641 section 3.4 but without timestamp comparison */
is_newer(int v1,int v2)1269 static inline bool is_newer(int v1, int v2)
1270 {
1271 return (v1 < v2 && v2 - v1 < (1 << 23))
1272 || (v1 > v2 && v1 - v2 > (1 << 23));
1273 }
1274
coap_response_received(const struct coap_packet * response,const struct sockaddr * from,struct coap_reply * replies,size_t len)1275 struct coap_reply *coap_response_received(
1276 const struct coap_packet *response,
1277 const struct sockaddr *from,
1278 struct coap_reply *replies, size_t len)
1279 {
1280 struct coap_reply *r;
1281 uint8_t token[COAP_TOKEN_MAX_LEN];
1282 uint16_t id;
1283 uint8_t tkl;
1284 size_t i;
1285
1286 id = coap_header_get_id(response);
1287 tkl = coap_header_get_token(response, token);
1288
1289 for (i = 0, r = replies; i < len; i++, r++) {
1290 int age;
1291
1292 if ((r->id == 0U) && (r->tkl == 0U)) {
1293 continue;
1294 }
1295
1296 /* Piggybacked must match id when token is empty */
1297 if ((r->id != id) && (tkl == 0U)) {
1298 continue;
1299 }
1300
1301 if (tkl > 0 && memcmp(r->token, token, tkl)) {
1302 continue;
1303 }
1304
1305 age = coap_get_option_int(response, COAP_OPTION_OBSERVE);
1306 /* handle observed requests only if received in order */
1307 if (age == -ENOENT || is_newer(r->age, age)) {
1308 r->age = age;
1309 r->reply(response, r, from);
1310 }
1311
1312 return r;
1313 }
1314
1315 return NULL;
1316 }
1317
coap_reply_init(struct coap_reply * reply,const struct coap_packet * request)1318 void coap_reply_init(struct coap_reply *reply,
1319 const struct coap_packet *request)
1320 {
1321 uint8_t token[COAP_TOKEN_MAX_LEN];
1322 uint8_t tkl;
1323
1324 reply->id = coap_header_get_id(request);
1325 tkl = coap_header_get_token(request, token);
1326
1327 if (tkl > 0) {
1328 memcpy(reply->token, token, tkl);
1329 }
1330
1331 reply->tkl = tkl;
1332
1333 /* Any initial observe response should be accepted */
1334 reply->age = -1;
1335 }
1336
coap_reply_clear(struct coap_reply * reply)1337 void coap_reply_clear(struct coap_reply *reply)
1338 {
1339 (void)memset(reply, 0, sizeof(*reply));
1340 }
1341
coap_replies_clear(struct coap_reply * replies,size_t len)1342 void coap_replies_clear(struct coap_reply *replies, size_t len)
1343 {
1344 struct coap_reply *r;
1345 size_t i;
1346
1347 for (i = 0, r = replies; i < len; i++, r++) {
1348 coap_reply_clear(r);
1349 }
1350 }
1351
coap_resource_notify(struct coap_resource * resource)1352 int coap_resource_notify(struct coap_resource *resource)
1353 {
1354 struct coap_observer *o;
1355
1356 if (!resource->notify) {
1357 return -ENOENT;
1358 }
1359
1360 resource->age++;
1361
1362 SYS_SLIST_FOR_EACH_CONTAINER(&resource->observers, o, list) {
1363 resource->notify(resource, o);
1364 }
1365
1366 return 0;
1367 }
1368
coap_request_is_observe(const struct coap_packet * request)1369 bool coap_request_is_observe(const struct coap_packet *request)
1370 {
1371 return coap_get_option_int(request, COAP_OPTION_OBSERVE) == 0;
1372 }
1373
coap_observer_init(struct coap_observer * observer,const struct coap_packet * request,const struct sockaddr * addr)1374 void coap_observer_init(struct coap_observer *observer,
1375 const struct coap_packet *request,
1376 const struct sockaddr *addr)
1377 {
1378 observer->tkl = coap_header_get_token(request, observer->token);
1379
1380 net_ipaddr_copy(&observer->addr, addr);
1381 }
1382
coap_register_observer(struct coap_resource * resource,struct coap_observer * observer)1383 bool coap_register_observer(struct coap_resource *resource,
1384 struct coap_observer *observer)
1385 {
1386 bool first;
1387
1388 sys_slist_append(&resource->observers, &observer->list);
1389
1390 first = resource->age == 0;
1391 if (first) {
1392 resource->age = 2;
1393 }
1394
1395 return first;
1396 }
1397
coap_remove_observer(struct coap_resource * resource,struct coap_observer * observer)1398 void coap_remove_observer(struct coap_resource *resource,
1399 struct coap_observer *observer)
1400 {
1401 sys_slist_find_and_remove(&resource->observers, &observer->list);
1402 }
1403
sockaddr_equal(const struct sockaddr * a,const struct sockaddr * b)1404 static bool sockaddr_equal(const struct sockaddr *a,
1405 const struct sockaddr *b)
1406 {
1407 /* FIXME: Should we consider ipv6-mapped ipv4 addresses as equal to
1408 * ipv4 addresses?
1409 */
1410 if (a->sa_family != b->sa_family) {
1411 return false;
1412 }
1413
1414 if (a->sa_family == AF_INET) {
1415 const struct sockaddr_in *a4 = net_sin(a);
1416 const struct sockaddr_in *b4 = net_sin(b);
1417
1418 if (a4->sin_port != b4->sin_port) {
1419 return false;
1420 }
1421
1422 return net_ipv4_addr_cmp(&a4->sin_addr, &b4->sin_addr);
1423 }
1424
1425 if (b->sa_family == AF_INET6) {
1426 const struct sockaddr_in6 *a6 = net_sin6(a);
1427 const struct sockaddr_in6 *b6 = net_sin6(b);
1428
1429 if (a6->sin6_port != b6->sin6_port) {
1430 return false;
1431 }
1432
1433 return net_ipv6_addr_cmp(&a6->sin6_addr, &b6->sin6_addr);
1434 }
1435
1436 /* Invalid address family */
1437 return false;
1438 }
1439
coap_find_observer_by_addr(struct coap_observer * observers,size_t len,const struct sockaddr * addr)1440 struct coap_observer *coap_find_observer_by_addr(
1441 struct coap_observer *observers, size_t len,
1442 const struct sockaddr *addr)
1443 {
1444 size_t i;
1445
1446 for (i = 0; i < len; i++) {
1447 struct coap_observer *o = &observers[i];
1448
1449 if (sockaddr_equal(&o->addr, addr)) {
1450 return o;
1451 }
1452 }
1453
1454 return NULL;
1455 }
1456
1457 /**
1458 * @brief Internal initialization function for CoAP library.
1459 *
1460 * Called by the network layer init procedure. Seeds the CoAP @message_id with a
1461 * random number in accordance with recommendations in CoAP specification.
1462 *
1463 * @note This function is not exposed in a public header, as it's for internal
1464 * use and should therefore not be exposed to applications.
1465 *
1466 * @return N/A
1467 */
net_coap_init(void)1468 void net_coap_init(void)
1469 {
1470 /* Initialize message_id to a random number */
1471 message_id = (uint16_t)sys_rand32_get();
1472 }
1473
coap_next_id(void)1474 uint16_t coap_next_id(void)
1475 {
1476 return message_id++;
1477 }
1478