3 * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
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/ieee802_11_defs.h"
13 #include "common/ieee802_11_common.h"
14 #include "common/wpa_ctrl.h"
15 #include "rsn_supp/wpa.h"
16 #include "wpa_supplicant_i.h"
19 #include "ctrl_iface.h"
22 #include "hs20_supplicant.h"
24 #define MAX_TFS_IE_LEN 1024
25 #define WNM_MAX_NEIGHBOR_REPORT 10
28 /* get the TFS IE from driver */
29 static int ieee80211_11_get_tfs_ie(struct wpa_supplicant
*wpa_s
, u8
*buf
,
30 u16
*buf_len
, enum wnm_oper oper
)
32 wpa_printf(MSG_DEBUG
, "%s: TFS get operation %d", __func__
, oper
);
34 return wpa_drv_wnm_oper(wpa_s
, oper
, wpa_s
->bssid
, buf
, buf_len
);
38 /* set the TFS IE to driver */
39 static int ieee80211_11_set_tfs_ie(struct wpa_supplicant
*wpa_s
,
40 const u8
*addr
, u8
*buf
, u16
*buf_len
,
43 wpa_printf(MSG_DEBUG
, "%s: TFS set operation %d", __func__
, oper
);
45 return wpa_drv_wnm_oper(wpa_s
, oper
, addr
, buf
, buf_len
);
49 /* MLME-SLEEPMODE.request */
50 int ieee802_11_send_wnmsleep_req(struct wpa_supplicant
*wpa_s
,
51 u8 action
, u16 intval
, struct wpabuf
*tfs_req
)
53 struct ieee80211_mgmt
*mgmt
;
56 struct wnm_sleep_element
*wnmsleep_ie
;
59 u16 wnmtfs_ie_len
; /* possibly multiple IE(s) */
60 enum wnm_oper tfs_oper
= action
== 0 ? WNM_SLEEP_TFS_REQ_IE_ADD
:
61 WNM_SLEEP_TFS_REQ_IE_NONE
;
63 wpa_printf(MSG_DEBUG
, "WNM: Request to send WNM-Sleep Mode Request "
64 "action=%s to " MACSTR
,
65 action
== 0 ? "enter" : "exit",
66 MAC2STR(wpa_s
->bssid
));
68 /* WNM-Sleep Mode IE */
69 wnmsleep_ie_len
= sizeof(struct wnm_sleep_element
);
70 wnmsleep_ie
= os_zalloc(sizeof(struct wnm_sleep_element
));
71 if (wnmsleep_ie
== NULL
)
73 wnmsleep_ie
->eid
= WLAN_EID_WNMSLEEP
;
74 wnmsleep_ie
->len
= wnmsleep_ie_len
- 2;
75 wnmsleep_ie
->action_type
= action
;
76 wnmsleep_ie
->status
= WNM_STATUS_SLEEP_ACCEPT
;
77 wnmsleep_ie
->intval
= host_to_le16(intval
);
78 wpa_hexdump(MSG_DEBUG
, "WNM: WNM-Sleep Mode element",
79 (u8
*) wnmsleep_ie
, wnmsleep_ie_len
);
83 wnmtfs_ie_len
= wpabuf_len(tfs_req
);
84 wnmtfs_ie
= os_malloc(wnmtfs_ie_len
);
85 if (wnmtfs_ie
== NULL
) {
89 os_memcpy(wnmtfs_ie
, wpabuf_head(tfs_req
), wnmtfs_ie_len
);
91 wnmtfs_ie
= os_zalloc(MAX_TFS_IE_LEN
);
92 if (wnmtfs_ie
== NULL
) {
96 if (ieee80211_11_get_tfs_ie(wpa_s
, wnmtfs_ie
, &wnmtfs_ie_len
,
103 wpa_hexdump(MSG_DEBUG
, "WNM: TFS Request element",
104 (u8
*) wnmtfs_ie
, wnmtfs_ie_len
);
106 mgmt
= os_zalloc(sizeof(*mgmt
) + wnmsleep_ie_len
+ wnmtfs_ie_len
);
108 wpa_printf(MSG_DEBUG
, "MLME: Failed to allocate buffer for "
109 "WNM-Sleep Request action frame");
110 os_free(wnmsleep_ie
);
115 os_memcpy(mgmt
->da
, wpa_s
->bssid
, ETH_ALEN
);
116 os_memcpy(mgmt
->sa
, wpa_s
->own_addr
, ETH_ALEN
);
117 os_memcpy(mgmt
->bssid
, wpa_s
->bssid
, ETH_ALEN
);
118 mgmt
->frame_control
= IEEE80211_FC(WLAN_FC_TYPE_MGMT
,
119 WLAN_FC_STYPE_ACTION
);
120 mgmt
->u
.action
.category
= WLAN_ACTION_WNM
;
121 mgmt
->u
.action
.u
.wnm_sleep_req
.action
= WNM_SLEEP_MODE_REQ
;
122 mgmt
->u
.action
.u
.wnm_sleep_req
.dialogtoken
= 1;
123 os_memcpy(mgmt
->u
.action
.u
.wnm_sleep_req
.variable
, wnmsleep_ie
,
125 /* copy TFS IE here */
126 if (wnmtfs_ie_len
> 0) {
127 os_memcpy(mgmt
->u
.action
.u
.wnm_sleep_req
.variable
+
128 wnmsleep_ie_len
, wnmtfs_ie
, wnmtfs_ie_len
);
131 len
= 1 + sizeof(mgmt
->u
.action
.u
.wnm_sleep_req
) + wnmsleep_ie_len
+
134 res
= wpa_drv_send_action(wpa_s
, wpa_s
->assoc_freq
, 0, wpa_s
->bssid
,
135 wpa_s
->own_addr
, wpa_s
->bssid
,
136 &mgmt
->u
.action
.category
, len
, 0);
138 wpa_printf(MSG_DEBUG
, "Failed to send WNM-Sleep Request "
139 "(action=%d, intval=%d)", action
, intval
);
141 os_free(wnmsleep_ie
);
149 static void wnm_sleep_mode_enter_success(struct wpa_supplicant
*wpa_s
,
150 u8
*tfsresp_ie_start
,
153 wpa_drv_wnm_oper(wpa_s
, WNM_SLEEP_ENTER_CONFIRM
,
154 wpa_s
->bssid
, NULL
, NULL
);
155 /* remove GTK/IGTK ?? */
157 /* set the TFS Resp IE(s) */
158 if (tfsresp_ie_start
&& tfsresp_ie_end
&&
159 tfsresp_ie_end
- tfsresp_ie_start
>= 0) {
161 tfsresp_ie_len
= (tfsresp_ie_end
+ tfsresp_ie_end
[1] + 2) -
163 wpa_printf(MSG_DEBUG
, "TFS Resp IE(s) found");
164 /* pass the TFS Resp IE(s) to driver for processing */
165 if (ieee80211_11_set_tfs_ie(wpa_s
, wpa_s
->bssid
,
168 WNM_SLEEP_TFS_RESP_IE_SET
))
169 wpa_printf(MSG_DEBUG
, "WNM: Fail to set TFS Resp IE");
174 static void wnm_sleep_mode_exit_success(struct wpa_supplicant
*wpa_s
,
175 const u8
*frm
, u16 key_len_total
)
180 wpa_drv_wnm_oper(wpa_s
, WNM_SLEEP_EXIT_CONFIRM
, wpa_s
->bssid
,
183 /* Install GTK/IGTK */
185 /* point to key data field */
186 ptr
= (u8
*) frm
+ 1 + 2;
187 end
= ptr
+ key_len_total
;
188 wpa_hexdump_key(MSG_DEBUG
, "WNM: Key Data", ptr
, key_len_total
);
190 while (ptr
+ 1 < end
) {
191 if (ptr
+ 2 + ptr
[1] > end
) {
192 wpa_printf(MSG_DEBUG
, "WNM: Invalid Key Data element "
195 wpa_hexdump(MSG_DEBUG
, "WNM: Remaining data",
200 if (*ptr
== WNM_SLEEP_SUBELEM_GTK
) {
201 if (ptr
[1] < 11 + 5) {
202 wpa_printf(MSG_DEBUG
, "WNM: Too short GTK "
206 gtk_len
= *(ptr
+ 4);
207 if (ptr
[1] < 11 + gtk_len
||
208 gtk_len
< 5 || gtk_len
> 32) {
209 wpa_printf(MSG_DEBUG
, "WNM: Invalid GTK "
213 wpa_wnmsleep_install_key(
215 WNM_SLEEP_SUBELEM_GTK
,
218 #ifdef CONFIG_IEEE80211W
219 } else if (*ptr
== WNM_SLEEP_SUBELEM_IGTK
) {
220 if (ptr
[1] < 2 + 6 + WPA_IGTK_LEN
) {
221 wpa_printf(MSG_DEBUG
, "WNM: Too short IGTK "
225 wpa_wnmsleep_install_key(wpa_s
->wpa
,
226 WNM_SLEEP_SUBELEM_IGTK
, ptr
);
227 ptr
+= 10 + WPA_IGTK_LEN
;
228 #endif /* CONFIG_IEEE80211W */
230 break; /* skip the loop */
235 static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant
*wpa_s
,
236 const u8
*frm
, int len
)
239 * Action [1] | Dialog Token [1] | Key Data Len [2] | Key Data |
240 * WNM-Sleep Mode IE | TFS Response IE
242 u8
*pos
= (u8
*) frm
; /* point to payload after the action field */
244 struct wnm_sleep_element
*wnmsleep_ie
= NULL
;
245 /* multiple TFS Resp IE (assuming consecutive) */
246 u8
*tfsresp_ie_start
= NULL
;
247 u8
*tfsresp_ie_end
= NULL
;
252 key_len_total
= WPA_GET_LE16(frm
+ 1);
254 wpa_printf(MSG_DEBUG
, "WNM-Sleep Mode Response token=%u key_len_total=%d",
255 frm
[0], key_len_total
);
257 if (key_len_total
> left
) {
258 wpa_printf(MSG_INFO
, "WNM: Too short frame for Key Data field");
261 pos
+= 3 + key_len_total
;
262 while (pos
- frm
< len
) {
263 u8 ie_len
= *(pos
+ 1);
264 if (pos
+ 2 + ie_len
> frm
+ len
) {
265 wpa_printf(MSG_INFO
, "WNM: Invalid IE len %u", ie_len
);
268 wpa_hexdump(MSG_DEBUG
, "WNM: Element", pos
, 2 + ie_len
);
269 if (*pos
== WLAN_EID_WNMSLEEP
)
270 wnmsleep_ie
= (struct wnm_sleep_element
*) pos
;
271 else if (*pos
== WLAN_EID_TFS_RESP
) {
272 if (!tfsresp_ie_start
)
273 tfsresp_ie_start
= pos
;
274 tfsresp_ie_end
= pos
;
276 wpa_printf(MSG_DEBUG
, "EID %d not recognized", *pos
);
281 wpa_printf(MSG_DEBUG
, "No WNM-Sleep IE found");
285 if (wnmsleep_ie
->status
== WNM_STATUS_SLEEP_ACCEPT
||
286 wnmsleep_ie
->status
== WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE
) {
287 wpa_printf(MSG_DEBUG
, "Successfully recv WNM-Sleep Response "
288 "frame (action=%d, intval=%d)",
289 wnmsleep_ie
->action_type
, wnmsleep_ie
->intval
);
290 if (wnmsleep_ie
->action_type
== WNM_SLEEP_MODE_ENTER
) {
291 wnm_sleep_mode_enter_success(wpa_s
, tfsresp_ie_start
,
293 } else if (wnmsleep_ie
->action_type
== WNM_SLEEP_MODE_EXIT
) {
294 wnm_sleep_mode_exit_success(wpa_s
, frm
, key_len_total
);
297 wpa_printf(MSG_DEBUG
, "Reject recv WNM-Sleep Response frame "
298 "(action=%d, intval=%d)",
299 wnmsleep_ie
->action_type
, wnmsleep_ie
->intval
);
300 if (wnmsleep_ie
->action_type
== WNM_SLEEP_MODE_ENTER
)
301 wpa_drv_wnm_oper(wpa_s
, WNM_SLEEP_ENTER_FAIL
,
302 wpa_s
->bssid
, NULL
, NULL
);
303 else if (wnmsleep_ie
->action_type
== WNM_SLEEP_MODE_EXIT
)
304 wpa_drv_wnm_oper(wpa_s
, WNM_SLEEP_EXIT_FAIL
,
305 wpa_s
->bssid
, NULL
, NULL
);
310 void wnm_deallocate_memory(struct wpa_supplicant
*wpa_s
)
314 for (i
= 0; i
< wpa_s
->wnm_num_neighbor_report
; i
++) {
315 os_free(wpa_s
->wnm_neighbor_report_elements
[i
].meas_pilot
);
316 os_free(wpa_s
->wnm_neighbor_report_elements
[i
].mul_bssid
);
319 wpa_s
->wnm_num_neighbor_report
= 0;
320 os_free(wpa_s
->wnm_neighbor_report_elements
);
321 wpa_s
->wnm_neighbor_report_elements
= NULL
;
325 static void wnm_parse_neighbor_report_elem(struct neighbor_report
*rep
,
326 u8 id
, u8 elen
, const u8
*pos
)
329 case WNM_NEIGHBOR_TSF
:
331 wpa_printf(MSG_DEBUG
, "WNM: Too short TSF");
334 rep
->tsf_offset
= WPA_GET_LE16(pos
);
335 rep
->beacon_int
= WPA_GET_LE16(pos
+ 2);
336 rep
->tsf_present
= 1;
338 case WNM_NEIGHBOR_CONDENSED_COUNTRY_STRING
:
340 wpa_printf(MSG_DEBUG
, "WNM: Too short condensed "
344 os_memcpy(rep
->country
, pos
, 2);
345 rep
->country_present
= 1;
347 case WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE
:
349 wpa_printf(MSG_DEBUG
, "WNM: Too short BSS transition "
353 rep
->preference
= pos
[0];
354 rep
->preference_present
= 1;
356 case WNM_NEIGHBOR_BSS_TERMINATION_DURATION
:
357 rep
->bss_term_tsf
= WPA_GET_LE64(pos
);
358 rep
->bss_term_dur
= WPA_GET_LE16(pos
+ 8);
359 rep
->bss_term_present
= 1;
361 case WNM_NEIGHBOR_BEARING
:
363 wpa_printf(MSG_DEBUG
, "WNM: Too short neighbor "
367 rep
->bearing
= WPA_GET_LE16(pos
);
368 rep
->distance
= WPA_GET_LE32(pos
+ 2);
369 rep
->rel_height
= WPA_GET_LE16(pos
+ 2 + 4);
370 rep
->bearing_present
= 1;
372 case WNM_NEIGHBOR_MEASUREMENT_PILOT
:
374 wpa_printf(MSG_DEBUG
, "WNM: Too short measurement "
378 os_free(rep
->meas_pilot
);
379 rep
->meas_pilot
= os_zalloc(sizeof(struct measurement_pilot
));
380 if (rep
->meas_pilot
== NULL
)
382 rep
->meas_pilot
->measurement_pilot
= pos
[0];
383 rep
->meas_pilot
->subelem_len
= elen
- 1;
384 os_memcpy(rep
->meas_pilot
->subelems
, pos
+ 1, elen
- 1);
386 case WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES
:
388 wpa_printf(MSG_DEBUG
, "WNM: Too short RRM enabled "
392 os_memcpy(rep
->rm_capab
, pos
, 5);
393 rep
->rm_capab_present
= 1;
395 case WNM_NEIGHBOR_MULTIPLE_BSSID
:
397 wpa_printf(MSG_DEBUG
, "WNM: Too short multiple BSSID");
400 os_free(rep
->mul_bssid
);
401 rep
->mul_bssid
= os_zalloc(sizeof(struct multiple_bssid
));
402 if (rep
->mul_bssid
== NULL
)
404 rep
->mul_bssid
->max_bssid_indicator
= pos
[0];
405 rep
->mul_bssid
->subelem_len
= elen
- 1;
406 os_memcpy(rep
->mul_bssid
->subelems
, pos
+ 1, elen
- 1);
412 static int wnm_nei_get_chan(struct wpa_supplicant
*wpa_s
, u8 op_class
, u8 chan
)
414 struct wpa_bss
*bss
= wpa_s
->current_bss
;
415 const char *country
= NULL
;
418 const u8
*elem
= wpa_bss_get_ie(bss
, WLAN_EID_COUNTRY
);
420 if (elem
&& elem
[1] >= 2)
421 country
= (const char *) (elem
+ 2);
424 return ieee80211_chan_to_freq(country
, op_class
, chan
);
428 static void wnm_parse_neighbor_report(struct wpa_supplicant
*wpa_s
,
429 const u8
*pos
, u8 len
,
430 struct neighbor_report
*rep
)
435 wpa_printf(MSG_DEBUG
, "WNM: Too short neighbor report");
439 os_memcpy(rep
->bssid
, pos
, ETH_ALEN
);
440 rep
->bssid_info
= WPA_GET_LE32(pos
+ ETH_ALEN
);
441 rep
->regulatory_class
= *(pos
+ 10);
442 rep
->channel_number
= *(pos
+ 11);
443 rep
->phy_type
= *(pos
+ 12);
453 wpa_printf(MSG_DEBUG
, "WNM: Subelement id=%u len=%u", id
, elen
);
456 wpa_printf(MSG_DEBUG
,
457 "WNM: Truncated neighbor report subelement");
460 wnm_parse_neighbor_report_elem(rep
, id
, elen
, pos
);
465 rep
->freq
= wnm_nei_get_chan(wpa_s
, rep
->regulatory_class
,
466 rep
->channel_number
);
470 static struct wpa_bss
*
471 compare_scan_neighbor_results(struct wpa_supplicant
*wpa_s
)
475 struct wpa_bss
*bss
= wpa_s
->current_bss
;
476 struct wpa_bss
*target
;
481 wpa_printf(MSG_DEBUG
, "WNM: Current BSS " MACSTR
" RSSI %d",
482 MAC2STR(wpa_s
->bssid
), bss
->level
);
484 for (i
= 0; i
< wpa_s
->wnm_num_neighbor_report
; i
++) {
485 struct neighbor_report
*nei
;
487 nei
= &wpa_s
->wnm_neighbor_report_elements
[i
];
488 if (nei
->preference_present
&& nei
->preference
== 0) {
489 wpa_printf(MSG_DEBUG
, "Skip excluded BSS " MACSTR
,
490 MAC2STR(nei
->bssid
));
494 target
= wpa_bss_get_bssid(wpa_s
, nei
->bssid
);
496 wpa_printf(MSG_DEBUG
, "Candidate BSS " MACSTR
497 " (pref %d) not found in scan results",
499 nei
->preference_present
? nei
->preference
:
504 if (bss
->ssid_len
!= target
->ssid_len
||
505 os_memcmp(bss
->ssid
, target
->ssid
, bss
->ssid_len
) != 0) {
507 * TODO: Could consider allowing transition to another
508 * ESS if PMF was enabled for the association.
510 wpa_printf(MSG_DEBUG
, "Candidate BSS " MACSTR
511 " (pref %d) in different ESS",
513 nei
->preference_present
? nei
->preference
:
518 if (target
->level
< bss
->level
&& target
->level
< -80) {
519 wpa_printf(MSG_DEBUG
, "Candidate BSS " MACSTR
520 " (pref %d) does not have sufficient signal level (%d)",
522 nei
->preference_present
? nei
->preference
:
528 wpa_printf(MSG_DEBUG
,
529 "WNM: Found an acceptable preferred transition candidate BSS "
531 MAC2STR(nei
->bssid
), target
->level
);
539 static void wnm_send_bss_transition_mgmt_resp(
540 struct wpa_supplicant
*wpa_s
, u8 dialog_token
,
541 enum bss_trans_mgmt_status_code status
, u8 delay
,
542 const u8
*target_bssid
)
545 struct ieee80211_mgmt
*mgmt
;
549 wpa_printf(MSG_DEBUG
, "WNM: Send BSS Transition Management Response "
550 "to " MACSTR
" dialog_token=%u status=%u delay=%d",
551 MAC2STR(wpa_s
->bssid
), dialog_token
, status
, delay
);
552 if (!wpa_s
->current_bss
) {
553 wpa_printf(MSG_DEBUG
,
554 "WNM: Current BSS not known - drop response");
558 mgmt
= (struct ieee80211_mgmt
*) buf
;
559 os_memset(&buf
, 0, sizeof(buf
));
560 os_memcpy(mgmt
->da
, wpa_s
->bssid
, ETH_ALEN
);
561 os_memcpy(mgmt
->sa
, wpa_s
->own_addr
, ETH_ALEN
);
562 os_memcpy(mgmt
->bssid
, wpa_s
->bssid
, ETH_ALEN
);
563 mgmt
->frame_control
= IEEE80211_FC(WLAN_FC_TYPE_MGMT
,
564 WLAN_FC_STYPE_ACTION
);
565 mgmt
->u
.action
.category
= WLAN_ACTION_WNM
;
566 mgmt
->u
.action
.u
.bss_tm_resp
.action
= WNM_BSS_TRANS_MGMT_RESP
;
567 mgmt
->u
.action
.u
.bss_tm_resp
.dialog_token
= dialog_token
;
568 mgmt
->u
.action
.u
.bss_tm_resp
.status_code
= status
;
569 mgmt
->u
.action
.u
.bss_tm_resp
.bss_termination_delay
= delay
;
570 pos
= mgmt
->u
.action
.u
.bss_tm_resp
.variable
;
572 os_memcpy(pos
, target_bssid
, ETH_ALEN
);
574 } else if (status
== WNM_BSS_TM_ACCEPT
) {
576 * P802.11-REVmc clarifies that the Target BSSID field is always
577 * present when status code is zero, so use a fake value here if
578 * no BSSID is yet known.
580 os_memset(pos
, 0, ETH_ALEN
);
584 len
= pos
- (u8
*) &mgmt
->u
.action
.category
;
586 res
= wpa_drv_send_action(wpa_s
, wpa_s
->assoc_freq
, 0, wpa_s
->bssid
,
587 wpa_s
->own_addr
, wpa_s
->bssid
,
588 &mgmt
->u
.action
.category
, len
, 0);
590 wpa_printf(MSG_DEBUG
,
591 "WNM: Failed to send BSS Transition Management Response");
596 int wnm_scan_process(struct wpa_supplicant
*wpa_s
, int reply_on_fail
)
599 struct wpa_ssid
*ssid
= wpa_s
->current_ssid
;
600 enum bss_trans_mgmt_status_code status
= WNM_BSS_TM_REJECT_UNSPECIFIED
;
602 if (!wpa_s
->wnm_neighbor_report_elements
)
605 if (os_reltime_before(&wpa_s
->wnm_cand_valid_until
,
606 &wpa_s
->scan_trigger_time
)) {
607 wpa_printf(MSG_DEBUG
, "WNM: Previously stored BSS transition candidate list is not valid anymore - drop it");
608 wnm_deallocate_memory(wpa_s
);
612 if (!wpa_s
->current_bss
||
613 os_memcmp(wpa_s
->wnm_cand_from_bss
, wpa_s
->current_bss
->bssid
,
615 wpa_printf(MSG_DEBUG
, "WNM: Stored BSS transition candidate list not from the current BSS - ignore it");
619 /* Compare the Neighbor Report and scan results */
620 bss
= compare_scan_neighbor_results(wpa_s
);
622 wpa_printf(MSG_DEBUG
, "WNM: No BSS transition candidate match found");
623 status
= WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES
;
624 goto send_bss_resp_fail
;
627 /* Associate to the network */
628 /* Send the BSS Management Response - Accept */
629 if (wpa_s
->wnm_reply
) {
630 wpa_s
->wnm_reply
= 0;
631 wnm_send_bss_transition_mgmt_resp(wpa_s
,
632 wpa_s
->wnm_dialog_token
,
637 if (bss
== wpa_s
->current_bss
) {
638 wpa_printf(MSG_DEBUG
,
639 "WNM: Already associated with the preferred candidate");
643 wpa_s
->reassociate
= 1;
644 wpa_supplicant_connect(wpa_s
, bss
, ssid
);
645 wnm_deallocate_memory(wpa_s
);
652 /* Send reject response for all the failures */
654 if (wpa_s
->wnm_reply
) {
655 wpa_s
->wnm_reply
= 0;
656 wnm_send_bss_transition_mgmt_resp(wpa_s
,
657 wpa_s
->wnm_dialog_token
,
660 wnm_deallocate_memory(wpa_s
);
666 static int cand_pref_compar(const void *a
, const void *b
)
668 const struct neighbor_report
*aa
= a
;
669 const struct neighbor_report
*bb
= b
;
671 if (!aa
->preference_present
&& !bb
->preference_present
)
673 if (!aa
->preference_present
)
675 if (!bb
->preference_present
)
677 if (bb
->preference
> aa
->preference
)
679 if (bb
->preference
< aa
->preference
)
685 static void wnm_sort_cand_list(struct wpa_supplicant
*wpa_s
)
687 if (!wpa_s
->wnm_neighbor_report_elements
)
689 qsort(wpa_s
->wnm_neighbor_report_elements
,
690 wpa_s
->wnm_num_neighbor_report
, sizeof(struct neighbor_report
),
695 static void wnm_dump_cand_list(struct wpa_supplicant
*wpa_s
)
699 wpa_printf(MSG_DEBUG
, "WNM: BSS Transition Candidate List");
700 if (!wpa_s
->wnm_neighbor_report_elements
)
702 for (i
= 0; i
< wpa_s
->wnm_num_neighbor_report
; i
++) {
703 struct neighbor_report
*nei
;
705 nei
= &wpa_s
->wnm_neighbor_report_elements
[i
];
706 wpa_printf(MSG_DEBUG
, "%u: " MACSTR
707 " info=0x%x op_class=%u chan=%u phy=%u pref=%d freq=%d",
708 i
, MAC2STR(nei
->bssid
), nei
->bssid_info
,
709 nei
->regulatory_class
,
710 nei
->channel_number
, nei
->phy_type
,
711 nei
->preference_present
? nei
->preference
: -1,
717 static int chan_supported(struct wpa_supplicant
*wpa_s
, int freq
)
721 for (i
= 0; i
< wpa_s
->hw
.num_modes
; i
++) {
722 struct hostapd_hw_modes
*mode
= &wpa_s
->hw
.modes
[i
];
725 for (j
= 0; j
< mode
->num_channels
; j
++) {
726 struct hostapd_channel_data
*chan
;
728 chan
= &mode
->channels
[j
];
729 if (chan
->freq
== freq
&&
730 !(chan
->flag
& HOSTAPD_CHAN_DISABLED
))
739 static void wnm_set_scan_freqs(struct wpa_supplicant
*wpa_s
)
745 if (!wpa_s
->wnm_neighbor_report_elements
)
748 if (wpa_s
->hw
.modes
== NULL
)
751 os_free(wpa_s
->next_scan_freqs
);
752 wpa_s
->next_scan_freqs
= NULL
;
754 freqs
= os_calloc(wpa_s
->wnm_num_neighbor_report
+ 1, sizeof(int));
758 for (i
= 0; i
< wpa_s
->wnm_num_neighbor_report
; i
++) {
759 struct neighbor_report
*nei
;
761 nei
= &wpa_s
->wnm_neighbor_report_elements
[i
];
762 if (nei
->freq
<= 0) {
763 wpa_printf(MSG_DEBUG
,
764 "WNM: Unknown neighbor operating frequency for "
765 MACSTR
" - scan all channels",
766 MAC2STR(nei
->bssid
));
770 if (chan_supported(wpa_s
, nei
->freq
))
771 add_freq(freqs
, &num_freqs
, nei
->freq
);
774 if (num_freqs
== 0) {
779 wpa_printf(MSG_DEBUG
,
780 "WNM: Scan %d frequencies based on transition candidate list",
782 wpa_s
->next_scan_freqs
= freqs
;
786 static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant
*wpa_s
,
787 const u8
*pos
, const u8
*end
,
790 unsigned int beacon_int
;
796 if (wpa_s
->current_bss
)
797 beacon_int
= wpa_s
->current_bss
->beacon_int
;
799 beacon_int
= 100; /* best guess */
801 wpa_s
->wnm_dialog_token
= pos
[0];
802 wpa_s
->wnm_mode
= pos
[1];
803 wpa_s
->wnm_dissoc_timer
= WPA_GET_LE16(pos
+ 2);
805 wpa_s
->wnm_reply
= reply
;
807 wpa_printf(MSG_DEBUG
, "WNM: BSS Transition Management Request: "
808 "dialog_token=%u request_mode=0x%x "
809 "disassoc_timer=%u validity_interval=%u",
810 wpa_s
->wnm_dialog_token
, wpa_s
->wnm_mode
,
811 wpa_s
->wnm_dissoc_timer
, valid_int
);
815 if (wpa_s
->wnm_mode
& WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED
) {
816 if (pos
+ 12 > end
) {
817 wpa_printf(MSG_DEBUG
, "WNM: Too short BSS TM Request");
820 os_memcpy(wpa_s
->wnm_bss_termination_duration
, pos
, 12);
821 pos
+= 12; /* BSS Termination Duration */
824 if (wpa_s
->wnm_mode
& WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT
) {
827 if (pos
+ 1 > end
|| pos
+ 1 + pos
[0] > end
) {
828 wpa_printf(MSG_DEBUG
, "WNM: Invalid BSS Transition "
829 "Management Request (URL)");
832 os_memcpy(url
, pos
+ 1, pos
[0]);
836 wpa_msg(wpa_s
, MSG_INFO
, ESS_DISASSOC_IMMINENT
"%d %u %s",
837 wpa_sm_pmf_enabled(wpa_s
->wpa
),
838 wpa_s
->wnm_dissoc_timer
* beacon_int
* 128 / 125, url
);
841 if (wpa_s
->wnm_mode
& WNM_BSS_TM_REQ_DISASSOC_IMMINENT
) {
842 wpa_msg(wpa_s
, MSG_INFO
, "WNM: Disassociation Imminent - "
843 "Disassociation Timer %u", wpa_s
->wnm_dissoc_timer
);
844 if (wpa_s
->wnm_dissoc_timer
&& !wpa_s
->scanning
) {
845 /* TODO: mark current BSS less preferred for
847 wpa_printf(MSG_DEBUG
, "Trying to find another BSS");
848 wpa_supplicant_req_scan(wpa_s
, 0, 0);
852 if (wpa_s
->wnm_mode
& WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED
) {
853 unsigned int valid_ms
;
855 wpa_msg(wpa_s
, MSG_INFO
, "WNM: Preferred List Available");
856 wnm_deallocate_memory(wpa_s
);
857 wpa_s
->wnm_neighbor_report_elements
= os_calloc(
858 WNM_MAX_NEIGHBOR_REPORT
,
859 sizeof(struct neighbor_report
));
860 if (wpa_s
->wnm_neighbor_report_elements
== NULL
)
863 while (pos
+ 2 <= end
&&
864 wpa_s
->wnm_num_neighbor_report
< WNM_MAX_NEIGHBOR_REPORT
)
869 wpa_printf(MSG_DEBUG
, "WNM: Neighbor report tag %u",
871 if (pos
+ len
> end
) {
872 wpa_printf(MSG_DEBUG
, "WNM: Truncated request");
875 if (tag
== WLAN_EID_NEIGHBOR_REPORT
) {
876 struct neighbor_report
*rep
;
877 rep
= &wpa_s
->wnm_neighbor_report_elements
[
878 wpa_s
->wnm_num_neighbor_report
];
879 wnm_parse_neighbor_report(wpa_s
, pos
, len
, rep
);
883 wpa_s
->wnm_num_neighbor_report
++;
885 wnm_sort_cand_list(wpa_s
);
886 wnm_dump_cand_list(wpa_s
);
887 valid_ms
= valid_int
* beacon_int
* 128 / 125;
888 wpa_printf(MSG_DEBUG
, "WNM: Candidate list valid for %u ms",
890 os_get_reltime(&wpa_s
->wnm_cand_valid_until
);
891 wpa_s
->wnm_cand_valid_until
.sec
+= valid_ms
/ 1000;
892 wpa_s
->wnm_cand_valid_until
.usec
+= (valid_ms
% 1000) * 1000;
893 wpa_s
->wnm_cand_valid_until
.sec
+=
894 wpa_s
->wnm_cand_valid_until
.usec
/ 1000000;
895 wpa_s
->wnm_cand_valid_until
.usec
%= 1000000;
896 os_memcpy(wpa_s
->wnm_cand_from_bss
, wpa_s
->bssid
, ETH_ALEN
);
898 if (wpa_s
->last_scan_res_used
> 0) {
899 struct os_reltime now
;
901 os_get_reltime(&now
);
902 if (!os_reltime_expired(&now
, &wpa_s
->last_scan
, 10)) {
903 wpa_printf(MSG_DEBUG
,
904 "WNM: Try to use recent scan results");
905 if (wnm_scan_process(wpa_s
, 0) > 0)
907 wpa_printf(MSG_DEBUG
,
908 "WNM: No match in previous scan results - try a new scan");
912 wnm_set_scan_freqs(wpa_s
);
913 wpa_supplicant_req_scan(wpa_s
, 0, 0);
915 enum bss_trans_mgmt_status_code status
;
916 if (wpa_s
->wnm_mode
& WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT
)
917 status
= WNM_BSS_TM_ACCEPT
;
919 wpa_msg(wpa_s
, MSG_INFO
, "WNM: BSS Transition Management Request did not include candidates");
920 status
= WNM_BSS_TM_REJECT_UNSPECIFIED
;
922 wnm_send_bss_transition_mgmt_resp(wpa_s
,
923 wpa_s
->wnm_dialog_token
,
929 int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant
*wpa_s
,
933 struct ieee80211_mgmt
*mgmt
;
937 wpa_printf(MSG_DEBUG
, "WNM: Send BSS Transition Management Query to "
938 MACSTR
" query_reason=%u",
939 MAC2STR(wpa_s
->bssid
), query_reason
);
941 mgmt
= (struct ieee80211_mgmt
*) buf
;
942 os_memset(&buf
, 0, sizeof(buf
));
943 os_memcpy(mgmt
->da
, wpa_s
->bssid
, ETH_ALEN
);
944 os_memcpy(mgmt
->sa
, wpa_s
->own_addr
, ETH_ALEN
);
945 os_memcpy(mgmt
->bssid
, wpa_s
->bssid
, ETH_ALEN
);
946 mgmt
->frame_control
= IEEE80211_FC(WLAN_FC_TYPE_MGMT
,
947 WLAN_FC_STYPE_ACTION
);
948 mgmt
->u
.action
.category
= WLAN_ACTION_WNM
;
949 mgmt
->u
.action
.u
.bss_tm_query
.action
= WNM_BSS_TRANS_MGMT_QUERY
;
950 mgmt
->u
.action
.u
.bss_tm_query
.dialog_token
= 1;
951 mgmt
->u
.action
.u
.bss_tm_query
.query_reason
= query_reason
;
952 pos
= mgmt
->u
.action
.u
.bss_tm_query
.variable
;
954 len
= pos
- (u8
*) &mgmt
->u
.action
.category
;
956 ret
= wpa_drv_send_action(wpa_s
, wpa_s
->assoc_freq
, 0, wpa_s
->bssid
,
957 wpa_s
->own_addr
, wpa_s
->bssid
,
958 &mgmt
->u
.action
.category
, len
, 0);
964 static void ieee802_11_rx_wnm_notif_req_wfa(struct wpa_supplicant
*wpa_s
,
965 const u8
*sa
, const u8
*data
,
968 const u8
*pos
, *end
, *next
;
974 while (pos
+ 1 < end
) {
977 wpa_printf(MSG_DEBUG
, "WNM: WFA subelement %u len %u",
979 if (ie_len
> end
- pos
) {
980 wpa_printf(MSG_DEBUG
, "WNM: Not enough room for "
989 wpa_printf(MSG_DEBUG
, "WNM: Subelement OUI %06x type %u",
990 WPA_GET_BE24(pos
), pos
[3]);
993 if (ie
== WLAN_EID_VENDOR_SPECIFIC
&& ie_len
>= 5 &&
994 WPA_GET_BE24(pos
) == OUI_WFA
&&
995 pos
[3] == HS20_WNM_SUB_REM_NEEDED
) {
996 /* Subscription Remediation subelement */
1002 wpa_printf(MSG_DEBUG
, "WNM: Subscription Remediation "
1004 ie_end
= pos
+ ie_len
;
1008 wpa_printf(MSG_DEBUG
, "WNM: No Server URL included");
1012 if (pos
+ url_len
+ 1 > ie_end
) {
1013 wpa_printf(MSG_DEBUG
, "WNM: Not enough room for Server URL (len=%u) and Server Method (left %d)",
1015 (int) (ie_end
- pos
));
1018 url
= os_malloc(url_len
+ 1);
1021 os_memcpy(url
, pos
, url_len
);
1022 url
[url_len
] = '\0';
1023 osu_method
= pos
[url_len
];
1025 hs20_rx_subscription_remediation(wpa_s
, url
,
1032 if (ie
== WLAN_EID_VENDOR_SPECIFIC
&& ie_len
>= 8 &&
1033 WPA_GET_BE24(pos
) == OUI_WFA
&&
1034 pos
[3] == HS20_WNM_DEAUTH_IMMINENT_NOTICE
) {
1041 ie_end
= pos
+ ie_len
;
1044 reauth_delay
= WPA_GET_LE16(pos
);
1047 wpa_printf(MSG_DEBUG
, "WNM: HS 2.0 Deauthentication "
1048 "Imminent - Reason Code %u "
1049 "Re-Auth Delay %u URL Length %u",
1050 code
, reauth_delay
, url_len
);
1051 if (pos
+ url_len
> ie_end
)
1053 url
= os_malloc(url_len
+ 1);
1056 os_memcpy(url
, pos
, url_len
);
1057 url
[url_len
] = '\0';
1058 hs20_rx_deauth_imminent_notice(wpa_s
, code
,
1064 #endif /* CONFIG_HS20 */
1071 static void ieee802_11_rx_wnm_notif_req(struct wpa_supplicant
*wpa_s
,
1072 const u8
*sa
, const u8
*frm
, int len
)
1074 const u8
*pos
, *end
;
1075 u8 dialog_token
, type
;
1077 /* Dialog Token [1] | Type [1] | Subelements */
1079 if (len
< 2 || sa
== NULL
)
1083 dialog_token
= *pos
++;
1086 wpa_dbg(wpa_s
, MSG_DEBUG
, "WNM: Received WNM-Notification Request "
1087 "(dialog_token %u type %u sa " MACSTR
")",
1088 dialog_token
, type
, MAC2STR(sa
));
1089 wpa_hexdump(MSG_DEBUG
, "WNM-Notification Request subelements",
1092 if (wpa_s
->wpa_state
!= WPA_COMPLETED
||
1093 os_memcmp(sa
, wpa_s
->bssid
, ETH_ALEN
) != 0) {
1094 wpa_dbg(wpa_s
, MSG_DEBUG
, "WNM: WNM-Notification frame not "
1095 "from our AP - ignore it");
1101 ieee802_11_rx_wnm_notif_req_wfa(wpa_s
, sa
, pos
, end
- pos
);
1104 wpa_dbg(wpa_s
, MSG_DEBUG
, "WNM: Ignore unknown "
1105 "WNM-Notification type %u", type
);
1111 void ieee802_11_rx_wnm_action(struct wpa_supplicant
*wpa_s
,
1112 const struct ieee80211_mgmt
*mgmt
, size_t len
)
1114 const u8
*pos
, *end
;
1117 if (len
< IEEE80211_HDRLEN
+ 2)
1120 pos
= ((const u8
*) mgmt
) + IEEE80211_HDRLEN
+ 1;
1122 end
= ((const u8
*) mgmt
) + len
;
1124 wpa_printf(MSG_DEBUG
, "WNM: RX action %u from " MACSTR
,
1125 act
, MAC2STR(mgmt
->sa
));
1126 if (wpa_s
->wpa_state
< WPA_ASSOCIATED
||
1127 os_memcmp(mgmt
->sa
, wpa_s
->bssid
, ETH_ALEN
) != 0) {
1128 wpa_printf(MSG_DEBUG
, "WNM: Ignore unexpected WNM Action "
1134 case WNM_BSS_TRANS_MGMT_REQ
:
1135 ieee802_11_rx_bss_trans_mgmt_req(wpa_s
, pos
, end
,
1136 !(mgmt
->da
[0] & 0x01));
1138 case WNM_SLEEP_MODE_RESP
:
1139 ieee802_11_rx_wnmsleep_resp(wpa_s
, pos
, end
- pos
);
1141 case WNM_NOTIFICATION_REQ
:
1142 ieee802_11_rx_wnm_notif_req(wpa_s
, mgmt
->sa
, pos
, end
- pos
);
1145 wpa_printf(MSG_ERROR
, "WNM: Unknown request");