LCOV - code coverage report
Current view: top level - wpa_supplicant - wnm_sta.c (source / functions) Hit Total Coverage
Test: wpa_supplicant/hostapd combined for hwsim test run 1475438200 Lines: 754 828 91.1 %
Date: 2016-10-02 Functions: 30 30 100.0 %

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

Generated by: LCOV version 1.10