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_client_sample, LOG_LEVEL_DBG);
9
10 #include <errno.h>
11 #include <sys/printk.h>
12 #include <sys/byteorder.h>
13 #include <zephyr.h>
14
15 #include <net/socket.h>
16 #include <net/net_mgmt.h>
17 #include <net/net_ip.h>
18 #include <net/udp.h>
19 #include <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 0;
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,uint8_t * token,uint8_t tkl)391 static int send_obs_reply_ack(uint16_t id, uint8_t *token, uint8_t tkl)
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 -ENOMEM;
400 }
401
402 r = coap_packet_init(&request, data, MAX_COAP_MSG_LEN,
403 COAP_VERSION_1, COAP_TYPE_ACK, tkl, token, 0, id);
404 if (r < 0) {
405 LOG_ERR("Failed to init CoAP message");
406 goto end;
407 }
408
409 net_hexdump("Request", request.data, request.offset);
410
411 r = send(sock, request.data, request.offset, 0);
412 end:
413 k_free(data);
414
415 return r;
416 }
417
process_obs_coap_reply(void)418 static int process_obs_coap_reply(void)
419 {
420 struct coap_packet reply;
421 uint16_t id;
422 uint8_t token[COAP_TOKEN_MAX_LEN];
423 uint8_t *data;
424 uint8_t type;
425 uint8_t tkl;
426 int rcvd;
427 int ret;
428
429 wait();
430
431 data = (uint8_t *)k_malloc(MAX_COAP_MSG_LEN);
432 if (!data) {
433 return -ENOMEM;
434 }
435
436 rcvd = recv(sock, data, MAX_COAP_MSG_LEN, MSG_DONTWAIT);
437 if (rcvd == 0) {
438 ret = -EIO;
439 goto end;
440 }
441
442 if (rcvd < 0) {
443 if (errno == EAGAIN || errno == EWOULDBLOCK) {
444 ret = 0;
445 } else {
446 ret = -errno;
447 }
448
449 goto end;
450 }
451
452 net_hexdump("Response", data, rcvd);
453
454 ret = coap_packet_parse(&reply, data, rcvd, NULL, 0);
455 if (ret < 0) {
456 LOG_ERR("Invalid data received");
457 goto end;
458 }
459
460 tkl = coap_header_get_token(&reply, token);
461 id = coap_header_get_id(&reply);
462
463 type = coap_header_get_type(&reply);
464 if (type == COAP_TYPE_ACK) {
465 ret = 0;
466 } else if (type == COAP_TYPE_CON) {
467 ret = send_obs_reply_ack(id, token, tkl);
468 }
469 end:
470 k_free(data);
471
472 return ret;
473 }
474
send_obs_coap_request(void)475 static int send_obs_coap_request(void)
476 {
477 struct coap_packet request;
478 const char * const *p;
479 uint8_t *data;
480 int r;
481
482 data = (uint8_t *)k_malloc(MAX_COAP_MSG_LEN);
483 if (!data) {
484 return -ENOMEM;
485 }
486
487 r = coap_packet_init(&request, data, MAX_COAP_MSG_LEN,
488 COAP_VERSION_1, COAP_TYPE_CON,
489 COAP_TOKEN_MAX_LEN, coap_next_token(),
490 COAP_METHOD_GET, coap_next_id());
491 if (r < 0) {
492 LOG_ERR("Failed to init CoAP message");
493 goto end;
494 }
495
496 r = coap_append_option_int(&request, COAP_OPTION_OBSERVE, 0);
497 if (r < 0) {
498 LOG_ERR("Failed to append Observe option");
499 goto end;
500 }
501
502 for (p = obs_path; p && *p; p++) {
503 r = coap_packet_append_option(&request, COAP_OPTION_URI_PATH,
504 *p, strlen(*p));
505 if (r < 0) {
506 LOG_ERR("Unable add option to request");
507 goto end;
508 }
509 }
510
511 net_hexdump("Request", request.data, request.offset);
512
513 r = send(sock, request.data, request.offset, 0);
514
515 end:
516 k_free(data);
517
518 return r;
519 }
520
send_obs_reset_coap_request(void)521 static int send_obs_reset_coap_request(void)
522 {
523 struct coap_packet request;
524 const char * const *p;
525 uint8_t *data;
526 int r;
527
528 data = (uint8_t *)k_malloc(MAX_COAP_MSG_LEN);
529 if (!data) {
530 return -ENOMEM;
531 }
532
533 r = coap_packet_init(&request, data, MAX_COAP_MSG_LEN,
534 COAP_VERSION_1, COAP_TYPE_RESET,
535 COAP_TOKEN_MAX_LEN, coap_next_token(),
536 0, coap_next_id());
537 if (r < 0) {
538 LOG_ERR("Failed to init CoAP message");
539 goto end;
540 }
541
542 r = coap_append_option_int(&request, COAP_OPTION_OBSERVE, 0);
543 if (r < 0) {
544 LOG_ERR("Failed to append Observe option");
545 goto end;
546 }
547
548 for (p = obs_path; p && *p; p++) {
549 r = coap_packet_append_option(&request, COAP_OPTION_URI_PATH,
550 *p, strlen(*p));
551 if (r < 0) {
552 LOG_ERR("Unable add option to request");
553 goto end;
554 }
555 }
556
557 net_hexdump("Request", request.data, request.offset);
558
559 r = send(sock, request.data, request.offset, 0);
560
561 end:
562 k_free(data);
563
564 return r;
565 }
566
register_observer(void)567 static int register_observer(void)
568 {
569 uint8_t counter = 0U;
570 int r;
571
572 while (1) {
573 /* Test CoAP OBS GET method */
574 if (!counter) {
575 printk("\nCoAP client OBS GET\n");
576 r = send_obs_coap_request();
577 if (r < 0) {
578 return r;
579 }
580 } else {
581 printk("\nCoAP OBS Notification\n");
582 }
583
584 r = process_obs_coap_reply();
585 if (r < 0) {
586 return r;
587 }
588
589 counter++;
590
591 /* Unregister */
592 if (counter == 5U) {
593 /* TODO: Functionality can be verified byt waiting for
594 * some time and make sure client shouldn't receive
595 * any notifications. If client still receives
596 * notifications means, Observer is not removed.
597 */
598 return send_obs_reset_coap_request();
599 }
600 }
601
602 return 0;
603 }
604
main(void)605 void main(void)
606 {
607 int r;
608
609 LOG_DBG("Start CoAP-client sample");
610 r = start_coap_client();
611 if (r < 0) {
612 goto quit;
613 }
614
615 /* GET, PUT, POST, DELETE */
616 r = send_simple_coap_msgs_and_wait_for_reply();
617 if (r < 0) {
618 goto quit;
619 }
620
621 /* Block-wise transfer */
622 r = get_large_coap_msgs();
623 if (r < 0) {
624 goto quit;
625 }
626
627 /* Register observer, get notifications and unregister */
628 r = register_observer();
629 if (r < 0) {
630 goto quit;
631 }
632
633 /* Close the socket */
634 (void)close(sock);
635
636 LOG_DBG("Done");
637
638 return;
639
640 quit:
641 (void)close(sock);
642
643 LOG_ERR("quit");
644 }
645