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