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 1443382998 Lines: 541 626 86.4 %
Date: 2015-09-27 Functions: 22 23 95.7 %

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

Generated by: LCOV version 1.10