1 /*
2 * Copyright (c) 2018 Intel Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/logging/log.h>
8 LOG_MODULE_REGISTER(net_coap_client_sample, LOG_LEVEL_DBG);
9
10 #include <errno.h>
11 #include <zephyr/sys/printk.h>
12 #include <zephyr/sys/byteorder.h>
13 #include <zephyr/kernel.h>
14
15 #include <zephyr/net/socket.h>
16 #include <zephyr/net/net_mgmt.h>
17 #include <zephyr/net/net_ip.h>
18 #include <zephyr/net/udp.h>
19 #include <zephyr/net/coap.h>
20
21 #include "net_private.h"
22
23 #define PEER_PORT 5683
24 #define MAX_COAP_MSG_LEN 256
25
26 /* CoAP socket fd */
27 static int sock;
28
29 struct pollfd fds[1];
30 static int nfds;
31
32 /* CoAP Options */
33 static const char * const test_path[] = { "test", NULL };
34
35 static const char * const large_path[] = { "large", NULL };
36
37 static const char * const obs_path[] = { "obs", NULL };
38
39 #define BLOCK_WISE_TRANSFER_SIZE_GET 2048
40
41 static struct coap_block_context blk_ctx;
42
wait(void)43 static void wait(void)
44 {
45 if (poll(fds, nfds, -1) < 0) {
46 LOG_ERR("Error in poll:%d", errno);
47 }
48 }
49
prepare_fds(void)50 static void prepare_fds(void)
51 {
52 fds[nfds].fd = sock;
53 fds[nfds].events = POLLIN;
54 nfds++;
55 }
56
start_coap_client(void)57 static int start_coap_client(void)
58 {
59 int ret = 0;
60 struct sockaddr_in6 addr6;
61
62 addr6.sin6_family = AF_INET6;
63 addr6.sin6_port = htons(PEER_PORT);
64 addr6.sin6_scope_id = 0U;
65
66 inet_pton(AF_INET6, CONFIG_NET_CONFIG_PEER_IPV6_ADDR,
67 &addr6.sin6_addr);
68
69 sock = socket(addr6.sin6_family, SOCK_DGRAM, IPPROTO_UDP);
70 if (sock < 0) {
71 LOG_ERR("Failed to create UDP socket %d", errno);
72 return -errno;
73 }
74
75 ret = connect(sock, (struct sockaddr *)&addr6, sizeof(addr6));
76 if (ret < 0) {
77 LOG_ERR("Cannot connect to UDP remote : %d", errno);
78 return -errno;
79 }
80
81 prepare_fds();
82
83 return 0;
84 }
85
process_simple_coap_reply(void)86 static int process_simple_coap_reply(void)
87 {
88 struct coap_packet reply;
89 uint8_t *data;
90 int rcvd;
91 int ret;
92
93 wait();
94
95 data = (uint8_t *)k_malloc(MAX_COAP_MSG_LEN);
96 if (!data) {
97 return -ENOMEM;
98 }
99
100 rcvd = recv(sock, data, MAX_COAP_MSG_LEN, MSG_DONTWAIT);
101 if (rcvd == 0) {
102 ret = -EIO;
103 goto end;
104 }
105
106 if (rcvd < 0) {
107 if (errno == EAGAIN || errno == EWOULDBLOCK) {
108 ret = 0;
109 } else {
110 ret = -errno;
111 }
112
113 goto end;
114 }
115
116 net_hexdump("Response", data, rcvd);
117
118 ret = coap_packet_parse(&reply, data, rcvd, NULL, 0);
119 if (ret < 0) {
120 LOG_ERR("Invalid data received");
121 }
122
123 end:
124 k_free(data);
125
126 return ret;
127 }
128
send_simple_coap_request(uint8_t method)129 static int send_simple_coap_request(uint8_t method)
130 {
131 uint8_t payload[] = "payload";
132 struct coap_packet request;
133 const char * const *p;
134 uint8_t *data;
135 int r;
136
137 data = (uint8_t *)k_malloc(MAX_COAP_MSG_LEN);
138 if (!data) {
139 return -ENOMEM;
140 }
141
142 r = coap_packet_init(&request, data, MAX_COAP_MSG_LEN,
143 COAP_VERSION_1, COAP_TYPE_CON,
144 COAP_TOKEN_MAX_LEN, coap_next_token(),
145 method, coap_next_id());
146 if (r < 0) {
147 LOG_ERR("Failed to init CoAP message");
148 goto end;
149 }
150
151 for (p = test_path; p && *p; p++) {
152 r = coap_packet_append_option(&request, COAP_OPTION_URI_PATH,
153 *p, strlen(*p));
154 if (r < 0) {
155 LOG_ERR("Unable add option to request");
156 goto end;
157 }
158 }
159
160 switch (method) {
161 case COAP_METHOD_GET:
162 case COAP_METHOD_DELETE:
163 break;
164
165 case COAP_METHOD_PUT:
166 case COAP_METHOD_POST:
167 r = coap_packet_append_payload_marker(&request);
168 if (r < 0) {
169 LOG_ERR("Unable to append payload marker");
170 goto end;
171 }
172
173 r = coap_packet_append_payload(&request, (uint8_t *)payload,
174 sizeof(payload) - 1);
175 if (r < 0) {
176 LOG_ERR("Not able to append payload");
177 goto end;
178 }
179
180 break;
181 default:
182 r = -EINVAL;
183 goto end;
184 }
185
186 net_hexdump("Request", request.data, request.offset);
187
188 r = send(sock, request.data, request.offset, 0);
189
190 end:
191 k_free(data);
192
193 return r;
194 }
195
send_simple_coap_msgs_and_wait_for_reply(void)196 static int send_simple_coap_msgs_and_wait_for_reply(void)
197 {
198 uint8_t test_type = 0U;
199 int r;
200
201 while (1) {
202 switch (test_type) {
203 case 0:
204 /* Test CoAP GET method */
205 printk("\nCoAP client GET\n");
206 r = send_simple_coap_request(COAP_METHOD_GET);
207 if (r < 0) {
208 return r;
209 }
210
211 break;
212 case 1:
213 /* Test CoAP PUT method */
214 printk("\nCoAP client PUT\n");
215 r = send_simple_coap_request(COAP_METHOD_PUT);
216 if (r < 0) {
217 return r;
218 }
219
220 break;
221 case 2:
222 /* Test CoAP POST method*/
223 printk("\nCoAP client POST\n");
224 r = send_simple_coap_request(COAP_METHOD_POST);
225 if (r < 0) {
226 return r;
227 }
228
229 break;
230 case 3:
231 /* Test CoAP DELETE method*/
232 printk("\nCoAP client DELETE\n");
233 r = send_simple_coap_request(COAP_METHOD_DELETE);
234 if (r < 0) {
235 return r;
236 }
237
238 break;
239 default:
240 return 0;
241 }
242
243 r = process_simple_coap_reply();
244 if (r < 0) {
245 return r;
246 }
247
248 test_type++;
249 }
250
251 return 0;
252 }
253
process_large_coap_reply(void)254 static int process_large_coap_reply(void)
255 {
256 struct coap_packet reply;
257 uint8_t *data;
258 bool last_block;
259 int rcvd;
260 int ret;
261
262 wait();
263
264 data = (uint8_t *)k_malloc(MAX_COAP_MSG_LEN);
265 if (!data) {
266 return -ENOMEM;
267 }
268
269 rcvd = recv(sock, data, MAX_COAP_MSG_LEN, MSG_DONTWAIT);
270 if (rcvd == 0) {
271 ret = -EIO;
272 goto end;
273 }
274
275 if (rcvd < 0) {
276 if (errno == EAGAIN || errno == EWOULDBLOCK) {
277 ret = 0;
278 } else {
279 ret = -errno;
280 }
281
282 goto end;
283 }
284
285 net_hexdump("Response", data, rcvd);
286
287 ret = coap_packet_parse(&reply, data, rcvd, NULL, 0);
288 if (ret < 0) {
289 LOG_ERR("Invalid data received");
290 goto end;
291 }
292
293 ret = coap_update_from_block(&reply, &blk_ctx);
294 if (ret < 0) {
295 goto end;
296 }
297
298 last_block = coap_next_block(&reply, &blk_ctx);
299 if (!last_block) {
300 ret = 1;
301 goto end;
302 }
303
304 ret = 0;
305
306 end:
307 k_free(data);
308
309 return ret;
310 }
311
send_large_coap_request(void)312 static int send_large_coap_request(void)
313 {
314 struct coap_packet request;
315 const char * const *p;
316 uint8_t *data;
317 int r;
318
319 if (blk_ctx.total_size == 0) {
320 coap_block_transfer_init(&blk_ctx, COAP_BLOCK_64,
321 BLOCK_WISE_TRANSFER_SIZE_GET);
322 }
323
324 data = (uint8_t *)k_malloc(MAX_COAP_MSG_LEN);
325 if (!data) {
326 return -ENOMEM;
327 }
328
329 r = coap_packet_init(&request, data, MAX_COAP_MSG_LEN,
330 COAP_VERSION_1, COAP_TYPE_CON,
331 COAP_TOKEN_MAX_LEN, coap_next_token(),
332 COAP_METHOD_GET, coap_next_id());
333 if (r < 0) {
334 LOG_ERR("Failed to init CoAP message");
335 goto end;
336 }
337
338 for (p = large_path; p && *p; p++) {
339 r = coap_packet_append_option(&request, COAP_OPTION_URI_PATH,
340 *p, strlen(*p));
341 if (r < 0) {
342 LOG_ERR("Unable add option to request");
343 goto end;
344 }
345 }
346
347 r = coap_append_block2_option(&request, &blk_ctx);
348 if (r < 0) {
349 LOG_ERR("Unable to add block2 option.");
350 goto end;
351 }
352
353 net_hexdump("Request", request.data, request.offset);
354
355 r = send(sock, request.data, request.offset, 0);
356
357 end:
358 k_free(data);
359
360 return r;
361 }
362
get_large_coap_msgs(void)363 static int get_large_coap_msgs(void)
364 {
365 int r;
366
367 while (1) {
368 /* Test CoAP Large GET method */
369 printk("\nCoAP client Large GET (block %zd)\n",
370 blk_ctx.current / 64 /*COAP_BLOCK_64*/);
371 r = send_large_coap_request();
372 if (r < 0) {
373 return r;
374 }
375
376 r = process_large_coap_reply();
377 if (r < 0) {
378 return r;
379 }
380
381 /* Received last block */
382 if (r == 1) {
383 memset(&blk_ctx, 0, sizeof(blk_ctx));
384 return 0;
385 }
386 }
387
388 return 0;
389 }
390
send_obs_reply_ack(uint16_t id)391 static void send_obs_reply_ack(uint16_t id)
392 {
393 struct coap_packet request;
394 uint8_t *data;
395 int r;
396
397 data = (uint8_t *)k_malloc(MAX_COAP_MSG_LEN);
398 if (!data) {
399 return;
400 }
401
402 r = coap_packet_init(&request, data, MAX_COAP_MSG_LEN,
403 COAP_VERSION_1, COAP_TYPE_ACK, 0, NULL, 0, id);
404 if (r < 0) {
405 LOG_ERR("Failed to init CoAP message");
406 goto end;
407 }
408
409 net_hexdump("ACK", request.data, request.offset);
410
411 r = send(sock, request.data, request.offset, 0);
412 if (r < 0) {
413 LOG_ERR("Failed to send CoAP ACK");
414 }
415 end:
416 k_free(data);
417 }
418
419
obs_notification_cb(const struct coap_packet * response,struct coap_reply * reply,const struct sockaddr * from)420 static int obs_notification_cb(const struct coap_packet *response,
421 struct coap_reply *reply,
422 const struct sockaddr *from)
423 {
424 uint16_t id = coap_header_get_id(response);
425 uint8_t type = coap_header_get_type(response);
426 uint8_t *counter = (uint8_t *)reply->user_data;
427
428 ARG_UNUSED(from);
429
430 printk("\nCoAP OBS Notification\n");
431
432 (*counter)++;
433
434 if (type == COAP_TYPE_CON) {
435 send_obs_reply_ack(id);
436 }
437
438 return 0;
439 }
440
process_obs_coap_reply(struct coap_reply * reply)441 static int process_obs_coap_reply(struct coap_reply *reply)
442 {
443 struct coap_packet reply_msg;
444 uint8_t *data;
445 int rcvd;
446 int ret;
447
448 wait();
449
450 data = (uint8_t *)k_malloc(MAX_COAP_MSG_LEN);
451 if (!data) {
452 return -ENOMEM;
453 }
454
455 rcvd = recv(sock, data, MAX_COAP_MSG_LEN, MSG_DONTWAIT);
456 if (rcvd == 0) {
457 ret = -EIO;
458 goto end;
459 }
460
461 if (rcvd < 0) {
462 if (errno == EAGAIN || errno == EWOULDBLOCK) {
463 ret = 0;
464 } else {
465 ret = -errno;
466 }
467
468 goto end;
469 }
470
471 net_hexdump("Response", data, rcvd);
472
473 ret = coap_packet_parse(&reply_msg, data, rcvd, NULL, 0);
474 if (ret < 0) {
475 LOG_ERR("Invalid data received");
476 goto end;
477 }
478
479 if (coap_response_received(&reply_msg, NULL, reply, 1) == NULL) {
480 printk("\nOther response received\n");
481 }
482
483 end:
484 k_free(data);
485
486 return ret;
487 }
488
send_obs_coap_request(struct coap_reply * reply,void * user_data)489 static int send_obs_coap_request(struct coap_reply *reply, void *user_data)
490 {
491 struct coap_packet request;
492 const char * const *p;
493 uint8_t *data;
494 int r;
495
496 data = (uint8_t *)k_malloc(MAX_COAP_MSG_LEN);
497 if (!data) {
498 return -ENOMEM;
499 }
500
501 r = coap_packet_init(&request, data, MAX_COAP_MSG_LEN,
502 COAP_VERSION_1, COAP_TYPE_CON,
503 COAP_TOKEN_MAX_LEN, coap_next_token(),
504 COAP_METHOD_GET, coap_next_id());
505 if (r < 0) {
506 LOG_ERR("Failed to init CoAP message");
507 goto end;
508 }
509
510 r = coap_append_option_int(&request, COAP_OPTION_OBSERVE, 0);
511 if (r < 0) {
512 LOG_ERR("Failed to append Observe option");
513 goto end;
514 }
515
516 for (p = obs_path; p && *p; p++) {
517 r = coap_packet_append_option(&request, COAP_OPTION_URI_PATH,
518 *p, strlen(*p));
519 if (r < 0) {
520 LOG_ERR("Unable add option to request");
521 goto end;
522 }
523 }
524
525 net_hexdump("Request", request.data, request.offset);
526
527 coap_reply_init(reply, &request);
528 reply->reply = obs_notification_cb;
529 reply->user_data = user_data;
530
531 r = send(sock, request.data, request.offset, 0);
532
533 end:
534 k_free(data);
535
536 return r;
537 }
538
send_obs_reset_coap_request(struct coap_reply * reply)539 static int send_obs_reset_coap_request(struct coap_reply *reply)
540 {
541 struct coap_packet request;
542 const char * const *p;
543 uint8_t *data;
544 int r;
545
546 data = (uint8_t *)k_malloc(MAX_COAP_MSG_LEN);
547 if (!data) {
548 return -ENOMEM;
549 }
550
551 r = coap_packet_init(&request, data, MAX_COAP_MSG_LEN,
552 COAP_VERSION_1, COAP_TYPE_CON,
553 reply->tkl, reply->token,
554 COAP_METHOD_GET, coap_next_id());
555 if (r < 0) {
556 LOG_ERR("Failed to init CoAP message");
557 goto end;
558 }
559
560 r = coap_append_option_int(&request, COAP_OPTION_OBSERVE, 1);
561 if (r < 0) {
562 LOG_ERR("Failed to append Observe option");
563 goto end;
564 }
565
566 for (p = obs_path; p && *p; p++) {
567 r = coap_packet_append_option(&request, COAP_OPTION_URI_PATH,
568 *p, strlen(*p));
569 if (r < 0) {
570 LOG_ERR("Unable add option to request");
571 goto end;
572 }
573 }
574
575 net_hexdump("Request", request.data, request.offset);
576
577 r = send(sock, request.data, request.offset, 0);
578
579 end:
580 k_free(data);
581
582 return r;
583 }
584
register_observer(void)585 static int register_observer(void)
586 {
587 struct coap_reply reply;
588 uint8_t counter = 0;
589 int r;
590
591 printk("\nCoAP client OBS GET\n");
592 r = send_obs_coap_request(&reply, &counter);
593 if (r < 0) {
594 return r;
595 }
596
597 while (1) {
598 r = process_obs_coap_reply(&reply);
599 if (r < 0) {
600 return r;
601 }
602
603 if (counter >= 5) {
604 /* TODO: Functionality can be verified byt waiting for
605 * some time and make sure client shouldn't receive
606 * any notifications. If client still receives
607 * notifications means, Observer is not removed.
608 */
609 r = send_obs_reset_coap_request(&reply);
610 if (r < 0) {
611 return r;
612 }
613
614 /* Wait for the final ACK */
615 r = process_obs_coap_reply(&reply);
616 if (r < 0) {
617 return r;
618 }
619
620 break;
621 }
622 }
623
624 return 0;
625 }
626
main(void)627 int main(void)
628 {
629 int r;
630
631 LOG_DBG("Start CoAP-client sample");
632 r = start_coap_client();
633 if (r < 0) {
634 goto quit;
635 }
636
637 /* GET, PUT, POST, DELETE */
638 r = send_simple_coap_msgs_and_wait_for_reply();
639 if (r < 0) {
640 goto quit;
641 }
642
643 /* Block-wise transfer */
644 r = get_large_coap_msgs();
645 if (r < 0) {
646 goto quit;
647 }
648
649 /* Register observer, get notifications and unregister */
650 r = register_observer();
651 if (r < 0) {
652 goto quit;
653 }
654
655 /* Close the socket */
656 (void)close(sock);
657
658 LOG_DBG("Done");
659
660 return 0;
661
662 quit:
663 (void)close(sock);
664
665 LOG_ERR("quit");
666 return 0;
667 }
668