]> git.ipfire.org Git - thirdparty/hostap.git/blob - wlantest/wlantest_cli.c
wlantest: Add preliminary infrastructure for injecting frames
[thirdparty/hostap.git] / wlantest / wlantest_cli.c
1 /*
2 * wlantest controller
3 * Copyright (c) 2010, 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 "utils/includes.h"
16 #include <sys/un.h>
17
18 #include "utils/common.h"
19 #include "wlantest_ctrl.h"
20
21
22 static u8 * attr_get(u8 *buf, size_t buflen, enum wlantest_ctrl_attr attr,
23 size_t *len)
24 {
25 u8 *pos = buf;
26
27 while (pos + 8 <= buf + buflen) {
28 enum wlantest_ctrl_attr a;
29 size_t alen;
30 a = WPA_GET_BE32(pos);
31 pos += 4;
32 alen = WPA_GET_BE32(pos);
33 pos += 4;
34 if (pos + alen > buf + buflen) {
35 printf("Invalid control message attribute\n");
36 return NULL;
37 }
38 if (a == attr) {
39 *len = alen;
40 return pos;
41 }
42 pos += alen;
43 }
44
45 return NULL;
46 }
47
48
49 static u8 * attr_hdr_add(u8 *pos, u8 *end, enum wlantest_ctrl_attr attr,
50 size_t len)
51 {
52 if (pos == NULL || end - pos < 8 + len)
53 return NULL;
54 WPA_PUT_BE32(pos, attr);
55 pos += 4;
56 WPA_PUT_BE32(pos, len);
57 pos += 4;
58 return pos;
59 }
60
61
62 static u8 * attr_add_be32(u8 *pos, u8 *end, enum wlantest_ctrl_attr attr,
63 u32 val)
64 {
65 if (pos == NULL || end - pos < 12)
66 return NULL;
67 WPA_PUT_BE32(pos, attr);
68 pos += 4;
69 WPA_PUT_BE32(pos, 4);
70 pos += 4;
71 WPA_PUT_BE32(pos, val);
72 pos += 4;
73 return pos;
74 }
75
76
77 static int cmd_send_and_recv(int s, const u8 *cmd, size_t cmd_len,
78 u8 *resp, size_t max_resp_len)
79 {
80 int res;
81 enum wlantest_ctrl_cmd cmd_resp;
82
83 if (send(s, cmd, cmd_len, 0) < 0)
84 return -1;
85 res = recv(s, resp, max_resp_len, 0);
86 if (res < 4)
87 return -1;
88
89 cmd_resp = WPA_GET_BE32(resp);
90 if (cmd_resp == WLANTEST_CTRL_SUCCESS)
91 return res;
92
93 if (cmd_resp == WLANTEST_CTRL_UNKNOWN_CMD)
94 printf("Unknown command\n");
95 else if (cmd_resp == WLANTEST_CTRL_INVALID_CMD)
96 printf("Invalid command\n");
97
98 return -1;
99 }
100
101
102 static int cmd_simple(int s, enum wlantest_ctrl_cmd cmd)
103 {
104 u8 buf[4];
105 int res;
106 WPA_PUT_BE32(buf, cmd);
107 res = cmd_send_and_recv(s, buf, sizeof(buf), buf, sizeof(buf));
108 return res < 0 ? -1 : 0;
109 }
110
111
112 static int cmd_ping(int s, int argc, char *argv[])
113 {
114 int res = cmd_simple(s, WLANTEST_CTRL_PING);
115 if (res == 0)
116 printf("PONG\n");
117 return res == 0;
118 }
119
120
121 static int cmd_terminate(int s, int argc, char *argv[])
122 {
123 return cmd_simple(s, WLANTEST_CTRL_TERMINATE);
124 }
125
126
127 static int cmd_list_bss(int s, int argc, char *argv[])
128 {
129 u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
130 u8 buf[4];
131 u8 *bssid;
132 size_t len;
133 int rlen, i;
134
135 WPA_PUT_BE32(buf, WLANTEST_CTRL_LIST_BSS);
136 rlen = cmd_send_and_recv(s, buf, sizeof(buf), resp, sizeof(resp));
137 if (rlen < 0)
138 return -1;
139
140 bssid = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_BSSID, &len);
141 if (bssid == NULL)
142 return -1;
143
144 for (i = 0; i < len / ETH_ALEN; i++)
145 printf(MACSTR " ", MAC2STR(bssid + ETH_ALEN * i));
146 printf("\n");
147
148 return 0;
149 }
150
151
152 static int cmd_list_sta(int s, int argc, char *argv[])
153 {
154 u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
155 u8 buf[100], *pos;
156 u8 *addr;
157 size_t len;
158 int rlen, i;
159
160 if (argc < 1) {
161 printf("list_sta needs one argument: BSSID\n");
162 return -1;
163 }
164
165 pos = buf;
166 WPA_PUT_BE32(pos, WLANTEST_CTRL_LIST_STA);
167 pos += 4;
168 WPA_PUT_BE32(pos, WLANTEST_ATTR_BSSID);
169 pos += 4;
170 WPA_PUT_BE32(pos, ETH_ALEN);
171 pos += 4;
172 if (hwaddr_aton(argv[0], pos) < 0) {
173 printf("Invalid BSSID '%s'\n", argv[0]);
174 return -1;
175 }
176 pos += ETH_ALEN;
177
178 rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
179 if (rlen < 0)
180 return -1;
181
182 addr = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_STA_ADDR, &len);
183 if (addr == NULL)
184 return -1;
185
186 for (i = 0; i < len / ETH_ALEN; i++)
187 printf(MACSTR " ", MAC2STR(addr + ETH_ALEN * i));
188 printf("\n");
189
190 return 0;
191 }
192
193
194 static int cmd_flush(int s, int argc, char *argv[])
195 {
196 return cmd_simple(s, WLANTEST_CTRL_FLUSH);
197 }
198
199
200 static int cmd_clear_sta_counters(int s, int argc, char *argv[])
201 {
202 u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
203 u8 buf[100], *pos;
204 int rlen;
205
206 if (argc < 2) {
207 printf("clear_sta_counters needs two arguments: BSSID and "
208 "STA address\n");
209 return -1;
210 }
211
212 pos = buf;
213 WPA_PUT_BE32(pos, WLANTEST_CTRL_CLEAR_STA_COUNTERS);
214 pos += 4;
215 WPA_PUT_BE32(pos, WLANTEST_ATTR_BSSID);
216 pos += 4;
217 WPA_PUT_BE32(pos, ETH_ALEN);
218 pos += 4;
219 if (hwaddr_aton(argv[0], pos) < 0) {
220 printf("Invalid BSSID '%s'\n", argv[0]);
221 return -1;
222 }
223 pos += ETH_ALEN;
224
225 WPA_PUT_BE32(pos, WLANTEST_ATTR_STA_ADDR);
226 pos += 4;
227 WPA_PUT_BE32(pos, ETH_ALEN);
228 pos += 4;
229 if (hwaddr_aton(argv[1], pos) < 0) {
230 printf("Invalid STA address '%s'\n", argv[1]);
231 return -1;
232 }
233 pos += ETH_ALEN;
234
235 rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
236 if (rlen < 0)
237 return -1;
238 printf("OK\n");
239 return 0;
240 }
241
242
243 static int cmd_clear_bss_counters(int s, int argc, char *argv[])
244 {
245 u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
246 u8 buf[100], *pos;
247 int rlen;
248
249 if (argc < 1) {
250 printf("clear_bss_counters needs one argument: BSSID\n");
251 return -1;
252 }
253
254 pos = buf;
255 WPA_PUT_BE32(pos, WLANTEST_CTRL_CLEAR_BSS_COUNTERS);
256 pos += 4;
257 WPA_PUT_BE32(pos, WLANTEST_ATTR_BSSID);
258 pos += 4;
259 WPA_PUT_BE32(pos, ETH_ALEN);
260 pos += 4;
261 if (hwaddr_aton(argv[0], pos) < 0) {
262 printf("Invalid BSSID '%s'\n", argv[0]);
263 return -1;
264 }
265 pos += ETH_ALEN;
266
267 rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
268 if (rlen < 0)
269 return -1;
270 printf("OK\n");
271 return 0;
272 }
273
274
275 struct sta_counters {
276 const char *name;
277 enum wlantest_sta_counter num;
278 };
279
280 static const struct sta_counters sta_counters[] = {
281 { "auth_tx", WLANTEST_STA_COUNTER_AUTH_TX },
282 { "auth_rx", WLANTEST_STA_COUNTER_AUTH_RX },
283 { "assocreq_tx", WLANTEST_STA_COUNTER_ASSOCREQ_TX },
284 { "reassocreq_tx", WLANTEST_STA_COUNTER_REASSOCREQ_TX },
285 { "ptk_learned", WLANTEST_STA_COUNTER_PTK_LEARNED },
286 { "valid_deauth_tx", WLANTEST_STA_COUNTER_VALID_DEAUTH_TX },
287 { "valid_deauth_rx", WLANTEST_STA_COUNTER_VALID_DEAUTH_RX },
288 { "invalid_deauth_tx", WLANTEST_STA_COUNTER_INVALID_DEAUTH_TX },
289 { "invalid_deauth_rx", WLANTEST_STA_COUNTER_INVALID_DEAUTH_RX },
290 { "valid_disassoc_tx", WLANTEST_STA_COUNTER_VALID_DISASSOC_TX },
291 { "valid_disassoc_rx", WLANTEST_STA_COUNTER_VALID_DISASSOC_RX },
292 { "invalid_disassoc_tx", WLANTEST_STA_COUNTER_INVALID_DISASSOC_TX },
293 { "invalid_disassoc_rx", WLANTEST_STA_COUNTER_INVALID_DISASSOC_RX },
294 { "valid_saqueryreq_tx", WLANTEST_STA_COUNTER_VALID_SAQUERYREQ_TX },
295 { "valid_saqueryreq_rx", WLANTEST_STA_COUNTER_VALID_SAQUERYREQ_RX },
296 { "invalid_saqueryreq_tx",
297 WLANTEST_STA_COUNTER_INVALID_SAQUERYREQ_TX },
298 { "invalid_saqueryreq_rx",
299 WLANTEST_STA_COUNTER_INVALID_SAQUERYREQ_RX },
300 { "valid_saqueryresp_tx", WLANTEST_STA_COUNTER_VALID_SAQUERYRESP_TX },
301 { "valid_saqueryresp_rx", WLANTEST_STA_COUNTER_VALID_SAQUERYRESP_RX },
302 { "invalid_saqueryresp_tx",
303 WLANTEST_STA_COUNTER_INVALID_SAQUERYRESP_TX },
304 { "invalid_saqueryresp_rx",
305 WLANTEST_STA_COUNTER_INVALID_SAQUERYRESP_RX },
306 { NULL, 0 }
307 };
308
309 static int cmd_get_sta_counter(int s, int argc, char *argv[])
310 {
311 u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
312 u8 buf[100], *end, *pos;
313 int rlen, i;
314 size_t len;
315
316 if (argc != 3) {
317 printf("get_sta_counter needs at three arguments: "
318 "counter name, BSSID, and STA address\n");
319 return -1;
320 }
321
322 pos = buf;
323 end = buf + sizeof(buf);
324 WPA_PUT_BE32(pos, WLANTEST_CTRL_GET_STA_COUNTER);
325 pos += 4;
326
327 for (i = 0; sta_counters[i].name; i++) {
328 if (os_strcasecmp(sta_counters[i].name, argv[0]) == 0)
329 break;
330 }
331 if (sta_counters[i].name == NULL) {
332 printf("Unknown STA counter '%s'\n", argv[0]);
333 printf("Counters:");
334 for (i = 0; sta_counters[i].name; i++)
335 printf(" %s", sta_counters[i].name);
336 printf("\n");
337 return -1;
338 }
339
340 pos = attr_add_be32(pos, end, WLANTEST_ATTR_STA_COUNTER,
341 sta_counters[i].num);
342 pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN);
343 if (hwaddr_aton(argv[1], pos) < 0) {
344 printf("Invalid BSSID '%s'\n", argv[1]);
345 return -1;
346 }
347 pos += ETH_ALEN;
348
349 pos = attr_hdr_add(pos, end, WLANTEST_ATTR_STA_ADDR, ETH_ALEN);
350 if (hwaddr_aton(argv[2], pos) < 0) {
351 printf("Invalid STA address '%s'\n", argv[2]);
352 return -1;
353 }
354 pos += ETH_ALEN;
355
356 rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
357 if (rlen < 0)
358 return -1;
359
360 pos = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_COUNTER, &len);
361 if (pos == NULL || len != 4)
362 return -1;
363 printf("%u\n", WPA_GET_BE32(pos));
364 return 0;
365 }
366
367
368 struct bss_counters {
369 const char *name;
370 enum wlantest_bss_counter num;
371 };
372
373 static const struct bss_counters bss_counters[] = {
374 { "valid_bip_mmie", WLANTEST_BSS_COUNTER_VALID_BIP_MMIE },
375 { "invalid_bip_mmie", WLANTEST_BSS_COUNTER_INVALID_BIP_MMIE },
376 { "missing_bip_mmie", WLANTEST_BSS_COUNTER_MISSING_BIP_MMIE },
377 { NULL, 0 }
378 };
379
380 static int cmd_get_bss_counter(int s, int argc, char *argv[])
381 {
382 u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
383 u8 buf[100], *end, *pos;
384 int rlen, i;
385 size_t len;
386
387 if (argc != 2) {
388 printf("get_bss_counter needs at three arguments: "
389 "counter name and BSSID\n");
390 return -1;
391 }
392
393 pos = buf;
394 end = buf + sizeof(buf);
395 WPA_PUT_BE32(pos, WLANTEST_CTRL_GET_BSS_COUNTER);
396 pos += 4;
397
398 for (i = 0; bss_counters[i].name; i++) {
399 if (os_strcasecmp(bss_counters[i].name, argv[0]) == 0)
400 break;
401 }
402 if (bss_counters[i].name == NULL) {
403 printf("Unknown BSS counter '%s'\n", argv[0]);
404 printf("Counters:");
405 for (i = 0; bss_counters[i].name; i++)
406 printf(" %s", bss_counters[i].name);
407 printf("\n");
408 return -1;
409 }
410
411 pos = attr_add_be32(pos, end, WLANTEST_ATTR_BSS_COUNTER,
412 bss_counters[i].num);
413 pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN);
414 if (hwaddr_aton(argv[1], pos) < 0) {
415 printf("Invalid BSSID '%s'\n", argv[1]);
416 return -1;
417 }
418 pos += ETH_ALEN;
419
420 rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
421 if (rlen < 0)
422 return -1;
423
424 pos = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_COUNTER, &len);
425 if (pos == NULL || len != 4)
426 return -1;
427 printf("%u\n", WPA_GET_BE32(pos));
428 return 0;
429 }
430
431
432 struct inject_frames {
433 const char *name;
434 enum wlantest_inject_frame frame;
435 };
436
437 static const struct inject_frames inject_frames[] = {
438 { "auth", WLANTEST_FRAME_AUTH },
439 { "assocreq", WLANTEST_FRAME_ASSOCREQ },
440 { "reassocreq", WLANTEST_FRAME_REASSOCREQ },
441 { "deauth", WLANTEST_FRAME_DEAUTH },
442 { "disassoc", WLANTEST_FRAME_DISASSOC },
443 { "saqueryreq", WLANTEST_FRAME_SAQUERYREQ },
444 { NULL, 0 }
445 };
446
447 static int cmd_inject(int s, int argc, char *argv[])
448 {
449 u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
450 u8 buf[100], *end, *pos;
451 int rlen, i;
452 enum wlantest_inject_protection prot;
453
454 /* <frame> <prot> <sender> <BSSID> <STA/ff:ff:ff:ff:ff:ff> */
455
456 if (argc < 5) {
457 printf("inject needs five arguments: frame, protection, "
458 "sender, BSSID, STA/ff:ff:ff:ff:ff:ff\n");
459 return -1;
460 }
461
462 pos = buf;
463 end = buf + sizeof(buf);
464 WPA_PUT_BE32(pos, WLANTEST_CTRL_INJECT);
465 pos += 4;
466
467 for (i = 0; inject_frames[i].name; i++) {
468 if (os_strcasecmp(inject_frames[i].name, argv[0]) == 0)
469 break;
470 }
471 if (inject_frames[i].name == NULL) {
472 printf("Unknown inject frame '%s'\n", argv[0]);
473 printf("Frames:");
474 for (i = 0; inject_frames[i].name; i++)
475 printf(" %s", inject_frames[i].name);
476 printf("\n");
477 return -1;
478 }
479
480 pos = attr_add_be32(pos, end, WLANTEST_ATTR_INJECT_FRAME,
481 inject_frames[i].frame);
482
483 if (os_strcasecmp(argv[1], "normal") == 0)
484 prot = WLANTEST_INJECT_NORMAL;
485 else if (os_strcasecmp(argv[1], "protected") == 0)
486 prot = WLANTEST_INJECT_PROTECTED;
487 else if (os_strcasecmp(argv[1], "unprotected") == 0)
488 prot = WLANTEST_INJECT_UNPROTECTED;
489 else if (os_strcasecmp(argv[1], "incorrect") == 0)
490 prot = WLANTEST_INJECT_INCORRECT_KEY;
491 else {
492 printf("Unknown protection type '%s'\n", argv[1]);
493 printf("Protection types: normal protected unprotected "
494 "incorrect\n");
495 return -1;
496 }
497 pos = attr_add_be32(pos, end, WLANTEST_ATTR_INJECT_PROTECTION, prot);
498
499 if (os_strcasecmp(argv[2], "ap") == 0) {
500 pos = attr_add_be32(pos, end, WLANTEST_ATTR_INJECT_SENDER_AP,
501 1);
502 } else if (os_strcasecmp(argv[2], "sta") == 0) {
503 pos = attr_add_be32(pos, end, WLANTEST_ATTR_INJECT_SENDER_AP,
504 0);
505 } else {
506 printf("Unknown sender '%s'\n", argv[2]);
507 printf("Sender types: ap sta\n");
508 return -1;
509 }
510
511 pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN);
512 if (hwaddr_aton(argv[3], pos) < 0) {
513 printf("Invalid BSSID '%s'\n", argv[3]);
514 return -1;
515 }
516 pos += ETH_ALEN;
517
518 pos = attr_hdr_add(pos, end, WLANTEST_ATTR_STA_ADDR, ETH_ALEN);
519 if (hwaddr_aton(argv[4], pos) < 0) {
520 printf("Invalid STA '%s'\n", argv[4]);
521 return -1;
522 }
523 pos += ETH_ALEN;
524
525 rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
526 if (rlen < 0)
527 return -1;
528 printf("OK\n");
529 return 0;
530 }
531
532
533 struct wlantest_cli_cmd {
534 const char *cmd;
535 int (*handler)(int s, int argc, char *argv[]);
536 const char *usage;
537 };
538
539 static const struct wlantest_cli_cmd wlantest_cli_commands[] = {
540 { "ping", cmd_ping, "= test connection to wlantest" },
541 { "terminate", cmd_terminate, "= terminate wlantest" },
542 { "list_bss", cmd_list_bss, "= get BSS list" },
543 { "list_sta", cmd_list_sta, "<BSSID> = get STA list" },
544 { "flush", cmd_flush, "= drop all collected BSS data" },
545 { "clear_sta_counters", cmd_clear_sta_counters,
546 "<BSSID> <STA> = clear STA counters" },
547 { "clear_bss_counters", cmd_clear_bss_counters,
548 "<BSSID> = clear BSS counters" },
549 { "get_sta_counter", cmd_get_sta_counter,
550 "<counter> <BSSID> <STA> = get STA counter value" },
551 { "get_bss_counter", cmd_get_bss_counter,
552 "<counter> <BSSID> = get BSS counter value" },
553 { "inject", cmd_inject,
554 "<frame> <prot> <sender> <BSSID> <STA/ff:ff:ff:ff:ff:ff>" },
555 { NULL, NULL, NULL }
556 };
557
558
559 static int ctrl_command(int s, int argc, char *argv[])
560 {
561 const struct wlantest_cli_cmd *cmd, *match = NULL;
562 int count = 0;
563 int ret = 0;
564
565 for (cmd = wlantest_cli_commands; cmd->cmd; cmd++) {
566 if (os_strncasecmp(cmd->cmd, argv[0], os_strlen(argv[0])) == 0)
567 {
568 match = cmd;
569 if (os_strcasecmp(cmd->cmd, argv[0]) == 0) {
570 /* exact match */
571 count = 1;
572 break;
573 }
574 count++;
575 }
576 }
577
578 if (count > 1) {
579 printf("Ambiguous command '%s'; possible commands:", argv[0]);
580 for (cmd = wlantest_cli_commands; cmd->cmd; cmd++) {
581 if (os_strncasecmp(cmd->cmd, argv[0],
582 os_strlen(argv[0])) == 0) {
583 printf(" %s", cmd->cmd);
584 }
585 }
586 printf("\n");
587 ret = 1;
588 } else if (count == 0) {
589 printf("Unknown command '%s'\n", argv[0]);
590 ret = 1;
591 } else {
592 ret = match->handler(s, argc - 1, &argv[1]);
593 }
594
595 return ret;
596 }
597
598
599 int main(int argc, char *argv[])
600 {
601 int s;
602 struct sockaddr_un addr;
603 int ret = 0;
604
605 s = socket(AF_UNIX, SOCK_SEQPACKET, 0);
606 if (s < 0) {
607 perror("socket");
608 return -1;
609 }
610
611 os_memset(&addr, 0, sizeof(addr));
612 addr.sun_family = AF_UNIX;
613 os_strlcpy(addr.sun_path + 1, WLANTEST_SOCK_NAME,
614 sizeof(addr.sun_path) - 1);
615 if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
616 perror("connect");
617 close(s);
618 return -1;
619 }
620
621 if (argc > 1) {
622 ret = ctrl_command(s, argc - 1, &argv[1]);
623 if (ret < 0)
624 printf("FAIL\n");
625 } else {
626 /* TODO: interactive */
627 }
628
629 close(s);
630 return ret;
631 }