wlantest: Add BSS/STA statistics counters
[mech_eap.git] / wlantest / wlantest_cli.c
1 /*
2  * wlantest controller
3  * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation.
8  *
9  * Alternatively, this software may be distributed under the terms of BSD
10  * license.
11  *
12  * See README and COPYING for more details.
13  */
14
15 #include "utils/includes.h"
16 #include <sys/un.h>
17
18 #include "utils/common.h"
19 #include "wlantest_ctrl.h"
20
21
22 static u8 * attr_get(u8 *buf, size_t buflen, enum wlantest_ctrl_attr attr,
23                      size_t *len)
24 {
25         u8 *pos = buf;
26
27         while (pos + 8 <= buf + buflen) {
28                 enum wlantest_ctrl_attr a;
29                 size_t alen;
30                 a = WPA_GET_BE32(pos);
31                 pos += 4;
32                 alen = WPA_GET_BE32(pos);
33                 pos += 4;
34                 if (pos + alen > buf + buflen) {
35                         printf("Invalid control message attribute\n");
36                         return NULL;
37                 }
38                 if (a == attr) {
39                         *len = alen;
40                         return pos;
41                 }
42                 pos += alen;
43         }
44
45         return NULL;
46 }
47
48
49 static u8 * attr_hdr_add(u8 *pos, u8 *end, enum wlantest_ctrl_attr attr,
50                          size_t len)
51 {
52         if (pos == NULL || end - pos < 8 + len)
53                 return NULL;
54         WPA_PUT_BE32(pos, attr);
55         pos += 4;
56         WPA_PUT_BE32(pos, len);
57         pos += 4;
58         return pos;
59 }
60
61
62 static u8 * attr_add_be32(u8 *pos, u8 *end, enum wlantest_ctrl_attr attr,
63                           u32 val)
64 {
65         if (pos == NULL || end - pos < 12)
66                 return NULL;
67         WPA_PUT_BE32(pos, attr);
68         pos += 4;
69         WPA_PUT_BE32(pos, 4);
70         pos += 4;
71         WPA_PUT_BE32(pos, val);
72         pos += 4;
73         return pos;
74 }
75
76
77 static int cmd_send_and_recv(int s, const u8 *cmd, size_t cmd_len,
78                              u8 *resp, size_t max_resp_len)
79 {
80         int res;
81         enum wlantest_ctrl_cmd cmd_resp;
82
83         if (send(s, cmd, cmd_len, 0) < 0)
84                 return -1;
85         res = recv(s, resp, max_resp_len, 0);
86         if (res < 4)
87                 return -1;
88
89         cmd_resp = WPA_GET_BE32(resp);
90         if (cmd_resp == WLANTEST_CTRL_SUCCESS)
91                 return res;
92
93         if (cmd_resp == WLANTEST_CTRL_UNKNOWN_CMD)
94                 printf("Unknown command\n");
95         else if (cmd_resp == WLANTEST_CTRL_INVALID_CMD)
96                 printf("Invalid command\n");
97
98         return -1;
99 }
100
101
102 static int cmd_simple(int s, enum wlantest_ctrl_cmd cmd)
103 {
104         u8 buf[4];
105         int res;
106         WPA_PUT_BE32(buf, cmd);
107         res = cmd_send_and_recv(s, buf, sizeof(buf), buf, sizeof(buf));
108         return res < 0 ? -1 : 0;
109 }
110
111
112 static int cmd_ping(int s, int argc, char *argv[])
113 {
114         int res = cmd_simple(s, WLANTEST_CTRL_PING);
115         if (res == 0)
116                 printf("PONG\n");
117         return res == 0;
118 }
119
120
121 static int cmd_terminate(int s, int argc, char *argv[])
122 {
123         return cmd_simple(s, WLANTEST_CTRL_TERMINATE);
124 }
125
126
127 static int cmd_list_bss(int s, int argc, char *argv[])
128 {
129         u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
130         u8 buf[4];
131         u8 *bssid;
132         size_t len;
133         int rlen, i;
134
135         WPA_PUT_BE32(buf, WLANTEST_CTRL_LIST_BSS);
136         rlen = cmd_send_and_recv(s, buf, sizeof(buf), resp, sizeof(resp));
137         if (rlen < 0)
138                 return -1;
139
140         bssid = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_BSSID, &len);
141         if (bssid == NULL)
142                 return -1;
143
144         for (i = 0; i < len / ETH_ALEN; i++)
145                 printf(MACSTR " ", MAC2STR(bssid + ETH_ALEN * i));
146         printf("\n");
147
148         return 0;
149 }
150
151
152 static int cmd_list_sta(int s, int argc, char *argv[])
153 {
154         u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
155         u8 buf[100], *pos;
156         u8 *addr;
157         size_t len;
158         int rlen, i;
159
160         if (argc < 1) {
161                 printf("list_sta needs one argument: BSSID\n");
162                 return -1;
163         }
164
165         pos = buf;
166         WPA_PUT_BE32(pos, WLANTEST_CTRL_LIST_STA);
167         pos += 4;
168         WPA_PUT_BE32(pos, WLANTEST_ATTR_BSSID);
169         pos += 4;
170         WPA_PUT_BE32(pos, ETH_ALEN);
171         pos += 4;
172         if (hwaddr_aton(argv[0], pos) < 0) {
173                 printf("Invalid BSSID '%s'\n", argv[0]);
174                 return -1;
175         }
176         pos += ETH_ALEN;
177
178         rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
179         if (rlen < 0)
180                 return -1;
181
182         addr = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_STA_ADDR, &len);
183         if (addr == NULL)
184                 return -1;
185
186         for (i = 0; i < len / ETH_ALEN; i++)
187                 printf(MACSTR " ", MAC2STR(addr + ETH_ALEN * i));
188         printf("\n");
189
190         return 0;
191 }
192
193
194 static int cmd_flush(int s, int argc, char *argv[])
195 {
196         return cmd_simple(s, WLANTEST_CTRL_FLUSH);
197 }
198
199
200 static int cmd_clear_sta_counters(int s, int argc, char *argv[])
201 {
202         u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
203         u8 buf[100], *pos;
204         int rlen;
205
206         if (argc < 2) {
207                 printf("clear_sta_counters needs two arguments: BSSID and "
208                        "STA address\n");
209                 return -1;
210         }
211
212         pos = buf;
213         WPA_PUT_BE32(pos, WLANTEST_CTRL_CLEAR_STA_COUNTERS);
214         pos += 4;
215         WPA_PUT_BE32(pos, WLANTEST_ATTR_BSSID);
216         pos += 4;
217         WPA_PUT_BE32(pos, ETH_ALEN);
218         pos += 4;
219         if (hwaddr_aton(argv[0], pos) < 0) {
220                 printf("Invalid BSSID '%s'\n", argv[0]);
221                 return -1;
222         }
223         pos += ETH_ALEN;
224
225         WPA_PUT_BE32(pos, WLANTEST_ATTR_STA_ADDR);
226         pos += 4;
227         WPA_PUT_BE32(pos, ETH_ALEN);
228         pos += 4;
229         if (hwaddr_aton(argv[1], pos) < 0) {
230                 printf("Invalid STA address '%s'\n", argv[1]);
231                 return -1;
232         }
233         pos += ETH_ALEN;
234
235         rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
236         if (rlen < 0)
237                 return -1;
238         printf("OK\n");
239         return 0;
240 }
241
242
243 static int cmd_clear_bss_counters(int s, int argc, char *argv[])
244 {
245         u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
246         u8 buf[100], *pos;
247         int rlen;
248
249         if (argc < 1) {
250                 printf("clear_bss_counters needs one argument: BSSID\n");
251                 return -1;
252         }
253
254         pos = buf;
255         WPA_PUT_BE32(pos, WLANTEST_CTRL_CLEAR_BSS_COUNTERS);
256         pos += 4;
257         WPA_PUT_BE32(pos, WLANTEST_ATTR_BSSID);
258         pos += 4;
259         WPA_PUT_BE32(pos, ETH_ALEN);
260         pos += 4;
261         if (hwaddr_aton(argv[0], pos) < 0) {
262                 printf("Invalid BSSID '%s'\n", argv[0]);
263                 return -1;
264         }
265         pos += ETH_ALEN;
266
267         rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
268         if (rlen < 0)
269                 return -1;
270         printf("OK\n");
271         return 0;
272 }
273
274
275 struct sta_counters {
276         const char *name;
277         enum wlantest_sta_counter num;
278 };
279
280 static const struct sta_counters sta_counters[] = {
281         { "auth_tx", WLANTEST_STA_COUNTER_AUTH_TX },
282         { "auth_rx", WLANTEST_STA_COUNTER_AUTH_RX },
283         { "assocreq_tx", WLANTEST_STA_COUNTER_ASSOCREQ_TX },
284         { "reassocreq_tx", WLANTEST_STA_COUNTER_REASSOCREQ_TX },
285         { "ptk_learned", WLANTEST_STA_COUNTER_PTK_LEARNED },
286         { "valid_deauth_tx", WLANTEST_STA_COUNTER_VALID_DEAUTH_TX },
287         { "valid_deauth_rx", WLANTEST_STA_COUNTER_VALID_DEAUTH_RX },
288         { "invalid_deauth_tx", WLANTEST_STA_COUNTER_INVALID_DEAUTH_TX },
289         { "invalid_deauth_rx", WLANTEST_STA_COUNTER_INVALID_DEAUTH_RX },
290         { "valid_disassoc_tx", WLANTEST_STA_COUNTER_VALID_DISASSOC_TX },
291         { "valid_disassoc_rx", WLANTEST_STA_COUNTER_VALID_DISASSOC_RX },
292         { "invalid_disassoc_tx", WLANTEST_STA_COUNTER_INVALID_DISASSOC_TX },
293         { "invalid_disassoc_rx", WLANTEST_STA_COUNTER_INVALID_DISASSOC_RX },
294         { "valid_saqueryreq_tx", WLANTEST_STA_COUNTER_VALID_SAQUERYREQ_TX },
295         { "valid_saqueryreq_rx", WLANTEST_STA_COUNTER_VALID_SAQUERYREQ_RX },
296         { "invalid_saqueryreq_tx",
297           WLANTEST_STA_COUNTER_INVALID_SAQUERYREQ_TX },
298         { "invalid_saqueryreq_rx",
299           WLANTEST_STA_COUNTER_INVALID_SAQUERYREQ_RX },
300         { "valid_saqueryresp_tx", WLANTEST_STA_COUNTER_VALID_SAQUERYRESP_TX },
301         { "valid_saqueryresp_rx", WLANTEST_STA_COUNTER_VALID_SAQUERYRESP_RX },
302         { "invalid_saqueryresp_tx",
303           WLANTEST_STA_COUNTER_INVALID_SAQUERYRESP_TX },
304         { "invalid_saqueryresp_rx",
305           WLANTEST_STA_COUNTER_INVALID_SAQUERYRESP_RX },
306         { NULL, 0 }
307 };
308
309 static int cmd_get_sta_counter(int s, int argc, char *argv[])
310 {
311         u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
312         u8 buf[100], *end, *pos;
313         int rlen, i;
314         size_t len;
315
316         if (argc != 3) {
317                 printf("get_sta_counter needs at three arguments: "
318                        "counter name, BSSID, and STA address\n");
319                 return -1;
320         }
321
322         pos = buf;
323         end = buf + sizeof(buf);
324         WPA_PUT_BE32(pos, WLANTEST_CTRL_GET_STA_COUNTER);
325         pos += 4;
326
327         for (i = 0; sta_counters[i].name; i++) {
328                 if (os_strcasecmp(sta_counters[i].name, argv[0]) == 0)
329                         break;
330         }
331         if (sta_counters[i].name == NULL) {
332                 printf("Unknown STA counter '%s'\n", argv[0]);
333                 printf("Counters:");
334                 for (i = 0; sta_counters[i].name; i++)
335                         printf(" %s", sta_counters[i].name);
336                 printf("\n");
337                 return -1;
338         }
339
340         pos = attr_add_be32(pos, end, WLANTEST_ATTR_STA_COUNTER,
341                             sta_counters[i].num);
342         pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN);
343         if (hwaddr_aton(argv[1], pos) < 0) {
344                 printf("Invalid BSSID '%s'\n", argv[1]);
345                 return -1;
346         }
347         pos += ETH_ALEN;
348
349         pos = attr_hdr_add(pos, end, WLANTEST_ATTR_STA_ADDR, ETH_ALEN);
350         if (hwaddr_aton(argv[2], pos) < 0) {
351                 printf("Invalid STA address '%s'\n", argv[2]);
352                 return -1;
353         }
354         pos += ETH_ALEN;
355
356         rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
357         if (rlen < 0)
358                 return -1;
359
360         pos = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_COUNTER, &len);
361         if (pos == NULL || len != 4)
362                 return -1;
363         printf("%u\n", WPA_GET_BE32(pos));
364         return 0;
365 }
366
367
368 struct bss_counters {
369         const char *name;
370         enum wlantest_bss_counter num;
371 };
372
373 static const struct bss_counters bss_counters[] = {
374         { "valid_bip_mmie", WLANTEST_BSS_COUNTER_VALID_BIP_MMIE },
375         { "invalid_bip_mmie", WLANTEST_BSS_COUNTER_INVALID_BIP_MMIE },
376         { "missing_bip_mmie", WLANTEST_BSS_COUNTER_MISSING_BIP_MMIE },
377         { NULL, 0 }
378 };
379
380 static int cmd_get_bss_counter(int s, int argc, char *argv[])
381 {
382         u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
383         u8 buf[100], *end, *pos;
384         int rlen, i;
385         size_t len;
386
387         if (argc != 2) {
388                 printf("get_bss_counter needs at three arguments: "
389                        "counter name and BSSID\n");
390                 return -1;
391         }
392
393         pos = buf;
394         end = buf + sizeof(buf);
395         WPA_PUT_BE32(pos, WLANTEST_CTRL_GET_BSS_COUNTER);
396         pos += 4;
397
398         for (i = 0; bss_counters[i].name; i++) {
399                 if (os_strcasecmp(bss_counters[i].name, argv[0]) == 0)
400                         break;
401         }
402         if (bss_counters[i].name == NULL) {
403                 printf("Unknown BSS counter '%s'\n", argv[0]);
404                 printf("Counters:");
405                 for (i = 0; bss_counters[i].name; i++)
406                         printf(" %s", bss_counters[i].name);
407                 printf("\n");
408                 return -1;
409         }
410
411         pos = attr_add_be32(pos, end, WLANTEST_ATTR_BSS_COUNTER,
412                             bss_counters[i].num);
413         pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN);
414         if (hwaddr_aton(argv[1], pos) < 0) {
415                 printf("Invalid BSSID '%s'\n", argv[1]);
416                 return -1;
417         }
418         pos += ETH_ALEN;
419
420         rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
421         if (rlen < 0)
422                 return -1;
423
424         pos = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_COUNTER, &len);
425         if (pos == NULL || len != 4)
426                 return -1;
427         printf("%u\n", WPA_GET_BE32(pos));
428         return 0;
429 }
430
431
432 struct wlantest_cli_cmd {
433         const char *cmd;
434         int (*handler)(int s, int argc, char *argv[]);
435         const char *usage;
436 };
437
438 static const struct wlantest_cli_cmd wlantest_cli_commands[] = {
439         { "ping", cmd_ping, "= test connection to wlantest" },
440         { "terminate", cmd_terminate, "= terminate wlantest" },
441         { "list_bss", cmd_list_bss, "= get BSS list" },
442         { "list_sta", cmd_list_sta, "<BSSID> = get STA list" },
443         { "flush", cmd_flush, "= drop all collected BSS data" },
444         { "clear_sta_counters", cmd_clear_sta_counters,
445           "<BSSID> <STA> = clear STA counters" },
446         { "clear_bss_counters", cmd_clear_bss_counters,
447           "<BSSID> = clear BSS counters" },
448         { "get_sta_counter", cmd_get_sta_counter,
449           "<counter> <BSSID> <STA> = get STA counter value" },
450         { "get_bss_counter", cmd_get_bss_counter,
451           "<counter> <BSSID> = get BSS counter value" },
452         { NULL, NULL, NULL }
453 };
454
455
456 static int ctrl_command(int s, int argc, char *argv[])
457 {
458         const struct wlantest_cli_cmd *cmd, *match = NULL;
459         int count = 0;
460         int ret = 0;
461
462         for (cmd = wlantest_cli_commands; cmd->cmd; cmd++) {
463                 if (os_strncasecmp(cmd->cmd, argv[0], os_strlen(argv[0])) == 0)
464                 {
465                         match = cmd;
466                         if (os_strcasecmp(cmd->cmd, argv[0]) == 0) {
467                                 /* exact match */
468                                 count = 1;
469                                 break;
470                         }
471                         count++;
472                 }
473         }
474
475         if (count > 1) {
476                 printf("Ambiguous command '%s'; possible commands:", argv[0]);
477                 for (cmd = wlantest_cli_commands; cmd->cmd; cmd++) {
478                         if (os_strncasecmp(cmd->cmd, argv[0],
479                                            os_strlen(argv[0])) == 0) {
480                                 printf(" %s", cmd->cmd);
481                         }
482                 }
483                 printf("\n");
484                 ret = 1;
485         } else if (count == 0) {
486                 printf("Unknown command '%s'\n", argv[0]);
487                 ret = 1;
488         } else {
489                 ret = match->handler(s, argc - 1, &argv[1]);
490         }
491
492         return ret;
493 }
494
495
496 int main(int argc, char *argv[])
497 {
498         int s;
499         struct sockaddr_un addr;
500         int ret = 0;
501
502         s = socket(AF_UNIX, SOCK_SEQPACKET, 0);
503         if (s < 0) {
504                 perror("socket");
505                 return -1;
506         }
507
508         os_memset(&addr, 0, sizeof(addr));
509         addr.sun_family = AF_UNIX;
510         os_strlcpy(addr.sun_path + 1, WLANTEST_SOCK_NAME,
511                    sizeof(addr.sun_path) - 1);
512         if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
513                 perror("connect");
514                 close(s);
515                 return -1;
516         }
517
518         if (argc > 1) {
519                 ret = ctrl_command(s, argc - 1, &argv[1]);
520                 if (ret < 0)
521                         printf("FAIL\n");
522         } else {
523                 /* TODO: interactive */
524         }
525
526         close(s);
527         return ret;
528 }