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(&param->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, &param);
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], &param);
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(&param, 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 *)&param->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(&param.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(&param.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(&param.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(&param.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, &param, 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(&param.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(&param.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, &param, 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, &param);
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], &param);
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(&param, 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