]>
Commit | Line | Data |
---|---|---|
644fb8c8 JM |
1 | /* |
2 | * wlantest controller | |
8cb9bc19 | 3 | * Copyright (c) 2010-2013, Jouni Malinen <j@w1.fi> |
644fb8c8 | 4 | * |
0f3d578e JM |
5 | * This software may be distributed under the terms of the BSD license. |
6 | * See README for more details. | |
644fb8c8 JM |
7 | */ |
8 | ||
9 | #include "utils/includes.h" | |
10 | #include <sys/un.h> | |
11 | ||
12 | #include "utils/common.h" | |
ef49bb80 JM |
13 | #include "utils/eloop.h" |
14 | #include "utils/edit.h" | |
644fb8c8 JM |
15 | #include "wlantest_ctrl.h" |
16 | ||
17 | ||
79a670cb JM |
18 | static int get_cmd_arg_num(const char *str, int pos) |
19 | { | |
20 | int arg = 0, i; | |
21 | ||
22 | for (i = 0; i <= pos; i++) { | |
23 | if (str[i] != ' ') { | |
24 | arg++; | |
25 | while (i <= pos && str[i] != ' ') | |
26 | i++; | |
27 | } | |
28 | } | |
29 | ||
30 | if (arg > 0) | |
31 | arg--; | |
32 | return arg; | |
33 | } | |
34 | ||
35 | ||
36 | static int get_prev_arg_pos(const char *str, int pos) | |
37 | { | |
38 | while (pos > 0 && str[pos - 1] != ' ') | |
39 | pos--; | |
40 | while (pos > 0 && str[pos - 1] == ' ') | |
41 | pos--; | |
42 | while (pos > 0 && str[pos - 1] != ' ') | |
43 | pos--; | |
44 | return pos; | |
45 | } | |
46 | ||
47 | ||
6f2346c1 JM |
48 | static u8 * attr_get(u8 *buf, size_t buflen, enum wlantest_ctrl_attr attr, |
49 | size_t *len) | |
50 | { | |
51 | u8 *pos = buf; | |
52 | ||
53 | while (pos + 8 <= buf + buflen) { | |
54 | enum wlantest_ctrl_attr a; | |
55 | size_t alen; | |
56 | a = WPA_GET_BE32(pos); | |
57 | pos += 4; | |
58 | alen = WPA_GET_BE32(pos); | |
59 | pos += 4; | |
60 | if (pos + alen > buf + buflen) { | |
61 | printf("Invalid control message attribute\n"); | |
62 | return NULL; | |
63 | } | |
64 | if (a == attr) { | |
65 | *len = alen; | |
66 | return pos; | |
67 | } | |
68 | pos += alen; | |
69 | } | |
70 | ||
71 | return NULL; | |
72 | } | |
73 | ||
74 | ||
6d5ce9fc JM |
75 | static u8 * attr_hdr_add(u8 *pos, u8 *end, enum wlantest_ctrl_attr attr, |
76 | size_t len) | |
77 | { | |
78 | if (pos == NULL || end - pos < 8 + len) | |
79 | return NULL; | |
80 | WPA_PUT_BE32(pos, attr); | |
81 | pos += 4; | |
82 | WPA_PUT_BE32(pos, len); | |
83 | pos += 4; | |
84 | return pos; | |
85 | } | |
86 | ||
87 | ||
fbdd2132 JM |
88 | static u8 * attr_add_str(u8 *pos, u8 *end, enum wlantest_ctrl_attr attr, |
89 | const char *str) | |
90 | { | |
91 | size_t len = os_strlen(str); | |
92 | ||
93 | if (pos == NULL || end - pos < 8 + len) | |
94 | return NULL; | |
95 | WPA_PUT_BE32(pos, attr); | |
96 | pos += 4; | |
97 | WPA_PUT_BE32(pos, len); | |
98 | pos += 4; | |
99 | os_memcpy(pos, str, len); | |
100 | pos += len; | |
101 | return pos; | |
102 | } | |
103 | ||
104 | ||
6d5ce9fc JM |
105 | static u8 * attr_add_be32(u8 *pos, u8 *end, enum wlantest_ctrl_attr attr, |
106 | u32 val) | |
107 | { | |
108 | if (pos == NULL || end - pos < 12) | |
109 | return NULL; | |
110 | WPA_PUT_BE32(pos, attr); | |
111 | pos += 4; | |
112 | WPA_PUT_BE32(pos, 4); | |
113 | pos += 4; | |
114 | WPA_PUT_BE32(pos, val); | |
115 | pos += 4; | |
116 | return pos; | |
117 | } | |
118 | ||
119 | ||
6f2346c1 JM |
120 | static int cmd_send_and_recv(int s, const u8 *cmd, size_t cmd_len, |
121 | u8 *resp, size_t max_resp_len) | |
644fb8c8 | 122 | { |
644fb8c8 | 123 | int res; |
6f2346c1 | 124 | enum wlantest_ctrl_cmd cmd_resp; |
644fb8c8 | 125 | |
6f2346c1 | 126 | if (send(s, cmd, cmd_len, 0) < 0) |
644fb8c8 | 127 | return -1; |
6f2346c1 | 128 | res = recv(s, resp, max_resp_len, 0); |
644fb8c8 JM |
129 | if (res < 4) |
130 | return -1; | |
131 | ||
6f2346c1 JM |
132 | cmd_resp = WPA_GET_BE32(resp); |
133 | if (cmd_resp == WLANTEST_CTRL_SUCCESS) | |
134 | return res; | |
135 | ||
136 | if (cmd_resp == WLANTEST_CTRL_UNKNOWN_CMD) | |
644fb8c8 | 137 | printf("Unknown command\n"); |
6f2346c1 JM |
138 | else if (cmd_resp == WLANTEST_CTRL_INVALID_CMD) |
139 | printf("Invalid command\n"); | |
644fb8c8 | 140 | |
6f2346c1 JM |
141 | return -1; |
142 | } | |
143 | ||
144 | ||
145 | static int cmd_simple(int s, enum wlantest_ctrl_cmd cmd) | |
146 | { | |
147 | u8 buf[4]; | |
148 | int res; | |
149 | WPA_PUT_BE32(buf, cmd); | |
150 | res = cmd_send_and_recv(s, buf, sizeof(buf), buf, sizeof(buf)); | |
151 | return res < 0 ? -1 : 0; | |
644fb8c8 JM |
152 | } |
153 | ||
154 | ||
79a670cb JM |
155 | static char ** get_bssid_list(int s) |
156 | { | |
157 | u8 resp[WLANTEST_CTRL_MAX_RESP_LEN]; | |
158 | u8 buf[4]; | |
159 | u8 *bssid; | |
160 | size_t len; | |
161 | int rlen, i; | |
162 | char **res; | |
163 | ||
164 | WPA_PUT_BE32(buf, WLANTEST_CTRL_LIST_BSS); | |
165 | rlen = cmd_send_and_recv(s, buf, sizeof(buf), resp, sizeof(resp)); | |
166 | if (rlen < 0) | |
167 | return NULL; | |
168 | ||
169 | bssid = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_BSSID, &len); | |
170 | if (bssid == NULL) | |
171 | return NULL; | |
172 | ||
f9884c09 | 173 | res = os_calloc(len / ETH_ALEN + 1, sizeof(char *)); |
79a670cb JM |
174 | if (res == NULL) |
175 | return NULL; | |
176 | for (i = 0; i < len / ETH_ALEN; i++) { | |
177 | res[i] = os_zalloc(18); | |
178 | if (res[i] == NULL) | |
179 | break; | |
180 | os_snprintf(res[i], 18, MACSTR, MAC2STR(bssid + ETH_ALEN * i)); | |
181 | } | |
182 | ||
183 | return res; | |
184 | } | |
185 | ||
186 | ||
187 | static char ** get_sta_list(int s, const u8 *bssid, int add_bcast) | |
188 | { | |
189 | u8 resp[WLANTEST_CTRL_MAX_RESP_LEN]; | |
190 | u8 buf[100], *pos, *end; | |
191 | u8 *addr; | |
192 | size_t len; | |
193 | int rlen, i; | |
194 | char **res; | |
195 | ||
196 | pos = buf; | |
197 | end = buf + sizeof(buf); | |
198 | WPA_PUT_BE32(pos, WLANTEST_CTRL_LIST_STA); | |
199 | pos += 4; | |
200 | pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN); | |
201 | os_memcpy(pos, bssid, ETH_ALEN); | |
202 | pos += ETH_ALEN; | |
0954399c | 203 | rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp)); |
79a670cb JM |
204 | if (rlen < 0) |
205 | return NULL; | |
206 | ||
207 | addr = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_STA_ADDR, &len); | |
208 | if (addr == NULL) | |
209 | return NULL; | |
210 | ||
f9884c09 | 211 | res = os_calloc(len / ETH_ALEN + 1 + add_bcast, sizeof(char *)); |
79a670cb JM |
212 | if (res == NULL) |
213 | return NULL; | |
214 | for (i = 0; i < len / ETH_ALEN; i++) { | |
215 | res[i] = os_zalloc(18); | |
216 | if (res[i] == NULL) | |
217 | break; | |
218 | os_snprintf(res[i], 18, MACSTR, MAC2STR(addr + ETH_ALEN * i)); | |
219 | } | |
220 | if (add_bcast) | |
221 | res[i] = os_strdup("ff:ff:ff:ff:ff:ff"); | |
222 | ||
223 | return res; | |
224 | } | |
225 | ||
226 | ||
644fb8c8 JM |
227 | static int cmd_ping(int s, int argc, char *argv[]) |
228 | { | |
6f2346c1 JM |
229 | int res = cmd_simple(s, WLANTEST_CTRL_PING); |
230 | if (res == 0) | |
231 | printf("PONG\n"); | |
232 | return res == 0; | |
644fb8c8 JM |
233 | } |
234 | ||
235 | ||
236 | static int cmd_terminate(int s, int argc, char *argv[]) | |
237 | { | |
238 | return cmd_simple(s, WLANTEST_CTRL_TERMINATE); | |
239 | } | |
240 | ||
241 | ||
6f2346c1 JM |
242 | static int cmd_list_bss(int s, int argc, char *argv[]) |
243 | { | |
244 | u8 resp[WLANTEST_CTRL_MAX_RESP_LEN]; | |
245 | u8 buf[4]; | |
246 | u8 *bssid; | |
247 | size_t len; | |
248 | int rlen, i; | |
249 | ||
250 | WPA_PUT_BE32(buf, WLANTEST_CTRL_LIST_BSS); | |
251 | rlen = cmd_send_and_recv(s, buf, sizeof(buf), resp, sizeof(resp)); | |
252 | if (rlen < 0) | |
253 | return -1; | |
254 | ||
255 | bssid = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_BSSID, &len); | |
256 | if (bssid == NULL) | |
257 | return -1; | |
258 | ||
259 | for (i = 0; i < len / ETH_ALEN; i++) | |
260 | printf(MACSTR " ", MAC2STR(bssid + ETH_ALEN * i)); | |
261 | printf("\n"); | |
262 | ||
263 | return 0; | |
264 | } | |
265 | ||
266 | ||
267 | static int cmd_list_sta(int s, int argc, char *argv[]) | |
268 | { | |
269 | u8 resp[WLANTEST_CTRL_MAX_RESP_LEN]; | |
270 | u8 buf[100], *pos; | |
271 | u8 *addr; | |
272 | size_t len; | |
273 | int rlen, i; | |
274 | ||
275 | if (argc < 1) { | |
276 | printf("list_sta needs one argument: BSSID\n"); | |
277 | return -1; | |
278 | } | |
279 | ||
280 | pos = buf; | |
281 | WPA_PUT_BE32(pos, WLANTEST_CTRL_LIST_STA); | |
282 | pos += 4; | |
283 | WPA_PUT_BE32(pos, WLANTEST_ATTR_BSSID); | |
284 | pos += 4; | |
285 | WPA_PUT_BE32(pos, ETH_ALEN); | |
286 | pos += 4; | |
287 | if (hwaddr_aton(argv[0], pos) < 0) { | |
288 | printf("Invalid BSSID '%s'\n", argv[0]); | |
289 | return -1; | |
290 | } | |
291 | pos += ETH_ALEN; | |
292 | ||
293 | rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp)); | |
294 | if (rlen < 0) | |
295 | return -1; | |
296 | ||
297 | addr = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_STA_ADDR, &len); | |
298 | if (addr == NULL) | |
299 | return -1; | |
300 | ||
301 | for (i = 0; i < len / ETH_ALEN; i++) | |
302 | printf(MACSTR " ", MAC2STR(addr + ETH_ALEN * i)); | |
303 | printf("\n"); | |
304 | ||
305 | return 0; | |
306 | } | |
307 | ||
308 | ||
79a670cb JM |
309 | static char ** complete_list_sta(int s, const char *str, int pos) |
310 | { | |
311 | if (get_cmd_arg_num(str, pos) == 1) | |
312 | return get_bssid_list(s); | |
313 | return NULL; | |
314 | } | |
315 | ||
316 | ||
d356bd63 JM |
317 | static int cmd_flush(int s, int argc, char *argv[]) |
318 | { | |
319 | return cmd_simple(s, WLANTEST_CTRL_FLUSH); | |
320 | } | |
321 | ||
322 | ||
6d5ce9fc JM |
323 | static int cmd_clear_sta_counters(int s, int argc, char *argv[]) |
324 | { | |
325 | u8 resp[WLANTEST_CTRL_MAX_RESP_LEN]; | |
326 | u8 buf[100], *pos; | |
327 | int rlen; | |
328 | ||
329 | if (argc < 2) { | |
330 | printf("clear_sta_counters needs two arguments: BSSID and " | |
331 | "STA address\n"); | |
332 | return -1; | |
333 | } | |
334 | ||
335 | pos = buf; | |
336 | WPA_PUT_BE32(pos, WLANTEST_CTRL_CLEAR_STA_COUNTERS); | |
337 | pos += 4; | |
338 | WPA_PUT_BE32(pos, WLANTEST_ATTR_BSSID); | |
339 | pos += 4; | |
340 | WPA_PUT_BE32(pos, ETH_ALEN); | |
341 | pos += 4; | |
342 | if (hwaddr_aton(argv[0], pos) < 0) { | |
343 | printf("Invalid BSSID '%s'\n", argv[0]); | |
344 | return -1; | |
345 | } | |
346 | pos += ETH_ALEN; | |
347 | ||
348 | WPA_PUT_BE32(pos, WLANTEST_ATTR_STA_ADDR); | |
349 | pos += 4; | |
350 | WPA_PUT_BE32(pos, ETH_ALEN); | |
351 | pos += 4; | |
352 | if (hwaddr_aton(argv[1], pos) < 0) { | |
353 | printf("Invalid STA address '%s'\n", argv[1]); | |
354 | return -1; | |
355 | } | |
356 | pos += ETH_ALEN; | |
357 | ||
358 | rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp)); | |
359 | if (rlen < 0) | |
360 | return -1; | |
361 | printf("OK\n"); | |
362 | return 0; | |
363 | } | |
364 | ||
365 | ||
79a670cb JM |
366 | static char ** complete_clear_sta_counters(int s, const char *str, int pos) |
367 | { | |
368 | int arg = get_cmd_arg_num(str, pos); | |
369 | char **res = NULL; | |
370 | u8 addr[ETH_ALEN]; | |
371 | ||
372 | switch (arg) { | |
373 | case 1: | |
374 | res = get_bssid_list(s); | |
375 | break; | |
376 | case 2: | |
377 | if (hwaddr_aton(&str[get_prev_arg_pos(str, pos)], addr) < 0) | |
378 | break; | |
379 | res = get_sta_list(s, addr, 0); | |
380 | break; | |
381 | } | |
382 | ||
383 | return res; | |
384 | } | |
385 | ||
386 | ||
6d5ce9fc JM |
387 | static int cmd_clear_bss_counters(int s, int argc, char *argv[]) |
388 | { | |
389 | u8 resp[WLANTEST_CTRL_MAX_RESP_LEN]; | |
390 | u8 buf[100], *pos; | |
391 | int rlen; | |
392 | ||
393 | if (argc < 1) { | |
394 | printf("clear_bss_counters needs one argument: BSSID\n"); | |
395 | return -1; | |
396 | } | |
397 | ||
398 | pos = buf; | |
399 | WPA_PUT_BE32(pos, WLANTEST_CTRL_CLEAR_BSS_COUNTERS); | |
400 | pos += 4; | |
401 | WPA_PUT_BE32(pos, WLANTEST_ATTR_BSSID); | |
402 | pos += 4; | |
403 | WPA_PUT_BE32(pos, ETH_ALEN); | |
404 | pos += 4; | |
405 | if (hwaddr_aton(argv[0], pos) < 0) { | |
406 | printf("Invalid BSSID '%s'\n", argv[0]); | |
407 | return -1; | |
408 | } | |
409 | pos += ETH_ALEN; | |
410 | ||
411 | rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp)); | |
412 | if (rlen < 0) | |
413 | return -1; | |
414 | printf("OK\n"); | |
415 | return 0; | |
416 | } | |
417 | ||
418 | ||
79a670cb JM |
419 | static char ** complete_clear_bss_counters(int s, const char *str, int pos) |
420 | { | |
421 | if (get_cmd_arg_num(str, pos) == 1) | |
422 | return get_bssid_list(s); | |
423 | return NULL; | |
424 | } | |
425 | ||
426 | ||
0e42fff3 JM |
427 | static int cmd_clear_tdls_counters(int s, int argc, char *argv[]) |
428 | { | |
429 | u8 resp[WLANTEST_CTRL_MAX_RESP_LEN]; | |
430 | u8 buf[100], *pos; | |
431 | int rlen; | |
432 | ||
433 | if (argc < 3) { | |
434 | printf("clear_tdls_counters needs three arguments: BSSID, " | |
435 | "STA1 address, STA2 address\n"); | |
436 | return -1; | |
437 | } | |
438 | ||
439 | pos = buf; | |
440 | WPA_PUT_BE32(pos, WLANTEST_CTRL_CLEAR_TDLS_COUNTERS); | |
441 | pos += 4; | |
442 | WPA_PUT_BE32(pos, WLANTEST_ATTR_BSSID); | |
443 | pos += 4; | |
444 | WPA_PUT_BE32(pos, ETH_ALEN); | |
445 | pos += 4; | |
446 | if (hwaddr_aton(argv[0], pos) < 0) { | |
447 | printf("Invalid BSSID '%s'\n", argv[0]); | |
448 | return -1; | |
449 | } | |
450 | pos += ETH_ALEN; | |
451 | ||
452 | WPA_PUT_BE32(pos, WLANTEST_ATTR_STA_ADDR); | |
453 | pos += 4; | |
454 | WPA_PUT_BE32(pos, ETH_ALEN); | |
455 | pos += 4; | |
456 | if (hwaddr_aton(argv[1], pos) < 0) { | |
457 | printf("Invalid STA1 address '%s'\n", argv[1]); | |
458 | return -1; | |
459 | } | |
460 | pos += ETH_ALEN; | |
461 | ||
462 | WPA_PUT_BE32(pos, WLANTEST_ATTR_STA2_ADDR); | |
463 | pos += 4; | |
464 | WPA_PUT_BE32(pos, ETH_ALEN); | |
465 | pos += 4; | |
466 | if (hwaddr_aton(argv[2], pos) < 0) { | |
467 | printf("Invalid STA2 address '%s'\n", argv[2]); | |
468 | return -1; | |
469 | } | |
470 | pos += ETH_ALEN; | |
471 | ||
472 | rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp)); | |
473 | if (rlen < 0) | |
474 | return -1; | |
475 | printf("OK\n"); | |
476 | return 0; | |
477 | } | |
478 | ||
479 | ||
480 | static char ** complete_clear_tdls_counters(int s, const char *str, int pos) | |
481 | { | |
482 | int arg = get_cmd_arg_num(str, pos); | |
483 | char **res = NULL; | |
484 | u8 addr[ETH_ALEN]; | |
485 | ||
486 | switch (arg) { | |
487 | case 1: | |
488 | res = get_bssid_list(s); | |
489 | break; | |
490 | case 2: | |
491 | case 3: | |
492 | if (hwaddr_aton(&str[get_prev_arg_pos(str, pos)], addr) < 0) | |
493 | break; | |
494 | res = get_sta_list(s, addr, 0); | |
495 | break; | |
496 | } | |
497 | ||
498 | return res; | |
499 | } | |
500 | ||
501 | ||
6d5ce9fc JM |
502 | struct sta_counters { |
503 | const char *name; | |
504 | enum wlantest_sta_counter num; | |
505 | }; | |
506 | ||
507 | static const struct sta_counters sta_counters[] = { | |
508 | { "auth_tx", WLANTEST_STA_COUNTER_AUTH_TX }, | |
509 | { "auth_rx", WLANTEST_STA_COUNTER_AUTH_RX }, | |
510 | { "assocreq_tx", WLANTEST_STA_COUNTER_ASSOCREQ_TX }, | |
511 | { "reassocreq_tx", WLANTEST_STA_COUNTER_REASSOCREQ_TX }, | |
512 | { "ptk_learned", WLANTEST_STA_COUNTER_PTK_LEARNED }, | |
513 | { "valid_deauth_tx", WLANTEST_STA_COUNTER_VALID_DEAUTH_TX }, | |
514 | { "valid_deauth_rx", WLANTEST_STA_COUNTER_VALID_DEAUTH_RX }, | |
515 | { "invalid_deauth_tx", WLANTEST_STA_COUNTER_INVALID_DEAUTH_TX }, | |
516 | { "invalid_deauth_rx", WLANTEST_STA_COUNTER_INVALID_DEAUTH_RX }, | |
517 | { "valid_disassoc_tx", WLANTEST_STA_COUNTER_VALID_DISASSOC_TX }, | |
518 | { "valid_disassoc_rx", WLANTEST_STA_COUNTER_VALID_DISASSOC_RX }, | |
519 | { "invalid_disassoc_tx", WLANTEST_STA_COUNTER_INVALID_DISASSOC_TX }, | |
520 | { "invalid_disassoc_rx", WLANTEST_STA_COUNTER_INVALID_DISASSOC_RX }, | |
521 | { "valid_saqueryreq_tx", WLANTEST_STA_COUNTER_VALID_SAQUERYREQ_TX }, | |
522 | { "valid_saqueryreq_rx", WLANTEST_STA_COUNTER_VALID_SAQUERYREQ_RX }, | |
523 | { "invalid_saqueryreq_tx", | |
524 | WLANTEST_STA_COUNTER_INVALID_SAQUERYREQ_TX }, | |
525 | { "invalid_saqueryreq_rx", | |
526 | WLANTEST_STA_COUNTER_INVALID_SAQUERYREQ_RX }, | |
527 | { "valid_saqueryresp_tx", WLANTEST_STA_COUNTER_VALID_SAQUERYRESP_TX }, | |
528 | { "valid_saqueryresp_rx", WLANTEST_STA_COUNTER_VALID_SAQUERYRESP_RX }, | |
529 | { "invalid_saqueryresp_tx", | |
530 | WLANTEST_STA_COUNTER_INVALID_SAQUERYRESP_TX }, | |
531 | { "invalid_saqueryresp_rx", | |
532 | WLANTEST_STA_COUNTER_INVALID_SAQUERYRESP_RX }, | |
ee3b84be | 533 | { "ping_ok", WLANTEST_STA_COUNTER_PING_OK }, |
cdd71e30 JM |
534 | { "assocresp_comeback", WLANTEST_STA_COUNTER_ASSOCRESP_COMEBACK }, |
535 | { "reassocresp_comeback", WLANTEST_STA_COUNTER_REASSOCRESP_COMEBACK }, | |
1da4da6f | 536 | { "ping_ok_first_assoc", WLANTEST_STA_COUNTER_PING_OK_FIRST_ASSOC }, |
fb8f5fc6 JM |
537 | { "valid_deauth_rx_ack", WLANTEST_STA_COUNTER_VALID_DEAUTH_RX_ACK }, |
538 | { "valid_disassoc_rx_ack", | |
539 | WLANTEST_STA_COUNTER_VALID_DISASSOC_RX_ACK }, | |
540 | { "invalid_deauth_rx_ack", | |
541 | WLANTEST_STA_COUNTER_INVALID_DEAUTH_RX_ACK }, | |
542 | { "invalid_disassoc_rx_ack", | |
543 | WLANTEST_STA_COUNTER_INVALID_DISASSOC_RX_ACK }, | |
e7ba4e2c JM |
544 | { "deauth_rx_asleep", WLANTEST_STA_COUNTER_DEAUTH_RX_ASLEEP }, |
545 | { "deauth_rx_awake", WLANTEST_STA_COUNTER_DEAUTH_RX_AWAKE }, | |
546 | { "disassoc_rx_asleep", WLANTEST_STA_COUNTER_DISASSOC_RX_ASLEEP }, | |
547 | { "disassoc_rx_awake", WLANTEST_STA_COUNTER_DISASSOC_RX_AWAKE }, | |
0a9ddd92 | 548 | { "prot_data_tx", WLANTEST_STA_COUNTER_PROT_DATA_TX }, |
62f05ce9 JM |
549 | { "deauth_rx_rc6", WLANTEST_STA_COUNTER_DEAUTH_RX_RC6 }, |
550 | { "deauth_rx_rc7", WLANTEST_STA_COUNTER_DEAUTH_RX_RC7 }, | |
551 | { "disassoc_rx_rc6", WLANTEST_STA_COUNTER_DISASSOC_RX_RC6 }, | |
552 | { "disassoc_rx_rc7", WLANTEST_STA_COUNTER_DISASSOC_RX_RC7 }, | |
6d5ce9fc JM |
553 | { NULL, 0 } |
554 | }; | |
555 | ||
556 | static int cmd_get_sta_counter(int s, int argc, char *argv[]) | |
557 | { | |
558 | u8 resp[WLANTEST_CTRL_MAX_RESP_LEN]; | |
559 | u8 buf[100], *end, *pos; | |
560 | int rlen, i; | |
561 | size_t len; | |
562 | ||
563 | if (argc != 3) { | |
564 | printf("get_sta_counter needs at three arguments: " | |
565 | "counter name, BSSID, and STA address\n"); | |
566 | return -1; | |
567 | } | |
568 | ||
569 | pos = buf; | |
570 | end = buf + sizeof(buf); | |
571 | WPA_PUT_BE32(pos, WLANTEST_CTRL_GET_STA_COUNTER); | |
572 | pos += 4; | |
573 | ||
574 | for (i = 0; sta_counters[i].name; i++) { | |
575 | if (os_strcasecmp(sta_counters[i].name, argv[0]) == 0) | |
576 | break; | |
577 | } | |
578 | if (sta_counters[i].name == NULL) { | |
579 | printf("Unknown STA counter '%s'\n", argv[0]); | |
580 | printf("Counters:"); | |
581 | for (i = 0; sta_counters[i].name; i++) | |
582 | printf(" %s", sta_counters[i].name); | |
583 | printf("\n"); | |
584 | return -1; | |
585 | } | |
586 | ||
587 | pos = attr_add_be32(pos, end, WLANTEST_ATTR_STA_COUNTER, | |
588 | sta_counters[i].num); | |
589 | pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN); | |
590 | if (hwaddr_aton(argv[1], pos) < 0) { | |
591 | printf("Invalid BSSID '%s'\n", argv[1]); | |
592 | return -1; | |
593 | } | |
594 | pos += ETH_ALEN; | |
595 | ||
596 | pos = attr_hdr_add(pos, end, WLANTEST_ATTR_STA_ADDR, ETH_ALEN); | |
597 | if (hwaddr_aton(argv[2], pos) < 0) { | |
598 | printf("Invalid STA address '%s'\n", argv[2]); | |
599 | return -1; | |
600 | } | |
601 | pos += ETH_ALEN; | |
602 | ||
603 | rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp)); | |
604 | if (rlen < 0) | |
605 | return -1; | |
606 | ||
607 | pos = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_COUNTER, &len); | |
608 | if (pos == NULL || len != 4) | |
609 | return -1; | |
610 | printf("%u\n", WPA_GET_BE32(pos)); | |
611 | return 0; | |
612 | } | |
613 | ||
614 | ||
79a670cb JM |
615 | static char ** complete_get_sta_counter(int s, const char *str, int pos) |
616 | { | |
617 | int arg = get_cmd_arg_num(str, pos); | |
618 | char **res = NULL; | |
619 | int i, count; | |
620 | u8 addr[ETH_ALEN]; | |
621 | ||
622 | switch (arg) { | |
623 | case 1: | |
624 | /* counter list */ | |
e7ecab4a | 625 | count = ARRAY_SIZE(sta_counters); |
f9884c09 | 626 | res = os_calloc(count, sizeof(char *)); |
79a670cb JM |
627 | if (res == NULL) |
628 | return NULL; | |
629 | for (i = 0; sta_counters[i].name; i++) { | |
630 | res[i] = os_strdup(sta_counters[i].name); | |
631 | if (res[i] == NULL) | |
632 | break; | |
633 | } | |
634 | break; | |
635 | case 2: | |
636 | res = get_bssid_list(s); | |
637 | break; | |
638 | case 3: | |
639 | if (hwaddr_aton(&str[get_prev_arg_pos(str, pos)], addr) < 0) | |
640 | break; | |
641 | res = get_sta_list(s, addr, 0); | |
642 | break; | |
643 | } | |
644 | ||
645 | return res; | |
646 | } | |
647 | ||
648 | ||
6d5ce9fc JM |
649 | struct bss_counters { |
650 | const char *name; | |
651 | enum wlantest_bss_counter num; | |
652 | }; | |
653 | ||
654 | static const struct bss_counters bss_counters[] = { | |
655 | { "valid_bip_mmie", WLANTEST_BSS_COUNTER_VALID_BIP_MMIE }, | |
656 | { "invalid_bip_mmie", WLANTEST_BSS_COUNTER_INVALID_BIP_MMIE }, | |
657 | { "missing_bip_mmie", WLANTEST_BSS_COUNTER_MISSING_BIP_MMIE }, | |
783a082c JM |
658 | { "bip_deauth", WLANTEST_BSS_COUNTER_BIP_DEAUTH }, |
659 | { "bip_disassoc", WLANTEST_BSS_COUNTER_BIP_DISASSOC }, | |
ae98e1f5 | 660 | { "probe_response", WLANTEST_BSS_COUNTER_PROBE_RESPONSE }, |
6d5ce9fc JM |
661 | { NULL, 0 } |
662 | }; | |
663 | ||
664 | static int cmd_get_bss_counter(int s, int argc, char *argv[]) | |
665 | { | |
666 | u8 resp[WLANTEST_CTRL_MAX_RESP_LEN]; | |
667 | u8 buf[100], *end, *pos; | |
668 | int rlen, i; | |
669 | size_t len; | |
670 | ||
671 | if (argc != 2) { | |
2fc0cd54 | 672 | printf("get_bss_counter needs at two arguments: " |
6d5ce9fc JM |
673 | "counter name and BSSID\n"); |
674 | return -1; | |
675 | } | |
676 | ||
677 | pos = buf; | |
678 | end = buf + sizeof(buf); | |
679 | WPA_PUT_BE32(pos, WLANTEST_CTRL_GET_BSS_COUNTER); | |
680 | pos += 4; | |
681 | ||
682 | for (i = 0; bss_counters[i].name; i++) { | |
683 | if (os_strcasecmp(bss_counters[i].name, argv[0]) == 0) | |
684 | break; | |
685 | } | |
686 | if (bss_counters[i].name == NULL) { | |
687 | printf("Unknown BSS counter '%s'\n", argv[0]); | |
688 | printf("Counters:"); | |
689 | for (i = 0; bss_counters[i].name; i++) | |
690 | printf(" %s", bss_counters[i].name); | |
691 | printf("\n"); | |
692 | return -1; | |
693 | } | |
694 | ||
695 | pos = attr_add_be32(pos, end, WLANTEST_ATTR_BSS_COUNTER, | |
696 | bss_counters[i].num); | |
697 | pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN); | |
698 | if (hwaddr_aton(argv[1], pos) < 0) { | |
699 | printf("Invalid BSSID '%s'\n", argv[1]); | |
700 | return -1; | |
701 | } | |
702 | pos += ETH_ALEN; | |
703 | ||
704 | rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp)); | |
705 | if (rlen < 0) | |
706 | return -1; | |
707 | ||
708 | pos = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_COUNTER, &len); | |
709 | if (pos == NULL || len != 4) | |
710 | return -1; | |
711 | printf("%u\n", WPA_GET_BE32(pos)); | |
712 | return 0; | |
713 | } | |
714 | ||
715 | ||
79a670cb JM |
716 | static char ** complete_get_bss_counter(int s, const char *str, int pos) |
717 | { | |
718 | int arg = get_cmd_arg_num(str, pos); | |
719 | char **res = NULL; | |
720 | int i, count; | |
721 | ||
722 | switch (arg) { | |
723 | case 1: | |
724 | /* counter list */ | |
e7ecab4a | 725 | count = ARRAY_SIZE(bss_counters); |
f9884c09 | 726 | res = os_calloc(count, sizeof(char *)); |
79a670cb JM |
727 | if (res == NULL) |
728 | return NULL; | |
729 | for (i = 0; bss_counters[i].name; i++) { | |
730 | res[i] = os_strdup(bss_counters[i].name); | |
731 | if (res[i] == NULL) | |
732 | break; | |
733 | } | |
734 | break; | |
735 | case 2: | |
736 | res = get_bssid_list(s); | |
737 | break; | |
738 | } | |
739 | ||
740 | return res; | |
741 | } | |
742 | ||
743 | ||
c99a721e JM |
744 | static int cmd_relog(int s, int argc, char *argv[]) |
745 | { | |
746 | return cmd_simple(s, WLANTEST_CTRL_RELOG); | |
747 | } | |
748 | ||
749 | ||
0e42fff3 JM |
750 | struct tdls_counters { |
751 | const char *name; | |
752 | enum wlantest_tdls_counter num; | |
753 | }; | |
754 | ||
755 | static const struct tdls_counters tdls_counters[] = { | |
756 | { "valid_direct_link", WLANTEST_TDLS_COUNTER_VALID_DIRECT_LINK }, | |
757 | { "invalid_direct_link", WLANTEST_TDLS_COUNTER_INVALID_DIRECT_LINK }, | |
758 | { "valid_ap_path", WLANTEST_TDLS_COUNTER_VALID_AP_PATH }, | |
759 | { "invalid_ap_path", WLANTEST_TDLS_COUNTER_INVALID_AP_PATH }, | |
eb4923fd JM |
760 | { "setup_req", WLANTEST_TDLS_COUNTER_SETUP_REQ }, |
761 | { "setup_resp_ok", WLANTEST_TDLS_COUNTER_SETUP_RESP_OK }, | |
762 | { "setup_resp_fail", WLANTEST_TDLS_COUNTER_SETUP_RESP_FAIL }, | |
763 | { "setup_conf_ok", WLANTEST_TDLS_COUNTER_SETUP_CONF_OK }, | |
764 | { "setup_conf_fail", WLANTEST_TDLS_COUNTER_SETUP_CONF_FAIL }, | |
5acf56f6 | 765 | { "teardown", WLANTEST_TDLS_COUNTER_TEARDOWN }, |
0e42fff3 JM |
766 | { NULL, 0 } |
767 | }; | |
768 | ||
769 | static int cmd_get_tdls_counter(int s, int argc, char *argv[]) | |
770 | { | |
771 | u8 resp[WLANTEST_CTRL_MAX_RESP_LEN]; | |
772 | u8 buf[100], *end, *pos; | |
773 | int rlen, i; | |
774 | size_t len; | |
775 | ||
776 | if (argc != 4) { | |
777 | printf("get_tdls_counter needs four arguments: " | |
778 | "counter name, BSSID, STA1 address, STA2 address\n"); | |
779 | return -1; | |
780 | } | |
781 | ||
782 | pos = buf; | |
783 | end = buf + sizeof(buf); | |
784 | WPA_PUT_BE32(pos, WLANTEST_CTRL_GET_TDLS_COUNTER); | |
785 | pos += 4; | |
786 | ||
787 | for (i = 0; tdls_counters[i].name; i++) { | |
788 | if (os_strcasecmp(tdls_counters[i].name, argv[0]) == 0) | |
789 | break; | |
790 | } | |
791 | if (tdls_counters[i].name == NULL) { | |
792 | printf("Unknown TDLS counter '%s'\n", argv[0]); | |
793 | printf("Counters:"); | |
794 | for (i = 0; tdls_counters[i].name; i++) | |
795 | printf(" %s", tdls_counters[i].name); | |
796 | printf("\n"); | |
797 | return -1; | |
798 | } | |
799 | ||
800 | pos = attr_add_be32(pos, end, WLANTEST_ATTR_TDLS_COUNTER, | |
801 | tdls_counters[i].num); | |
802 | pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN); | |
803 | if (hwaddr_aton(argv[1], pos) < 0) { | |
804 | printf("Invalid BSSID '%s'\n", argv[1]); | |
805 | return -1; | |
806 | } | |
807 | pos += ETH_ALEN; | |
808 | ||
809 | pos = attr_hdr_add(pos, end, WLANTEST_ATTR_STA_ADDR, ETH_ALEN); | |
810 | if (hwaddr_aton(argv[2], pos) < 0) { | |
811 | printf("Invalid STA1 address '%s'\n", argv[2]); | |
812 | return -1; | |
813 | } | |
814 | pos += ETH_ALEN; | |
815 | ||
816 | pos = attr_hdr_add(pos, end, WLANTEST_ATTR_STA2_ADDR, ETH_ALEN); | |
817 | if (hwaddr_aton(argv[3], pos) < 0) { | |
818 | printf("Invalid STA2 address '%s'\n", argv[3]); | |
819 | return -1; | |
820 | } | |
821 | pos += ETH_ALEN; | |
822 | ||
823 | rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp)); | |
824 | if (rlen < 0) | |
825 | return -1; | |
826 | ||
827 | pos = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_COUNTER, &len); | |
828 | if (pos == NULL || len != 4) | |
829 | return -1; | |
830 | printf("%u\n", WPA_GET_BE32(pos)); | |
831 | return 0; | |
832 | } | |
833 | ||
834 | ||
835 | static char ** complete_get_tdls_counter(int s, const char *str, int pos) | |
836 | { | |
837 | int arg = get_cmd_arg_num(str, pos); | |
838 | char **res = NULL; | |
839 | int i, count; | |
840 | u8 addr[ETH_ALEN]; | |
841 | ||
842 | switch (arg) { | |
843 | case 1: | |
844 | /* counter list */ | |
e7ecab4a | 845 | count = ARRAY_SIZE(tdls_counters); |
f9884c09 | 846 | res = os_calloc(count, sizeof(char *)); |
0e42fff3 JM |
847 | if (res == NULL) |
848 | return NULL; | |
849 | for (i = 0; tdls_counters[i].name; i++) { | |
850 | res[i] = os_strdup(tdls_counters[i].name); | |
851 | if (res[i] == NULL) | |
852 | break; | |
853 | } | |
854 | break; | |
855 | case 2: | |
856 | res = get_bssid_list(s); | |
857 | break; | |
858 | case 3: | |
859 | case 4: | |
860 | if (hwaddr_aton(&str[get_prev_arg_pos(str, pos)], addr) < 0) | |
861 | break; | |
862 | res = get_sta_list(s, addr, 0); | |
863 | break; | |
864 | } | |
865 | ||
866 | return res; | |
867 | } | |
868 | ||
869 | ||
7d23e971 JM |
870 | struct inject_frames { |
871 | const char *name; | |
872 | enum wlantest_inject_frame frame; | |
873 | }; | |
874 | ||
875 | static const struct inject_frames inject_frames[] = { | |
876 | { "auth", WLANTEST_FRAME_AUTH }, | |
877 | { "assocreq", WLANTEST_FRAME_ASSOCREQ }, | |
878 | { "reassocreq", WLANTEST_FRAME_REASSOCREQ }, | |
879 | { "deauth", WLANTEST_FRAME_DEAUTH }, | |
880 | { "disassoc", WLANTEST_FRAME_DISASSOC }, | |
881 | { "saqueryreq", WLANTEST_FRAME_SAQUERYREQ }, | |
882 | { NULL, 0 } | |
883 | }; | |
884 | ||
885 | static int cmd_inject(int s, int argc, char *argv[]) | |
886 | { | |
887 | u8 resp[WLANTEST_CTRL_MAX_RESP_LEN]; | |
888 | u8 buf[100], *end, *pos; | |
889 | int rlen, i; | |
890 | enum wlantest_inject_protection prot; | |
891 | ||
892 | /* <frame> <prot> <sender> <BSSID> <STA/ff:ff:ff:ff:ff:ff> */ | |
893 | ||
894 | if (argc < 5) { | |
895 | printf("inject needs five arguments: frame, protection, " | |
896 | "sender, BSSID, STA/ff:ff:ff:ff:ff:ff\n"); | |
897 | return -1; | |
898 | } | |
899 | ||
900 | pos = buf; | |
901 | end = buf + sizeof(buf); | |
902 | WPA_PUT_BE32(pos, WLANTEST_CTRL_INJECT); | |
903 | pos += 4; | |
904 | ||
905 | for (i = 0; inject_frames[i].name; i++) { | |
906 | if (os_strcasecmp(inject_frames[i].name, argv[0]) == 0) | |
907 | break; | |
908 | } | |
909 | if (inject_frames[i].name == NULL) { | |
910 | printf("Unknown inject frame '%s'\n", argv[0]); | |
911 | printf("Frames:"); | |
912 | for (i = 0; inject_frames[i].name; i++) | |
913 | printf(" %s", inject_frames[i].name); | |
914 | printf("\n"); | |
915 | return -1; | |
916 | } | |
917 | ||
918 | pos = attr_add_be32(pos, end, WLANTEST_ATTR_INJECT_FRAME, | |
919 | inject_frames[i].frame); | |
920 | ||
921 | if (os_strcasecmp(argv[1], "normal") == 0) | |
922 | prot = WLANTEST_INJECT_NORMAL; | |
923 | else if (os_strcasecmp(argv[1], "protected") == 0) | |
924 | prot = WLANTEST_INJECT_PROTECTED; | |
925 | else if (os_strcasecmp(argv[1], "unprotected") == 0) | |
926 | prot = WLANTEST_INJECT_UNPROTECTED; | |
927 | else if (os_strcasecmp(argv[1], "incorrect") == 0) | |
928 | prot = WLANTEST_INJECT_INCORRECT_KEY; | |
929 | else { | |
930 | printf("Unknown protection type '%s'\n", argv[1]); | |
931 | printf("Protection types: normal protected unprotected " | |
932 | "incorrect\n"); | |
933 | return -1; | |
934 | } | |
935 | pos = attr_add_be32(pos, end, WLANTEST_ATTR_INJECT_PROTECTION, prot); | |
936 | ||
937 | if (os_strcasecmp(argv[2], "ap") == 0) { | |
938 | pos = attr_add_be32(pos, end, WLANTEST_ATTR_INJECT_SENDER_AP, | |
939 | 1); | |
940 | } else if (os_strcasecmp(argv[2], "sta") == 0) { | |
941 | pos = attr_add_be32(pos, end, WLANTEST_ATTR_INJECT_SENDER_AP, | |
942 | 0); | |
943 | } else { | |
944 | printf("Unknown sender '%s'\n", argv[2]); | |
945 | printf("Sender types: ap sta\n"); | |
946 | return -1; | |
947 | } | |
948 | ||
949 | pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN); | |
950 | if (hwaddr_aton(argv[3], pos) < 0) { | |
951 | printf("Invalid BSSID '%s'\n", argv[3]); | |
952 | return -1; | |
953 | } | |
954 | pos += ETH_ALEN; | |
955 | ||
956 | pos = attr_hdr_add(pos, end, WLANTEST_ATTR_STA_ADDR, ETH_ALEN); | |
957 | if (hwaddr_aton(argv[4], pos) < 0) { | |
958 | printf("Invalid STA '%s'\n", argv[4]); | |
959 | return -1; | |
960 | } | |
961 | pos += ETH_ALEN; | |
962 | ||
963 | rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp)); | |
964 | if (rlen < 0) | |
965 | return -1; | |
966 | printf("OK\n"); | |
967 | return 0; | |
968 | } | |
969 | ||
970 | ||
79a670cb JM |
971 | static char ** complete_inject(int s, const char *str, int pos) |
972 | { | |
973 | int arg = get_cmd_arg_num(str, pos); | |
974 | char **res = NULL; | |
975 | int i, count; | |
976 | u8 addr[ETH_ALEN]; | |
977 | ||
978 | switch (arg) { | |
979 | case 1: | |
980 | /* frame list */ | |
e7ecab4a | 981 | count = ARRAY_SIZE(inject_frames); |
f9884c09 | 982 | res = os_calloc(count, sizeof(char *)); |
79a670cb JM |
983 | if (res == NULL) |
984 | break; | |
985 | for (i = 0; inject_frames[i].name; i++) { | |
986 | res[i] = os_strdup(inject_frames[i].name); | |
987 | if (res[i] == NULL) | |
988 | break; | |
989 | } | |
990 | break; | |
991 | case 2: | |
f9884c09 | 992 | res = os_calloc(5, sizeof(char *)); |
79a670cb JM |
993 | if (res == NULL) |
994 | break; | |
995 | res[0] = os_strdup("normal"); | |
996 | if (res[0] == NULL) | |
997 | break; | |
998 | res[1] = os_strdup("protected"); | |
999 | if (res[1] == NULL) | |
1000 | break; | |
1001 | res[2] = os_strdup("unprotected"); | |
1002 | if (res[2] == NULL) | |
1003 | break; | |
1004 | res[3] = os_strdup("incorrect"); | |
1005 | if (res[3] == NULL) | |
1006 | break; | |
1007 | break; | |
1008 | case 3: | |
f9884c09 | 1009 | res = os_calloc(3, sizeof(char *)); |
79a670cb JM |
1010 | if (res == NULL) |
1011 | break; | |
1012 | res[0] = os_strdup("ap"); | |
1013 | if (res[0] == NULL) | |
1014 | break; | |
1015 | res[1] = os_strdup("sta"); | |
1016 | if (res[1] == NULL) | |
1017 | break; | |
1018 | break; | |
1019 | case 4: | |
1020 | res = get_bssid_list(s); | |
1021 | break; | |
1022 | case 5: | |
1023 | if (hwaddr_aton(&str[get_prev_arg_pos(str, pos)], addr) < 0) | |
1024 | break; | |
1025 | res = get_sta_list(s, addr, 1); | |
1026 | break; | |
1027 | } | |
1028 | ||
1029 | return res; | |
1030 | } | |
1031 | ||
1032 | ||
b3a6d9d4 JM |
1033 | static u8 * add_hex(u8 *pos, u8 *end, const char *str) |
1034 | { | |
1035 | const char *s; | |
1036 | int val; | |
1037 | ||
1038 | s = str; | |
1039 | while (*s) { | |
1040 | while (*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n' || | |
1041 | *s == ':') | |
1042 | s++; | |
1043 | if (*s == '\0') | |
1044 | break; | |
1045 | if (*s == '#') { | |
1046 | while (*s != '\0' && *s != '\r' && *s != '\n') | |
1047 | s++; | |
1048 | continue; | |
1049 | } | |
1050 | ||
1051 | val = hex2byte(s); | |
1052 | if (val < 0) { | |
1053 | printf("Invalid hex encoding '%s'\n", s); | |
1054 | return NULL; | |
1055 | } | |
1056 | if (pos == end) { | |
1057 | printf("Too long frame\n"); | |
1058 | return NULL; | |
1059 | } | |
1060 | *pos++ = val; | |
1061 | s += 2; | |
1062 | } | |
1063 | ||
1064 | return pos; | |
1065 | } | |
1066 | ||
1067 | ||
1068 | static int cmd_send(int s, int argc, char *argv[]) | |
1069 | { | |
1070 | u8 resp[WLANTEST_CTRL_MAX_RESP_LEN]; | |
1071 | u8 buf[WLANTEST_CTRL_MAX_CMD_LEN], *end, *pos, *len_pos; | |
1072 | int rlen; | |
1073 | enum wlantest_inject_protection prot; | |
1074 | int arg; | |
1075 | ||
1076 | /* <prot> <raw frame as hex dump> */ | |
1077 | ||
1078 | if (argc < 2) { | |
1079 | printf("send needs two arguments: protected/unprotected, " | |
1080 | "raw frame as hex dump\n"); | |
1081 | return -1; | |
1082 | } | |
1083 | ||
1084 | pos = buf; | |
1085 | end = buf + sizeof(buf); | |
1086 | WPA_PUT_BE32(pos, WLANTEST_CTRL_SEND); | |
1087 | pos += 4; | |
1088 | ||
1089 | if (os_strcasecmp(argv[0], "normal") == 0) | |
1090 | prot = WLANTEST_INJECT_NORMAL; | |
1091 | else if (os_strcasecmp(argv[0], "protected") == 0) | |
1092 | prot = WLANTEST_INJECT_PROTECTED; | |
1093 | else if (os_strcasecmp(argv[0], "unprotected") == 0) | |
1094 | prot = WLANTEST_INJECT_UNPROTECTED; | |
1095 | else if (os_strcasecmp(argv[0], "incorrect") == 0) | |
1096 | prot = WLANTEST_INJECT_INCORRECT_KEY; | |
1097 | else { | |
1098 | printf("Unknown protection type '%s'\n", argv[1]); | |
1099 | printf("Protection types: normal protected unprotected " | |
1100 | "incorrect\n"); | |
1101 | return -1; | |
1102 | } | |
1103 | pos = attr_add_be32(pos, end, WLANTEST_ATTR_INJECT_PROTECTION, prot); | |
1104 | ||
1105 | WPA_PUT_BE32(pos, WLANTEST_ATTR_FRAME); | |
1106 | pos += 4; | |
1107 | len_pos = pos; | |
1108 | pos += 4; | |
1109 | ||
1110 | for (arg = 1; pos && arg < argc; arg++) | |
1111 | pos = add_hex(pos, end, argv[arg]); | |
1112 | if (pos == NULL) | |
1113 | return -1; | |
1114 | ||
1115 | WPA_PUT_BE32(len_pos, pos - len_pos - 4); | |
1116 | ||
1117 | rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp)); | |
1118 | if (rlen < 0) | |
1119 | return -1; | |
1120 | printf("OK\n"); | |
1121 | return 0; | |
1122 | } | |
1123 | ||
1124 | ||
1125 | static char ** complete_send(int s, const char *str, int pos) | |
1126 | { | |
1127 | int arg = get_cmd_arg_num(str, pos); | |
1128 | char **res = NULL; | |
1129 | ||
1130 | switch (arg) { | |
1131 | case 1: | |
f9884c09 | 1132 | res = os_calloc(5, sizeof(char *)); |
b3a6d9d4 JM |
1133 | if (res == NULL) |
1134 | break; | |
1135 | res[0] = os_strdup("normal"); | |
1136 | if (res[0] == NULL) | |
1137 | break; | |
1138 | res[1] = os_strdup("protected"); | |
1139 | if (res[1] == NULL) | |
1140 | break; | |
1141 | res[2] = os_strdup("unprotected"); | |
1142 | if (res[2] == NULL) | |
1143 | break; | |
1144 | res[3] = os_strdup("incorrect"); | |
1145 | if (res[3] == NULL) | |
1146 | break; | |
1147 | break; | |
1148 | } | |
1149 | ||
1150 | return res; | |
1151 | } | |
1152 | ||
1153 | ||
a16c8590 JM |
1154 | static int cmd_version(int s, int argc, char *argv[]) |
1155 | { | |
1156 | u8 resp[WLANTEST_CTRL_MAX_RESP_LEN]; | |
1157 | u8 buf[4]; | |
1158 | char *version; | |
1159 | size_t len; | |
1160 | int rlen, i; | |
1161 | ||
1162 | WPA_PUT_BE32(buf, WLANTEST_CTRL_VERSION); | |
1163 | rlen = cmd_send_and_recv(s, buf, sizeof(buf), resp, sizeof(resp)); | |
1164 | if (rlen < 0) | |
1165 | return -1; | |
1166 | ||
1167 | version = (char *) attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_VERSION, | |
1168 | &len); | |
1169 | if (version == NULL) | |
1170 | return -1; | |
1171 | ||
1172 | for (i = 0; i < len; i++) | |
1173 | putchar(version[i]); | |
1174 | printf("\n"); | |
1175 | ||
1176 | return 0; | |
1177 | } | |
1178 | ||
1179 | ||
fbdd2132 JM |
1180 | static int cmd_add_passphrase(int s, int argc, char *argv[]) |
1181 | { | |
1182 | u8 resp[WLANTEST_CTRL_MAX_RESP_LEN]; | |
1183 | u8 buf[100], *pos, *end; | |
1184 | size_t len; | |
1185 | int rlen; | |
1186 | ||
1187 | if (argc < 1) { | |
1188 | printf("add_passphrase needs one argument: passphrase\n"); | |
1189 | return -1; | |
1190 | } | |
1191 | ||
1192 | len = os_strlen(argv[0]); | |
1193 | if (len < 8 || len > 63) { | |
1194 | printf("Invalid passphrase '%s'\n", argv[0]); | |
1195 | return -1; | |
1196 | } | |
1197 | pos = buf; | |
1198 | end = buf + sizeof(buf); | |
1199 | WPA_PUT_BE32(pos, WLANTEST_CTRL_ADD_PASSPHRASE); | |
1200 | pos += 4; | |
1201 | pos = attr_add_str(pos, end, WLANTEST_ATTR_PASSPHRASE, | |
1202 | argv[0]); | |
1203 | if (argc > 1) { | |
1204 | pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN); | |
1205 | if (hwaddr_aton(argv[1], pos) < 0) { | |
1206 | printf("Invalid BSSID '%s'\n", argv[3]); | |
1207 | return -1; | |
1208 | } | |
1209 | pos += ETH_ALEN; | |
1210 | } | |
1211 | ||
1212 | rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp)); | |
1213 | if (rlen < 0) | |
1214 | return -1; | |
1215 | return 0; | |
1216 | } | |
1217 | ||
1218 | ||
8cb9bc19 JM |
1219 | static int cmd_add_wepkey(int s, int argc, char *argv[]) |
1220 | { | |
1221 | u8 resp[WLANTEST_CTRL_MAX_RESP_LEN]; | |
1222 | u8 buf[100], *pos, *end; | |
8cb9bc19 JM |
1223 | int rlen; |
1224 | ||
1225 | if (argc < 1) { | |
1226 | printf("add_wepkey needs one argument: WEP key\n"); | |
1227 | return -1; | |
1228 | } | |
1229 | ||
8cb9bc19 JM |
1230 | pos = buf; |
1231 | end = buf + sizeof(buf); | |
1232 | WPA_PUT_BE32(pos, WLANTEST_CTRL_ADD_PASSPHRASE); | |
1233 | pos += 4; | |
1234 | pos = attr_add_str(pos, end, WLANTEST_ATTR_WEPKEY, argv[0]); | |
1235 | ||
1236 | rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp)); | |
1237 | if (rlen < 0) | |
1238 | return -1; | |
1239 | return 0; | |
1240 | } | |
1241 | ||
1242 | ||
2fc0cd54 JM |
1243 | struct sta_infos { |
1244 | const char *name; | |
1245 | enum wlantest_sta_info num; | |
1246 | }; | |
1247 | ||
1248 | static const struct sta_infos sta_infos[] = { | |
1249 | { "proto", WLANTEST_STA_INFO_PROTO }, | |
1250 | { "pairwise", WLANTEST_STA_INFO_PAIRWISE }, | |
1251 | { "key_mgmt", WLANTEST_STA_INFO_KEY_MGMT }, | |
1252 | { "rsn_capab", WLANTEST_STA_INFO_RSN_CAPAB }, | |
1253 | { "state", WLANTEST_STA_INFO_STATE }, | |
fd848ab9 | 1254 | { "gtk", WLANTEST_STA_INFO_GTK }, |
2fc0cd54 JM |
1255 | { NULL, 0 } |
1256 | }; | |
1257 | ||
1258 | static int cmd_info_sta(int s, int argc, char *argv[]) | |
1259 | { | |
1260 | u8 resp[WLANTEST_CTRL_MAX_RESP_LEN]; | |
1261 | u8 buf[100], *end, *pos; | |
1262 | int rlen, i; | |
1263 | size_t len; | |
1264 | char info[100]; | |
1265 | ||
1266 | if (argc != 3) { | |
1267 | printf("sta_info needs at three arguments: " | |
1268 | "counter name, BSSID, and STA address\n"); | |
1269 | return -1; | |
1270 | } | |
1271 | ||
1272 | pos = buf; | |
1273 | end = buf + sizeof(buf); | |
1274 | WPA_PUT_BE32(pos, WLANTEST_CTRL_INFO_STA); | |
1275 | pos += 4; | |
1276 | ||
1277 | for (i = 0; sta_infos[i].name; i++) { | |
1278 | if (os_strcasecmp(sta_infos[i].name, argv[0]) == 0) | |
1279 | break; | |
1280 | } | |
1281 | if (sta_infos[i].name == NULL) { | |
1282 | printf("Unknown STA info '%s'\n", argv[0]); | |
1283 | printf("Info fields:"); | |
1284 | for (i = 0; sta_infos[i].name; i++) | |
1285 | printf(" %s", sta_infos[i].name); | |
1286 | printf("\n"); | |
1287 | return -1; | |
1288 | } | |
1289 | ||
1290 | pos = attr_add_be32(pos, end, WLANTEST_ATTR_STA_INFO, | |
1291 | sta_infos[i].num); | |
1292 | pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN); | |
1293 | if (hwaddr_aton(argv[1], pos) < 0) { | |
1294 | printf("Invalid BSSID '%s'\n", argv[1]); | |
1295 | return -1; | |
1296 | } | |
1297 | pos += ETH_ALEN; | |
1298 | ||
1299 | pos = attr_hdr_add(pos, end, WLANTEST_ATTR_STA_ADDR, ETH_ALEN); | |
1300 | if (hwaddr_aton(argv[2], pos) < 0) { | |
1301 | printf("Invalid STA address '%s'\n", argv[2]); | |
1302 | return -1; | |
1303 | } | |
1304 | pos += ETH_ALEN; | |
1305 | ||
1306 | rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp)); | |
1307 | if (rlen < 0) | |
1308 | return -1; | |
1309 | ||
1310 | pos = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_INFO, &len); | |
1311 | if (pos == NULL) | |
1312 | return -1; | |
1313 | if (len >= sizeof(info)) | |
1314 | len = sizeof(info) - 1; | |
1315 | os_memcpy(info, pos, len); | |
1316 | info[len] = '\0'; | |
1317 | printf("%s\n", info); | |
1318 | return 0; | |
1319 | } | |
1320 | ||
1321 | ||
1322 | static char ** complete_info_sta(int s, const char *str, int pos) | |
1323 | { | |
1324 | int arg = get_cmd_arg_num(str, pos); | |
1325 | char **res = NULL; | |
1326 | int i, count; | |
1327 | u8 addr[ETH_ALEN]; | |
1328 | ||
1329 | switch (arg) { | |
1330 | case 1: | |
1331 | /* counter list */ | |
e7ecab4a | 1332 | count = ARRAY_SIZE(sta_infos); |
f9884c09 | 1333 | res = os_calloc(count, sizeof(char *)); |
2fc0cd54 JM |
1334 | if (res == NULL) |
1335 | return NULL; | |
1336 | for (i = 0; sta_infos[i].name; i++) { | |
1337 | res[i] = os_strdup(sta_infos[i].name); | |
1338 | if (res[i] == NULL) | |
1339 | break; | |
1340 | } | |
1341 | break; | |
1342 | case 2: | |
1343 | res = get_bssid_list(s); | |
1344 | break; | |
1345 | case 3: | |
1346 | if (hwaddr_aton(&str[get_prev_arg_pos(str, pos)], addr) < 0) | |
1347 | break; | |
1348 | res = get_sta_list(s, addr, 0); | |
1349 | break; | |
1350 | } | |
1351 | ||
1352 | return res; | |
1353 | } | |
1354 | ||
1355 | ||
1356 | struct bss_infos { | |
1357 | const char *name; | |
1358 | enum wlantest_bss_info num; | |
1359 | }; | |
1360 | ||
1361 | static const struct bss_infos bss_infos[] = { | |
1362 | { "proto", WLANTEST_BSS_INFO_PROTO }, | |
1363 | { "pairwise", WLANTEST_BSS_INFO_PAIRWISE }, | |
1364 | { "group", WLANTEST_BSS_INFO_GROUP }, | |
1365 | { "group_mgmt", WLANTEST_BSS_INFO_GROUP_MGMT }, | |
1366 | { "key_mgmt", WLANTEST_BSS_INFO_KEY_MGMT }, | |
1367 | { "rsn_capab", WLANTEST_BSS_INFO_RSN_CAPAB }, | |
1368 | { NULL, 0 } | |
1369 | }; | |
1370 | ||
1371 | static int cmd_info_bss(int s, int argc, char *argv[]) | |
1372 | { | |
1373 | u8 resp[WLANTEST_CTRL_MAX_RESP_LEN]; | |
1374 | u8 buf[100], *end, *pos; | |
1375 | int rlen, i; | |
1376 | size_t len; | |
1377 | char info[100]; | |
1378 | ||
1379 | if (argc != 2) { | |
1380 | printf("bss_info needs at two arguments: " | |
1381 | "field name and BSSID\n"); | |
1382 | return -1; | |
1383 | } | |
1384 | ||
1385 | pos = buf; | |
1386 | end = buf + sizeof(buf); | |
1387 | WPA_PUT_BE32(pos, WLANTEST_CTRL_INFO_BSS); | |
1388 | pos += 4; | |
1389 | ||
1390 | for (i = 0; bss_infos[i].name; i++) { | |
1391 | if (os_strcasecmp(bss_infos[i].name, argv[0]) == 0) | |
1392 | break; | |
1393 | } | |
1394 | if (bss_infos[i].name == NULL) { | |
1395 | printf("Unknown BSS info '%s'\n", argv[0]); | |
1396 | printf("Info fields:"); | |
1397 | for (i = 0; bss_infos[i].name; i++) | |
1398 | printf(" %s", bss_infos[i].name); | |
1399 | printf("\n"); | |
1400 | return -1; | |
1401 | } | |
1402 | ||
1403 | pos = attr_add_be32(pos, end, WLANTEST_ATTR_BSS_INFO, | |
1404 | bss_infos[i].num); | |
1405 | pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN); | |
1406 | if (hwaddr_aton(argv[1], pos) < 0) { | |
1407 | printf("Invalid BSSID '%s'\n", argv[1]); | |
1408 | return -1; | |
1409 | } | |
1410 | pos += ETH_ALEN; | |
1411 | ||
1412 | rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp)); | |
1413 | if (rlen < 0) | |
1414 | return -1; | |
1415 | ||
1416 | pos = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_INFO, &len); | |
1417 | if (pos == NULL) | |
1418 | return -1; | |
1419 | if (len >= sizeof(info)) | |
1420 | len = sizeof(info) - 1; | |
1421 | os_memcpy(info, pos, len); | |
1422 | info[len] = '\0'; | |
1423 | printf("%s\n", info); | |
1424 | return 0; | |
1425 | } | |
1426 | ||
1427 | ||
1428 | static char ** complete_info_bss(int s, const char *str, int pos) | |
1429 | { | |
1430 | int arg = get_cmd_arg_num(str, pos); | |
1431 | char **res = NULL; | |
1432 | int i, count; | |
1433 | ||
1434 | switch (arg) { | |
1435 | case 1: | |
1436 | /* counter list */ | |
e7ecab4a | 1437 | count = ARRAY_SIZE(bss_infos); |
f9884c09 | 1438 | res = os_calloc(count, sizeof(char *)); |
2fc0cd54 JM |
1439 | if (res == NULL) |
1440 | return NULL; | |
1441 | for (i = 0; bss_infos[i].name; i++) { | |
1442 | res[i] = os_strdup(bss_infos[i].name); | |
1443 | if (res[i] == NULL) | |
1444 | break; | |
1445 | } | |
1446 | break; | |
1447 | case 2: | |
1448 | res = get_bssid_list(s); | |
1449 | break; | |
1450 | } | |
1451 | ||
1452 | return res; | |
1453 | } | |
1454 | ||
1455 | ||
99d7c1de JM |
1456 | static int cmd_get_tx_tid(int s, int argc, char *argv[]) |
1457 | { | |
1458 | u8 resp[WLANTEST_CTRL_MAX_RESP_LEN]; | |
1459 | u8 buf[100], *end, *pos; | |
1460 | int rlen; | |
1461 | size_t len; | |
1462 | ||
1463 | if (argc != 3) { | |
1464 | printf("get_tx_tid needs three arguments: " | |
1465 | "BSSID, STA address, and TID\n"); | |
1466 | return -1; | |
1467 | } | |
1468 | ||
1469 | pos = buf; | |
1470 | end = buf + sizeof(buf); | |
1471 | WPA_PUT_BE32(pos, WLANTEST_CTRL_GET_TX_TID); | |
1472 | pos += 4; | |
1473 | ||
1474 | pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN); | |
1475 | if (hwaddr_aton(argv[0], pos) < 0) { | |
1476 | printf("Invalid BSSID '%s'\n", argv[0]); | |
1477 | return -1; | |
1478 | } | |
1479 | pos += ETH_ALEN; | |
1480 | ||
1481 | pos = attr_hdr_add(pos, end, WLANTEST_ATTR_STA_ADDR, ETH_ALEN); | |
1482 | if (hwaddr_aton(argv[1], pos) < 0) { | |
1483 | printf("Invalid STA address '%s'\n", argv[1]); | |
1484 | return -1; | |
1485 | } | |
1486 | pos += ETH_ALEN; | |
1487 | ||
1488 | pos = attr_add_be32(pos, end, WLANTEST_ATTR_TID, atoi(argv[2])); | |
1489 | ||
1490 | rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp)); | |
1491 | if (rlen < 0) | |
1492 | return -1; | |
1493 | ||
1494 | pos = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_COUNTER, &len); | |
1495 | if (pos == NULL || len != 4) | |
1496 | return -1; | |
1497 | printf("%u\n", WPA_GET_BE32(pos)); | |
1498 | return 0; | |
1499 | } | |
1500 | ||
1501 | ||
1502 | static int cmd_get_rx_tid(int s, int argc, char *argv[]) | |
1503 | { | |
1504 | u8 resp[WLANTEST_CTRL_MAX_RESP_LEN]; | |
1505 | u8 buf[100], *end, *pos; | |
1506 | int rlen; | |
1507 | size_t len; | |
1508 | ||
1509 | if (argc != 3) { | |
1510 | printf("get_tx_tid needs three arguments: " | |
1511 | "BSSID, STA address, and TID\n"); | |
1512 | return -1; | |
1513 | } | |
1514 | ||
1515 | pos = buf; | |
1516 | end = buf + sizeof(buf); | |
1517 | WPA_PUT_BE32(pos, WLANTEST_CTRL_GET_RX_TID); | |
1518 | pos += 4; | |
1519 | ||
1520 | pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN); | |
1521 | if (hwaddr_aton(argv[0], pos) < 0) { | |
1522 | printf("Invalid BSSID '%s'\n", argv[0]); | |
1523 | return -1; | |
1524 | } | |
1525 | pos += ETH_ALEN; | |
1526 | ||
1527 | pos = attr_hdr_add(pos, end, WLANTEST_ATTR_STA_ADDR, ETH_ALEN); | |
1528 | if (hwaddr_aton(argv[1], pos) < 0) { | |
1529 | printf("Invalid STA address '%s'\n", argv[1]); | |
1530 | return -1; | |
1531 | } | |
1532 | pos += ETH_ALEN; | |
1533 | ||
1534 | pos = attr_add_be32(pos, end, WLANTEST_ATTR_TID, atoi(argv[2])); | |
1535 | ||
1536 | rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp)); | |
1537 | if (rlen < 0) | |
1538 | return -1; | |
1539 | ||
1540 | pos = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_COUNTER, &len); | |
1541 | if (pos == NULL || len != 4) | |
1542 | return -1; | |
1543 | printf("%u\n", WPA_GET_BE32(pos)); | |
1544 | return 0; | |
1545 | } | |
1546 | ||
1547 | ||
1548 | static char ** complete_get_tid(int s, const char *str, int pos) | |
1549 | { | |
1550 | int arg = get_cmd_arg_num(str, pos); | |
1551 | char **res = NULL; | |
1552 | u8 addr[ETH_ALEN]; | |
1553 | ||
1554 | switch (arg) { | |
1555 | case 1: | |
1556 | res = get_bssid_list(s); | |
1557 | break; | |
1558 | case 2: | |
1559 | if (hwaddr_aton(&str[get_prev_arg_pos(str, pos)], addr) < 0) | |
1560 | break; | |
1561 | res = get_sta_list(s, addr, 0); | |
1562 | break; | |
1563 | } | |
1564 | ||
1565 | return res; | |
1566 | } | |
1567 | ||
1568 | ||
644fb8c8 JM |
1569 | struct wlantest_cli_cmd { |
1570 | const char *cmd; | |
1571 | int (*handler)(int s, int argc, char *argv[]); | |
1572 | const char *usage; | |
79a670cb | 1573 | char ** (*complete)(int s, const char *str, int pos); |
644fb8c8 JM |
1574 | }; |
1575 | ||
1576 | static const struct wlantest_cli_cmd wlantest_cli_commands[] = { | |
79a670cb JM |
1577 | { "ping", cmd_ping, "= test connection to wlantest", NULL }, |
1578 | { "terminate", cmd_terminate, "= terminate wlantest", NULL }, | |
1579 | { "list_bss", cmd_list_bss, "= get BSS list", NULL }, | |
1580 | { "list_sta", cmd_list_sta, "<BSSID> = get STA list", | |
1581 | complete_list_sta }, | |
1582 | { "flush", cmd_flush, "= drop all collected BSS data", NULL }, | |
6d5ce9fc | 1583 | { "clear_sta_counters", cmd_clear_sta_counters, |
79a670cb | 1584 | "<BSSID> <STA> = clear STA counters", complete_clear_sta_counters }, |
6d5ce9fc | 1585 | { "clear_bss_counters", cmd_clear_bss_counters, |
79a670cb | 1586 | "<BSSID> = clear BSS counters", complete_clear_bss_counters }, |
6d5ce9fc | 1587 | { "get_sta_counter", cmd_get_sta_counter, |
79a670cb | 1588 | "<counter> <BSSID> <STA> = get STA counter value", |
2fc0cd54 | 1589 | complete_get_sta_counter }, |
6d5ce9fc | 1590 | { "get_bss_counter", cmd_get_bss_counter, |
79a670cb JM |
1591 | "<counter> <BSSID> = get BSS counter value", |
1592 | complete_get_bss_counter }, | |
7d23e971 | 1593 | { "inject", cmd_inject, |
79a670cb JM |
1594 | "<frame> <prot> <sender> <BSSID> <STA/ff:ff:ff:ff:ff:ff>", |
1595 | complete_inject }, | |
b3a6d9d4 JM |
1596 | { "send", cmd_send, |
1597 | "<prot> <raw frame as hex dump>", | |
1598 | complete_send }, | |
79a670cb | 1599 | { "version", cmd_version, "= get wlantest version", NULL }, |
fbdd2132 | 1600 | { "add_passphrase", cmd_add_passphrase, |
79a670cb | 1601 | "<passphrase> = add a known passphrase", NULL }, |
8cb9bc19 JM |
1602 | { "add_wepkey", cmd_add_wepkey, |
1603 | "<WEP key> = add a known WEP key", NULL }, | |
2fc0cd54 JM |
1604 | { "info_sta", cmd_info_sta, |
1605 | "<field> <BSSID> <STA> = get STA information", | |
1606 | complete_info_sta }, | |
1607 | { "info_bss", cmd_info_bss, | |
1608 | "<field> <BSSID> = get BSS information", | |
1609 | complete_info_bss }, | |
0e42fff3 JM |
1610 | { "clear_tdls_counters", cmd_clear_tdls_counters, |
1611 | "<BSSID> <STA1> <STA2> = clear TDLS counters", | |
1612 | complete_clear_tdls_counters }, | |
1613 | { "get_tdls_counter", cmd_get_tdls_counter, | |
1614 | "<counter> <BSSID> <STA1> <STA2> = get TDLS counter value", | |
1615 | complete_get_tdls_counter }, | |
1616 | { "get_bss_counter", cmd_get_bss_counter, | |
1617 | "<counter> <BSSID> = get BSS counter value", | |
1618 | complete_get_bss_counter }, | |
c99a721e | 1619 | { "relog", cmd_relog, "= re-open log-file (allow rolling logs)", NULL }, |
99d7c1de JM |
1620 | { "get_tx_tid", cmd_get_tx_tid, |
1621 | "<BSSID> <STA> <TID> = get STA TX TID counter value", | |
1622 | complete_get_tid }, | |
1623 | { "get_rx_tid", cmd_get_rx_tid, | |
1624 | "<BSSID> <STA> <TID> = get STA RX TID counter value", | |
1625 | complete_get_tid }, | |
79a670cb | 1626 | { NULL, NULL, NULL, NULL } |
644fb8c8 JM |
1627 | }; |
1628 | ||
1629 | ||
1630 | static int ctrl_command(int s, int argc, char *argv[]) | |
1631 | { | |
1632 | const struct wlantest_cli_cmd *cmd, *match = NULL; | |
1633 | int count = 0; | |
1634 | int ret = 0; | |
1635 | ||
1636 | for (cmd = wlantest_cli_commands; cmd->cmd; cmd++) { | |
1637 | if (os_strncasecmp(cmd->cmd, argv[0], os_strlen(argv[0])) == 0) | |
1638 | { | |
1639 | match = cmd; | |
1640 | if (os_strcasecmp(cmd->cmd, argv[0]) == 0) { | |
1641 | /* exact match */ | |
1642 | count = 1; | |
1643 | break; | |
1644 | } | |
1645 | count++; | |
1646 | } | |
1647 | } | |
1648 | ||
1649 | if (count > 1) { | |
1650 | printf("Ambiguous command '%s'; possible commands:", argv[0]); | |
1651 | for (cmd = wlantest_cli_commands; cmd->cmd; cmd++) { | |
1652 | if (os_strncasecmp(cmd->cmd, argv[0], | |
1653 | os_strlen(argv[0])) == 0) { | |
1654 | printf(" %s", cmd->cmd); | |
1655 | } | |
644fb8c8 JM |
1656 | } |
1657 | printf("\n"); | |
1658 | ret = 1; | |
1659 | } else if (count == 0) { | |
1660 | printf("Unknown command '%s'\n", argv[0]); | |
1661 | ret = 1; | |
1662 | } else { | |
1663 | ret = match->handler(s, argc - 1, &argv[1]); | |
1664 | } | |
1665 | ||
1666 | return ret; | |
1667 | } | |
1668 | ||
1669 | ||
ef49bb80 JM |
1670 | struct wlantest_cli { |
1671 | int s; | |
1672 | }; | |
1673 | ||
1674 | ||
1675 | #define max_args 10 | |
1676 | ||
1677 | static int tokenize_cmd(char *cmd, char *argv[]) | |
1678 | { | |
1679 | char *pos; | |
1680 | int argc = 0; | |
1681 | ||
1682 | pos = cmd; | |
1683 | for (;;) { | |
1684 | while (*pos == ' ') | |
1685 | pos++; | |
1686 | if (*pos == '\0') | |
1687 | break; | |
1688 | argv[argc] = pos; | |
1689 | argc++; | |
1690 | if (argc == max_args) | |
1691 | break; | |
1692 | if (*pos == '"') { | |
1693 | char *pos2 = os_strrchr(pos, '"'); | |
1694 | if (pos2) | |
1695 | pos = pos2 + 1; | |
1696 | } | |
1697 | while (*pos != '\0' && *pos != ' ') | |
1698 | pos++; | |
1699 | if (*pos == ' ') | |
1700 | *pos++ = '\0'; | |
1701 | } | |
1702 | ||
1703 | return argc; | |
1704 | } | |
1705 | ||
1706 | ||
1707 | static void wlantest_cli_edit_cmd_cb(void *ctx, char *cmd) | |
1708 | { | |
1709 | struct wlantest_cli *cli = ctx; | |
1710 | char *argv[max_args]; | |
1711 | int argc; | |
1712 | argc = tokenize_cmd(cmd, argv); | |
1713 | if (argc) { | |
1714 | int ret = ctrl_command(cli->s, argc, argv); | |
1715 | if (ret < 0) | |
1716 | printf("FAIL\n"); | |
1717 | } | |
1718 | } | |
1719 | ||
1720 | ||
1721 | static void wlantest_cli_eloop_terminate(int sig, void *signal_ctx) | |
1722 | { | |
1723 | eloop_terminate(); | |
1724 | } | |
1725 | ||
1726 | ||
1727 | static void wlantest_cli_edit_eof_cb(void *ctx) | |
1728 | { | |
1729 | eloop_terminate(); | |
1730 | } | |
1731 | ||
1732 | ||
1733 | static char ** wlantest_cli_cmd_list(void) | |
1734 | { | |
1735 | char **res; | |
e7ecab4a | 1736 | int i; |
ef49bb80 | 1737 | |
e7ecab4a | 1738 | res = os_calloc(ARRAY_SIZE(wlantest_cli_commands), sizeof(char *)); |
ef49bb80 JM |
1739 | if (res == NULL) |
1740 | return NULL; | |
1741 | ||
1742 | for (i = 0; wlantest_cli_commands[i].cmd; i++) { | |
1743 | res[i] = os_strdup(wlantest_cli_commands[i].cmd); | |
1744 | if (res[i] == NULL) | |
1745 | break; | |
1746 | } | |
1747 | ||
1748 | return res; | |
1749 | } | |
1750 | ||
1751 | ||
79a670cb JM |
1752 | static char ** wlantest_cli_cmd_completion(struct wlantest_cli *cli, |
1753 | const char *cmd, const char *str, | |
ef49bb80 JM |
1754 | int pos) |
1755 | { | |
1756 | int i; | |
1757 | ||
1758 | for (i = 0; wlantest_cli_commands[i].cmd; i++) { | |
79a670cb JM |
1759 | const struct wlantest_cli_cmd *c = &wlantest_cli_commands[i]; |
1760 | if (os_strcasecmp(c->cmd, cmd) == 0) { | |
ef49bb80 | 1761 | edit_clear_line(); |
79a670cb | 1762 | printf("\r%s\n", c->usage); |
ef49bb80 | 1763 | edit_redraw(); |
79a670cb JM |
1764 | if (c->complete) |
1765 | return c->complete(cli->s, str, pos); | |
ef49bb80 JM |
1766 | break; |
1767 | } | |
1768 | } | |
1769 | ||
1770 | return NULL; | |
1771 | } | |
1772 | ||
1773 | ||
1774 | static char ** wlantest_cli_edit_completion_cb(void *ctx, const char *str, | |
1775 | int pos) | |
1776 | { | |
79a670cb | 1777 | struct wlantest_cli *cli = ctx; |
ef49bb80 JM |
1778 | char **res; |
1779 | const char *end; | |
1780 | char *cmd; | |
1781 | ||
1782 | end = os_strchr(str, ' '); | |
1783 | if (end == NULL || str + pos < end) | |
1784 | return wlantest_cli_cmd_list(); | |
1785 | ||
1786 | cmd = os_malloc(pos + 1); | |
1787 | if (cmd == NULL) | |
1788 | return NULL; | |
1789 | os_memcpy(cmd, str, pos); | |
1790 | cmd[end - str] = '\0'; | |
79a670cb | 1791 | res = wlantest_cli_cmd_completion(cli, cmd, str, pos); |
ef49bb80 JM |
1792 | os_free(cmd); |
1793 | return res; | |
1794 | } | |
1795 | ||
1796 | ||
1797 | static void wlantest_cli_interactive(int s) | |
1798 | { | |
1799 | struct wlantest_cli cli; | |
13b9f3a1 | 1800 | char *home, *hfile = NULL; |
ef49bb80 JM |
1801 | |
1802 | if (eloop_init()) | |
1803 | return; | |
1804 | ||
13b9f3a1 JM |
1805 | home = getenv("HOME"); |
1806 | if (home) { | |
1807 | const char *fname = ".wlantest_cli_history"; | |
1808 | int hfile_len = os_strlen(home) + 1 + os_strlen(fname) + 1; | |
1809 | hfile = os_malloc(hfile_len); | |
1810 | if (hfile) | |
1811 | os_snprintf(hfile, hfile_len, "%s/%s", home, fname); | |
1812 | } | |
1813 | ||
ef49bb80 JM |
1814 | cli.s = s; |
1815 | eloop_register_signal_terminate(wlantest_cli_eloop_terminate, &cli); | |
8953e968 | 1816 | edit_init(wlantest_cli_edit_cmd_cb, wlantest_cli_edit_eof_cb, |
2d2398a1 | 1817 | wlantest_cli_edit_completion_cb, &cli, hfile, NULL); |
ef49bb80 JM |
1818 | |
1819 | eloop_run(); | |
1820 | ||
13b9f3a1 JM |
1821 | edit_deinit(hfile, NULL); |
1822 | os_free(hfile); | |
ef49bb80 JM |
1823 | eloop_destroy(); |
1824 | } | |
1825 | ||
1826 | ||
644fb8c8 JM |
1827 | int main(int argc, char *argv[]) |
1828 | { | |
1829 | int s; | |
1830 | struct sockaddr_un addr; | |
1831 | int ret = 0; | |
1832 | ||
ef49bb80 JM |
1833 | if (os_program_init()) |
1834 | return -1; | |
1835 | ||
644fb8c8 JM |
1836 | s = socket(AF_UNIX, SOCK_SEQPACKET, 0); |
1837 | if (s < 0) { | |
1838 | perror("socket"); | |
1839 | return -1; | |
1840 | } | |
1841 | ||
1842 | os_memset(&addr, 0, sizeof(addr)); | |
1843 | addr.sun_family = AF_UNIX; | |
1844 | os_strlcpy(addr.sun_path + 1, WLANTEST_SOCK_NAME, | |
1845 | sizeof(addr.sun_path) - 1); | |
1846 | if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { | |
1847 | perror("connect"); | |
1848 | close(s); | |
1849 | return -1; | |
1850 | } | |
1851 | ||
1852 | if (argc > 1) { | |
1853 | ret = ctrl_command(s, argc - 1, &argv[1]); | |
1854 | if (ret < 0) | |
1855 | printf("FAIL\n"); | |
1856 | } else { | |
ef49bb80 | 1857 | wlantest_cli_interactive(s); |
644fb8c8 JM |
1858 | } |
1859 | ||
1860 | close(s); | |
ef49bb80 JM |
1861 | |
1862 | os_program_deinit(); | |
1863 | ||
644fb8c8 JM |
1864 | return ret; |
1865 | } |