2 * Received Management frame processing
3 * Copyright (c) 2010-2015, Jouni Malinen <j@w1.fi>
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
9 #include "utils/includes.h"
11 #include "utils/common.h"
12 #include "common/defs.h"
13 #include "common/ieee802_11_defs.h"
14 #include "common/ieee802_11_common.h"
15 #include "crypto/aes_wrap.h"
19 static const char * mgmt_stype(u16 stype
)
22 case WLAN_FC_STYPE_ASSOC_REQ
:
24 case WLAN_FC_STYPE_ASSOC_RESP
:
26 case WLAN_FC_STYPE_REASSOC_REQ
:
28 case WLAN_FC_STYPE_REASSOC_RESP
:
29 return "REASSOC-RESP";
30 case WLAN_FC_STYPE_PROBE_REQ
:
32 case WLAN_FC_STYPE_PROBE_RESP
:
34 case WLAN_FC_STYPE_BEACON
:
36 case WLAN_FC_STYPE_ATIM
:
38 case WLAN_FC_STYPE_DISASSOC
:
40 case WLAN_FC_STYPE_AUTH
:
42 case WLAN_FC_STYPE_DEAUTH
:
44 case WLAN_FC_STYPE_ACTION
:
51 static void rx_mgmt_beacon(struct wlantest
*wt
, const u8
*data
, size_t len
)
53 const struct ieee80211_mgmt
*mgmt
;
54 struct wlantest_bss
*bss
;
55 struct ieee802_11_elems elems
;
58 mgmt
= (const struct ieee80211_mgmt
*) data
;
59 offset
= mgmt
->u
.beacon
.variable
- data
;
62 bss
= bss_get(wt
, mgmt
->bssid
);
65 if (bss
->proberesp_seen
)
66 return; /* do not override with Beacon data */
67 bss
->capab_info
= le_to_host16(mgmt
->u
.beacon
.capab_info
);
68 if (ieee802_11_parse_elems(mgmt
->u
.beacon
.variable
, len
- offset
,
69 &elems
, 0) == ParseFailed
) {
70 if (bss
->parse_error_reported
)
72 add_note(wt
, MSG_INFO
, "Invalid IEs in a Beacon frame from "
73 MACSTR
, MAC2STR(mgmt
->sa
));
74 bss
->parse_error_reported
= 1;
78 bss_update(wt
, bss
, &elems
);
82 static void rx_mgmt_probe_resp(struct wlantest
*wt
, const u8
*data
, size_t len
)
84 const struct ieee80211_mgmt
*mgmt
;
85 struct wlantest_bss
*bss
;
86 struct ieee802_11_elems elems
;
89 mgmt
= (const struct ieee80211_mgmt
*) data
;
90 offset
= mgmt
->u
.probe_resp
.variable
- data
;
93 bss
= bss_get(wt
, mgmt
->bssid
);
97 bss
->counters
[WLANTEST_BSS_COUNTER_PROBE_RESPONSE
]++;
98 bss
->capab_info
= le_to_host16(mgmt
->u
.probe_resp
.capab_info
);
99 if (ieee802_11_parse_elems(mgmt
->u
.probe_resp
.variable
, len
- offset
,
100 &elems
, 0) == ParseFailed
) {
101 if (bss
->parse_error_reported
)
103 add_note(wt
, MSG_INFO
, "Invalid IEs in a Probe Response frame "
104 "from " MACSTR
, MAC2STR(mgmt
->sa
));
105 bss
->parse_error_reported
= 1;
109 bss_update(wt
, bss
, &elems
);
113 static void rx_mgmt_auth(struct wlantest
*wt
, const u8
*data
, size_t len
)
115 const struct ieee80211_mgmt
*mgmt
;
116 struct wlantest_bss
*bss
;
117 struct wlantest_sta
*sta
;
118 u16 alg
, trans
, status
;
120 mgmt
= (const struct ieee80211_mgmt
*) data
;
121 bss
= bss_get(wt
, mgmt
->bssid
);
124 if (os_memcmp(mgmt
->sa
, mgmt
->bssid
, ETH_ALEN
) == 0)
125 sta
= sta_get(bss
, mgmt
->da
);
127 sta
= sta_get(bss
, mgmt
->sa
);
132 add_note(wt
, MSG_INFO
, "Too short Authentication frame from "
133 MACSTR
, MAC2STR(mgmt
->sa
));
137 alg
= le_to_host16(mgmt
->u
.auth
.auth_alg
);
138 trans
= le_to_host16(mgmt
->u
.auth
.auth_transaction
);
139 status
= le_to_host16(mgmt
->u
.auth
.status_code
);
141 wpa_printf(MSG_DEBUG
, "AUTH " MACSTR
" -> " MACSTR
142 " (alg=%u trans=%u status=%u)",
143 MAC2STR(mgmt
->sa
), MAC2STR(mgmt
->da
), alg
, trans
, status
);
145 if (alg
== 0 && trans
== 2 && status
== 0) {
146 if (sta
->state
== STATE1
) {
147 add_note(wt
, MSG_DEBUG
, "STA " MACSTR
148 " moved to State 2 with " MACSTR
,
149 MAC2STR(sta
->addr
), MAC2STR(bss
->bssid
));
154 if (os_memcmp(mgmt
->sa
, mgmt
->bssid
, ETH_ALEN
) == 0)
155 sta
->counters
[WLANTEST_STA_COUNTER_AUTH_RX
]++;
157 sta
->counters
[WLANTEST_STA_COUNTER_AUTH_TX
]++;
161 static void deauth_all_stas(struct wlantest
*wt
, struct wlantest_bss
*bss
)
163 struct wlantest_sta
*sta
;
164 dl_list_for_each(sta
, &bss
->sta
, struct wlantest_sta
, list
) {
165 if (sta
->state
== STATE1
)
167 add_note(wt
, MSG_DEBUG
, "STA " MACSTR
168 " moved to State 1 with " MACSTR
,
169 MAC2STR(sta
->addr
), MAC2STR(bss
->bssid
));
175 static void tdls_link_down(struct wlantest
*wt
, struct wlantest_bss
*bss
,
176 struct wlantest_sta
*sta
)
178 struct wlantest_tdls
*tdls
;
179 dl_list_for_each(tdls
, &bss
->tdls
, struct wlantest_tdls
, list
) {
180 if ((tdls
->init
== sta
|| tdls
->resp
== sta
) && tdls
->link_up
)
182 add_note(wt
, MSG_DEBUG
, "TDLS: Set link down based on "
183 "STA deauth/disassoc");
190 static void rx_mgmt_deauth(struct wlantest
*wt
, const u8
*data
, size_t len
,
193 const struct ieee80211_mgmt
*mgmt
;
194 struct wlantest_bss
*bss
;
195 struct wlantest_sta
*sta
;
198 mgmt
= (const struct ieee80211_mgmt
*) data
;
199 bss
= bss_get(wt
, mgmt
->bssid
);
202 if (os_memcmp(mgmt
->sa
, mgmt
->bssid
, ETH_ALEN
) == 0)
203 sta
= sta_get(bss
, mgmt
->da
);
205 sta
= sta_get(bss
, mgmt
->sa
);
208 add_note(wt
, MSG_INFO
, "Too short Deauthentication frame from "
209 MACSTR
, MAC2STR(mgmt
->sa
));
213 reason
= le_to_host16(mgmt
->u
.deauth
.reason_code
);
214 wpa_printf(MSG_DEBUG
, "DEAUTH " MACSTR
" -> " MACSTR
215 " (reason=%u) (valid=%d)",
216 MAC2STR(mgmt
->sa
), MAC2STR(mgmt
->da
),
218 wpa_hexdump(MSG_MSGDUMP
, "DEAUTH payload", data
+ 24, len
- 24);
221 if (valid
&& mgmt
->da
[0] == 0xff)
222 deauth_all_stas(wt
, bss
);
226 if (os_memcmp(mgmt
->sa
, mgmt
->bssid
, ETH_ALEN
) == 0) {
227 sta
->counters
[valid
? WLANTEST_STA_COUNTER_VALID_DEAUTH_RX
:
228 WLANTEST_STA_COUNTER_INVALID_DEAUTH_RX
]++;
229 if (sta
->pwrmgt
&& !sta
->pspoll
)
230 sta
->counters
[WLANTEST_STA_COUNTER_DEAUTH_RX_ASLEEP
]++;
232 sta
->counters
[WLANTEST_STA_COUNTER_DEAUTH_RX_AWAKE
]++;
234 fc
= le_to_host16(mgmt
->frame_control
);
235 if (!(fc
& WLAN_FC_ISWEP
) && reason
== 6)
236 sta
->counters
[WLANTEST_STA_COUNTER_DEAUTH_RX_RC6
]++;
237 else if (!(fc
& WLAN_FC_ISWEP
) && reason
== 7)
238 sta
->counters
[WLANTEST_STA_COUNTER_DEAUTH_RX_RC7
]++;
240 sta
->counters
[valid
? WLANTEST_STA_COUNTER_VALID_DEAUTH_TX
:
241 WLANTEST_STA_COUNTER_INVALID_DEAUTH_TX
]++;
244 add_note(wt
, MSG_INFO
, "Do not change STA " MACSTR
" State "
245 "since Disassociation frame was not protected "
246 "correctly", MAC2STR(sta
->addr
));
250 if (sta
->state
!= STATE1
) {
251 add_note(wt
, MSG_DEBUG
, "STA " MACSTR
252 " moved to State 1 with " MACSTR
,
253 MAC2STR(sta
->addr
), MAC2STR(bss
->bssid
));
256 tdls_link_down(wt
, bss
, sta
);
260 static void rx_mgmt_assoc_req(struct wlantest
*wt
, const u8
*data
, size_t len
)
262 const struct ieee80211_mgmt
*mgmt
;
263 struct wlantest_bss
*bss
;
264 struct wlantest_sta
*sta
;
265 struct ieee802_11_elems elems
;
267 mgmt
= (const struct ieee80211_mgmt
*) data
;
268 bss
= bss_get(wt
, mgmt
->bssid
);
271 sta
= sta_get(bss
, mgmt
->sa
);
276 add_note(wt
, MSG_INFO
, "Too short Association Request frame "
277 "from " MACSTR
, MAC2STR(mgmt
->sa
));
281 wpa_printf(MSG_DEBUG
, "ASSOCREQ " MACSTR
" -> " MACSTR
282 " (capab=0x%x listen_int=%u)",
283 MAC2STR(mgmt
->sa
), MAC2STR(mgmt
->da
),
284 le_to_host16(mgmt
->u
.assoc_req
.capab_info
),
285 le_to_host16(mgmt
->u
.assoc_req
.listen_interval
));
287 sta
->counters
[WLANTEST_STA_COUNTER_ASSOCREQ_TX
]++;
289 if (ieee802_11_parse_elems(mgmt
->u
.assoc_req
.variable
,
290 len
- (mgmt
->u
.assoc_req
.variable
- data
),
291 &elems
, 0) == ParseFailed
) {
292 add_note(wt
, MSG_INFO
, "Invalid IEs in Association Request "
293 "frame from " MACSTR
, MAC2STR(mgmt
->sa
));
297 sta
->assocreq_capab_info
= le_to_host16(mgmt
->u
.assoc_req
.capab_info
);
298 sta
->assocreq_listen_int
=
299 le_to_host16(mgmt
->u
.assoc_req
.listen_interval
);
300 os_free(sta
->assocreq_ies
);
301 sta
->assocreq_ies_len
= len
- (mgmt
->u
.assoc_req
.variable
- data
);
302 sta
->assocreq_ies
= os_malloc(sta
->assocreq_ies_len
);
303 if (sta
->assocreq_ies
)
304 os_memcpy(sta
->assocreq_ies
, mgmt
->u
.assoc_req
.variable
,
305 sta
->assocreq_ies_len
);
307 sta_update_assoc(sta
, &elems
);
311 static void rx_mgmt_assoc_resp(struct wlantest
*wt
, const u8
*data
, size_t len
)
313 const struct ieee80211_mgmt
*mgmt
;
314 struct wlantest_bss
*bss
;
315 struct wlantest_sta
*sta
;
316 u16 capab
, status
, aid
;
319 struct wpa_ft_ies parse
;
321 mgmt
= (const struct ieee80211_mgmt
*) data
;
322 bss
= bss_get(wt
, mgmt
->bssid
);
325 sta
= sta_get(bss
, mgmt
->da
);
330 add_note(wt
, MSG_INFO
, "Too short Association Response frame "
331 "from " MACSTR
, MAC2STR(mgmt
->sa
));
335 ies
= mgmt
->u
.assoc_resp
.variable
;
336 ies_len
= len
- (mgmt
->u
.assoc_resp
.variable
- data
);
338 capab
= le_to_host16(mgmt
->u
.assoc_resp
.capab_info
);
339 status
= le_to_host16(mgmt
->u
.assoc_resp
.status_code
);
340 aid
= le_to_host16(mgmt
->u
.assoc_resp
.aid
);
342 wpa_printf(MSG_DEBUG
, "ASSOCRESP " MACSTR
" -> " MACSTR
343 " (capab=0x%x status=%u aid=%u)",
344 MAC2STR(mgmt
->sa
), MAC2STR(mgmt
->da
), capab
, status
,
347 if (status
== WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY
) {
348 struct ieee802_11_elems elems
;
349 if (ieee802_11_parse_elems(ies
, ies_len
, &elems
, 0) ==
351 add_note(wt
, MSG_INFO
, "Failed to parse IEs in "
352 "AssocResp from " MACSTR
,
354 } else if (elems
.timeout_int
== NULL
||
355 elems
.timeout_int
[0] !=
356 WLAN_TIMEOUT_ASSOC_COMEBACK
) {
357 add_note(wt
, MSG_INFO
, "No valid Timeout Interval IE "
358 "with Assoc Comeback time in AssocResp "
359 "(status=30) from " MACSTR
,
363 WLANTEST_STA_COUNTER_ASSOCRESP_COMEBACK
]++;
370 if ((aid
& 0xc000) != 0xc000) {
371 add_note(wt
, MSG_DEBUG
, "Two MSBs of the AID were not set to 1 "
372 "in Association Response from " MACSTR
,
375 sta
->aid
= aid
& 0xc000;
377 if (sta
->state
< STATE2
) {
378 add_note(wt
, MSG_DEBUG
,
379 "STA " MACSTR
" was not in State 2 when "
380 "getting associated", MAC2STR(sta
->addr
));
383 if (sta
->state
< STATE3
) {
384 add_note(wt
, MSG_DEBUG
, "STA " MACSTR
385 " moved to State 3 with " MACSTR
,
386 MAC2STR(sta
->addr
), MAC2STR(bss
->bssid
));
390 if (wpa_ft_parse_ies(ies
, ies_len
, &parse
) == 0) {
392 os_memcpy(bss
->r0kh_id
, parse
.r0kh_id
,
394 bss
->r0kh_id_len
= parse
.r0kh_id_len
;
397 os_memcpy(bss
->r1kh_id
, parse
.r1kh_id
, FT_R1KH_ID_LEN
);
402 static void rx_mgmt_reassoc_req(struct wlantest
*wt
, const u8
*data
,
405 const struct ieee80211_mgmt
*mgmt
;
406 struct wlantest_bss
*bss
;
407 struct wlantest_sta
*sta
;
408 struct ieee802_11_elems elems
;
410 mgmt
= (const struct ieee80211_mgmt
*) data
;
411 bss
= bss_get(wt
, mgmt
->bssid
);
414 sta
= sta_get(bss
, mgmt
->sa
);
418 if (len
< 24 + 4 + ETH_ALEN
) {
419 add_note(wt
, MSG_INFO
, "Too short Reassociation Request frame "
420 "from " MACSTR
, MAC2STR(mgmt
->sa
));
424 wpa_printf(MSG_DEBUG
, "REASSOCREQ " MACSTR
" -> " MACSTR
425 " (capab=0x%x listen_int=%u current_ap=" MACSTR
")",
426 MAC2STR(mgmt
->sa
), MAC2STR(mgmt
->da
),
427 le_to_host16(mgmt
->u
.reassoc_req
.capab_info
),
428 le_to_host16(mgmt
->u
.reassoc_req
.listen_interval
),
429 MAC2STR(mgmt
->u
.reassoc_req
.current_ap
));
431 sta
->counters
[WLANTEST_STA_COUNTER_REASSOCREQ_TX
]++;
433 if (ieee802_11_parse_elems(mgmt
->u
.reassoc_req
.variable
,
434 len
- (mgmt
->u
.reassoc_req
.variable
- data
),
435 &elems
, 0) == ParseFailed
) {
436 add_note(wt
, MSG_INFO
, "Invalid IEs in Reassociation Request "
437 "frame from " MACSTR
, MAC2STR(mgmt
->sa
));
441 sta
->assocreq_capab_info
=
442 le_to_host16(mgmt
->u
.reassoc_req
.capab_info
);
443 sta
->assocreq_listen_int
=
444 le_to_host16(mgmt
->u
.reassoc_req
.listen_interval
);
445 os_free(sta
->assocreq_ies
);
446 sta
->assocreq_ies_len
= len
- (mgmt
->u
.reassoc_req
.variable
- data
);
447 sta
->assocreq_ies
= os_malloc(sta
->assocreq_ies_len
);
448 if (sta
->assocreq_ies
)
449 os_memcpy(sta
->assocreq_ies
, mgmt
->u
.reassoc_req
.variable
,
450 sta
->assocreq_ies_len
);
452 sta_update_assoc(sta
, &elems
);
456 static void rx_mgmt_reassoc_resp(struct wlantest
*wt
, const u8
*data
,
459 const struct ieee80211_mgmt
*mgmt
;
460 struct wlantest_bss
*bss
;
461 struct wlantest_sta
*sta
;
462 u16 capab
, status
, aid
;
464 mgmt
= (const struct ieee80211_mgmt
*) data
;
465 bss
= bss_get(wt
, mgmt
->bssid
);
468 sta
= sta_get(bss
, mgmt
->da
);
473 add_note(wt
, MSG_INFO
, "Too short Reassociation Response frame "
474 "from " MACSTR
, MAC2STR(mgmt
->sa
));
478 capab
= le_to_host16(mgmt
->u
.reassoc_resp
.capab_info
);
479 status
= le_to_host16(mgmt
->u
.reassoc_resp
.status_code
);
480 aid
= le_to_host16(mgmt
->u
.reassoc_resp
.aid
);
482 wpa_printf(MSG_DEBUG
, "REASSOCRESP " MACSTR
" -> " MACSTR
483 " (capab=0x%x status=%u aid=%u)",
484 MAC2STR(mgmt
->sa
), MAC2STR(mgmt
->da
), capab
, status
,
487 if (status
== WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY
) {
488 struct ieee802_11_elems elems
;
489 const u8
*ies
= mgmt
->u
.reassoc_resp
.variable
;
490 size_t ies_len
= len
- (mgmt
->u
.reassoc_resp
.variable
- data
);
491 if (ieee802_11_parse_elems(ies
, ies_len
, &elems
, 0) ==
493 add_note(wt
, MSG_INFO
, "Failed to parse IEs in "
494 "ReassocResp from " MACSTR
,
496 } else if (elems
.timeout_int
== NULL
||
497 elems
.timeout_int
[0] !=
498 WLAN_TIMEOUT_ASSOC_COMEBACK
) {
499 add_note(wt
, MSG_INFO
, "No valid Timeout Interval IE "
500 "with Assoc Comeback time in ReassocResp "
501 "(status=30) from " MACSTR
,
505 WLANTEST_STA_COUNTER_REASSOCRESP_COMEBACK
]++;
512 if ((aid
& 0xc000) != 0xc000) {
513 add_note(wt
, MSG_DEBUG
, "Two MSBs of the AID were not set to 1 "
514 "in Reassociation Response from " MACSTR
,
517 sta
->aid
= aid
& 0xc000;
519 if (sta
->state
< STATE2
) {
520 add_note(wt
, MSG_DEBUG
,
521 "STA " MACSTR
" was not in State 2 when "
522 "getting associated", MAC2STR(sta
->addr
));
525 if (sta
->state
< STATE3
) {
526 add_note(wt
, MSG_DEBUG
, "STA " MACSTR
527 " moved to State 3 with " MACSTR
,
528 MAC2STR(sta
->addr
), MAC2STR(bss
->bssid
));
534 static void disassoc_all_stas(struct wlantest
*wt
, struct wlantest_bss
*bss
)
536 struct wlantest_sta
*sta
;
537 dl_list_for_each(sta
, &bss
->sta
, struct wlantest_sta
, list
) {
538 if (sta
->state
<= STATE2
)
540 add_note(wt
, MSG_DEBUG
, "STA " MACSTR
541 " moved to State 2 with " MACSTR
,
542 MAC2STR(sta
->addr
), MAC2STR(bss
->bssid
));
548 static void rx_mgmt_disassoc(struct wlantest
*wt
, const u8
*data
, size_t len
,
551 const struct ieee80211_mgmt
*mgmt
;
552 struct wlantest_bss
*bss
;
553 struct wlantest_sta
*sta
;
556 mgmt
= (const struct ieee80211_mgmt
*) data
;
557 bss
= bss_get(wt
, mgmt
->bssid
);
560 if (os_memcmp(mgmt
->sa
, mgmt
->bssid
, ETH_ALEN
) == 0)
561 sta
= sta_get(bss
, mgmt
->da
);
563 sta
= sta_get(bss
, mgmt
->sa
);
566 add_note(wt
, MSG_INFO
, "Too short Disassociation frame from "
567 MACSTR
, MAC2STR(mgmt
->sa
));
571 reason
= le_to_host16(mgmt
->u
.disassoc
.reason_code
);
572 wpa_printf(MSG_DEBUG
, "DISASSOC " MACSTR
" -> " MACSTR
573 " (reason=%u) (valid=%d)",
574 MAC2STR(mgmt
->sa
), MAC2STR(mgmt
->da
),
576 wpa_hexdump(MSG_MSGDUMP
, "DISASSOC payload", data
+ 24, len
- 24);
579 if (valid
&& mgmt
->da
[0] == 0xff)
580 disassoc_all_stas(wt
, bss
);
584 if (os_memcmp(mgmt
->sa
, mgmt
->bssid
, ETH_ALEN
) == 0) {
585 sta
->counters
[valid
? WLANTEST_STA_COUNTER_VALID_DISASSOC_RX
:
586 WLANTEST_STA_COUNTER_INVALID_DISASSOC_RX
]++;
587 if (sta
->pwrmgt
&& !sta
->pspoll
)
589 WLANTEST_STA_COUNTER_DISASSOC_RX_ASLEEP
]++;
592 WLANTEST_STA_COUNTER_DISASSOC_RX_AWAKE
]++;
594 fc
= le_to_host16(mgmt
->frame_control
);
595 if (!(fc
& WLAN_FC_ISWEP
) && reason
== 6)
596 sta
->counters
[WLANTEST_STA_COUNTER_DISASSOC_RX_RC6
]++;
597 else if (!(fc
& WLAN_FC_ISWEP
) && reason
== 7)
598 sta
->counters
[WLANTEST_STA_COUNTER_DISASSOC_RX_RC7
]++;
600 sta
->counters
[valid
? WLANTEST_STA_COUNTER_VALID_DISASSOC_TX
:
601 WLANTEST_STA_COUNTER_INVALID_DISASSOC_TX
]++;
604 add_note(wt
, MSG_INFO
, "Do not change STA " MACSTR
" State "
605 "since Disassociation frame was not protected "
606 "correctly", MAC2STR(sta
->addr
));
610 if (sta
->state
< STATE2
) {
611 add_note(wt
, MSG_DEBUG
,
612 "STA " MACSTR
" was not in State 2 or 3 "
613 "when getting disassociated", MAC2STR(sta
->addr
));
616 if (sta
->state
> STATE2
) {
617 add_note(wt
, MSG_DEBUG
, "STA " MACSTR
618 " moved to State 2 with " MACSTR
,
619 MAC2STR(sta
->addr
), MAC2STR(bss
->bssid
));
622 tdls_link_down(wt
, bss
, sta
);
626 static void rx_mgmt_action_sa_query_req(struct wlantest
*wt
,
627 struct wlantest_sta
*sta
,
628 const struct ieee80211_mgmt
*mgmt
,
629 size_t len
, int valid
)
634 rx_id
= (const u8
*) mgmt
->u
.action
.u
.sa_query_req
.trans_id
;
635 if (os_memcmp(mgmt
->sa
, mgmt
->bssid
, ETH_ALEN
) == 0)
636 id
= sta
->ap_sa_query_tr
;
638 id
= sta
->sta_sa_query_tr
;
639 add_note(wt
, MSG_INFO
, "SA Query Request " MACSTR
" -> " MACSTR
640 " (trans_id=%02x%02x)%s",
641 MAC2STR(mgmt
->sa
), MAC2STR(mgmt
->da
), rx_id
[0], rx_id
[1],
642 valid
? "" : " (invalid protection)");
643 os_memcpy(id
, mgmt
->u
.action
.u
.sa_query_req
.trans_id
, 2);
644 if (os_memcmp(mgmt
->sa
, sta
->addr
, ETH_ALEN
) == 0)
645 sta
->counters
[valid
?
646 WLANTEST_STA_COUNTER_VALID_SAQUERYREQ_TX
:
647 WLANTEST_STA_COUNTER_INVALID_SAQUERYREQ_TX
]++;
649 sta
->counters
[valid
?
650 WLANTEST_STA_COUNTER_VALID_SAQUERYREQ_RX
:
651 WLANTEST_STA_COUNTER_INVALID_SAQUERYREQ_RX
]++;
655 static void rx_mgmt_action_sa_query_resp(struct wlantest
*wt
,
656 struct wlantest_sta
*sta
,
657 const struct ieee80211_mgmt
*mgmt
,
658 size_t len
, int valid
)
664 rx_id
= (const u8
*) mgmt
->u
.action
.u
.sa_query_resp
.trans_id
;
665 if (os_memcmp(mgmt
->sa
, mgmt
->bssid
, ETH_ALEN
) == 0)
666 id
= sta
->sta_sa_query_tr
;
668 id
= sta
->ap_sa_query_tr
;
669 match
= os_memcmp(rx_id
, id
, 2) == 0;
670 add_note(wt
, MSG_INFO
, "SA Query Response " MACSTR
" -> " MACSTR
671 " (trans_id=%02x%02x; %s)%s",
672 MAC2STR(mgmt
->sa
), MAC2STR(mgmt
->da
), rx_id
[0], rx_id
[1],
673 match
? "match" : "mismatch",
674 valid
? "" : " (invalid protection)");
675 if (os_memcmp(mgmt
->sa
, sta
->addr
, ETH_ALEN
) == 0)
676 sta
->counters
[(valid
&& match
) ?
677 WLANTEST_STA_COUNTER_VALID_SAQUERYRESP_TX
:
678 WLANTEST_STA_COUNTER_INVALID_SAQUERYRESP_TX
]++;
680 sta
->counters
[(valid
&& match
) ?
681 WLANTEST_STA_COUNTER_VALID_SAQUERYRESP_RX
:
682 WLANTEST_STA_COUNTER_INVALID_SAQUERYRESP_RX
]++;
686 static void rx_mgmt_action_sa_query(struct wlantest
*wt
,
687 struct wlantest_sta
*sta
,
688 const struct ieee80211_mgmt
*mgmt
,
689 size_t len
, int valid
)
691 if (len
< 24 + 2 + WLAN_SA_QUERY_TR_ID_LEN
) {
692 add_note(wt
, MSG_INFO
, "Too short SA Query frame from " MACSTR
,
697 if (len
> 24 + 2 + WLAN_SA_QUERY_TR_ID_LEN
) {
698 size_t elen
= len
- (24 + 2 + WLAN_SA_QUERY_TR_ID_LEN
);
699 add_note(wt
, MSG_INFO
, "Unexpected %u octets of extra data at "
700 "the end of SA Query frame from " MACSTR
,
701 (unsigned) elen
, MAC2STR(mgmt
->sa
));
702 wpa_hexdump(MSG_INFO
, "SA Query extra data",
703 ((const u8
*) mgmt
) + len
- elen
, elen
);
706 switch (mgmt
->u
.action
.u
.sa_query_req
.action
) {
707 case WLAN_SA_QUERY_REQUEST
:
708 rx_mgmt_action_sa_query_req(wt
, sta
, mgmt
, len
, valid
);
710 case WLAN_SA_QUERY_RESPONSE
:
711 rx_mgmt_action_sa_query_resp(wt
, sta
, mgmt
, len
, valid
);
714 add_note(wt
, MSG_INFO
, "Unexpected SA Query action value %u "
716 mgmt
->u
.action
.u
.sa_query_req
.action
,
722 static void rx_mgmt_action(struct wlantest
*wt
, const u8
*data
, size_t len
,
725 const struct ieee80211_mgmt
*mgmt
;
726 struct wlantest_bss
*bss
;
727 struct wlantest_sta
*sta
;
729 mgmt
= (const struct ieee80211_mgmt
*) data
;
730 if (mgmt
->da
[0] & 0x01) {
731 add_note(wt
, MSG_DEBUG
, "Group addressed Action frame: DA="
732 MACSTR
" SA=" MACSTR
" BSSID=" MACSTR
734 MAC2STR(mgmt
->da
), MAC2STR(mgmt
->sa
),
735 MAC2STR(mgmt
->bssid
), mgmt
->u
.action
.category
);
736 return; /* Ignore group addressed Action frames for now */
738 bss
= bss_get(wt
, mgmt
->bssid
);
741 if (os_memcmp(mgmt
->sa
, mgmt
->bssid
, ETH_ALEN
) == 0)
742 sta
= sta_get(bss
, mgmt
->da
);
744 sta
= sta_get(bss
, mgmt
->sa
);
749 add_note(wt
, MSG_INFO
, "Too short Action frame from " MACSTR
,
754 wpa_printf(MSG_DEBUG
, "ACTION " MACSTR
" -> " MACSTR
755 " (category=%u) (valid=%d)",
756 MAC2STR(mgmt
->sa
), MAC2STR(mgmt
->da
),
757 mgmt
->u
.action
.category
, valid
);
758 wpa_hexdump(MSG_MSGDUMP
, "ACTION payload", data
+ 24, len
- 24);
760 if (mgmt
->u
.action
.category
!= WLAN_ACTION_PUBLIC
&&
761 sta
->state
< STATE3
) {
762 add_note(wt
, MSG_INFO
, "Action frame sent when STA is not in "
763 "State 3 (SA=" MACSTR
" DATA=" MACSTR
")",
764 MAC2STR(mgmt
->sa
), MAC2STR(mgmt
->da
));
767 switch (mgmt
->u
.action
.category
) {
768 case WLAN_ACTION_SA_QUERY
:
769 rx_mgmt_action_sa_query(wt
, sta
, mgmt
, len
, valid
);
775 static int check_mmie_mic(unsigned int mgmt_group_cipher
,
776 const u8
*igtk
, size_t igtk_len
,
777 const u8
*data
, size_t len
)
782 const struct ieee80211_hdr
*hdr
;
785 if (!mgmt_group_cipher
|| igtk_len
< 16)
787 mic_len
= mgmt_group_cipher
== WPA_CIPHER_AES_128_CMAC
? 8 : 16;
789 if (len
< 24 || len
- 24 < mic_len
)
792 buf
= os_malloc(len
+ 20 - 24);
796 /* BIP AAD: FC(masked) A1 A2 A3 */
797 hdr
= (const struct ieee80211_hdr
*) data
;
798 fc
= le_to_host16(hdr
->frame_control
);
799 fc
&= ~(WLAN_FC_RETRY
| WLAN_FC_PWRMGT
| WLAN_FC_MOREDATA
);
800 WPA_PUT_LE16(buf
, fc
);
801 os_memcpy(buf
+ 2, hdr
->addr1
, 3 * ETH_ALEN
);
803 /* Frame body with MMIE MIC masked to zero */
804 os_memcpy(buf
+ 20, data
+ 24, len
- 24 - mic_len
);
805 os_memset(buf
+ 20 + len
- 24 - mic_len
, 0, mic_len
);
807 wpa_hexdump(MSG_MSGDUMP
, "BIP: AAD|Body(masked)", buf
, len
+ 20 - 24);
808 /* MIC = L(AES-128-CMAC(AAD || Frame Body(masked)), 0, 64) */
809 if (mgmt_group_cipher
== WPA_CIPHER_AES_128_CMAC
) {
810 ret
= omac1_aes_128(igtk
, buf
, len
+ 20 - 24, mic
);
811 } else if (mgmt_group_cipher
== WPA_CIPHER_BIP_CMAC_256
) {
812 ret
= omac1_aes_256(igtk
, buf
, len
+ 20 - 24, mic
);
813 } else if (mgmt_group_cipher
== WPA_CIPHER_BIP_GMAC_128
||
814 mgmt_group_cipher
== WPA_CIPHER_BIP_GMAC_256
) {
818 ipn
= data
+ len
- mic_len
- 6;
820 /* Nonce: A2 | IPN */
821 os_memcpy(nonce
, hdr
->addr2
, ETH_ALEN
);
822 npos
= nonce
+ ETH_ALEN
;
830 ret
= aes_gmac(igtk
, igtk_len
, nonce
, sizeof(nonce
),
831 buf
, len
+ 20 - 24, mic
);
842 if (os_memcmp(data
+ len
- mic_len
, mic
, mic_len
) != 0)
849 static int check_bip(struct wlantest
*wt
, const u8
*data
, size_t len
)
851 const struct ieee80211_mgmt
*mgmt
;
855 struct wlantest_bss
*bss
;
858 mgmt
= (const struct ieee80211_mgmt
*) data
;
859 fc
= le_to_host16(mgmt
->frame_control
);
860 stype
= WLAN_FC_GET_STYPE(fc
);
862 if (stype
== WLAN_FC_STYPE_ACTION
) {
865 if (mgmt
->u
.action
.category
== WLAN_ACTION_PUBLIC
)
866 return 0; /* Not a robust management frame */
869 bss
= bss_get(wt
, mgmt
->bssid
);
871 return 0; /* No key known yet */
873 mic_len
= bss
->mgmt_group_cipher
== WPA_CIPHER_AES_128_CMAC
? 8 : 16;
875 if (len
< 24 + 10 + mic_len
||
876 data
[len
- (10 + mic_len
)] != WLAN_EID_MMIE
||
877 data
[len
- (10 + mic_len
- 1)] != 8 + mic_len
) {
879 if (bss
->rsn_capab
& WPA_CAPABILITY_MFPC
) {
880 add_note(wt
, MSG_INFO
, "Robust group-addressed "
881 "management frame sent without BIP by "
882 MACSTR
, MAC2STR(mgmt
->sa
));
883 bss
->counters
[WLANTEST_BSS_COUNTER_MISSING_BIP_MMIE
]++;
889 mmie
= data
+ len
- (8 + mic_len
);
890 keyid
= WPA_GET_LE16(mmie
);
891 if (keyid
& 0xf000) {
892 add_note(wt
, MSG_INFO
, "MMIE KeyID reserved bits not zero "
893 "(%04x) from " MACSTR
, keyid
, MAC2STR(mgmt
->sa
));
896 if (keyid
< 4 || keyid
> 5) {
897 add_note(wt
, MSG_INFO
, "Unexpected MMIE KeyID %u from " MACSTR
,
898 keyid
, MAC2STR(mgmt
->sa
));
899 bss
->counters
[WLANTEST_BSS_COUNTER_INVALID_BIP_MMIE
]++;
902 wpa_printf(MSG_DEBUG
, "MMIE KeyID %u", keyid
);
903 wpa_hexdump(MSG_MSGDUMP
, "MMIE IPN", mmie
+ 2, 6);
904 wpa_hexdump(MSG_MSGDUMP
, "MMIE MIC", mmie
+ 8, mic_len
);
906 if (!bss
->igtk_len
[keyid
]) {
907 add_note(wt
, MSG_DEBUG
, "No IGTK known to validate BIP frame");
911 if (os_memcmp(mmie
+ 2, bss
->ipn
[keyid
], 6) <= 0) {
912 add_note(wt
, MSG_INFO
, "BIP replay detected: SA=" MACSTR
,
914 wpa_hexdump(MSG_INFO
, "RX IPN", mmie
+ 2, 6);
915 wpa_hexdump(MSG_INFO
, "Last RX IPN", bss
->ipn
[keyid
], 6);
918 if (check_mmie_mic(bss
->mgmt_group_cipher
, bss
->igtk
[keyid
],
919 bss
->igtk_len
[keyid
], data
, len
) < 0) {
920 add_note(wt
, MSG_INFO
, "Invalid MMIE MIC in a frame from "
921 MACSTR
, MAC2STR(mgmt
->sa
));
922 bss
->counters
[WLANTEST_BSS_COUNTER_INVALID_BIP_MMIE
]++;
926 add_note(wt
, MSG_DEBUG
, "Valid MMIE MIC");
927 os_memcpy(bss
->ipn
[keyid
], mmie
+ 2, 6);
928 bss
->counters
[WLANTEST_BSS_COUNTER_VALID_BIP_MMIE
]++;
930 if (stype
== WLAN_FC_STYPE_DEAUTH
)
931 bss
->counters
[WLANTEST_BSS_COUNTER_BIP_DEAUTH
]++;
932 else if (stype
== WLAN_FC_STYPE_DISASSOC
)
933 bss
->counters
[WLANTEST_BSS_COUNTER_BIP_DISASSOC
]++;
939 static u8
* mgmt_ccmp_decrypt(struct wlantest
*wt
, const u8
*data
, size_t len
,
942 struct wlantest_bss
*bss
;
943 struct wlantest_sta
*sta
;
944 const struct ieee80211_hdr
*hdr
;
946 u8
*decrypted
, *frame
= NULL
;
949 hdr
= (const struct ieee80211_hdr
*) data
;
950 bss
= bss_get(wt
, hdr
->addr3
);
953 if (os_memcmp(hdr
->addr1
, hdr
->addr3
, ETH_ALEN
) == 0)
954 sta
= sta_get(bss
, hdr
->addr2
);
956 sta
= sta_get(bss
, hdr
->addr1
);
957 if (sta
== NULL
|| !sta
->ptk_set
) {
958 add_note(wt
, MSG_MSGDUMP
, "No PTK known to decrypt the frame");
965 if (!(data
[24 + 3] & 0x20)) {
966 add_note(wt
, MSG_INFO
, "Expected CCMP frame from " MACSTR
967 " did not have ExtIV bit set to 1",
968 MAC2STR(hdr
->addr2
));
972 if (data
[24 + 2] != 0 || (data
[24 + 3] & 0x1f) != 0) {
973 add_note(wt
, MSG_INFO
, "CCMP mgmt frame from " MACSTR
" used "
974 "non-zero reserved bit", MAC2STR(hdr
->addr2
));
977 keyid
= data
[24 + 3] >> 6;
979 add_note(wt
, MSG_INFO
, "Unexpected non-zero KeyID %d in "
980 "individually addressed Management frame from "
981 MACSTR
, keyid
, MAC2STR(hdr
->addr2
));
984 if (os_memcmp(hdr
->addr1
, hdr
->addr3
, ETH_ALEN
) == 0)
985 rsc
= sta
->rsc_tods
[16];
987 rsc
= sta
->rsc_fromds
[16];
989 ccmp_get_pn(pn
, data
+ 24);
990 if (os_memcmp(pn
, rsc
, 6) <= 0) {
991 u16 seq_ctrl
= le_to_host16(hdr
->seq_ctrl
);
992 add_note(wt
, MSG_INFO
, "CCMP/TKIP replay detected: A1=" MACSTR
993 " A2=" MACSTR
" A3=" MACSTR
" seq=%u frag=%u%s",
994 MAC2STR(hdr
->addr1
), MAC2STR(hdr
->addr2
),
996 WLAN_GET_SEQ_SEQ(seq_ctrl
),
997 WLAN_GET_SEQ_FRAG(seq_ctrl
),
998 (le_to_host16(hdr
->frame_control
) & WLAN_FC_RETRY
) ?
1000 wpa_hexdump(MSG_INFO
, "RX PN", pn
, 6);
1001 wpa_hexdump(MSG_INFO
, "RSC", rsc
, 6);
1004 decrypted
= ccmp_decrypt(sta
->ptk
.tk
, hdr
, data
+ 24, len
- 24, dlen
);
1006 os_memcpy(rsc
, pn
, 6);
1007 frame
= os_malloc(24 + *dlen
);
1009 os_memcpy(frame
, data
, 24);
1010 os_memcpy(frame
+ 24, decrypted
, *dlen
);
1021 static int check_mgmt_ccmp(struct wlantest
*wt
, const u8
*data
, size_t len
)
1023 const struct ieee80211_mgmt
*mgmt
;
1025 struct wlantest_bss
*bss
;
1026 struct wlantest_sta
*sta
;
1028 mgmt
= (const struct ieee80211_mgmt
*) data
;
1029 fc
= le_to_host16(mgmt
->frame_control
);
1031 if (WLAN_FC_GET_STYPE(fc
) == WLAN_FC_STYPE_ACTION
) {
1033 mgmt
->u
.action
.category
== WLAN_ACTION_PUBLIC
)
1034 return 0; /* Not a robust management frame */
1037 bss
= bss_get(wt
, mgmt
->bssid
);
1040 if (os_memcmp(mgmt
->da
, mgmt
->bssid
, ETH_ALEN
) == 0)
1041 sta
= sta_get(bss
, mgmt
->sa
);
1043 sta
= sta_get(bss
, mgmt
->da
);
1047 if ((sta
->rsn_capab
& WPA_CAPABILITY_MFPC
) &&
1048 (sta
->state
== STATE3
||
1049 WLAN_FC_GET_STYPE(fc
) == WLAN_FC_STYPE_ACTION
)) {
1050 add_note(wt
, MSG_INFO
, "Robust individually-addressed "
1051 "management frame sent without CCMP by "
1052 MACSTR
, MAC2STR(mgmt
->sa
));
1060 void rx_mgmt(struct wlantest
*wt
, const u8
*data
, size_t len
)
1062 const struct ieee80211_hdr
*hdr
;
1065 u8
*decrypted
= NULL
;
1071 hdr
= (const struct ieee80211_hdr
*) data
;
1072 fc
= le_to_host16(hdr
->frame_control
);
1074 stype
= WLAN_FC_GET_STYPE(fc
);
1076 if ((hdr
->addr1
[0] & 0x01) &&
1077 (stype
== WLAN_FC_STYPE_DEAUTH
||
1078 stype
== WLAN_FC_STYPE_DISASSOC
||
1079 stype
== WLAN_FC_STYPE_ACTION
)) {
1080 if (check_bip(wt
, data
, len
) < 0)
1084 wpa_printf((stype
== WLAN_FC_STYPE_BEACON
||
1085 stype
== WLAN_FC_STYPE_PROBE_RESP
||
1086 stype
== WLAN_FC_STYPE_PROBE_REQ
) ?
1087 MSG_EXCESSIVE
: MSG_MSGDUMP
,
1088 "MGMT %s%s%s DA=" MACSTR
" SA=" MACSTR
" BSSID=" MACSTR
,
1090 fc
& WLAN_FC_PWRMGT
? " PwrMgt" : "",
1091 fc
& WLAN_FC_ISWEP
? " Prot" : "",
1092 MAC2STR(hdr
->addr1
), MAC2STR(hdr
->addr2
),
1093 MAC2STR(hdr
->addr3
));
1095 if ((fc
& WLAN_FC_ISWEP
) &&
1096 !(hdr
->addr1
[0] & 0x01) &&
1097 (stype
== WLAN_FC_STYPE_DEAUTH
||
1098 stype
== WLAN_FC_STYPE_DISASSOC
||
1099 stype
== WLAN_FC_STYPE_ACTION
)) {
1100 decrypted
= mgmt_ccmp_decrypt(wt
, data
, len
, &dlen
);
1102 write_pcap_decrypted(wt
, decrypted
, dlen
, NULL
, 0);
1109 if (!(fc
& WLAN_FC_ISWEP
) &&
1110 !(hdr
->addr1
[0] & 0x01) &&
1111 (stype
== WLAN_FC_STYPE_DEAUTH
||
1112 stype
== WLAN_FC_STYPE_DISASSOC
||
1113 stype
== WLAN_FC_STYPE_ACTION
)) {
1114 if (check_mgmt_ccmp(wt
, data
, len
) < 0)
1119 case WLAN_FC_STYPE_BEACON
:
1120 rx_mgmt_beacon(wt
, data
, len
);
1122 case WLAN_FC_STYPE_PROBE_RESP
:
1123 rx_mgmt_probe_resp(wt
, data
, len
);
1125 case WLAN_FC_STYPE_AUTH
:
1126 rx_mgmt_auth(wt
, data
, len
);
1128 case WLAN_FC_STYPE_DEAUTH
:
1129 rx_mgmt_deauth(wt
, data
, len
, valid
);
1131 case WLAN_FC_STYPE_ASSOC_REQ
:
1132 rx_mgmt_assoc_req(wt
, data
, len
);
1134 case WLAN_FC_STYPE_ASSOC_RESP
:
1135 rx_mgmt_assoc_resp(wt
, data
, len
);
1137 case WLAN_FC_STYPE_REASSOC_REQ
:
1138 rx_mgmt_reassoc_req(wt
, data
, len
);
1140 case WLAN_FC_STYPE_REASSOC_RESP
:
1141 rx_mgmt_reassoc_resp(wt
, data
, len
);
1143 case WLAN_FC_STYPE_DISASSOC
:
1144 rx_mgmt_disassoc(wt
, data
, len
, valid
);
1146 case WLAN_FC_STYPE_ACTION
:
1147 rx_mgmt_action(wt
, data
, len
, valid
);
1153 wt
->last_mgmt_valid
= valid
;
1157 static void rx_mgmt_deauth_ack(struct wlantest
*wt
,
1158 const struct ieee80211_hdr
*hdr
)
1160 const struct ieee80211_mgmt
*mgmt
;
1161 struct wlantest_bss
*bss
;
1162 struct wlantest_sta
*sta
;
1164 mgmt
= (const struct ieee80211_mgmt
*) hdr
;
1165 bss
= bss_get(wt
, mgmt
->bssid
);
1168 if (os_memcmp(mgmt
->sa
, mgmt
->bssid
, ETH_ALEN
) == 0)
1169 sta
= sta_get(bss
, mgmt
->da
);
1171 sta
= sta_get(bss
, mgmt
->sa
);
1175 add_note(wt
, MSG_DEBUG
, "DEAUTH from " MACSTR
" acknowledged by "
1176 MACSTR
, MAC2STR(mgmt
->sa
), MAC2STR(mgmt
->da
));
1177 if (os_memcmp(mgmt
->sa
, mgmt
->bssid
, ETH_ALEN
) == 0) {
1179 c
= wt
->last_mgmt_valid
?
1180 WLANTEST_STA_COUNTER_VALID_DEAUTH_RX_ACK
:
1181 WLANTEST_STA_COUNTER_INVALID_DEAUTH_RX_ACK
;
1187 static void rx_mgmt_disassoc_ack(struct wlantest
*wt
,
1188 const struct ieee80211_hdr
*hdr
)
1190 const struct ieee80211_mgmt
*mgmt
;
1191 struct wlantest_bss
*bss
;
1192 struct wlantest_sta
*sta
;
1194 mgmt
= (const struct ieee80211_mgmt
*) hdr
;
1195 bss
= bss_get(wt
, mgmt
->bssid
);
1198 if (os_memcmp(mgmt
->sa
, mgmt
->bssid
, ETH_ALEN
) == 0)
1199 sta
= sta_get(bss
, mgmt
->da
);
1201 sta
= sta_get(bss
, mgmt
->sa
);
1205 add_note(wt
, MSG_DEBUG
, "DISASSOC from " MACSTR
" acknowledged by "
1206 MACSTR
, MAC2STR(mgmt
->sa
), MAC2STR(mgmt
->da
));
1207 if (os_memcmp(mgmt
->sa
, mgmt
->bssid
, ETH_ALEN
) == 0) {
1209 c
= wt
->last_mgmt_valid
?
1210 WLANTEST_STA_COUNTER_VALID_DISASSOC_RX_ACK
:
1211 WLANTEST_STA_COUNTER_INVALID_DISASSOC_RX_ACK
;
1217 void rx_mgmt_ack(struct wlantest
*wt
, const struct ieee80211_hdr
*hdr
)
1220 fc
= le_to_host16(hdr
->frame_control
);
1221 stype
= WLAN_FC_GET_STYPE(fc
);
1223 wpa_printf(MSG_MSGDUMP
, "MGMT ACK: stype=%u a1=" MACSTR
" a2=" MACSTR
1225 stype
, MAC2STR(hdr
->addr1
), MAC2STR(hdr
->addr2
),
1226 MAC2STR(hdr
->addr3
));
1229 case WLAN_FC_STYPE_DEAUTH
:
1230 rx_mgmt_deauth_ack(wt
, hdr
);
1232 case WLAN_FC_STYPE_DISASSOC
:
1233 rx_mgmt_disassoc_ack(wt
, hdr
);