]> git.ipfire.org Git - thirdparty/hostap.git/blob - wlantest/rx_mgmt.c
Prepare for hostapd/wpa_supplicant v2.0 release
[thirdparty/hostap.git] / wlantest / rx_mgmt.c
1 /*
2 * Received Management frame processing
3 * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
4 *
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
7 */
8
9 #include "utils/includes.h"
10
11 #include "utils/common.h"
12 #include "common/ieee802_11_defs.h"
13 #include "common/ieee802_11_common.h"
14 #include "crypto/aes_wrap.h"
15 #include "wlantest.h"
16
17
18 static const char * mgmt_stype(u16 stype)
19 {
20 switch (stype) {
21 case WLAN_FC_STYPE_ASSOC_REQ:
22 return "ASSOC-REQ";
23 case WLAN_FC_STYPE_ASSOC_RESP:
24 return "ASSOC-RESP";
25 case WLAN_FC_STYPE_REASSOC_REQ:
26 return "REASSOC-REQ";
27 case WLAN_FC_STYPE_REASSOC_RESP:
28 return "REASSOC-RESP";
29 case WLAN_FC_STYPE_PROBE_REQ:
30 return "PROBE-REQ";
31 case WLAN_FC_STYPE_PROBE_RESP:
32 return "PROBE-RESP";
33 case WLAN_FC_STYPE_BEACON:
34 return "BEACON";
35 case WLAN_FC_STYPE_ATIM:
36 return "ATIM";
37 case WLAN_FC_STYPE_DISASSOC:
38 return "DISASSOC";
39 case WLAN_FC_STYPE_AUTH:
40 return "AUTH";
41 case WLAN_FC_STYPE_DEAUTH:
42 return "DEAUTH";
43 case WLAN_FC_STYPE_ACTION:
44 return "ACTION";
45 }
46 return "??";
47 }
48
49
50 static void rx_mgmt_beacon(struct wlantest *wt, const u8 *data, size_t len)
51 {
52 const struct ieee80211_mgmt *mgmt;
53 struct wlantest_bss *bss;
54 struct ieee802_11_elems elems;
55
56 mgmt = (const struct ieee80211_mgmt *) data;
57 bss = bss_get(wt, mgmt->bssid);
58 if (bss == NULL)
59 return;
60 if (bss->proberesp_seen)
61 return; /* do not override with Beacon data */
62 bss->capab_info = le_to_host16(mgmt->u.beacon.capab_info);
63 if (ieee802_11_parse_elems(mgmt->u.beacon.variable,
64 len - (mgmt->u.beacon.variable - data),
65 &elems, 0) == ParseFailed) {
66 if (bss->parse_error_reported)
67 return;
68 wpa_printf(MSG_INFO, "Invalid IEs in a Beacon frame from "
69 MACSTR, MAC2STR(mgmt->sa));
70 bss->parse_error_reported = 1;
71 return;
72 }
73
74 bss_update(wt, bss, &elems);
75 }
76
77
78 static void rx_mgmt_probe_resp(struct wlantest *wt, const u8 *data, size_t len)
79 {
80 const struct ieee80211_mgmt *mgmt;
81 struct wlantest_bss *bss;
82 struct ieee802_11_elems elems;
83
84 mgmt = (const struct ieee80211_mgmt *) data;
85 bss = bss_get(wt, mgmt->bssid);
86 if (bss == NULL)
87 return;
88
89 bss->capab_info = le_to_host16(mgmt->u.probe_resp.capab_info);
90 if (ieee802_11_parse_elems(mgmt->u.probe_resp.variable,
91 len - (mgmt->u.probe_resp.variable - data),
92 &elems, 0) == ParseFailed) {
93 if (bss->parse_error_reported)
94 return;
95 wpa_printf(MSG_INFO, "Invalid IEs in a Probe Response frame "
96 "from " MACSTR, MAC2STR(mgmt->sa));
97 bss->parse_error_reported = 1;
98 return;
99 }
100
101 bss_update(wt, bss, &elems);
102 }
103
104
105 static void rx_mgmt_auth(struct wlantest *wt, const u8 *data, size_t len)
106 {
107 const struct ieee80211_mgmt *mgmt;
108 struct wlantest_bss *bss;
109 struct wlantest_sta *sta;
110 u16 alg, trans, status;
111
112 mgmt = (const struct ieee80211_mgmt *) data;
113 bss = bss_get(wt, mgmt->bssid);
114 if (bss == NULL)
115 return;
116 if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0)
117 sta = sta_get(bss, mgmt->da);
118 else
119 sta = sta_get(bss, mgmt->sa);
120 if (sta == NULL)
121 return;
122
123 if (len < 24 + 6) {
124 wpa_printf(MSG_INFO, "Too short Authentication frame from "
125 MACSTR, MAC2STR(mgmt->sa));
126 return;
127 }
128
129 alg = le_to_host16(mgmt->u.auth.auth_alg);
130 trans = le_to_host16(mgmt->u.auth.auth_transaction);
131 status = le_to_host16(mgmt->u.auth.status_code);
132
133 wpa_printf(MSG_DEBUG, "AUTH " MACSTR " -> " MACSTR
134 " (alg=%u trans=%u status=%u)",
135 MAC2STR(mgmt->sa), MAC2STR(mgmt->da), alg, trans, status);
136
137 if (alg == 0 && trans == 2 && status == 0) {
138 if (sta->state == STATE1) {
139 wpa_printf(MSG_DEBUG, "STA " MACSTR
140 " moved to State 2 with " MACSTR,
141 MAC2STR(sta->addr), MAC2STR(bss->bssid));
142 sta->state = STATE2;
143 }
144 }
145
146 if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0)
147 sta->counters[WLANTEST_STA_COUNTER_AUTH_RX]++;
148 else
149 sta->counters[WLANTEST_STA_COUNTER_AUTH_TX]++;
150 }
151
152
153 static void deauth_all_stas(struct wlantest_bss *bss)
154 {
155 struct wlantest_sta *sta;
156 dl_list_for_each(sta, &bss->sta, struct wlantest_sta, list) {
157 if (sta->state == STATE1)
158 continue;
159 wpa_printf(MSG_DEBUG, "STA " MACSTR
160 " moved to State 1 with " MACSTR,
161 MAC2STR(sta->addr), MAC2STR(bss->bssid));
162 sta->state = STATE1;
163 }
164 }
165
166
167 static void tdls_link_down(struct wlantest_bss *bss, struct wlantest_sta *sta)
168 {
169 struct wlantest_tdls *tdls;
170 dl_list_for_each(tdls, &bss->tdls, struct wlantest_tdls, list) {
171 if ((tdls->init == sta || tdls->resp == sta) && tdls->link_up)
172 {
173 wpa_printf(MSG_DEBUG, "TDLS: Set link down based on "
174 "STA deauth/disassoc");
175 tdls->link_up = 0;
176 }
177 }
178 }
179
180
181 static void rx_mgmt_deauth(struct wlantest *wt, const u8 *data, size_t len,
182 int valid)
183 {
184 const struct ieee80211_mgmt *mgmt;
185 struct wlantest_bss *bss;
186 struct wlantest_sta *sta;
187 u16 fc, reason;
188
189 mgmt = (const struct ieee80211_mgmt *) data;
190 bss = bss_get(wt, mgmt->bssid);
191 if (bss == NULL)
192 return;
193 if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0)
194 sta = sta_get(bss, mgmt->da);
195 else
196 sta = sta_get(bss, mgmt->sa);
197
198 if (len < 24 + 2) {
199 wpa_printf(MSG_INFO, "Too short Deauthentication frame from "
200 MACSTR, MAC2STR(mgmt->sa));
201 return;
202 }
203
204 reason = le_to_host16(mgmt->u.deauth.reason_code);
205 wpa_printf(MSG_DEBUG, "DEAUTH " MACSTR " -> " MACSTR
206 " (reason=%u) (valid=%d)",
207 MAC2STR(mgmt->sa), MAC2STR(mgmt->da),
208 reason, valid);
209 wpa_hexdump(MSG_MSGDUMP, "DEAUTH payload", data + 24, len - 24);
210
211 if (sta == NULL) {
212 if (valid && mgmt->da[0] == 0xff)
213 deauth_all_stas(bss);
214 return;
215 }
216
217 if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0) {
218 sta->counters[valid ? WLANTEST_STA_COUNTER_VALID_DEAUTH_RX :
219 WLANTEST_STA_COUNTER_INVALID_DEAUTH_RX]++;
220 if (sta->pwrmgt && !sta->pspoll)
221 sta->counters[WLANTEST_STA_COUNTER_DEAUTH_RX_ASLEEP]++;
222 else
223 sta->counters[WLANTEST_STA_COUNTER_DEAUTH_RX_AWAKE]++;
224
225 fc = le_to_host16(mgmt->frame_control);
226 if (!(fc & WLAN_FC_ISWEP) && reason == 6)
227 sta->counters[WLANTEST_STA_COUNTER_DEAUTH_RX_RC6]++;
228 else if (!(fc & WLAN_FC_ISWEP) && reason == 7)
229 sta->counters[WLANTEST_STA_COUNTER_DEAUTH_RX_RC7]++;
230 } else
231 sta->counters[valid ? WLANTEST_STA_COUNTER_VALID_DEAUTH_TX :
232 WLANTEST_STA_COUNTER_INVALID_DEAUTH_TX]++;
233
234 if (!valid) {
235 wpa_printf(MSG_INFO, "Do not change STA " MACSTR " State "
236 "since Disassociation frame was not protected "
237 "correctly", MAC2STR(sta->addr));
238 return;
239 }
240
241 if (sta->state != STATE1) {
242 wpa_printf(MSG_DEBUG, "STA " MACSTR
243 " moved to State 1 with " MACSTR,
244 MAC2STR(sta->addr), MAC2STR(bss->bssid));
245 sta->state = STATE1;
246 }
247 tdls_link_down(bss, sta);
248 }
249
250
251 static void rx_mgmt_assoc_req(struct wlantest *wt, const u8 *data, size_t len)
252 {
253 const struct ieee80211_mgmt *mgmt;
254 struct wlantest_bss *bss;
255 struct wlantest_sta *sta;
256 struct ieee802_11_elems elems;
257
258 mgmt = (const struct ieee80211_mgmt *) data;
259 bss = bss_get(wt, mgmt->bssid);
260 if (bss == NULL)
261 return;
262 sta = sta_get(bss, mgmt->sa);
263 if (sta == NULL)
264 return;
265
266 if (len < 24 + 4) {
267 wpa_printf(MSG_INFO, "Too short Association Request frame "
268 "from " MACSTR, MAC2STR(mgmt->sa));
269 return;
270 }
271
272 wpa_printf(MSG_DEBUG, "ASSOCREQ " MACSTR " -> " MACSTR
273 " (capab=0x%x listen_int=%u)",
274 MAC2STR(mgmt->sa), MAC2STR(mgmt->da),
275 le_to_host16(mgmt->u.assoc_req.capab_info),
276 le_to_host16(mgmt->u.assoc_req.listen_interval));
277
278 sta->counters[WLANTEST_STA_COUNTER_ASSOCREQ_TX]++;
279
280 if (ieee802_11_parse_elems(mgmt->u.assoc_req.variable,
281 len - (mgmt->u.assoc_req.variable - data),
282 &elems, 0) == ParseFailed) {
283 wpa_printf(MSG_INFO, "Invalid IEs in Association Request "
284 "frame from " MACSTR, MAC2STR(mgmt->sa));
285 return;
286 }
287
288 sta->assocreq_capab_info = le_to_host16(mgmt->u.assoc_req.capab_info);
289 sta->assocreq_listen_int =
290 le_to_host16(mgmt->u.assoc_req.listen_interval);
291 os_free(sta->assocreq_ies);
292 sta->assocreq_ies_len = len - (mgmt->u.assoc_req.variable - data);
293 sta->assocreq_ies = os_malloc(sta->assocreq_ies_len);
294 if (sta->assocreq_ies)
295 os_memcpy(sta->assocreq_ies, mgmt->u.assoc_req.variable,
296 sta->assocreq_ies_len);
297
298 sta_update_assoc(sta, &elems);
299 }
300
301
302 static void rx_mgmt_assoc_resp(struct wlantest *wt, const u8 *data, size_t len)
303 {
304 const struct ieee80211_mgmt *mgmt;
305 struct wlantest_bss *bss;
306 struct wlantest_sta *sta;
307 u16 capab, status, aid;
308
309 mgmt = (const struct ieee80211_mgmt *) data;
310 bss = bss_get(wt, mgmt->bssid);
311 if (bss == NULL)
312 return;
313 sta = sta_get(bss, mgmt->da);
314 if (sta == NULL)
315 return;
316
317 if (len < 24 + 6) {
318 wpa_printf(MSG_INFO, "Too short Association Response frame "
319 "from " MACSTR, MAC2STR(mgmt->sa));
320 return;
321 }
322
323 capab = le_to_host16(mgmt->u.assoc_resp.capab_info);
324 status = le_to_host16(mgmt->u.assoc_resp.status_code);
325 aid = le_to_host16(mgmt->u.assoc_resp.aid);
326
327 wpa_printf(MSG_DEBUG, "ASSOCRESP " MACSTR " -> " MACSTR
328 " (capab=0x%x status=%u aid=%u)",
329 MAC2STR(mgmt->sa), MAC2STR(mgmt->da), capab, status,
330 aid & 0x3fff);
331
332 if (status == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) {
333 struct ieee802_11_elems elems;
334 const u8 *ies = mgmt->u.assoc_resp.variable;
335 size_t ies_len = len - (mgmt->u.assoc_resp.variable - data);
336 if (ieee802_11_parse_elems(ies, ies_len, &elems, 0) ==
337 ParseFailed) {
338 wpa_printf(MSG_INFO, "Failed to parse IEs in "
339 "AssocResp from " MACSTR,
340 MAC2STR(mgmt->sa));
341 } else if (elems.timeout_int == NULL ||
342 elems.timeout_int_len != 5 ||
343 elems.timeout_int[0] !=
344 WLAN_TIMEOUT_ASSOC_COMEBACK) {
345 wpa_printf(MSG_INFO, "No valid Timeout Interval IE "
346 "with Assoc Comeback time in AssocResp "
347 "(status=30) from " MACSTR,
348 MAC2STR(mgmt->sa));
349 } else {
350 sta->counters[
351 WLANTEST_STA_COUNTER_ASSOCRESP_COMEBACK]++;
352 }
353 }
354
355 if (status)
356 return;
357
358 if ((aid & 0xc000) != 0xc000) {
359 wpa_printf(MSG_DEBUG, "Two MSBs of the AID were not set to 1 "
360 "in Association Response from " MACSTR,
361 MAC2STR(mgmt->sa));
362 }
363 sta->aid = aid & 0xc000;
364
365 if (sta->state < STATE2) {
366 wpa_printf(MSG_DEBUG, "STA " MACSTR " was not in State 2 when "
367 "getting associated", MAC2STR(sta->addr));
368 }
369
370 if (sta->state < STATE3) {
371 wpa_printf(MSG_DEBUG, "STA " MACSTR
372 " moved to State 3 with " MACSTR,
373 MAC2STR(sta->addr), MAC2STR(bss->bssid));
374 sta->state = STATE3;
375 }
376 }
377
378
379 static void rx_mgmt_reassoc_req(struct wlantest *wt, const u8 *data,
380 size_t len)
381 {
382 const struct ieee80211_mgmt *mgmt;
383 struct wlantest_bss *bss;
384 struct wlantest_sta *sta;
385 struct ieee802_11_elems elems;
386
387 mgmt = (const struct ieee80211_mgmt *) data;
388 bss = bss_get(wt, mgmt->bssid);
389 if (bss == NULL)
390 return;
391 sta = sta_get(bss, mgmt->sa);
392 if (sta == NULL)
393 return;
394
395 if (len < 24 + 4 + ETH_ALEN) {
396 wpa_printf(MSG_INFO, "Too short Reassociation Request frame "
397 "from " MACSTR, MAC2STR(mgmt->sa));
398 return;
399 }
400
401 wpa_printf(MSG_DEBUG, "REASSOCREQ " MACSTR " -> " MACSTR
402 " (capab=0x%x listen_int=%u current_ap=" MACSTR ")",
403 MAC2STR(mgmt->sa), MAC2STR(mgmt->da),
404 le_to_host16(mgmt->u.reassoc_req.capab_info),
405 le_to_host16(mgmt->u.reassoc_req.listen_interval),
406 MAC2STR(mgmt->u.reassoc_req.current_ap));
407
408 sta->counters[WLANTEST_STA_COUNTER_REASSOCREQ_TX]++;
409
410 if (ieee802_11_parse_elems(mgmt->u.reassoc_req.variable,
411 len - (mgmt->u.reassoc_req.variable - data),
412 &elems, 0) == ParseFailed) {
413 wpa_printf(MSG_INFO, "Invalid IEs in Reassociation Request "
414 "frame from " MACSTR, MAC2STR(mgmt->sa));
415 return;
416 }
417
418 sta->assocreq_capab_info =
419 le_to_host16(mgmt->u.reassoc_req.capab_info);
420 sta->assocreq_listen_int =
421 le_to_host16(mgmt->u.reassoc_req.listen_interval);
422 os_free(sta->assocreq_ies);
423 sta->assocreq_ies_len = len - (mgmt->u.reassoc_req.variable - data);
424 sta->assocreq_ies = os_malloc(sta->assocreq_ies_len);
425 if (sta->assocreq_ies)
426 os_memcpy(sta->assocreq_ies, mgmt->u.reassoc_req.variable,
427 sta->assocreq_ies_len);
428
429 sta_update_assoc(sta, &elems);
430 }
431
432
433 static void rx_mgmt_reassoc_resp(struct wlantest *wt, const u8 *data,
434 size_t len)
435 {
436 const struct ieee80211_mgmt *mgmt;
437 struct wlantest_bss *bss;
438 struct wlantest_sta *sta;
439 u16 capab, status, aid;
440
441 mgmt = (const struct ieee80211_mgmt *) data;
442 bss = bss_get(wt, mgmt->bssid);
443 if (bss == NULL)
444 return;
445 sta = sta_get(bss, mgmt->da);
446 if (sta == NULL)
447 return;
448
449 if (len < 24 + 6) {
450 wpa_printf(MSG_INFO, "Too short Reassociation Response frame "
451 "from " MACSTR, MAC2STR(mgmt->sa));
452 return;
453 }
454
455 capab = le_to_host16(mgmt->u.reassoc_resp.capab_info);
456 status = le_to_host16(mgmt->u.reassoc_resp.status_code);
457 aid = le_to_host16(mgmt->u.reassoc_resp.aid);
458
459 wpa_printf(MSG_DEBUG, "REASSOCRESP " MACSTR " -> " MACSTR
460 " (capab=0x%x status=%u aid=%u)",
461 MAC2STR(mgmt->sa), MAC2STR(mgmt->da), capab, status,
462 aid & 0x3fff);
463
464 if (status == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) {
465 struct ieee802_11_elems elems;
466 const u8 *ies = mgmt->u.reassoc_resp.variable;
467 size_t ies_len = len - (mgmt->u.reassoc_resp.variable - data);
468 if (ieee802_11_parse_elems(ies, ies_len, &elems, 0) ==
469 ParseFailed) {
470 wpa_printf(MSG_INFO, "Failed to parse IEs in "
471 "ReassocResp from " MACSTR,
472 MAC2STR(mgmt->sa));
473 } else if (elems.timeout_int == NULL ||
474 elems.timeout_int_len != 5 ||
475 elems.timeout_int[0] !=
476 WLAN_TIMEOUT_ASSOC_COMEBACK) {
477 wpa_printf(MSG_INFO, "No valid Timeout Interval IE "
478 "with Assoc Comeback time in ReassocResp "
479 "(status=30) from " MACSTR,
480 MAC2STR(mgmt->sa));
481 } else {
482 sta->counters[
483 WLANTEST_STA_COUNTER_REASSOCRESP_COMEBACK]++;
484 }
485 }
486
487 if (status)
488 return;
489
490 if ((aid & 0xc000) != 0xc000) {
491 wpa_printf(MSG_DEBUG, "Two MSBs of the AID were not set to 1 "
492 "in Reassociation Response from " MACSTR,
493 MAC2STR(mgmt->sa));
494 }
495 sta->aid = aid & 0xc000;
496
497 if (sta->state < STATE2) {
498 wpa_printf(MSG_DEBUG, "STA " MACSTR " was not in State 2 when "
499 "getting associated", MAC2STR(sta->addr));
500 }
501
502 if (sta->state < STATE3) {
503 wpa_printf(MSG_DEBUG, "STA " MACSTR
504 " moved to State 3 with " MACSTR,
505 MAC2STR(sta->addr), MAC2STR(bss->bssid));
506 sta->state = STATE3;
507 }
508 }
509
510
511 static void disassoc_all_stas(struct wlantest_bss *bss)
512 {
513 struct wlantest_sta *sta;
514 dl_list_for_each(sta, &bss->sta, struct wlantest_sta, list) {
515 if (sta->state <= STATE2)
516 continue;
517 wpa_printf(MSG_DEBUG, "STA " MACSTR
518 " moved to State 2 with " MACSTR,
519 MAC2STR(sta->addr), MAC2STR(bss->bssid));
520 sta->state = STATE2;
521 }
522 }
523
524
525 static void rx_mgmt_disassoc(struct wlantest *wt, const u8 *data, size_t len,
526 int valid)
527 {
528 const struct ieee80211_mgmt *mgmt;
529 struct wlantest_bss *bss;
530 struct wlantest_sta *sta;
531 u16 fc, reason;
532
533 mgmt = (const struct ieee80211_mgmt *) data;
534 bss = bss_get(wt, mgmt->bssid);
535 if (bss == NULL)
536 return;
537 if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0)
538 sta = sta_get(bss, mgmt->da);
539 else
540 sta = sta_get(bss, mgmt->sa);
541
542 if (len < 24 + 2) {
543 wpa_printf(MSG_INFO, "Too short Disassociation frame from "
544 MACSTR, MAC2STR(mgmt->sa));
545 return;
546 }
547
548 reason = le_to_host16(mgmt->u.disassoc.reason_code);
549 wpa_printf(MSG_DEBUG, "DISASSOC " MACSTR " -> " MACSTR
550 " (reason=%u) (valid=%d)",
551 MAC2STR(mgmt->sa), MAC2STR(mgmt->da),
552 reason, valid);
553 wpa_hexdump(MSG_MSGDUMP, "DISASSOC payload", data + 24, len - 24);
554
555 if (sta == NULL) {
556 if (valid && mgmt->da[0] == 0xff)
557 disassoc_all_stas(bss);
558 return;
559 }
560
561 if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0) {
562 sta->counters[valid ? WLANTEST_STA_COUNTER_VALID_DISASSOC_RX :
563 WLANTEST_STA_COUNTER_INVALID_DISASSOC_RX]++;
564 if (sta->pwrmgt && !sta->pspoll)
565 sta->counters[
566 WLANTEST_STA_COUNTER_DISASSOC_RX_ASLEEP]++;
567 else
568 sta->counters[
569 WLANTEST_STA_COUNTER_DISASSOC_RX_AWAKE]++;
570
571 fc = le_to_host16(mgmt->frame_control);
572 if (!(fc & WLAN_FC_ISWEP) && reason == 6)
573 sta->counters[WLANTEST_STA_COUNTER_DISASSOC_RX_RC6]++;
574 else if (!(fc & WLAN_FC_ISWEP) && reason == 7)
575 sta->counters[WLANTEST_STA_COUNTER_DISASSOC_RX_RC7]++;
576 } else
577 sta->counters[valid ? WLANTEST_STA_COUNTER_VALID_DISASSOC_TX :
578 WLANTEST_STA_COUNTER_INVALID_DISASSOC_TX]++;
579
580 if (!valid) {
581 wpa_printf(MSG_INFO, "Do not change STA " MACSTR " State "
582 "since Disassociation frame was not protected "
583 "correctly", MAC2STR(sta->addr));
584 return;
585 }
586
587 if (sta->state < STATE2) {
588 wpa_printf(MSG_DEBUG, "STA " MACSTR " was not in State 2 or 3 "
589 "when getting disassociated", MAC2STR(sta->addr));
590 }
591
592 if (sta->state > STATE2) {
593 wpa_printf(MSG_DEBUG, "STA " MACSTR
594 " moved to State 2 with " MACSTR,
595 MAC2STR(sta->addr), MAC2STR(bss->bssid));
596 sta->state = STATE2;
597 }
598 tdls_link_down(bss, sta);
599 }
600
601
602 static void rx_mgmt_action_sa_query_req(struct wlantest *wt,
603 struct wlantest_sta *sta,
604 const struct ieee80211_mgmt *mgmt,
605 size_t len, int valid)
606 {
607 const u8 *rx_id;
608 u8 *id;
609
610 rx_id = (const u8 *) mgmt->u.action.u.sa_query_req.trans_id;
611 if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0)
612 id = sta->ap_sa_query_tr;
613 else
614 id = sta->sta_sa_query_tr;
615 wpa_printf(MSG_INFO, "SA Query Request " MACSTR " -> " MACSTR
616 " (trans_id=%02x%02x)%s",
617 MAC2STR(mgmt->sa), MAC2STR(mgmt->da), rx_id[0], rx_id[1],
618 valid ? "" : " (invalid protection)");
619 os_memcpy(id, mgmt->u.action.u.sa_query_req.trans_id, 2);
620 if (os_memcmp(mgmt->sa, sta->addr, ETH_ALEN) == 0)
621 sta->counters[valid ?
622 WLANTEST_STA_COUNTER_VALID_SAQUERYREQ_TX :
623 WLANTEST_STA_COUNTER_INVALID_SAQUERYREQ_TX]++;
624 else
625 sta->counters[valid ?
626 WLANTEST_STA_COUNTER_VALID_SAQUERYREQ_RX :
627 WLANTEST_STA_COUNTER_INVALID_SAQUERYREQ_RX]++;
628 }
629
630
631 static void rx_mgmt_action_sa_query_resp(struct wlantest *wt,
632 struct wlantest_sta *sta,
633 const struct ieee80211_mgmt *mgmt,
634 size_t len, int valid)
635 {
636 const u8 *rx_id;
637 u8 *id;
638 int match;
639
640 rx_id = (const u8 *) mgmt->u.action.u.sa_query_resp.trans_id;
641 if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0)
642 id = sta->sta_sa_query_tr;
643 else
644 id = sta->ap_sa_query_tr;
645 match = os_memcmp(rx_id, id, 2) == 0;
646 wpa_printf(MSG_INFO, "SA Query Response " MACSTR " -> " MACSTR
647 " (trans_id=%02x%02x; %s)%s",
648 MAC2STR(mgmt->sa), MAC2STR(mgmt->da), rx_id[0], rx_id[1],
649 match ? "match" : "mismatch",
650 valid ? "" : " (invalid protection)");
651 if (os_memcmp(mgmt->sa, sta->addr, ETH_ALEN) == 0)
652 sta->counters[(valid && match) ?
653 WLANTEST_STA_COUNTER_VALID_SAQUERYRESP_TX :
654 WLANTEST_STA_COUNTER_INVALID_SAQUERYRESP_TX]++;
655 else
656 sta->counters[(valid && match) ?
657 WLANTEST_STA_COUNTER_VALID_SAQUERYRESP_RX :
658 WLANTEST_STA_COUNTER_INVALID_SAQUERYRESP_RX]++;
659 }
660
661
662 static void rx_mgmt_action_sa_query(struct wlantest *wt,
663 struct wlantest_sta *sta,
664 const struct ieee80211_mgmt *mgmt,
665 size_t len, int valid)
666 {
667 if (len < 24 + 2 + WLAN_SA_QUERY_TR_ID_LEN) {
668 wpa_printf(MSG_INFO, "Too short SA Query frame from " MACSTR,
669 MAC2STR(mgmt->sa));
670 return;
671 }
672
673 if (len > 24 + 2 + WLAN_SA_QUERY_TR_ID_LEN) {
674 size_t elen = len - (24 + 2 + WLAN_SA_QUERY_TR_ID_LEN);
675 wpa_printf(MSG_INFO, "Unexpected %u octets of extra data at "
676 "the end of SA Query frame from " MACSTR,
677 (unsigned) elen, MAC2STR(mgmt->sa));
678 wpa_hexdump(MSG_INFO, "SA Query extra data",
679 ((const u8 *) mgmt) + len - elen, elen);
680 }
681
682 switch (mgmt->u.action.u.sa_query_req.action) {
683 case WLAN_SA_QUERY_REQUEST:
684 rx_mgmt_action_sa_query_req(wt, sta, mgmt, len, valid);
685 break;
686 case WLAN_SA_QUERY_RESPONSE:
687 rx_mgmt_action_sa_query_resp(wt, sta, mgmt, len, valid);
688 break;
689 default:
690 wpa_printf(MSG_INFO, "Unexpected SA Query action value %u "
691 "from " MACSTR,
692 mgmt->u.action.u.sa_query_req.action,
693 MAC2STR(mgmt->sa));
694 }
695 }
696
697
698 static void rx_mgmt_action(struct wlantest *wt, const u8 *data, size_t len,
699 int valid)
700 {
701 const struct ieee80211_mgmt *mgmt;
702 struct wlantest_bss *bss;
703 struct wlantest_sta *sta;
704
705 mgmt = (const struct ieee80211_mgmt *) data;
706 if (mgmt->da[0] & 0x01) {
707 wpa_printf(MSG_DEBUG, "Group addressed Action frame: DA="
708 MACSTR " SA=" MACSTR " BSSID=" MACSTR
709 " category=%u",
710 MAC2STR(mgmt->da), MAC2STR(mgmt->sa),
711 MAC2STR(mgmt->bssid), mgmt->u.action.category);
712 return; /* Ignore group addressed Action frames for now */
713 }
714 bss = bss_get(wt, mgmt->bssid);
715 if (bss == NULL)
716 return;
717 if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0)
718 sta = sta_get(bss, mgmt->da);
719 else
720 sta = sta_get(bss, mgmt->sa);
721 if (sta == NULL)
722 return;
723
724 if (len < 24 + 1) {
725 wpa_printf(MSG_INFO, "Too short Action frame from "
726 MACSTR, MAC2STR(mgmt->sa));
727 return;
728 }
729
730 wpa_printf(MSG_DEBUG, "ACTION " MACSTR " -> " MACSTR
731 " (category=%u) (valid=%d)",
732 MAC2STR(mgmt->sa), MAC2STR(mgmt->da),
733 mgmt->u.action.category, valid);
734 wpa_hexdump(MSG_MSGDUMP, "ACTION payload", data + 24, len - 24);
735
736 if (mgmt->u.action.category != WLAN_ACTION_PUBLIC &&
737 sta->state < STATE3) {
738 wpa_printf(MSG_INFO, "Action frame sent when STA is not in "
739 "State 3 (SA=" MACSTR " DATA=" MACSTR ")",
740 MAC2STR(mgmt->sa), MAC2STR(mgmt->da));
741 }
742
743 switch (mgmt->u.action.category) {
744 case WLAN_ACTION_SA_QUERY:
745 rx_mgmt_action_sa_query(wt, sta, mgmt, len, valid);
746 break;
747 }
748 }
749
750
751 static int check_mmie_mic(const u8 *igtk, const u8 *data, size_t len)
752 {
753 u8 *buf;
754 u8 mic[16];
755 u16 fc;
756 const struct ieee80211_hdr *hdr;
757
758 buf = os_malloc(len + 20 - 24);
759 if (buf == NULL)
760 return -1;
761
762 /* BIP AAD: FC(masked) A1 A2 A3 */
763 hdr = (const struct ieee80211_hdr *) data;
764 fc = le_to_host16(hdr->frame_control);
765 fc &= ~(WLAN_FC_RETRY | WLAN_FC_PWRMGT | WLAN_FC_MOREDATA);
766 WPA_PUT_LE16(buf, fc);
767 os_memcpy(buf + 2, hdr->addr1, 3 * ETH_ALEN);
768
769 /* Frame body with MMIE MIC masked to zero */
770 os_memcpy(buf + 20, data + 24, len - 24 - 8);
771 os_memset(buf + 20 + len - 24 - 8, 0, 8);
772
773 wpa_hexdump(MSG_MSGDUMP, "BIP: AAD|Body(masked)", buf, len + 20 - 24);
774 /* MIC = L(AES-128-CMAC(AAD || Frame Body(masked)), 0, 64) */
775 if (omac1_aes_128(igtk, buf, len + 20 - 24, mic) < 0) {
776 os_free(buf);
777 return -1;
778 }
779
780 os_free(buf);
781
782 if (os_memcmp(data + len - 8, mic, 8) != 0)
783 return -1;
784
785 return 0;
786 }
787
788
789 static int check_bip(struct wlantest *wt, const u8 *data, size_t len)
790 {
791 const struct ieee80211_mgmt *mgmt;
792 u16 fc, stype;
793 const u8 *mmie;
794 u16 keyid;
795 struct wlantest_bss *bss;
796
797 mgmt = (const struct ieee80211_mgmt *) data;
798 fc = le_to_host16(mgmt->frame_control);
799 stype = WLAN_FC_GET_STYPE(fc);
800
801 if (stype == WLAN_FC_STYPE_ACTION) {
802 if (len < 24 + 1)
803 return 0;
804 if (mgmt->u.action.category == WLAN_ACTION_PUBLIC)
805 return 0; /* Not a robust management frame */
806 }
807
808 bss = bss_get(wt, mgmt->bssid);
809 if (bss == NULL)
810 return 0; /* No key known yet */
811
812 if (len < 24 + 18 || data[len - 18] != WLAN_EID_MMIE ||
813 data[len - 17] != 16) {
814 /* No MMIE */
815 if (bss->rsn_capab & WPA_CAPABILITY_MFPC) {
816 wpa_printf(MSG_INFO, "Robust group-addressed "
817 "management frame sent without BIP by "
818 MACSTR, MAC2STR(mgmt->sa));
819 bss->counters[WLANTEST_BSS_COUNTER_MISSING_BIP_MMIE]++;
820 return -1;
821 }
822 return 0;
823 }
824
825 mmie = data + len - 16;
826 keyid = WPA_GET_LE16(mmie);
827 if (keyid & 0xf000) {
828 wpa_printf(MSG_INFO, "MMIE KeyID reserved bits not zero "
829 "(%04x) from " MACSTR, keyid, MAC2STR(mgmt->sa));
830 keyid &= 0x0fff;
831 }
832 if (keyid < 4 || keyid > 5) {
833 wpa_printf(MSG_INFO, "Unexpected MMIE KeyID %u from " MACSTR,
834 keyid, MAC2STR(mgmt->sa));
835 bss->counters[WLANTEST_BSS_COUNTER_INVALID_BIP_MMIE]++;
836 return 0;
837 }
838 wpa_printf(MSG_DEBUG, "MMIE KeyID %u", keyid);
839 wpa_hexdump(MSG_MSGDUMP, "MMIE IPN", mmie + 2, 6);
840 wpa_hexdump(MSG_MSGDUMP, "MMIE MIC", mmie + 8, 8);
841
842 if (!bss->igtk_set[keyid]) {
843 wpa_printf(MSG_DEBUG, "No IGTK known to validate BIP frame");
844 return 0;
845 }
846
847 if (os_memcmp(mmie + 2, bss->ipn[keyid], 6) <= 0) {
848 wpa_printf(MSG_INFO, "BIP replay detected: SA=" MACSTR,
849 MAC2STR(mgmt->sa));
850 wpa_hexdump(MSG_INFO, "RX IPN", mmie + 2, 6);
851 wpa_hexdump(MSG_INFO, "Last RX IPN", bss->ipn[keyid], 6);
852 }
853
854 if (check_mmie_mic(bss->igtk[keyid], data, len) < 0) {
855 wpa_printf(MSG_INFO, "Invalid MMIE MIC in a frame from "
856 MACSTR, MAC2STR(mgmt->sa));
857 bss->counters[WLANTEST_BSS_COUNTER_INVALID_BIP_MMIE]++;
858 return -1;
859 }
860
861 wpa_printf(MSG_DEBUG, "Valid MMIE MIC");
862 os_memcpy(bss->ipn[keyid], mmie + 2, 6);
863 bss->counters[WLANTEST_BSS_COUNTER_VALID_BIP_MMIE]++;
864
865 if (stype == WLAN_FC_STYPE_DEAUTH)
866 bss->counters[WLANTEST_BSS_COUNTER_BIP_DEAUTH]++;
867 else if (stype == WLAN_FC_STYPE_DISASSOC)
868 bss->counters[WLANTEST_BSS_COUNTER_BIP_DISASSOC]++;
869
870 return 0;
871 }
872
873
874 static u8 * mgmt_ccmp_decrypt(struct wlantest *wt, const u8 *data, size_t len,
875 size_t *dlen)
876 {
877 struct wlantest_bss *bss;
878 struct wlantest_sta *sta;
879 const struct ieee80211_hdr *hdr;
880 int keyid;
881 u8 *decrypted, *frame = NULL;
882 u8 pn[6], *rsc;
883
884 hdr = (const struct ieee80211_hdr *) data;
885 bss = bss_get(wt, hdr->addr3);
886 if (bss == NULL)
887 return NULL;
888 if (os_memcmp(hdr->addr1, hdr->addr3, ETH_ALEN) == 0)
889 sta = sta_get(bss, hdr->addr2);
890 else
891 sta = sta_get(bss, hdr->addr1);
892 if (sta == NULL || !sta->ptk_set) {
893 wpa_printf(MSG_MSGDUMP, "No PTK known to decrypt the frame");
894 return NULL;
895 }
896
897 if (len < 24 + 4)
898 return NULL;
899
900 if (!(data[24 + 3] & 0x20)) {
901 wpa_printf(MSG_INFO, "Expected CCMP frame from " MACSTR
902 " did not have ExtIV bit set to 1",
903 MAC2STR(hdr->addr2));
904 return NULL;
905 }
906
907 if (data[24 + 2] != 0 || (data[24 + 3] & 0x1f) != 0) {
908 wpa_printf(MSG_INFO, "CCMP mgmt frame from " MACSTR " used "
909 "non-zero reserved bit", MAC2STR(hdr->addr2));
910 }
911
912 keyid = data[24 + 3] >> 6;
913 if (keyid != 0) {
914 wpa_printf(MSG_INFO, "Unexpected non-zero KeyID %d in "
915 "individually addressed Management frame from "
916 MACSTR, keyid, MAC2STR(hdr->addr2));
917 }
918
919 if (os_memcmp(hdr->addr1, hdr->addr3, ETH_ALEN) == 0)
920 rsc = sta->rsc_tods[16];
921 else
922 rsc = sta->rsc_fromds[16];
923
924 ccmp_get_pn(pn, data + 24);
925 if (os_memcmp(pn, rsc, 6) <= 0) {
926 u16 seq_ctrl = le_to_host16(hdr->seq_ctrl);
927 wpa_printf(MSG_INFO, "CCMP/TKIP replay detected: A1=" MACSTR
928 " A2=" MACSTR " A3=" MACSTR " seq=%u frag=%u",
929 MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
930 MAC2STR(hdr->addr3),
931 WLAN_GET_SEQ_SEQ(seq_ctrl),
932 WLAN_GET_SEQ_FRAG(seq_ctrl));
933 wpa_hexdump(MSG_INFO, "RX PN", pn, 6);
934 wpa_hexdump(MSG_INFO, "RSC", rsc, 6);
935 }
936
937 decrypted = ccmp_decrypt(sta->ptk.tk1, hdr, data + 24, len - 24, dlen);
938 if (decrypted) {
939 os_memcpy(rsc, pn, 6);
940 frame = os_malloc(24 + *dlen);
941 if (frame) {
942 os_memcpy(frame, data, 24);
943 os_memcpy(frame + 24, decrypted, *dlen);
944 *dlen += 24;
945 }
946 }
947
948 os_free(decrypted);
949
950 return frame;
951 }
952
953
954 static int check_mgmt_ccmp(struct wlantest *wt, const u8 *data, size_t len)
955 {
956 const struct ieee80211_mgmt *mgmt;
957 u16 fc;
958 struct wlantest_bss *bss;
959 struct wlantest_sta *sta;
960
961 mgmt = (const struct ieee80211_mgmt *) data;
962 fc = le_to_host16(mgmt->frame_control);
963
964 if (WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION) {
965 if (len > 24 &&
966 mgmt->u.action.category == WLAN_ACTION_PUBLIC)
967 return 0; /* Not a robust management frame */
968 }
969
970 bss = bss_get(wt, mgmt->bssid);
971 if (bss == NULL)
972 return 0;
973 if (os_memcmp(mgmt->da, mgmt->bssid, ETH_ALEN) == 0)
974 sta = sta_get(bss, mgmt->sa);
975 else
976 sta = sta_get(bss, mgmt->da);
977 if (sta == NULL)
978 return 0;
979
980 if ((sta->rsn_capab & WPA_CAPABILITY_MFPC) &&
981 (sta->state == STATE3 ||
982 WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION)) {
983 wpa_printf(MSG_INFO, "Robust individually-addressed "
984 "management frame sent without CCMP by "
985 MACSTR, MAC2STR(mgmt->sa));
986 return -1;
987 }
988
989 return 0;
990 }
991
992
993 void rx_mgmt(struct wlantest *wt, const u8 *data, size_t len)
994 {
995 const struct ieee80211_hdr *hdr;
996 u16 fc, stype;
997 int valid = 1;
998 u8 *decrypted = NULL;
999 size_t dlen;
1000
1001 if (len < 24)
1002 return;
1003
1004 hdr = (const struct ieee80211_hdr *) data;
1005 fc = le_to_host16(hdr->frame_control);
1006 wt->rx_mgmt++;
1007 stype = WLAN_FC_GET_STYPE(fc);
1008
1009 if ((hdr->addr1[0] & 0x01) &&
1010 (stype == WLAN_FC_STYPE_DEAUTH ||
1011 stype == WLAN_FC_STYPE_DISASSOC ||
1012 stype == WLAN_FC_STYPE_ACTION)) {
1013 if (check_bip(wt, data, len) < 0)
1014 valid = 0;
1015 }
1016
1017 wpa_printf((stype == WLAN_FC_STYPE_BEACON ||
1018 stype == WLAN_FC_STYPE_PROBE_RESP ||
1019 stype == WLAN_FC_STYPE_PROBE_REQ) ?
1020 MSG_EXCESSIVE : MSG_MSGDUMP,
1021 "MGMT %s%s%s DA=" MACSTR " SA=" MACSTR " BSSID=" MACSTR,
1022 mgmt_stype(stype),
1023 fc & WLAN_FC_PWRMGT ? " PwrMgt" : "",
1024 fc & WLAN_FC_ISWEP ? " Prot" : "",
1025 MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
1026 MAC2STR(hdr->addr3));
1027
1028 if ((fc & WLAN_FC_ISWEP) &&
1029 !(hdr->addr1[0] & 0x01) &&
1030 (stype == WLAN_FC_STYPE_DEAUTH ||
1031 stype == WLAN_FC_STYPE_DISASSOC ||
1032 stype == WLAN_FC_STYPE_ACTION)) {
1033 decrypted = mgmt_ccmp_decrypt(wt, data, len, &dlen);
1034 if (decrypted) {
1035 write_pcap_decrypted(wt, decrypted, dlen, NULL, 0);
1036 data = decrypted;
1037 len = dlen;
1038 } else
1039 valid = 0;
1040 }
1041
1042 if (!(fc & WLAN_FC_ISWEP) &&
1043 !(hdr->addr1[0] & 0x01) &&
1044 (stype == WLAN_FC_STYPE_DEAUTH ||
1045 stype == WLAN_FC_STYPE_DISASSOC ||
1046 stype == WLAN_FC_STYPE_ACTION)) {
1047 if (check_mgmt_ccmp(wt, data, len) < 0)
1048 valid = 0;
1049 }
1050
1051 switch (stype) {
1052 case WLAN_FC_STYPE_BEACON:
1053 rx_mgmt_beacon(wt, data, len);
1054 break;
1055 case WLAN_FC_STYPE_PROBE_RESP:
1056 rx_mgmt_probe_resp(wt, data, len);
1057 break;
1058 case WLAN_FC_STYPE_AUTH:
1059 rx_mgmt_auth(wt, data, len);
1060 break;
1061 case WLAN_FC_STYPE_DEAUTH:
1062 rx_mgmt_deauth(wt, data, len, valid);
1063 break;
1064 case WLAN_FC_STYPE_ASSOC_REQ:
1065 rx_mgmt_assoc_req(wt, data, len);
1066 break;
1067 case WLAN_FC_STYPE_ASSOC_RESP:
1068 rx_mgmt_assoc_resp(wt, data, len);
1069 break;
1070 case WLAN_FC_STYPE_REASSOC_REQ:
1071 rx_mgmt_reassoc_req(wt, data, len);
1072 break;
1073 case WLAN_FC_STYPE_REASSOC_RESP:
1074 rx_mgmt_reassoc_resp(wt, data, len);
1075 break;
1076 case WLAN_FC_STYPE_DISASSOC:
1077 rx_mgmt_disassoc(wt, data, len, valid);
1078 break;
1079 case WLAN_FC_STYPE_ACTION:
1080 rx_mgmt_action(wt, data, len, valid);
1081 break;
1082 }
1083
1084 os_free(decrypted);
1085
1086 wt->last_mgmt_valid = valid;
1087 }
1088
1089
1090 static void rx_mgmt_deauth_ack(struct wlantest *wt,
1091 const struct ieee80211_hdr *hdr)
1092 {
1093 const struct ieee80211_mgmt *mgmt;
1094 struct wlantest_bss *bss;
1095 struct wlantest_sta *sta;
1096
1097 mgmt = (const struct ieee80211_mgmt *) hdr;
1098 bss = bss_get(wt, mgmt->bssid);
1099 if (bss == NULL)
1100 return;
1101 if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0)
1102 sta = sta_get(bss, mgmt->da);
1103 else
1104 sta = sta_get(bss, mgmt->sa);
1105 if (sta == NULL)
1106 return;
1107
1108 wpa_printf(MSG_DEBUG, "DEAUTH from " MACSTR " acknowledged by " MACSTR,
1109 MAC2STR(mgmt->sa), MAC2STR(mgmt->da));
1110 if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0) {
1111 int c;
1112 c = wt->last_mgmt_valid ?
1113 WLANTEST_STA_COUNTER_VALID_DEAUTH_RX_ACK :
1114 WLANTEST_STA_COUNTER_INVALID_DEAUTH_RX_ACK;
1115 sta->counters[c]++;
1116 }
1117 }
1118
1119
1120 static void rx_mgmt_disassoc_ack(struct wlantest *wt,
1121 const struct ieee80211_hdr *hdr)
1122 {
1123 const struct ieee80211_mgmt *mgmt;
1124 struct wlantest_bss *bss;
1125 struct wlantest_sta *sta;
1126
1127 mgmt = (const struct ieee80211_mgmt *) hdr;
1128 bss = bss_get(wt, mgmt->bssid);
1129 if (bss == NULL)
1130 return;
1131 if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0)
1132 sta = sta_get(bss, mgmt->da);
1133 else
1134 sta = sta_get(bss, mgmt->sa);
1135 if (sta == NULL)
1136 return;
1137
1138 wpa_printf(MSG_DEBUG, "DISASSOC from " MACSTR " acknowledged by "
1139 MACSTR, MAC2STR(mgmt->sa), MAC2STR(mgmt->da));
1140 if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0) {
1141 int c;
1142 c = wt->last_mgmt_valid ?
1143 WLANTEST_STA_COUNTER_VALID_DISASSOC_RX_ACK :
1144 WLANTEST_STA_COUNTER_INVALID_DISASSOC_RX_ACK;
1145 sta->counters[c]++;
1146 }
1147 }
1148
1149
1150 void rx_mgmt_ack(struct wlantest *wt, const struct ieee80211_hdr *hdr)
1151 {
1152 u16 fc, stype;
1153 fc = le_to_host16(hdr->frame_control);
1154 stype = WLAN_FC_GET_STYPE(fc);
1155
1156 wpa_printf(MSG_MSGDUMP, "MGMT ACK: stype=%u a1=" MACSTR " a2=" MACSTR
1157 " a3=" MACSTR,
1158 stype, MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
1159 MAC2STR(hdr->addr3));
1160
1161 switch (stype) {
1162 case WLAN_FC_STYPE_DEAUTH:
1163 rx_mgmt_deauth_ack(wt, hdr);
1164 break;
1165 case WLAN_FC_STYPE_DISASSOC:
1166 rx_mgmt_disassoc_ack(wt, hdr);
1167 break;
1168 }
1169 }