Line data Source code
1 : /*
2 : * wpa_supplicant - WNM
3 : * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
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 "common/wpa_ctrl.h"
15 : #include "rsn_supp/wpa.h"
16 : #include "wpa_supplicant_i.h"
17 : #include "driver_i.h"
18 : #include "scan.h"
19 : #include "ctrl_iface.h"
20 : #include "bss.h"
21 : #include "wnm_sta.h"
22 : #include "hs20_supplicant.h"
23 :
24 : #define MAX_TFS_IE_LEN 1024
25 : #define WNM_MAX_NEIGHBOR_REPORT 10
26 :
27 : #define WNM_SCAN_RESULT_AGE 2 /* 2 seconds */
28 :
29 : /* get the TFS IE from driver */
30 15 : static int ieee80211_11_get_tfs_ie(struct wpa_supplicant *wpa_s, u8 *buf,
31 : u16 *buf_len, enum wnm_oper oper)
32 : {
33 15 : wpa_printf(MSG_DEBUG, "%s: TFS get operation %d", __func__, oper);
34 :
35 15 : return wpa_drv_wnm_oper(wpa_s, oper, wpa_s->bssid, buf, buf_len);
36 : }
37 :
38 :
39 : /* set the TFS IE to driver */
40 1 : static int ieee80211_11_set_tfs_ie(struct wpa_supplicant *wpa_s,
41 : const u8 *addr, const u8 *buf, u16 buf_len,
42 : enum wnm_oper oper)
43 : {
44 1 : u16 len = buf_len;
45 :
46 1 : wpa_printf(MSG_DEBUG, "%s: TFS set operation %d", __func__, oper);
47 :
48 1 : return wpa_drv_wnm_oper(wpa_s, oper, addr, (u8 *) buf, &len);
49 : }
50 :
51 :
52 : /* MLME-SLEEPMODE.request */
53 20 : int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s,
54 : u8 action, u16 intval, struct wpabuf *tfs_req)
55 : {
56 : struct ieee80211_mgmt *mgmt;
57 : int res;
58 : size_t len;
59 : struct wnm_sleep_element *wnmsleep_ie;
60 : u8 *wnmtfs_ie;
61 : u8 wnmsleep_ie_len;
62 : u16 wnmtfs_ie_len; /* possibly multiple IE(s) */
63 20 : enum wnm_oper tfs_oper = action == 0 ? WNM_SLEEP_TFS_REQ_IE_ADD :
64 : WNM_SLEEP_TFS_REQ_IE_NONE;
65 :
66 120 : wpa_printf(MSG_DEBUG, "WNM: Request to send WNM-Sleep Mode Request "
67 : "action=%s to " MACSTR,
68 : action == 0 ? "enter" : "exit",
69 120 : MAC2STR(wpa_s->bssid));
70 :
71 : /* WNM-Sleep Mode IE */
72 20 : wnmsleep_ie_len = sizeof(struct wnm_sleep_element);
73 20 : wnmsleep_ie = os_zalloc(sizeof(struct wnm_sleep_element));
74 20 : if (wnmsleep_ie == NULL)
75 1 : return -1;
76 19 : wnmsleep_ie->eid = WLAN_EID_WNMSLEEP;
77 19 : wnmsleep_ie->len = wnmsleep_ie_len - 2;
78 19 : wnmsleep_ie->action_type = action;
79 19 : wnmsleep_ie->status = WNM_STATUS_SLEEP_ACCEPT;
80 19 : wnmsleep_ie->intval = host_to_le16(intval);
81 19 : wpa_hexdump(MSG_DEBUG, "WNM: WNM-Sleep Mode element",
82 : (u8 *) wnmsleep_ie, wnmsleep_ie_len);
83 :
84 : /* TFS IE(s) */
85 19 : if (tfs_req) {
86 4 : wnmtfs_ie_len = wpabuf_len(tfs_req);
87 4 : wnmtfs_ie = os_malloc(wnmtfs_ie_len);
88 4 : if (wnmtfs_ie == NULL) {
89 1 : os_free(wnmsleep_ie);
90 1 : return -1;
91 : }
92 3 : os_memcpy(wnmtfs_ie, wpabuf_head(tfs_req), wnmtfs_ie_len);
93 : } else {
94 15 : wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN);
95 15 : if (wnmtfs_ie == NULL) {
96 0 : os_free(wnmsleep_ie);
97 0 : return -1;
98 : }
99 15 : if (ieee80211_11_get_tfs_ie(wpa_s, wnmtfs_ie, &wnmtfs_ie_len,
100 : tfs_oper)) {
101 15 : wnmtfs_ie_len = 0;
102 15 : os_free(wnmtfs_ie);
103 15 : wnmtfs_ie = NULL;
104 : }
105 : }
106 18 : wpa_hexdump(MSG_DEBUG, "WNM: TFS Request element",
107 : (u8 *) wnmtfs_ie, wnmtfs_ie_len);
108 :
109 18 : mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len + wnmtfs_ie_len);
110 18 : if (mgmt == NULL) {
111 2 : wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for "
112 : "WNM-Sleep Request action frame");
113 2 : os_free(wnmsleep_ie);
114 2 : os_free(wnmtfs_ie);
115 2 : return -1;
116 : }
117 :
118 16 : os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
119 16 : os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
120 16 : os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
121 16 : mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
122 : WLAN_FC_STYPE_ACTION);
123 16 : mgmt->u.action.category = WLAN_ACTION_WNM;
124 16 : mgmt->u.action.u.wnm_sleep_req.action = WNM_SLEEP_MODE_REQ;
125 16 : mgmt->u.action.u.wnm_sleep_req.dialogtoken = 1;
126 16 : os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable, wnmsleep_ie,
127 : wnmsleep_ie_len);
128 : /* copy TFS IE here */
129 16 : if (wnmtfs_ie_len > 0) {
130 2 : os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable +
131 : wnmsleep_ie_len, wnmtfs_ie, wnmtfs_ie_len);
132 : }
133 :
134 16 : len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_req) + wnmsleep_ie_len +
135 : wnmtfs_ie_len;
136 :
137 16 : res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
138 16 : wpa_s->own_addr, wpa_s->bssid,
139 16 : &mgmt->u.action.category, len, 0);
140 16 : if (res < 0)
141 1 : wpa_printf(MSG_DEBUG, "Failed to send WNM-Sleep Request "
142 : "(action=%d, intval=%d)", action, intval);
143 : else
144 15 : wpa_s->wnmsleep_used = 1;
145 :
146 16 : os_free(wnmsleep_ie);
147 16 : os_free(wnmtfs_ie);
148 16 : os_free(mgmt);
149 :
150 16 : return res;
151 : }
152 :
153 :
154 9 : static void wnm_sleep_mode_enter_success(struct wpa_supplicant *wpa_s,
155 : const u8 *tfsresp_ie_start,
156 : const u8 *tfsresp_ie_end)
157 : {
158 9 : wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_CONFIRM,
159 9 : wpa_s->bssid, NULL, NULL);
160 : /* remove GTK/IGTK ?? */
161 :
162 : /* set the TFS Resp IE(s) */
163 10 : if (tfsresp_ie_start && tfsresp_ie_end &&
164 1 : tfsresp_ie_end - tfsresp_ie_start >= 0) {
165 : u16 tfsresp_ie_len;
166 1 : tfsresp_ie_len = (tfsresp_ie_end + tfsresp_ie_end[1] + 2) -
167 : tfsresp_ie_start;
168 1 : wpa_printf(MSG_DEBUG, "TFS Resp IE(s) found");
169 : /* pass the TFS Resp IE(s) to driver for processing */
170 1 : if (ieee80211_11_set_tfs_ie(wpa_s, wpa_s->bssid,
171 : tfsresp_ie_start,
172 : tfsresp_ie_len,
173 : WNM_SLEEP_TFS_RESP_IE_SET))
174 1 : wpa_printf(MSG_DEBUG, "WNM: Fail to set TFS Resp IE");
175 : }
176 9 : }
177 :
178 :
179 12 : static void wnm_sleep_mode_exit_success(struct wpa_supplicant *wpa_s,
180 : const u8 *frm, u16 key_len_total)
181 : {
182 : u8 *ptr, *end;
183 : u8 gtk_len;
184 :
185 12 : wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_EXIT_CONFIRM, wpa_s->bssid,
186 : NULL, NULL);
187 :
188 : /* Install GTK/IGTK */
189 :
190 : /* point to key data field */
191 12 : ptr = (u8 *) frm + 1 + 2;
192 12 : end = ptr + key_len_total;
193 12 : wpa_hexdump_key(MSG_DEBUG, "WNM: Key Data", ptr, key_len_total);
194 :
195 12 : if (key_len_total && !wpa_sm_pmf_enabled(wpa_s->wpa)) {
196 1 : wpa_msg(wpa_s, MSG_INFO,
197 : "WNM: Ignore Key Data in WNM-Sleep Mode Response - PMF not enabled");
198 13 : return;
199 : }
200 :
201 25 : while (end - ptr > 1) {
202 9 : if (2 + ptr[1] > end - ptr) {
203 1 : wpa_printf(MSG_DEBUG, "WNM: Invalid Key Data element "
204 : "length");
205 1 : if (end > ptr) {
206 1 : wpa_hexdump(MSG_DEBUG, "WNM: Remaining data",
207 1 : ptr, end - ptr);
208 : }
209 1 : break;
210 : }
211 8 : if (*ptr == WNM_SLEEP_SUBELEM_GTK) {
212 5 : if (ptr[1] < 11 + 5) {
213 1 : wpa_printf(MSG_DEBUG, "WNM: Too short GTK "
214 : "subelem");
215 1 : break;
216 : }
217 4 : gtk_len = *(ptr + 4);
218 4 : if (ptr[1] < 11 + gtk_len ||
219 2 : gtk_len < 5 || gtk_len > 32) {
220 2 : wpa_printf(MSG_DEBUG, "WNM: Invalid GTK "
221 : "subelem");
222 2 : break;
223 : }
224 2 : wpa_wnmsleep_install_key(
225 : wpa_s->wpa,
226 : WNM_SLEEP_SUBELEM_GTK,
227 : ptr);
228 2 : ptr += 13 + gtk_len;
229 : #ifdef CONFIG_IEEE80211W
230 3 : } else if (*ptr == WNM_SLEEP_SUBELEM_IGTK) {
231 2 : if (ptr[1] < 2 + 6 + WPA_IGTK_LEN) {
232 1 : wpa_printf(MSG_DEBUG, "WNM: Too short IGTK "
233 : "subelem");
234 1 : break;
235 : }
236 1 : wpa_wnmsleep_install_key(wpa_s->wpa,
237 : WNM_SLEEP_SUBELEM_IGTK, ptr);
238 1 : ptr += 10 + WPA_IGTK_LEN;
239 : #endif /* CONFIG_IEEE80211W */
240 : } else
241 1 : break; /* skip the loop */
242 : }
243 : }
244 :
245 :
246 32 : static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s,
247 : const u8 *frm, int len)
248 : {
249 : /*
250 : * Action [1] | Dialog Token [1] | Key Data Len [2] | Key Data |
251 : * WNM-Sleep Mode IE | TFS Response IE
252 : */
253 32 : const u8 *pos = frm; /* point to payload after the action field */
254 : u16 key_len_total;
255 32 : struct wnm_sleep_element *wnmsleep_ie = NULL;
256 : /* multiple TFS Resp IE (assuming consecutive) */
257 32 : const u8 *tfsresp_ie_start = NULL;
258 32 : const u8 *tfsresp_ie_end = NULL;
259 : size_t left;
260 :
261 32 : if (!wpa_s->wnmsleep_used) {
262 0 : wpa_printf(MSG_DEBUG,
263 : "WNM: Ignore WNM-Sleep Mode Response frame since WNM-Sleep Mode has not been used in this association");
264 0 : return;
265 : }
266 :
267 32 : if (len < 3)
268 2 : return;
269 30 : key_len_total = WPA_GET_LE16(frm + 1);
270 :
271 60 : wpa_printf(MSG_DEBUG, "WNM-Sleep Mode Response token=%u key_len_total=%d",
272 30 : frm[0], key_len_total);
273 30 : left = len - 3;
274 30 : if (key_len_total > left) {
275 2 : wpa_printf(MSG_INFO, "WNM: Too short frame for Key Data field");
276 2 : return;
277 : }
278 28 : pos += 3 + key_len_total;
279 93 : while (pos - frm + 1 < len) {
280 38 : u8 ie_len = *(pos + 1);
281 38 : if (2 + ie_len > frm + len - pos) {
282 1 : wpa_printf(MSG_INFO, "WNM: Invalid IE len %u", ie_len);
283 1 : break;
284 : }
285 37 : wpa_hexdump(MSG_DEBUG, "WNM: Element", pos, 2 + ie_len);
286 37 : if (*pos == WLAN_EID_WNMSLEEP && ie_len >= 4)
287 23 : wnmsleep_ie = (struct wnm_sleep_element *) pos;
288 14 : else if (*pos == WLAN_EID_TFS_RESP) {
289 12 : if (!tfsresp_ie_start)
290 12 : tfsresp_ie_start = pos;
291 12 : tfsresp_ie_end = pos;
292 : } else
293 2 : wpa_printf(MSG_DEBUG, "EID %d not recognized", *pos);
294 37 : pos += ie_len + 2;
295 : }
296 :
297 28 : if (!wnmsleep_ie) {
298 5 : wpa_printf(MSG_DEBUG, "No WNM-Sleep IE found");
299 5 : return;
300 : }
301 :
302 25 : if (wnmsleep_ie->status == WNM_STATUS_SLEEP_ACCEPT ||
303 2 : wnmsleep_ie->status == WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE) {
304 42 : wpa_printf(MSG_DEBUG, "Successfully recv WNM-Sleep Response "
305 : "frame (action=%d, intval=%d)",
306 42 : wnmsleep_ie->action_type, wnmsleep_ie->intval);
307 42 : if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER) {
308 9 : wnm_sleep_mode_enter_success(wpa_s, tfsresp_ie_start,
309 : tfsresp_ie_end);
310 12 : } else if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) {
311 12 : wnm_sleep_mode_exit_success(wpa_s, frm, key_len_total);
312 : }
313 : } else {
314 4 : wpa_printf(MSG_DEBUG, "Reject recv WNM-Sleep Response frame "
315 : "(action=%d, intval=%d)",
316 4 : wnmsleep_ie->action_type, wnmsleep_ie->intval);
317 2 : if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER)
318 1 : wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_FAIL,
319 1 : wpa_s->bssid, NULL, NULL);
320 1 : else if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT)
321 1 : wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_EXIT_FAIL,
322 1 : wpa_s->bssid, NULL, NULL);
323 : }
324 : }
325 :
326 :
327 797 : void wnm_deallocate_memory(struct wpa_supplicant *wpa_s)
328 : {
329 : int i;
330 :
331 910 : for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
332 113 : os_free(wpa_s->wnm_neighbor_report_elements[i].meas_pilot);
333 113 : os_free(wpa_s->wnm_neighbor_report_elements[i].mul_bssid);
334 : }
335 :
336 797 : wpa_s->wnm_num_neighbor_report = 0;
337 797 : os_free(wpa_s->wnm_neighbor_report_elements);
338 797 : wpa_s->wnm_neighbor_report_elements = NULL;
339 797 : }
340 :
341 :
342 47 : static void wnm_parse_neighbor_report_elem(struct neighbor_report *rep,
343 : u8 id, u8 elen, const u8 *pos)
344 : {
345 47 : switch (id) {
346 : case WNM_NEIGHBOR_TSF:
347 3 : if (elen < 2 + 2) {
348 1 : wpa_printf(MSG_DEBUG, "WNM: Too short TSF");
349 1 : break;
350 : }
351 2 : rep->tsf_offset = WPA_GET_LE16(pos);
352 2 : rep->beacon_int = WPA_GET_LE16(pos + 2);
353 2 : rep->tsf_present = 1;
354 2 : break;
355 : case WNM_NEIGHBOR_CONDENSED_COUNTRY_STRING:
356 3 : if (elen < 2) {
357 1 : wpa_printf(MSG_DEBUG, "WNM: Too short condensed "
358 : "country string");
359 1 : break;
360 : }
361 2 : os_memcpy(rep->country, pos, 2);
362 2 : rep->country_present = 1;
363 2 : break;
364 : case WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE:
365 26 : if (elen < 1) {
366 1 : wpa_printf(MSG_DEBUG, "WNM: Too short BSS transition "
367 : "candidate");
368 1 : break;
369 : }
370 25 : rep->preference = pos[0];
371 25 : rep->preference_present = 1;
372 25 : break;
373 : case WNM_NEIGHBOR_BSS_TERMINATION_DURATION:
374 3 : rep->bss_term_tsf = WPA_GET_LE64(pos);
375 3 : rep->bss_term_dur = WPA_GET_LE16(pos + 8);
376 3 : rep->bss_term_present = 1;
377 3 : break;
378 : case WNM_NEIGHBOR_BEARING:
379 3 : if (elen < 8) {
380 1 : wpa_printf(MSG_DEBUG, "WNM: Too short neighbor "
381 : "bearing");
382 1 : break;
383 : }
384 2 : rep->bearing = WPA_GET_LE16(pos);
385 2 : rep->distance = WPA_GET_LE32(pos + 2);
386 2 : rep->rel_height = WPA_GET_LE16(pos + 2 + 4);
387 2 : rep->bearing_present = 1;
388 2 : break;
389 : case WNM_NEIGHBOR_MEASUREMENT_PILOT:
390 3 : if (elen < 1) {
391 1 : wpa_printf(MSG_DEBUG, "WNM: Too short measurement "
392 : "pilot");
393 1 : break;
394 : }
395 2 : os_free(rep->meas_pilot);
396 2 : rep->meas_pilot = os_zalloc(sizeof(struct measurement_pilot));
397 2 : if (rep->meas_pilot == NULL)
398 0 : break;
399 2 : rep->meas_pilot->measurement_pilot = pos[0];
400 2 : rep->meas_pilot->subelem_len = elen - 1;
401 2 : os_memcpy(rep->meas_pilot->subelems, pos + 1, elen - 1);
402 2 : break;
403 : case WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES:
404 3 : if (elen < 5) {
405 1 : wpa_printf(MSG_DEBUG, "WNM: Too short RRM enabled "
406 : "capabilities");
407 1 : break;
408 : }
409 2 : os_memcpy(rep->rm_capab, pos, 5);
410 2 : rep->rm_capab_present = 1;
411 2 : break;
412 : case WNM_NEIGHBOR_MULTIPLE_BSSID:
413 3 : if (elen < 1) {
414 1 : wpa_printf(MSG_DEBUG, "WNM: Too short multiple BSSID");
415 1 : break;
416 : }
417 2 : os_free(rep->mul_bssid);
418 2 : rep->mul_bssid = os_zalloc(sizeof(struct multiple_bssid));
419 2 : if (rep->mul_bssid == NULL)
420 0 : break;
421 2 : rep->mul_bssid->max_bssid_indicator = pos[0];
422 2 : rep->mul_bssid->subelem_len = elen - 1;
423 2 : os_memcpy(rep->mul_bssid->subelems, pos + 1, elen - 1);
424 2 : break;
425 : }
426 47 : }
427 :
428 :
429 112 : static int wnm_nei_get_chan(struct wpa_supplicant *wpa_s, u8 op_class, u8 chan)
430 : {
431 112 : struct wpa_bss *bss = wpa_s->current_bss;
432 112 : const char *country = NULL;
433 : int freq;
434 :
435 112 : if (bss) {
436 112 : const u8 *elem = wpa_bss_get_ie(bss, WLAN_EID_COUNTRY);
437 :
438 112 : if (elem && elem[1] >= 2)
439 105 : country = (const char *) (elem + 2);
440 : }
441 :
442 112 : freq = ieee80211_chan_to_freq(country, op_class, chan);
443 112 : if (freq <= 0 && op_class == 0) {
444 : /*
445 : * Some APs do not advertise correct operating class
446 : * information. Try to determine the most likely operating
447 : * frequency based on the channel number.
448 : */
449 7 : if (chan >= 1 && chan <= 13)
450 1 : freq = 2407 + chan * 5;
451 6 : else if (chan == 14)
452 0 : freq = 2484;
453 6 : else if (chan >= 36 && chan <= 169)
454 1 : freq = 5000 + chan * 5;
455 : }
456 112 : return freq;
457 : }
458 :
459 :
460 113 : static void wnm_parse_neighbor_report(struct wpa_supplicant *wpa_s,
461 : const u8 *pos, u8 len,
462 : struct neighbor_report *rep)
463 : {
464 113 : u8 left = len;
465 :
466 113 : if (left < 13) {
467 1 : wpa_printf(MSG_DEBUG, "WNM: Too short neighbor report");
468 114 : return;
469 : }
470 :
471 112 : os_memcpy(rep->bssid, pos, ETH_ALEN);
472 112 : rep->bssid_info = WPA_GET_LE32(pos + ETH_ALEN);
473 112 : rep->regulatory_class = *(pos + 10);
474 112 : rep->channel_number = *(pos + 11);
475 112 : rep->phy_type = *(pos + 12);
476 :
477 112 : pos += 13;
478 112 : left -= 13;
479 :
480 271 : while (left >= 2) {
481 : u8 id, elen;
482 :
483 48 : id = *pos++;
484 48 : elen = *pos++;
485 48 : wpa_printf(MSG_DEBUG, "WNM: Subelement id=%u len=%u", id, elen);
486 48 : left -= 2;
487 48 : if (elen > left) {
488 1 : wpa_printf(MSG_DEBUG,
489 : "WNM: Truncated neighbor report subelement");
490 1 : break;
491 : }
492 47 : wnm_parse_neighbor_report_elem(rep, id, elen, pos);
493 47 : left -= elen;
494 47 : pos += elen;
495 : }
496 :
497 112 : rep->freq = wnm_nei_get_chan(wpa_s, rep->regulatory_class,
498 112 : rep->channel_number);
499 : }
500 :
501 :
502 : static struct wpa_bss *
503 54 : compare_scan_neighbor_results(struct wpa_supplicant *wpa_s, os_time_t age_secs)
504 : {
505 :
506 : u8 i;
507 54 : struct wpa_bss *bss = wpa_s->current_bss;
508 : struct wpa_bss *target;
509 :
510 54 : if (!bss)
511 0 : return NULL;
512 :
513 378 : wpa_printf(MSG_DEBUG, "WNM: Current BSS " MACSTR " RSSI %d",
514 324 : MAC2STR(wpa_s->bssid), bss->level);
515 :
516 261 : for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
517 : struct neighbor_report *nei;
518 :
519 216 : nei = &wpa_s->wnm_neighbor_report_elements[i];
520 216 : if (nei->preference_present && nei->preference == 0) {
521 0 : wpa_printf(MSG_DEBUG, "Skip excluded BSS " MACSTR,
522 0 : MAC2STR(nei->bssid));
523 0 : continue;
524 : }
525 :
526 216 : target = wpa_bss_get_bssid(wpa_s, nei->bssid);
527 216 : if (!target) {
528 1462 : wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
529 : " (pref %d) not found in scan results",
530 1224 : MAC2STR(nei->bssid),
531 238 : nei->preference_present ? nei->preference :
532 : -1);
533 204 : continue;
534 : }
535 :
536 12 : if (age_secs) {
537 : struct os_reltime now;
538 :
539 18 : if (os_get_reltime(&now) == 0 &&
540 9 : os_reltime_expired(&now, &target->last_update,
541 : age_secs)) {
542 0 : wpa_printf(MSG_DEBUG,
543 : "Candidate BSS is more than %ld seconds old",
544 : age_secs);
545 0 : continue;
546 : }
547 : }
548 :
549 24 : if (bss->ssid_len != target->ssid_len ||
550 12 : os_memcmp(bss->ssid, target->ssid, bss->ssid_len) != 0) {
551 : /*
552 : * TODO: Could consider allowing transition to another
553 : * ESS if PMF was enabled for the association.
554 : */
555 0 : wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
556 : " (pref %d) in different ESS",
557 0 : MAC2STR(nei->bssid),
558 0 : nei->preference_present ? nei->preference :
559 : -1);
560 0 : continue;
561 : }
562 :
563 24 : if (wpa_s->current_ssid &&
564 12 : !wpa_scan_res_match(wpa_s, 0, target, wpa_s->current_ssid,
565 : 1)) {
566 24 : wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
567 : " (pref %d) does not match the current network profile",
568 18 : MAC2STR(nei->bssid),
569 6 : nei->preference_present ? nei->preference :
570 : -1);
571 3 : continue;
572 : }
573 :
574 9 : if (wpa_is_bss_tmp_disallowed(wpa_s, target->bssid)) {
575 0 : wpa_printf(MSG_DEBUG,
576 : "MBO: Candidate BSS " MACSTR
577 : " retry delay is not over yet",
578 0 : MAC2STR(nei->bssid));
579 0 : continue;
580 : }
581 :
582 9 : if (target->level < bss->level && target->level < -80) {
583 0 : wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
584 : " (pref %d) does not have sufficient signal level (%d)",
585 0 : MAC2STR(nei->bssid),
586 0 : nei->preference_present ? nei->preference :
587 : -1,
588 : target->level);
589 0 : continue;
590 : }
591 :
592 63 : wpa_printf(MSG_DEBUG,
593 : "WNM: Found an acceptable preferred transition candidate BSS "
594 : MACSTR " (RSSI %d)",
595 54 : MAC2STR(nei->bssid), target->level);
596 9 : return target;
597 : }
598 :
599 45 : return NULL;
600 : }
601 :
602 :
603 48 : static int wpa_bss_ies_eq(struct wpa_bss *a, struct wpa_bss *b, u8 eid)
604 : {
605 : const u8 *ie_a, *ie_b;
606 :
607 48 : if (!a || !b)
608 0 : return 0;
609 :
610 48 : ie_a = wpa_bss_get_ie(a, eid);
611 48 : ie_b = wpa_bss_get_ie(b, eid);
612 :
613 48 : if (!ie_a || !ie_b || ie_a[1] != ie_b[1])
614 27 : return 0;
615 :
616 21 : return os_memcmp(ie_a, ie_b, ie_a[1]) == 0;
617 : }
618 :
619 :
620 24 : static u32 wnm_get_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
621 : {
622 24 : u32 info = 0;
623 :
624 24 : info |= NEI_REP_BSSID_INFO_AP_UNKNOWN_REACH;
625 :
626 : /*
627 : * Leave the security and key scope bits unset to indicate that the
628 : * security information is not available.
629 : */
630 :
631 24 : if (bss->caps & WLAN_CAPABILITY_SPECTRUM_MGMT)
632 0 : info |= NEI_REP_BSSID_INFO_SPECTRUM_MGMT;
633 24 : if (bss->caps & WLAN_CAPABILITY_QOS)
634 0 : info |= NEI_REP_BSSID_INFO_QOS;
635 24 : if (bss->caps & WLAN_CAPABILITY_APSD)
636 0 : info |= NEI_REP_BSSID_INFO_APSD;
637 24 : if (bss->caps & WLAN_CAPABILITY_RADIO_MEASUREMENT)
638 0 : info |= NEI_REP_BSSID_INFO_RM;
639 24 : if (bss->caps & WLAN_CAPABILITY_DELAYED_BLOCK_ACK)
640 0 : info |= NEI_REP_BSSID_INFO_DELAYED_BA;
641 24 : if (bss->caps & WLAN_CAPABILITY_IMM_BLOCK_ACK)
642 0 : info |= NEI_REP_BSSID_INFO_IMM_BA;
643 24 : if (wpa_bss_ies_eq(bss, wpa_s->current_bss, WLAN_EID_MOBILITY_DOMAIN))
644 0 : info |= NEI_REP_BSSID_INFO_MOBILITY_DOMAIN;
645 24 : if (wpa_bss_ies_eq(bss, wpa_s->current_bss, WLAN_EID_HT_CAP))
646 21 : info |= NEI_REP_BSSID_INFO_HT;
647 :
648 24 : return info;
649 : }
650 :
651 :
652 24 : static int wnm_add_nei_rep(u8 *buf, size_t len, const u8 *bssid, u32 bss_info,
653 : u8 op_class, u8 chan, u8 phy_type, u8 pref)
654 : {
655 24 : u8 *pos = buf;
656 :
657 24 : if (len < 18) {
658 0 : wpa_printf(MSG_DEBUG,
659 : "WNM: Not enough room for Neighbor Report element");
660 0 : return -1;
661 : }
662 :
663 24 : *pos++ = WLAN_EID_NEIGHBOR_REPORT;
664 : /* length: 13 for basic neighbor report + 3 for preference subelement */
665 24 : *pos++ = 16;
666 24 : os_memcpy(pos, bssid, ETH_ALEN);
667 24 : pos += ETH_ALEN;
668 24 : WPA_PUT_LE32(pos, bss_info);
669 24 : pos += 4;
670 24 : *pos++ = op_class;
671 24 : *pos++ = chan;
672 24 : *pos++ = phy_type;
673 24 : *pos++ = WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE;
674 24 : *pos++ = 1;
675 24 : *pos++ = pref;
676 24 : return pos - buf;
677 : }
678 :
679 :
680 24 : static int wnm_nei_rep_add_bss(struct wpa_supplicant *wpa_s,
681 : struct wpa_bss *bss, u8 *buf, size_t len,
682 : u8 pref)
683 : {
684 : const u8 *ie;
685 : u8 op_class, chan;
686 24 : int sec_chan = 0, vht = 0;
687 : enum phy_type phy_type;
688 : u32 info;
689 24 : struct ieee80211_ht_operation *ht_oper = NULL;
690 24 : struct ieee80211_vht_operation *vht_oper = NULL;
691 :
692 24 : ie = wpa_bss_get_ie(bss, WLAN_EID_HT_OPERATION);
693 24 : if (ie && ie[1] >= 2) {
694 21 : ht_oper = (struct ieee80211_ht_operation *) (ie + 2);
695 :
696 21 : if (ht_oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
697 0 : sec_chan = 1;
698 21 : else if (ht_oper->ht_param &
699 : HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
700 0 : sec_chan = -1;
701 : }
702 :
703 24 : ie = wpa_bss_get_ie(bss, WLAN_EID_VHT_OPERATION);
704 24 : if (ie && ie[1] >= 1) {
705 1 : vht_oper = (struct ieee80211_vht_operation *) (ie + 2);
706 :
707 2 : if (vht_oper->vht_op_info_chwidth == VHT_CHANWIDTH_80MHZ ||
708 2 : vht_oper->vht_op_info_chwidth == VHT_CHANWIDTH_160MHZ ||
709 1 : vht_oper->vht_op_info_chwidth == VHT_CHANWIDTH_80P80MHZ)
710 0 : vht = vht_oper->vht_op_info_chwidth;
711 : }
712 :
713 24 : if (ieee80211_freq_to_channel_ext(bss->freq, sec_chan, vht, &op_class,
714 : &chan) == NUM_HOSTAPD_MODES) {
715 0 : wpa_printf(MSG_DEBUG,
716 : "WNM: Cannot determine operating class and channel");
717 0 : return -2;
718 : }
719 :
720 24 : phy_type = ieee80211_get_phy_type(bss->freq, (ht_oper != NULL),
721 : (vht_oper != NULL));
722 24 : if (phy_type == PHY_TYPE_UNSPECIFIED) {
723 0 : wpa_printf(MSG_DEBUG,
724 : "WNM: Cannot determine BSS phy type for Neighbor Report");
725 0 : return -2;
726 : }
727 :
728 24 : info = wnm_get_bss_info(wpa_s, bss);
729 :
730 24 : return wnm_add_nei_rep(buf, len, bss->bssid, info, op_class, chan,
731 : phy_type, pref);
732 : }
733 :
734 :
735 15 : static int wnm_add_cand_list(struct wpa_supplicant *wpa_s, u8 *buf, size_t len)
736 : {
737 15 : u8 *pos = buf;
738 15 : unsigned int i, pref = 255;
739 : struct os_reltime now;
740 15 : struct wpa_ssid *ssid = wpa_s->current_ssid;
741 :
742 15 : if (!ssid)
743 0 : return 0;
744 :
745 : /*
746 : * TODO: Define when scan results are no longer valid for the candidate
747 : * list.
748 : */
749 15 : os_get_reltime(&now);
750 15 : if (os_reltime_expired(&now, &wpa_s->last_scan, 10))
751 0 : return 0;
752 :
753 15 : wpa_printf(MSG_DEBUG,
754 : "WNM: Add candidate list to BSS Transition Management Response frame");
755 43 : for (i = 0; i < wpa_s->last_scan_res_used && pref; i++) {
756 28 : struct wpa_bss *bss = wpa_s->last_scan_res[i];
757 : int res;
758 :
759 28 : if (wpa_scan_res_match(wpa_s, i, bss, ssid, 1)) {
760 24 : res = wnm_nei_rep_add_bss(wpa_s, bss, pos, len, pref--);
761 24 : if (res == -2)
762 0 : continue; /* could not build entry for BSS */
763 24 : if (res < 0)
764 0 : break; /* no more room for candidates */
765 24 : if (pref == 1)
766 0 : break;
767 :
768 24 : pos += res;
769 24 : len -= res;
770 : }
771 : }
772 :
773 15 : wpa_hexdump(MSG_DEBUG,
774 : "WNM: BSS Transition Management Response candidate list",
775 15 : buf, pos - buf);
776 :
777 15 : return pos - buf;
778 : }
779 :
780 :
781 54 : static void wnm_send_bss_transition_mgmt_resp(
782 : struct wpa_supplicant *wpa_s, u8 dialog_token,
783 : enum bss_trans_mgmt_status_code status, u8 delay,
784 : const u8 *target_bssid)
785 : {
786 : u8 buf[2000], *pos;
787 : struct ieee80211_mgmt *mgmt;
788 : size_t len;
789 : int res;
790 :
791 378 : wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Response "
792 : "to " MACSTR " dialog_token=%u status=%u delay=%d",
793 324 : MAC2STR(wpa_s->bssid), dialog_token, status, delay);
794 54 : if (!wpa_s->current_bss) {
795 0 : wpa_printf(MSG_DEBUG,
796 : "WNM: Current BSS not known - drop response");
797 54 : return;
798 : }
799 :
800 54 : mgmt = (struct ieee80211_mgmt *) buf;
801 54 : os_memset(&buf, 0, sizeof(buf));
802 54 : os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
803 54 : os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
804 54 : os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
805 54 : mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
806 : WLAN_FC_STYPE_ACTION);
807 54 : mgmt->u.action.category = WLAN_ACTION_WNM;
808 54 : mgmt->u.action.u.bss_tm_resp.action = WNM_BSS_TRANS_MGMT_RESP;
809 54 : mgmt->u.action.u.bss_tm_resp.dialog_token = dialog_token;
810 54 : mgmt->u.action.u.bss_tm_resp.status_code = status;
811 54 : mgmt->u.action.u.bss_tm_resp.bss_termination_delay = delay;
812 54 : pos = mgmt->u.action.u.bss_tm_resp.variable;
813 54 : if (target_bssid) {
814 9 : os_memcpy(pos, target_bssid, ETH_ALEN);
815 9 : pos += ETH_ALEN;
816 45 : } else if (status == WNM_BSS_TM_ACCEPT) {
817 : /*
818 : * P802.11-REVmc clarifies that the Target BSSID field is always
819 : * present when status code is zero, so use a fake value here if
820 : * no BSSID is yet known.
821 : */
822 5 : os_memset(pos, 0, ETH_ALEN);
823 5 : pos += ETH_ALEN;
824 : }
825 :
826 54 : if (status == WNM_BSS_TM_ACCEPT)
827 14 : pos += wnm_add_cand_list(wpa_s, pos, buf + sizeof(buf) - pos);
828 :
829 : #ifdef CONFIG_MBO
830 54 : if (status != WNM_BSS_TM_ACCEPT) {
831 40 : pos += wpas_mbo_ie_bss_trans_reject(
832 40 : wpa_s, pos, buf + sizeof(buf) - pos,
833 : MBO_TRANSITION_REJECT_REASON_UNSPECIFIED);
834 : }
835 : #endif /* CONFIG_MBO */
836 :
837 54 : len = pos - (u8 *) &mgmt->u.action.category;
838 :
839 54 : res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
840 54 : wpa_s->own_addr, wpa_s->bssid,
841 54 : &mgmt->u.action.category, len, 0);
842 54 : if (res < 0) {
843 0 : wpa_printf(MSG_DEBUG,
844 : "WNM: Failed to send BSS Transition Management Response");
845 : }
846 : }
847 :
848 :
849 9 : static void wnm_bss_tm_connect(struct wpa_supplicant *wpa_s,
850 : struct wpa_bss *bss, struct wpa_ssid *ssid,
851 : int after_new_scan)
852 : {
853 9 : wpa_dbg(wpa_s, MSG_DEBUG,
854 : "WNM: Transition to BSS " MACSTR
855 : " based on BSS Transition Management Request (old BSSID "
856 : MACSTR " after_new_scan=%d)",
857 : MAC2STR(bss->bssid), MAC2STR(wpa_s->bssid), after_new_scan);
858 :
859 : /* Send the BSS Management Response - Accept */
860 9 : if (wpa_s->wnm_reply) {
861 9 : wpa_s->wnm_reply = 0;
862 9 : wpa_printf(MSG_DEBUG,
863 : "WNM: Sending successful BSS Transition Management Response");
864 9 : wnm_send_bss_transition_mgmt_resp(wpa_s,
865 9 : wpa_s->wnm_dialog_token,
866 : WNM_BSS_TM_ACCEPT,
867 9 : 0, bss->bssid);
868 : }
869 :
870 9 : if (bss == wpa_s->current_bss) {
871 1 : wpa_printf(MSG_DEBUG,
872 : "WNM: Already associated with the preferred candidate");
873 1 : wnm_deallocate_memory(wpa_s);
874 10 : return;
875 : }
876 :
877 8 : wpa_s->reassociate = 1;
878 8 : wpa_printf(MSG_DEBUG, "WNM: Issuing connect");
879 8 : wpa_supplicant_connect(wpa_s, bss, ssid);
880 8 : wnm_deallocate_memory(wpa_s);
881 : }
882 :
883 :
884 3091 : int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail)
885 : {
886 : struct wpa_bss *bss;
887 3091 : struct wpa_ssid *ssid = wpa_s->current_ssid;
888 3091 : enum bss_trans_mgmt_status_code status = WNM_BSS_TM_REJECT_UNSPECIFIED;
889 :
890 3091 : if (!wpa_s->wnm_neighbor_report_elements)
891 3046 : return 0;
892 :
893 45 : wpa_dbg(wpa_s, MSG_DEBUG,
894 : "WNM: Process scan results for BSS Transition Management");
895 45 : if (os_reltime_before(&wpa_s->wnm_cand_valid_until,
896 : &wpa_s->scan_trigger_time)) {
897 0 : wpa_printf(MSG_DEBUG, "WNM: Previously stored BSS transition candidate list is not valid anymore - drop it");
898 0 : wnm_deallocate_memory(wpa_s);
899 0 : return 0;
900 : }
901 :
902 90 : if (!wpa_s->current_bss ||
903 45 : os_memcmp(wpa_s->wnm_cand_from_bss, wpa_s->current_bss->bssid,
904 : ETH_ALEN) != 0) {
905 0 : wpa_printf(MSG_DEBUG, "WNM: Stored BSS transition candidate list not from the current BSS - ignore it");
906 0 : return 0;
907 : }
908 :
909 : /* Compare the Neighbor Report and scan results */
910 45 : bss = compare_scan_neighbor_results(wpa_s, 0);
911 45 : if (!bss) {
912 44 : wpa_printf(MSG_DEBUG, "WNM: No BSS transition candidate match found");
913 44 : status = WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES;
914 44 : goto send_bss_resp_fail;
915 : }
916 :
917 : /* Associate to the network */
918 1 : wnm_bss_tm_connect(wpa_s, bss, ssid, 1);
919 1 : return 1;
920 :
921 : send_bss_resp_fail:
922 44 : if (!reply_on_fail)
923 22 : return 0;
924 :
925 : /* Send reject response for all the failures */
926 :
927 22 : if (wpa_s->wnm_reply) {
928 22 : wpa_s->wnm_reply = 0;
929 22 : wnm_send_bss_transition_mgmt_resp(wpa_s,
930 22 : wpa_s->wnm_dialog_token,
931 : status, 0, NULL);
932 : }
933 22 : wnm_deallocate_memory(wpa_s);
934 :
935 22 : return 0;
936 : }
937 :
938 :
939 125 : static int cand_pref_compar(const void *a, const void *b)
940 : {
941 125 : const struct neighbor_report *aa = a;
942 125 : const struct neighbor_report *bb = b;
943 :
944 125 : if (!aa->preference_present && !bb->preference_present)
945 95 : return 0;
946 30 : if (!aa->preference_present)
947 2 : return 1;
948 28 : if (!bb->preference_present)
949 20 : return -1;
950 8 : if (bb->preference > aa->preference)
951 1 : return 1;
952 7 : if (bb->preference < aa->preference)
953 7 : return -1;
954 0 : return 0;
955 : }
956 :
957 :
958 31 : static void wnm_sort_cand_list(struct wpa_supplicant *wpa_s)
959 : {
960 31 : if (!wpa_s->wnm_neighbor_report_elements)
961 31 : return;
962 31 : qsort(wpa_s->wnm_neighbor_report_elements,
963 31 : wpa_s->wnm_num_neighbor_report, sizeof(struct neighbor_report),
964 : cand_pref_compar);
965 : }
966 :
967 :
968 31 : static void wnm_dump_cand_list(struct wpa_supplicant *wpa_s)
969 : {
970 : unsigned int i;
971 :
972 31 : wpa_printf(MSG_DEBUG, "WNM: BSS Transition Candidate List");
973 31 : if (!wpa_s->wnm_neighbor_report_elements)
974 31 : return;
975 144 : for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
976 : struct neighbor_report *nei;
977 :
978 113 : nei = &wpa_s->wnm_neighbor_report_elements[i];
979 1267 : wpa_printf(MSG_DEBUG, "%u: " MACSTR
980 : " info=0x%x op_class=%u chan=%u phy=%u pref=%d freq=%d",
981 678 : i, MAC2STR(nei->bssid), nei->bssid_info,
982 113 : nei->regulatory_class,
983 226 : nei->channel_number, nei->phy_type,
984 137 : nei->preference_present ? nei->preference : -1,
985 : nei->freq);
986 : }
987 : }
988 :
989 :
990 42 : static int chan_supported(struct wpa_supplicant *wpa_s, int freq)
991 : {
992 : unsigned int i;
993 :
994 82 : for (i = 0; i < wpa_s->hw.num_modes; i++) {
995 75 : struct hostapd_hw_modes *mode = &wpa_s->hw.modes[i];
996 : int j;
997 :
998 915 : for (j = 0; j < mode->num_channels; j++) {
999 : struct hostapd_channel_data *chan;
1000 :
1001 875 : chan = &mode->channels[j];
1002 911 : if (chan->freq == freq &&
1003 36 : !(chan->flag & HOSTAPD_CHAN_DISABLED))
1004 35 : return 1;
1005 : }
1006 : }
1007 :
1008 7 : return 0;
1009 : }
1010 :
1011 :
1012 23 : static void wnm_set_scan_freqs(struct wpa_supplicant *wpa_s)
1013 : {
1014 : int *freqs;
1015 23 : int num_freqs = 0;
1016 : unsigned int i;
1017 :
1018 23 : if (!wpa_s->wnm_neighbor_report_elements)
1019 9 : return;
1020 :
1021 23 : if (wpa_s->hw.modes == NULL)
1022 0 : return;
1023 :
1024 23 : os_free(wpa_s->next_scan_freqs);
1025 23 : wpa_s->next_scan_freqs = NULL;
1026 :
1027 23 : freqs = os_calloc(wpa_s->wnm_num_neighbor_report + 1, sizeof(int));
1028 23 : if (freqs == NULL)
1029 0 : return;
1030 :
1031 65 : for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
1032 : struct neighbor_report *nei;
1033 :
1034 51 : nei = &wpa_s->wnm_neighbor_report_elements[i];
1035 51 : if (nei->freq <= 0) {
1036 54 : wpa_printf(MSG_DEBUG,
1037 : "WNM: Unknown neighbor operating frequency for "
1038 : MACSTR " - scan all channels",
1039 54 : MAC2STR(nei->bssid));
1040 9 : os_free(freqs);
1041 9 : return;
1042 : }
1043 42 : if (chan_supported(wpa_s, nei->freq))
1044 35 : add_freq(freqs, &num_freqs, nei->freq);
1045 : }
1046 :
1047 14 : if (num_freqs == 0) {
1048 0 : os_free(freqs);
1049 0 : return;
1050 : }
1051 :
1052 14 : wpa_printf(MSG_DEBUG,
1053 : "WNM: Scan %d frequencies based on transition candidate list",
1054 : num_freqs);
1055 14 : wpa_s->next_scan_freqs = freqs;
1056 : }
1057 :
1058 :
1059 31 : static int wnm_fetch_scan_results(struct wpa_supplicant *wpa_s)
1060 : {
1061 : struct wpa_scan_results *scan_res;
1062 : struct wpa_bss *bss;
1063 31 : struct wpa_ssid *ssid = wpa_s->current_ssid;
1064 31 : u8 i, found = 0;
1065 : size_t j;
1066 :
1067 31 : wpa_dbg(wpa_s, MSG_DEBUG,
1068 : "WNM: Fetch current scan results from the driver for checking transition candidates");
1069 31 : scan_res = wpa_drv_get_scan_results2(wpa_s);
1070 31 : if (!scan_res) {
1071 0 : wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Failed to get scan results");
1072 0 : return 0;
1073 : }
1074 :
1075 31 : if (scan_res->fetch_time.sec == 0)
1076 31 : os_get_reltime(&scan_res->fetch_time);
1077 :
1078 31 : filter_scan_res(wpa_s, scan_res);
1079 :
1080 144 : for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
1081 : struct neighbor_report *nei;
1082 :
1083 113 : nei = &wpa_s->wnm_neighbor_report_elements[i];
1084 113 : if (nei->preference_present && nei->preference == 0)
1085 0 : continue;
1086 :
1087 245 : for (j = 0; j < scan_res->num; j++) {
1088 : struct wpa_scan_res *res;
1089 : const u8 *ssid_ie;
1090 :
1091 132 : res = scan_res->res[j];
1092 144 : if (os_memcmp(nei->bssid, res->bssid, ETH_ALEN) != 0 ||
1093 12 : res->age > WNM_SCAN_RESULT_AGE * 1000)
1094 122 : continue;
1095 10 : bss = wpa_s->current_bss;
1096 10 : ssid_ie = wpa_scan_get_ie(res, WLAN_EID_SSID);
1097 20 : if (bss && ssid_ie &&
1098 20 : (bss->ssid_len != ssid_ie[1] ||
1099 10 : os_memcmp(bss->ssid, ssid_ie + 2,
1100 : bss->ssid_len) != 0))
1101 0 : continue;
1102 :
1103 : /* Potential candidate found */
1104 10 : found = 1;
1105 10 : scan_snr(res);
1106 10 : scan_est_throughput(wpa_s, res);
1107 10 : wpa_bss_update_scan_res(wpa_s, res,
1108 : &scan_res->fetch_time);
1109 : }
1110 : }
1111 :
1112 31 : wpa_scan_results_free(scan_res);
1113 31 : if (!found) {
1114 22 : wpa_dbg(wpa_s, MSG_DEBUG,
1115 : "WNM: No transition candidate matches existing scan results");
1116 22 : return 0;
1117 : }
1118 :
1119 9 : bss = compare_scan_neighbor_results(wpa_s, WNM_SCAN_RESULT_AGE);
1120 9 : if (!bss) {
1121 1 : wpa_dbg(wpa_s, MSG_DEBUG,
1122 : "WNM: Comparison of scan results against transition candidates did not find matches");
1123 1 : return 0;
1124 : }
1125 :
1126 : /* Associate to the network */
1127 8 : wnm_bss_tm_connect(wpa_s, bss, ssid, 0);
1128 8 : return 1;
1129 : }
1130 :
1131 :
1132 59 : static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
1133 : const u8 *pos, const u8 *end,
1134 : int reply)
1135 : {
1136 : unsigned int beacon_int;
1137 : u8 valid_int;
1138 : #ifdef CONFIG_MBO
1139 : const u8 *vendor;
1140 : #endif /* CONFIG_MBO */
1141 :
1142 59 : if (end - pos < 5)
1143 1 : return;
1144 :
1145 58 : if (wpa_s->current_bss)
1146 58 : beacon_int = wpa_s->current_bss->beacon_int;
1147 : else
1148 0 : beacon_int = 100; /* best guess */
1149 :
1150 58 : wpa_s->wnm_dialog_token = pos[0];
1151 58 : wpa_s->wnm_mode = pos[1];
1152 58 : wpa_s->wnm_dissoc_timer = WPA_GET_LE16(pos + 2);
1153 58 : valid_int = pos[4];
1154 58 : wpa_s->wnm_reply = reply;
1155 :
1156 232 : wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Request: "
1157 : "dialog_token=%u request_mode=0x%x "
1158 : "disassoc_timer=%u validity_interval=%u",
1159 116 : wpa_s->wnm_dialog_token, wpa_s->wnm_mode,
1160 58 : wpa_s->wnm_dissoc_timer, valid_int);
1161 :
1162 : #if defined(CONFIG_MBO) && defined(CONFIG_TESTING_OPTIONS)
1163 58 : if (wpa_s->reject_btm_req_reason) {
1164 2 : wpa_printf(MSG_INFO,
1165 : "WNM: Testing - reject BSS Transition Management Request: reject_btm_req_reason=%d",
1166 : wpa_s->reject_btm_req_reason);
1167 2 : wnm_send_bss_transition_mgmt_resp(wpa_s,
1168 2 : wpa_s->wnm_dialog_token,
1169 2 : wpa_s->reject_btm_req_reason,
1170 : 0, NULL);
1171 2 : return;
1172 : }
1173 : #endif /* CONFIG_MBO && CONFIG_TESTING_OPTIONS */
1174 :
1175 56 : pos += 5;
1176 :
1177 56 : if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) {
1178 3 : if (end - pos < 12) {
1179 1 : wpa_printf(MSG_DEBUG, "WNM: Too short BSS TM Request");
1180 1 : return;
1181 : }
1182 2 : os_memcpy(wpa_s->wnm_bss_termination_duration, pos, 12);
1183 2 : pos += 12; /* BSS Termination Duration */
1184 : }
1185 :
1186 55 : if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) {
1187 : char url[256];
1188 :
1189 7 : if (end - pos < 1 || 1 + pos[0] > end - pos) {
1190 2 : wpa_printf(MSG_DEBUG, "WNM: Invalid BSS Transition "
1191 : "Management Request (URL)");
1192 2 : return;
1193 : }
1194 5 : os_memcpy(url, pos + 1, pos[0]);
1195 5 : url[pos[0]] = '\0';
1196 5 : pos += 1 + pos[0];
1197 :
1198 5 : wpa_msg(wpa_s, MSG_INFO, ESS_DISASSOC_IMMINENT "%d %u %s",
1199 : wpa_sm_pmf_enabled(wpa_s->wpa),
1200 5 : wpa_s->wnm_dissoc_timer * beacon_int * 128 / 125, url);
1201 : }
1202 :
1203 53 : if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) {
1204 7 : wpa_msg(wpa_s, MSG_INFO, "WNM: Disassociation Imminent - "
1205 7 : "Disassociation Timer %u", wpa_s->wnm_dissoc_timer);
1206 7 : if (wpa_s->wnm_dissoc_timer && !wpa_s->scanning) {
1207 : /* TODO: mark current BSS less preferred for
1208 : * selection */
1209 7 : wpa_printf(MSG_DEBUG, "Trying to find another BSS");
1210 7 : wpa_supplicant_req_scan(wpa_s, 0, 0);
1211 : }
1212 : }
1213 :
1214 : #ifdef CONFIG_MBO
1215 53 : vendor = get_ie(pos, end - pos, WLAN_EID_VENDOR_SPECIFIC);
1216 53 : if (vendor)
1217 8 : wpas_mbo_ie_trans_req(wpa_s, vendor + 2, vendor[1]);
1218 : #endif /* CONFIG_MBO */
1219 :
1220 53 : if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED) {
1221 : unsigned int valid_ms;
1222 :
1223 33 : wpa_msg(wpa_s, MSG_INFO, "WNM: Preferred List Available");
1224 33 : wnm_deallocate_memory(wpa_s);
1225 33 : wpa_s->wnm_neighbor_report_elements = os_calloc(
1226 : WNM_MAX_NEIGHBOR_REPORT,
1227 : sizeof(struct neighbor_report));
1228 33 : if (wpa_s->wnm_neighbor_report_elements == NULL)
1229 0 : return;
1230 :
1231 295 : while (end - pos >= 2 &&
1232 115 : wpa_s->wnm_num_neighbor_report < WNM_MAX_NEIGHBOR_REPORT)
1233 : {
1234 115 : u8 tag = *pos++;
1235 115 : u8 len = *pos++;
1236 :
1237 115 : wpa_printf(MSG_DEBUG, "WNM: Neighbor report tag %u",
1238 : tag);
1239 115 : if (len > end - pos) {
1240 1 : wpa_printf(MSG_DEBUG, "WNM: Truncated request");
1241 1 : return;
1242 : }
1243 114 : if (tag == WLAN_EID_NEIGHBOR_REPORT) {
1244 : struct neighbor_report *rep;
1245 226 : rep = &wpa_s->wnm_neighbor_report_elements[
1246 113 : wpa_s->wnm_num_neighbor_report];
1247 113 : wnm_parse_neighbor_report(wpa_s, pos, len, rep);
1248 113 : wpa_s->wnm_num_neighbor_report++;
1249 : }
1250 :
1251 114 : pos += len;
1252 : }
1253 :
1254 32 : if (!wpa_s->wnm_num_neighbor_report) {
1255 1 : wpa_printf(MSG_DEBUG,
1256 : "WNM: Candidate list included bit is set, but no candidates found");
1257 1 : wnm_send_bss_transition_mgmt_resp(
1258 1 : wpa_s, wpa_s->wnm_dialog_token,
1259 : WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES,
1260 : 0, NULL);
1261 1 : return;
1262 : }
1263 :
1264 31 : wnm_sort_cand_list(wpa_s);
1265 31 : wnm_dump_cand_list(wpa_s);
1266 31 : valid_ms = valid_int * beacon_int * 128 / 125;
1267 31 : wpa_printf(MSG_DEBUG, "WNM: Candidate list valid for %u ms",
1268 : valid_ms);
1269 31 : os_get_reltime(&wpa_s->wnm_cand_valid_until);
1270 31 : wpa_s->wnm_cand_valid_until.sec += valid_ms / 1000;
1271 31 : wpa_s->wnm_cand_valid_until.usec += (valid_ms % 1000) * 1000;
1272 62 : wpa_s->wnm_cand_valid_until.sec +=
1273 31 : wpa_s->wnm_cand_valid_until.usec / 1000000;
1274 31 : wpa_s->wnm_cand_valid_until.usec %= 1000000;
1275 31 : os_memcpy(wpa_s->wnm_cand_from_bss, wpa_s->bssid, ETH_ALEN);
1276 :
1277 : /*
1278 : * Fetch the latest scan results from the kernel and check for
1279 : * candidates based on those results first. This can help in
1280 : * finding more up-to-date information should the driver has
1281 : * done some internal scanning operations after the last scan
1282 : * result update in wpa_supplicant.
1283 : */
1284 31 : if (wnm_fetch_scan_results(wpa_s) > 0)
1285 8 : return;
1286 :
1287 : /*
1288 : * Try to use previously received scan results, if they are
1289 : * recent enough to use for a connection.
1290 : */
1291 23 : if (wpa_s->last_scan_res_used > 0) {
1292 : struct os_reltime now;
1293 :
1294 23 : os_get_reltime(&now);
1295 23 : if (!os_reltime_expired(&now, &wpa_s->last_scan, 10)) {
1296 22 : wpa_printf(MSG_DEBUG,
1297 : "WNM: Try to use recent scan results");
1298 22 : if (wnm_scan_process(wpa_s, 0) > 0)
1299 0 : return;
1300 22 : wpa_printf(MSG_DEBUG,
1301 : "WNM: No match in previous scan results - try a new scan");
1302 : }
1303 : }
1304 :
1305 23 : wnm_set_scan_freqs(wpa_s);
1306 23 : if (wpa_s->wnm_num_neighbor_report == 1) {
1307 9 : os_memcpy(wpa_s->next_scan_bssid,
1308 : wpa_s->wnm_neighbor_report_elements[0].bssid,
1309 : ETH_ALEN);
1310 54 : wpa_printf(MSG_DEBUG,
1311 : "WNM: Scan only for a specific BSSID since there is only a single candidate "
1312 54 : MACSTR, MAC2STR(wpa_s->next_scan_bssid));
1313 : }
1314 23 : wpa_supplicant_req_scan(wpa_s, 0, 0);
1315 20 : } else if (reply) {
1316 : enum bss_trans_mgmt_status_code status;
1317 20 : if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT)
1318 5 : status = WNM_BSS_TM_ACCEPT;
1319 : else {
1320 15 : wpa_msg(wpa_s, MSG_INFO, "WNM: BSS Transition Management Request did not include candidates");
1321 15 : status = WNM_BSS_TM_REJECT_UNSPECIFIED;
1322 : }
1323 20 : wnm_send_bss_transition_mgmt_resp(wpa_s,
1324 20 : wpa_s->wnm_dialog_token,
1325 : status, 0, NULL);
1326 : }
1327 : }
1328 :
1329 :
1330 2 : int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s,
1331 : u8 query_reason, int cand_list)
1332 : {
1333 : u8 buf[2000], *pos;
1334 : struct ieee80211_mgmt *mgmt;
1335 : size_t len;
1336 : int ret;
1337 :
1338 14 : wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Query to "
1339 : MACSTR " query_reason=%u%s",
1340 12 : MAC2STR(wpa_s->bssid), query_reason,
1341 : cand_list ? " candidate list" : "");
1342 :
1343 2 : mgmt = (struct ieee80211_mgmt *) buf;
1344 2 : os_memset(&buf, 0, sizeof(buf));
1345 2 : os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
1346 2 : os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
1347 2 : os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
1348 2 : mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
1349 : WLAN_FC_STYPE_ACTION);
1350 2 : mgmt->u.action.category = WLAN_ACTION_WNM;
1351 2 : mgmt->u.action.u.bss_tm_query.action = WNM_BSS_TRANS_MGMT_QUERY;
1352 2 : mgmt->u.action.u.bss_tm_query.dialog_token = 1;
1353 2 : mgmt->u.action.u.bss_tm_query.query_reason = query_reason;
1354 2 : pos = mgmt->u.action.u.bss_tm_query.variable;
1355 :
1356 2 : if (cand_list)
1357 1 : pos += wnm_add_cand_list(wpa_s, pos, buf + sizeof(buf) - pos);
1358 :
1359 2 : len = pos - (u8 *) &mgmt->u.action.category;
1360 :
1361 2 : ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
1362 2 : wpa_s->own_addr, wpa_s->bssid,
1363 2 : &mgmt->u.action.category, len, 0);
1364 :
1365 2 : return ret;
1366 : }
1367 :
1368 :
1369 17 : static void ieee802_11_rx_wnm_notif_req_wfa(struct wpa_supplicant *wpa_s,
1370 : const u8 *sa, const u8 *data,
1371 : int len)
1372 : {
1373 : const u8 *pos, *end, *next;
1374 : u8 ie, ie_len;
1375 :
1376 17 : pos = data;
1377 17 : end = data + len;
1378 :
1379 44 : while (end - pos > 1) {
1380 17 : ie = *pos++;
1381 17 : ie_len = *pos++;
1382 17 : wpa_printf(MSG_DEBUG, "WNM: WFA subelement %u len %u",
1383 : ie, ie_len);
1384 17 : if (ie_len > end - pos) {
1385 2 : wpa_printf(MSG_DEBUG, "WNM: Not enough room for "
1386 : "subelement");
1387 2 : break;
1388 : }
1389 15 : next = pos + ie_len;
1390 15 : if (ie_len < 4) {
1391 1 : pos = next;
1392 1 : continue;
1393 : }
1394 14 : wpa_printf(MSG_DEBUG, "WNM: Subelement OUI %06x type %u",
1395 14 : WPA_GET_BE24(pos), pos[3]);
1396 :
1397 : #ifdef CONFIG_HS20
1398 27 : if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 5 &&
1399 26 : WPA_GET_BE24(pos) == OUI_WFA &&
1400 13 : pos[3] == HS20_WNM_SUB_REM_NEEDED) {
1401 : /* Subscription Remediation subelement */
1402 : const u8 *ie_end;
1403 : u8 url_len;
1404 : char *url;
1405 : u8 osu_method;
1406 :
1407 7 : wpa_printf(MSG_DEBUG, "WNM: Subscription Remediation "
1408 : "subelement");
1409 7 : ie_end = pos + ie_len;
1410 7 : pos += 4;
1411 7 : url_len = *pos++;
1412 7 : if (url_len == 0) {
1413 1 : wpa_printf(MSG_DEBUG, "WNM: No Server URL included");
1414 1 : url = NULL;
1415 1 : osu_method = 1;
1416 : } else {
1417 6 : if (url_len + 1 > ie_end - pos) {
1418 3 : wpa_printf(MSG_DEBUG, "WNM: Not enough room for Server URL (len=%u) and Server Method (left %d)",
1419 : url_len,
1420 3 : (int) (ie_end - pos));
1421 3 : break;
1422 : }
1423 3 : url = os_malloc(url_len + 1);
1424 3 : if (url == NULL)
1425 0 : break;
1426 3 : os_memcpy(url, pos, url_len);
1427 3 : url[url_len] = '\0';
1428 3 : osu_method = pos[url_len];
1429 : }
1430 4 : hs20_rx_subscription_remediation(wpa_s, url,
1431 : osu_method);
1432 4 : os_free(url);
1433 4 : pos = next;
1434 4 : continue;
1435 : }
1436 :
1437 13 : if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 8 &&
1438 12 : WPA_GET_BE24(pos) == OUI_WFA &&
1439 6 : pos[3] == HS20_WNM_DEAUTH_IMMINENT_NOTICE) {
1440 : const u8 *ie_end;
1441 : u8 url_len;
1442 : char *url;
1443 : u8 code;
1444 : u16 reauth_delay;
1445 :
1446 6 : ie_end = pos + ie_len;
1447 6 : pos += 4;
1448 6 : code = *pos++;
1449 6 : reauth_delay = WPA_GET_LE16(pos);
1450 6 : pos += 2;
1451 6 : url_len = *pos++;
1452 6 : wpa_printf(MSG_DEBUG, "WNM: HS 2.0 Deauthentication "
1453 : "Imminent - Reason Code %u "
1454 : "Re-Auth Delay %u URL Length %u",
1455 : code, reauth_delay, url_len);
1456 6 : if (url_len > ie_end - pos)
1457 2 : break;
1458 4 : url = os_malloc(url_len + 1);
1459 4 : if (url == NULL)
1460 0 : break;
1461 4 : os_memcpy(url, pos, url_len);
1462 4 : url[url_len] = '\0';
1463 4 : hs20_rx_deauth_imminent_notice(wpa_s, code,
1464 : reauth_delay, url);
1465 4 : os_free(url);
1466 4 : pos = next;
1467 4 : continue;
1468 : }
1469 : #endif /* CONFIG_HS20 */
1470 :
1471 1 : pos = next;
1472 : }
1473 17 : }
1474 :
1475 :
1476 19 : static void ieee802_11_rx_wnm_notif_req(struct wpa_supplicant *wpa_s,
1477 : const u8 *sa, const u8 *frm, int len)
1478 : {
1479 : const u8 *pos, *end;
1480 : u8 dialog_token, type;
1481 :
1482 : /* Dialog Token [1] | Type [1] | Subelements */
1483 :
1484 19 : if (len < 2 || sa == NULL)
1485 1 : return;
1486 18 : end = frm + len;
1487 18 : pos = frm;
1488 18 : dialog_token = *pos++;
1489 18 : type = *pos++;
1490 :
1491 18 : wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Received WNM-Notification Request "
1492 : "(dialog_token %u type %u sa " MACSTR ")",
1493 : dialog_token, type, MAC2STR(sa));
1494 18 : wpa_hexdump(MSG_DEBUG, "WNM-Notification Request subelements",
1495 18 : pos, end - pos);
1496 :
1497 36 : if (wpa_s->wpa_state != WPA_COMPLETED ||
1498 18 : os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0) {
1499 0 : wpa_dbg(wpa_s, MSG_DEBUG, "WNM: WNM-Notification frame not "
1500 : "from our AP - ignore it");
1501 0 : return;
1502 : }
1503 :
1504 18 : switch (type) {
1505 : case 1:
1506 17 : ieee802_11_rx_wnm_notif_req_wfa(wpa_s, sa, pos, end - pos);
1507 17 : break;
1508 : default:
1509 1 : wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Ignore unknown "
1510 : "WNM-Notification type %u", type);
1511 1 : break;
1512 : }
1513 : }
1514 :
1515 :
1516 110 : void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s,
1517 : const struct ieee80211_mgmt *mgmt, size_t len)
1518 : {
1519 : const u8 *pos, *end;
1520 : u8 act;
1521 :
1522 110 : if (len < IEEE80211_HDRLEN + 2)
1523 0 : return;
1524 :
1525 110 : pos = ((const u8 *) mgmt) + IEEE80211_HDRLEN + 1;
1526 110 : act = *pos++;
1527 110 : end = ((const u8 *) mgmt) + len;
1528 :
1529 660 : wpa_printf(MSG_DEBUG, "WNM: RX action %u from " MACSTR,
1530 660 : act, MAC2STR(mgmt->sa));
1531 220 : if (wpa_s->wpa_state < WPA_ASSOCIATED ||
1532 110 : os_memcmp(mgmt->sa, wpa_s->bssid, ETH_ALEN) != 0) {
1533 0 : wpa_printf(MSG_DEBUG, "WNM: Ignore unexpected WNM Action "
1534 : "frame");
1535 0 : return;
1536 : }
1537 :
1538 110 : switch (act) {
1539 : case WNM_BSS_TRANS_MGMT_REQ:
1540 59 : ieee802_11_rx_bss_trans_mgmt_req(wpa_s, pos, end,
1541 59 : !(mgmt->da[0] & 0x01));
1542 59 : break;
1543 : case WNM_SLEEP_MODE_RESP:
1544 32 : ieee802_11_rx_wnmsleep_resp(wpa_s, pos, end - pos);
1545 32 : break;
1546 : case WNM_NOTIFICATION_REQ:
1547 19 : ieee802_11_rx_wnm_notif_req(wpa_s, mgmt->sa, pos, end - pos);
1548 19 : break;
1549 : default:
1550 0 : wpa_printf(MSG_ERROR, "WNM: Unknown request");
1551 0 : break;
1552 : }
1553 : }
|