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/wpa_ctrl.h"
14 #include "rsn_supp/wpa.h"
15 #include "wpa_supplicant_i.h"
18 #include "ctrl_iface.h"
22 #define MAX_TFS_IE_LEN 1024
23 #define WNM_MAX_NEIGHBOR_REPORT 10
26 /* get the TFS IE from driver */
27 static int ieee80211_11_get_tfs_ie(struct wpa_supplicant
*wpa_s
, u8
*buf
,
28 u16
*buf_len
, enum wnm_oper oper
)
30 wpa_printf(MSG_DEBUG
, "%s: TFS get operation %d", __func__
, oper
);
32 return wpa_drv_wnm_oper(wpa_s
, oper
, wpa_s
->bssid
, buf
, buf_len
);
36 /* set the TFS IE to driver */
37 static int ieee80211_11_set_tfs_ie(struct wpa_supplicant
*wpa_s
,
38 const u8
*addr
, u8
*buf
, u16
*buf_len
,
41 wpa_printf(MSG_DEBUG
, "%s: TFS set operation %d", __func__
, oper
);
43 return wpa_drv_wnm_oper(wpa_s
, oper
, addr
, buf
, buf_len
);
47 /* MLME-SLEEPMODE.request */
48 int ieee802_11_send_wnmsleep_req(struct wpa_supplicant
*wpa_s
,
49 u8 action
, u16 intval
, struct wpabuf
*tfs_req
)
51 struct ieee80211_mgmt
*mgmt
;
54 struct wnm_sleep_element
*wnmsleep_ie
;
57 u16 wnmtfs_ie_len
; /* possibly multiple IE(s) */
58 enum wnm_oper tfs_oper
= action
== 0 ? WNM_SLEEP_TFS_REQ_IE_ADD
:
59 WNM_SLEEP_TFS_REQ_IE_NONE
;
61 wpa_printf(MSG_DEBUG
, "WNM: Request to send WNM-Sleep Mode Request "
62 "action=%s to " MACSTR
,
63 action
== 0 ? "enter" : "exit",
64 MAC2STR(wpa_s
->bssid
));
66 /* WNM-Sleep Mode IE */
67 wnmsleep_ie_len
= sizeof(struct wnm_sleep_element
);
68 wnmsleep_ie
= os_zalloc(sizeof(struct wnm_sleep_element
));
69 if (wnmsleep_ie
== NULL
)
71 wnmsleep_ie
->eid
= WLAN_EID_WNMSLEEP
;
72 wnmsleep_ie
->len
= wnmsleep_ie_len
- 2;
73 wnmsleep_ie
->action_type
= action
;
74 wnmsleep_ie
->status
= WNM_STATUS_SLEEP_ACCEPT
;
75 wnmsleep_ie
->intval
= host_to_le16(intval
);
76 wpa_hexdump(MSG_DEBUG
, "WNM: WNM-Sleep Mode element",
77 (u8
*) wnmsleep_ie
, wnmsleep_ie_len
);
81 wnmtfs_ie_len
= wpabuf_len(tfs_req
);
82 wnmtfs_ie
= os_malloc(wnmtfs_ie_len
);
83 if (wnmtfs_ie
== NULL
) {
87 os_memcpy(wnmtfs_ie
, wpabuf_head(tfs_req
), wnmtfs_ie_len
);
89 wnmtfs_ie
= os_zalloc(MAX_TFS_IE_LEN
);
90 if (wnmtfs_ie
== NULL
) {
94 if (ieee80211_11_get_tfs_ie(wpa_s
, wnmtfs_ie
, &wnmtfs_ie_len
,
101 wpa_hexdump(MSG_DEBUG
, "WNM: TFS Request element",
102 (u8
*) wnmtfs_ie
, wnmtfs_ie_len
);
104 mgmt
= os_zalloc(sizeof(*mgmt
) + wnmsleep_ie_len
+ wnmtfs_ie_len
);
106 wpa_printf(MSG_DEBUG
, "MLME: Failed to allocate buffer for "
107 "WNM-Sleep Request action frame");
108 os_free(wnmsleep_ie
);
113 os_memcpy(mgmt
->da
, wpa_s
->bssid
, ETH_ALEN
);
114 os_memcpy(mgmt
->sa
, wpa_s
->own_addr
, ETH_ALEN
);
115 os_memcpy(mgmt
->bssid
, wpa_s
->bssid
, ETH_ALEN
);
116 mgmt
->frame_control
= IEEE80211_FC(WLAN_FC_TYPE_MGMT
,
117 WLAN_FC_STYPE_ACTION
);
118 mgmt
->u
.action
.category
= WLAN_ACTION_WNM
;
119 mgmt
->u
.action
.u
.wnm_sleep_req
.action
= WNM_SLEEP_MODE_REQ
;
120 mgmt
->u
.action
.u
.wnm_sleep_req
.dialogtoken
= 1;
121 os_memcpy(mgmt
->u
.action
.u
.wnm_sleep_req
.variable
, wnmsleep_ie
,
123 /* copy TFS IE here */
124 if (wnmtfs_ie_len
> 0) {
125 os_memcpy(mgmt
->u
.action
.u
.wnm_sleep_req
.variable
+
126 wnmsleep_ie_len
, wnmtfs_ie
, wnmtfs_ie_len
);
129 len
= 1 + sizeof(mgmt
->u
.action
.u
.wnm_sleep_req
) + wnmsleep_ie_len
+
132 res
= wpa_drv_send_action(wpa_s
, wpa_s
->assoc_freq
, 0, wpa_s
->bssid
,
133 wpa_s
->own_addr
, wpa_s
->bssid
,
134 &mgmt
->u
.action
.category
, len
, 0);
136 wpa_printf(MSG_DEBUG
, "Failed to send WNM-Sleep Request "
137 "(action=%d, intval=%d)", action
, intval
);
139 os_free(wnmsleep_ie
);
147 static void wnm_sleep_mode_enter_success(struct wpa_supplicant
*wpa_s
,
148 u8
*tfsresp_ie_start
,
151 wpa_drv_wnm_oper(wpa_s
, WNM_SLEEP_ENTER_CONFIRM
,
152 wpa_s
->bssid
, NULL
, NULL
);
153 /* remove GTK/IGTK ?? */
155 /* set the TFS Resp IE(s) */
156 if (tfsresp_ie_start
&& tfsresp_ie_end
&&
157 tfsresp_ie_end
- tfsresp_ie_start
>= 0) {
159 tfsresp_ie_len
= (tfsresp_ie_end
+ tfsresp_ie_end
[1] + 2) -
161 wpa_printf(MSG_DEBUG
, "TFS Resp IE(s) found");
162 /* pass the TFS Resp IE(s) to driver for processing */
163 if (ieee80211_11_set_tfs_ie(wpa_s
, wpa_s
->bssid
,
166 WNM_SLEEP_TFS_RESP_IE_SET
))
167 wpa_printf(MSG_DEBUG
, "WNM: Fail to set TFS Resp IE");
172 static void wnm_sleep_mode_exit_success(struct wpa_supplicant
*wpa_s
,
173 const u8
*frm
, u16 key_len_total
)
178 wpa_drv_wnm_oper(wpa_s
, WNM_SLEEP_EXIT_CONFIRM
, wpa_s
->bssid
,
181 /* Install GTK/IGTK */
183 /* point to key data field */
184 ptr
= (u8
*) frm
+ 1 + 1 + 2;
185 end
= ptr
+ key_len_total
;
186 wpa_hexdump_key(MSG_DEBUG
, "WNM: Key Data", ptr
, key_len_total
);
188 while (ptr
+ 1 < end
) {
189 if (ptr
+ 2 + ptr
[1] > end
) {
190 wpa_printf(MSG_DEBUG
, "WNM: Invalid Key Data element "
193 wpa_hexdump(MSG_DEBUG
, "WNM: Remaining data",
198 if (*ptr
== WNM_SLEEP_SUBELEM_GTK
) {
199 if (ptr
[1] < 11 + 5) {
200 wpa_printf(MSG_DEBUG
, "WNM: Too short GTK "
204 gtk_len
= *(ptr
+ 4);
205 if (ptr
[1] < 11 + gtk_len
||
206 gtk_len
< 5 || gtk_len
> 32) {
207 wpa_printf(MSG_DEBUG
, "WNM: Invalid GTK "
211 wpa_wnmsleep_install_key(
213 WNM_SLEEP_SUBELEM_GTK
,
216 #ifdef CONFIG_IEEE80211W
217 } else if (*ptr
== WNM_SLEEP_SUBELEM_IGTK
) {
218 if (ptr
[1] < 2 + 6 + WPA_IGTK_LEN
) {
219 wpa_printf(MSG_DEBUG
, "WNM: Too short IGTK "
223 wpa_wnmsleep_install_key(wpa_s
->wpa
,
224 WNM_SLEEP_SUBELEM_IGTK
, ptr
);
225 ptr
+= 10 + WPA_IGTK_LEN
;
226 #endif /* CONFIG_IEEE80211W */
228 break; /* skip the loop */
233 static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant
*wpa_s
,
234 const u8
*frm
, int len
)
237 * Action [1] | Diaglog Token [1] | Key Data Len [2] | Key Data |
238 * WNM-Sleep Mode IE | TFS Response IE
240 u8
*pos
= (u8
*) frm
; /* point to action field */
241 u16 key_len_total
= le_to_host16(*((u16
*)(frm
+2)));
242 struct wnm_sleep_element
*wnmsleep_ie
= NULL
;
243 /* multiple TFS Resp IE (assuming consecutive) */
244 u8
*tfsresp_ie_start
= NULL
;
245 u8
*tfsresp_ie_end
= NULL
;
247 wpa_printf(MSG_DEBUG
, "action=%d token = %d key_len_total = %d",
248 frm
[0], frm
[1], key_len_total
);
249 pos
+= 4 + key_len_total
;
250 if (pos
> frm
+ len
) {
251 wpa_printf(MSG_INFO
, "WNM: Too short frame for Key Data field");
254 while (pos
- frm
< len
) {
255 u8 ie_len
= *(pos
+ 1);
256 if (pos
+ 2 + ie_len
> frm
+ len
) {
257 wpa_printf(MSG_INFO
, "WNM: Invalid IE len %u", ie_len
);
260 wpa_hexdump(MSG_DEBUG
, "WNM: Element", pos
, 2 + ie_len
);
261 if (*pos
== WLAN_EID_WNMSLEEP
)
262 wnmsleep_ie
= (struct wnm_sleep_element
*) pos
;
263 else if (*pos
== WLAN_EID_TFS_RESP
) {
264 if (!tfsresp_ie_start
)
265 tfsresp_ie_start
= pos
;
266 tfsresp_ie_end
= pos
;
268 wpa_printf(MSG_DEBUG
, "EID %d not recognized", *pos
);
273 wpa_printf(MSG_DEBUG
, "No WNM-Sleep IE found");
277 if (wnmsleep_ie
->status
== WNM_STATUS_SLEEP_ACCEPT
||
278 wnmsleep_ie
->status
== WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE
) {
279 wpa_printf(MSG_DEBUG
, "Successfully recv WNM-Sleep Response "
280 "frame (action=%d, intval=%d)",
281 wnmsleep_ie
->action_type
, wnmsleep_ie
->intval
);
282 if (wnmsleep_ie
->action_type
== WNM_SLEEP_MODE_ENTER
) {
283 wnm_sleep_mode_enter_success(wpa_s
, tfsresp_ie_start
,
285 } else if (wnmsleep_ie
->action_type
== WNM_SLEEP_MODE_EXIT
) {
286 wnm_sleep_mode_exit_success(wpa_s
, frm
, key_len_total
);
289 wpa_printf(MSG_DEBUG
, "Reject recv WNM-Sleep Response frame "
290 "(action=%d, intval=%d)",
291 wnmsleep_ie
->action_type
, wnmsleep_ie
->intval
);
292 if (wnmsleep_ie
->action_type
== WNM_SLEEP_MODE_ENTER
)
293 wpa_drv_wnm_oper(wpa_s
, WNM_SLEEP_ENTER_FAIL
,
294 wpa_s
->bssid
, NULL
, NULL
);
295 else if (wnmsleep_ie
->action_type
== WNM_SLEEP_MODE_EXIT
)
296 wpa_drv_wnm_oper(wpa_s
, WNM_SLEEP_EXIT_FAIL
,
297 wpa_s
->bssid
, NULL
, NULL
);
302 void wnm_deallocate_memory(struct wpa_supplicant
*wpa_s
)
306 for (i
= 0; i
< wpa_s
->wnm_num_neighbor_report
; i
++) {
307 os_free(wpa_s
->wnm_neighbor_report_elements
[i
].tsf_info
);
308 os_free(wpa_s
->wnm_neighbor_report_elements
[i
].con_coun_str
);
309 os_free(wpa_s
->wnm_neighbor_report_elements
[i
].bss_tran_can
);
310 os_free(wpa_s
->wnm_neighbor_report_elements
[i
].bss_term_dur
);
311 os_free(wpa_s
->wnm_neighbor_report_elements
[i
].bearing
);
312 os_free(wpa_s
->wnm_neighbor_report_elements
[i
].meas_pilot
);
313 os_free(wpa_s
->wnm_neighbor_report_elements
[i
].rrm_cap
);
314 os_free(wpa_s
->wnm_neighbor_report_elements
[i
].mul_bssid
);
317 os_free(wpa_s
->wnm_neighbor_report_elements
);
318 wpa_s
->wnm_neighbor_report_elements
= NULL
;
322 static void wnm_parse_neighbor_report_elem(struct neighbor_report
*rep
,
323 u8 id
, u8 elen
, const u8
*pos
)
326 case WNM_NEIGHBOR_TSF
:
328 wpa_printf(MSG_DEBUG
, "WNM: Too short TSF");
331 rep
->tsf_info
= os_zalloc(sizeof(struct tsf_info
));
332 if (rep
->tsf_info
== NULL
)
334 rep
->tsf_info
->present
= 1;
335 os_memcpy(rep
->tsf_info
->tsf_offset
, pos
, 2);
336 os_memcpy(rep
->tsf_info
->beacon_interval
, pos
+ 2, 2);
338 case WNM_NEIGHBOR_CONDENSED_COUNTRY_STRING
:
340 wpa_printf(MSG_DEBUG
, "WNM: Too short condensed "
345 os_zalloc(sizeof(struct condensed_country_string
));
346 if (rep
->con_coun_str
== NULL
)
348 rep
->con_coun_str
->present
= 1;
349 os_memcpy(rep
->con_coun_str
->country_string
, pos
, 2);
351 case WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE
:
353 wpa_printf(MSG_DEBUG
, "WNM: Too short BSS transition "
358 os_zalloc(sizeof(struct bss_transition_candidate
));
359 if (rep
->bss_tran_can
== NULL
)
361 rep
->bss_tran_can
->present
= 1;
362 rep
->bss_tran_can
->preference
= pos
[0];
364 case WNM_NEIGHBOR_BSS_TERMINATION_DURATION
:
366 wpa_printf(MSG_DEBUG
, "WNM: Too short BSS termination "
371 os_zalloc(sizeof(struct bss_termination_duration
));
372 if (rep
->bss_term_dur
== NULL
)
374 rep
->bss_term_dur
->present
= 1;
375 os_memcpy(rep
->bss_term_dur
->duration
, pos
, 12);
377 case WNM_NEIGHBOR_BEARING
:
379 wpa_printf(MSG_DEBUG
, "WNM: Too short neighbor "
383 rep
->bearing
= os_zalloc(sizeof(struct bearing
));
384 if (rep
->bearing
== NULL
)
386 rep
->bearing
->present
= 1;
387 os_memcpy(rep
->bearing
->bearing
, pos
, 8);
389 case WNM_NEIGHBOR_MEASUREMENT_PILOT
:
391 wpa_printf(MSG_DEBUG
, "WNM: Too short measurement "
395 rep
->meas_pilot
= os_zalloc(sizeof(struct measurement_pilot
));
396 if (rep
->meas_pilot
== NULL
)
398 rep
->meas_pilot
->present
= 1;
399 rep
->meas_pilot
->measurement_pilot
= pos
[0];
400 rep
->meas_pilot
->num_vendor_specific
= pos
[1];
401 os_memcpy(rep
->meas_pilot
->vendor_specific
, pos
+ 2, elen
- 2);
403 case WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES
:
405 wpa_printf(MSG_DEBUG
, "WNM: Too short RRM enabled "
410 os_zalloc(sizeof(struct rrm_enabled_capabilities
));
411 if (rep
->rrm_cap
== NULL
)
413 rep
->rrm_cap
->present
= 1;
414 os_memcpy(rep
->rrm_cap
->capabilities
, pos
, 4);
416 case WNM_NEIGHBOR_MULTIPLE_BSSID
:
418 wpa_printf(MSG_DEBUG
, "WNM: Too short multiple BSSID");
421 rep
->mul_bssid
= os_zalloc(sizeof(struct multiple_bssid
));
422 if (rep
->mul_bssid
== NULL
)
424 rep
->mul_bssid
->present
= 1;
425 rep
->mul_bssid
->max_bssid_indicator
= pos
[0];
426 rep
->mul_bssid
->num_vendor_specific
= pos
[1];
427 os_memcpy(rep
->mul_bssid
->vendor_specific
, pos
+ 2, elen
- 2);
433 static void wnm_parse_neighbor_report(struct wpa_supplicant
*wpa_s
,
434 const u8
*pos
, u8 len
,
435 struct neighbor_report
*rep
)
440 wpa_printf(MSG_DEBUG
, "WNM: Too short neighbor report");
444 os_memcpy(rep
->bssid
, pos
, ETH_ALEN
);
445 os_memcpy(rep
->bssid_information
, pos
+ ETH_ALEN
, 4);
446 rep
->regulatory_class
= *(pos
+ 10);
447 rep
->channel_number
= *(pos
+ 11);
448 rep
->phy_type
= *(pos
+ 12);
458 wnm_parse_neighbor_report_elem(rep
, id
, elen
, pos
);
465 static int compare_scan_neighbor_results(struct wpa_supplicant
*wpa_s
,
466 struct wpa_scan_results
*scan_res
,
467 struct neighbor_report
*neigh_rep
,
468 u8 num_neigh_rep
, u8
*bssid_to_connect
)
473 if (scan_res
== NULL
|| num_neigh_rep
== 0)
476 wpa_printf(MSG_DEBUG
, "WNM: Current BSS " MACSTR
" RSSI %d",
477 MAC2STR(wpa_s
->bssid
),
478 wpa_s
->current_bss
? wpa_s
->current_bss
->level
: 0);
480 for (i
= 0; i
< num_neigh_rep
; i
++) {
481 for (j
= 0; j
< scan_res
->num
; j
++) {
482 /* Check for a better RSSI AP */
483 if (os_memcmp(scan_res
->res
[j
]->bssid
,
484 neigh_rep
[i
].bssid
, ETH_ALEN
) == 0 &&
485 scan_res
->res
[j
]->level
>
486 wpa_s
->current_bss
->level
) {
487 /* Got a BSSID with better RSSI value */
488 os_memcpy(bssid_to_connect
, neigh_rep
[i
].bssid
,
490 wpa_printf(MSG_DEBUG
, "Found a BSS " MACSTR
491 " with better scan RSSI %d",
492 MAC2STR(scan_res
->res
[j
]->bssid
),
493 scan_res
->res
[j
]->level
);
496 wpa_printf(MSG_DEBUG
, "scan_res[%d] " MACSTR
498 MAC2STR(scan_res
->res
[j
]->bssid
),
499 scan_res
->res
[j
]->level
);
507 static void wnm_send_bss_transition_mgmt_resp(
508 struct wpa_supplicant
*wpa_s
, u8 dialog_token
,
509 enum bss_trans_mgmt_status_code status
, u8 delay
,
510 const u8
*target_bssid
)
513 struct ieee80211_mgmt
*mgmt
;
516 wpa_printf(MSG_DEBUG
, "WNM: Send BSS Transition Management Response "
517 "to " MACSTR
" dialog_token=%u status=%u delay=%d",
518 MAC2STR(wpa_s
->bssid
), dialog_token
, status
, delay
);
520 mgmt
= (struct ieee80211_mgmt
*) buf
;
521 os_memset(&buf
, 0, sizeof(buf
));
522 os_memcpy(mgmt
->da
, wpa_s
->bssid
, ETH_ALEN
);
523 os_memcpy(mgmt
->sa
, wpa_s
->own_addr
, ETH_ALEN
);
524 os_memcpy(mgmt
->bssid
, wpa_s
->bssid
, ETH_ALEN
);
525 mgmt
->frame_control
= IEEE80211_FC(WLAN_FC_TYPE_MGMT
,
526 WLAN_FC_STYPE_ACTION
);
527 mgmt
->u
.action
.category
= WLAN_ACTION_WNM
;
528 mgmt
->u
.action
.u
.bss_tm_resp
.action
= WNM_BSS_TRANS_MGMT_RESP
;
529 mgmt
->u
.action
.u
.bss_tm_resp
.dialog_token
= dialog_token
;
530 mgmt
->u
.action
.u
.bss_tm_resp
.status_code
= status
;
531 mgmt
->u
.action
.u
.bss_tm_resp
.bss_termination_delay
= delay
;
532 pos
= mgmt
->u
.action
.u
.bss_tm_resp
.variable
;
534 os_memcpy(pos
, target_bssid
, ETH_ALEN
);
538 len
= pos
- (u8
*) &mgmt
->u
.action
.category
;
540 wpa_drv_send_action(wpa_s
, wpa_s
->assoc_freq
, 0, wpa_s
->bssid
,
541 wpa_s
->own_addr
, wpa_s
->bssid
,
542 &mgmt
->u
.action
.category
, len
, 0);
546 void wnm_scan_response(struct wpa_supplicant
*wpa_s
,
547 struct wpa_scan_results
*scan_res
)
551 if (scan_res
== NULL
) {
552 wpa_printf(MSG_ERROR
, "Scan result is NULL");
553 goto send_bss_resp_fail
;
556 /* Compare the Neighbor Report and scan results */
557 if (compare_scan_neighbor_results(wpa_s
, scan_res
,
558 wpa_s
->wnm_neighbor_report_elements
,
559 wpa_s
->wnm_num_neighbor_report
,
561 /* Associate to the network */
563 struct wpa_ssid
*ssid
= wpa_s
->current_ssid
;
565 bss
= wpa_bss_get_bssid(wpa_s
, bssid
);
567 wpa_printf(MSG_DEBUG
, "WNM: Target AP not found from "
569 goto send_bss_resp_fail
;
572 /* Send the BSS Management Response - Accept */
573 if (wpa_s
->wnm_reply
) {
574 wnm_send_bss_transition_mgmt_resp(wpa_s
,
575 wpa_s
->wnm_dialog_token
,
580 wpa_s
->reassociate
= 1;
581 wpa_supplicant_connect(wpa_s
, bss
, ssid
);
582 wnm_deallocate_memory(wpa_s
);
586 /* Send reject response for all the failures */
588 wnm_deallocate_memory(wpa_s
);
589 if (wpa_s
->wnm_reply
) {
590 wnm_send_bss_transition_mgmt_resp(wpa_s
,
591 wpa_s
->wnm_dialog_token
,
592 WNM_BSS_TM_REJECT_UNSPECIFIED
,
599 static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant
*wpa_s
,
600 const u8
*pos
, const u8
*end
,
606 wpa_s
->wnm_dialog_token
= pos
[0];
607 wpa_s
->wnm_mode
= pos
[1];
608 wpa_s
->wnm_dissoc_timer
= WPA_GET_LE16(pos
+ 2);
609 wpa_s
->wnm_validity_interval
= pos
[4];
610 wpa_s
->wnm_reply
= reply
;
612 wpa_printf(MSG_DEBUG
, "WNM: BSS Transition Management Request: "
613 "dialog_token=%u request_mode=0x%x "
614 "disassoc_timer=%u validity_interval=%u",
615 wpa_s
->wnm_dialog_token
, wpa_s
->wnm_mode
,
616 wpa_s
->wnm_dissoc_timer
, wpa_s
->wnm_validity_interval
);
620 if (wpa_s
->wnm_mode
& WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED
) {
621 if (pos
+ 12 > end
) {
622 wpa_printf(MSG_DEBUG
, "WNM: Too short BSS TM Request");
625 os_memcpy(wpa_s
->wnm_bss_termination_duration
, pos
, 12);
626 pos
+= 12; /* BSS Termination Duration */
629 if (wpa_s
->wnm_mode
& WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT
) {
631 unsigned int beacon_int
;
633 if (pos
+ 1 > end
|| pos
+ 1 + pos
[0] > end
) {
634 wpa_printf(MSG_DEBUG
, "WNM: Invalid BSS Transition "
635 "Management Request (URL)");
638 os_memcpy(url
, pos
+ 1, pos
[0]);
642 if (wpa_s
->current_bss
)
643 beacon_int
= wpa_s
->current_bss
->beacon_int
;
645 beacon_int
= 100; /* best guess */
647 wpa_msg(wpa_s
, MSG_INFO
, ESS_DISASSOC_IMMINENT
"%d %u %s",
648 wpa_sm_pmf_enabled(wpa_s
->wpa
),
649 wpa_s
->wnm_dissoc_timer
* beacon_int
* 128 / 125, url
);
652 if (wpa_s
->wnm_mode
& WNM_BSS_TM_REQ_DISASSOC_IMMINENT
) {
653 wpa_msg(wpa_s
, MSG_INFO
, "WNM: Disassociation Imminent - "
654 "Disassociation Timer %u", wpa_s
->wnm_dissoc_timer
);
655 if (wpa_s
->wnm_dissoc_timer
&& !wpa_s
->scanning
) {
656 /* TODO: mark current BSS less preferred for
658 wpa_printf(MSG_DEBUG
, "Trying to find another BSS");
659 wpa_supplicant_req_scan(wpa_s
, 0, 0);
663 if (wpa_s
->wnm_mode
& WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED
) {
664 wpa_msg(wpa_s
, MSG_INFO
, "WNM: Preferred List Available");
665 wpa_s
->wnm_num_neighbor_report
= 0;
666 os_free(wpa_s
->wnm_neighbor_report_elements
);
667 wpa_s
->wnm_neighbor_report_elements
= os_zalloc(
668 WNM_MAX_NEIGHBOR_REPORT
*
669 sizeof(struct neighbor_report
));
670 if (wpa_s
->wnm_neighbor_report_elements
== NULL
)
673 while (pos
+ 2 <= end
&&
674 wpa_s
->wnm_num_neighbor_report
< WNM_MAX_NEIGHBOR_REPORT
)
679 wpa_printf(MSG_DEBUG
, "WNM: Neighbor report tag %u",
681 if (pos
+ len
> end
) {
682 wpa_printf(MSG_DEBUG
, "WNM: Truncated request");
685 wnm_parse_neighbor_report(
687 &wpa_s
->wnm_neighbor_report_elements
[
688 wpa_s
->wnm_num_neighbor_report
]);
691 wpa_s
->wnm_num_neighbor_report
++;
694 wpa_s
->scan_res_handler
= wnm_scan_response
;
695 wpa_supplicant_req_scan(wpa_s
, 0, 0);
697 enum bss_trans_mgmt_status_code status
;
698 if (wpa_s
->wnm_mode
& WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT
)
699 status
= WNM_BSS_TM_ACCEPT
;
701 wpa_msg(wpa_s
, MSG_INFO
, "WNM: BSS Transition Management Request did not include candidates");
702 status
= WNM_BSS_TM_REJECT_UNSPECIFIED
;
704 wnm_send_bss_transition_mgmt_resp(wpa_s
,
705 wpa_s
->wnm_dialog_token
,
711 int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant
*wpa_s
,
715 struct ieee80211_mgmt
*mgmt
;
719 wpa_printf(MSG_DEBUG
, "WNM: Send BSS Transition Management Query to "
720 MACSTR
" query_reason=%u",
721 MAC2STR(wpa_s
->bssid
), query_reason
);
723 mgmt
= (struct ieee80211_mgmt
*) buf
;
724 os_memset(&buf
, 0, sizeof(buf
));
725 os_memcpy(mgmt
->da
, wpa_s
->bssid
, ETH_ALEN
);
726 os_memcpy(mgmt
->sa
, wpa_s
->own_addr
, ETH_ALEN
);
727 os_memcpy(mgmt
->bssid
, wpa_s
->bssid
, ETH_ALEN
);
728 mgmt
->frame_control
= IEEE80211_FC(WLAN_FC_TYPE_MGMT
,
729 WLAN_FC_STYPE_ACTION
);
730 mgmt
->u
.action
.category
= WLAN_ACTION_WNM
;
731 mgmt
->u
.action
.u
.bss_tm_query
.action
= WNM_BSS_TRANS_MGMT_QUERY
;
732 mgmt
->u
.action
.u
.bss_tm_query
.dialog_token
= 1;
733 mgmt
->u
.action
.u
.bss_tm_query
.query_reason
= query_reason
;
734 pos
= mgmt
->u
.action
.u
.bss_tm_query
.variable
;
736 len
= pos
- (u8
*) &mgmt
->u
.action
.category
;
738 ret
= wpa_drv_send_action(wpa_s
, wpa_s
->assoc_freq
, 0, wpa_s
->bssid
,
739 wpa_s
->own_addr
, wpa_s
->bssid
,
740 &mgmt
->u
.action
.category
, len
, 0);
746 void ieee802_11_rx_wnm_action(struct wpa_supplicant
*wpa_s
,
747 struct rx_action
*action
)
752 if (action
->data
== NULL
|| action
->len
== 0)
756 end
= pos
+ action
->len
;
759 wpa_printf(MSG_DEBUG
, "WNM: RX action %u from " MACSTR
,
760 act
, MAC2STR(action
->sa
));
761 if (wpa_s
->wpa_state
< WPA_ASSOCIATED
||
762 os_memcmp(action
->sa
, wpa_s
->bssid
, ETH_ALEN
) != 0) {
763 wpa_printf(MSG_DEBUG
, "WNM: Ignore unexpected WNM Action "
769 case WNM_BSS_TRANS_MGMT_REQ
:
770 ieee802_11_rx_bss_trans_mgmt_req(wpa_s
, pos
, end
,
771 !(action
->da
[0] & 0x01));
773 case WNM_SLEEP_MODE_RESP
:
774 ieee802_11_rx_wnmsleep_resp(wpa_s
, action
->data
, action
->len
);
777 wpa_printf(MSG_ERROR
, "WNM: Unknown request");