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 #include <zephyr/sys/util_macro.h>
23
24 #include "zperf_internal.h"
25 #include "zperf_session.h"
26
27 /* Get some useful debug routings from net_private.h, requires
28 * that NET_LOG_ENABLED is set.
29 */
30 #define NET_LOG_ENABLED 1
31 #include "net_private.h"
32
33 #include "ipv6.h" /* to get infinite lifetime */
34
35
36 static const char *CONFIG =
37 "unified"
38 #if defined(CONFIG_WIFI)
39 " wifi"
40 #endif
41 #if defined(CONFIG_NET_L2_ETHERNET)
42 " ethernet"
43 #endif
44 #if defined(CONFIG_NET_IPV4)
45 " ipv4"
46 #endif
47 #if defined(CONFIG_NET_IPV6)
48 " ipv6"
49 #endif
50 "";
51
52 static struct sockaddr_in6 in6_addr_my = {
53 .sin6_family = AF_INET6,
54 .sin6_port = htons(MY_SRC_PORT),
55 };
56
57 static struct sockaddr_in6 in6_addr_dst = {
58 .sin6_family = AF_INET6,
59 .sin6_port = htons(DEF_PORT),
60 };
61
62 static struct sockaddr_in in4_addr_dst = {
63 .sin_family = AF_INET,
64 .sin_port = htons(DEF_PORT),
65 };
66
67 static struct sockaddr_in in4_addr_my = {
68 .sin_family = AF_INET,
69 .sin_port = htons(MY_SRC_PORT),
70 };
71
72 static struct in6_addr shell_ipv6;
73
74 static struct in_addr shell_ipv4;
75
76 #define DEVICE_NAME "zperf shell"
77
78 const uint32_t TIME_US[] = { 60 * 1000 * 1000, 1000 * 1000, 1000, 0 };
79 const char *TIME_US_UNIT[] = { "m", "s", "ms", "us" };
80 const uint32_t KBPS[] = { 1000, 0 };
81 const char *KBPS_UNIT[] = { "Mbps", "Kbps" };
82 const uint32_t K[] = { 1000 * 1000, 1000, 0 };
83 const char *K_UNIT[] = { "M", "K", "" };
84
print_number(const struct shell * sh,uint32_t value,const uint32_t * divisor_arr,const char ** units)85 static void print_number(const struct shell *sh, uint32_t value,
86 const uint32_t *divisor_arr, const char **units)
87 {
88 const char **unit;
89 const uint32_t *divisor;
90 uint32_t dec, radix;
91
92 unit = units;
93 divisor = divisor_arr;
94
95 while (value < *divisor) {
96 divisor++;
97 unit++;
98 }
99
100 if (*divisor != 0U) {
101 radix = value / *divisor;
102 dec = (value % *divisor) * 100U / *divisor;
103 shell_fprintf(sh, SHELL_NORMAL, "%u.%s%u %s", radix,
104 (dec < 10) ? "0" : "", dec, *unit);
105 } else {
106 shell_fprintf(sh, SHELL_NORMAL, "%u %s", value, *unit);
107 }
108 }
109
print_number_64(const struct shell * sh,uint64_t value,const uint32_t * divisor_arr,const char ** units)110 static void print_number_64(const struct shell *sh, uint64_t value,
111 const uint32_t *divisor_arr, const char **units)
112 {
113 const char **unit;
114 const uint32_t *divisor;
115 uint32_t dec;
116 uint64_t radix;
117
118 unit = units;
119 divisor = divisor_arr;
120
121 while (value < *divisor) {
122 divisor++;
123 unit++;
124 }
125
126 if (*divisor != 0U) {
127 radix = value / *divisor;
128 dec = (value % *divisor) * 100U / *divisor;
129 shell_fprintf(sh, SHELL_NORMAL, "%llu.%s%u %s", radix,
130 (dec < 10) ? "0" : "", dec, *unit);
131 } else {
132 shell_fprintf(sh, SHELL_NORMAL, "%llu %s", value, *unit);
133 }
134 }
135
parse_number(const char * string,const uint32_t * divisor_arr,const char ** units)136 static long parse_number(const char *string, const uint32_t *divisor_arr,
137 const char **units)
138 {
139 const char **unit;
140 const uint32_t *divisor;
141 char *suffix;
142 long dec;
143 int cmp;
144
145 dec = strtoul(string, &suffix, 10);
146 unit = units;
147 divisor = divisor_arr;
148
149 do {
150 cmp = strncasecmp(suffix, *unit++, 1);
151 } while (cmp != 0 && *++divisor != 0U);
152
153 return (*divisor == 0U) ? dec : dec * *divisor;
154 }
155
parse_ipv6_addr(const struct shell * sh,char * host,char * port,struct sockaddr_in6 * addr)156 static int parse_ipv6_addr(const struct shell *sh, char *host, char *port,
157 struct sockaddr_in6 *addr)
158 {
159 int ret;
160
161 if (!host) {
162 return -EINVAL;
163 }
164
165 ret = net_addr_pton(AF_INET6, host, &addr->sin6_addr);
166 if (ret < 0) {
167 return -EDESTADDRREQ;
168 }
169
170 addr->sin6_port = htons(strtoul(port, NULL, 10));
171 if (!addr->sin6_port) {
172 shell_fprintf(sh, SHELL_WARNING,
173 "Invalid port %s\n", port);
174 return -EINVAL;
175 }
176
177 return 0;
178 }
179
parse_ipv4_addr(const struct shell * sh,char * host,char * port,struct sockaddr_in * addr)180 static int parse_ipv4_addr(const struct shell *sh, char *host, char *port,
181 struct sockaddr_in *addr)
182 {
183 int ret;
184
185 if (!host) {
186 return -EINVAL;
187 }
188
189 ret = net_addr_pton(AF_INET, host, &addr->sin_addr);
190 if (ret < 0) {
191 return -EDESTADDRREQ;
192 }
193
194 addr->sin_port = htons(strtoul(port, NULL, 10));
195 if (!addr->sin_port) {
196 shell_fprintf(sh, SHELL_WARNING,
197 "Invalid port %s\n", port);
198 return -EINVAL;
199 }
200
201 return 0;
202 }
203
204 #ifdef CONFIG_NET_ZPERF_SERVER
205
zperf_bind_host(const struct shell * sh,size_t argc,char * argv[],struct zperf_download_params * param)206 static int zperf_bind_host(const struct shell *sh,
207 size_t argc, char *argv[],
208 struct zperf_download_params *param)
209 {
210 int ret;
211
212 /* Parse options */
213 if (argc >= 2) {
214 param->port = strtoul(argv[1], NULL, 10);
215 } else {
216 param->port = DEF_PORT;
217 }
218
219 if (argc >= 3) {
220 char *addr_str = argv[2];
221 struct sockaddr addr;
222
223 memset(&addr, 0, sizeof(addr));
224
225 ret = net_ipaddr_parse(addr_str, strlen(addr_str), &addr);
226 if (ret < 0) {
227 shell_fprintf(sh, SHELL_WARNING,
228 "Cannot parse address \"%s\"\n",
229 addr_str);
230 return ret;
231 }
232
233 memcpy(¶m->addr, &addr, sizeof(struct sockaddr));
234 }
235
236 return 0;
237 }
238
239 #endif
240
cmd_setip(const struct shell * sh,size_t argc,char * argv[])241 static int cmd_setip(const struct shell *sh, size_t argc, char *argv[])
242 {
243 int start = 0;
244
245 if (IS_ENABLED(CONFIG_NET_IPV6) && !IS_ENABLED(CONFIG_NET_IPV4)) {
246 if (argc != 3) {
247 shell_help(sh);
248 return -ENOEXEC;
249 }
250
251 if (zperf_get_ipv6_addr(argv[start + 1], argv[start + 2],
252 &shell_ipv6) < 0) {
253 shell_fprintf(sh, SHELL_WARNING,
254 "Unable to set %s address (%s disabled)\n", "IPv6", "IPv4");
255 return 0;
256 }
257
258 shell_fprintf(sh, SHELL_NORMAL,
259 "Setting IP address %s\n",
260 net_sprint_ipv6_addr(&shell_ipv6));
261 }
262
263 if (IS_ENABLED(CONFIG_NET_IPV4) && !IS_ENABLED(CONFIG_NET_IPV6)) {
264 if (argc != 2) {
265 shell_help(sh);
266 return -ENOEXEC;
267 }
268
269 if (zperf_get_ipv4_addr(argv[start + 1], &shell_ipv4) < 0) {
270 shell_fprintf(sh, SHELL_WARNING,
271 "Unable to set %s address (%s disabled)\n", "IPv4", "IPv6");
272 return -ENOEXEC;
273 }
274
275 shell_fprintf(sh, SHELL_NORMAL,
276 "Setting IP address %s\n",
277 net_sprint_ipv4_addr(&shell_ipv4));
278 }
279
280 if (IS_ENABLED(CONFIG_NET_IPV6) && IS_ENABLED(CONFIG_NET_IPV4)) {
281 if (net_addr_pton(AF_INET6, argv[start + 1], &shell_ipv6) < 0) {
282 if (argc != 2) {
283 shell_help(sh);
284 return -ENOEXEC;
285 }
286
287 if (zperf_get_ipv4_addr(argv[start + 1],
288 &shell_ipv4) < 0) {
289 shell_fprintf(sh, SHELL_WARNING,
290 "Unable to set %s address\n", "IPv4");
291 return -ENOEXEC;
292 }
293
294 shell_fprintf(sh, SHELL_NORMAL,
295 "Setting IP address %s\n",
296 net_sprint_ipv4_addr(&shell_ipv4));
297 } else {
298 if (argc != 3) {
299 shell_help(sh);
300 return -ENOEXEC;
301 }
302
303 if (zperf_get_ipv6_addr(argv[start + 1],
304 argv[start + 2], &shell_ipv6) < 0) {
305 shell_fprintf(sh, SHELL_WARNING,
306 "Unable to set %s address\n", "IPv6");
307 return -ENOEXEC;
308 }
309
310 shell_fprintf(sh, SHELL_NORMAL,
311 "Setting IP address %s\n",
312 net_sprint_ipv6_addr(&shell_ipv6));
313 }
314 }
315
316 return 0;
317 }
318
319 #ifdef CONFIG_NET_ZPERF_SERVER
320
udp_session_cb(enum zperf_status status,struct zperf_results * result,void * user_data)321 static void udp_session_cb(enum zperf_status status,
322 struct zperf_results *result,
323 void *user_data)
324 {
325 const struct shell *sh = user_data;
326
327 switch (status) {
328 case ZPERF_SESSION_STARTED:
329 shell_fprintf(sh, SHELL_NORMAL, "New session started.\n");
330 break;
331
332 case ZPERF_SESSION_FINISHED: {
333 uint32_t rate_in_kbps;
334
335 /* Compute baud rate */
336 if (result->time_in_us != 0U) {
337 rate_in_kbps = (uint32_t)
338 ((result->total_len * 8ULL * USEC_PER_SEC) /
339 (result->time_in_us * 1000ULL));
340 } else {
341 rate_in_kbps = 0U;
342 }
343
344 shell_fprintf(sh, SHELL_NORMAL, "End of session!\n");
345
346 shell_fprintf(sh, SHELL_NORMAL, " duration:\t\t");
347 print_number_64(sh, result->time_in_us, TIME_US, TIME_US_UNIT);
348 shell_fprintf(sh, SHELL_NORMAL, "\n");
349
350 shell_fprintf(sh, SHELL_NORMAL, " received packets:\t%u\n",
351 result->nb_packets_rcvd);
352 shell_fprintf(sh, SHELL_NORMAL, " nb packets lost:\t%u\n",
353 result->nb_packets_lost);
354 shell_fprintf(sh, SHELL_NORMAL, " nb packets outorder:\t%u\n",
355 result->nb_packets_outorder);
356
357 shell_fprintf(sh, SHELL_NORMAL, " jitter:\t\t\t");
358 print_number(sh, result->jitter_in_us, TIME_US, TIME_US_UNIT);
359 shell_fprintf(sh, SHELL_NORMAL, "\n");
360
361 shell_fprintf(sh, SHELL_NORMAL, " rate:\t\t\t");
362 print_number(sh, rate_in_kbps, KBPS, KBPS_UNIT);
363 shell_fprintf(sh, SHELL_NORMAL, "\n");
364
365 break;
366 }
367
368 case ZPERF_SESSION_ERROR:
369 shell_fprintf(sh, SHELL_ERROR, "UDP session error.\n");
370 break;
371
372 default:
373 break;
374 }
375 }
376
377 /*
378 * parse download options with '-'
379 * return < 0 if parse error
380 * return 0 if no '-' options
381 * return > 0 num of argc we parsed
382 * and following parse starts from this num
383 */
shell_cmd_download(const struct shell * sh,size_t argc,char * argv[],struct zperf_download_params * param)384 static int shell_cmd_download(const struct shell *sh, size_t argc,
385 char *argv[],
386 struct zperf_download_params *param)
387 {
388 int opt_cnt = 0;
389 size_t i;
390
391 for (i = 1; i < argc; ++i) {
392 if (*argv[i] != '-') {
393 break;
394 }
395
396 switch (argv[i][1]) {
397 case 'I':
398 /*
399 * IFNAMSIZ by default CONFIG_NET_INTERFACE_NAME_LEN
400 * is at least 1 so no overflow risk here
401 */
402 i++;
403 if (i >= argc) {
404 shell_fprintf(sh, SHELL_WARNING,
405 "-I <interface name>\n");
406 return -ENOEXEC;
407 }
408 (void)memset(param->if_name, 0x0, IFNAMSIZ);
409 strncpy(param->if_name, argv[i], IFNAMSIZ - 1);
410
411 opt_cnt += 2;
412 break;
413
414 default:
415 shell_fprintf(sh, SHELL_WARNING,
416 "Unrecognized argument: %s\n", argv[i]);
417 return -ENOEXEC;
418 }
419 }
420
421 return opt_cnt;
422 }
423
cmd_udp_download_stop(const struct shell * sh,size_t argc,char * argv[])424 static int cmd_udp_download_stop(const struct shell *sh, size_t argc,
425 char *argv[])
426 {
427 int ret;
428
429 if (!IS_ENABLED(CONFIG_NET_UDP)) {
430 shell_warn(sh, "UDP not supported");
431 return -ENOEXEC;
432 }
433
434 ret = zperf_udp_download_stop();
435 if (ret < 0) {
436 shell_fprintf(sh, SHELL_WARNING, "UDP server not running!\n");
437 return -ENOEXEC;
438 }
439
440 shell_fprintf(sh, SHELL_NORMAL, "UDP server stopped\n");
441
442 return 0;
443 }
444
cmd_udp_download(const struct shell * sh,size_t argc,char * argv[])445 static int cmd_udp_download(const struct shell *sh, size_t argc,
446 char *argv[])
447 {
448 if (IS_ENABLED(CONFIG_NET_UDP)) {
449 struct zperf_download_params param = { 0 };
450 int ret;
451 int start;
452
453 start = shell_cmd_download(sh, argc, argv, ¶m);
454 if (start < 0) {
455 shell_fprintf(sh, SHELL_WARNING,
456 "Unable to parse option.\n");
457 return -ENOEXEC;
458 }
459
460 ret = zperf_bind_host(sh, argc - start, &argv[start], ¶m);
461 if (ret < 0) {
462 shell_fprintf(sh, SHELL_WARNING,
463 "Unable to bind host.\n");
464 shell_help(sh);
465 return -ENOEXEC;
466 }
467
468 ret = zperf_udp_download(¶m, udp_session_cb, (void *)sh);
469 if (ret == -EALREADY) {
470 shell_fprintf(sh, SHELL_WARNING,
471 "UDP server already started!\n");
472 return -ENOEXEC;
473 } else if (ret < 0) {
474 shell_fprintf(sh, SHELL_ERROR,
475 "Failed to start UDP server!\n");
476 return -ENOEXEC;
477 }
478
479 k_yield();
480
481 shell_fprintf(sh, SHELL_NORMAL,
482 "UDP server started on port %u\n", param.port);
483
484 return 0;
485 } else {
486 return -ENOTSUP;
487 }
488 }
489
490 #endif
491
shell_udp_upload_print_stats(const struct shell * sh,struct zperf_results * results,bool is_async)492 static void shell_udp_upload_print_stats(const struct shell *sh,
493 struct zperf_results *results,
494 bool is_async)
495 {
496 if (IS_ENABLED(CONFIG_NET_UDP)) {
497 uint64_t rate_in_kbps, client_rate_in_kbps;
498
499 shell_fprintf(sh, SHELL_NORMAL, "-\nUpload completed!\n");
500
501 if (results->time_in_us != 0U) {
502 rate_in_kbps = (uint32_t)
503 ((results->total_len * 8 * USEC_PER_SEC) /
504 (results->time_in_us * 1000U));
505 } else {
506 rate_in_kbps = 0U;
507 }
508
509 if (results->client_time_in_us != 0U) {
510 client_rate_in_kbps = (uint32_t)
511 (((uint64_t)results->nb_packets_sent *
512 (uint64_t)results->packet_size * (uint64_t)8 *
513 (uint64_t)USEC_PER_SEC) /
514 (results->client_time_in_us * 1000U));
515 } else {
516 client_rate_in_kbps = 0U;
517 }
518
519 /* Print warning if no server stats in unicast case; for multicast,
520 * server stats are not expected.
521 */
522 if (!rate_in_kbps && !results->is_multicast) {
523 shell_fprintf(sh, SHELL_ERROR,
524 "LAST PACKET NOT RECEIVED!!!\n");
525 }
526
527 if (results->is_multicast) {
528 shell_fprintf(sh, SHELL_NORMAL, "Statistics (client only)\n");
529 shell_fprintf(sh, SHELL_NORMAL, "Duration:\t\t");
530 print_number_64(sh, results->client_time_in_us, TIME_US, TIME_US_UNIT);
531 shell_fprintf(sh, SHELL_NORMAL, "\n");
532 shell_fprintf(sh, SHELL_NORMAL, "Num packets:\t\t%u\n",
533 results->nb_packets_sent);
534 shell_fprintf(sh, SHELL_NORMAL, "Rate:\t\t\t");
535 print_number(sh, client_rate_in_kbps, KBPS, KBPS_UNIT);
536 shell_fprintf(sh, SHELL_NORMAL, "\n");
537 } else {
538 shell_fprintf(sh, SHELL_NORMAL,
539 "Statistics:\t\tserver\t(client)\n");
540 shell_fprintf(sh, SHELL_NORMAL, "Duration:\t\t");
541 print_number_64(sh, results->time_in_us, TIME_US,
542 TIME_US_UNIT);
543 shell_fprintf(sh, SHELL_NORMAL, "\t(");
544 print_number_64(sh, results->client_time_in_us, TIME_US,
545 TIME_US_UNIT);
546 shell_fprintf(sh, SHELL_NORMAL, ")\n");
547
548 shell_fprintf(sh, SHELL_NORMAL, "Num packets:\t\t%u\t(%u)\n",
549 results->nb_packets_rcvd,
550 results->nb_packets_sent);
551
552 shell_fprintf(sh, SHELL_NORMAL,
553 "Num packets out order:\t%u\n",
554 results->nb_packets_outorder);
555 shell_fprintf(sh, SHELL_NORMAL, "Num packets lost:\t%u\n",
556 results->nb_packets_lost);
557
558 shell_fprintf(sh, SHELL_NORMAL, "Jitter:\t\t\t");
559 print_number(sh, results->jitter_in_us, TIME_US,
560 TIME_US_UNIT);
561 shell_fprintf(sh, SHELL_NORMAL, "\n");
562
563 shell_fprintf(sh, SHELL_NORMAL, "Rate:\t\t\t");
564 print_number(sh, rate_in_kbps, KBPS, KBPS_UNIT);
565 shell_fprintf(sh, SHELL_NORMAL, "\t(");
566 print_number(sh, client_rate_in_kbps, KBPS, KBPS_UNIT);
567 shell_fprintf(sh, SHELL_NORMAL, ")\n");
568 }
569
570 #ifdef CONFIG_ZPERF_SESSION_PER_THREAD
571 if (is_async) {
572 struct session *ses = CONTAINER_OF(results,
573 struct session,
574 result);
575
576 #ifdef CONFIG_NET_CONTEXT_PRIORITY
577 shell_fprintf(sh, SHELL_NORMAL,
578 "Packet priority:\t%d\n",
579 ses->async_upload_ctx.param.options.priority);
580 #endif /* CONFIG_NET_CONTEXT_PRIORITY */
581
582 shell_fprintf(sh, SHELL_NORMAL,
583 "Thread priority:\t%d\n",
584 ses->async_upload_ctx.param.options.thread_priority);
585 shell_fprintf(sh, SHELL_NORMAL,
586 "Protocol:\t\t%s\n",
587 ses->proto == SESSION_UDP ? "UDP" : "TCP");
588 shell_fprintf(sh, SHELL_NORMAL,
589 "Session id:\t\t%d\n", ses->id);
590 }
591 #endif /* CONFIG_ZPERF_SESSION_PER_THREAD */
592 }
593 }
594
shell_tcp_upload_print_stats(const struct shell * sh,struct zperf_results * results,bool is_async)595 static void shell_tcp_upload_print_stats(const struct shell *sh,
596 struct zperf_results *results,
597 bool is_async)
598 {
599 if (IS_ENABLED(CONFIG_NET_TCP)) {
600 uint64_t client_rate_in_kbps;
601
602 shell_fprintf(sh, SHELL_NORMAL, "-\nUpload completed!\n");
603
604 if (results->client_time_in_us != 0U) {
605 client_rate_in_kbps = (uint32_t)
606 (((uint64_t)results->nb_packets_sent *
607 (uint64_t)results->packet_size * (uint64_t)8 *
608 (uint64_t)USEC_PER_SEC) /
609 (results->client_time_in_us * 1000U));
610 } else {
611 client_rate_in_kbps = 0U;
612 }
613
614 shell_fprintf(sh, SHELL_NORMAL, "Duration:\t\t");
615 print_number_64(sh, results->client_time_in_us,
616 TIME_US, TIME_US_UNIT);
617 shell_fprintf(sh, SHELL_NORMAL, "\n");
618 shell_fprintf(sh, SHELL_NORMAL, "Num packets:\t\t%u\n",
619 results->nb_packets_sent);
620 shell_fprintf(sh, SHELL_NORMAL,
621 "Num errors:\t\t%u (retry or fail)\n",
622 results->nb_packets_errors);
623 shell_fprintf(sh, SHELL_NORMAL, "Rate:\t\t\t");
624 print_number(sh, client_rate_in_kbps, KBPS, KBPS_UNIT);
625 shell_fprintf(sh, SHELL_NORMAL, "\n");
626
627 #ifdef CONFIG_ZPERF_SESSION_PER_THREAD
628 if (is_async) {
629 struct session *ses = CONTAINER_OF(results,
630 struct session,
631 result);
632
633 #ifdef CONFIG_NET_CONTEXT_PRIORITY
634 shell_fprintf(sh, SHELL_NORMAL,
635 "Packet priority:\t%d\n",
636 ses->async_upload_ctx.param.options.priority);
637 #endif /* CONFIG_NET_CONTEXT_PRIORITY */
638
639 shell_fprintf(sh, SHELL_NORMAL,
640 "Thread priority:\t%d\n",
641 ses->async_upload_ctx.param.options.thread_priority);
642 shell_fprintf(sh, SHELL_NORMAL,
643 "Protocol:\t\t%s\n",
644 ses->proto == SESSION_UDP ? "UDP" : "TCP");
645 shell_fprintf(sh, SHELL_NORMAL,
646 "Session id:\t\t%d\n", ses->id);
647 }
648 #endif /* CONFIG_ZPERF_SESSION_PER_THREAD */
649 }
650 }
651
shell_tcp_upload_print_periodic(const struct shell * sh,struct zperf_results * results)652 static void shell_tcp_upload_print_periodic(const struct shell *sh,
653 struct zperf_results *results)
654 {
655 if (IS_ENABLED(CONFIG_NET_TCP)) {
656 uint64_t client_rate_in_kbps;
657
658 if (results->client_time_in_us != 0U) {
659 client_rate_in_kbps = (uint32_t)
660 (((uint64_t)results->nb_packets_sent *
661 (uint64_t)results->packet_size * (uint64_t)8 *
662 (uint64_t)USEC_PER_SEC) /
663 (results->client_time_in_us * 1000U));
664 } else {
665 client_rate_in_kbps = 0U;
666 }
667
668 shell_fprintf(sh, SHELL_NORMAL, "Duration: ");
669 print_number_64(sh, results->client_time_in_us,
670 TIME_US, TIME_US_UNIT);
671 shell_fprintf(sh, SHELL_NORMAL, " | ");
672 shell_fprintf(sh, SHELL_NORMAL, "Packets: %6u | ",
673 results->nb_packets_sent);
674 shell_fprintf(sh, SHELL_NORMAL,
675 "Errors: %6u | ",
676 results->nb_packets_errors);
677 shell_fprintf(sh, SHELL_NORMAL, "Rate: ");
678 print_number(sh, client_rate_in_kbps, KBPS, KBPS_UNIT);
679 shell_fprintf(sh, SHELL_NORMAL, "\n");
680 }
681 }
682
udp_upload_cb(enum zperf_status status,struct zperf_results * result,void * user_data)683 static void udp_upload_cb(enum zperf_status status,
684 struct zperf_results *result,
685 void *user_data)
686 {
687 const struct shell *sh = user_data;
688
689 #ifdef CONFIG_ZPERF_SESSION_PER_THREAD
690 struct session *ses = CONTAINER_OF(result, struct session, result);
691 #endif /* CONFIG_ZPERF_SESSION_PER_THREAD */
692
693 switch (status) {
694 case ZPERF_SESSION_STARTED:
695 break;
696
697 case ZPERF_SESSION_FINISHED: {
698 shell_udp_upload_print_stats(sh, result, true);
699
700 #ifdef CONFIG_ZPERF_SESSION_PER_THREAD
701 ses->in_progress = false;
702 ses->state = STATE_COMPLETED;
703 #endif /* CONFIG_ZPERF_SESSION_PER_THREAD */
704
705 break;
706 }
707
708 case ZPERF_SESSION_ERROR:
709 shell_fprintf(sh, SHELL_ERROR, "UDP upload failed\n");
710
711 #ifdef CONFIG_ZPERF_SESSION_PER_THREAD
712 ses->in_progress = false;
713 #endif /* CONFIG_ZPERF_SESSION_PER_THREAD */
714
715 break;
716
717 default:
718 break;
719 }
720 }
721
tcp_upload_cb(enum zperf_status status,struct zperf_results * result,void * user_data)722 static void tcp_upload_cb(enum zperf_status status,
723 struct zperf_results *result,
724 void *user_data)
725 {
726 const struct shell *sh = user_data;
727
728 #ifdef CONFIG_ZPERF_SESSION_PER_THREAD
729 struct session *ses = CONTAINER_OF(result, struct session, result);
730 #endif /* CONFIG_ZPERF_SESSION_PER_THREAD */
731
732 switch (status) {
733 case ZPERF_SESSION_STARTED:
734 break;
735
736 case ZPERF_SESSION_PERIODIC_RESULT:
737 shell_tcp_upload_print_periodic(sh, result);
738 break;
739
740 case ZPERF_SESSION_FINISHED: {
741 shell_tcp_upload_print_stats(sh, result, true);
742
743 #ifdef CONFIG_ZPERF_SESSION_PER_THREAD
744 ses->in_progress = false;
745 ses->state = STATE_COMPLETED;
746 #endif /* CONFIG_ZPERF_SESSION_PER_THREAD */
747
748 break;
749 }
750
751 case ZPERF_SESSION_ERROR:
752 shell_fprintf(sh, SHELL_ERROR, "TCP upload failed\n");
753
754 #ifdef CONFIG_ZPERF_SESSION_PER_THREAD
755 ses->in_progress = false;
756 #endif /* CONFIG_ZPERF_SESSION_PER_THREAD */
757
758 break;
759 }
760 }
761
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)762 static int ping_handler(struct net_icmp_ctx *ctx,
763 struct net_pkt *pkt,
764 struct net_icmp_ip_hdr *ip_hdr,
765 struct net_icmp_hdr *icmp_hdr,
766 void *user_data)
767 {
768 struct k_sem *sem_wait = user_data;
769
770 ARG_UNUSED(ctx);
771 ARG_UNUSED(pkt);
772 ARG_UNUSED(ip_hdr);
773 ARG_UNUSED(icmp_hdr);
774
775 k_sem_give(sem_wait);
776
777 return 0;
778 }
779
send_ping(const struct shell * sh,struct in6_addr * addr,int timeout_ms)780 static void send_ping(const struct shell *sh,
781 struct in6_addr *addr,
782 int timeout_ms)
783 {
784 static struct k_sem sem_wait;
785 struct sockaddr_in6 dest_addr = { 0 };
786 struct net_icmp_ctx ctx;
787 int ret;
788
789 ret = net_icmp_init_ctx(&ctx, NET_ICMPV6_ECHO_REPLY, 0, ping_handler);
790 if (ret < 0) {
791 shell_fprintf(sh, SHELL_WARNING, "Cannot send ping (%d)\n", ret);
792 return;
793 }
794
795 dest_addr.sin6_family = AF_INET6;
796 net_ipv6_addr_copy_raw((uint8_t *)&dest_addr.sin6_addr, (uint8_t *)addr);
797
798 k_sem_init(&sem_wait, 0, 1);
799
800 (void)net_icmp_send_echo_request(&ctx,
801 net_if_get_default(),
802 (struct sockaddr *)&dest_addr,
803 NULL, &sem_wait);
804
805 ret = k_sem_take(&sem_wait, K_MSEC(timeout_ms));
806 if (ret == -EAGAIN) {
807 shell_fprintf(sh, SHELL_WARNING, "ping %s timeout\n",
808 net_sprint_ipv6_addr(addr));
809 }
810
811 (void)net_icmp_cleanup_ctx(&ctx);
812 }
813
execute_upload(const struct shell * sh,const struct zperf_upload_params * param,bool is_udp,bool async)814 static int execute_upload(const struct shell *sh,
815 const struct zperf_upload_params *param,
816 bool is_udp, bool async)
817 {
818 struct zperf_results results = { 0 };
819 int ret;
820
821 shell_fprintf(sh, SHELL_NORMAL, "Duration:\t");
822 print_number_64(sh, (uint64_t)param->duration_ms * USEC_PER_MSEC, TIME_US,
823 TIME_US_UNIT);
824 shell_fprintf(sh, SHELL_NORMAL, "\n");
825 shell_fprintf(sh, SHELL_NORMAL, "Packet size:\t%u bytes\n",
826 param->packet_size);
827 shell_fprintf(sh, SHELL_NORMAL, "Rate:\t\t");
828 print_number(sh, param->rate_kbps, KBPS, KBPS_UNIT);
829 shell_fprintf(sh, SHELL_NORMAL, "\n");
830
831 if (IS_ENABLED(CONFIG_ZPERF_SESSION_PER_THREAD) &&
832 COND_CODE_1(CONFIG_ZPERF_SESSION_PER_THREAD,
833 (param->options.wait_for_start), (0))) {
834 shell_fprintf(sh, SHELL_NORMAL, "Waiting \"zperf jobs start\" command.\n");
835 } else {
836 shell_fprintf(sh, SHELL_NORMAL, "Starting...\n");
837 }
838
839 if (IS_ENABLED(CONFIG_NET_IPV6) && param->peer_addr.sa_family == AF_INET6) {
840 struct sockaddr_in6 *ipv6 =
841 (struct sockaddr_in6 *)¶m->peer_addr;
842 /* For IPv6, we should make sure that neighbor discovery
843 * has been done for the peer. So send ping here, wait
844 * some time and start the test after that.
845 */
846 send_ping(sh, &ipv6->sin6_addr, MSEC_PER_SEC);
847 }
848
849 if (is_udp && IS_ENABLED(CONFIG_NET_UDP)) {
850 uint32_t packet_duration =
851 zperf_packet_duration(param->packet_size, param->rate_kbps);
852
853 shell_fprintf(sh, SHELL_NORMAL, "Rate:\t\t");
854 print_number(sh, param->rate_kbps, KBPS, KBPS_UNIT);
855 shell_fprintf(sh, SHELL_NORMAL, "\n");
856
857 if (packet_duration > 1000U) {
858 shell_fprintf(sh, SHELL_NORMAL, "Packet duration %u ms\n",
859 (unsigned int)(packet_duration / 1000U));
860 } else {
861 shell_fprintf(sh, SHELL_NORMAL, "Packet duration %u us\n",
862 (unsigned int)packet_duration);
863 }
864
865 if (async) {
866 ret = zperf_udp_upload_async(param, udp_upload_cb,
867 (void *)sh);
868 if (ret < 0) {
869 shell_fprintf(sh, SHELL_ERROR,
870 "Failed to start UDP async upload (%d)\n", ret);
871 return ret;
872 }
873 } else {
874 ret = zperf_udp_upload(param, &results);
875 if (ret < 0) {
876 shell_fprintf(sh, SHELL_ERROR,
877 "UDP upload failed (%d)\n", ret);
878 return ret;
879 }
880
881 shell_udp_upload_print_stats(sh, &results, false);
882 }
883 } else {
884 if (is_udp && !IS_ENABLED(CONFIG_NET_UDP)) {
885 shell_fprintf(sh, SHELL_WARNING,
886 "UDP not supported\n");
887 }
888 }
889
890 if (!is_udp && IS_ENABLED(CONFIG_NET_TCP)) {
891 if (async) {
892 ret = zperf_tcp_upload_async(param, tcp_upload_cb,
893 (void *)sh);
894 if (ret < 0) {
895 shell_fprintf(sh, SHELL_ERROR,
896 "Failed to start TCP async upload (%d)\n", ret);
897 return ret;
898 }
899 } else {
900 ret = zperf_tcp_upload(param, &results);
901 if (ret < 0) {
902 shell_fprintf(sh, SHELL_ERROR,
903 "TCP upload failed (%d)\n", ret);
904 return ret;
905 }
906
907 shell_tcp_upload_print_stats(sh, &results, false);
908 }
909 } else {
910 if (!is_udp && !IS_ENABLED(CONFIG_NET_TCP)) {
911 shell_fprintf(sh, SHELL_WARNING,
912 "TCP not supported\n");
913 }
914 }
915
916 return 0;
917 }
918
parse_arg(size_t * i,size_t argc,char * argv[])919 static int parse_arg(size_t *i, size_t argc, char *argv[])
920 {
921 int res = -1;
922 const char *str = argv[*i] + 2;
923 char *endptr;
924
925 if (*str == 0) {
926 if (*i + 1 >= argc) {
927 return -1;
928 }
929
930 *i += 1;
931 str = argv[*i];
932 }
933
934 errno = 0;
935 if (strncmp(str, "0x", 2) == 0) {
936 res = strtol(str, &endptr, 16);
937 } else {
938 res = strtol(str, &endptr, 10);
939 }
940
941 if (errno || (endptr == str)) {
942 return -1;
943 }
944
945 return res;
946 }
947
948 #ifdef CONFIG_ZPERF_SESSION_PER_THREAD
check_priority(const struct shell * sh,int priority)949 static bool check_priority(const struct shell *sh, int priority)
950 {
951 if (!((priority >= -CONFIG_NUM_COOP_PRIORITIES && priority <= -1) ||
952 (priority >= 0 && priority <= (CONFIG_NUM_PREEMPT_PRIORITIES - 1)))) {
953 shell_fprintf(sh, SHELL_WARNING,
954 "Invalid priority: %d\n"
955 "Valid values are [%d, %d] for co-operative "
956 "and [%d, %d] for pre-emptive threads\n",
957 priority,
958 -CONFIG_NUM_COOP_PRIORITIES, -1,
959 0, CONFIG_NUM_PREEMPT_PRIORITIES - 1);
960 return false;
961 }
962
963 return true;
964 }
965 #endif /* CONFIG_ZPERF_SESSION_PER_THREAD */
966
shell_cmd_upload(const struct shell * sh,size_t argc,char * argv[],enum net_ip_protocol proto)967 static int shell_cmd_upload(const struct shell *sh, size_t argc,
968 char *argv[], enum net_ip_protocol proto)
969 {
970 struct zperf_upload_params param = { 0 };
971 struct sockaddr_in6 ipv6 = { .sin6_family = AF_INET6 };
972 struct sockaddr_in ipv4 = { .sin_family = AF_INET };
973 char *port_str;
974 bool async = false;
975 bool is_udp;
976 int start = 0;
977 size_t opt_cnt = 0;
978 int ret;
979 int seconds;
980
981 param.unix_offset_us = k_uptime_get() * USEC_PER_MSEC;
982 param.options.priority = -1;
983 is_udp = proto == IPPROTO_UDP;
984
985 /* Parse options */
986 for (size_t i = 1; i < argc; ++i) {
987 if (*argv[i] != '-') {
988 break;
989 }
990
991 switch (argv[i][1]) {
992 case 'S': {
993 int tos = parse_arg(&i, argc, argv);
994
995 if (tos < 0 || tos > UINT8_MAX) {
996 shell_fprintf(sh, SHELL_WARNING,
997 "Parse error: %s\n", argv[i]);
998 return -ENOEXEC;
999 }
1000
1001 param.options.tos = tos;
1002 opt_cnt += 2;
1003 break;
1004 }
1005
1006 case 'a':
1007 async = true;
1008 opt_cnt += 1;
1009 break;
1010
1011 case 'n':
1012 if (is_udp) {
1013 shell_fprintf(sh, SHELL_WARNING,
1014 "UDP does not support -n option\n");
1015 return -ENOEXEC;
1016 }
1017 param.options.tcp_nodelay = 1;
1018 opt_cnt += 1;
1019 break;
1020
1021 #ifdef CONFIG_ZPERF_SESSION_PER_THREAD
1022 case 't':
1023 param.options.thread_priority = parse_arg(&i, argc, argv);
1024 if (!check_priority(sh, param.options.thread_priority)) {
1025 shell_fprintf(sh, SHELL_WARNING,
1026 "Parse error: %s\n", argv[i]);
1027 return -ENOEXEC;
1028 }
1029 opt_cnt += 2;
1030 async = true;
1031 break;
1032
1033 case 'w':
1034 param.options.wait_for_start = true;
1035 opt_cnt += 1;
1036 break;
1037 #endif /* CONFIG_ZPERF_SESSION_PER_THREAD */
1038
1039 #ifdef CONFIG_NET_CONTEXT_PRIORITY
1040 case 'p':
1041 param.options.priority = parse_arg(&i, argc, argv);
1042 if (param.options.priority < 0 ||
1043 param.options.priority > UINT8_MAX) {
1044 shell_fprintf(sh, SHELL_WARNING,
1045 "Parse error: %s\n", argv[i]);
1046 return -ENOEXEC;
1047 }
1048 opt_cnt += 2;
1049 break;
1050 #endif /* CONFIG_NET_CONTEXT_PRIORITY */
1051
1052 case 'I':
1053 i++;
1054 if (i >= argc) {
1055 shell_fprintf(sh, SHELL_WARNING,
1056 "-I <interface name>\n");
1057 return -ENOEXEC;
1058 }
1059 (void)memset(param.if_name, 0x0, IFNAMSIZ);
1060 strncpy(param.if_name, argv[i], IFNAMSIZ - 1);
1061
1062 opt_cnt += 2;
1063 break;
1064
1065 case 'i':
1066 seconds = parse_arg(&i, argc, argv);
1067
1068 if (is_udp) {
1069 shell_fprintf(sh, SHELL_WARNING,
1070 "UDP does not support -i option\n");
1071 return -ENOEXEC;
1072 }
1073 if (seconds < 0 || seconds > UINT16_MAX) {
1074 shell_fprintf(sh, SHELL_WARNING,
1075 "Parse error: %s\n", argv[i]);
1076 return -ENOEXEC;
1077 }
1078
1079 param.options.report_interval_ms = seconds * MSEC_PER_SEC;
1080 opt_cnt += 2;
1081 break;
1082
1083 default:
1084 shell_fprintf(sh, SHELL_WARNING,
1085 "Unrecognized argument: %s\n", argv[i]);
1086 return -ENOEXEC;
1087 }
1088 }
1089
1090 start += opt_cnt;
1091 argc -= opt_cnt;
1092
1093 if (argc < 2) {
1094 shell_fprintf(sh, SHELL_WARNING,
1095 "Not enough parameters.\n");
1096
1097 if (is_udp) {
1098 if (IS_ENABLED(CONFIG_NET_UDP)) {
1099 shell_help(sh);
1100 return -ENOEXEC;
1101 }
1102 } else {
1103 if (IS_ENABLED(CONFIG_NET_TCP)) {
1104 shell_help(sh);
1105 return -ENOEXEC;
1106 }
1107 }
1108
1109 return -ENOEXEC;
1110 }
1111
1112 if (argc > 2) {
1113 port_str = argv[start + 2];
1114 shell_fprintf(sh, SHELL_NORMAL,
1115 "Remote port is %s\n", port_str);
1116 } else {
1117 port_str = DEF_PORT_STR;
1118 }
1119
1120 if (IS_ENABLED(CONFIG_NET_IPV6) && !IS_ENABLED(CONFIG_NET_IPV4)) {
1121 ret = parse_ipv6_addr(sh, argv[start + 1], port_str, &ipv6);
1122 if (ret == -EDESTADDRREQ) {
1123 shell_fprintf(sh, SHELL_WARNING,
1124 "Invalid IPv6 address %s\n", argv[start + 1]);
1125 }
1126 if (ret < 0) {
1127 shell_fprintf(sh, SHELL_WARNING,
1128 "Please specify the IP address of the "
1129 "remote server.\n");
1130 return -ENOEXEC;
1131 }
1132
1133 shell_fprintf(sh, SHELL_NORMAL, "Connecting to %s\n",
1134 net_sprint_ipv6_addr(&ipv6.sin6_addr));
1135
1136 memcpy(¶m.peer_addr, &ipv6, sizeof(ipv6));
1137 }
1138
1139 if (IS_ENABLED(CONFIG_NET_IPV4) && !IS_ENABLED(CONFIG_NET_IPV6)) {
1140 ret = parse_ipv4_addr(sh, argv[start + 1], port_str, &ipv4);
1141 if (ret == -EDESTADDRREQ) {
1142 shell_fprintf(sh, SHELL_WARNING,
1143 "Invalid IPv4 address %s\n", argv[start + 1]);
1144 }
1145 if (ret < 0) {
1146 shell_fprintf(sh, SHELL_WARNING,
1147 "Please specify the IP address of the "
1148 "remote server.\n");
1149 return -ENOEXEC;
1150 }
1151
1152 shell_fprintf(sh, SHELL_NORMAL, "Connecting to %s\n",
1153 net_sprint_ipv4_addr(&ipv4.sin_addr));
1154
1155 memcpy(¶m.peer_addr, &ipv4, sizeof(ipv4));
1156 }
1157
1158 if (IS_ENABLED(CONFIG_NET_IPV6) && IS_ENABLED(CONFIG_NET_IPV4)) {
1159 ret = parse_ipv6_addr(sh, argv[start + 1], port_str, &ipv6);
1160 if (ret < 0) {
1161 ret = parse_ipv4_addr(sh, argv[start + 1], port_str, &ipv4);
1162 if (ret == -EDESTADDRREQ) {
1163 shell_fprintf(sh, SHELL_WARNING,
1164 "Invalid IP address %s\n", argv[start + 1]);
1165 }
1166 if (ret < 0) {
1167 shell_fprintf(sh, SHELL_WARNING,
1168 "Please specify the IP address "
1169 "of the remote server.\n");
1170 return -ENOEXEC;
1171 }
1172
1173 shell_fprintf(sh, SHELL_NORMAL,
1174 "Connecting to %s\n",
1175 net_sprint_ipv4_addr(&ipv4.sin_addr));
1176
1177 memcpy(¶m.peer_addr, &ipv4, sizeof(ipv4));
1178 } else {
1179 shell_fprintf(sh, SHELL_NORMAL,
1180 "Connecting to %s\n",
1181 net_sprint_ipv6_addr(&ipv6.sin6_addr));
1182
1183 memcpy(¶m.peer_addr, &ipv6, sizeof(ipv6));
1184 }
1185 }
1186
1187 if (argc > 3) {
1188 param.duration_ms = MSEC_PER_SEC * strtoul(argv[start + 3],
1189 NULL, 10);
1190 } else {
1191 param.duration_ms = MSEC_PER_SEC * DEF_DURATION_SECONDS;
1192 }
1193
1194 if (argc > 4) {
1195 param.packet_size = parse_number(argv[start + 4], K, K_UNIT);
1196 } else {
1197 param.packet_size = DEF_PACKET_SIZE;
1198 }
1199
1200 if (argc > 5) {
1201 param.rate_kbps =
1202 (parse_number(argv[start + 5], K, K_UNIT) + 999) / 1000;
1203 if (!is_udp) {
1204 shell_fprintf(sh, SHELL_WARNING,
1205 "TCP upload will ignore <baud rate> argument\n");
1206 }
1207 } else {
1208 param.rate_kbps = DEF_RATE_KBPS;
1209 }
1210
1211 return execute_upload(sh, ¶m, is_udp, async);
1212 }
1213
cmd_tcp_upload(const struct shell * sh,size_t argc,char * argv[])1214 static int cmd_tcp_upload(const struct shell *sh, size_t argc, char *argv[])
1215 {
1216 return shell_cmd_upload(sh, argc, argv, IPPROTO_TCP);
1217 }
1218
cmd_udp_upload(const struct shell * sh,size_t argc,char * argv[])1219 static int cmd_udp_upload(const struct shell *sh, size_t argc, char *argv[])
1220 {
1221 return shell_cmd_upload(sh, argc, argv, IPPROTO_UDP);
1222 }
1223
shell_cmd_upload2(const struct shell * sh,size_t argc,char * argv[],enum net_ip_protocol proto)1224 static int shell_cmd_upload2(const struct shell *sh, size_t argc,
1225 char *argv[], enum net_ip_protocol proto)
1226 {
1227 struct zperf_upload_params param = { 0 };
1228 sa_family_t family;
1229 uint8_t is_udp;
1230 bool async = false;
1231 int start = 0;
1232 size_t opt_cnt = 0;
1233 int seconds;
1234
1235 param.unix_offset_us = k_uptime_get() * USEC_PER_MSEC;
1236 is_udp = proto == IPPROTO_UDP;
1237
1238 /* Parse options */
1239 for (size_t i = 1; i < argc; ++i) {
1240 if (*argv[i] != '-') {
1241 break;
1242 }
1243
1244 switch (argv[i][1]) {
1245 case 'S': {
1246 int tos = parse_arg(&i, argc, argv);
1247
1248 if (tos < 0 || tos > UINT8_MAX) {
1249 shell_fprintf(sh, SHELL_WARNING,
1250 "Parse error: %s\n", argv[i]);
1251 return -ENOEXEC;
1252 }
1253
1254 param.options.tos = tos;
1255 opt_cnt += 2;
1256 break;
1257 }
1258
1259 case 'a':
1260 async = true;
1261 opt_cnt += 1;
1262 break;
1263
1264 case 'n':
1265 if (is_udp) {
1266 shell_fprintf(sh, SHELL_WARNING,
1267 "UDP does not support -n option\n");
1268 return -ENOEXEC;
1269 }
1270 param.options.tcp_nodelay = 1;
1271 opt_cnt += 1;
1272 break;
1273
1274 #ifdef CONFIG_ZPERF_SESSION_PER_THREAD
1275 case 't':
1276 param.options.thread_priority = parse_arg(&i, argc, argv);
1277 if (!check_priority(sh, param.options.thread_priority)) {
1278 shell_fprintf(sh, SHELL_WARNING,
1279 "Parse error: %s\n", argv[i]);
1280 return -ENOEXEC;
1281 }
1282 opt_cnt += 2;
1283 async = true;
1284 break;
1285
1286 case 'w':
1287 param.options.wait_for_start = true;
1288 opt_cnt += 1;
1289 break;
1290 #endif /* CONFIG_ZPERF_SESSION_PER_THREAD */
1291
1292 #ifdef CONFIG_NET_CONTEXT_PRIORITY
1293 case 'p':
1294 param.options.priority = parse_arg(&i, argc, argv);
1295 if (param.options.priority == -1 ||
1296 param.options.priority > UINT8_MAX) {
1297 shell_fprintf(sh, SHELL_WARNING,
1298 "Parse error: %s\n", argv[i]);
1299 return -ENOEXEC;
1300 }
1301 opt_cnt += 2;
1302 break;
1303 #endif /* CONFIG_NET_CONTEXT_PRIORITY */
1304
1305 case 'I':
1306 i++;
1307 if (i >= argc) {
1308 shell_fprintf(sh, SHELL_WARNING,
1309 "-I <interface name>\n");
1310 return -ENOEXEC;
1311 }
1312 (void)memset(param.if_name, 0x0, IFNAMSIZ);
1313 strncpy(param.if_name, argv[i], IFNAMSIZ - 1);
1314
1315 opt_cnt += 2;
1316 break;
1317
1318 case 'i':
1319 seconds = parse_arg(&i, argc, argv);
1320
1321 if (is_udp) {
1322 shell_fprintf(sh, SHELL_WARNING,
1323 "UDP does not support -i option\n");
1324 return -ENOEXEC;
1325 }
1326 if (seconds < 0 || seconds > UINT16_MAX) {
1327 shell_fprintf(sh, SHELL_WARNING,
1328 "Parse error: %s\n", argv[i]);
1329 return -ENOEXEC;
1330 }
1331
1332 param.options.report_interval_ms = seconds * MSEC_PER_SEC;
1333 opt_cnt += 2;
1334 break;
1335
1336 default:
1337 shell_fprintf(sh, SHELL_WARNING,
1338 "Unrecognized argument: %s\n", argv[i]);
1339 return -ENOEXEC;
1340 }
1341 }
1342
1343 start += opt_cnt;
1344 argc -= opt_cnt;
1345
1346 if (argc < 2) {
1347 shell_fprintf(sh, SHELL_WARNING,
1348 "Not enough parameters.\n");
1349
1350 if (is_udp) {
1351 if (IS_ENABLED(CONFIG_NET_UDP)) {
1352 shell_help(sh);
1353 return -ENOEXEC;
1354 }
1355 } else {
1356 if (IS_ENABLED(CONFIG_NET_TCP)) {
1357 shell_help(sh);
1358 return -ENOEXEC;
1359 }
1360 }
1361
1362 return -ENOEXEC;
1363 }
1364
1365 family = !strcmp(argv[start + 1], "v4") ? AF_INET : AF_INET6;
1366
1367 if (family == AF_INET6) {
1368 if (net_ipv6_is_addr_unspecified(&in6_addr_dst.sin6_addr)) {
1369 shell_fprintf(sh, SHELL_WARNING,
1370 "Invalid destination IPv6 address.\n");
1371 return -ENOEXEC;
1372 }
1373
1374 shell_fprintf(sh, SHELL_NORMAL,
1375 "Connecting to %s\n",
1376 net_sprint_ipv6_addr(&in6_addr_dst.sin6_addr));
1377
1378 memcpy(¶m.peer_addr, &in6_addr_dst, sizeof(in6_addr_dst));
1379 } else {
1380 if (net_ipv4_is_addr_unspecified(&in4_addr_dst.sin_addr)) {
1381 shell_fprintf(sh, SHELL_WARNING,
1382 "Invalid destination IPv4 address.\n");
1383 return -ENOEXEC;
1384 }
1385
1386 shell_fprintf(sh, SHELL_NORMAL,
1387 "Connecting to %s\n",
1388 net_sprint_ipv4_addr(&in4_addr_dst.sin_addr));
1389
1390 memcpy(¶m.peer_addr, &in4_addr_dst, sizeof(in4_addr_dst));
1391 }
1392
1393 if (argc > 2) {
1394 param.duration_ms = MSEC_PER_SEC * strtoul(argv[start + 2],
1395 NULL, 10);
1396 } else {
1397 param.duration_ms = MSEC_PER_SEC * DEF_DURATION_SECONDS;
1398 }
1399
1400 if (argc > 3) {
1401 param.packet_size = parse_number(argv[start + 3], K, K_UNIT);
1402 } else {
1403 param.packet_size = DEF_PACKET_SIZE;
1404 }
1405
1406 if (argc > 4) {
1407 param.rate_kbps =
1408 (parse_number(argv[start + 4], K, K_UNIT) + 999) / 1000;
1409 if (!is_udp) {
1410 shell_fprintf(sh, SHELL_WARNING,
1411 "TCP upload will ignore <baud rate> argument\n");
1412 }
1413 } else {
1414 param.rate_kbps = DEF_RATE_KBPS;
1415 }
1416
1417 return execute_upload(sh, ¶m, is_udp, async);
1418 }
1419
cmd_tcp_upload2(const struct shell * sh,size_t argc,char * argv[])1420 static int cmd_tcp_upload2(const struct shell *sh, size_t argc,
1421 char *argv[])
1422 {
1423 return shell_cmd_upload2(sh, argc, argv, IPPROTO_TCP);
1424 }
1425
cmd_udp_upload2(const struct shell * sh,size_t argc,char * argv[])1426 static int cmd_udp_upload2(const struct shell *sh, size_t argc,
1427 char *argv[])
1428 {
1429 return shell_cmd_upload2(sh, argc, argv, IPPROTO_UDP);
1430 }
1431
cmd_tcp(const struct shell * sh,size_t argc,char * argv[])1432 static int cmd_tcp(const struct shell *sh, size_t argc, char *argv[])
1433 {
1434 if (IS_ENABLED(CONFIG_NET_TCP)) {
1435 shell_help(sh);
1436 return -ENOEXEC;
1437 }
1438
1439 shell_fprintf(sh, SHELL_INFO, "TCP support is not enabled. "
1440 "Set CONFIG_NET_TCP=y in your config file.\n");
1441
1442 return -ENOTSUP;
1443 }
1444
cmd_udp(const struct shell * sh,size_t argc,char * argv[])1445 static int cmd_udp(const struct shell *sh, size_t argc, char *argv[])
1446 {
1447 if (IS_ENABLED(CONFIG_NET_UDP)) {
1448 shell_help(sh);
1449 return -ENOEXEC;
1450 }
1451
1452 shell_fprintf(sh, SHELL_INFO, "UDP support is not enabled. "
1453 "Set CONFIG_NET_UDP=y in your config file.\n");
1454
1455 return -ENOTSUP;
1456 }
1457
cmd_connectap(const struct shell * sh,size_t argc,char * argv[])1458 static int cmd_connectap(const struct shell *sh, size_t argc, char *argv[])
1459 {
1460 shell_fprintf(sh, SHELL_INFO,
1461 "Zephyr has not been built with %s support.\n", "Wi-Fi");
1462
1463 return 0;
1464 }
1465
1466 #ifdef CONFIG_NET_ZPERF_SERVER
1467
tcp_session_cb(enum zperf_status status,struct zperf_results * result,void * user_data)1468 static void tcp_session_cb(enum zperf_status status,
1469 struct zperf_results *result,
1470 void *user_data)
1471 {
1472 const struct shell *sh = user_data;
1473
1474 switch (status) {
1475 case ZPERF_SESSION_STARTED:
1476 shell_fprintf(sh, SHELL_NORMAL, "New TCP session started.\n");
1477 break;
1478
1479 case ZPERF_SESSION_FINISHED: {
1480 uint32_t rate_in_kbps;
1481
1482 /* Compute baud rate */
1483 if (result->time_in_us != 0U) {
1484 rate_in_kbps = (uint32_t)
1485 ((result->total_len * 8ULL * USEC_PER_SEC) /
1486 (result->time_in_us * 1000ULL));
1487 } else {
1488 rate_in_kbps = 0U;
1489 }
1490
1491 shell_fprintf(sh, SHELL_NORMAL, "TCP session ended\n");
1492
1493 shell_fprintf(sh, SHELL_NORMAL, " Duration:\t\t");
1494 print_number_64(sh, result->time_in_us, TIME_US, TIME_US_UNIT);
1495 shell_fprintf(sh, SHELL_NORMAL, "\n");
1496
1497 shell_fprintf(sh, SHELL_NORMAL, " rate:\t\t\t");
1498 print_number(sh, rate_in_kbps, KBPS, KBPS_UNIT);
1499 shell_fprintf(sh, SHELL_NORMAL, "\n");
1500
1501 break;
1502 }
1503
1504 case ZPERF_SESSION_ERROR:
1505 shell_fprintf(sh, SHELL_ERROR, "TCP session error.\n");
1506 break;
1507
1508 default:
1509 break;
1510 }
1511 }
1512
cmd_tcp_download_stop(const struct shell * sh,size_t argc,char * argv[])1513 static int cmd_tcp_download_stop(const struct shell *sh, size_t argc,
1514 char *argv[])
1515 {
1516 int ret;
1517
1518 if (!IS_ENABLED(CONFIG_NET_TCP)) {
1519 shell_warn(sh, "TCP not supported");
1520 return -ENOEXEC;
1521 }
1522
1523 ret = zperf_tcp_download_stop();
1524 if (ret < 0) {
1525 shell_fprintf(sh, SHELL_WARNING, "TCP server not running!\n");
1526 return -ENOEXEC;
1527 }
1528
1529 shell_fprintf(sh, SHELL_NORMAL, "TCP server stopped\n");
1530
1531 return 0;
1532 }
1533
cmd_tcp_download(const struct shell * sh,size_t argc,char * argv[])1534 static int cmd_tcp_download(const struct shell *sh, size_t argc,
1535 char *argv[])
1536 {
1537 if (IS_ENABLED(CONFIG_NET_TCP)) {
1538 struct zperf_download_params param = { 0 };
1539 int ret;
1540 int start;
1541
1542 start = shell_cmd_download(sh, argc, argv, ¶m);
1543 if (start < 0) {
1544 shell_fprintf(sh, SHELL_WARNING,
1545 "Unable to parse option.\n");
1546 return -ENOEXEC;
1547 }
1548
1549 ret = zperf_bind_host(sh, argc - start, &argv[start], ¶m);
1550 if (ret < 0) {
1551 shell_fprintf(sh, SHELL_WARNING,
1552 "Unable to bind host.\n");
1553 shell_help(sh);
1554 return -ENOEXEC;
1555 }
1556
1557 ret = zperf_tcp_download(¶m, tcp_session_cb, (void *)sh);
1558 if (ret == -EALREADY) {
1559 shell_fprintf(sh, SHELL_WARNING,
1560 "TCP server already started!\n");
1561 return -ENOEXEC;
1562 } else if (ret < 0) {
1563 shell_fprintf(sh, SHELL_ERROR,
1564 "Failed to start TCP server!\n");
1565 return -ENOEXEC;
1566 }
1567
1568 shell_fprintf(sh, SHELL_NORMAL,
1569 "TCP server started on port %u\n", param.port);
1570
1571 return 0;
1572 } else {
1573 return -ENOTSUP;
1574 }
1575 }
1576
1577 #endif
1578
cmd_version(const struct shell * sh,size_t argc,char * argv[])1579 static int cmd_version(const struct shell *sh, size_t argc, char *argv[])
1580 {
1581 shell_fprintf(sh, SHELL_NORMAL, "Version: %s\nConfig: %s\n",
1582 ZPERF_VERSION, CONFIG);
1583
1584 return 0;
1585 }
1586
1587 #ifdef CONFIG_ZPERF_SESSION_PER_THREAD
1588 struct zperf_shell_user_data {
1589 const struct shell *sh;
1590 void *user_data;
1591 int in_progress_count;
1592 int finalized_count;
1593 bool active;
1594 };
1595
session_cb(struct session * ses,enum session_proto proto,void * user_data)1596 static void session_cb(struct session *ses,
1597 enum session_proto proto,
1598 void *user_data)
1599 {
1600 struct zperf_shell_user_data *data = user_data;
1601 const struct shell *sh = data->sh;
1602 bool active = data->active;
1603
1604 if (ses->state == STATE_NULL) {
1605 return;
1606 }
1607
1608 if (active) {
1609 if (ses->in_progress) {
1610 uint32_t remaining;
1611
1612 if (ses->state != STATE_STARTING && ses->state != STATE_ONGOING) {
1613 return;
1614 }
1615
1616 if (ses->proto != proto) {
1617 return;
1618 }
1619
1620 if (data->in_progress_count == 0) {
1621 shell_fprintf(sh, SHELL_NORMAL,
1622 " Thread Remaining\n"
1623 "Id Proto Priority time (sec)\n");
1624 }
1625
1626 remaining = (uint32_t)
1627 (((uint64_t)ses->async_upload_ctx.param.duration_ms -
1628 (k_uptime_get() -
1629 k_ticks_to_ms_ceil64(ses->start_time))) / MSEC_PER_SEC);
1630
1631 shell_fprintf(sh, SHELL_NORMAL,
1632 "[%d] %s %d\t\t%d\n",
1633 ses->id, ses->proto == SESSION_UDP ? "UDP" : "TCP",
1634 ses->async_upload_ctx.param.options.thread_priority,
1635 remaining);
1636
1637 data->in_progress_count++;
1638 }
1639
1640 return;
1641 }
1642
1643 if (!ses->in_progress) {
1644 if (ses->state != STATE_COMPLETED) {
1645 return;
1646 }
1647
1648 if (ses->proto != proto) {
1649 return;
1650 }
1651
1652 if (data->finalized_count == 0) {
1653 shell_fprintf(sh, SHELL_NORMAL,
1654 " Thread\n"
1655 "Id Proto Priority \tDuration\tRate\n");
1656 }
1657
1658 shell_fprintf(sh, SHELL_NORMAL,
1659 "[%d] %s %d\t\t",
1660 ses->id, ses->proto == SESSION_UDP ? "UDP" : "TCP",
1661 ses->async_upload_ctx.param.options.thread_priority);
1662 print_number_64(sh,
1663 (uint64_t)ses->async_upload_ctx.param.duration_ms * USEC_PER_MSEC,
1664 TIME_US, TIME_US_UNIT);
1665 shell_fprintf(sh, SHELL_NORMAL, "\t\t");
1666 print_number(sh, ses->async_upload_ctx.param.rate_kbps, KBPS, KBPS_UNIT);
1667 shell_fprintf(sh, SHELL_NORMAL, "\n");
1668
1669 data->finalized_count++;
1670 }
1671 }
1672
session_all_cb(struct session * ses,enum session_proto proto,void * user_data)1673 static void session_all_cb(struct session *ses,
1674 enum session_proto proto,
1675 void *user_data)
1676 {
1677 struct zperf_shell_user_data *data = user_data;
1678 const struct shell *sh = data->sh;
1679
1680 if (ses->state == STATE_NULL) {
1681 return;
1682 }
1683
1684 if (!ses->in_progress) {
1685 if (ses->state != STATE_COMPLETED) {
1686 return;
1687 }
1688
1689 if (ses->proto != proto) {
1690 return;
1691 }
1692
1693 if (proto == SESSION_UDP) {
1694 shell_udp_upload_print_stats(sh, &ses->result, true);
1695 } else {
1696 shell_tcp_upload_print_stats(sh, &ses->result, true);
1697 }
1698
1699 data->finalized_count++;
1700 }
1701 }
1702
session_clear_cb(struct session * ses,enum session_proto proto,void * user_data)1703 static void session_clear_cb(struct session *ses,
1704 enum session_proto proto,
1705 void *user_data)
1706 {
1707 struct zperf_shell_user_data *data = user_data;
1708
1709 if (ses->state == STATE_NULL) {
1710 return;
1711 }
1712
1713 if (!ses->in_progress) {
1714 if (ses->state == STATE_COMPLETED) {
1715 ses->state = STATE_NULL;
1716 data->finalized_count++;
1717 }
1718 } else {
1719 if (ses->state == STATE_STARTING || ses->state == STATE_ONGOING) {
1720 data->in_progress_count++;
1721 }
1722 }
1723 }
1724 #endif /* CONFIG_ZPERF_SESSION_PER_THREAD */
1725
cmd_jobs(const struct shell * sh,size_t argc,char * argv[])1726 static int cmd_jobs(const struct shell *sh, size_t argc, char *argv[])
1727 {
1728 #ifdef CONFIG_ZPERF_SESSION_PER_THREAD
1729 struct zperf_shell_user_data user_data;
1730
1731 user_data.sh = sh;
1732 user_data.in_progress_count = 0;
1733 user_data.finalized_count = 0;
1734 user_data.active = true;
1735
1736 zperf_session_foreach(SESSION_UDP, session_cb, &user_data);
1737 zperf_session_foreach(SESSION_TCP, session_cb, &user_data);
1738
1739 if (user_data.in_progress_count == 0) {
1740 shell_fprintf(sh, SHELL_NORMAL,
1741 "No active upload sessions\n");
1742 }
1743
1744 shell_fprintf(sh, SHELL_NORMAL, "\n");
1745
1746 user_data.active = false;
1747
1748 zperf_session_foreach(SESSION_UDP, session_cb, &user_data);
1749 zperf_session_foreach(SESSION_TCP, session_cb, &user_data);
1750
1751 if (user_data.finalized_count == 0 && user_data.in_progress_count > 0) {
1752 shell_fprintf(sh, SHELL_NORMAL,
1753 "Active sessions have not yet finished\n");
1754 } else if (user_data.finalized_count == 0) {
1755 shell_fprintf(sh, SHELL_NORMAL,
1756 "No finished sessions found\n");
1757 } else {
1758 shell_fprintf(sh, SHELL_NORMAL,
1759 "Total %d sessions done\n",
1760 user_data.finalized_count);
1761 }
1762 #else
1763 shell_fprintf(sh, SHELL_INFO,
1764 "Zephyr has not been built with %s support.\n",
1765 "CONFIG_ZPERF_SESSION_PER_THREAD");
1766 #endif
1767 return 0;
1768 }
1769
cmd_jobs_all(const struct shell * sh,size_t argc,char * argv[])1770 static int cmd_jobs_all(const struct shell *sh, size_t argc, char *argv[])
1771 {
1772 #ifdef CONFIG_ZPERF_SESSION_PER_THREAD
1773 struct zperf_shell_user_data user_data;
1774
1775 user_data.sh = sh;
1776 user_data.in_progress_count = 0;
1777 user_data.finalized_count = 0;
1778 user_data.active = false;
1779
1780 zperf_session_foreach(SESSION_UDP, session_all_cb, &user_data);
1781 zperf_session_foreach(SESSION_TCP, session_all_cb, &user_data);
1782
1783 if (user_data.finalized_count == 0 && user_data.in_progress_count > 0) {
1784 shell_fprintf(sh, SHELL_NORMAL,
1785 "Active sessions have not yet finished\n");
1786 } else if (user_data.finalized_count == 0) {
1787 shell_fprintf(sh, SHELL_NORMAL,
1788 "No finished sessions found\n");
1789 } else {
1790 shell_fprintf(sh, SHELL_NORMAL,
1791 "Total %d sessions done\n",
1792 user_data.finalized_count);
1793 }
1794 #else
1795 shell_fprintf(sh, SHELL_INFO,
1796 "Zephyr has not been built with %s support.\n",
1797 "CONFIG_ZPERF_SESSION_PER_THREAD");
1798 #endif
1799 return 0;
1800 }
1801
cmd_jobs_clear(const struct shell * sh,size_t argc,char * argv[])1802 static int cmd_jobs_clear(const struct shell *sh, size_t argc, char *argv[])
1803 {
1804 #ifdef CONFIG_ZPERF_SESSION_PER_THREAD
1805 struct zperf_shell_user_data user_data;
1806
1807 user_data.sh = sh;
1808 user_data.in_progress_count = 0;
1809 user_data.finalized_count = 0;
1810 user_data.active = false;
1811
1812 zperf_session_foreach(SESSION_UDP, session_clear_cb, &user_data);
1813 zperf_session_foreach(SESSION_TCP, session_clear_cb, &user_data);
1814
1815 if (user_data.finalized_count == 0 && user_data.in_progress_count > 0) {
1816 shell_fprintf(sh, SHELL_NORMAL,
1817 "Active sessions have not yet finished, not clearing\n");
1818 } else if (user_data.finalized_count == 0) {
1819 shell_fprintf(sh, SHELL_NORMAL, "All sessions already cleared\n");
1820 } else {
1821 shell_fprintf(sh, SHELL_NORMAL,
1822 "Cleared data from %d sessions\n",
1823 user_data.finalized_count);
1824 }
1825 #else
1826 shell_fprintf(sh, SHELL_INFO,
1827 "Zephyr has not been built with %s support.\n",
1828 "CONFIG_ZPERF_SESSION_PER_THREAD");
1829 #endif
1830 return 0;
1831 }
1832
cmd_jobs_start(const struct shell * sh,size_t argc,char * argv[])1833 static int cmd_jobs_start(const struct shell *sh, size_t argc, char *argv[])
1834 {
1835 #ifdef CONFIG_ZPERF_SESSION_PER_THREAD
1836 ARG_UNUSED(argc);
1837 ARG_UNUSED(argv);
1838 ARG_UNUSED(sh);
1839
1840 start_jobs();
1841 #else
1842 shell_fprintf(sh, SHELL_INFO,
1843 "Zephyr has not been built with %s support.\n",
1844 "CONFIG_ZPERF_SESSION_PER_THREAD");
1845 #endif
1846 return 0;
1847 }
1848
zperf_shell_init(void)1849 void zperf_shell_init(void)
1850 {
1851 int ret;
1852
1853 if (IS_ENABLED(MY_IP6ADDR_SET) && MY_IP6ADDR) {
1854 ret = net_addr_pton(AF_INET6, MY_IP6ADDR,
1855 &in6_addr_my.sin6_addr);
1856 if (ret < 0) {
1857 NET_WARN("Unable to set %s address\n", "IPv6");
1858 } else {
1859 NET_INFO("Setting IP address %s",
1860 net_sprint_ipv6_addr(&in6_addr_my.sin6_addr));
1861 }
1862
1863 ret = net_addr_pton(AF_INET6, DST_IP6ADDR,
1864 &in6_addr_dst.sin6_addr);
1865 if (ret < 0) {
1866 NET_WARN("Unable to set destination %s address %s",
1867 "IPv6",
1868 DST_IP6ADDR ? DST_IP6ADDR
1869 : "(not set)");
1870 } else {
1871 NET_INFO("Setting destination IP address %s",
1872 net_sprint_ipv6_addr(&in6_addr_dst.sin6_addr));
1873 }
1874 }
1875
1876 if (IS_ENABLED(MY_IP4ADDR_SET) && MY_IP4ADDR) {
1877 ret = net_addr_pton(AF_INET, MY_IP4ADDR,
1878 &in4_addr_my.sin_addr);
1879 if (ret < 0) {
1880 NET_WARN("Unable to set %s address\n", "IPv4");
1881 } else {
1882 NET_INFO("Setting IP address %s",
1883 net_sprint_ipv4_addr(&in4_addr_my.sin_addr));
1884 }
1885
1886 ret = net_addr_pton(AF_INET, DST_IP4ADDR,
1887 &in4_addr_dst.sin_addr);
1888 if (ret < 0) {
1889 NET_WARN("Unable to set destination %s address %s",
1890 "IPv4",
1891 DST_IP4ADDR ? DST_IP4ADDR
1892 : "(not set)");
1893 } else {
1894 NET_INFO("Setting destination IP address %s",
1895 net_sprint_ipv4_addr(&in4_addr_dst.sin_addr));
1896 }
1897 }
1898 }
1899
1900 #ifdef CONFIG_NET_ZPERF_SERVER
1901
1902 SHELL_STATIC_SUBCMD_SET_CREATE(zperf_cmd_tcp_download,
1903 SHELL_CMD(stop, NULL, "Stop TCP server\n", cmd_tcp_download_stop),
1904 SHELL_SUBCMD_SET_END
1905 );
1906
1907 #endif
1908
1909 SHELL_STATIC_SUBCMD_SET_CREATE(zperf_cmd_tcp,
1910 SHELL_CMD(upload, NULL,
1911 "[<options>] <dest ip> <dest port> <duration> <packet size>[K]\n"
1912 "<options> command options (optional): [-S tos -a]\n"
1913 "<dest ip> IP destination\n"
1914 "<dest port> port destination\n"
1915 "<duration> of the test in seconds "
1916 "(default " DEF_DURATION_SECONDS_STR ")\n"
1917 "<packet size> in byte or kilobyte "
1918 "(with suffix K) "
1919 "(default " DEF_PACKET_SIZE_STR ")\n"
1920 "Available options:\n"
1921 "-S tos: Specify IPv4/6 type of service\n"
1922 "-a: Asynchronous call (shell will not block for the upload)\n"
1923 "-i sec: Periodic reporting interval in seconds (async only)\n"
1924 "-n: Disable Nagle's algorithm\n"
1925 #ifdef CONFIG_ZPERF_SESSION_PER_THREAD
1926 "-t: Specify custom thread priority\n"
1927 "-w: Wait for start signal before starting the tests\n"
1928 #endif /* CONFIG_ZPERF_SESSION_PER_THREAD */
1929 #ifdef CONFIG_NET_CONTEXT_PRIORITY
1930 "-p: Specify custom packet priority\n"
1931 #endif /* CONFIG_NET_CONTEXT_PRIORITY */
1932 "Example: tcp upload 192.0.2.2 1111 1 1K\n"
1933 "Example: tcp upload 2001:db8::2\n",
1934 cmd_tcp_upload),
1935 SHELL_CMD(upload2, NULL,
1936 "[<options>] v6|v4 <duration> <packet size>[K]\n"
1937 "<options> command options (optional): [-S tos -a]\n"
1938 "<v6|v4>: Use either IPv6 or IPv4\n"
1939 "<duration> of the test in seconds "
1940 "(default " DEF_DURATION_SECONDS_STR ")\n"
1941 "<packet size> in byte or kilobyte "
1942 "(with suffix K) "
1943 "(default " DEF_PACKET_SIZE_STR ")\n"
1944 "Available options:\n"
1945 "-S tos: Specify IPv4/6 type of service\n"
1946 "-a: Asynchronous call (shell will not block for the upload)\n"
1947 "-i sec: Periodic reporting interval in seconds (async only)\n"
1948 "-n: Disable Nagle's algorithm\n"
1949 #ifdef CONFIG_ZPERF_SESSION_PER_THREAD
1950 "-t: Specify custom thread priority\n"
1951 "-w: Wait for start signal before starting the tests\n"
1952 #endif /* CONFIG_ZPERF_SESSION_PER_THREAD */
1953 #ifdef CONFIG_NET_CONTEXT_PRIORITY
1954 "-p: Specify custom packet priority\n"
1955 #endif /* CONFIG_NET_CONTEXT_PRIORITY */
1956 "Example: tcp upload2 v6 1 1K\n"
1957 "Example: tcp upload2 v4\n"
1958 #if defined(CONFIG_NET_IPV6) && defined(MY_IP6ADDR_SET)
1959 "Default IPv6 address is " MY_IP6ADDR
1960 ", destination [" DST_IP6ADDR "]:" DEF_PORT_STR "\n"
1961 #endif
1962 #if defined(CONFIG_NET_IPV4) && defined(MY_IP4ADDR_SET)
1963 "Default IPv4 address is " MY_IP4ADDR
1964 ", destination " DST_IP4ADDR ":" DEF_PORT_STR "\n"
1965 #endif
1966 ,
1967 cmd_tcp_upload2),
1968 #ifdef CONFIG_NET_ZPERF_SERVER
1969 SHELL_CMD(download, &zperf_cmd_tcp_download,
1970 "[<port>]: Server port to listen on/connect to\n"
1971 "[<host>]: Bind to <host>, an interface address\n"
1972 "Example: tcp download 5001 192.168.0.1\n",
1973 cmd_tcp_download),
1974 #endif
1975 SHELL_SUBCMD_SET_END
1976 );
1977
1978 #ifdef CONFIG_NET_ZPERF_SERVER
1979 SHELL_STATIC_SUBCMD_SET_CREATE(zperf_cmd_udp_download,
1980 SHELL_CMD(stop, NULL, "Stop UDP server\n", cmd_udp_download_stop),
1981 SHELL_SUBCMD_SET_END
1982 );
1983 #endif
1984
1985 SHELL_STATIC_SUBCMD_SET_CREATE(zperf_cmd_udp,
1986 SHELL_CMD(upload, NULL,
1987 "[<options>] <dest ip> [<dest port> <duration> <packet size>[K] "
1988 "<baud rate>[K|M]]\n"
1989 "<options> command options (optional): [-S tos -a]\n"
1990 "<dest ip> IP destination\n"
1991 "<dest port> port destination\n"
1992 "<duration> of the test in seconds "
1993 "(default " DEF_DURATION_SECONDS_STR ")\n"
1994 "<packet size> in byte or kilobyte "
1995 "(with suffix K) "
1996 "(default " DEF_PACKET_SIZE_STR ")\n"
1997 "<baud rate> in kilobyte or megabyte "
1998 "(default " DEF_RATE_KBPS_STR "K)\n"
1999 "Available options:\n"
2000 "-S tos: Specify IPv4/6 type of service\n"
2001 "-a: Asynchronous call (shell will not block for the upload)\n"
2002 #ifdef CONFIG_ZPERF_SESSION_PER_THREAD
2003 "-t: Specify custom thread priority\n"
2004 "-w: Wait for start signal before starting the tests\n"
2005 #endif /* CONFIG_ZPERF_SESSION_PER_THREAD */
2006 #ifdef CONFIG_NET_CONTEXT_PRIORITY
2007 "-p: Specify custom packet priority\n"
2008 #endif /* CONFIG_NET_CONTEXT_PRIORITY */
2009 "-I: Specify host interface name\n"
2010 "Example: udp upload 192.0.2.2 1111 1 1K 1M\n"
2011 "Example: udp upload 2001:db8::2\n",
2012 cmd_udp_upload),
2013 SHELL_CMD(upload2, NULL,
2014 "[<options>] v6|v4 [<duration> <packet size>[K] <baud rate>[K|M]]\n"
2015 "<options> command options (optional): [-S tos -a]\n"
2016 "<v6|v4>: Use either IPv6 or IPv4\n"
2017 "<duration> of the test in seconds "
2018 "(default " DEF_DURATION_SECONDS_STR ")\n"
2019 "<packet size> in byte or kilobyte "
2020 "(with suffix K) "
2021 "(default " DEF_PACKET_SIZE_STR ")\n"
2022 "<baud rate> in kilobyte or megabyte "
2023 "(default " DEF_RATE_KBPS_STR "K)\n"
2024 "Available options:\n"
2025 "-S tos: Specify IPv4/6 type of service\n"
2026 "-a: Asynchronous call (shell will not block for the upload)\n"
2027 #ifdef CONFIG_ZPERF_SESSION_PER_THREAD
2028 "-t: Specify custom thread priority\n"
2029 "-w: Wait for start signal before starting the tests\n"
2030 #endif /* CONFIG_ZPERF_SESSION_PER_THREAD */
2031 #ifdef CONFIG_NET_CONTEXT_PRIORITY
2032 "-p: Specify custom packet priority\n"
2033 #endif /* CONFIG_NET_CONTEXT_PRIORITY */
2034 "-I: Specify host interface name\n"
2035 "Example: udp upload2 v4 1 1K 1M\n"
2036 "Example: udp upload2 v6\n"
2037 #if defined(CONFIG_NET_IPV6) && defined(MY_IP6ADDR_SET)
2038 "Default IPv6 address is " MY_IP6ADDR
2039 ", destination [" DST_IP6ADDR "]:" DEF_PORT_STR "\n"
2040 #endif
2041 #if defined(CONFIG_NET_IPV4) && defined(MY_IP4ADDR_SET)
2042 "Default IPv4 address is " MY_IP4ADDR
2043 ", destination " DST_IP4ADDR ":" DEF_PORT_STR "\n"
2044 #endif
2045 ,
2046 cmd_udp_upload2),
2047 #ifdef CONFIG_NET_ZPERF_SERVER
2048 SHELL_CMD(download, &zperf_cmd_udp_download,
2049 "[<options>] command options (optional): [-I eth0]\n"
2050 "[<port>]: Server port to listen on/connect to\n"
2051 "[<host>]: Bind to <host>, an interface address\n"
2052 "Available options:\n"
2053 "-I <interface name>: Specify host interface name\n"
2054 "Example: udp download 5001 192.168.0.1\n",
2055 cmd_udp_download),
2056 #endif
2057 SHELL_SUBCMD_SET_END
2058 );
2059
2060 SHELL_STATIC_SUBCMD_SET_CREATE(zperf_cmd_jobs,
2061 SHELL_CMD(all, NULL, "Show all statistics", cmd_jobs_all),
2062 SHELL_CMD(clear, NULL, "Clear all statistics", cmd_jobs_clear),
2063 SHELL_CMD(start, NULL, "Start waiting jobs", cmd_jobs_start),
2064 );
2065
2066 SHELL_STATIC_SUBCMD_SET_CREATE(zperf_commands,
2067 SHELL_CMD(connectap, NULL,
2068 "Connect to AP",
2069 cmd_connectap),
2070 SHELL_CMD(jobs, &zperf_cmd_jobs,
2071 "Show currently active tests",
2072 cmd_jobs),
2073 SHELL_CMD(setip, NULL,
2074 "Set IP address\n"
2075 "<my ip> <prefix len>\n"
2076 "Example setip 2001:db8::2 64\n"
2077 "Example setip 192.0.2.2\n",
2078 cmd_setip),
2079 SHELL_CMD(tcp, &zperf_cmd_tcp,
2080 "Upload/Download TCP data",
2081 cmd_tcp),
2082 SHELL_CMD(udp, &zperf_cmd_udp,
2083 "Upload/Download UDP data",
2084 cmd_udp),
2085 SHELL_CMD(version, NULL,
2086 "Zperf version",
2087 cmd_version),
2088 SHELL_SUBCMD_SET_END
2089 );
2090
2091 SHELL_CMD_REGISTER(zperf, &zperf_commands, "Zperf commands", NULL);
2092