1 /*
2 * Copyright (c) 2016 Intel Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/logging/log.h>
8 LOG_MODULE_DECLARE(net_zperf, CONFIG_NET_ZPERF_LOG_LEVEL);
9
10 #include <ctype.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <strings.h>
14
15 #include <zephyr/kernel.h>
16 #include <zephyr/shell/shell.h>
17
18 #include <zephyr/net/net_ip.h>
19 #include <zephyr/net/net_core.h>
20 #include <zephyr/net/socket.h>
21 #include <zephyr/net/zperf.h>
22
23 #include "zperf_internal.h"
24 #include "zperf_session.h"
25
26 /* Get some useful debug routings from net_private.h, requires
27 * that NET_LOG_ENABLED is set.
28 */
29 #define NET_LOG_ENABLED 1
30 #include "net_private.h"
31
32 #include "ipv6.h" /* to get infinite lifetime */
33
34
35 static const char *CONFIG =
36 "unified"
37 #if defined(CONFIG_WIFI)
38 " wifi"
39 #endif
40 #if defined(CONFIG_NET_L2_ETHERNET)
41 " ethernet"
42 #endif
43 #if defined(CONFIG_NET_IPV4)
44 " ipv4"
45 #endif
46 #if defined(CONFIG_NET_IPV6)
47 " ipv6"
48 #endif
49 "";
50
51 static struct sockaddr_in6 in6_addr_my = {
52 .sin6_family = AF_INET6,
53 .sin6_port = htons(MY_SRC_PORT),
54 };
55
56 static struct sockaddr_in6 in6_addr_dst = {
57 .sin6_family = AF_INET6,
58 .sin6_port = htons(DEF_PORT),
59 };
60
61 static struct sockaddr_in in4_addr_dst = {
62 .sin_family = AF_INET,
63 .sin_port = htons(DEF_PORT),
64 };
65
66 static struct sockaddr_in in4_addr_my = {
67 .sin_family = AF_INET,
68 .sin_port = htons(MY_SRC_PORT),
69 };
70
71 static struct in6_addr shell_ipv6;
72
73 static struct in_addr shell_ipv4;
74
75 #define DEVICE_NAME "zperf shell"
76
77 const uint32_t TIME_US[] = { 60 * 1000 * 1000, 1000 * 1000, 1000, 0 };
78 const char *TIME_US_UNIT[] = { "m", "s", "ms", "us" };
79 const uint32_t KBPS[] = { 1000, 0 };
80 const char *KBPS_UNIT[] = { "Mbps", "Kbps" };
81 const uint32_t K[] = { 1000 * 1000, 1000, 0 };
82 const char *K_UNIT[] = { "M", "K", "" };
83
print_number(const struct shell * sh,uint32_t value,const uint32_t * divisor_arr,const char ** units)84 static void print_number(const struct shell *sh, uint32_t value,
85 const uint32_t *divisor_arr, const char **units)
86 {
87 const char **unit;
88 const uint32_t *divisor;
89 uint32_t dec, radix;
90
91 unit = units;
92 divisor = divisor_arr;
93
94 while (value < *divisor) {
95 divisor++;
96 unit++;
97 }
98
99 if (*divisor != 0U) {
100 radix = value / *divisor;
101 dec = (value % *divisor) * 100U / *divisor;
102 shell_fprintf(sh, SHELL_NORMAL, "%u.%s%u %s", radix,
103 (dec < 10) ? "0" : "", dec, *unit);
104 } else {
105 shell_fprintf(sh, SHELL_NORMAL, "%u %s", value, *unit);
106 }
107 }
108
print_number_64(const struct shell * sh,uint64_t value,const uint32_t * divisor_arr,const char ** units)109 static void print_number_64(const struct shell *sh, uint64_t value,
110 const uint32_t *divisor_arr, const char **units)
111 {
112 const char **unit;
113 const uint32_t *divisor;
114 uint32_t dec;
115 uint64_t radix;
116
117 unit = units;
118 divisor = divisor_arr;
119
120 while (value < *divisor) {
121 divisor++;
122 unit++;
123 }
124
125 if (*divisor != 0U) {
126 radix = value / *divisor;
127 dec = (value % *divisor) * 100U / *divisor;
128 shell_fprintf(sh, SHELL_NORMAL, "%llu.%s%u %s", radix,
129 (dec < 10) ? "0" : "", dec, *unit);
130 } else {
131 shell_fprintf(sh, SHELL_NORMAL, "%llu %s", value, *unit);
132 }
133 }
134
parse_number(const char * string,const uint32_t * divisor_arr,const char ** units)135 static long parse_number(const char *string, const uint32_t *divisor_arr,
136 const char **units)
137 {
138 const char **unit;
139 const uint32_t *divisor;
140 char *suffix;
141 long dec;
142 int cmp;
143
144 dec = strtoul(string, &suffix, 10);
145 unit = units;
146 divisor = divisor_arr;
147
148 do {
149 cmp = strncasecmp(suffix, *unit++, 1);
150 } while (cmp != 0 && *++divisor != 0U);
151
152 return (*divisor == 0U) ? dec : dec * *divisor;
153 }
154
parse_ipv6_addr(const struct shell * sh,char * host,char * port,struct sockaddr_in6 * addr)155 static int parse_ipv6_addr(const struct shell *sh, char *host, char *port,
156 struct sockaddr_in6 *addr)
157 {
158 int ret;
159
160 if (!host) {
161 return -EINVAL;
162 }
163
164 ret = net_addr_pton(AF_INET6, host, &addr->sin6_addr);
165 if (ret < 0) {
166 return -EDESTADDRREQ;
167 }
168
169 addr->sin6_port = htons(strtoul(port, NULL, 10));
170 if (!addr->sin6_port) {
171 shell_fprintf(sh, SHELL_WARNING,
172 "Invalid port %s\n", port);
173 return -EINVAL;
174 }
175
176 return 0;
177 }
178
parse_ipv4_addr(const struct shell * sh,char * host,char * port,struct sockaddr_in * addr)179 static int parse_ipv4_addr(const struct shell *sh, char *host, char *port,
180 struct sockaddr_in *addr)
181 {
182 int ret;
183
184 if (!host) {
185 return -EINVAL;
186 }
187
188 ret = net_addr_pton(AF_INET, host, &addr->sin_addr);
189 if (ret < 0) {
190 return -EDESTADDRREQ;
191 }
192
193 addr->sin_port = htons(strtoul(port, NULL, 10));
194 if (!addr->sin_port) {
195 shell_fprintf(sh, SHELL_WARNING,
196 "Invalid port %s\n", port);
197 return -EINVAL;
198 }
199
200 return 0;
201 }
202
zperf_bind_host(const struct shell * sh,size_t argc,char * argv[],struct zperf_download_params * param)203 static int zperf_bind_host(const struct shell *sh,
204 size_t argc, char *argv[],
205 struct zperf_download_params *param)
206 {
207 int ret;
208
209 /* Parse options */
210 if (argc >= 2) {
211 param->port = strtoul(argv[1], NULL, 10);
212 } else {
213 param->port = DEF_PORT;
214 }
215
216 if (argc >= 3) {
217 char *addr_str = argv[2];
218 struct sockaddr addr;
219
220 memset(&addr, 0, sizeof(addr));
221
222 ret = net_ipaddr_parse(addr_str, strlen(addr_str), &addr);
223 if (ret < 0) {
224 shell_fprintf(sh, SHELL_WARNING,
225 "Cannot parse address \"%s\"\n",
226 addr_str);
227 return ret;
228 }
229
230 memcpy(¶m->addr, &addr, sizeof(struct sockaddr));
231 }
232
233 return 0;
234 }
235
cmd_setip(const struct shell * sh,size_t argc,char * argv[])236 static int cmd_setip(const struct shell *sh, size_t argc, char *argv[])
237 {
238 int start = 0;
239
240 if (IS_ENABLED(CONFIG_NET_IPV6) && !IS_ENABLED(CONFIG_NET_IPV4)) {
241 if (argc != 3) {
242 shell_help(sh);
243 return -ENOEXEC;
244 }
245
246 if (zperf_get_ipv6_addr(argv[start + 1], argv[start + 2],
247 &shell_ipv6) < 0) {
248 shell_fprintf(sh, SHELL_WARNING,
249 "Unable to set %s address (%s disabled)\n", "IPv6", "IPv4");
250 return 0;
251 }
252
253 shell_fprintf(sh, SHELL_NORMAL,
254 "Setting IP address %s\n",
255 net_sprint_ipv6_addr(&shell_ipv6));
256 }
257
258 if (IS_ENABLED(CONFIG_NET_IPV4) && !IS_ENABLED(CONFIG_NET_IPV6)) {
259 if (argc != 2) {
260 shell_help(sh);
261 return -ENOEXEC;
262 }
263
264 if (zperf_get_ipv4_addr(argv[start + 1], &shell_ipv4) < 0) {
265 shell_fprintf(sh, SHELL_WARNING,
266 "Unable to set %s address (%s disabled)\n", "IPv4", "IPv6");
267 return -ENOEXEC;
268 }
269
270 shell_fprintf(sh, SHELL_NORMAL,
271 "Setting IP address %s\n",
272 net_sprint_ipv4_addr(&shell_ipv4));
273 }
274
275 if (IS_ENABLED(CONFIG_NET_IPV6) && IS_ENABLED(CONFIG_NET_IPV4)) {
276 if (net_addr_pton(AF_INET6, argv[start + 1], &shell_ipv6) < 0) {
277 if (argc != 2) {
278 shell_help(sh);
279 return -ENOEXEC;
280 }
281
282 if (zperf_get_ipv4_addr(argv[start + 1],
283 &shell_ipv4) < 0) {
284 shell_fprintf(sh, SHELL_WARNING,
285 "Unable to set %s address\n", "IPv4");
286 return -ENOEXEC;
287 }
288
289 shell_fprintf(sh, SHELL_NORMAL,
290 "Setting IP address %s\n",
291 net_sprint_ipv4_addr(&shell_ipv4));
292 } else {
293 if (argc != 3) {
294 shell_help(sh);
295 return -ENOEXEC;
296 }
297
298 if (zperf_get_ipv6_addr(argv[start + 1],
299 argv[start + 2], &shell_ipv6) < 0) {
300 shell_fprintf(sh, SHELL_WARNING,
301 "Unable to set %s address\n", "IPv6");
302 return -ENOEXEC;
303 }
304
305 shell_fprintf(sh, SHELL_NORMAL,
306 "Setting IP address %s\n",
307 net_sprint_ipv6_addr(&shell_ipv6));
308 }
309 }
310
311 return 0;
312 }
313
udp_session_cb(enum zperf_status status,struct zperf_results * result,void * user_data)314 static void udp_session_cb(enum zperf_status status,
315 struct zperf_results *result,
316 void *user_data)
317 {
318 const struct shell *sh = user_data;
319
320 switch (status) {
321 case ZPERF_SESSION_STARTED:
322 shell_fprintf(sh, SHELL_NORMAL, "New session started.\n");
323 break;
324
325 case ZPERF_SESSION_FINISHED: {
326 uint32_t rate_in_kbps;
327
328 /* Compute baud rate */
329 if (result->time_in_us != 0U) {
330 rate_in_kbps = (uint32_t)
331 ((result->total_len * 8ULL * USEC_PER_SEC) /
332 (result->time_in_us * 1000ULL));
333 } else {
334 rate_in_kbps = 0U;
335 }
336
337 shell_fprintf(sh, SHELL_NORMAL, "End of session!\n");
338
339 shell_fprintf(sh, SHELL_NORMAL, " duration:\t\t");
340 print_number_64(sh, result->time_in_us, TIME_US, TIME_US_UNIT);
341 shell_fprintf(sh, SHELL_NORMAL, "\n");
342
343 shell_fprintf(sh, SHELL_NORMAL, " received packets:\t%u\n",
344 result->nb_packets_rcvd);
345 shell_fprintf(sh, SHELL_NORMAL, " nb packets lost:\t%u\n",
346 result->nb_packets_lost);
347 shell_fprintf(sh, SHELL_NORMAL, " nb packets outorder:\t%u\n",
348 result->nb_packets_outorder);
349
350 shell_fprintf(sh, SHELL_NORMAL, " jitter:\t\t\t");
351 print_number(sh, result->jitter_in_us, TIME_US, TIME_US_UNIT);
352 shell_fprintf(sh, SHELL_NORMAL, "\n");
353
354 shell_fprintf(sh, SHELL_NORMAL, " rate:\t\t\t");
355 print_number(sh, rate_in_kbps, KBPS, KBPS_UNIT);
356 shell_fprintf(sh, SHELL_NORMAL, "\n");
357
358 break;
359 }
360
361 case ZPERF_SESSION_ERROR:
362 shell_fprintf(sh, SHELL_ERROR, "UDP session error.\n");
363 break;
364
365 default:
366 break;
367 }
368 }
369
370 /*
371 * parse download options with '-'
372 * return < 0 if parse error
373 * return 0 if no '-' options
374 * return > 0 num of argc we parsed
375 * and following parse starts from this num
376 */
shell_cmd_download(const struct shell * sh,size_t argc,char * argv[],struct zperf_download_params * param)377 static int shell_cmd_download(const struct shell *sh, size_t argc,
378 char *argv[],
379 struct zperf_download_params *param)
380 {
381 int opt_cnt = 0;
382 size_t i;
383
384 for (i = 1; i < argc; ++i) {
385 if (*argv[i] != '-') {
386 break;
387 }
388
389 switch (argv[i][1]) {
390 case 'I':
391 /*
392 * IFNAMSIZ by default CONFIG_NET_INTERFACE_NAME_LEN
393 * is at least 1 so no overflow risk here
394 */
395 i++;
396 if (i >= argc) {
397 shell_fprintf(sh, SHELL_WARNING,
398 "-I <interface name>\n");
399 return -ENOEXEC;
400 }
401 (void)memset(param->if_name, 0x0, IFNAMSIZ);
402 strncpy(param->if_name, argv[i], IFNAMSIZ - 1);
403
404 opt_cnt += 2;
405 break;
406
407 default:
408 shell_fprintf(sh, SHELL_WARNING,
409 "Unrecognized argument: %s\n", argv[i]);
410 return -ENOEXEC;
411 }
412 }
413
414 return opt_cnt;
415 }
416
cmd_udp_download_stop(const struct shell * sh,size_t argc,char * argv[])417 static int cmd_udp_download_stop(const struct shell *sh, size_t argc,
418 char *argv[])
419 {
420 int ret;
421
422 ret = zperf_udp_download_stop();
423 if (ret < 0) {
424 shell_fprintf(sh, SHELL_WARNING, "UDP server not running!\n");
425 return -ENOEXEC;
426 }
427
428 shell_fprintf(sh, SHELL_NORMAL, "UDP server stopped\n");
429
430 return 0;
431 }
432
cmd_udp_download(const struct shell * sh,size_t argc,char * argv[])433 static int cmd_udp_download(const struct shell *sh, size_t argc,
434 char *argv[])
435 {
436 if (IS_ENABLED(CONFIG_NET_UDP)) {
437 struct zperf_download_params param = { 0 };
438 int ret;
439 int start;
440
441 start = shell_cmd_download(sh, argc, argv, ¶m);
442 if (start < 0) {
443 shell_fprintf(sh, SHELL_WARNING,
444 "Unable to parse option.\n");
445 return -ENOEXEC;
446 }
447
448 ret = zperf_bind_host(sh, argc - start, &argv[start], ¶m);
449 if (ret < 0) {
450 shell_fprintf(sh, SHELL_WARNING,
451 "Unable to bind host.\n");
452 shell_help(sh);
453 return -ENOEXEC;
454 }
455
456 ret = zperf_udp_download(¶m, udp_session_cb, (void *)sh);
457 if (ret == -EALREADY) {
458 shell_fprintf(sh, SHELL_WARNING,
459 "UDP server already started!\n");
460 return -ENOEXEC;
461 } else if (ret < 0) {
462 shell_fprintf(sh, SHELL_ERROR,
463 "Failed to start UDP server!\n");
464 return -ENOEXEC;
465 }
466
467 k_yield();
468
469 shell_fprintf(sh, SHELL_NORMAL,
470 "UDP server started on port %u\n", param.port);
471
472 return 0;
473 } else {
474 return -ENOTSUP;
475 }
476 }
477
shell_udp_upload_print_stats(const struct shell * sh,struct zperf_results * results)478 static void shell_udp_upload_print_stats(const struct shell *sh,
479 struct zperf_results *results)
480 {
481 if (IS_ENABLED(CONFIG_NET_UDP)) {
482 uint64_t rate_in_kbps, client_rate_in_kbps;
483
484 shell_fprintf(sh, SHELL_NORMAL, "-\nUpload completed!\n");
485
486 if (results->time_in_us != 0U) {
487 rate_in_kbps = (uint32_t)
488 ((results->total_len * 8 * USEC_PER_SEC) /
489 (results->time_in_us * 1000U));
490 } else {
491 rate_in_kbps = 0U;
492 }
493
494 if (results->client_time_in_us != 0U) {
495 client_rate_in_kbps = (uint32_t)
496 (((uint64_t)results->nb_packets_sent *
497 (uint64_t)results->packet_size * (uint64_t)8 *
498 (uint64_t)USEC_PER_SEC) /
499 (results->client_time_in_us * 1000U));
500 } else {
501 client_rate_in_kbps = 0U;
502 }
503
504 if (!rate_in_kbps) {
505 shell_fprintf(sh, SHELL_ERROR,
506 "LAST PACKET NOT RECEIVED!!!\n");
507 }
508
509 shell_fprintf(sh, SHELL_NORMAL,
510 "Statistics:\t\tserver\t(client)\n");
511 shell_fprintf(sh, SHELL_NORMAL, "Duration:\t\t");
512 print_number_64(sh, results->time_in_us, TIME_US,
513 TIME_US_UNIT);
514 shell_fprintf(sh, SHELL_NORMAL, "\t(");
515 print_number_64(sh, results->client_time_in_us, TIME_US,
516 TIME_US_UNIT);
517 shell_fprintf(sh, SHELL_NORMAL, ")\n");
518
519 shell_fprintf(sh, SHELL_NORMAL, "Num packets:\t\t%u\t(%u)\n",
520 results->nb_packets_rcvd,
521 results->nb_packets_sent);
522
523 shell_fprintf(sh, SHELL_NORMAL,
524 "Num packets out order:\t%u\n",
525 results->nb_packets_outorder);
526 shell_fprintf(sh, SHELL_NORMAL, "Num packets lost:\t%u\n",
527 results->nb_packets_lost);
528
529 shell_fprintf(sh, SHELL_NORMAL, "Jitter:\t\t\t");
530 print_number(sh, results->jitter_in_us, TIME_US,
531 TIME_US_UNIT);
532 shell_fprintf(sh, SHELL_NORMAL, "\n");
533
534 shell_fprintf(sh, SHELL_NORMAL, "Rate:\t\t\t");
535 print_number(sh, rate_in_kbps, KBPS, KBPS_UNIT);
536 shell_fprintf(sh, SHELL_NORMAL, "\t(");
537 print_number(sh, client_rate_in_kbps, KBPS, KBPS_UNIT);
538 shell_fprintf(sh, SHELL_NORMAL, ")\n");
539 }
540 }
541
shell_tcp_upload_print_stats(const struct shell * sh,struct zperf_results * results)542 static void shell_tcp_upload_print_stats(const struct shell *sh,
543 struct zperf_results *results)
544 {
545 if (IS_ENABLED(CONFIG_NET_TCP)) {
546 uint64_t client_rate_in_kbps;
547
548 shell_fprintf(sh, SHELL_NORMAL, "-\nUpload completed!\n");
549
550 if (results->client_time_in_us != 0U) {
551 client_rate_in_kbps = (uint32_t)
552 (((uint64_t)results->nb_packets_sent *
553 (uint64_t)results->packet_size * (uint64_t)8 *
554 (uint64_t)USEC_PER_SEC) /
555 (results->client_time_in_us * 1000U));
556 } else {
557 client_rate_in_kbps = 0U;
558 }
559
560 shell_fprintf(sh, SHELL_NORMAL, "Duration:\t");
561 print_number_64(sh, results->client_time_in_us,
562 TIME_US, TIME_US_UNIT);
563 shell_fprintf(sh, SHELL_NORMAL, "\n");
564 shell_fprintf(sh, SHELL_NORMAL, "Num packets:\t%u\n",
565 results->nb_packets_sent);
566 shell_fprintf(sh, SHELL_NORMAL,
567 "Num errors:\t%u (retry or fail)\n",
568 results->nb_packets_errors);
569 shell_fprintf(sh, SHELL_NORMAL, "Rate:\t\t");
570 print_number(sh, client_rate_in_kbps, KBPS, KBPS_UNIT);
571 shell_fprintf(sh, SHELL_NORMAL, "\n");
572 }
573 }
574
shell_tcp_upload_print_periodic(const struct shell * sh,struct zperf_results * results)575 static void shell_tcp_upload_print_periodic(const struct shell *sh,
576 struct zperf_results *results)
577 {
578 if (IS_ENABLED(CONFIG_NET_TCP)) {
579 uint64_t client_rate_in_kbps;
580
581 if (results->client_time_in_us != 0U) {
582 client_rate_in_kbps = (uint32_t)
583 (((uint64_t)results->nb_packets_sent *
584 (uint64_t)results->packet_size * (uint64_t)8 *
585 (uint64_t)USEC_PER_SEC) /
586 (results->client_time_in_us * 1000U));
587 } else {
588 client_rate_in_kbps = 0U;
589 }
590
591 shell_fprintf(sh, SHELL_NORMAL, "Duration: ");
592 print_number_64(sh, results->client_time_in_us,
593 TIME_US, TIME_US_UNIT);
594 shell_fprintf(sh, SHELL_NORMAL, " | ");
595 shell_fprintf(sh, SHELL_NORMAL, "Packets: %6u | ",
596 results->nb_packets_sent);
597 shell_fprintf(sh, SHELL_NORMAL,
598 "Errors: %6u | ",
599 results->nb_packets_errors);
600 shell_fprintf(sh, SHELL_NORMAL, "Rate: ");
601 print_number(sh, client_rate_in_kbps, KBPS, KBPS_UNIT);
602 shell_fprintf(sh, SHELL_NORMAL, "\n");
603 }
604 }
605
udp_upload_cb(enum zperf_status status,struct zperf_results * result,void * user_data)606 static void udp_upload_cb(enum zperf_status status,
607 struct zperf_results *result,
608 void *user_data)
609 {
610 const struct shell *sh = user_data;
611
612 switch (status) {
613 case ZPERF_SESSION_STARTED:
614 break;
615
616 case ZPERF_SESSION_FINISHED: {
617 shell_udp_upload_print_stats(sh, result);
618 break;
619 }
620
621 case ZPERF_SESSION_ERROR:
622 shell_fprintf(sh, SHELL_ERROR, "UDP upload failed\n");
623 break;
624
625 default:
626 break;
627 }
628 }
629
tcp_upload_cb(enum zperf_status status,struct zperf_results * result,void * user_data)630 static void tcp_upload_cb(enum zperf_status status,
631 struct zperf_results *result,
632 void *user_data)
633 {
634 const struct shell *sh = user_data;
635
636 switch (status) {
637 case ZPERF_SESSION_STARTED:
638 break;
639
640 case ZPERF_SESSION_PERIODIC_RESULT:
641 shell_tcp_upload_print_periodic(sh, result);
642 break;
643
644 case ZPERF_SESSION_FINISHED: {
645 shell_tcp_upload_print_stats(sh, result);
646 break;
647 }
648
649 case ZPERF_SESSION_ERROR:
650 shell_fprintf(sh, SHELL_ERROR, "TCP upload failed\n");
651 break;
652 }
653 }
654
ping_handler(struct net_icmp_ctx * ctx,struct net_pkt * pkt,struct net_icmp_ip_hdr * ip_hdr,struct net_icmp_hdr * icmp_hdr,void * user_data)655 static int ping_handler(struct net_icmp_ctx *ctx,
656 struct net_pkt *pkt,
657 struct net_icmp_ip_hdr *ip_hdr,
658 struct net_icmp_hdr *icmp_hdr,
659 void *user_data)
660 {
661 struct k_sem *sem_wait = user_data;
662
663 ARG_UNUSED(ctx);
664 ARG_UNUSED(pkt);
665 ARG_UNUSED(ip_hdr);
666 ARG_UNUSED(icmp_hdr);
667
668 k_sem_give(sem_wait);
669
670 return 0;
671 }
672
send_ping(const struct shell * sh,struct in6_addr * addr,int timeout_ms)673 static void send_ping(const struct shell *sh,
674 struct in6_addr *addr,
675 int timeout_ms)
676 {
677 static struct k_sem sem_wait;
678 struct sockaddr_in6 dest_addr = { 0 };
679 struct net_icmp_ctx ctx;
680 int ret;
681
682 ret = net_icmp_init_ctx(&ctx, NET_ICMPV6_ECHO_REPLY, 0, ping_handler);
683 if (ret < 0) {
684 shell_fprintf(sh, SHELL_WARNING, "Cannot send ping (%d)\n", ret);
685 return;
686 }
687
688 dest_addr.sin6_family = AF_INET6;
689 net_ipv6_addr_copy_raw((uint8_t *)&dest_addr.sin6_addr, (uint8_t *)addr);
690
691 k_sem_init(&sem_wait, 0, 1);
692
693 (void)net_icmp_send_echo_request(&ctx,
694 net_if_get_default(),
695 (struct sockaddr *)&dest_addr,
696 NULL, &sem_wait);
697
698 ret = k_sem_take(&sem_wait, K_MSEC(timeout_ms));
699 if (ret == -EAGAIN) {
700 shell_fprintf(sh, SHELL_WARNING, "ping %s timeout\n",
701 net_sprint_ipv6_addr(addr));
702 }
703
704 (void)net_icmp_cleanup_ctx(&ctx);
705 }
706
execute_upload(const struct shell * sh,const struct zperf_upload_params * param,bool is_udp,bool async)707 static int execute_upload(const struct shell *sh,
708 const struct zperf_upload_params *param,
709 bool is_udp, bool async)
710 {
711 struct zperf_results results = { 0 };
712 int ret;
713
714 shell_fprintf(sh, SHELL_NORMAL, "Duration:\t");
715 print_number_64(sh, (uint64_t)param->duration_ms * USEC_PER_MSEC, TIME_US,
716 TIME_US_UNIT);
717 shell_fprintf(sh, SHELL_NORMAL, "\n");
718 shell_fprintf(sh, SHELL_NORMAL, "Packet size:\t%u bytes\n",
719 param->packet_size);
720 shell_fprintf(sh, SHELL_NORMAL, "Rate:\t\t%u kbps\n",
721 param->rate_kbps);
722 shell_fprintf(sh, SHELL_NORMAL, "Starting...\n");
723
724 if (IS_ENABLED(CONFIG_NET_IPV6) && param->peer_addr.sa_family == AF_INET6) {
725 struct sockaddr_in6 *ipv6 =
726 (struct sockaddr_in6 *)¶m->peer_addr;
727 /* For IPv6, we should make sure that neighbor discovery
728 * has been done for the peer. So send ping here, wait
729 * some time and start the test after that.
730 */
731 send_ping(sh, &ipv6->sin6_addr, MSEC_PER_SEC);
732 }
733
734 if (is_udp && IS_ENABLED(CONFIG_NET_UDP)) {
735 uint32_t packet_duration =
736 zperf_packet_duration(param->packet_size, param->rate_kbps);
737
738 shell_fprintf(sh, SHELL_NORMAL, "Rate:\t\t");
739 print_number(sh, param->rate_kbps, KBPS, KBPS_UNIT);
740 shell_fprintf(sh, SHELL_NORMAL, "\n");
741
742 if (packet_duration > 1000U) {
743 shell_fprintf(sh, SHELL_NORMAL, "Packet duration %u ms\n",
744 (unsigned int)(packet_duration / 1000U));
745 } else {
746 shell_fprintf(sh, SHELL_NORMAL, "Packet duration %u us\n",
747 (unsigned int)packet_duration);
748 }
749
750 if (async) {
751 ret = zperf_udp_upload_async(param, udp_upload_cb,
752 (void *)sh);
753 if (ret < 0) {
754 shell_fprintf(sh, SHELL_ERROR,
755 "Failed to start UDP async upload (%d)\n", ret);
756 return ret;
757 }
758 } else {
759 ret = zperf_udp_upload(param, &results);
760 if (ret < 0) {
761 shell_fprintf(sh, SHELL_ERROR,
762 "UDP upload failed (%d)\n", ret);
763 return ret;
764 }
765
766 shell_udp_upload_print_stats(sh, &results);
767 }
768 } else {
769 if (is_udp && !IS_ENABLED(CONFIG_NET_UDP)) {
770 shell_fprintf(sh, SHELL_WARNING,
771 "UDP not supported\n");
772 }
773 }
774
775 if (!is_udp && IS_ENABLED(CONFIG_NET_TCP)) {
776 if (async) {
777 ret = zperf_tcp_upload_async(param, tcp_upload_cb,
778 (void *)sh);
779 if (ret < 0) {
780 shell_fprintf(sh, SHELL_ERROR,
781 "Failed to start TCP async upload (%d)\n", ret);
782 return ret;
783 }
784 } else {
785 ret = zperf_tcp_upload(param, &results);
786 if (ret < 0) {
787 shell_fprintf(sh, SHELL_ERROR,
788 "TCP upload failed (%d)\n", ret);
789 return ret;
790 }
791
792 shell_tcp_upload_print_stats(sh, &results);
793 }
794 } else {
795 if (!is_udp && !IS_ENABLED(CONFIG_NET_TCP)) {
796 shell_fprintf(sh, SHELL_WARNING,
797 "TCP not supported\n");
798 }
799 }
800
801 return 0;
802 }
803
parse_arg(size_t * i,size_t argc,char * argv[])804 static int parse_arg(size_t *i, size_t argc, char *argv[])
805 {
806 int res = -1;
807 const char *str = argv[*i] + 2;
808 char *endptr;
809
810 if (*str == 0) {
811 if (*i + 1 >= argc) {
812 return -1;
813 }
814
815 *i += 1;
816 str = argv[*i];
817 }
818
819 errno = 0;
820 if (strncmp(str, "0x", 2) == 0) {
821 res = strtol(str, &endptr, 16);
822 } else {
823 res = strtol(str, &endptr, 10);
824 }
825
826 if (errno || (endptr == str)) {
827 return -1;
828 }
829
830 return res;
831 }
832
shell_cmd_upload(const struct shell * sh,size_t argc,char * argv[],enum net_ip_protocol proto)833 static int shell_cmd_upload(const struct shell *sh, size_t argc,
834 char *argv[], enum net_ip_protocol proto)
835 {
836 struct zperf_upload_params param = { 0 };
837 struct sockaddr_in6 ipv6 = { .sin6_family = AF_INET6 };
838 struct sockaddr_in ipv4 = { .sin_family = AF_INET };
839 char *port_str;
840 bool async = false;
841 bool is_udp;
842 int start = 0;
843 size_t opt_cnt = 0;
844 int ret;
845
846 param.options.priority = -1;
847 is_udp = proto == IPPROTO_UDP;
848
849 /* Parse options */
850 for (size_t i = 1; i < argc; ++i) {
851 if (*argv[i] != '-') {
852 break;
853 }
854
855 switch (argv[i][1]) {
856 case 'S': {
857 int tos = parse_arg(&i, argc, argv);
858
859 if (tos < 0 || tos > UINT8_MAX) {
860 shell_fprintf(sh, SHELL_WARNING,
861 "Parse error: %s\n", argv[i]);
862 return -ENOEXEC;
863 }
864
865 param.options.tos = tos;
866 opt_cnt += 2;
867 break;
868 }
869
870 case 'a':
871 async = true;
872 opt_cnt += 1;
873 break;
874
875 case 'n':
876 if (is_udp) {
877 shell_fprintf(sh, SHELL_WARNING,
878 "UDP does not support -n option\n");
879 return -ENOEXEC;
880 }
881 param.options.tcp_nodelay = 1;
882 opt_cnt += 1;
883 break;
884
885 #ifdef CONFIG_NET_CONTEXT_PRIORITY
886 case 'p':
887 param.options.priority = parse_arg(&i, argc, argv);
888 if (param.options.priority < 0 ||
889 param.options.priority > UINT8_MAX) {
890 shell_fprintf(sh, SHELL_WARNING,
891 "Parse error: %s\n", argv[i]);
892 return -ENOEXEC;
893 }
894 opt_cnt += 2;
895 break;
896 #endif /* CONFIG_NET_CONTEXT_PRIORITY */
897
898 case 'I':
899 i++;
900 if (i >= argc) {
901 shell_fprintf(sh, SHELL_WARNING,
902 "-I <interface name>\n");
903 return -ENOEXEC;
904 }
905 (void)memset(param.if_name, 0x0, IFNAMSIZ);
906 strncpy(param.if_name, argv[i], IFNAMSIZ - 1);
907
908 opt_cnt += 2;
909 break;
910
911 case 'i':
912 int seconds = parse_arg(&i, argc, argv);
913
914 if (is_udp) {
915 shell_fprintf(sh, SHELL_WARNING,
916 "UDP does not support -i option\n");
917 return -ENOEXEC;
918 }
919 if (seconds < 0 || seconds > UINT16_MAX) {
920 shell_fprintf(sh, SHELL_WARNING,
921 "Parse error: %s\n", argv[i]);
922 return -ENOEXEC;
923 }
924
925 param.options.report_interval_ms = seconds * MSEC_PER_SEC;
926 opt_cnt += 2;
927 break;
928
929 default:
930 shell_fprintf(sh, SHELL_WARNING,
931 "Unrecognized argument: %s\n", argv[i]);
932 return -ENOEXEC;
933 }
934 }
935
936 start += opt_cnt;
937 argc -= opt_cnt;
938
939 if (argc < 2) {
940 shell_fprintf(sh, SHELL_WARNING,
941 "Not enough parameters.\n");
942
943 if (is_udp) {
944 if (IS_ENABLED(CONFIG_NET_UDP)) {
945 shell_help(sh);
946 return -ENOEXEC;
947 }
948 } else {
949 if (IS_ENABLED(CONFIG_NET_TCP)) {
950 shell_help(sh);
951 return -ENOEXEC;
952 }
953 }
954
955 return -ENOEXEC;
956 }
957
958 if (argc > 2) {
959 port_str = argv[start + 2];
960 shell_fprintf(sh, SHELL_NORMAL,
961 "Remote port is %s\n", port_str);
962 } else {
963 port_str = DEF_PORT_STR;
964 }
965
966 if (IS_ENABLED(CONFIG_NET_IPV6) && !IS_ENABLED(CONFIG_NET_IPV4)) {
967 ret = parse_ipv6_addr(sh, argv[start + 1], port_str, &ipv6);
968 if (ret == -EDESTADDRREQ) {
969 shell_fprintf(sh, SHELL_WARNING,
970 "Invalid IPv6 address %s\n", argv[start + 1]);
971 }
972 if (ret < 0) {
973 shell_fprintf(sh, SHELL_WARNING,
974 "Please specify the IP address of the "
975 "remote server.\n");
976 return -ENOEXEC;
977 }
978
979 shell_fprintf(sh, SHELL_NORMAL, "Connecting to %s\n",
980 net_sprint_ipv6_addr(&ipv6.sin6_addr));
981
982 memcpy(¶m.peer_addr, &ipv6, sizeof(ipv6));
983 }
984
985 if (IS_ENABLED(CONFIG_NET_IPV4) && !IS_ENABLED(CONFIG_NET_IPV6)) {
986 ret = parse_ipv4_addr(sh, argv[start + 1], port_str, &ipv4);
987 if (ret == -EDESTADDRREQ) {
988 shell_fprintf(sh, SHELL_WARNING,
989 "Invalid IPv4 address %s\n", argv[start + 1]);
990 }
991 if (ret < 0) {
992 shell_fprintf(sh, SHELL_WARNING,
993 "Please specify the IP address of the "
994 "remote server.\n");
995 return -ENOEXEC;
996 }
997
998 shell_fprintf(sh, SHELL_NORMAL, "Connecting to %s\n",
999 net_sprint_ipv4_addr(&ipv4.sin_addr));
1000
1001 memcpy(¶m.peer_addr, &ipv4, sizeof(ipv4));
1002 }
1003
1004 if (IS_ENABLED(CONFIG_NET_IPV6) && IS_ENABLED(CONFIG_NET_IPV4)) {
1005 ret = parse_ipv6_addr(sh, argv[start + 1], port_str, &ipv6);
1006 if (ret < 0) {
1007 ret = parse_ipv4_addr(sh, argv[start + 1], port_str, &ipv4);
1008 if (ret == -EDESTADDRREQ) {
1009 shell_fprintf(sh, SHELL_WARNING,
1010 "Invalid IP address %s\n", argv[start + 1]);
1011 }
1012 if (ret < 0) {
1013 shell_fprintf(sh, SHELL_WARNING,
1014 "Please specify the IP address "
1015 "of the remote server.\n");
1016 return -ENOEXEC;
1017 }
1018
1019 shell_fprintf(sh, SHELL_NORMAL,
1020 "Connecting to %s\n",
1021 net_sprint_ipv4_addr(&ipv4.sin_addr));
1022
1023 memcpy(¶m.peer_addr, &ipv4, sizeof(ipv4));
1024 } else {
1025 shell_fprintf(sh, SHELL_NORMAL,
1026 "Connecting to %s\n",
1027 net_sprint_ipv6_addr(&ipv6.sin6_addr));
1028
1029 memcpy(¶m.peer_addr, &ipv6, sizeof(ipv6));
1030 }
1031 }
1032
1033 if (argc > 3) {
1034 param.duration_ms = MSEC_PER_SEC * strtoul(argv[start + 3],
1035 NULL, 10);
1036 } else {
1037 param.duration_ms = MSEC_PER_SEC * DEF_DURATION_SECONDS;
1038 }
1039
1040 if (argc > 4) {
1041 param.packet_size = parse_number(argv[start + 4], K, K_UNIT);
1042 } else {
1043 param.packet_size = DEF_PACKET_SIZE;
1044 }
1045
1046 if (argc > 5) {
1047 param.rate_kbps =
1048 (parse_number(argv[start + 5], K, K_UNIT) + 999) / 1000;
1049 if (!is_udp) {
1050 shell_fprintf(sh, SHELL_WARNING,
1051 "TCP upload will ignore <baud rate> argument\n");
1052 }
1053 } else {
1054 param.rate_kbps = DEF_RATE_KBPS;
1055 }
1056
1057 return execute_upload(sh, ¶m, is_udp, async);
1058 }
1059
cmd_tcp_upload(const struct shell * sh,size_t argc,char * argv[])1060 static int cmd_tcp_upload(const struct shell *sh, size_t argc, char *argv[])
1061 {
1062 return shell_cmd_upload(sh, argc, argv, IPPROTO_TCP);
1063 }
1064
cmd_udp_upload(const struct shell * sh,size_t argc,char * argv[])1065 static int cmd_udp_upload(const struct shell *sh, size_t argc, char *argv[])
1066 {
1067 return shell_cmd_upload(sh, argc, argv, IPPROTO_UDP);
1068 }
1069
shell_cmd_upload2(const struct shell * sh,size_t argc,char * argv[],enum net_ip_protocol proto)1070 static int shell_cmd_upload2(const struct shell *sh, size_t argc,
1071 char *argv[], enum net_ip_protocol proto)
1072 {
1073 struct zperf_upload_params param = { 0 };
1074 sa_family_t family;
1075 uint8_t is_udp;
1076 bool async = false;
1077 int start = 0;
1078 size_t opt_cnt = 0;
1079
1080 is_udp = proto == IPPROTO_UDP;
1081
1082 /* Parse options */
1083 for (size_t i = 1; i < argc; ++i) {
1084 if (*argv[i] != '-') {
1085 break;
1086 }
1087
1088 switch (argv[i][1]) {
1089 case 'S': {
1090 int tos = parse_arg(&i, argc, argv);
1091
1092 if (tos < 0 || tos > UINT8_MAX) {
1093 shell_fprintf(sh, SHELL_WARNING,
1094 "Parse error: %s\n", argv[i]);
1095 return -ENOEXEC;
1096 }
1097
1098 param.options.tos = tos;
1099 opt_cnt += 2;
1100 break;
1101 }
1102
1103 case 'a':
1104 async = true;
1105 opt_cnt += 1;
1106 break;
1107
1108 case 'n':
1109 if (is_udp) {
1110 shell_fprintf(sh, SHELL_WARNING,
1111 "UDP does not support -n option\n");
1112 return -ENOEXEC;
1113 }
1114 param.options.tcp_nodelay = 1;
1115 opt_cnt += 1;
1116 break;
1117
1118 #ifdef CONFIG_NET_CONTEXT_PRIORITY
1119 case 'p':
1120 param.options.priority = parse_arg(&i, argc, argv);
1121 if (param.options.priority == -1 ||
1122 param.options.priority > UINT8_MAX) {
1123 shell_fprintf(sh, SHELL_WARNING,
1124 "Parse error: %s\n", argv[i]);
1125 return -ENOEXEC;
1126 }
1127 opt_cnt += 2;
1128 break;
1129 #endif /* CONFIG_NET_CONTEXT_PRIORITY */
1130
1131 case 'I':
1132 i++;
1133 if (i >= argc) {
1134 shell_fprintf(sh, SHELL_WARNING,
1135 "-I <interface name>\n");
1136 return -ENOEXEC;
1137 }
1138 (void)memset(param.if_name, 0x0, IFNAMSIZ);
1139 strncpy(param.if_name, argv[i], IFNAMSIZ - 1);
1140
1141 opt_cnt += 2;
1142 break;
1143
1144 case 'i':
1145 int seconds = parse_arg(&i, argc, argv);
1146
1147 if (is_udp) {
1148 shell_fprintf(sh, SHELL_WARNING,
1149 "UDP does not support -i option\n");
1150 return -ENOEXEC;
1151 }
1152 if (seconds < 0 || seconds > UINT16_MAX) {
1153 shell_fprintf(sh, SHELL_WARNING,
1154 "Parse error: %s\n", argv[i]);
1155 return -ENOEXEC;
1156 }
1157
1158 param.options.report_interval_ms = seconds * MSEC_PER_SEC;
1159 opt_cnt += 2;
1160 break;
1161
1162 default:
1163 shell_fprintf(sh, SHELL_WARNING,
1164 "Unrecognized argument: %s\n", argv[i]);
1165 return -ENOEXEC;
1166 }
1167 }
1168
1169 start += opt_cnt;
1170 argc -= opt_cnt;
1171
1172 if (argc < 2) {
1173 shell_fprintf(sh, SHELL_WARNING,
1174 "Not enough parameters.\n");
1175
1176 if (is_udp) {
1177 if (IS_ENABLED(CONFIG_NET_UDP)) {
1178 shell_help(sh);
1179 return -ENOEXEC;
1180 }
1181 } else {
1182 if (IS_ENABLED(CONFIG_NET_TCP)) {
1183 shell_help(sh);
1184 return -ENOEXEC;
1185 }
1186 }
1187
1188 return -ENOEXEC;
1189 }
1190
1191 family = !strcmp(argv[start + 1], "v4") ? AF_INET : AF_INET6;
1192
1193 if (family == AF_INET6) {
1194 if (net_ipv6_is_addr_unspecified(&in6_addr_dst.sin6_addr)) {
1195 shell_fprintf(sh, SHELL_WARNING,
1196 "Invalid destination IPv6 address.\n");
1197 return -ENOEXEC;
1198 }
1199
1200 shell_fprintf(sh, SHELL_NORMAL,
1201 "Connecting to %s\n",
1202 net_sprint_ipv6_addr(&in6_addr_dst.sin6_addr));
1203
1204 memcpy(¶m.peer_addr, &in6_addr_dst, sizeof(in6_addr_dst));
1205 } else {
1206 if (net_ipv4_is_addr_unspecified(&in4_addr_dst.sin_addr)) {
1207 shell_fprintf(sh, SHELL_WARNING,
1208 "Invalid destination IPv4 address.\n");
1209 return -ENOEXEC;
1210 }
1211
1212 shell_fprintf(sh, SHELL_NORMAL,
1213 "Connecting to %s\n",
1214 net_sprint_ipv4_addr(&in4_addr_dst.sin_addr));
1215
1216 memcpy(¶m.peer_addr, &in4_addr_dst, sizeof(in4_addr_dst));
1217 }
1218
1219 if (argc > 2) {
1220 param.duration_ms = MSEC_PER_SEC * strtoul(argv[start + 2],
1221 NULL, 10);
1222 } else {
1223 param.duration_ms = MSEC_PER_SEC * DEF_DURATION_SECONDS;
1224 }
1225
1226 if (argc > 3) {
1227 param.packet_size = parse_number(argv[start + 3], K, K_UNIT);
1228 } else {
1229 param.packet_size = DEF_PACKET_SIZE;
1230 }
1231
1232 if (argc > 4) {
1233 param.rate_kbps =
1234 (parse_number(argv[start + 4], K, K_UNIT) + 999) / 1000;
1235 if (!is_udp) {
1236 shell_fprintf(sh, SHELL_WARNING,
1237 "TCP upload will ignore <baud rate> argument\n");
1238 }
1239 } else {
1240 param.rate_kbps = DEF_RATE_KBPS;
1241 }
1242
1243 return execute_upload(sh, ¶m, is_udp, async);
1244 }
1245
cmd_tcp_upload2(const struct shell * sh,size_t argc,char * argv[])1246 static int cmd_tcp_upload2(const struct shell *sh, size_t argc,
1247 char *argv[])
1248 {
1249 return shell_cmd_upload2(sh, argc, argv, IPPROTO_TCP);
1250 }
1251
cmd_udp_upload2(const struct shell * sh,size_t argc,char * argv[])1252 static int cmd_udp_upload2(const struct shell *sh, size_t argc,
1253 char *argv[])
1254 {
1255 return shell_cmd_upload2(sh, argc, argv, IPPROTO_UDP);
1256 }
1257
cmd_tcp(const struct shell * sh,size_t argc,char * argv[])1258 static int cmd_tcp(const struct shell *sh, size_t argc, char *argv[])
1259 {
1260 if (IS_ENABLED(CONFIG_NET_TCP)) {
1261 shell_help(sh);
1262 return -ENOEXEC;
1263 }
1264
1265 shell_fprintf(sh, SHELL_INFO, "TCP support is not enabled. "
1266 "Set CONFIG_NET_TCP=y in your config file.\n");
1267
1268 return -ENOTSUP;
1269 }
1270
cmd_udp(const struct shell * sh,size_t argc,char * argv[])1271 static int cmd_udp(const struct shell *sh, size_t argc, char *argv[])
1272 {
1273 if (IS_ENABLED(CONFIG_NET_UDP)) {
1274 shell_help(sh);
1275 return -ENOEXEC;
1276 }
1277
1278 shell_fprintf(sh, SHELL_INFO, "UDP support is not enabled. "
1279 "Set CONFIG_NET_UDP=y in your config file.\n");
1280
1281 return -ENOTSUP;
1282 }
1283
cmd_connectap(const struct shell * sh,size_t argc,char * argv[])1284 static int cmd_connectap(const struct shell *sh, size_t argc, char *argv[])
1285 {
1286 shell_fprintf(sh, SHELL_INFO,
1287 "Zephyr has not been built with Wi-Fi support.\n");
1288
1289 return 0;
1290 }
1291
tcp_session_cb(enum zperf_status status,struct zperf_results * result,void * user_data)1292 static void tcp_session_cb(enum zperf_status status,
1293 struct zperf_results *result,
1294 void *user_data)
1295 {
1296 const struct shell *sh = user_data;
1297
1298 switch (status) {
1299 case ZPERF_SESSION_STARTED:
1300 shell_fprintf(sh, SHELL_NORMAL, "New TCP session started.\n");
1301 break;
1302
1303 case ZPERF_SESSION_FINISHED: {
1304 uint32_t rate_in_kbps;
1305
1306 /* Compute baud rate */
1307 if (result->time_in_us != 0U) {
1308 rate_in_kbps = (uint32_t)
1309 ((result->total_len * 8ULL * USEC_PER_SEC) /
1310 (result->time_in_us * 1000ULL));
1311 } else {
1312 rate_in_kbps = 0U;
1313 }
1314
1315 shell_fprintf(sh, SHELL_NORMAL, "TCP session ended\n");
1316
1317 shell_fprintf(sh, SHELL_NORMAL, " Duration:\t\t");
1318 print_number_64(sh, result->time_in_us, TIME_US, TIME_US_UNIT);
1319 shell_fprintf(sh, SHELL_NORMAL, "\n");
1320
1321 shell_fprintf(sh, SHELL_NORMAL, " rate:\t\t\t");
1322 print_number(sh, rate_in_kbps, KBPS, KBPS_UNIT);
1323 shell_fprintf(sh, SHELL_NORMAL, "\n");
1324
1325 break;
1326 }
1327
1328 case ZPERF_SESSION_ERROR:
1329 shell_fprintf(sh, SHELL_ERROR, "TCP session error.\n");
1330 break;
1331
1332 default:
1333 break;
1334 }
1335 }
1336
cmd_tcp_download_stop(const struct shell * sh,size_t argc,char * argv[])1337 static int cmd_tcp_download_stop(const struct shell *sh, size_t argc,
1338 char *argv[])
1339 {
1340 int ret;
1341
1342 ret = zperf_tcp_download_stop();
1343 if (ret < 0) {
1344 shell_fprintf(sh, SHELL_WARNING, "TCP server not running!\n");
1345 return -ENOEXEC;
1346 }
1347
1348 shell_fprintf(sh, SHELL_NORMAL, "TCP server stopped\n");
1349
1350 return 0;
1351 }
1352
cmd_tcp_download(const struct shell * sh,size_t argc,char * argv[])1353 static int cmd_tcp_download(const struct shell *sh, size_t argc,
1354 char *argv[])
1355 {
1356 if (IS_ENABLED(CONFIG_NET_TCP)) {
1357 struct zperf_download_params param = { 0 };
1358 int ret;
1359 int start;
1360
1361 start = shell_cmd_download(sh, argc, argv, ¶m);
1362 if (start < 0) {
1363 shell_fprintf(sh, SHELL_WARNING,
1364 "Unable to parse option.\n");
1365 return -ENOEXEC;
1366 }
1367
1368 ret = zperf_bind_host(sh, argc - start, &argv[start], ¶m);
1369 if (ret < 0) {
1370 shell_fprintf(sh, SHELL_WARNING,
1371 "Unable to bind host.\n");
1372 shell_help(sh);
1373 return -ENOEXEC;
1374 }
1375
1376 ret = zperf_tcp_download(¶m, tcp_session_cb, (void *)sh);
1377 if (ret == -EALREADY) {
1378 shell_fprintf(sh, SHELL_WARNING,
1379 "TCP server already started!\n");
1380 return -ENOEXEC;
1381 } else if (ret < 0) {
1382 shell_fprintf(sh, SHELL_ERROR,
1383 "Failed to start TCP server!\n");
1384 return -ENOEXEC;
1385 }
1386
1387 shell_fprintf(sh, SHELL_NORMAL,
1388 "TCP server started on port %u\n", param.port);
1389
1390 return 0;
1391 } else {
1392 return -ENOTSUP;
1393 }
1394 }
1395
cmd_version(const struct shell * sh,size_t argc,char * argv[])1396 static int cmd_version(const struct shell *sh, size_t argc, char *argv[])
1397 {
1398 shell_fprintf(sh, SHELL_NORMAL, "Version: %s\nConfig: %s\n",
1399 ZPERF_VERSION, CONFIG);
1400
1401 return 0;
1402 }
1403
zperf_shell_init(void)1404 void zperf_shell_init(void)
1405 {
1406 int ret;
1407
1408 if (IS_ENABLED(MY_IP6ADDR_SET) && MY_IP6ADDR) {
1409 ret = net_addr_pton(AF_INET6, MY_IP6ADDR,
1410 &in6_addr_my.sin6_addr);
1411 if (ret < 0) {
1412 NET_WARN("Unable to set %s address\n", "IPv6");
1413 } else {
1414 NET_INFO("Setting IP address %s",
1415 net_sprint_ipv6_addr(&in6_addr_my.sin6_addr));
1416 }
1417
1418 ret = net_addr_pton(AF_INET6, DST_IP6ADDR,
1419 &in6_addr_dst.sin6_addr);
1420 if (ret < 0) {
1421 NET_WARN("Unable to set destination %s address %s",
1422 "IPv6",
1423 DST_IP6ADDR ? DST_IP6ADDR
1424 : "(not set)");
1425 } else {
1426 NET_INFO("Setting destination IP address %s",
1427 net_sprint_ipv6_addr(&in6_addr_dst.sin6_addr));
1428 }
1429 }
1430
1431 if (IS_ENABLED(MY_IP4ADDR_SET) && MY_IP4ADDR) {
1432 ret = net_addr_pton(AF_INET, MY_IP4ADDR,
1433 &in4_addr_my.sin_addr);
1434 if (ret < 0) {
1435 NET_WARN("Unable to set %s address\n", "IPv4");
1436 } else {
1437 NET_INFO("Setting IP address %s",
1438 net_sprint_ipv4_addr(&in4_addr_my.sin_addr));
1439 }
1440
1441 ret = net_addr_pton(AF_INET, DST_IP4ADDR,
1442 &in4_addr_dst.sin_addr);
1443 if (ret < 0) {
1444 NET_WARN("Unable to set destination %s address %s",
1445 "IPv4",
1446 DST_IP4ADDR ? DST_IP4ADDR
1447 : "(not set)");
1448 } else {
1449 NET_INFO("Setting destination IP address %s",
1450 net_sprint_ipv4_addr(&in4_addr_dst.sin_addr));
1451 }
1452 }
1453 }
1454
1455 SHELL_STATIC_SUBCMD_SET_CREATE(zperf_cmd_tcp_download,
1456 SHELL_CMD(stop, NULL, "Stop TCP server\n", cmd_tcp_download_stop),
1457 SHELL_SUBCMD_SET_END
1458 );
1459
1460 SHELL_STATIC_SUBCMD_SET_CREATE(zperf_cmd_tcp,
1461 SHELL_CMD(upload, NULL,
1462 "[<options>] <dest ip> <dest port> <duration> <packet size>[K]\n"
1463 "<options> command options (optional): [-S tos -a]\n"
1464 "<dest ip> IP destination\n"
1465 "<dest port> port destination\n"
1466 "<duration> of the test in seconds "
1467 "(default " DEF_DURATION_SECONDS_STR ")\n"
1468 "<packet size> in byte or kilobyte "
1469 "(with suffix K) "
1470 "(default " DEF_PACKET_SIZE_STR ")\n"
1471 "Available options:\n"
1472 "-S tos: Specify IPv4/6 type of service\n"
1473 "-a: Asynchronous call (shell will not block for the upload)\n"
1474 "-i sec: Periodic reporting interval in seconds (async only)\n"
1475 "-n: Disable Nagle's algorithm\n"
1476 #ifdef CONFIG_NET_CONTEXT_PRIORITY
1477 "-p: Specify custom packet priority\n"
1478 #endif /* CONFIG_NET_CONTEXT_PRIORITY */
1479 "Example: tcp upload 192.0.2.2 1111 1 1K\n"
1480 "Example: tcp upload 2001:db8::2\n",
1481 cmd_tcp_upload),
1482 SHELL_CMD(upload2, NULL,
1483 "[<options>] v6|v4 <duration> <packet size>[K]\n"
1484 "<options> command options (optional): [-S tos -a]\n"
1485 "<v6|v4>: Use either IPv6 or IPv4\n"
1486 "<duration> of the test in seconds "
1487 "(default " DEF_DURATION_SECONDS_STR ")\n"
1488 "<packet size> in byte or kilobyte "
1489 "(with suffix K) "
1490 "(default " DEF_PACKET_SIZE_STR ")\n"
1491 "Available options:\n"
1492 "-S tos: Specify IPv4/6 type of service\n"
1493 "-a: Asynchronous call (shell will not block for the upload)\n"
1494 "-i sec: Periodic reporting interval in seconds (async only)\n"
1495 "-n: Disable Nagle's algorithm\n"
1496 #ifdef CONFIG_NET_CONTEXT_PRIORITY
1497 "-p: Specify custom packet priority\n"
1498 #endif /* CONFIG_NET_CONTEXT_PRIORITY */
1499 "Example: tcp upload2 v6 1 1K\n"
1500 "Example: tcp upload2 v4\n"
1501 #if defined(CONFIG_NET_IPV6) && defined(MY_IP6ADDR_SET)
1502 "Default IPv6 address is " MY_IP6ADDR
1503 ", destination [" DST_IP6ADDR "]:" DEF_PORT_STR "\n"
1504 #endif
1505 #if defined(CONFIG_NET_IPV4) && defined(MY_IP4ADDR_SET)
1506 "Default IPv4 address is " MY_IP4ADDR
1507 ", destination " DST_IP4ADDR ":" DEF_PORT_STR "\n"
1508 #endif
1509 ,
1510 cmd_tcp_upload2),
1511 SHELL_CMD(download, &zperf_cmd_tcp_download,
1512 "[<port>]: Server port to listen on/connect to\n"
1513 "[<host>]: Bind to <host>, an interface address\n"
1514 "Example: tcp download 5001 192.168.0.1\n",
1515 cmd_tcp_download),
1516 SHELL_SUBCMD_SET_END
1517 );
1518
1519 SHELL_STATIC_SUBCMD_SET_CREATE(zperf_cmd_udp_download,
1520 SHELL_CMD(stop, NULL, "Stop UDP server\n", cmd_udp_download_stop),
1521 SHELL_SUBCMD_SET_END
1522 );
1523
1524 SHELL_STATIC_SUBCMD_SET_CREATE(zperf_cmd_udp,
1525 SHELL_CMD(upload, NULL,
1526 "[<options>] <dest ip> [<dest port> <duration> <packet size>[K] "
1527 "<baud rate>[K|M]]\n"
1528 "<options> command options (optional): [-S tos -a]\n"
1529 "<dest ip> IP destination\n"
1530 "<dest port> port destination\n"
1531 "<duration> of the test in seconds "
1532 "(default " DEF_DURATION_SECONDS_STR ")\n"
1533 "<packet size> in byte or kilobyte "
1534 "(with suffix K) "
1535 "(default " DEF_PACKET_SIZE_STR ")\n"
1536 "<baud rate> in kilobyte or megabyte "
1537 "(default " DEF_RATE_KBPS_STR "K)\n"
1538 "Available options:\n"
1539 "-S tos: Specify IPv4/6 type of service\n"
1540 "-a: Asynchronous call (shell will not block for the upload)\n"
1541 #ifdef CONFIG_NET_CONTEXT_PRIORITY
1542 "-p: Specify custom packet priority\n"
1543 #endif /* CONFIG_NET_CONTEXT_PRIORITY */
1544 "-I: Specify host interface name\n"
1545 "Example: udp upload 192.0.2.2 1111 1 1K 1M\n"
1546 "Example: udp upload 2001:db8::2\n",
1547 cmd_udp_upload),
1548 SHELL_CMD(upload2, NULL,
1549 "[<options>] v6|v4 [<duration> <packet size>[K] <baud rate>[K|M]]\n"
1550 "<options> command options (optional): [-S tos -a]\n"
1551 "<v6|v4>: Use either IPv6 or IPv4\n"
1552 "<duration> of the test in seconds "
1553 "(default " DEF_DURATION_SECONDS_STR ")\n"
1554 "<packet size> in byte or kilobyte "
1555 "(with suffix K) "
1556 "(default " DEF_PACKET_SIZE_STR ")\n"
1557 "<baud rate> in kilobyte or megabyte "
1558 "(default " DEF_RATE_KBPS_STR "K)\n"
1559 "Available options:\n"
1560 "-S tos: Specify IPv4/6 type of service\n"
1561 "-a: Asynchronous call (shell will not block for the upload)\n"
1562 #ifdef CONFIG_NET_CONTEXT_PRIORITY
1563 "-p: Specify custom packet priority\n"
1564 #endif /* CONFIG_NET_CONTEXT_PRIORITY */
1565 "-I: Specify host interface name\n"
1566 "Example: udp upload2 v4 1 1K 1M\n"
1567 "Example: udp upload2 v6\n"
1568 #if defined(CONFIG_NET_IPV6) && defined(MY_IP6ADDR_SET)
1569 "Default IPv6 address is " MY_IP6ADDR
1570 ", destination [" DST_IP6ADDR "]:" DEF_PORT_STR "\n"
1571 #endif
1572 #if defined(CONFIG_NET_IPV4) && defined(MY_IP4ADDR_SET)
1573 "Default IPv4 address is " MY_IP4ADDR
1574 ", destination " DST_IP4ADDR ":" DEF_PORT_STR "\n"
1575 #endif
1576 ,
1577 cmd_udp_upload2),
1578 SHELL_CMD(download, &zperf_cmd_udp_download,
1579 "[<options>] command options (optional): [-I eth0]\n"
1580 "[<port>]: Server port to listen on/connect to\n"
1581 "[<host>]: Bind to <host>, an interface address\n"
1582 "Available options:\n"
1583 "-I <interface name>: Specify host interface name\n"
1584 "Example: udp download 5001 192.168.0.1\n",
1585 cmd_udp_download),
1586 SHELL_SUBCMD_SET_END
1587 );
1588
1589 SHELL_STATIC_SUBCMD_SET_CREATE(zperf_commands,
1590 SHELL_CMD(connectap, NULL,
1591 "Connect to AP",
1592 cmd_connectap),
1593 SHELL_CMD(setip, NULL,
1594 "Set IP address\n"
1595 "<my ip> <prefix len>\n"
1596 "Example setip 2001:db8::2 64\n"
1597 "Example setip 192.0.2.2\n",
1598 cmd_setip),
1599 SHELL_CMD(tcp, &zperf_cmd_tcp,
1600 "Upload/Download TCP data",
1601 cmd_tcp),
1602 SHELL_CMD(udp, &zperf_cmd_udp,
1603 "Upload/Download UDP data",
1604 cmd_udp),
1605 SHELL_CMD(version, NULL,
1606 "Zperf version",
1607 cmd_version),
1608 SHELL_SUBCMD_SET_END
1609 );
1610
1611 SHELL_CMD_REGISTER(zperf, &zperf_commands, "Zperf commands", NULL);
1612