]>
Commit | Line | Data |
---|---|---|
644fb8c8 JM |
1 | /* |
2 | * wlantest controller | |
3 | * Copyright (c) 2010, Jouni Malinen <j@w1.fi> | |
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 | ||
173 | res = os_zalloc((len / ETH_ALEN + 1) * sizeof(char *)); | |
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 | ||
211 | res = os_zalloc((len / ETH_ALEN + 1 + add_bcast) * sizeof(char *)); | |
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 */ | |
625 | count = sizeof(sta_counters) / sizeof(sta_counters[0]); | |
626 | res = os_zalloc(count * sizeof(char *)); | |
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 }, | |
6d5ce9fc JM |
660 | { NULL, 0 } |
661 | }; | |
662 | ||
663 | static int cmd_get_bss_counter(int s, int argc, char *argv[]) | |
664 | { | |
665 | u8 resp[WLANTEST_CTRL_MAX_RESP_LEN]; | |
666 | u8 buf[100], *end, *pos; | |
667 | int rlen, i; | |
668 | size_t len; | |
669 | ||
670 | if (argc != 2) { | |
2fc0cd54 | 671 | printf("get_bss_counter needs at two arguments: " |
6d5ce9fc JM |
672 | "counter name and BSSID\n"); |
673 | return -1; | |
674 | } | |
675 | ||
676 | pos = buf; | |
677 | end = buf + sizeof(buf); | |
678 | WPA_PUT_BE32(pos, WLANTEST_CTRL_GET_BSS_COUNTER); | |
679 | pos += 4; | |
680 | ||
681 | for (i = 0; bss_counters[i].name; i++) { | |
682 | if (os_strcasecmp(bss_counters[i].name, argv[0]) == 0) | |
683 | break; | |
684 | } | |
685 | if (bss_counters[i].name == NULL) { | |
686 | printf("Unknown BSS counter '%s'\n", argv[0]); | |
687 | printf("Counters:"); | |
688 | for (i = 0; bss_counters[i].name; i++) | |
689 | printf(" %s", bss_counters[i].name); | |
690 | printf("\n"); | |
691 | return -1; | |
692 | } | |
693 | ||
694 | pos = attr_add_be32(pos, end, WLANTEST_ATTR_BSS_COUNTER, | |
695 | bss_counters[i].num); | |
696 | pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN); | |
697 | if (hwaddr_aton(argv[1], pos) < 0) { | |
698 | printf("Invalid BSSID '%s'\n", argv[1]); | |
699 | return -1; | |
700 | } | |
701 | pos += ETH_ALEN; | |
702 | ||
703 | rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp)); | |
704 | if (rlen < 0) | |
705 | return -1; | |
706 | ||
707 | pos = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_COUNTER, &len); | |
708 | if (pos == NULL || len != 4) | |
709 | return -1; | |
710 | printf("%u\n", WPA_GET_BE32(pos)); | |
711 | return 0; | |
712 | } | |
713 | ||
714 | ||
79a670cb JM |
715 | static char ** complete_get_bss_counter(int s, const char *str, int pos) |
716 | { | |
717 | int arg = get_cmd_arg_num(str, pos); | |
718 | char **res = NULL; | |
719 | int i, count; | |
720 | ||
721 | switch (arg) { | |
722 | case 1: | |
723 | /* counter list */ | |
724 | count = sizeof(bss_counters) / sizeof(bss_counters[0]); | |
725 | res = os_zalloc(count * sizeof(char *)); | |
726 | if (res == NULL) | |
727 | return NULL; | |
728 | for (i = 0; bss_counters[i].name; i++) { | |
729 | res[i] = os_strdup(bss_counters[i].name); | |
730 | if (res[i] == NULL) | |
731 | break; | |
732 | } | |
733 | break; | |
734 | case 2: | |
735 | res = get_bssid_list(s); | |
736 | break; | |
737 | } | |
738 | ||
739 | return res; | |
740 | } | |
741 | ||
742 | ||
0e42fff3 JM |
743 | struct tdls_counters { |
744 | const char *name; | |
745 | enum wlantest_tdls_counter num; | |
746 | }; | |
747 | ||
748 | static const struct tdls_counters tdls_counters[] = { | |
749 | { "valid_direct_link", WLANTEST_TDLS_COUNTER_VALID_DIRECT_LINK }, | |
750 | { "invalid_direct_link", WLANTEST_TDLS_COUNTER_INVALID_DIRECT_LINK }, | |
751 | { "valid_ap_path", WLANTEST_TDLS_COUNTER_VALID_AP_PATH }, | |
752 | { "invalid_ap_path", WLANTEST_TDLS_COUNTER_INVALID_AP_PATH }, | |
eb4923fd JM |
753 | { "setup_req", WLANTEST_TDLS_COUNTER_SETUP_REQ }, |
754 | { "setup_resp_ok", WLANTEST_TDLS_COUNTER_SETUP_RESP_OK }, | |
755 | { "setup_resp_fail", WLANTEST_TDLS_COUNTER_SETUP_RESP_FAIL }, | |
756 | { "setup_conf_ok", WLANTEST_TDLS_COUNTER_SETUP_CONF_OK }, | |
757 | { "setup_conf_fail", WLANTEST_TDLS_COUNTER_SETUP_CONF_FAIL }, | |
5acf56f6 | 758 | { "teardown", WLANTEST_TDLS_COUNTER_TEARDOWN }, |
0e42fff3 JM |
759 | { NULL, 0 } |
760 | }; | |
761 | ||
762 | static int cmd_get_tdls_counter(int s, int argc, char *argv[]) | |
763 | { | |
764 | u8 resp[WLANTEST_CTRL_MAX_RESP_LEN]; | |
765 | u8 buf[100], *end, *pos; | |
766 | int rlen, i; | |
767 | size_t len; | |
768 | ||
769 | if (argc != 4) { | |
770 | printf("get_tdls_counter needs four arguments: " | |
771 | "counter name, BSSID, STA1 address, STA2 address\n"); | |
772 | return -1; | |
773 | } | |
774 | ||
775 | pos = buf; | |
776 | end = buf + sizeof(buf); | |
777 | WPA_PUT_BE32(pos, WLANTEST_CTRL_GET_TDLS_COUNTER); | |
778 | pos += 4; | |
779 | ||
780 | for (i = 0; tdls_counters[i].name; i++) { | |
781 | if (os_strcasecmp(tdls_counters[i].name, argv[0]) == 0) | |
782 | break; | |
783 | } | |
784 | if (tdls_counters[i].name == NULL) { | |
785 | printf("Unknown TDLS counter '%s'\n", argv[0]); | |
786 | printf("Counters:"); | |
787 | for (i = 0; tdls_counters[i].name; i++) | |
788 | printf(" %s", tdls_counters[i].name); | |
789 | printf("\n"); | |
790 | return -1; | |
791 | } | |
792 | ||
793 | pos = attr_add_be32(pos, end, WLANTEST_ATTR_TDLS_COUNTER, | |
794 | tdls_counters[i].num); | |
795 | pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN); | |
796 | if (hwaddr_aton(argv[1], pos) < 0) { | |
797 | printf("Invalid BSSID '%s'\n", argv[1]); | |
798 | return -1; | |
799 | } | |
800 | pos += ETH_ALEN; | |
801 | ||
802 | pos = attr_hdr_add(pos, end, WLANTEST_ATTR_STA_ADDR, ETH_ALEN); | |
803 | if (hwaddr_aton(argv[2], pos) < 0) { | |
804 | printf("Invalid STA1 address '%s'\n", argv[2]); | |
805 | return -1; | |
806 | } | |
807 | pos += ETH_ALEN; | |
808 | ||
809 | pos = attr_hdr_add(pos, end, WLANTEST_ATTR_STA2_ADDR, ETH_ALEN); | |
810 | if (hwaddr_aton(argv[3], pos) < 0) { | |
811 | printf("Invalid STA2 address '%s'\n", argv[3]); | |
812 | return -1; | |
813 | } | |
814 | pos += ETH_ALEN; | |
815 | ||
816 | rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp)); | |
817 | if (rlen < 0) | |
818 | return -1; | |
819 | ||
820 | pos = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_COUNTER, &len); | |
821 | if (pos == NULL || len != 4) | |
822 | return -1; | |
823 | printf("%u\n", WPA_GET_BE32(pos)); | |
824 | return 0; | |
825 | } | |
826 | ||
827 | ||
828 | static char ** complete_get_tdls_counter(int s, const char *str, int pos) | |
829 | { | |
830 | int arg = get_cmd_arg_num(str, pos); | |
831 | char **res = NULL; | |
832 | int i, count; | |
833 | u8 addr[ETH_ALEN]; | |
834 | ||
835 | switch (arg) { | |
836 | case 1: | |
837 | /* counter list */ | |
838 | count = sizeof(tdls_counters) / sizeof(tdls_counters[0]); | |
839 | res = os_zalloc(count * sizeof(char *)); | |
840 | if (res == NULL) | |
841 | return NULL; | |
842 | for (i = 0; tdls_counters[i].name; i++) { | |
843 | res[i] = os_strdup(tdls_counters[i].name); | |
844 | if (res[i] == NULL) | |
845 | break; | |
846 | } | |
847 | break; | |
848 | case 2: | |
849 | res = get_bssid_list(s); | |
850 | break; | |
851 | case 3: | |
852 | case 4: | |
853 | if (hwaddr_aton(&str[get_prev_arg_pos(str, pos)], addr) < 0) | |
854 | break; | |
855 | res = get_sta_list(s, addr, 0); | |
856 | break; | |
857 | } | |
858 | ||
859 | return res; | |
860 | } | |
861 | ||
862 | ||
7d23e971 JM |
863 | struct inject_frames { |
864 | const char *name; | |
865 | enum wlantest_inject_frame frame; | |
866 | }; | |
867 | ||
868 | static const struct inject_frames inject_frames[] = { | |
869 | { "auth", WLANTEST_FRAME_AUTH }, | |
870 | { "assocreq", WLANTEST_FRAME_ASSOCREQ }, | |
871 | { "reassocreq", WLANTEST_FRAME_REASSOCREQ }, | |
872 | { "deauth", WLANTEST_FRAME_DEAUTH }, | |
873 | { "disassoc", WLANTEST_FRAME_DISASSOC }, | |
874 | { "saqueryreq", WLANTEST_FRAME_SAQUERYREQ }, | |
875 | { NULL, 0 } | |
876 | }; | |
877 | ||
878 | static int cmd_inject(int s, int argc, char *argv[]) | |
879 | { | |
880 | u8 resp[WLANTEST_CTRL_MAX_RESP_LEN]; | |
881 | u8 buf[100], *end, *pos; | |
882 | int rlen, i; | |
883 | enum wlantest_inject_protection prot; | |
884 | ||
885 | /* <frame> <prot> <sender> <BSSID> <STA/ff:ff:ff:ff:ff:ff> */ | |
886 | ||
887 | if (argc < 5) { | |
888 | printf("inject needs five arguments: frame, protection, " | |
889 | "sender, BSSID, STA/ff:ff:ff:ff:ff:ff\n"); | |
890 | return -1; | |
891 | } | |
892 | ||
893 | pos = buf; | |
894 | end = buf + sizeof(buf); | |
895 | WPA_PUT_BE32(pos, WLANTEST_CTRL_INJECT); | |
896 | pos += 4; | |
897 | ||
898 | for (i = 0; inject_frames[i].name; i++) { | |
899 | if (os_strcasecmp(inject_frames[i].name, argv[0]) == 0) | |
900 | break; | |
901 | } | |
902 | if (inject_frames[i].name == NULL) { | |
903 | printf("Unknown inject frame '%s'\n", argv[0]); | |
904 | printf("Frames:"); | |
905 | for (i = 0; inject_frames[i].name; i++) | |
906 | printf(" %s", inject_frames[i].name); | |
907 | printf("\n"); | |
908 | return -1; | |
909 | } | |
910 | ||
911 | pos = attr_add_be32(pos, end, WLANTEST_ATTR_INJECT_FRAME, | |
912 | inject_frames[i].frame); | |
913 | ||
914 | if (os_strcasecmp(argv[1], "normal") == 0) | |
915 | prot = WLANTEST_INJECT_NORMAL; | |
916 | else if (os_strcasecmp(argv[1], "protected") == 0) | |
917 | prot = WLANTEST_INJECT_PROTECTED; | |
918 | else if (os_strcasecmp(argv[1], "unprotected") == 0) | |
919 | prot = WLANTEST_INJECT_UNPROTECTED; | |
920 | else if (os_strcasecmp(argv[1], "incorrect") == 0) | |
921 | prot = WLANTEST_INJECT_INCORRECT_KEY; | |
922 | else { | |
923 | printf("Unknown protection type '%s'\n", argv[1]); | |
924 | printf("Protection types: normal protected unprotected " | |
925 | "incorrect\n"); | |
926 | return -1; | |
927 | } | |
928 | pos = attr_add_be32(pos, end, WLANTEST_ATTR_INJECT_PROTECTION, prot); | |
929 | ||
930 | if (os_strcasecmp(argv[2], "ap") == 0) { | |
931 | pos = attr_add_be32(pos, end, WLANTEST_ATTR_INJECT_SENDER_AP, | |
932 | 1); | |
933 | } else if (os_strcasecmp(argv[2], "sta") == 0) { | |
934 | pos = attr_add_be32(pos, end, WLANTEST_ATTR_INJECT_SENDER_AP, | |
935 | 0); | |
936 | } else { | |
937 | printf("Unknown sender '%s'\n", argv[2]); | |
938 | printf("Sender types: ap sta\n"); | |
939 | return -1; | |
940 | } | |
941 | ||
942 | pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN); | |
943 | if (hwaddr_aton(argv[3], pos) < 0) { | |
944 | printf("Invalid BSSID '%s'\n", argv[3]); | |
945 | return -1; | |
946 | } | |
947 | pos += ETH_ALEN; | |
948 | ||
949 | pos = attr_hdr_add(pos, end, WLANTEST_ATTR_STA_ADDR, ETH_ALEN); | |
950 | if (hwaddr_aton(argv[4], pos) < 0) { | |
951 | printf("Invalid STA '%s'\n", argv[4]); | |
952 | return -1; | |
953 | } | |
954 | pos += ETH_ALEN; | |
955 | ||
956 | rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp)); | |
957 | if (rlen < 0) | |
958 | return -1; | |
959 | printf("OK\n"); | |
960 | return 0; | |
961 | } | |
962 | ||
963 | ||
79a670cb JM |
964 | static char ** complete_inject(int s, const char *str, int pos) |
965 | { | |
966 | int arg = get_cmd_arg_num(str, pos); | |
967 | char **res = NULL; | |
968 | int i, count; | |
969 | u8 addr[ETH_ALEN]; | |
970 | ||
971 | switch (arg) { | |
972 | case 1: | |
973 | /* frame list */ | |
974 | count = sizeof(inject_frames) / sizeof(inject_frames[0]); | |
975 | res = os_zalloc(count * sizeof(char *)); | |
976 | if (res == NULL) | |
977 | break; | |
978 | for (i = 0; inject_frames[i].name; i++) { | |
979 | res[i] = os_strdup(inject_frames[i].name); | |
980 | if (res[i] == NULL) | |
981 | break; | |
982 | } | |
983 | break; | |
984 | case 2: | |
985 | res = os_zalloc(5 * sizeof(char *)); | |
986 | if (res == NULL) | |
987 | break; | |
988 | res[0] = os_strdup("normal"); | |
989 | if (res[0] == NULL) | |
990 | break; | |
991 | res[1] = os_strdup("protected"); | |
992 | if (res[1] == NULL) | |
993 | break; | |
994 | res[2] = os_strdup("unprotected"); | |
995 | if (res[2] == NULL) | |
996 | break; | |
997 | res[3] = os_strdup("incorrect"); | |
998 | if (res[3] == NULL) | |
999 | break; | |
1000 | break; | |
1001 | case 3: | |
1002 | res = os_zalloc(3 * sizeof(char *)); | |
1003 | if (res == NULL) | |
1004 | break; | |
1005 | res[0] = os_strdup("ap"); | |
1006 | if (res[0] == NULL) | |
1007 | break; | |
1008 | res[1] = os_strdup("sta"); | |
1009 | if (res[1] == NULL) | |
1010 | break; | |
1011 | break; | |
1012 | case 4: | |
1013 | res = get_bssid_list(s); | |
1014 | break; | |
1015 | case 5: | |
1016 | if (hwaddr_aton(&str[get_prev_arg_pos(str, pos)], addr) < 0) | |
1017 | break; | |
1018 | res = get_sta_list(s, addr, 1); | |
1019 | break; | |
1020 | } | |
1021 | ||
1022 | return res; | |
1023 | } | |
1024 | ||
1025 | ||
b3a6d9d4 JM |
1026 | static u8 * add_hex(u8 *pos, u8 *end, const char *str) |
1027 | { | |
1028 | const char *s; | |
1029 | int val; | |
1030 | ||
1031 | s = str; | |
1032 | while (*s) { | |
1033 | while (*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n' || | |
1034 | *s == ':') | |
1035 | s++; | |
1036 | if (*s == '\0') | |
1037 | break; | |
1038 | if (*s == '#') { | |
1039 | while (*s != '\0' && *s != '\r' && *s != '\n') | |
1040 | s++; | |
1041 | continue; | |
1042 | } | |
1043 | ||
1044 | val = hex2byte(s); | |
1045 | if (val < 0) { | |
1046 | printf("Invalid hex encoding '%s'\n", s); | |
1047 | return NULL; | |
1048 | } | |
1049 | if (pos == end) { | |
1050 | printf("Too long frame\n"); | |
1051 | return NULL; | |
1052 | } | |
1053 | *pos++ = val; | |
1054 | s += 2; | |
1055 | } | |
1056 | ||
1057 | return pos; | |
1058 | } | |
1059 | ||
1060 | ||
1061 | static int cmd_send(int s, int argc, char *argv[]) | |
1062 | { | |
1063 | u8 resp[WLANTEST_CTRL_MAX_RESP_LEN]; | |
1064 | u8 buf[WLANTEST_CTRL_MAX_CMD_LEN], *end, *pos, *len_pos; | |
1065 | int rlen; | |
1066 | enum wlantest_inject_protection prot; | |
1067 | int arg; | |
1068 | ||
1069 | /* <prot> <raw frame as hex dump> */ | |
1070 | ||
1071 | if (argc < 2) { | |
1072 | printf("send needs two arguments: protected/unprotected, " | |
1073 | "raw frame as hex dump\n"); | |
1074 | return -1; | |
1075 | } | |
1076 | ||
1077 | pos = buf; | |
1078 | end = buf + sizeof(buf); | |
1079 | WPA_PUT_BE32(pos, WLANTEST_CTRL_SEND); | |
1080 | pos += 4; | |
1081 | ||
1082 | if (os_strcasecmp(argv[0], "normal") == 0) | |
1083 | prot = WLANTEST_INJECT_NORMAL; | |
1084 | else if (os_strcasecmp(argv[0], "protected") == 0) | |
1085 | prot = WLANTEST_INJECT_PROTECTED; | |
1086 | else if (os_strcasecmp(argv[0], "unprotected") == 0) | |
1087 | prot = WLANTEST_INJECT_UNPROTECTED; | |
1088 | else if (os_strcasecmp(argv[0], "incorrect") == 0) | |
1089 | prot = WLANTEST_INJECT_INCORRECT_KEY; | |
1090 | else { | |
1091 | printf("Unknown protection type '%s'\n", argv[1]); | |
1092 | printf("Protection types: normal protected unprotected " | |
1093 | "incorrect\n"); | |
1094 | return -1; | |
1095 | } | |
1096 | pos = attr_add_be32(pos, end, WLANTEST_ATTR_INJECT_PROTECTION, prot); | |
1097 | ||
1098 | WPA_PUT_BE32(pos, WLANTEST_ATTR_FRAME); | |
1099 | pos += 4; | |
1100 | len_pos = pos; | |
1101 | pos += 4; | |
1102 | ||
1103 | for (arg = 1; pos && arg < argc; arg++) | |
1104 | pos = add_hex(pos, end, argv[arg]); | |
1105 | if (pos == NULL) | |
1106 | return -1; | |
1107 | ||
1108 | WPA_PUT_BE32(len_pos, pos - len_pos - 4); | |
1109 | ||
1110 | rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp)); | |
1111 | if (rlen < 0) | |
1112 | return -1; | |
1113 | printf("OK\n"); | |
1114 | return 0; | |
1115 | } | |
1116 | ||
1117 | ||
1118 | static char ** complete_send(int s, const char *str, int pos) | |
1119 | { | |
1120 | int arg = get_cmd_arg_num(str, pos); | |
1121 | char **res = NULL; | |
1122 | ||
1123 | switch (arg) { | |
1124 | case 1: | |
1125 | res = os_zalloc(5 * sizeof(char *)); | |
1126 | if (res == NULL) | |
1127 | break; | |
1128 | res[0] = os_strdup("normal"); | |
1129 | if (res[0] == NULL) | |
1130 | break; | |
1131 | res[1] = os_strdup("protected"); | |
1132 | if (res[1] == NULL) | |
1133 | break; | |
1134 | res[2] = os_strdup("unprotected"); | |
1135 | if (res[2] == NULL) | |
1136 | break; | |
1137 | res[3] = os_strdup("incorrect"); | |
1138 | if (res[3] == NULL) | |
1139 | break; | |
1140 | break; | |
1141 | } | |
1142 | ||
1143 | return res; | |
1144 | } | |
1145 | ||
1146 | ||
a16c8590 JM |
1147 | static int cmd_version(int s, int argc, char *argv[]) |
1148 | { | |
1149 | u8 resp[WLANTEST_CTRL_MAX_RESP_LEN]; | |
1150 | u8 buf[4]; | |
1151 | char *version; | |
1152 | size_t len; | |
1153 | int rlen, i; | |
1154 | ||
1155 | WPA_PUT_BE32(buf, WLANTEST_CTRL_VERSION); | |
1156 | rlen = cmd_send_and_recv(s, buf, sizeof(buf), resp, sizeof(resp)); | |
1157 | if (rlen < 0) | |
1158 | return -1; | |
1159 | ||
1160 | version = (char *) attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_VERSION, | |
1161 | &len); | |
1162 | if (version == NULL) | |
1163 | return -1; | |
1164 | ||
1165 | for (i = 0; i < len; i++) | |
1166 | putchar(version[i]); | |
1167 | printf("\n"); | |
1168 | ||
1169 | return 0; | |
1170 | } | |
1171 | ||
1172 | ||
fbdd2132 JM |
1173 | static int cmd_add_passphrase(int s, int argc, char *argv[]) |
1174 | { | |
1175 | u8 resp[WLANTEST_CTRL_MAX_RESP_LEN]; | |
1176 | u8 buf[100], *pos, *end; | |
1177 | size_t len; | |
1178 | int rlen; | |
1179 | ||
1180 | if (argc < 1) { | |
1181 | printf("add_passphrase needs one argument: passphrase\n"); | |
1182 | return -1; | |
1183 | } | |
1184 | ||
1185 | len = os_strlen(argv[0]); | |
1186 | if (len < 8 || len > 63) { | |
1187 | printf("Invalid passphrase '%s'\n", argv[0]); | |
1188 | return -1; | |
1189 | } | |
1190 | pos = buf; | |
1191 | end = buf + sizeof(buf); | |
1192 | WPA_PUT_BE32(pos, WLANTEST_CTRL_ADD_PASSPHRASE); | |
1193 | pos += 4; | |
1194 | pos = attr_add_str(pos, end, WLANTEST_ATTR_PASSPHRASE, | |
1195 | argv[0]); | |
1196 | if (argc > 1) { | |
1197 | pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN); | |
1198 | if (hwaddr_aton(argv[1], pos) < 0) { | |
1199 | printf("Invalid BSSID '%s'\n", argv[3]); | |
1200 | return -1; | |
1201 | } | |
1202 | pos += ETH_ALEN; | |
1203 | } | |
1204 | ||
1205 | rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp)); | |
1206 | if (rlen < 0) | |
1207 | return -1; | |
1208 | return 0; | |
1209 | } | |
1210 | ||
1211 | ||
2fc0cd54 JM |
1212 | struct sta_infos { |
1213 | const char *name; | |
1214 | enum wlantest_sta_info num; | |
1215 | }; | |
1216 | ||
1217 | static const struct sta_infos sta_infos[] = { | |
1218 | { "proto", WLANTEST_STA_INFO_PROTO }, | |
1219 | { "pairwise", WLANTEST_STA_INFO_PAIRWISE }, | |
1220 | { "key_mgmt", WLANTEST_STA_INFO_KEY_MGMT }, | |
1221 | { "rsn_capab", WLANTEST_STA_INFO_RSN_CAPAB }, | |
1222 | { "state", WLANTEST_STA_INFO_STATE }, | |
fd848ab9 | 1223 | { "gtk", WLANTEST_STA_INFO_GTK }, |
2fc0cd54 JM |
1224 | { NULL, 0 } |
1225 | }; | |
1226 | ||
1227 | static int cmd_info_sta(int s, int argc, char *argv[]) | |
1228 | { | |
1229 | u8 resp[WLANTEST_CTRL_MAX_RESP_LEN]; | |
1230 | u8 buf[100], *end, *pos; | |
1231 | int rlen, i; | |
1232 | size_t len; | |
1233 | char info[100]; | |
1234 | ||
1235 | if (argc != 3) { | |
1236 | printf("sta_info needs at three arguments: " | |
1237 | "counter name, BSSID, and STA address\n"); | |
1238 | return -1; | |
1239 | } | |
1240 | ||
1241 | pos = buf; | |
1242 | end = buf + sizeof(buf); | |
1243 | WPA_PUT_BE32(pos, WLANTEST_CTRL_INFO_STA); | |
1244 | pos += 4; | |
1245 | ||
1246 | for (i = 0; sta_infos[i].name; i++) { | |
1247 | if (os_strcasecmp(sta_infos[i].name, argv[0]) == 0) | |
1248 | break; | |
1249 | } | |
1250 | if (sta_infos[i].name == NULL) { | |
1251 | printf("Unknown STA info '%s'\n", argv[0]); | |
1252 | printf("Info fields:"); | |
1253 | for (i = 0; sta_infos[i].name; i++) | |
1254 | printf(" %s", sta_infos[i].name); | |
1255 | printf("\n"); | |
1256 | return -1; | |
1257 | } | |
1258 | ||
1259 | pos = attr_add_be32(pos, end, WLANTEST_ATTR_STA_INFO, | |
1260 | sta_infos[i].num); | |
1261 | pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN); | |
1262 | if (hwaddr_aton(argv[1], pos) < 0) { | |
1263 | printf("Invalid BSSID '%s'\n", argv[1]); | |
1264 | return -1; | |
1265 | } | |
1266 | pos += ETH_ALEN; | |
1267 | ||
1268 | pos = attr_hdr_add(pos, end, WLANTEST_ATTR_STA_ADDR, ETH_ALEN); | |
1269 | if (hwaddr_aton(argv[2], pos) < 0) { | |
1270 | printf("Invalid STA address '%s'\n", argv[2]); | |
1271 | return -1; | |
1272 | } | |
1273 | pos += ETH_ALEN; | |
1274 | ||
1275 | rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp)); | |
1276 | if (rlen < 0) | |
1277 | return -1; | |
1278 | ||
1279 | pos = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_INFO, &len); | |
1280 | if (pos == NULL) | |
1281 | return -1; | |
1282 | if (len >= sizeof(info)) | |
1283 | len = sizeof(info) - 1; | |
1284 | os_memcpy(info, pos, len); | |
1285 | info[len] = '\0'; | |
1286 | printf("%s\n", info); | |
1287 | return 0; | |
1288 | } | |
1289 | ||
1290 | ||
1291 | static char ** complete_info_sta(int s, const char *str, int pos) | |
1292 | { | |
1293 | int arg = get_cmd_arg_num(str, pos); | |
1294 | char **res = NULL; | |
1295 | int i, count; | |
1296 | u8 addr[ETH_ALEN]; | |
1297 | ||
1298 | switch (arg) { | |
1299 | case 1: | |
1300 | /* counter list */ | |
1301 | count = sizeof(sta_infos) / sizeof(sta_infos[0]); | |
1302 | res = os_zalloc(count * sizeof(char *)); | |
1303 | if (res == NULL) | |
1304 | return NULL; | |
1305 | for (i = 0; sta_infos[i].name; i++) { | |
1306 | res[i] = os_strdup(sta_infos[i].name); | |
1307 | if (res[i] == NULL) | |
1308 | break; | |
1309 | } | |
1310 | break; | |
1311 | case 2: | |
1312 | res = get_bssid_list(s); | |
1313 | break; | |
1314 | case 3: | |
1315 | if (hwaddr_aton(&str[get_prev_arg_pos(str, pos)], addr) < 0) | |
1316 | break; | |
1317 | res = get_sta_list(s, addr, 0); | |
1318 | break; | |
1319 | } | |
1320 | ||
1321 | return res; | |
1322 | } | |
1323 | ||
1324 | ||
1325 | struct bss_infos { | |
1326 | const char *name; | |
1327 | enum wlantest_bss_info num; | |
1328 | }; | |
1329 | ||
1330 | static const struct bss_infos bss_infos[] = { | |
1331 | { "proto", WLANTEST_BSS_INFO_PROTO }, | |
1332 | { "pairwise", WLANTEST_BSS_INFO_PAIRWISE }, | |
1333 | { "group", WLANTEST_BSS_INFO_GROUP }, | |
1334 | { "group_mgmt", WLANTEST_BSS_INFO_GROUP_MGMT }, | |
1335 | { "key_mgmt", WLANTEST_BSS_INFO_KEY_MGMT }, | |
1336 | { "rsn_capab", WLANTEST_BSS_INFO_RSN_CAPAB }, | |
1337 | { NULL, 0 } | |
1338 | }; | |
1339 | ||
1340 | static int cmd_info_bss(int s, int argc, char *argv[]) | |
1341 | { | |
1342 | u8 resp[WLANTEST_CTRL_MAX_RESP_LEN]; | |
1343 | u8 buf[100], *end, *pos; | |
1344 | int rlen, i; | |
1345 | size_t len; | |
1346 | char info[100]; | |
1347 | ||
1348 | if (argc != 2) { | |
1349 | printf("bss_info needs at two arguments: " | |
1350 | "field name and BSSID\n"); | |
1351 | return -1; | |
1352 | } | |
1353 | ||
1354 | pos = buf; | |
1355 | end = buf + sizeof(buf); | |
1356 | WPA_PUT_BE32(pos, WLANTEST_CTRL_INFO_BSS); | |
1357 | pos += 4; | |
1358 | ||
1359 | for (i = 0; bss_infos[i].name; i++) { | |
1360 | if (os_strcasecmp(bss_infos[i].name, argv[0]) == 0) | |
1361 | break; | |
1362 | } | |
1363 | if (bss_infos[i].name == NULL) { | |
1364 | printf("Unknown BSS info '%s'\n", argv[0]); | |
1365 | printf("Info fields:"); | |
1366 | for (i = 0; bss_infos[i].name; i++) | |
1367 | printf(" %s", bss_infos[i].name); | |
1368 | printf("\n"); | |
1369 | return -1; | |
1370 | } | |
1371 | ||
1372 | pos = attr_add_be32(pos, end, WLANTEST_ATTR_BSS_INFO, | |
1373 | bss_infos[i].num); | |
1374 | pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN); | |
1375 | if (hwaddr_aton(argv[1], pos) < 0) { | |
1376 | printf("Invalid BSSID '%s'\n", argv[1]); | |
1377 | return -1; | |
1378 | } | |
1379 | pos += ETH_ALEN; | |
1380 | ||
1381 | rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp)); | |
1382 | if (rlen < 0) | |
1383 | return -1; | |
1384 | ||
1385 | pos = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_INFO, &len); | |
1386 | if (pos == NULL) | |
1387 | return -1; | |
1388 | if (len >= sizeof(info)) | |
1389 | len = sizeof(info) - 1; | |
1390 | os_memcpy(info, pos, len); | |
1391 | info[len] = '\0'; | |
1392 | printf("%s\n", info); | |
1393 | return 0; | |
1394 | } | |
1395 | ||
1396 | ||
1397 | static char ** complete_info_bss(int s, const char *str, int pos) | |
1398 | { | |
1399 | int arg = get_cmd_arg_num(str, pos); | |
1400 | char **res = NULL; | |
1401 | int i, count; | |
1402 | ||
1403 | switch (arg) { | |
1404 | case 1: | |
1405 | /* counter list */ | |
1406 | count = sizeof(bss_infos) / sizeof(bss_infos[0]); | |
1407 | res = os_zalloc(count * sizeof(char *)); | |
1408 | if (res == NULL) | |
1409 | return NULL; | |
1410 | for (i = 0; bss_infos[i].name; i++) { | |
1411 | res[i] = os_strdup(bss_infos[i].name); | |
1412 | if (res[i] == NULL) | |
1413 | break; | |
1414 | } | |
1415 | break; | |
1416 | case 2: | |
1417 | res = get_bssid_list(s); | |
1418 | break; | |
1419 | } | |
1420 | ||
1421 | return res; | |
1422 | } | |
1423 | ||
1424 | ||
644fb8c8 JM |
1425 | struct wlantest_cli_cmd { |
1426 | const char *cmd; | |
1427 | int (*handler)(int s, int argc, char *argv[]); | |
1428 | const char *usage; | |
79a670cb | 1429 | char ** (*complete)(int s, const char *str, int pos); |
644fb8c8 JM |
1430 | }; |
1431 | ||
1432 | static const struct wlantest_cli_cmd wlantest_cli_commands[] = { | |
79a670cb JM |
1433 | { "ping", cmd_ping, "= test connection to wlantest", NULL }, |
1434 | { "terminate", cmd_terminate, "= terminate wlantest", NULL }, | |
1435 | { "list_bss", cmd_list_bss, "= get BSS list", NULL }, | |
1436 | { "list_sta", cmd_list_sta, "<BSSID> = get STA list", | |
1437 | complete_list_sta }, | |
1438 | { "flush", cmd_flush, "= drop all collected BSS data", NULL }, | |
6d5ce9fc | 1439 | { "clear_sta_counters", cmd_clear_sta_counters, |
79a670cb | 1440 | "<BSSID> <STA> = clear STA counters", complete_clear_sta_counters }, |
6d5ce9fc | 1441 | { "clear_bss_counters", cmd_clear_bss_counters, |
79a670cb | 1442 | "<BSSID> = clear BSS counters", complete_clear_bss_counters }, |
6d5ce9fc | 1443 | { "get_sta_counter", cmd_get_sta_counter, |
79a670cb | 1444 | "<counter> <BSSID> <STA> = get STA counter value", |
2fc0cd54 | 1445 | complete_get_sta_counter }, |
6d5ce9fc | 1446 | { "get_bss_counter", cmd_get_bss_counter, |
79a670cb JM |
1447 | "<counter> <BSSID> = get BSS counter value", |
1448 | complete_get_bss_counter }, | |
7d23e971 | 1449 | { "inject", cmd_inject, |
79a670cb JM |
1450 | "<frame> <prot> <sender> <BSSID> <STA/ff:ff:ff:ff:ff:ff>", |
1451 | complete_inject }, | |
b3a6d9d4 JM |
1452 | { "send", cmd_send, |
1453 | "<prot> <raw frame as hex dump>", | |
1454 | complete_send }, | |
79a670cb | 1455 | { "version", cmd_version, "= get wlantest version", NULL }, |
fbdd2132 | 1456 | { "add_passphrase", cmd_add_passphrase, |
79a670cb | 1457 | "<passphrase> = add a known passphrase", NULL }, |
2fc0cd54 JM |
1458 | { "info_sta", cmd_info_sta, |
1459 | "<field> <BSSID> <STA> = get STA information", | |
1460 | complete_info_sta }, | |
1461 | { "info_bss", cmd_info_bss, | |
1462 | "<field> <BSSID> = get BSS information", | |
1463 | complete_info_bss }, | |
0e42fff3 JM |
1464 | { "clear_tdls_counters", cmd_clear_tdls_counters, |
1465 | "<BSSID> <STA1> <STA2> = clear TDLS counters", | |
1466 | complete_clear_tdls_counters }, | |
1467 | { "get_tdls_counter", cmd_get_tdls_counter, | |
1468 | "<counter> <BSSID> <STA1> <STA2> = get TDLS counter value", | |
1469 | complete_get_tdls_counter }, | |
1470 | { "get_bss_counter", cmd_get_bss_counter, | |
1471 | "<counter> <BSSID> = get BSS counter value", | |
1472 | complete_get_bss_counter }, | |
79a670cb | 1473 | { NULL, NULL, NULL, NULL } |
644fb8c8 JM |
1474 | }; |
1475 | ||
1476 | ||
1477 | static int ctrl_command(int s, int argc, char *argv[]) | |
1478 | { | |
1479 | const struct wlantest_cli_cmd *cmd, *match = NULL; | |
1480 | int count = 0; | |
1481 | int ret = 0; | |
1482 | ||
1483 | for (cmd = wlantest_cli_commands; cmd->cmd; cmd++) { | |
1484 | if (os_strncasecmp(cmd->cmd, argv[0], os_strlen(argv[0])) == 0) | |
1485 | { | |
1486 | match = cmd; | |
1487 | if (os_strcasecmp(cmd->cmd, argv[0]) == 0) { | |
1488 | /* exact match */ | |
1489 | count = 1; | |
1490 | break; | |
1491 | } | |
1492 | count++; | |
1493 | } | |
1494 | } | |
1495 | ||
1496 | if (count > 1) { | |
1497 | printf("Ambiguous command '%s'; possible commands:", argv[0]); | |
1498 | for (cmd = wlantest_cli_commands; cmd->cmd; cmd++) { | |
1499 | if (os_strncasecmp(cmd->cmd, argv[0], | |
1500 | os_strlen(argv[0])) == 0) { | |
1501 | printf(" %s", cmd->cmd); | |
1502 | } | |
644fb8c8 JM |
1503 | } |
1504 | printf("\n"); | |
1505 | ret = 1; | |
1506 | } else if (count == 0) { | |
1507 | printf("Unknown command '%s'\n", argv[0]); | |
1508 | ret = 1; | |
1509 | } else { | |
1510 | ret = match->handler(s, argc - 1, &argv[1]); | |
1511 | } | |
1512 | ||
1513 | return ret; | |
1514 | } | |
1515 | ||
1516 | ||
ef49bb80 JM |
1517 | struct wlantest_cli { |
1518 | int s; | |
1519 | }; | |
1520 | ||
1521 | ||
1522 | #define max_args 10 | |
1523 | ||
1524 | static int tokenize_cmd(char *cmd, char *argv[]) | |
1525 | { | |
1526 | char *pos; | |
1527 | int argc = 0; | |
1528 | ||
1529 | pos = cmd; | |
1530 | for (;;) { | |
1531 | while (*pos == ' ') | |
1532 | pos++; | |
1533 | if (*pos == '\0') | |
1534 | break; | |
1535 | argv[argc] = pos; | |
1536 | argc++; | |
1537 | if (argc == max_args) | |
1538 | break; | |
1539 | if (*pos == '"') { | |
1540 | char *pos2 = os_strrchr(pos, '"'); | |
1541 | if (pos2) | |
1542 | pos = pos2 + 1; | |
1543 | } | |
1544 | while (*pos != '\0' && *pos != ' ') | |
1545 | pos++; | |
1546 | if (*pos == ' ') | |
1547 | *pos++ = '\0'; | |
1548 | } | |
1549 | ||
1550 | return argc; | |
1551 | } | |
1552 | ||
1553 | ||
1554 | static void wlantest_cli_edit_cmd_cb(void *ctx, char *cmd) | |
1555 | { | |
1556 | struct wlantest_cli *cli = ctx; | |
1557 | char *argv[max_args]; | |
1558 | int argc; | |
1559 | argc = tokenize_cmd(cmd, argv); | |
1560 | if (argc) { | |
1561 | int ret = ctrl_command(cli->s, argc, argv); | |
1562 | if (ret < 0) | |
1563 | printf("FAIL\n"); | |
1564 | } | |
1565 | } | |
1566 | ||
1567 | ||
1568 | static void wlantest_cli_eloop_terminate(int sig, void *signal_ctx) | |
1569 | { | |
1570 | eloop_terminate(); | |
1571 | } | |
1572 | ||
1573 | ||
1574 | static void wlantest_cli_edit_eof_cb(void *ctx) | |
1575 | { | |
1576 | eloop_terminate(); | |
1577 | } | |
1578 | ||
1579 | ||
1580 | static char ** wlantest_cli_cmd_list(void) | |
1581 | { | |
1582 | char **res; | |
1583 | int i, count; | |
1584 | ||
1585 | count = sizeof(wlantest_cli_commands) / | |
1586 | sizeof(wlantest_cli_commands[0]); | |
1587 | res = os_zalloc(count * sizeof(char *)); | |
1588 | if (res == NULL) | |
1589 | return NULL; | |
1590 | ||
1591 | for (i = 0; wlantest_cli_commands[i].cmd; i++) { | |
1592 | res[i] = os_strdup(wlantest_cli_commands[i].cmd); | |
1593 | if (res[i] == NULL) | |
1594 | break; | |
1595 | } | |
1596 | ||
1597 | return res; | |
1598 | } | |
1599 | ||
1600 | ||
79a670cb JM |
1601 | static char ** wlantest_cli_cmd_completion(struct wlantest_cli *cli, |
1602 | const char *cmd, const char *str, | |
ef49bb80 JM |
1603 | int pos) |
1604 | { | |
1605 | int i; | |
1606 | ||
1607 | for (i = 0; wlantest_cli_commands[i].cmd; i++) { | |
79a670cb JM |
1608 | const struct wlantest_cli_cmd *c = &wlantest_cli_commands[i]; |
1609 | if (os_strcasecmp(c->cmd, cmd) == 0) { | |
ef49bb80 | 1610 | edit_clear_line(); |
79a670cb | 1611 | printf("\r%s\n", c->usage); |
ef49bb80 | 1612 | edit_redraw(); |
79a670cb JM |
1613 | if (c->complete) |
1614 | return c->complete(cli->s, str, pos); | |
ef49bb80 JM |
1615 | break; |
1616 | } | |
1617 | } | |
1618 | ||
1619 | return NULL; | |
1620 | } | |
1621 | ||
1622 | ||
1623 | static char ** wlantest_cli_edit_completion_cb(void *ctx, const char *str, | |
1624 | int pos) | |
1625 | { | |
79a670cb | 1626 | struct wlantest_cli *cli = ctx; |
ef49bb80 JM |
1627 | char **res; |
1628 | const char *end; | |
1629 | char *cmd; | |
1630 | ||
1631 | end = os_strchr(str, ' '); | |
1632 | if (end == NULL || str + pos < end) | |
1633 | return wlantest_cli_cmd_list(); | |
1634 | ||
1635 | cmd = os_malloc(pos + 1); | |
1636 | if (cmd == NULL) | |
1637 | return NULL; | |
1638 | os_memcpy(cmd, str, pos); | |
1639 | cmd[end - str] = '\0'; | |
79a670cb | 1640 | res = wlantest_cli_cmd_completion(cli, cmd, str, pos); |
ef49bb80 JM |
1641 | os_free(cmd); |
1642 | return res; | |
1643 | } | |
1644 | ||
1645 | ||
1646 | static void wlantest_cli_interactive(int s) | |
1647 | { | |
1648 | struct wlantest_cli cli; | |
13b9f3a1 | 1649 | char *home, *hfile = NULL; |
ef49bb80 JM |
1650 | |
1651 | if (eloop_init()) | |
1652 | return; | |
1653 | ||
13b9f3a1 JM |
1654 | home = getenv("HOME"); |
1655 | if (home) { | |
1656 | const char *fname = ".wlantest_cli_history"; | |
1657 | int hfile_len = os_strlen(home) + 1 + os_strlen(fname) + 1; | |
1658 | hfile = os_malloc(hfile_len); | |
1659 | if (hfile) | |
1660 | os_snprintf(hfile, hfile_len, "%s/%s", home, fname); | |
1661 | } | |
1662 | ||
ef49bb80 JM |
1663 | cli.s = s; |
1664 | eloop_register_signal_terminate(wlantest_cli_eloop_terminate, &cli); | |
8953e968 | 1665 | edit_init(wlantest_cli_edit_cmd_cb, wlantest_cli_edit_eof_cb, |
13b9f3a1 | 1666 | wlantest_cli_edit_completion_cb, &cli, hfile); |
ef49bb80 JM |
1667 | |
1668 | eloop_run(); | |
1669 | ||
13b9f3a1 JM |
1670 | edit_deinit(hfile, NULL); |
1671 | os_free(hfile); | |
ef49bb80 JM |
1672 | eloop_destroy(); |
1673 | } | |
1674 | ||
1675 | ||
644fb8c8 JM |
1676 | int main(int argc, char *argv[]) |
1677 | { | |
1678 | int s; | |
1679 | struct sockaddr_un addr; | |
1680 | int ret = 0; | |
1681 | ||
ef49bb80 JM |
1682 | if (os_program_init()) |
1683 | return -1; | |
1684 | ||
644fb8c8 JM |
1685 | s = socket(AF_UNIX, SOCK_SEQPACKET, 0); |
1686 | if (s < 0) { | |
1687 | perror("socket"); | |
1688 | return -1; | |
1689 | } | |
1690 | ||
1691 | os_memset(&addr, 0, sizeof(addr)); | |
1692 | addr.sun_family = AF_UNIX; | |
1693 | os_strlcpy(addr.sun_path + 1, WLANTEST_SOCK_NAME, | |
1694 | sizeof(addr.sun_path) - 1); | |
1695 | if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { | |
1696 | perror("connect"); | |
1697 | close(s); | |
1698 | return -1; | |
1699 | } | |
1700 | ||
1701 | if (argc > 1) { | |
1702 | ret = ctrl_command(s, argc - 1, &argv[1]); | |
1703 | if (ret < 0) | |
1704 | printf("FAIL\n"); | |
1705 | } else { | |
ef49bb80 | 1706 | wlantest_cli_interactive(s); |
644fb8c8 JM |
1707 | } |
1708 | ||
1709 | close(s); | |
ef49bb80 JM |
1710 | |
1711 | os_program_deinit(); | |
1712 | ||
644fb8c8 JM |
1713 | return ret; |
1714 | } |