Updated through tag hostap_2_5 from git://w1.fi/hostap.git
[mech_eap.git] / libeap / 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
28 /* get the TFS IE from driver */
29 static int ieee80211_11_get_tfs_ie(struct wpa_supplicant *wpa_s, u8 *buf,
30                                    u16 *buf_len, enum wnm_oper oper)
31 {
32         wpa_printf(MSG_DEBUG, "%s: TFS get operation %d", __func__, oper);
33
34         return wpa_drv_wnm_oper(wpa_s, oper, wpa_s->bssid, buf, buf_len);
35 }
36
37
38 /* set the TFS IE to driver */
39 static int ieee80211_11_set_tfs_ie(struct wpa_supplicant *wpa_s,
40                                    const u8 *addr, u8 *buf, u16 *buf_len,
41                                    enum wnm_oper oper)
42 {
43         wpa_printf(MSG_DEBUG, "%s: TFS set operation %d", __func__, oper);
44
45         return wpa_drv_wnm_oper(wpa_s, oper, addr, buf, buf_len);
46 }
47
48
49 /* MLME-SLEEPMODE.request */
50 int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s,
51                                  u8 action, u16 intval, struct wpabuf *tfs_req)
52 {
53         struct ieee80211_mgmt *mgmt;
54         int res;
55         size_t len;
56         struct wnm_sleep_element *wnmsleep_ie;
57         u8 *wnmtfs_ie;
58         u8 wnmsleep_ie_len;
59         u16 wnmtfs_ie_len;  /* possibly multiple IE(s) */
60         enum wnm_oper tfs_oper = action == 0 ? WNM_SLEEP_TFS_REQ_IE_ADD :
61                 WNM_SLEEP_TFS_REQ_IE_NONE;
62
63         wpa_printf(MSG_DEBUG, "WNM: Request to send WNM-Sleep Mode Request "
64                    "action=%s to " MACSTR,
65                    action == 0 ? "enter" : "exit",
66                    MAC2STR(wpa_s->bssid));
67
68         /* WNM-Sleep Mode IE */
69         wnmsleep_ie_len = sizeof(struct wnm_sleep_element);
70         wnmsleep_ie = os_zalloc(sizeof(struct wnm_sleep_element));
71         if (wnmsleep_ie == NULL)
72                 return -1;
73         wnmsleep_ie->eid = WLAN_EID_WNMSLEEP;
74         wnmsleep_ie->len = wnmsleep_ie_len - 2;
75         wnmsleep_ie->action_type = action;
76         wnmsleep_ie->status = WNM_STATUS_SLEEP_ACCEPT;
77         wnmsleep_ie->intval = host_to_le16(intval);
78         wpa_hexdump(MSG_DEBUG, "WNM: WNM-Sleep Mode element",
79                     (u8 *) wnmsleep_ie, wnmsleep_ie_len);
80
81         /* TFS IE(s) */
82         if (tfs_req) {
83                 wnmtfs_ie_len = wpabuf_len(tfs_req);
84                 wnmtfs_ie = os_malloc(wnmtfs_ie_len);
85                 if (wnmtfs_ie == NULL) {
86                         os_free(wnmsleep_ie);
87                         return -1;
88                 }
89                 os_memcpy(wnmtfs_ie, wpabuf_head(tfs_req), wnmtfs_ie_len);
90         } else {
91                 wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN);
92                 if (wnmtfs_ie == NULL) {
93                         os_free(wnmsleep_ie);
94                         return -1;
95                 }
96                 if (ieee80211_11_get_tfs_ie(wpa_s, wnmtfs_ie, &wnmtfs_ie_len,
97                                             tfs_oper)) {
98                         wnmtfs_ie_len = 0;
99                         os_free(wnmtfs_ie);
100                         wnmtfs_ie = NULL;
101                 }
102         }
103         wpa_hexdump(MSG_DEBUG, "WNM: TFS Request element",
104                     (u8 *) wnmtfs_ie, wnmtfs_ie_len);
105
106         mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len + wnmtfs_ie_len);
107         if (mgmt == NULL) {
108                 wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for "
109                            "WNM-Sleep Request action frame");
110                 os_free(wnmsleep_ie);
111                 os_free(wnmtfs_ie);
112                 return -1;
113         }
114
115         os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
116         os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
117         os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
118         mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
119                                            WLAN_FC_STYPE_ACTION);
120         mgmt->u.action.category = WLAN_ACTION_WNM;
121         mgmt->u.action.u.wnm_sleep_req.action = WNM_SLEEP_MODE_REQ;
122         mgmt->u.action.u.wnm_sleep_req.dialogtoken = 1;
123         os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable, wnmsleep_ie,
124                   wnmsleep_ie_len);
125         /* copy TFS IE here */
126         if (wnmtfs_ie_len > 0) {
127                 os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable +
128                           wnmsleep_ie_len, wnmtfs_ie, wnmtfs_ie_len);
129         }
130
131         len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_req) + wnmsleep_ie_len +
132                 wnmtfs_ie_len;
133
134         res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
135                                   wpa_s->own_addr, wpa_s->bssid,
136                                   &mgmt->u.action.category, len, 0);
137         if (res < 0)
138                 wpa_printf(MSG_DEBUG, "Failed to send WNM-Sleep Request "
139                            "(action=%d, intval=%d)", action, intval);
140
141         os_free(wnmsleep_ie);
142         os_free(wnmtfs_ie);
143         os_free(mgmt);
144
145         return res;
146 }
147
148
149 static void wnm_sleep_mode_enter_success(struct wpa_supplicant *wpa_s,
150                                          u8 *tfsresp_ie_start,
151                                          u8 *tfsresp_ie_end)
152 {
153         wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_CONFIRM,
154                          wpa_s->bssid, NULL, NULL);
155         /* remove GTK/IGTK ?? */
156
157         /* set the TFS Resp IE(s) */
158         if (tfsresp_ie_start && tfsresp_ie_end &&
159             tfsresp_ie_end - tfsresp_ie_start >= 0) {
160                 u16 tfsresp_ie_len;
161                 tfsresp_ie_len = (tfsresp_ie_end + tfsresp_ie_end[1] + 2) -
162                         tfsresp_ie_start;
163                 wpa_printf(MSG_DEBUG, "TFS Resp IE(s) found");
164                 /* pass the TFS Resp IE(s) to driver for processing */
165                 if (ieee80211_11_set_tfs_ie(wpa_s, wpa_s->bssid,
166                                             tfsresp_ie_start,
167                                             &tfsresp_ie_len,
168                                             WNM_SLEEP_TFS_RESP_IE_SET))
169                         wpa_printf(MSG_DEBUG, "WNM: Fail to set TFS Resp IE");
170         }
171 }
172
173
174 static void wnm_sleep_mode_exit_success(struct wpa_supplicant *wpa_s,
175                                         const u8 *frm, u16 key_len_total)
176 {
177         u8 *ptr, *end;
178         u8 gtk_len;
179
180         wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_EXIT_CONFIRM,  wpa_s->bssid,
181                          NULL, NULL);
182
183         /* Install GTK/IGTK */
184
185         /* point to key data field */
186         ptr = (u8 *) frm + 1 + 2;
187         end = ptr + key_len_total;
188         wpa_hexdump_key(MSG_DEBUG, "WNM: Key Data", ptr, key_len_total);
189
190         while (ptr + 1 < end) {
191                 if (ptr + 2 + ptr[1] > end) {
192                         wpa_printf(MSG_DEBUG, "WNM: Invalid Key Data element "
193                                    "length");
194                         if (end > ptr) {
195                                 wpa_hexdump(MSG_DEBUG, "WNM: Remaining data",
196                                             ptr, end - ptr);
197                         }
198                         break;
199                 }
200                 if (*ptr == WNM_SLEEP_SUBELEM_GTK) {
201                         if (ptr[1] < 11 + 5) {
202                                 wpa_printf(MSG_DEBUG, "WNM: Too short GTK "
203                                            "subelem");
204                                 break;
205                         }
206                         gtk_len = *(ptr + 4);
207                         if (ptr[1] < 11 + gtk_len ||
208                             gtk_len < 5 || gtk_len > 32) {
209                                 wpa_printf(MSG_DEBUG, "WNM: Invalid GTK "
210                                            "subelem");
211                                 break;
212                         }
213                         wpa_wnmsleep_install_key(
214                                 wpa_s->wpa,
215                                 WNM_SLEEP_SUBELEM_GTK,
216                                 ptr);
217                         ptr += 13 + gtk_len;
218 #ifdef CONFIG_IEEE80211W
219                 } else if (*ptr == WNM_SLEEP_SUBELEM_IGTK) {
220                         if (ptr[1] < 2 + 6 + WPA_IGTK_LEN) {
221                                 wpa_printf(MSG_DEBUG, "WNM: Too short IGTK "
222                                            "subelem");
223                                 break;
224                         }
225                         wpa_wnmsleep_install_key(wpa_s->wpa,
226                                                  WNM_SLEEP_SUBELEM_IGTK, ptr);
227                         ptr += 10 + WPA_IGTK_LEN;
228 #endif /* CONFIG_IEEE80211W */
229                 } else
230                         break; /* skip the loop */
231         }
232 }
233
234
235 static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s,
236                                         const u8 *frm, int len)
237 {
238         /*
239          * Action [1] | Dialog Token [1] | Key Data Len [2] | Key Data |
240          * WNM-Sleep Mode IE | TFS Response IE
241          */
242         u8 *pos = (u8 *) frm; /* point to payload after the action field */
243         u16 key_len_total;
244         struct wnm_sleep_element *wnmsleep_ie = NULL;
245         /* multiple TFS Resp IE (assuming consecutive) */
246         u8 *tfsresp_ie_start = NULL;
247         u8 *tfsresp_ie_end = NULL;
248         size_t left;
249
250         if (len < 3)
251                 return;
252         key_len_total = WPA_GET_LE16(frm + 1);
253
254         wpa_printf(MSG_DEBUG, "WNM-Sleep Mode Response token=%u key_len_total=%d",
255                    frm[0], key_len_total);
256         left = len - 3;
257         if (key_len_total > left) {
258                 wpa_printf(MSG_INFO, "WNM: Too short frame for Key Data field");
259                 return;
260         }
261         pos += 3 + key_len_total;
262         while (pos - frm < len) {
263                 u8 ie_len = *(pos + 1);
264                 if (pos + 2 + ie_len > frm + len) {
265                         wpa_printf(MSG_INFO, "WNM: Invalid IE len %u", ie_len);
266                         break;
267                 }
268                 wpa_hexdump(MSG_DEBUG, "WNM: Element", pos, 2 + ie_len);
269                 if (*pos == WLAN_EID_WNMSLEEP)
270                         wnmsleep_ie = (struct wnm_sleep_element *) pos;
271                 else if (*pos == WLAN_EID_TFS_RESP) {
272                         if (!tfsresp_ie_start)
273                                 tfsresp_ie_start = pos;
274                         tfsresp_ie_end = pos;
275                 } else
276                         wpa_printf(MSG_DEBUG, "EID %d not recognized", *pos);
277                 pos += ie_len + 2;
278         }
279
280         if (!wnmsleep_ie) {
281                 wpa_printf(MSG_DEBUG, "No WNM-Sleep IE found");
282                 return;
283         }
284
285         if (wnmsleep_ie->status == WNM_STATUS_SLEEP_ACCEPT ||
286             wnmsleep_ie->status == WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE) {
287                 wpa_printf(MSG_DEBUG, "Successfully recv WNM-Sleep Response "
288                            "frame (action=%d, intval=%d)",
289                            wnmsleep_ie->action_type, wnmsleep_ie->intval);
290                 if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER) {
291                         wnm_sleep_mode_enter_success(wpa_s, tfsresp_ie_start,
292                                                      tfsresp_ie_end);
293                 } else if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) {
294                         wnm_sleep_mode_exit_success(wpa_s, frm, key_len_total);
295                 }
296         } else {
297                 wpa_printf(MSG_DEBUG, "Reject recv WNM-Sleep Response frame "
298                            "(action=%d, intval=%d)",
299                            wnmsleep_ie->action_type, wnmsleep_ie->intval);
300                 if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER)
301                         wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_FAIL,
302                                          wpa_s->bssid, NULL, NULL);
303                 else if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT)
304                         wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_EXIT_FAIL,
305                                          wpa_s->bssid, NULL, NULL);
306         }
307 }
308
309
310 void wnm_deallocate_memory(struct wpa_supplicant *wpa_s)
311 {
312         int i;
313
314         for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
315                 os_free(wpa_s->wnm_neighbor_report_elements[i].meas_pilot);
316                 os_free(wpa_s->wnm_neighbor_report_elements[i].mul_bssid);
317         }
318
319         wpa_s->wnm_num_neighbor_report = 0;
320         os_free(wpa_s->wnm_neighbor_report_elements);
321         wpa_s->wnm_neighbor_report_elements = NULL;
322 }
323
324
325 static void wnm_parse_neighbor_report_elem(struct neighbor_report *rep,
326                                            u8 id, u8 elen, const u8 *pos)
327 {
328         switch (id) {
329         case WNM_NEIGHBOR_TSF:
330                 if (elen < 2 + 2) {
331                         wpa_printf(MSG_DEBUG, "WNM: Too short TSF");
332                         break;
333                 }
334                 rep->tsf_offset = WPA_GET_LE16(pos);
335                 rep->beacon_int = WPA_GET_LE16(pos + 2);
336                 rep->tsf_present = 1;
337                 break;
338         case WNM_NEIGHBOR_CONDENSED_COUNTRY_STRING:
339                 if (elen < 2) {
340                         wpa_printf(MSG_DEBUG, "WNM: Too short condensed "
341                                    "country string");
342                         break;
343                 }
344                 os_memcpy(rep->country, pos, 2);
345                 rep->country_present = 1;
346                 break;
347         case WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE:
348                 if (elen < 1) {
349                         wpa_printf(MSG_DEBUG, "WNM: Too short BSS transition "
350                                    "candidate");
351                         break;
352                 }
353                 rep->preference = pos[0];
354                 rep->preference_present = 1;
355                 break;
356         case WNM_NEIGHBOR_BSS_TERMINATION_DURATION:
357                 rep->bss_term_tsf = WPA_GET_LE64(pos);
358                 rep->bss_term_dur = WPA_GET_LE16(pos + 8);
359                 rep->bss_term_present = 1;
360                 break;
361         case WNM_NEIGHBOR_BEARING:
362                 if (elen < 8) {
363                         wpa_printf(MSG_DEBUG, "WNM: Too short neighbor "
364                                    "bearing");
365                         break;
366                 }
367                 rep->bearing = WPA_GET_LE16(pos);
368                 rep->distance = WPA_GET_LE32(pos + 2);
369                 rep->rel_height = WPA_GET_LE16(pos + 2 + 4);
370                 rep->bearing_present = 1;
371                 break;
372         case WNM_NEIGHBOR_MEASUREMENT_PILOT:
373                 if (elen < 1) {
374                         wpa_printf(MSG_DEBUG, "WNM: Too short measurement "
375                                    "pilot");
376                         break;
377                 }
378                 os_free(rep->meas_pilot);
379                 rep->meas_pilot = os_zalloc(sizeof(struct measurement_pilot));
380                 if (rep->meas_pilot == NULL)
381                         break;
382                 rep->meas_pilot->measurement_pilot = pos[0];
383                 rep->meas_pilot->subelem_len = elen - 1;
384                 os_memcpy(rep->meas_pilot->subelems, pos + 1, elen - 1);
385                 break;
386         case WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES:
387                 if (elen < 5) {
388                         wpa_printf(MSG_DEBUG, "WNM: Too short RRM enabled "
389                                    "capabilities");
390                         break;
391                 }
392                 os_memcpy(rep->rm_capab, pos, 5);
393                 rep->rm_capab_present = 1;
394                 break;
395         case WNM_NEIGHBOR_MULTIPLE_BSSID:
396                 if (elen < 1) {
397                         wpa_printf(MSG_DEBUG, "WNM: Too short multiple BSSID");
398                         break;
399                 }
400                 os_free(rep->mul_bssid);
401                 rep->mul_bssid = os_zalloc(sizeof(struct multiple_bssid));
402                 if (rep->mul_bssid == NULL)
403                         break;
404                 rep->mul_bssid->max_bssid_indicator = pos[0];
405                 rep->mul_bssid->subelem_len = elen - 1;
406                 os_memcpy(rep->mul_bssid->subelems, pos + 1, elen - 1);
407                 break;
408         }
409 }
410
411
412 static int wnm_nei_get_chan(struct wpa_supplicant *wpa_s, u8 op_class, u8 chan)
413 {
414         struct wpa_bss *bss = wpa_s->current_bss;
415         const char *country = NULL;
416
417         if (bss) {
418                 const u8 *elem = wpa_bss_get_ie(bss, WLAN_EID_COUNTRY);
419
420                 if (elem && elem[1] >= 2)
421                         country = (const char *) (elem + 2);
422         }
423
424         return ieee80211_chan_to_freq(country, op_class, chan);
425 }
426
427
428 static void wnm_parse_neighbor_report(struct wpa_supplicant *wpa_s,
429                                       const u8 *pos, u8 len,
430                                       struct neighbor_report *rep)
431 {
432         u8 left = len;
433
434         if (left < 13) {
435                 wpa_printf(MSG_DEBUG, "WNM: Too short neighbor report");
436                 return;
437         }
438
439         os_memcpy(rep->bssid, pos, ETH_ALEN);
440         rep->bssid_info = WPA_GET_LE32(pos + ETH_ALEN);
441         rep->regulatory_class = *(pos + 10);
442         rep->channel_number = *(pos + 11);
443         rep->phy_type = *(pos + 12);
444
445         pos += 13;
446         left -= 13;
447
448         while (left >= 2) {
449                 u8 id, elen;
450
451                 id = *pos++;
452                 elen = *pos++;
453                 wpa_printf(MSG_DEBUG, "WNM: Subelement id=%u len=%u", id, elen);
454                 left -= 2;
455                 if (elen > left) {
456                         wpa_printf(MSG_DEBUG,
457                                    "WNM: Truncated neighbor report subelement");
458                         break;
459                 }
460                 wnm_parse_neighbor_report_elem(rep, id, elen, pos);
461                 left -= elen;
462                 pos += elen;
463         }
464
465         rep->freq = wnm_nei_get_chan(wpa_s, rep->regulatory_class,
466                                      rep->channel_number);
467 }
468
469
470 static struct wpa_bss *
471 compare_scan_neighbor_results(struct wpa_supplicant *wpa_s)
472 {
473
474         u8 i;
475         struct wpa_bss *bss = wpa_s->current_bss;
476         struct wpa_bss *target;
477
478         if (!bss)
479                 return 0;
480
481         wpa_printf(MSG_DEBUG, "WNM: Current BSS " MACSTR " RSSI %d",
482                    MAC2STR(wpa_s->bssid), bss->level);
483
484         for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
485                 struct neighbor_report *nei;
486
487                 nei = &wpa_s->wnm_neighbor_report_elements[i];
488                 if (nei->preference_present && nei->preference == 0) {
489                         wpa_printf(MSG_DEBUG, "Skip excluded BSS " MACSTR,
490                                    MAC2STR(nei->bssid));
491                         continue;
492                 }
493
494                 target = wpa_bss_get_bssid(wpa_s, nei->bssid);
495                 if (!target) {
496                         wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
497                                    " (pref %d) not found in scan results",
498                                    MAC2STR(nei->bssid),
499                                    nei->preference_present ? nei->preference :
500                                    -1);
501                         continue;
502                 }
503
504                 if (bss->ssid_len != target->ssid_len ||
505                     os_memcmp(bss->ssid, target->ssid, bss->ssid_len) != 0) {
506                         /*
507                          * TODO: Could consider allowing transition to another
508                          * ESS if PMF was enabled for the association.
509                          */
510                         wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
511                                    " (pref %d) in different ESS",
512                                    MAC2STR(nei->bssid),
513                                    nei->preference_present ? nei->preference :
514                                    -1);
515                         continue;
516                 }
517
518                 if (target->level < bss->level && target->level < -80) {
519                         wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
520                                    " (pref %d) does not have sufficient signal level (%d)",
521                                    MAC2STR(nei->bssid),
522                                    nei->preference_present ? nei->preference :
523                                    -1,
524                                    target->level);
525                         continue;
526                 }
527
528                 wpa_printf(MSG_DEBUG,
529                            "WNM: Found an acceptable preferred transition candidate BSS "
530                            MACSTR " (RSSI %d)",
531                            MAC2STR(nei->bssid), target->level);
532                 return target;
533         }
534
535         return NULL;
536 }
537
538
539 static void wnm_send_bss_transition_mgmt_resp(
540         struct wpa_supplicant *wpa_s, u8 dialog_token,
541         enum bss_trans_mgmt_status_code status, u8 delay,
542         const u8 *target_bssid)
543 {
544         u8 buf[1000], *pos;
545         struct ieee80211_mgmt *mgmt;
546         size_t len;
547         int res;
548
549         wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Response "
550                    "to " MACSTR " dialog_token=%u status=%u delay=%d",
551                    MAC2STR(wpa_s->bssid), dialog_token, status, delay);
552         if (!wpa_s->current_bss) {
553                 wpa_printf(MSG_DEBUG,
554                            "WNM: Current BSS not known - drop response");
555                 return;
556         }
557
558         mgmt = (struct ieee80211_mgmt *) buf;
559         os_memset(&buf, 0, sizeof(buf));
560         os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
561         os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
562         os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
563         mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
564                                            WLAN_FC_STYPE_ACTION);
565         mgmt->u.action.category = WLAN_ACTION_WNM;
566         mgmt->u.action.u.bss_tm_resp.action = WNM_BSS_TRANS_MGMT_RESP;
567         mgmt->u.action.u.bss_tm_resp.dialog_token = dialog_token;
568         mgmt->u.action.u.bss_tm_resp.status_code = status;
569         mgmt->u.action.u.bss_tm_resp.bss_termination_delay = delay;
570         pos = mgmt->u.action.u.bss_tm_resp.variable;
571         if (target_bssid) {
572                 os_memcpy(pos, target_bssid, ETH_ALEN);
573                 pos += ETH_ALEN;
574         } else if (status == WNM_BSS_TM_ACCEPT) {
575                 /*
576                  * P802.11-REVmc clarifies that the Target BSSID field is always
577                  * present when status code is zero, so use a fake value here if
578                  * no BSSID is yet known.
579                  */
580                 os_memset(pos, 0, ETH_ALEN);
581                 pos += ETH_ALEN;
582         }
583
584         len = pos - (u8 *) &mgmt->u.action.category;
585
586         res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
587                                   wpa_s->own_addr, wpa_s->bssid,
588                                   &mgmt->u.action.category, len, 0);
589         if (res < 0) {
590                 wpa_printf(MSG_DEBUG,
591                            "WNM: Failed to send BSS Transition Management Response");
592         }
593 }
594
595
596 int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail)
597 {
598         struct wpa_bss *bss;
599         struct wpa_ssid *ssid = wpa_s->current_ssid;
600         enum bss_trans_mgmt_status_code status = WNM_BSS_TM_REJECT_UNSPECIFIED;
601
602         if (!wpa_s->wnm_neighbor_report_elements)
603                 return 0;
604
605         if (os_reltime_before(&wpa_s->wnm_cand_valid_until,
606                               &wpa_s->scan_trigger_time)) {
607                 wpa_printf(MSG_DEBUG, "WNM: Previously stored BSS transition candidate list is not valid anymore - drop it");
608                 wnm_deallocate_memory(wpa_s);
609                 return 0;
610         }
611
612         if (!wpa_s->current_bss ||
613             os_memcmp(wpa_s->wnm_cand_from_bss, wpa_s->current_bss->bssid,
614                       ETH_ALEN) != 0) {
615                 wpa_printf(MSG_DEBUG, "WNM: Stored BSS transition candidate list not from the current BSS - ignore it");
616                 return 0;
617         }
618
619         /* Compare the Neighbor Report and scan results */
620         bss = compare_scan_neighbor_results(wpa_s);
621         if (!bss) {
622                 wpa_printf(MSG_DEBUG, "WNM: No BSS transition candidate match found");
623                 status = WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES;
624                 goto send_bss_resp_fail;
625         }
626
627         /* Associate to the network */
628         /* Send the BSS Management Response - Accept */
629         if (wpa_s->wnm_reply) {
630                 wpa_s->wnm_reply = 0;
631                 wnm_send_bss_transition_mgmt_resp(wpa_s,
632                                                   wpa_s->wnm_dialog_token,
633                                                   WNM_BSS_TM_ACCEPT,
634                                                   0, bss->bssid);
635         }
636
637         if (bss == wpa_s->current_bss) {
638                 wpa_printf(MSG_DEBUG,
639                            "WNM: Already associated with the preferred candidate");
640                 return 1;
641         }
642
643         wpa_s->reassociate = 1;
644         wpa_supplicant_connect(wpa_s, bss, ssid);
645         wnm_deallocate_memory(wpa_s);
646         return 1;
647
648 send_bss_resp_fail:
649         if (!reply_on_fail)
650                 return 0;
651
652         /* Send reject response for all the failures */
653
654         if (wpa_s->wnm_reply) {
655                 wpa_s->wnm_reply = 0;
656                 wnm_send_bss_transition_mgmt_resp(wpa_s,
657                                                   wpa_s->wnm_dialog_token,
658                                                   status, 0, NULL);
659         }
660         wnm_deallocate_memory(wpa_s);
661
662         return 0;
663 }
664
665
666 static int cand_pref_compar(const void *a, const void *b)
667 {
668         const struct neighbor_report *aa = a;
669         const struct neighbor_report *bb = b;
670
671         if (!aa->preference_present && !bb->preference_present)
672                 return 0;
673         if (!aa->preference_present)
674                 return 1;
675         if (!bb->preference_present)
676                 return -1;
677         if (bb->preference > aa->preference)
678                 return 1;
679         if (bb->preference < aa->preference)
680                 return -1;
681         return 0;
682 }
683
684
685 static void wnm_sort_cand_list(struct wpa_supplicant *wpa_s)
686 {
687         if (!wpa_s->wnm_neighbor_report_elements)
688                 return;
689         qsort(wpa_s->wnm_neighbor_report_elements,
690               wpa_s->wnm_num_neighbor_report, sizeof(struct neighbor_report),
691               cand_pref_compar);
692 }
693
694
695 static void wnm_dump_cand_list(struct wpa_supplicant *wpa_s)
696 {
697         unsigned int i;
698
699         wpa_printf(MSG_DEBUG, "WNM: BSS Transition Candidate List");
700         if (!wpa_s->wnm_neighbor_report_elements)
701                 return;
702         for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
703                 struct neighbor_report *nei;
704
705                 nei = &wpa_s->wnm_neighbor_report_elements[i];
706                 wpa_printf(MSG_DEBUG, "%u: " MACSTR
707                            " info=0x%x op_class=%u chan=%u phy=%u pref=%d freq=%d",
708                            i, MAC2STR(nei->bssid), nei->bssid_info,
709                            nei->regulatory_class,
710                            nei->channel_number, nei->phy_type,
711                            nei->preference_present ? nei->preference : -1,
712                            nei->freq);
713         }
714 }
715
716
717 static int chan_supported(struct wpa_supplicant *wpa_s, int freq)
718 {
719         unsigned int i;
720
721         for (i = 0; i < wpa_s->hw.num_modes; i++) {
722                 struct hostapd_hw_modes *mode = &wpa_s->hw.modes[i];
723                 int j;
724
725                 for (j = 0; j < mode->num_channels; j++) {
726                         struct hostapd_channel_data *chan;
727
728                         chan = &mode->channels[j];
729                         if (chan->freq == freq &&
730                             !(chan->flag & HOSTAPD_CHAN_DISABLED))
731                                 return 1;
732                 }
733         }
734
735         return 0;
736 }
737
738
739 static void wnm_set_scan_freqs(struct wpa_supplicant *wpa_s)
740 {
741         int *freqs;
742         int num_freqs = 0;
743         unsigned int i;
744
745         if (!wpa_s->wnm_neighbor_report_elements)
746                 return;
747
748         if (wpa_s->hw.modes == NULL)
749                 return;
750
751         os_free(wpa_s->next_scan_freqs);
752         wpa_s->next_scan_freqs = NULL;
753
754         freqs = os_calloc(wpa_s->wnm_num_neighbor_report + 1, sizeof(int));
755         if (freqs == NULL)
756                 return;
757
758         for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
759                 struct neighbor_report *nei;
760
761                 nei = &wpa_s->wnm_neighbor_report_elements[i];
762                 if (nei->freq <= 0) {
763                         wpa_printf(MSG_DEBUG,
764                                    "WNM: Unknown neighbor operating frequency for "
765                                    MACSTR " - scan all channels",
766                                    MAC2STR(nei->bssid));
767                         os_free(freqs);
768                         return;
769                 }
770                 if (chan_supported(wpa_s, nei->freq))
771                         add_freq(freqs, &num_freqs, nei->freq);
772         }
773
774         if (num_freqs == 0) {
775                 os_free(freqs);
776                 return;
777         }
778
779         wpa_printf(MSG_DEBUG,
780                    "WNM: Scan %d frequencies based on transition candidate list",
781                    num_freqs);
782         wpa_s->next_scan_freqs = freqs;
783 }
784
785
786 static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
787                                              const u8 *pos, const u8 *end,
788                                              int reply)
789 {
790         unsigned int beacon_int;
791         u8 valid_int;
792
793         if (pos + 5 > end)
794                 return;
795
796         if (wpa_s->current_bss)
797                 beacon_int = wpa_s->current_bss->beacon_int;
798         else
799                 beacon_int = 100; /* best guess */
800
801         wpa_s->wnm_dialog_token = pos[0];
802         wpa_s->wnm_mode = pos[1];
803         wpa_s->wnm_dissoc_timer = WPA_GET_LE16(pos + 2);
804         valid_int = pos[4];
805         wpa_s->wnm_reply = reply;
806
807         wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Request: "
808                    "dialog_token=%u request_mode=0x%x "
809                    "disassoc_timer=%u validity_interval=%u",
810                    wpa_s->wnm_dialog_token, wpa_s->wnm_mode,
811                    wpa_s->wnm_dissoc_timer, valid_int);
812
813         pos += 5;
814
815         if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) {
816                 if (pos + 12 > end) {
817                         wpa_printf(MSG_DEBUG, "WNM: Too short BSS TM Request");
818                         return;
819                 }
820                 os_memcpy(wpa_s->wnm_bss_termination_duration, pos, 12);
821                 pos += 12; /* BSS Termination Duration */
822         }
823
824         if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) {
825                 char url[256];
826
827                 if (pos + 1 > end || pos + 1 + pos[0] > end) {
828                         wpa_printf(MSG_DEBUG, "WNM: Invalid BSS Transition "
829                                    "Management Request (URL)");
830                         return;
831                 }
832                 os_memcpy(url, pos + 1, pos[0]);
833                 url[pos[0]] = '\0';
834                 pos += 1 + pos[0];
835
836                 wpa_msg(wpa_s, MSG_INFO, ESS_DISASSOC_IMMINENT "%d %u %s",
837                         wpa_sm_pmf_enabled(wpa_s->wpa),
838                         wpa_s->wnm_dissoc_timer * beacon_int * 128 / 125, url);
839         }
840
841         if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) {
842                 wpa_msg(wpa_s, MSG_INFO, "WNM: Disassociation Imminent - "
843                         "Disassociation Timer %u", wpa_s->wnm_dissoc_timer);
844                 if (wpa_s->wnm_dissoc_timer && !wpa_s->scanning) {
845                         /* TODO: mark current BSS less preferred for
846                          * selection */
847                         wpa_printf(MSG_DEBUG, "Trying to find another BSS");
848                         wpa_supplicant_req_scan(wpa_s, 0, 0);
849                 }
850         }
851
852         if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED) {
853                 unsigned int valid_ms;
854
855                 wpa_msg(wpa_s, MSG_INFO, "WNM: Preferred List Available");
856                 wnm_deallocate_memory(wpa_s);
857                 wpa_s->wnm_neighbor_report_elements = os_calloc(
858                         WNM_MAX_NEIGHBOR_REPORT,
859                         sizeof(struct neighbor_report));
860                 if (wpa_s->wnm_neighbor_report_elements == NULL)
861                         return;
862
863                 while (pos + 2 <= end &&
864                        wpa_s->wnm_num_neighbor_report < WNM_MAX_NEIGHBOR_REPORT)
865                 {
866                         u8 tag = *pos++;
867                         u8 len = *pos++;
868
869                         wpa_printf(MSG_DEBUG, "WNM: Neighbor report tag %u",
870                                    tag);
871                         if (pos + len > end) {
872                                 wpa_printf(MSG_DEBUG, "WNM: Truncated request");
873                                 return;
874                         }
875                         if (tag == WLAN_EID_NEIGHBOR_REPORT) {
876                                 struct neighbor_report *rep;
877                                 rep = &wpa_s->wnm_neighbor_report_elements[
878                                         wpa_s->wnm_num_neighbor_report];
879                                 wnm_parse_neighbor_report(wpa_s, pos, len, rep);
880                         }
881
882                         pos += len;
883                         wpa_s->wnm_num_neighbor_report++;
884                 }
885                 wnm_sort_cand_list(wpa_s);
886                 wnm_dump_cand_list(wpa_s);
887                 valid_ms = valid_int * beacon_int * 128 / 125;
888                 wpa_printf(MSG_DEBUG, "WNM: Candidate list valid for %u ms",
889                            valid_ms);
890                 os_get_reltime(&wpa_s->wnm_cand_valid_until);
891                 wpa_s->wnm_cand_valid_until.sec += valid_ms / 1000;
892                 wpa_s->wnm_cand_valid_until.usec += (valid_ms % 1000) * 1000;
893                 wpa_s->wnm_cand_valid_until.sec +=
894                         wpa_s->wnm_cand_valid_until.usec / 1000000;
895                 wpa_s->wnm_cand_valid_until.usec %= 1000000;
896                 os_memcpy(wpa_s->wnm_cand_from_bss, wpa_s->bssid, ETH_ALEN);
897
898                 if (wpa_s->last_scan_res_used > 0) {
899                         struct os_reltime now;
900
901                         os_get_reltime(&now);
902                         if (!os_reltime_expired(&now, &wpa_s->last_scan, 10)) {
903                                 wpa_printf(MSG_DEBUG,
904                                            "WNM: Try to use recent scan results");
905                                 if (wnm_scan_process(wpa_s, 0) > 0)
906                                         return;
907                                 wpa_printf(MSG_DEBUG,
908                                            "WNM: No match in previous scan results - try a new scan");
909                         }
910                 }
911
912                 wnm_set_scan_freqs(wpa_s);
913                 wpa_supplicant_req_scan(wpa_s, 0, 0);
914         } else if (reply) {
915                 enum bss_trans_mgmt_status_code status;
916                 if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT)
917                         status = WNM_BSS_TM_ACCEPT;
918                 else {
919                         wpa_msg(wpa_s, MSG_INFO, "WNM: BSS Transition Management Request did not include candidates");
920                         status = WNM_BSS_TM_REJECT_UNSPECIFIED;
921                 }
922                 wnm_send_bss_transition_mgmt_resp(wpa_s,
923                                                   wpa_s->wnm_dialog_token,
924                                                   status, 0, NULL);
925         }
926 }
927
928
929 int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s,
930                                        u8 query_reason)
931 {
932         u8 buf[1000], *pos;
933         struct ieee80211_mgmt *mgmt;
934         size_t len;
935         int ret;
936
937         wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Query to "
938                    MACSTR " query_reason=%u",
939                    MAC2STR(wpa_s->bssid), query_reason);
940
941         mgmt = (struct ieee80211_mgmt *) buf;
942         os_memset(&buf, 0, sizeof(buf));
943         os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
944         os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
945         os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
946         mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
947                                            WLAN_FC_STYPE_ACTION);
948         mgmt->u.action.category = WLAN_ACTION_WNM;
949         mgmt->u.action.u.bss_tm_query.action = WNM_BSS_TRANS_MGMT_QUERY;
950         mgmt->u.action.u.bss_tm_query.dialog_token = 1;
951         mgmt->u.action.u.bss_tm_query.query_reason = query_reason;
952         pos = mgmt->u.action.u.bss_tm_query.variable;
953
954         len = pos - (u8 *) &mgmt->u.action.category;
955
956         ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
957                                   wpa_s->own_addr, wpa_s->bssid,
958                                   &mgmt->u.action.category, len, 0);
959
960         return ret;
961 }
962
963
964 static void ieee802_11_rx_wnm_notif_req_wfa(struct wpa_supplicant *wpa_s,
965                                             const u8 *sa, const u8 *data,
966                                             int len)
967 {
968         const u8 *pos, *end, *next;
969         u8 ie, ie_len;
970
971         pos = data;
972         end = data + len;
973
974         while (pos + 1 < end) {
975                 ie = *pos++;
976                 ie_len = *pos++;
977                 wpa_printf(MSG_DEBUG, "WNM: WFA subelement %u len %u",
978                            ie, ie_len);
979                 if (ie_len > end - pos) {
980                         wpa_printf(MSG_DEBUG, "WNM: Not enough room for "
981                                    "subelement");
982                         break;
983                 }
984                 next = pos + ie_len;
985                 if (ie_len < 4) {
986                         pos = next;
987                         continue;
988                 }
989                 wpa_printf(MSG_DEBUG, "WNM: Subelement OUI %06x type %u",
990                            WPA_GET_BE24(pos), pos[3]);
991
992 #ifdef CONFIG_HS20
993                 if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 5 &&
994                     WPA_GET_BE24(pos) == OUI_WFA &&
995                     pos[3] == HS20_WNM_SUB_REM_NEEDED) {
996                         /* Subscription Remediation subelement */
997                         const u8 *ie_end;
998                         u8 url_len;
999                         char *url;
1000                         u8 osu_method;
1001
1002                         wpa_printf(MSG_DEBUG, "WNM: Subscription Remediation "
1003                                    "subelement");
1004                         ie_end = pos + ie_len;
1005                         pos += 4;
1006                         url_len = *pos++;
1007                         if (url_len == 0) {
1008                                 wpa_printf(MSG_DEBUG, "WNM: No Server URL included");
1009                                 url = NULL;
1010                                 osu_method = 1;
1011                         } else {
1012                                 if (pos + url_len + 1 > ie_end) {
1013                                         wpa_printf(MSG_DEBUG, "WNM: Not enough room for Server URL (len=%u) and Server Method (left %d)",
1014                                                    url_len,
1015                                                    (int) (ie_end - pos));
1016                                         break;
1017                                 }
1018                                 url = os_malloc(url_len + 1);
1019                                 if (url == NULL)
1020                                         break;
1021                                 os_memcpy(url, pos, url_len);
1022                                 url[url_len] = '\0';
1023                                 osu_method = pos[url_len];
1024                         }
1025                         hs20_rx_subscription_remediation(wpa_s, url,
1026                                                          osu_method);
1027                         os_free(url);
1028                         pos = next;
1029                         continue;
1030                 }
1031
1032                 if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 8 &&
1033                     WPA_GET_BE24(pos) == OUI_WFA &&
1034                     pos[3] == HS20_WNM_DEAUTH_IMMINENT_NOTICE) {
1035                         const u8 *ie_end;
1036                         u8 url_len;
1037                         char *url;
1038                         u8 code;
1039                         u16 reauth_delay;
1040
1041                         ie_end = pos + ie_len;
1042                         pos += 4;
1043                         code = *pos++;
1044                         reauth_delay = WPA_GET_LE16(pos);
1045                         pos += 2;
1046                         url_len = *pos++;
1047                         wpa_printf(MSG_DEBUG, "WNM: HS 2.0 Deauthentication "
1048                                    "Imminent - Reason Code %u   "
1049                                    "Re-Auth Delay %u  URL Length %u",
1050                                    code, reauth_delay, url_len);
1051                         if (pos + url_len > ie_end)
1052                                 break;
1053                         url = os_malloc(url_len + 1);
1054                         if (url == NULL)
1055                                 break;
1056                         os_memcpy(url, pos, url_len);
1057                         url[url_len] = '\0';
1058                         hs20_rx_deauth_imminent_notice(wpa_s, code,
1059                                                        reauth_delay, url);
1060                         os_free(url);
1061                         pos = next;
1062                         continue;
1063                 }
1064 #endif /* CONFIG_HS20 */
1065
1066                 pos = next;
1067         }
1068 }
1069
1070
1071 static void ieee802_11_rx_wnm_notif_req(struct wpa_supplicant *wpa_s,
1072                                         const u8 *sa, const u8 *frm, int len)
1073 {
1074         const u8 *pos, *end;
1075         u8 dialog_token, type;
1076
1077         /* Dialog Token [1] | Type [1] | Subelements */
1078
1079         if (len < 2 || sa == NULL)
1080                 return;
1081         end = frm + len;
1082         pos = frm;
1083         dialog_token = *pos++;
1084         type = *pos++;
1085
1086         wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Received WNM-Notification Request "
1087                 "(dialog_token %u type %u sa " MACSTR ")",
1088                 dialog_token, type, MAC2STR(sa));
1089         wpa_hexdump(MSG_DEBUG, "WNM-Notification Request subelements",
1090                     pos, end - pos);
1091
1092         if (wpa_s->wpa_state != WPA_COMPLETED ||
1093             os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0) {
1094                 wpa_dbg(wpa_s, MSG_DEBUG, "WNM: WNM-Notification frame not "
1095                         "from our AP - ignore it");
1096                 return;
1097         }
1098
1099         switch (type) {
1100         case 1:
1101                 ieee802_11_rx_wnm_notif_req_wfa(wpa_s, sa, pos, end - pos);
1102                 break;
1103         default:
1104                 wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Ignore unknown "
1105                         "WNM-Notification type %u", type);
1106                 break;
1107         }
1108 }
1109
1110
1111 void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s,
1112                               const struct ieee80211_mgmt *mgmt, size_t len)
1113 {
1114         const u8 *pos, *end;
1115         u8 act;
1116
1117         if (len < IEEE80211_HDRLEN + 2)
1118                 return;
1119
1120         pos = ((const u8 *) mgmt) + IEEE80211_HDRLEN + 1;
1121         act = *pos++;
1122         end = ((const u8 *) mgmt) + len;
1123
1124         wpa_printf(MSG_DEBUG, "WNM: RX action %u from " MACSTR,
1125                    act, MAC2STR(mgmt->sa));
1126         if (wpa_s->wpa_state < WPA_ASSOCIATED ||
1127             os_memcmp(mgmt->sa, wpa_s->bssid, ETH_ALEN) != 0) {
1128                 wpa_printf(MSG_DEBUG, "WNM: Ignore unexpected WNM Action "
1129                            "frame");
1130                 return;
1131         }
1132
1133         switch (act) {
1134         case WNM_BSS_TRANS_MGMT_REQ:
1135                 ieee802_11_rx_bss_trans_mgmt_req(wpa_s, pos, end,
1136                                                  !(mgmt->da[0] & 0x01));
1137                 break;
1138         case WNM_SLEEP_MODE_RESP:
1139                 ieee802_11_rx_wnmsleep_resp(wpa_s, pos, end - pos);
1140                 break;
1141         case WNM_NOTIFICATION_REQ:
1142                 ieee802_11_rx_wnm_notif_req(wpa_s, mgmt->sa, pos, end - pos);
1143                 break;
1144         default:
1145                 wpa_printf(MSG_ERROR, "WNM: Unknown request");
1146                 break;
1147         }
1148 }