xref: /wlan-dirver/utils/sigma-dut/traffic.c (revision 5bfb16a8a7193d99dbc63b7c2f6fb37940c522f1)
1 /*
2  * Sigma Control API DUT (station/AP)
3  * Copyright (c) 2010, Atheros Communications, Inc.
4  * Copyright (c) 2011-2013, 2016-2017 Qualcomm Atheros, Inc.
5  * Copyright (c) 2018-2019, The Linux Foundation
6  * All Rights Reserved.
7  * Licensed under the Clear BSD license. See README for more details.
8  */
9 
10 #include "sigma_dut.h"
11 #include <sys/types.h>
12 #include <sys/stat.h>
13 #include <fcntl.h>
14 #include <signal.h>
15 #include <ctype.h>
16 #include <ifaddrs.h>
17 
18 #include "wpa_helpers.h"
19 
20 #ifdef ANDROID
21 #define SHELL "/system/bin/sh"
22 #else /* ANDROID */
23 #define SHELL "/bin/sh"
24 #endif /* ANDROID */
25 
26 
cmd_traffic_send_ping(struct sigma_dut * dut,struct sigma_conn * conn,struct sigma_cmd * cmd)27 static enum sigma_cmd_result cmd_traffic_send_ping(struct sigma_dut *dut,
28 						   struct sigma_conn *conn,
29 						   struct sigma_cmd *cmd)
30 {
31 	const char *dst, *val;
32 	int size, dur, pkts;
33 	int id;
34 	char resp[100];
35 	float interval;
36 	double rate;
37 	FILE *f;
38 	char buf[100];
39 	int type = 1;
40 	int dscp = 0, use_dscp = 0;
41 	char extra[100], int_arg[100], intf_arg[100], ip_dst[100], ping[100];
42 
43 	val = get_param(cmd, "Type");
44 	if (!val)
45 		val = get_param(cmd, "IPType");
46 	if (val)
47 		type = atoi(val);
48 	if (type != 1 && type != 2) {
49 		send_resp(dut, conn, SIGMA_ERROR,
50 			  "ErrorCode,Unsupported address type");
51 		return STATUS_SENT;
52 	}
53 
54 	dst = get_param(cmd, "destination");
55 	if (dst == NULL || (type == 1 && !is_ip_addr(dst)) ||
56 	    (type == 2 && !is_ipv6_addr(dst)))
57 		return INVALID_SEND_STATUS;
58 	if (dut->ndp_enable && type == 2) {
59 		snprintf(ip_dst, sizeof(ip_dst), "%s%%nan0", dst);
60 		dst = ip_dst;
61 	}
62 
63 	val = get_param(cmd, "frameSize");
64 	if (val == NULL)
65 		return INVALID_SEND_STATUS;
66 	size = atoi(val);
67 
68 	val = get_param(cmd, "frameRate");
69 	if (val == NULL)
70 		return INVALID_SEND_STATUS;
71 	rate = atof(val);
72 	if (rate <= 0)
73 		return INVALID_SEND_STATUS;
74 
75 	val = get_param(cmd, "duration");
76 	if (val == NULL)
77 		return INVALID_SEND_STATUS;
78 	dur = atoi(val);
79 	if (dur <= 0 || dur > 3600)
80 		dur = 3600;
81 
82 	pkts = dur * rate;
83 	interval = (float) 1 / rate;
84 	if (interval > 100000)
85 		return INVALID_SEND_STATUS;
86 
87 	val = get_param(cmd, "DSCP");
88 	if (val) {
89 		dscp = atoi(val);
90 		if (dscp < 0 || dscp > 63) {
91 			send_resp(dut, conn, SIGMA_ERROR,
92 				  "ErrorCode,Invalid DSCP value");
93 			return STATUS_SENT;
94 		}
95 		use_dscp = 1;
96 	}
97 
98 	id = dut->next_streamid++;
99 	snprintf(buf, sizeof(buf), "%s/sigma_dut-ping.%d",
100 		 dut->sigma_tmpdir, id);
101 	unlink(buf);
102 	snprintf(buf, sizeof(buf), "%s/sigma_dut-ping-pid.%d",
103 		 dut->sigma_tmpdir, id);
104 	unlink(buf);
105 
106 	sigma_dut_print(dut, DUT_MSG_DEBUG, "Send ping: pkts=%d interval=%f "
107 			"streamid=%d",
108 			pkts, interval, id);
109 
110 	f = fopen(concat_sigma_tmpdir(dut, "/sigma_dut-ping.sh", ping,
111 				      sizeof(ping)), "w");
112 	if (f == NULL)
113 		return ERROR_SEND_STATUS;
114 
115 	extra[0] = '\0';
116 	if (use_dscp) {
117 		snprintf(extra, sizeof(extra), " -Q 0x%02x",
118 			 dscp << 2);
119 	}
120 
121 	int_arg[0] = '\0';
122 	if (rate != 1)
123 		snprintf(int_arg, sizeof(int_arg), " -i %f", interval);
124 	if (!dut->ndp_enable && type == 2)
125 		snprintf(intf_arg, sizeof(intf_arg), " -I %s",
126 			 get_station_ifname(dut));
127 	else
128 		intf_arg[0] = '\0';
129 	fprintf(f, "#!" SHELL "\n"
130 		"ping%s -c %d%s -s %d%s -q%s %s > %s"
131 		"/sigma_dut-ping.%d &\n"
132 		"echo $! > %s/sigma_dut-ping-pid.%d\n",
133 		type == 2 ? "6" : "", pkts, int_arg, size, extra,
134 		intf_arg, dst, dut->sigma_tmpdir, id, dut->sigma_tmpdir, id);
135 
136 	fclose(f);
137 	if (chmod(concat_sigma_tmpdir(dut, "/sigma_dut-ping.sh", ping,
138 				      sizeof(ping)),
139 		  S_IRUSR | S_IWUSR | S_IXUSR) < 0)
140 		return ERROR_SEND_STATUS;
141 
142 	if (system(concat_sigma_tmpdir(dut, "/sigma_dut-ping.sh", ping,
143 				       sizeof(ping))) != 0) {
144 		sigma_dut_print(dut, DUT_MSG_ERROR, "Failed to start ping");
145 		return ERROR_SEND_STATUS;
146 	}
147 
148 	unlink(concat_sigma_tmpdir(dut, "/sigma_dut-ping.sh", ping,
149 				   sizeof(ping)));
150 
151 	snprintf(resp, sizeof(resp), "streamID,%d", id);
152 	send_resp(dut, conn, SIGMA_COMPLETE, resp);
153 	return STATUS_SENT;
154 }
155 
156 
cmd_traffic_stop_ping(struct sigma_dut * dut,struct sigma_conn * conn,struct sigma_cmd * cmd)157 static enum sigma_cmd_result cmd_traffic_stop_ping(struct sigma_dut *dut,
158 						   struct sigma_conn *conn,
159 						   struct sigma_cmd *cmd)
160 {
161 	const char *val;
162 	int id, pid;
163 	FILE *f;
164 	char buf[100];
165 	int res_found = 0, sent = 0, received = 0;
166 
167 	val = get_param(cmd, "streamID");
168 	if (val == NULL)
169 		return INVALID_SEND_STATUS;
170 	id = atoi(val);
171 
172 	snprintf(buf, sizeof(buf), "%s/sigma_dut-ping-pid.%d",
173 		 dut->sigma_tmpdir, id);
174 	f = fopen(buf, "r");
175 	if (f == NULL) {
176 		send_resp(dut, conn, SIGMA_ERROR,
177 			  "ErrorCode,Unknown streamID");
178 		return STATUS_SENT;
179 	}
180 	if (fscanf(f, "%d", &pid) != 1 || pid <= 0) {
181 		sigma_dut_print(dut, DUT_MSG_ERROR, "No PID for ping process");
182 		fclose(f);
183 		unlink(buf);
184 		return ERROR_SEND_STATUS;
185 	}
186 
187 	fclose(f);
188 	unlink(buf);
189 
190 	sigma_dut_print(dut, DUT_MSG_DEBUG, "Ping process pid %d", pid);
191 	if (kill(pid, SIGINT) < 0 && errno != ESRCH) {
192 		sigma_dut_print(dut, DUT_MSG_DEBUG, "kill failed: %s",
193 				strerror(errno));
194 	}
195 	usleep(250000);
196 
197 	snprintf(buf, sizeof(buf), "%s/sigma_dut-ping.%d",
198 		 dut->sigma_tmpdir, id);
199 	f = fopen(buf, "r");
200 	if (f == NULL) {
201 		sigma_dut_print(dut, DUT_MSG_DEBUG,
202 				"No ping result file found");
203 		send_resp(dut, conn, SIGMA_COMPLETE, "sent,0,replies,0");
204 		return STATUS_SENT;
205 	}
206 
207 	while (fgets(buf, sizeof(buf), f)) {
208 		char *pos;
209 
210 		pos = strstr(buf, " packets transmitted");
211 		if (pos) {
212 			pos--;
213 			while (pos > buf && isdigit(pos[-1]))
214 				pos--;
215 			sent = atoi(pos);
216 			res_found = 1;
217 		}
218 
219 		pos = strstr(buf, " packets received");
220 		if (pos == NULL)
221 			pos = strstr(buf, " received");
222 		if (pos) {
223 			pos--;
224 			while (pos > buf && isdigit(pos[-1]))
225 				pos--;
226 			received = atoi(pos);
227 			res_found = 1;
228 		}
229 	}
230 	fclose(f);
231 	snprintf(buf, sizeof(buf), "%s/sigma_dut-ping.%d",
232 		 dut->sigma_tmpdir, id);
233 	unlink(buf);
234 
235 	if (!res_found) {
236 		sigma_dut_print(dut, DUT_MSG_DEBUG,
237 				"No ping results found");
238 		send_resp(dut, conn, SIGMA_COMPLETE, "sent,0,replies,0");
239 		return STATUS_SENT;
240 	}
241 
242 	snprintf(buf, sizeof(buf), "sent,%d,replies,%d", sent, received);
243 	send_resp(dut, conn, SIGMA_COMPLETE, buf);
244 	return STATUS_SENT;
245 }
246 
247 
get_ip_addr(const char * ifname,int ipv6,char * buf,size_t len)248 static int get_ip_addr(const char *ifname, int ipv6, char *buf, size_t len)
249 {
250 	struct ifaddrs *ifa, *ifa_tmp;
251 
252 	if (getifaddrs(&ifa) == -1)
253 		return -1;
254 
255 	for (ifa_tmp = ifa; ifa_tmp; ifa_tmp = ifa_tmp->ifa_next) {
256 		if (!ifa_tmp->ifa_addr ||
257 		    strcasecmp(ifname, ifa_tmp->ifa_name) != 0)
258 			continue;
259 
260 		if (!ipv6 && ifa_tmp->ifa_addr->sa_family == AF_INET) {
261 			struct sockaddr_in *in;
262 
263 			in = (struct sockaddr_in *) ifa_tmp->ifa_addr;
264 			if (!inet_ntop(AF_INET, &in->sin_addr, buf, len))
265 				return -1;
266 			return 0;
267 		}
268 
269 		if (ipv6 && ifa_tmp->ifa_addr->sa_family == AF_INET6) {
270 			struct sockaddr_in6 *in6;
271 
272 			in6 = (struct sockaddr_in6 *) ifa_tmp->ifa_addr;
273 			if (!inet_ntop(AF_INET6, &in6->sin6_addr, buf, len))
274 				return -1;
275 			return 0;
276 		}
277 	}
278 
279 	return -1;
280 }
281 
282 
cmd_traffic_start_iperf(struct sigma_dut * dut,struct sigma_conn * conn,struct sigma_cmd * cmd)283 static enum sigma_cmd_result cmd_traffic_start_iperf(struct sigma_dut *dut,
284 						     struct sigma_conn *conn,
285 						     struct sigma_cmd *cmd)
286 {
287 	const char *val, *dst;
288 	const char *iptype;
289 	int port, duration;
290 	const char *proto;
291 	char buf[256];
292 	const char *ifname;
293 	char port_str[20], iperf[100];
294 	FILE *f;
295 	int server, ipv6 = 0;
296 	char *pos;
297 	int dscp, reverse = 0;
298 	char tos[20], client_port_str[100];
299 
300 	val = get_param(cmd, "mode");
301 	if (!val) {
302 		send_resp(dut, conn, SIGMA_ERROR,
303 			  "errorCode,Missing mode parameter");
304 		return STATUS_SENT;
305 	}
306 	server = strcasecmp(val, "server") == 0;
307 
308 	iptype = "";
309 	val = get_param(cmd, "iptype");
310 	if (val) {
311 		if (strcasecmp(val, "ipv6") == 0 ||
312 		    strcasecmp(val, "version6") == 0) {
313 			iptype = "-6";
314 			ipv6 = 1;
315 		} else {
316 			iptype = "-4";
317 			ipv6 = 0;
318 		}
319 	}
320 
321 	port_str[0] = '\0';
322 	val = get_param(cmd, "port");
323 	if (val) {
324 		port = atoi(val);
325 		snprintf(port_str, sizeof(port_str), "-p %d", port);
326 	}
327 
328 	proto = "";
329 	val = get_param(cmd, "transproto");
330 	if (val && strcasecmp(val, "udp") == 0)
331 		proto = "-u";
332 
333 	dst = get_param(cmd, "destination");
334 	pos = dst ? strchr(dst, '%') : NULL;
335 	if (pos) {
336 		*pos++ = '\0';
337 		ifname = pos;
338 	} else if (dut->ndpe) {
339 		ifname = "nan0";
340 	} else {
341 		ifname = get_station_ifname(dut);
342 	}
343 
344 	if (!server && (!dst || (!is_ip_addr(dst) && !is_ipv6_addr(dst)))) {
345 		send_resp(dut, conn, SIGMA_ERROR,
346 			  "errorCode,Invalid destination address");
347 		return STATUS_SENT;
348 	}
349 
350 	val = get_param(cmd, "duration");
351 	if (val)
352 		duration = atoi(val);
353 	else
354 		duration = 0;
355 
356 	client_port_str[0] = '\0';
357 	val = get_param(cmd, "clientport");
358 	if (val) {
359 		char ipaddr[100];
360 		int res;
361 
362 		port = atoi(val);
363 		if (get_ip_addr(ifname, ipv6, ipaddr, sizeof(ipaddr))) {
364 			send_resp(dut, conn, SIGMA_ERROR,
365 				"errorCode,Cannot get own IP address");
366 			return STATUS_SENT;
367 		}
368 
369 		if (ipv6)
370 			snprintf(buf, sizeof(buf), "%s%%%s", ipaddr, ifname);
371 		else
372 			snprintf(buf, sizeof(buf), "%s", ipaddr);
373 
374 		res = snprintf(client_port_str, sizeof(client_port_str),
375 			       " -B %s --cport %d", buf, port);
376 		if (res < 0 || res >= sizeof(client_port_str))
377 			return ERROR_SEND_STATUS;
378 	}
379 
380 	val = get_param(cmd, "reverse");
381 	if (val)
382 		reverse = atoi(val);
383 
384 	tos[0] = '\0';
385 	val = get_param(cmd, "DSCP");
386 	if (val) {
387 		dscp = atoi(val);
388 		if (dscp < 0 || dscp > 63) {
389 			send_resp(dut, conn, SIGMA_ERROR,
390 				  "ErrorCode,Invalid DSCP value");
391 			return STATUS_SENT_ERROR;
392 		}
393 		snprintf(tos, sizeof(tos), " -S 0x%02x", dscp << 2);
394 	}
395 
396 	unlink(concat_sigma_tmpdir(dut, "/sigma_dut-iperf", iperf,
397 				   sizeof(iperf)));
398 	unlink(concat_sigma_tmpdir(dut, "/sigma_dut-iperf-pid", iperf,
399 				   sizeof(iperf)));
400 
401 	f = fopen(concat_sigma_tmpdir(dut, "/sigma_dut-iperf.sh", iperf,
402 				      sizeof(iperf)), "w");
403 	if (!f) {
404 		send_resp(dut, conn, SIGMA_ERROR,
405 			  "errorCode,Can not write sigma_dut-iperf.sh");
406 		return STATUS_SENT;
407 	}
408 
409 	if (server) {
410 		/* write server side command to shell file */
411 		fprintf(f, "#!" SHELL "\n"
412 			"iperf3 -s %s %s > %s"
413 			"/sigma_dut-iperf &\n"
414 			"echo $! > %s/sigma_dut-iperf-pid\n",
415 			port_str, iptype, dut->sigma_tmpdir, dut->sigma_tmpdir);
416 	} else {
417 		/* write client side command to shell file */
418 		if (!dst)
419 			return INVALID_SEND_STATUS;
420 		if (ipv6)
421 			snprintf(buf, sizeof(buf), "%s%%%s", dst, ifname);
422 		else
423 			snprintf(buf, sizeof(buf), "%s", dst);
424 		fprintf(f, "#!" SHELL "\n"
425 			"iperf3 -c %s -t %d %s %s %s%s%s%s > %s"
426 			"/sigma_dut-iperf &\n"
427 			"echo $! > %s/sigma_dut-iperf-pid\n",
428 			buf, duration, iptype, proto, port_str,
429 			client_port_str, tos, reverse ? " -R" : "",
430 			dut->sigma_tmpdir, dut->sigma_tmpdir);
431 	}
432 
433 	fclose(f);
434 
435 	if (chmod(concat_sigma_tmpdir(dut, "/sigma_dut-iperf.sh", iperf,
436 				      sizeof(iperf)),
437 		  S_IRUSR | S_IWUSR | S_IXUSR) < 0) {
438 		send_resp(dut, conn, SIGMA_ERROR,
439 			  "errorCode,Can not chmod sigma_dut-iperf.sh");
440 		return STATUS_SENT;
441 	}
442 
443 	sigma_dut_print(dut, DUT_MSG_DEBUG, "Starting iperf");
444 	if (system(concat_sigma_tmpdir(dut, "/sigma_dut-iperf.sh", iperf,
445 				       sizeof(iperf))) != 0) {
446 		sigma_dut_print(dut, DUT_MSG_ERROR, "Failed to start iperf");
447 		send_resp(dut, conn, SIGMA_ERROR,
448 			  "errorCode,Failed to run sigma_dut-iperf.sh");
449 		return STATUS_SENT;
450 	}
451 
452 	unlink(concat_sigma_tmpdir(dut, "/sigma_dut-iperf.sh", iperf,
453 				   sizeof(iperf)));
454 	return SUCCESS_SEND_STATUS;
455 }
456 
457 
cmd_traffic_stop_iperf(struct sigma_dut * dut,struct sigma_conn * conn,struct sigma_cmd * cmd)458 static enum sigma_cmd_result cmd_traffic_stop_iperf(struct sigma_dut *dut,
459 						    struct sigma_conn *conn,
460 						    struct sigma_cmd *cmd)
461 {
462 	int pid;
463 	FILE *f;
464 	char buf[100], summary_buf[100], iperf[100];
465 	float bandwidth, totalbytes, factor;
466 	char *pos;
467 	long l_bandwidth, l_totalbytes;
468 
469 	f = fopen(concat_sigma_tmpdir(dut, "/sigma_dut-iperf-pid", iperf,
470 				      sizeof(iperf)), "r");
471 	if (!f) {
472 		send_resp(dut, conn, SIGMA_ERROR,
473 			  "errorCode,PID file does not exist");
474 		return STATUS_SENT;
475 	}
476 	if (fscanf(f, "%d", &pid) != 1 || pid <= 0) {
477 		sigma_dut_print(dut, DUT_MSG_ERROR, "No PID for iperf process");
478 		fclose(f);
479 		unlink(concat_sigma_tmpdir(dut, "/sigma_dut-iperf-pid", iperf,
480 					   sizeof(iperf)));
481 		return ERROR_SEND_STATUS;
482 	}
483 
484 	fclose(f);
485 	unlink(concat_sigma_tmpdir(dut, "/sigma_dut-iperf-pid", iperf,
486 				   sizeof(iperf)));
487 
488 	if (kill(pid, SIGINT) < 0 && errno != ESRCH) {
489 		sigma_dut_print(dut, DUT_MSG_DEBUG, "kill failed: %s",
490 				strerror(errno));
491 	}
492 	usleep(250000);
493 
494 	/* parse iperf output which is stored in sigma_dut-iperf */
495 	summary_buf[0] = '\0';
496 	f = fopen(concat_sigma_tmpdir(dut, "/sigma_dut-iperf", iperf,
497 				      sizeof(iperf)), "r");
498 	if (!f) {
499 		sigma_dut_print(dut, DUT_MSG_DEBUG,
500 				"No iperf result file found");
501 		send_resp(dut, conn, SIGMA_COMPLETE,
502 			  "bandwidth,0,totalbytes,0");
503 		return STATUS_SENT;
504 	}
505 
506 	/* find the last line which has the received bytes summary */
507 	while (fgets(buf, sizeof(buf), f)) {
508 		char *pos;
509 
510 		pos = strchr(buf, '\n');
511 		if (pos)
512 			*pos = '\0';
513 		sigma_dut_print(dut, DUT_MSG_DEBUG, "iperf: %s", buf);
514 		pos = strstr(buf, "  sec  ");
515 		if (pos)
516 			strlcpy(summary_buf, buf, sizeof(summary_buf));
517 	}
518 
519 	fclose(f);
520 	unlink(concat_sigma_tmpdir(dut, "/sigma_dut-iperf", iperf,
521 				   sizeof(iperf)));
522 
523 	pos = strstr(summary_buf, "Bytes");
524 	if (!pos || pos == summary_buf) {
525 		sigma_dut_print(dut, DUT_MSG_DEBUG,
526 				"Can not parse iperf results");
527 		send_resp(dut, conn, SIGMA_COMPLETE,
528 			  "bandwidth,0,totalbytes,0");
529 		return STATUS_SENT;
530 	}
531 
532 	if (pos[-1] == 'G')
533 		factor = 1024 * 1024 * 1024;
534 	else if (pos[-1] == 'M')
535 		factor = 1024 * 1024;
536 	else if (pos[-1] == 'K')
537 		factor = 1024;
538 	else
539 		factor = 1;
540 
541 	if (pos) {
542 		pos -= 2;
543 		while (pos > summary_buf && (pos[-1] != ' '))
544 			pos--;
545 		totalbytes = atof(pos);
546 	} else
547 		totalbytes = 0;
548 	l_totalbytes = totalbytes * factor;
549 
550 	pos = strstr(summary_buf, "bits/sec");
551 	if (!pos || pos == summary_buf) {
552 		sigma_dut_print(dut, DUT_MSG_DEBUG,
553 				"Can not parse iperf results");
554 		send_resp(dut, conn, SIGMA_COMPLETE,
555 			  "bandwidth,0,totalbytes,0");
556 		return STATUS_SENT;
557 	}
558 
559 	if (pos[-1] == 'G')
560 		factor = 1024 * 1024 * 1024 / 8;
561 	else if (pos[-1] == 'M')
562 		factor = 1024 * 1024 / 8;
563 	else if (pos[-1] == 'K')
564 		factor = 1024 / 8;
565 	else
566 		factor = 1 / 8;
567 
568 	if (pos && pos - summary_buf > 2) {
569 		pos -= 2;
570 		while (pos > summary_buf && (pos[-1] != ' '))
571 			pos--;
572 		bandwidth = atof(pos);
573 	} else
574 		bandwidth = 0;
575 	l_bandwidth = bandwidth * factor;
576 
577 	snprintf(buf, sizeof(buf), "bandwidth,%lu,totalbytes,%lu",
578 		 l_bandwidth, l_totalbytes);
579 	send_resp(dut, conn, SIGMA_COMPLETE, buf);
580 	return STATUS_SENT;
581 }
582 
583 
traffic_register_cmds(void)584 void traffic_register_cmds(void)
585 {
586 	sigma_dut_reg_cmd("traffic_send_ping", NULL, cmd_traffic_send_ping);
587 	sigma_dut_reg_cmd("traffic_stop_ping", NULL, cmd_traffic_stop_ping);
588 	sigma_dut_reg_cmd("traffic_start_iperf", NULL, cmd_traffic_start_iperf);
589 	sigma_dut_reg_cmd("traffic_stop_iperf", NULL, cmd_traffic_stop_iperf);
590 }
591