xref: /freebsd/contrib/wpa/hostapd/hostapd_cli.c (revision 3157ba21)
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> [timeout]  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 - at least two arguments, "
265 		       "UUID and PIN, are required.\n");
266 		return -1;
267 	}
268 	if (argc > 2)
269 		snprintf(buf, sizeof(buf), "WPS_PIN %s %s %s",
270 			 argv[0], argv[1], argv[2]);
271 	else
272 		snprintf(buf, sizeof(buf), "WPS_PIN %s %s", argv[0], argv[1]);
273 	return wpa_ctrl_command(ctrl, buf);
274 }
275 
276 
277 static int hostapd_cli_cmd_wps_pbc(struct wpa_ctrl *ctrl, int argc,
278 				   char *argv[])
279 {
280 	return wpa_ctrl_command(ctrl, "WPS_PBC");
281 }
282 #endif /* CONFIG_WPS */
283 
284 
285 static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd,
286 				char *addr, size_t addr_len)
287 {
288 	char buf[4096], *pos;
289 	size_t len;
290 	int ret;
291 
292 	if (ctrl_conn == NULL) {
293 		printf("Not connected to hostapd - command dropped.\n");
294 		return -1;
295 	}
296 	len = sizeof(buf) - 1;
297 	ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len,
298 			       hostapd_cli_msg_cb);
299 	if (ret == -2) {
300 		printf("'%s' command timed out.\n", cmd);
301 		return -2;
302 	} else if (ret < 0) {
303 		printf("'%s' command failed.\n", cmd);
304 		return -1;
305 	}
306 
307 	buf[len] = '\0';
308 	if (memcmp(buf, "FAIL", 4) == 0)
309 		return -1;
310 	printf("%s", buf);
311 
312 	pos = buf;
313 	while (*pos != '\0' && *pos != '\n')
314 		pos++;
315 	*pos = '\0';
316 	os_strlcpy(addr, buf, addr_len);
317 	return 0;
318 }
319 
320 
321 static int hostapd_cli_cmd_all_sta(struct wpa_ctrl *ctrl, int argc,
322 				   char *argv[])
323 {
324 	char addr[32], cmd[64];
325 
326 	if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr)))
327 		return 0;
328 	do {
329 		snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
330 	} while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr)) == 0);
331 
332 	return -1;
333 }
334 
335 
336 static int hostapd_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[])
337 {
338 	printf("%s", commands_help);
339 	return 0;
340 }
341 
342 
343 static int hostapd_cli_cmd_license(struct wpa_ctrl *ctrl, int argc,
344 				   char *argv[])
345 {
346 	printf("%s\n\n%s\n", hostapd_cli_version, hostapd_cli_full_license);
347 	return 0;
348 }
349 
350 
351 static int hostapd_cli_cmd_quit(struct wpa_ctrl *ctrl, int argc, char *argv[])
352 {
353 	hostapd_cli_quit = 1;
354 	return 0;
355 }
356 
357 
358 static int hostapd_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[])
359 {
360 	char cmd[256];
361 	if (argc != 1) {
362 		printf("Invalid LEVEL command: needs one argument (debug "
363 		       "level)\n");
364 		return 0;
365 	}
366 	snprintf(cmd, sizeof(cmd), "LEVEL %s", argv[0]);
367 	return wpa_ctrl_command(ctrl, cmd);
368 }
369 
370 
371 static void hostapd_cli_list_interfaces(struct wpa_ctrl *ctrl)
372 {
373 	struct dirent *dent;
374 	DIR *dir;
375 
376 	dir = opendir(ctrl_iface_dir);
377 	if (dir == NULL) {
378 		printf("Control interface directory '%s' could not be "
379 		       "openned.\n", ctrl_iface_dir);
380 		return;
381 	}
382 
383 	printf("Available interfaces:\n");
384 	while ((dent = readdir(dir))) {
385 		if (strcmp(dent->d_name, ".") == 0 ||
386 		    strcmp(dent->d_name, "..") == 0)
387 			continue;
388 		printf("%s\n", dent->d_name);
389 	}
390 	closedir(dir);
391 }
392 
393 
394 static int hostapd_cli_cmd_interface(struct wpa_ctrl *ctrl, int argc,
395 				     char *argv[])
396 {
397 	if (argc < 1) {
398 		hostapd_cli_list_interfaces(ctrl);
399 		return 0;
400 	}
401 
402 	hostapd_cli_close_connection();
403 	free(ctrl_ifname);
404 	ctrl_ifname = strdup(argv[0]);
405 
406 	if (hostapd_cli_open_connection(ctrl_ifname)) {
407 		printf("Connected to interface '%s.\n", ctrl_ifname);
408 		if (wpa_ctrl_attach(ctrl_conn) == 0) {
409 			hostapd_cli_attached = 1;
410 		} else {
411 			printf("Warning: Failed to attach to "
412 			       "hostapd.\n");
413 		}
414 	} else {
415 		printf("Could not connect to interface '%s' - re-trying\n",
416 			ctrl_ifname);
417 	}
418 	return 0;
419 }
420 
421 
422 struct hostapd_cli_cmd {
423 	const char *cmd;
424 	int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
425 };
426 
427 static struct hostapd_cli_cmd hostapd_cli_commands[] = {
428 	{ "ping", hostapd_cli_cmd_ping },
429 	{ "mib", hostapd_cli_cmd_mib },
430 	{ "sta", hostapd_cli_cmd_sta },
431 	{ "all_sta", hostapd_cli_cmd_all_sta },
432 	{ "new_sta", hostapd_cli_cmd_new_sta },
433 #ifdef CONFIG_IEEE80211W
434 	{ "sa_query", hostapd_cli_cmd_sa_query },
435 #endif /* CONFIG_IEEE80211W */
436 #ifdef CONFIG_WPS
437 	{ "wps_pin", hostapd_cli_cmd_wps_pin },
438 	{ "wps_pbc", hostapd_cli_cmd_wps_pbc },
439 #endif /* CONFIG_WPS */
440 	{ "help", hostapd_cli_cmd_help },
441 	{ "interface", hostapd_cli_cmd_interface },
442 	{ "level", hostapd_cli_cmd_level },
443 	{ "license", hostapd_cli_cmd_license },
444 	{ "quit", hostapd_cli_cmd_quit },
445 	{ NULL, NULL }
446 };
447 
448 
449 static void wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[])
450 {
451 	struct hostapd_cli_cmd *cmd, *match = NULL;
452 	int count;
453 
454 	count = 0;
455 	cmd = hostapd_cli_commands;
456 	while (cmd->cmd) {
457 		if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) == 0) {
458 			match = cmd;
459 			count++;
460 		}
461 		cmd++;
462 	}
463 
464 	if (count > 1) {
465 		printf("Ambiguous command '%s'; possible commands:", argv[0]);
466 		cmd = hostapd_cli_commands;
467 		while (cmd->cmd) {
468 			if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) ==
469 			    0) {
470 				printf(" %s", cmd->cmd);
471 			}
472 			cmd++;
473 		}
474 		printf("\n");
475 	} else if (count == 0) {
476 		printf("Unknown command '%s'\n", argv[0]);
477 	} else {
478 		match->handler(ctrl, argc - 1, &argv[1]);
479 	}
480 }
481 
482 
483 static void hostapd_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read)
484 {
485 	int first = 1;
486 	if (ctrl_conn == NULL)
487 		return;
488 	while (wpa_ctrl_pending(ctrl)) {
489 		char buf[256];
490 		size_t len = sizeof(buf) - 1;
491 		if (wpa_ctrl_recv(ctrl, buf, &len) == 0) {
492 			buf[len] = '\0';
493 			if (in_read && first)
494 				printf("\n");
495 			first = 0;
496 			printf("%s\n", buf);
497 		} else {
498 			printf("Could not read pending message.\n");
499 			break;
500 		}
501 	}
502 }
503 
504 
505 static void hostapd_cli_interactive(void)
506 {
507 	const int max_args = 10;
508 	char cmd[256], *res, *argv[max_args], *pos;
509 	int argc;
510 
511 	printf("\nInteractive mode\n\n");
512 
513 	do {
514 		hostapd_cli_recv_pending(ctrl_conn, 0);
515 		printf("> ");
516 		alarm(ping_interval);
517 		res = fgets(cmd, sizeof(cmd), stdin);
518 		alarm(0);
519 		if (res == NULL)
520 			break;
521 		pos = cmd;
522 		while (*pos != '\0') {
523 			if (*pos == '\n') {
524 				*pos = '\0';
525 				break;
526 			}
527 			pos++;
528 		}
529 		argc = 0;
530 		pos = cmd;
531 		for (;;) {
532 			while (*pos == ' ')
533 				pos++;
534 			if (*pos == '\0')
535 				break;
536 			argv[argc] = pos;
537 			argc++;
538 			if (argc == max_args)
539 				break;
540 			while (*pos != '\0' && *pos != ' ')
541 				pos++;
542 			if (*pos == ' ')
543 				*pos++ = '\0';
544 		}
545 		if (argc)
546 			wpa_request(ctrl_conn, argc, argv);
547 	} while (!hostapd_cli_quit);
548 }
549 
550 
551 static void hostapd_cli_terminate(int sig)
552 {
553 	hostapd_cli_close_connection();
554 	exit(0);
555 }
556 
557 
558 static void hostapd_cli_alarm(int sig)
559 {
560 	if (ctrl_conn && _wpa_ctrl_command(ctrl_conn, "PING", 0)) {
561 		printf("Connection to hostapd lost - trying to reconnect\n");
562 		hostapd_cli_close_connection();
563 	}
564 	if (!ctrl_conn) {
565 		ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
566 		if (ctrl_conn) {
567 			printf("Connection to hostapd re-established\n");
568 			if (wpa_ctrl_attach(ctrl_conn) == 0) {
569 				hostapd_cli_attached = 1;
570 			} else {
571 				printf("Warning: Failed to attach to "
572 				       "hostapd.\n");
573 			}
574 		}
575 	}
576 	if (ctrl_conn)
577 		hostapd_cli_recv_pending(ctrl_conn, 1);
578 	alarm(ping_interval);
579 }
580 
581 
582 int main(int argc, char *argv[])
583 {
584 	int interactive;
585 	int warning_displayed = 0;
586 	int c;
587 
588 	for (;;) {
589 		c = getopt(argc, argv, "hG:i:p:v");
590 		if (c < 0)
591 			break;
592 		switch (c) {
593 		case 'G':
594 			ping_interval = atoi(optarg);
595 			break;
596 		case 'h':
597 			usage();
598 			return 0;
599 		case 'v':
600 			printf("%s\n", hostapd_cli_version);
601 			return 0;
602 		case 'i':
603 			free(ctrl_ifname);
604 			ctrl_ifname = strdup(optarg);
605 			break;
606 		case 'p':
607 			ctrl_iface_dir = optarg;
608 			break;
609 		default:
610 			usage();
611 			return -1;
612 		}
613 	}
614 
615 	interactive = argc == optind;
616 
617 	if (interactive) {
618 		printf("%s\n\n%s\n\n", hostapd_cli_version,
619 		       hostapd_cli_license);
620 	}
621 
622 	for (;;) {
623 		if (ctrl_ifname == NULL) {
624 			struct dirent *dent;
625 			DIR *dir = opendir(ctrl_iface_dir);
626 			if (dir) {
627 				while ((dent = readdir(dir))) {
628 					if (strcmp(dent->d_name, ".") == 0 ||
629 					    strcmp(dent->d_name, "..") == 0)
630 						continue;
631 					printf("Selected interface '%s'\n",
632 					       dent->d_name);
633 					ctrl_ifname = strdup(dent->d_name);
634 					break;
635 				}
636 				closedir(dir);
637 			}
638 		}
639 		ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
640 		if (ctrl_conn) {
641 			if (warning_displayed)
642 				printf("Connection established.\n");
643 			break;
644 		}
645 
646 		if (!interactive) {
647 			perror("Failed to connect to hostapd - "
648 			       "wpa_ctrl_open");
649 			return -1;
650 		}
651 
652 		if (!warning_displayed) {
653 			printf("Could not connect to hostapd - re-trying\n");
654 			warning_displayed = 1;
655 		}
656 		sleep(1);
657 		continue;
658 	}
659 
660 	signal(SIGINT, hostapd_cli_terminate);
661 	signal(SIGTERM, hostapd_cli_terminate);
662 	signal(SIGALRM, hostapd_cli_alarm);
663 
664 	if (interactive) {
665 		if (wpa_ctrl_attach(ctrl_conn) == 0) {
666 			hostapd_cli_attached = 1;
667 		} else {
668 			printf("Warning: Failed to attach to hostapd.\n");
669 		}
670 		hostapd_cli_interactive();
671 	} else
672 		wpa_request(ctrl_conn, argc - optind, &argv[optind]);
673 
674 	free(ctrl_ifname);
675 	hostapd_cli_close_connection();
676 	return 0;
677 }
678