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