xref: /freebsd/contrib/wpa/hostapd/hostapd_cli.c (revision 39beb93c)
1 /*
2  * hostapd - command line interface for hostapd daemon
3  * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation.
8  *
9  * Alternatively, this software may be distributed under the terms of BSD
10  * license.
11  *
12  * See README and COPYING for more details.
13  */
14 
15 #include "includes.h"
16 #include <dirent.h>
17 
18 #include "wpa_ctrl.h"
19 #include "common.h"
20 #include "version.h"
21 
22 
23 static const char *hostapd_cli_version =
24 "hostapd_cli v" VERSION_STR "\n"
25 "Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> and contributors";
26 
27 
28 static const char *hostapd_cli_license =
29 "This program is free software. You can distribute it and/or modify it\n"
30 "under the terms of the GNU General Public License version 2.\n"
31 "\n"
32 "Alternatively, this software may be distributed under the terms of the\n"
33 "BSD license. See README and COPYING for more details.\n";
34 
35 static const char *hostapd_cli_full_license =
36 "This program is free software; you can redistribute it and/or modify\n"
37 "it under the terms of the GNU General Public License version 2 as\n"
38 "published by the Free Software Foundation.\n"
39 "\n"
40 "This program is distributed in the hope that it will be useful,\n"
41 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
42 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
43 "GNU General Public License for more details.\n"
44 "\n"
45 "You should have received a copy of the GNU General Public License\n"
46 "along with this program; if not, write to the Free Software\n"
47 "Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n"
48 "\n"
49 "Alternatively, this software may be distributed under the terms of the\n"
50 "BSD license.\n"
51 "\n"
52 "Redistribution and use in source and binary forms, with or without\n"
53 "modification, are permitted provided that the following conditions are\n"
54 "met:\n"
55 "\n"
56 "1. Redistributions of source code must retain the above copyright\n"
57 "   notice, this list of conditions and the following disclaimer.\n"
58 "\n"
59 "2. Redistributions in binary form must reproduce the above copyright\n"
60 "   notice, this list of conditions and the following disclaimer in the\n"
61 "   documentation and/or other materials provided with the distribution.\n"
62 "\n"
63 "3. Neither the name(s) of the above-listed copyright holder(s) nor the\n"
64 "   names of its contributors may be used to endorse or promote products\n"
65 "   derived from this software without specific prior written permission.\n"
66 "\n"
67 "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n"
68 "\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n"
69 "LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n"
70 "A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n"
71 "OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n"
72 "SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n"
73 "LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n"
74 "DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n"
75 "THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"
76 "(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n"
77 "OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
78 "\n";
79 
80 static const char *commands_help =
81 "Commands:\n"
82 "   mib                  get MIB variables (dot1x, dot11, radius)\n"
83 "   sta <addr>           get MIB variables for one station\n"
84 "   all_sta              get MIB variables for all stations\n"
85 "   new_sta <addr>       add a new station\n"
86 #ifdef CONFIG_IEEE80211W
87 "   sa_query <addr>      send SA Query to a station\n"
88 #endif /* CONFIG_IEEE80211W */
89 #ifdef CONFIG_WPS
90 "   wps_pin <uuid> <pin> add WPS Enrollee PIN (Device Password)\n"
91 "   wps_pbc              indicate button pushed to initiate PBC\n"
92 #endif /* CONFIG_WPS */
93 "   help                 show this usage help\n"
94 "   interface [ifname]   show interfaces/select interface\n"
95 "   level <debug level>  change debug level\n"
96 "   license              show full hostapd_cli license\n"
97 "   quit                 exit hostapd_cli\n";
98 
99 static struct wpa_ctrl *ctrl_conn;
100 static int hostapd_cli_quit = 0;
101 static int hostapd_cli_attached = 0;
102 static const char *ctrl_iface_dir = "/var/run/hostapd";
103 static char *ctrl_ifname = NULL;
104 static int ping_interval = 5;
105 
106 
107 static void usage(void)
108 {
109 	fprintf(stderr, "%s\n", hostapd_cli_version);
110 	fprintf(stderr,
111 		"\n"
112 		"usage: hostapd_cli [-p<path>] [-i<ifname>] [-hv] "
113 		"[-G<ping interval>] \\\n"
114 		"        [command..]\n"
115 		"\n"
116 		"Options:\n"
117 		"   -h           help (show this usage text)\n"
118 		"   -v           shown version information\n"
119 		"   -p<path>     path to find control sockets (default: "
120 		"/var/run/hostapd)\n"
121 		"   -i<ifname>   Interface to listen on (default: first "
122 		"interface found in the\n"
123 		"                socket path)\n\n"
124 		"%s",
125 		commands_help);
126 }
127 
128 
129 static struct wpa_ctrl * hostapd_cli_open_connection(const char *ifname)
130 {
131 	char *cfile;
132 	int flen;
133 
134 	if (ifname == NULL)
135 		return NULL;
136 
137 	flen = strlen(ctrl_iface_dir) + strlen(ifname) + 2;
138 	cfile = malloc(flen);
139 	if (cfile == NULL)
140 		return NULL;
141 	snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ifname);
142 
143 	ctrl_conn = wpa_ctrl_open(cfile);
144 	free(cfile);
145 	return ctrl_conn;
146 }
147 
148 
149 static void hostapd_cli_close_connection(void)
150 {
151 	if (ctrl_conn == NULL)
152 		return;
153 
154 	if (hostapd_cli_attached) {
155 		wpa_ctrl_detach(ctrl_conn);
156 		hostapd_cli_attached = 0;
157 	}
158 	wpa_ctrl_close(ctrl_conn);
159 	ctrl_conn = NULL;
160 }
161 
162 
163 static void hostapd_cli_msg_cb(char *msg, size_t len)
164 {
165 	printf("%s\n", msg);
166 }
167 
168 
169 static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd, int print)
170 {
171 	char buf[4096];
172 	size_t len;
173 	int ret;
174 
175 	if (ctrl_conn == NULL) {
176 		printf("Not connected to hostapd - command dropped.\n");
177 		return -1;
178 	}
179 	len = sizeof(buf) - 1;
180 	ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len,
181 			       hostapd_cli_msg_cb);
182 	if (ret == -2) {
183 		printf("'%s' command timed out.\n", cmd);
184 		return -2;
185 	} else if (ret < 0) {
186 		printf("'%s' command failed.\n", cmd);
187 		return -1;
188 	}
189 	if (print) {
190 		buf[len] = '\0';
191 		printf("%s", buf);
192 	}
193 	return 0;
194 }
195 
196 
197 static inline int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd)
198 {
199 	return _wpa_ctrl_command(ctrl, cmd, 1);
200 }
201 
202 
203 static int hostapd_cli_cmd_ping(struct wpa_ctrl *ctrl, int argc, char *argv[])
204 {
205 	return wpa_ctrl_command(ctrl, "PING");
206 }
207 
208 
209 static int hostapd_cli_cmd_mib(struct wpa_ctrl *ctrl, int argc, char *argv[])
210 {
211 	return wpa_ctrl_command(ctrl, "MIB");
212 }
213 
214 
215 static int hostapd_cli_cmd_sta(struct wpa_ctrl *ctrl, int argc, char *argv[])
216 {
217 	char buf[64];
218 	if (argc != 1) {
219 		printf("Invalid 'sta' command - exactly one argument, STA "
220 		       "address, is required.\n");
221 		return -1;
222 	}
223 	snprintf(buf, sizeof(buf), "STA %s", argv[0]);
224 	return wpa_ctrl_command(ctrl, buf);
225 }
226 
227 
228 static int hostapd_cli_cmd_new_sta(struct wpa_ctrl *ctrl, int argc,
229 				   char *argv[])
230 {
231 	char buf[64];
232 	if (argc != 1) {
233 		printf("Invalid 'new_sta' command - exactly one argument, STA "
234 		       "address, is required.\n");
235 		return -1;
236 	}
237 	snprintf(buf, sizeof(buf), "NEW_STA %s", argv[0]);
238 	return wpa_ctrl_command(ctrl, buf);
239 }
240 
241 
242 #ifdef CONFIG_IEEE80211W
243 static int hostapd_cli_cmd_sa_query(struct wpa_ctrl *ctrl, int argc,
244 				    char *argv[])
245 {
246 	char buf[64];
247 	if (argc != 1) {
248 		printf("Invalid 'sa_query' command - exactly one argument, "
249 		       "STA address, is required.\n");
250 		return -1;
251 	}
252 	snprintf(buf, sizeof(buf), "SA_QUERY %s", argv[0]);
253 	return wpa_ctrl_command(ctrl, buf);
254 }
255 #endif /* CONFIG_IEEE80211W */
256 
257 
258 #ifdef CONFIG_WPS
259 static int hostapd_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc,
260 				   char *argv[])
261 {
262 	char buf[64];
263 	if (argc != 2) {
264 		printf("Invalid 'wps_pin' command - exactly two arguments, "
265 		       "UUID and PIN, are required.\n");
266 		return -1;
267 	}
268 	snprintf(buf, sizeof(buf), "WPS_PIN %s %s", argv[0], argv[1]);
269 	return wpa_ctrl_command(ctrl, buf);
270 }
271 
272 
273 static int hostapd_cli_cmd_wps_pbc(struct wpa_ctrl *ctrl, int argc,
274 				   char *argv[])
275 {
276 	return wpa_ctrl_command(ctrl, "WPS_PBC");
277 }
278 #endif /* CONFIG_WPS */
279 
280 
281 static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd,
282 				char *addr, size_t addr_len)
283 {
284 	char buf[4096], *pos;
285 	size_t len;
286 	int ret;
287 
288 	if (ctrl_conn == NULL) {
289 		printf("Not connected to hostapd - command dropped.\n");
290 		return -1;
291 	}
292 	len = sizeof(buf) - 1;
293 	ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len,
294 			       hostapd_cli_msg_cb);
295 	if (ret == -2) {
296 		printf("'%s' command timed out.\n", cmd);
297 		return -2;
298 	} else if (ret < 0) {
299 		printf("'%s' command failed.\n", cmd);
300 		return -1;
301 	}
302 
303 	buf[len] = '\0';
304 	if (memcmp(buf, "FAIL", 4) == 0)
305 		return -1;
306 	printf("%s", buf);
307 
308 	pos = buf;
309 	while (*pos != '\0' && *pos != '\n')
310 		pos++;
311 	*pos = '\0';
312 	os_strlcpy(addr, buf, addr_len);
313 	return 0;
314 }
315 
316 
317 static int hostapd_cli_cmd_all_sta(struct wpa_ctrl *ctrl, int argc,
318 				   char *argv[])
319 {
320 	char addr[32], cmd[64];
321 
322 	if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr)))
323 		return 0;
324 	do {
325 		snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
326 	} while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr)) == 0);
327 
328 	return -1;
329 }
330 
331 
332 static int hostapd_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[])
333 {
334 	printf("%s", commands_help);
335 	return 0;
336 }
337 
338 
339 static int hostapd_cli_cmd_license(struct wpa_ctrl *ctrl, int argc,
340 				   char *argv[])
341 {
342 	printf("%s\n\n%s\n", hostapd_cli_version, hostapd_cli_full_license);
343 	return 0;
344 }
345 
346 
347 static int hostapd_cli_cmd_quit(struct wpa_ctrl *ctrl, int argc, char *argv[])
348 {
349 	hostapd_cli_quit = 1;
350 	return 0;
351 }
352 
353 
354 static int hostapd_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[])
355 {
356 	char cmd[256];
357 	if (argc != 1) {
358 		printf("Invalid LEVEL command: needs one argument (debug "
359 		       "level)\n");
360 		return 0;
361 	}
362 	snprintf(cmd, sizeof(cmd), "LEVEL %s", argv[0]);
363 	return wpa_ctrl_command(ctrl, cmd);
364 }
365 
366 
367 static void hostapd_cli_list_interfaces(struct wpa_ctrl *ctrl)
368 {
369 	struct dirent *dent;
370 	DIR *dir;
371 
372 	dir = opendir(ctrl_iface_dir);
373 	if (dir == NULL) {
374 		printf("Control interface directory '%s' could not be "
375 		       "openned.\n", ctrl_iface_dir);
376 		return;
377 	}
378 
379 	printf("Available interfaces:\n");
380 	while ((dent = readdir(dir))) {
381 		if (strcmp(dent->d_name, ".") == 0 ||
382 		    strcmp(dent->d_name, "..") == 0)
383 			continue;
384 		printf("%s\n", dent->d_name);
385 	}
386 	closedir(dir);
387 }
388 
389 
390 static int hostapd_cli_cmd_interface(struct wpa_ctrl *ctrl, int argc,
391 				     char *argv[])
392 {
393 	if (argc < 1) {
394 		hostapd_cli_list_interfaces(ctrl);
395 		return 0;
396 	}
397 
398 	hostapd_cli_close_connection();
399 	free(ctrl_ifname);
400 	ctrl_ifname = strdup(argv[0]);
401 
402 	if (hostapd_cli_open_connection(ctrl_ifname)) {
403 		printf("Connected to interface '%s.\n", ctrl_ifname);
404 		if (wpa_ctrl_attach(ctrl_conn) == 0) {
405 			hostapd_cli_attached = 1;
406 		} else {
407 			printf("Warning: Failed to attach to "
408 			       "hostapd.\n");
409 		}
410 	} else {
411 		printf("Could not connect to interface '%s' - re-trying\n",
412 			ctrl_ifname);
413 	}
414 	return 0;
415 }
416 
417 
418 struct hostapd_cli_cmd {
419 	const char *cmd;
420 	int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
421 };
422 
423 static struct hostapd_cli_cmd hostapd_cli_commands[] = {
424 	{ "ping", hostapd_cli_cmd_ping },
425 	{ "mib", hostapd_cli_cmd_mib },
426 	{ "sta", hostapd_cli_cmd_sta },
427 	{ "all_sta", hostapd_cli_cmd_all_sta },
428 	{ "new_sta", hostapd_cli_cmd_new_sta },
429 #ifdef CONFIG_IEEE80211W
430 	{ "sa_query", hostapd_cli_cmd_sa_query },
431 #endif /* CONFIG_IEEE80211W */
432 #ifdef CONFIG_WPS
433 	{ "wps_pin", hostapd_cli_cmd_wps_pin },
434 	{ "wps_pbc", hostapd_cli_cmd_wps_pbc },
435 #endif /* CONFIG_WPS */
436 	{ "help", hostapd_cli_cmd_help },
437 	{ "interface", hostapd_cli_cmd_interface },
438 	{ "level", hostapd_cli_cmd_level },
439 	{ "license", hostapd_cli_cmd_license },
440 	{ "quit", hostapd_cli_cmd_quit },
441 	{ NULL, NULL }
442 };
443 
444 
445 static void wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[])
446 {
447 	struct hostapd_cli_cmd *cmd, *match = NULL;
448 	int count;
449 
450 	count = 0;
451 	cmd = hostapd_cli_commands;
452 	while (cmd->cmd) {
453 		if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) == 0) {
454 			match = cmd;
455 			count++;
456 		}
457 		cmd++;
458 	}
459 
460 	if (count > 1) {
461 		printf("Ambiguous command '%s'; possible commands:", argv[0]);
462 		cmd = hostapd_cli_commands;
463 		while (cmd->cmd) {
464 			if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) ==
465 			    0) {
466 				printf(" %s", cmd->cmd);
467 			}
468 			cmd++;
469 		}
470 		printf("\n");
471 	} else if (count == 0) {
472 		printf("Unknown command '%s'\n", argv[0]);
473 	} else {
474 		match->handler(ctrl, argc - 1, &argv[1]);
475 	}
476 }
477 
478 
479 static void hostapd_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read)
480 {
481 	int first = 1;
482 	if (ctrl_conn == NULL)
483 		return;
484 	while (wpa_ctrl_pending(ctrl)) {
485 		char buf[256];
486 		size_t len = sizeof(buf) - 1;
487 		if (wpa_ctrl_recv(ctrl, buf, &len) == 0) {
488 			buf[len] = '\0';
489 			if (in_read && first)
490 				printf("\n");
491 			first = 0;
492 			printf("%s\n", buf);
493 		} else {
494 			printf("Could not read pending message.\n");
495 			break;
496 		}
497 	}
498 }
499 
500 
501 static void hostapd_cli_interactive(void)
502 {
503 	const int max_args = 10;
504 	char cmd[256], *res, *argv[max_args], *pos;
505 	int argc;
506 
507 	printf("\nInteractive mode\n\n");
508 
509 	do {
510 		hostapd_cli_recv_pending(ctrl_conn, 0);
511 		printf("> ");
512 		alarm(ping_interval);
513 		res = fgets(cmd, sizeof(cmd), stdin);
514 		alarm(0);
515 		if (res == NULL)
516 			break;
517 		pos = cmd;
518 		while (*pos != '\0') {
519 			if (*pos == '\n') {
520 				*pos = '\0';
521 				break;
522 			}
523 			pos++;
524 		}
525 		argc = 0;
526 		pos = cmd;
527 		for (;;) {
528 			while (*pos == ' ')
529 				pos++;
530 			if (*pos == '\0')
531 				break;
532 			argv[argc] = pos;
533 			argc++;
534 			if (argc == max_args)
535 				break;
536 			while (*pos != '\0' && *pos != ' ')
537 				pos++;
538 			if (*pos == ' ')
539 				*pos++ = '\0';
540 		}
541 		if (argc)
542 			wpa_request(ctrl_conn, argc, argv);
543 	} while (!hostapd_cli_quit);
544 }
545 
546 
547 static void hostapd_cli_terminate(int sig)
548 {
549 	hostapd_cli_close_connection();
550 	exit(0);
551 }
552 
553 
554 static void hostapd_cli_alarm(int sig)
555 {
556 	if (ctrl_conn && _wpa_ctrl_command(ctrl_conn, "PING", 0)) {
557 		printf("Connection to hostapd lost - trying to reconnect\n");
558 		hostapd_cli_close_connection();
559 	}
560 	if (!ctrl_conn) {
561 		ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
562 		if (ctrl_conn) {
563 			printf("Connection to hostapd re-established\n");
564 			if (wpa_ctrl_attach(ctrl_conn) == 0) {
565 				hostapd_cli_attached = 1;
566 			} else {
567 				printf("Warning: Failed to attach to "
568 				       "hostapd.\n");
569 			}
570 		}
571 	}
572 	if (ctrl_conn)
573 		hostapd_cli_recv_pending(ctrl_conn, 1);
574 	alarm(ping_interval);
575 }
576 
577 
578 int main(int argc, char *argv[])
579 {
580 	int interactive;
581 	int warning_displayed = 0;
582 	int c;
583 
584 	for (;;) {
585 		c = getopt(argc, argv, "hG:i:p:v");
586 		if (c < 0)
587 			break;
588 		switch (c) {
589 		case 'G':
590 			ping_interval = atoi(optarg);
591 			break;
592 		case 'h':
593 			usage();
594 			return 0;
595 		case 'v':
596 			printf("%s\n", hostapd_cli_version);
597 			return 0;
598 		case 'i':
599 			free(ctrl_ifname);
600 			ctrl_ifname = strdup(optarg);
601 			break;
602 		case 'p':
603 			ctrl_iface_dir = optarg;
604 			break;
605 		default:
606 			usage();
607 			return -1;
608 		}
609 	}
610 
611 	interactive = argc == optind;
612 
613 	if (interactive) {
614 		printf("%s\n\n%s\n\n", hostapd_cli_version,
615 		       hostapd_cli_license);
616 	}
617 
618 	for (;;) {
619 		if (ctrl_ifname == NULL) {
620 			struct dirent *dent;
621 			DIR *dir = opendir(ctrl_iface_dir);
622 			if (dir) {
623 				while ((dent = readdir(dir))) {
624 					if (strcmp(dent->d_name, ".") == 0 ||
625 					    strcmp(dent->d_name, "..") == 0)
626 						continue;
627 					printf("Selected interface '%s'\n",
628 					       dent->d_name);
629 					ctrl_ifname = strdup(dent->d_name);
630 					break;
631 				}
632 				closedir(dir);
633 			}
634 		}
635 		ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
636 		if (ctrl_conn) {
637 			if (warning_displayed)
638 				printf("Connection established.\n");
639 			break;
640 		}
641 
642 		if (!interactive) {
643 			perror("Failed to connect to hostapd - "
644 			       "wpa_ctrl_open");
645 			return -1;
646 		}
647 
648 		if (!warning_displayed) {
649 			printf("Could not connect to hostapd - re-trying\n");
650 			warning_displayed = 1;
651 		}
652 		sleep(1);
653 		continue;
654 	}
655 
656 	signal(SIGINT, hostapd_cli_terminate);
657 	signal(SIGTERM, hostapd_cli_terminate);
658 	signal(SIGALRM, hostapd_cli_alarm);
659 
660 	if (interactive) {
661 		if (wpa_ctrl_attach(ctrl_conn) == 0) {
662 			hostapd_cli_attached = 1;
663 		} else {
664 			printf("Warning: Failed to attach to hostapd.\n");
665 		}
666 		hostapd_cli_interactive();
667 	} else
668 		wpa_request(ctrl_conn, argc - optind, &argv[optind]);
669 
670 	free(ctrl_ifname);
671 	hostapd_cli_close_connection();
672 	return 0;
673 }
674