]>
Commit | Line | Data |
---|---|---|
a149fcc7 JM |
1 | /* |
2 | * wlantest - IEEE 802.11 protocol monitoring and testing tool | |
c99a721e | 3 | * Copyright (c) 2010-2013, Jouni Malinen <j@w1.fi> |
a149fcc7 | 4 | * |
0f3d578e JM |
5 | * This software may be distributed under the terms of the BSD license. |
6 | * See README for more details. | |
a149fcc7 JM |
7 | */ |
8 | ||
9 | #include "utils/includes.h" | |
10 | ||
11 | #include "utils/common.h" | |
12 | #include "utils/eloop.h" | |
13 | #include "wlantest.h" | |
14 | ||
15 | ||
a149fcc7 JM |
16 | static void wlantest_terminate(int sig, void *signal_ctx) |
17 | { | |
18 | eloop_terminate(); | |
19 | } | |
20 | ||
21 | ||
22 | static void usage(void) | |
23 | { | |
de8bb171 | 24 | printf("wlantest [-cddhqqFt] [-i<ifname>] [-r<pcap file>] " |
3215df77 | 25 | "[-p<passphrase>]\n" |
ba2beacc | 26 | " [-I<wired ifname>] [-R<wired pcap file>] " |
64f45d07 | 27 | "[-P<RADIUS shared secret>]\n" |
ba2beacc | 28 | " [-n<write pcapng file>]\n" |
d33fef57 | 29 | " [-w<write pcap file>] [-f<MSK/PMK file>]\n" |
a0530dff | 30 | " [-L<log file>] [-T<PTK file>]\n"); |
53650bca JM |
31 | } |
32 | ||
33 | ||
34 | static void passphrase_deinit(struct wlantest_passphrase *p) | |
35 | { | |
36 | dl_list_del(&p->list); | |
37 | os_free(p); | |
a149fcc7 JM |
38 | } |
39 | ||
40 | ||
d06df64d JM |
41 | static void secret_deinit(struct wlantest_radius_secret *r) |
42 | { | |
43 | dl_list_del(&r->list); | |
44 | os_free(r); | |
45 | } | |
46 | ||
47 | ||
d84d3893 JM |
48 | static void wlantest_init(struct wlantest *wt) |
49 | { | |
644fb8c8 | 50 | int i; |
d84d3893 JM |
51 | os_memset(wt, 0, sizeof(*wt)); |
52 | wt->monitor_sock = -1; | |
644fb8c8 JM |
53 | wt->ctrl_sock = -1; |
54 | for (i = 0; i < MAX_CTRL_CONNECTIONS; i++) | |
55 | wt->ctrl_socks[i] = -1; | |
53650bca | 56 | dl_list_init(&wt->passphrase); |
d84d3893 | 57 | dl_list_init(&wt->bss); |
d06df64d JM |
58 | dl_list_init(&wt->secret); |
59 | dl_list_init(&wt->radius); | |
60 | dl_list_init(&wt->pmk); | |
a0530dff | 61 | dl_list_init(&wt->ptk); |
2e479416 | 62 | dl_list_init(&wt->wep); |
d06df64d JM |
63 | } |
64 | ||
65 | ||
66 | void radius_deinit(struct wlantest_radius *r) | |
67 | { | |
68 | dl_list_del(&r->list); | |
69 | os_free(r); | |
d84d3893 JM |
70 | } |
71 | ||
72 | ||
a0530dff JM |
73 | static void ptk_deinit(struct wlantest_ptk *ptk) |
74 | { | |
75 | dl_list_del(&ptk->list); | |
76 | os_free(ptk); | |
77 | } | |
78 | ||
79 | ||
d84d3893 JM |
80 | static void wlantest_deinit(struct wlantest *wt) |
81 | { | |
53650bca | 82 | struct wlantest_passphrase *p, *pn; |
d06df64d JM |
83 | struct wlantest_radius_secret *s, *sn; |
84 | struct wlantest_radius *r, *rn; | |
85 | struct wlantest_pmk *pmk, *np; | |
a0530dff | 86 | struct wlantest_ptk *ptk, *npt; |
2e479416 | 87 | struct wlantest_wep *wep, *nw; |
d06df64d | 88 | |
644fb8c8 JM |
89 | if (wt->ctrl_sock >= 0) |
90 | ctrl_deinit(wt); | |
d84d3893 JM |
91 | if (wt->monitor_sock >= 0) |
92 | monitor_deinit(wt); | |
d356bd63 | 93 | bss_flush(wt); |
53650bca JM |
94 | dl_list_for_each_safe(p, pn, &wt->passphrase, |
95 | struct wlantest_passphrase, list) | |
96 | passphrase_deinit(p); | |
d06df64d JM |
97 | dl_list_for_each_safe(s, sn, &wt->secret, |
98 | struct wlantest_radius_secret, list) | |
99 | secret_deinit(s); | |
100 | dl_list_for_each_safe(r, rn, &wt->radius, struct wlantest_radius, list) | |
101 | radius_deinit(r); | |
102 | dl_list_for_each_safe(pmk, np, &wt->pmk, struct wlantest_pmk, list) | |
103 | pmk_deinit(pmk); | |
a0530dff JM |
104 | dl_list_for_each_safe(ptk, npt, &wt->ptk, struct wlantest_ptk, list) |
105 | ptk_deinit(ptk); | |
2e479416 JM |
106 | dl_list_for_each_safe(wep, nw, &wt->wep, struct wlantest_wep, list) |
107 | os_free(wep); | |
64f45d07 | 108 | write_pcap_deinit(wt); |
ba2beacc JM |
109 | write_pcapng_deinit(wt); |
110 | clear_notes(wt); | |
111 | os_free(wt->decrypted); | |
112 | wt->decrypted = NULL; | |
53650bca JM |
113 | } |
114 | ||
115 | ||
116 | static void add_passphrase(struct wlantest *wt, const char *passphrase) | |
117 | { | |
118 | struct wlantest_passphrase *p; | |
119 | size_t len = os_strlen(passphrase); | |
120 | ||
121 | if (len < 8 || len > 63) | |
122 | return; | |
123 | p = os_zalloc(sizeof(*p)); | |
124 | if (p == NULL) | |
125 | return; | |
126 | os_memcpy(p->passphrase, passphrase, len); | |
127 | dl_list_add(&wt->passphrase, &p->list); | |
d84d3893 JM |
128 | } |
129 | ||
130 | ||
d06df64d JM |
131 | static void add_secret(struct wlantest *wt, const char *secret) |
132 | { | |
133 | struct wlantest_radius_secret *s; | |
134 | size_t len = os_strlen(secret); | |
135 | ||
136 | if (len >= MAX_RADIUS_SECRET_LEN) | |
137 | return; | |
138 | s = os_zalloc(sizeof(*s)); | |
139 | if (s == NULL) | |
140 | return; | |
141 | os_memcpy(s->secret, secret, len); | |
142 | dl_list_add(&wt->secret, &s->list); | |
143 | } | |
144 | ||
145 | ||
219fd441 JM |
146 | static int add_pmk_file(struct wlantest *wt, const char *pmk_file) |
147 | { | |
148 | FILE *f; | |
149 | u8 pmk[32]; | |
150 | char buf[300], *pos; | |
151 | struct wlantest_pmk *p; | |
152 | ||
153 | f = fopen(pmk_file, "r"); | |
154 | if (f == NULL) { | |
155 | wpa_printf(MSG_ERROR, "Could not open '%s'", pmk_file); | |
156 | return -1; | |
157 | } | |
158 | ||
159 | while (fgets(buf, sizeof(buf), f)) { | |
160 | pos = buf; | |
161 | while (*pos && *pos != '\r' && *pos != '\n') | |
162 | pos++; | |
163 | *pos = '\0'; | |
164 | if (pos - buf < 2 * 32) | |
165 | continue; | |
166 | if (hexstr2bin(buf, pmk, 32) < 0) | |
167 | continue; | |
168 | p = os_zalloc(sizeof(*p)); | |
169 | if (p == NULL) | |
170 | break; | |
171 | os_memcpy(p->pmk, pmk, 32); | |
172 | dl_list_add(&wt->pmk, &p->list); | |
173 | wpa_hexdump(MSG_DEBUG, "Added PMK from file", pmk, 32); | |
174 | } | |
175 | ||
176 | fclose(f); | |
177 | return 0; | |
178 | } | |
179 | ||
180 | ||
a0530dff JM |
181 | static int add_ptk_file(struct wlantest *wt, const char *ptk_file) |
182 | { | |
183 | FILE *f; | |
184 | u8 ptk[64]; | |
185 | size_t ptk_len; | |
186 | char buf[300], *pos; | |
187 | struct wlantest_ptk *p; | |
188 | ||
189 | f = fopen(ptk_file, "r"); | |
190 | if (f == NULL) { | |
191 | wpa_printf(MSG_ERROR, "Could not open '%s'", ptk_file); | |
192 | return -1; | |
193 | } | |
194 | ||
195 | while (fgets(buf, sizeof(buf), f)) { | |
196 | pos = buf; | |
197 | while (*pos && *pos != '\r' && *pos != '\n') | |
198 | pos++; | |
199 | *pos = '\0'; | |
200 | ptk_len = pos - buf; | |
201 | if (ptk_len & 1) | |
202 | continue; | |
203 | ptk_len /= 2; | |
204 | if (ptk_len != 16 && ptk_len != 32 && | |
205 | ptk_len != 48 && ptk_len != 64) | |
206 | continue; | |
207 | if (hexstr2bin(buf, ptk, ptk_len) < 0) | |
208 | continue; | |
209 | p = os_zalloc(sizeof(*p)); | |
210 | if (p == NULL) | |
211 | break; | |
212 | if (ptk_len < 48) { | |
213 | os_memcpy(p->ptk.tk1, ptk, ptk_len); | |
214 | p->ptk_len = 32 + ptk_len; | |
215 | } else { | |
216 | os_memcpy(&p->ptk, ptk, ptk_len); | |
217 | p->ptk_len = ptk_len; | |
218 | } | |
219 | dl_list_add(&wt->ptk, &p->list); | |
220 | wpa_hexdump(MSG_DEBUG, "Added PTK from file", ptk, ptk_len); | |
221 | } | |
222 | ||
223 | fclose(f); | |
224 | return 0; | |
225 | } | |
226 | ||
227 | ||
9a994178 | 228 | int add_wep(struct wlantest *wt, const char *key) |
2e479416 JM |
229 | { |
230 | struct wlantest_wep *w; | |
231 | size_t len = os_strlen(key); | |
232 | ||
233 | if (len != 2 * 5 && len != 2 * 13) { | |
234 | wpa_printf(MSG_INFO, "Invalid WEP key '%s'", key); | |
9a994178 | 235 | return -1; |
2e479416 JM |
236 | } |
237 | w = os_zalloc(sizeof(*w)); | |
238 | if (w == NULL) | |
9a994178 | 239 | return -1; |
2e479416 JM |
240 | if (hexstr2bin(key, w->key, len / 2) < 0) { |
241 | os_free(w); | |
242 | wpa_printf(MSG_INFO, "Invalid WEP key '%s'", key); | |
9a994178 | 243 | return -1; |
2e479416 JM |
244 | } |
245 | w->key_len = len / 2; | |
246 | dl_list_add(&wt->wep, &w->list); | |
9a994178 | 247 | return 0; |
2e479416 JM |
248 | } |
249 | ||
250 | ||
ba2beacc JM |
251 | void add_note(struct wlantest *wt, int level, const char *fmt, ...) |
252 | { | |
253 | va_list ap; | |
254 | size_t len = 1000; | |
255 | int wlen; | |
256 | ||
257 | if (wt->num_notes == MAX_NOTES) | |
258 | return; | |
259 | ||
260 | wt->notes[wt->num_notes] = os_malloc(len); | |
261 | if (wt->notes[wt->num_notes] == NULL) | |
262 | return; | |
263 | va_start(ap, fmt); | |
264 | wlen = vsnprintf(wt->notes[wt->num_notes], len, fmt, ap); | |
265 | va_end(ap); | |
266 | if (wlen < 0) { | |
267 | os_free(wt->notes[wt->num_notes]); | |
268 | wt->notes[wt->num_notes] = NULL; | |
269 | return; | |
270 | } | |
271 | if (wlen >= len) | |
272 | wt->notes[wt->num_notes][len - 1] = '\0'; | |
273 | wpa_printf(level, "%s", wt->notes[wt->num_notes]); | |
274 | wt->num_notes++; | |
275 | } | |
276 | ||
277 | ||
278 | void clear_notes(struct wlantest *wt) | |
279 | { | |
280 | size_t i; | |
281 | ||
282 | for (i = 0; i < wt->num_notes; i++) { | |
283 | os_free(wt->notes[i]); | |
284 | wt->notes[i] = NULL; | |
285 | } | |
286 | ||
287 | wt->num_notes = 0; | |
288 | } | |
289 | ||
290 | ||
291 | size_t notes_len(struct wlantest *wt, size_t hdrlen) | |
292 | { | |
293 | size_t i; | |
294 | size_t len = wt->num_notes * hdrlen; | |
295 | ||
296 | for (i = 0; i < wt->num_notes; i++) | |
297 | len += os_strlen(wt->notes[i]); | |
298 | ||
299 | return len; | |
300 | } | |
301 | ||
302 | ||
c99a721e JM |
303 | int wlantest_relog(struct wlantest *wt) |
304 | { | |
305 | int ret = 0; | |
306 | ||
307 | wpa_printf(MSG_INFO, "Re-open log/capture files"); | |
d33fef57 JM |
308 | if (wpa_debug_reopen_file()) |
309 | ret = -1; | |
c99a721e JM |
310 | |
311 | if (wt->write_file) { | |
312 | write_pcap_deinit(wt); | |
313 | if (write_pcap_init(wt, wt->write_file) < 0) | |
314 | ret = -1; | |
315 | } | |
316 | ||
317 | if (wt->pcapng_file) { | |
318 | write_pcapng_deinit(wt); | |
319 | if (write_pcapng_init(wt, wt->pcapng_file) < 0) | |
320 | ret = -1; | |
321 | } | |
322 | ||
323 | return ret; | |
324 | } | |
325 | ||
326 | ||
a149fcc7 JM |
327 | int main(int argc, char *argv[]) |
328 | { | |
329 | int c; | |
330 | const char *read_file = NULL; | |
3215df77 | 331 | const char *read_wired_file = NULL; |
a149fcc7 | 332 | const char *ifname = NULL; |
3215df77 | 333 | const char *ifname_wired = NULL; |
d33fef57 | 334 | const char *logfile = NULL; |
a149fcc7 | 335 | struct wlantest wt; |
644fb8c8 | 336 | int ctrl_iface = 0; |
a149fcc7 JM |
337 | |
338 | wpa_debug_level = MSG_INFO; | |
53650bca | 339 | wpa_debug_show_keys = 1; |
a149fcc7 JM |
340 | |
341 | if (os_program_init()) | |
342 | return -1; | |
343 | ||
d84d3893 | 344 | wlantest_init(&wt); |
a149fcc7 JM |
345 | |
346 | for (;;) { | |
a0530dff | 347 | c = getopt(argc, argv, "cdf:Fhi:I:L:n:p:P:qr:R:tT:w:W:"); |
a149fcc7 JM |
348 | if (c < 0) |
349 | break; | |
350 | switch (c) { | |
644fb8c8 JM |
351 | case 'c': |
352 | ctrl_iface = 1; | |
353 | break; | |
a149fcc7 JM |
354 | case 'd': |
355 | if (wpa_debug_level > 0) | |
356 | wpa_debug_level--; | |
357 | break; | |
219fd441 JM |
358 | case 'f': |
359 | if (add_pmk_file(&wt, optarg) < 0) | |
360 | return -1; | |
361 | break; | |
25315176 JM |
362 | case 'F': |
363 | wt.assume_fcs = 1; | |
364 | break; | |
a149fcc7 JM |
365 | case 'h': |
366 | usage(); | |
367 | return 0; | |
368 | case 'i': | |
369 | ifname = optarg; | |
370 | break; | |
3215df77 JM |
371 | case 'I': |
372 | ifname_wired = optarg; | |
373 | break; | |
d33fef57 JM |
374 | case 'L': |
375 | logfile = optarg; | |
376 | break; | |
ba2beacc | 377 | case 'n': |
c99a721e | 378 | wt.pcapng_file = optarg; |
ba2beacc | 379 | break; |
53650bca JM |
380 | case 'p': |
381 | add_passphrase(&wt, optarg); | |
382 | break; | |
d06df64d JM |
383 | case 'P': |
384 | add_secret(&wt, optarg); | |
385 | break; | |
a149fcc7 JM |
386 | case 'q': |
387 | wpa_debug_level++; | |
388 | break; | |
389 | case 'r': | |
390 | read_file = optarg; | |
391 | break; | |
3215df77 JM |
392 | case 'R': |
393 | read_wired_file = optarg; | |
394 | break; | |
de8bb171 JM |
395 | case 't': |
396 | wpa_debug_timestamp = 1; | |
397 | break; | |
a0530dff JM |
398 | case 'T': |
399 | if (add_ptk_file(&wt, optarg) < 0) | |
400 | return -1; | |
401 | break; | |
64f45d07 | 402 | case 'w': |
c99a721e | 403 | wt.write_file = optarg; |
64f45d07 | 404 | break; |
2e479416 | 405 | case 'W': |
9a994178 JM |
406 | if (add_wep(&wt, optarg) < 0) |
407 | return -1; | |
2e479416 | 408 | break; |
a149fcc7 JM |
409 | default: |
410 | usage(); | |
411 | return -1; | |
412 | } | |
413 | } | |
414 | ||
3215df77 JM |
415 | if (ifname == NULL && ifname_wired == NULL && |
416 | read_file == NULL && read_wired_file == NULL) { | |
a149fcc7 JM |
417 | usage(); |
418 | return 0; | |
419 | } | |
420 | ||
421 | if (eloop_init()) | |
422 | return -1; | |
423 | ||
d33fef57 JM |
424 | if (logfile) |
425 | wpa_debug_open_file(logfile); | |
426 | ||
c99a721e | 427 | if (wt.write_file && write_pcap_init(&wt, wt.write_file) < 0) |
64f45d07 JM |
428 | return -1; |
429 | ||
c99a721e | 430 | if (wt.pcapng_file && write_pcapng_init(&wt, wt.pcapng_file) < 0) |
ba2beacc JM |
431 | return -1; |
432 | ||
3215df77 JM |
433 | if (read_wired_file && read_wired_cap_file(&wt, read_wired_file) < 0) |
434 | return -1; | |
435 | ||
a149fcc7 JM |
436 | if (read_file && read_cap_file(&wt, read_file) < 0) |
437 | return -1; | |
438 | ||
439 | if (ifname && monitor_init(&wt, ifname) < 0) | |
440 | return -1; | |
441 | ||
3215df77 JM |
442 | if (ifname_wired && monitor_init_wired(&wt, ifname_wired) < 0) |
443 | return -1; | |
444 | ||
644fb8c8 JM |
445 | if (ctrl_iface && ctrl_init(&wt) < 0) |
446 | return -1; | |
447 | ||
a149fcc7 JM |
448 | eloop_register_signal_terminate(wlantest_terminate, &wt); |
449 | ||
450 | eloop_run(); | |
451 | ||
452 | wpa_printf(MSG_INFO, "Processed: rx_mgmt=%u rx_ctrl=%u rx_data=%u " | |
453 | "fcs_error=%u", | |
454 | wt.rx_mgmt, wt.rx_ctrl, wt.rx_data, wt.fcs_error); | |
455 | ||
d84d3893 | 456 | wlantest_deinit(&wt); |
a149fcc7 | 457 | |
d33fef57 | 458 | wpa_debug_close_file(); |
a149fcc7 JM |
459 | eloop_destroy(); |
460 | os_program_deinit(); | |
461 | ||
462 | return 0; | |
463 | } |