hostapd: Use stations nsts capability in (Re)Association Response frame
[mech_eap.git] / wpa_supplicant / wpa_supplicant.c
index 1f0fcd0..5bb1f72 100644 (file)
@@ -575,6 +575,9 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
 #endif /* CONFIG_MBO */
 
        free_bss_tmp_disallowed(wpa_s);
+
+       wpabuf_free(wpa_s->lci);
+       wpa_s->lci = NULL;
 }
 
 
@@ -1870,6 +1873,13 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s,
        if (!mode)
                return;
 
+#ifdef CONFIG_HT_OVERRIDES
+       if (ssid->disable_ht) {
+               freq->ht_enabled = 0;
+               return;
+       }
+#endif /* CONFIG_HT_OVERRIDES */
+
        freq->ht_enabled = ht_supported(mode);
        if (!freq->ht_enabled)
                return;
@@ -1891,6 +1901,11 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s,
        if (pri_chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR))
                return;
 
+#ifdef CONFIG_HT_OVERRIDES
+       if (ssid->disable_ht40)
+               return;
+#endif /* CONFIG_HT_OVERRIDES */
+
        /* Check/setup HT40+/HT40- */
        for (j = 0; j < ARRAY_SIZE(ht40plus); j++) {
                if (ht40plus[j] == channel) {
@@ -1915,22 +1930,16 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s,
 
        freq->channel = pri_chan->chan;
 
-       switch (ht40) {
-       case -1:
+       if (ht40 == -1) {
                if (!(pri_chan->flag & HOSTAPD_CHAN_HT40MINUS))
                        return;
-               freq->sec_channel_offset = -1;
-               break;
-       case 1:
+       } else {
                if (!(pri_chan->flag & HOSTAPD_CHAN_HT40PLUS))
                        return;
-               freq->sec_channel_offset = 1;
-               break;
-       default:
-               break;
        }
+       freq->sec_channel_offset = ht40;
 
-       if (freq->sec_channel_offset && obss_scan) {
+       if (obss_scan) {
                struct wpa_scan_results *scan_res;
 
                scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL, 0);
@@ -2036,6 +2045,16 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s,
                        if (chwidth == VHT_CHANWIDTH_80P80MHZ)
                                break;
                }
+       } else if (ssid->max_oper_chwidth == VHT_CHANWIDTH_160MHZ) {
+               if (freq->freq == 5180) {
+                       chwidth = VHT_CHANWIDTH_160MHZ;
+                       vht_caps |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
+                       seg0 = 50;
+               } else if (freq->freq == 5520) {
+                       chwidth = VHT_CHANWIDTH_160MHZ;
+                       vht_caps |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
+                       seg0 = 114;
+               }
        }
 
        if (hostapd_set_freq_params(&vht_freq, mode->mode, freq->freq,
@@ -2288,6 +2307,11 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
         * element in all cases, it is justifiable to skip it to avoid
         * interoperability issues.
         */
+       if (ssid->p2p_group)
+               wpa_drv_get_ext_capa(wpa_s, WPA_IF_P2P_CLIENT);
+       else
+               wpa_drv_get_ext_capa(wpa_s, WPA_IF_STATION);
+
        if (!bss || wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB)) {
                u8 ext_capab[18];
                int ext_capab_len;
@@ -2729,6 +2753,95 @@ static void wpa_supplicant_enable_one_network(struct wpa_supplicant *wpa_s,
 
 
 /**
+ * wpa_supplicant_add_network - Add a new network
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: The new network configuration or %NULL if operation failed
+ *
+ * This function performs the following operations:
+ * 1. Adds a new network.
+ * 2. Send network addition notification.
+ * 3. Marks the network disabled.
+ * 4. Set network default parameters.
+ */
+struct wpa_ssid * wpa_supplicant_add_network(struct wpa_supplicant *wpa_s)
+{
+       struct wpa_ssid *ssid;
+
+       ssid = wpa_config_add_network(wpa_s->conf);
+       if (!ssid)
+               return NULL;
+       wpas_notify_network_added(wpa_s, ssid);
+       ssid->disabled = 1;
+       wpa_config_set_network_defaults(ssid);
+
+       return ssid;
+}
+
+
+/**
+ * wpa_supplicant_remove_network - Remove a configured network based on id
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * @id: Unique network id to search for
+ * Returns: 0 on success, or -1 if the network was not found, -2 if the network
+ * could not be removed
+ *
+ * This function performs the following operations:
+ * 1. Removes the network.
+ * 2. Send network removal notification.
+ * 3. Update internal state machines.
+ * 4. Stop any running sched scans.
+ */
+int wpa_supplicant_remove_network(struct wpa_supplicant *wpa_s, int id)
+{
+       struct wpa_ssid *ssid;
+       int was_disabled;
+
+       ssid = wpa_config_get_network(wpa_s->conf, id);
+       if (!ssid)
+               return -1;
+       wpas_notify_network_removed(wpa_s, ssid);
+
+       if (wpa_s->last_ssid == ssid)
+               wpa_s->last_ssid = NULL;
+
+       if (ssid == wpa_s->current_ssid || !wpa_s->current_ssid) {
+#ifdef CONFIG_SME
+               wpa_s->sme.prev_bssid_set = 0;
+#endif /* CONFIG_SME */
+               /*
+                * Invalidate the EAP session cache if the current or
+                * previously used network is removed.
+                */
+               eapol_sm_invalidate_cached_session(wpa_s->eapol);
+       }
+
+       if (ssid == wpa_s->current_ssid) {
+               wpa_sm_set_config(wpa_s->wpa, NULL);
+               eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
+
+               if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
+                       wpa_s->own_disconnect_req = 1;
+               wpa_supplicant_deauthenticate(wpa_s,
+                                             WLAN_REASON_DEAUTH_LEAVING);
+       }
+
+       was_disabled = ssid->disabled;
+
+       if (wpa_config_remove_network(wpa_s->conf, id) < 0)
+               return -2;
+
+       if (!was_disabled && wpa_s->sched_scanning) {
+               wpa_printf(MSG_DEBUG,
+                          "Stop ongoing sched_scan to remove network from filters");
+               wpa_supplicant_cancel_sched_scan(wpa_s);
+               wpa_supplicant_req_scan(wpa_s, 0, 0);
+       }
+
+       return 0;
+}
+
+
+/**
  * wpa_supplicant_enable_network - Mark a configured network as enabled
  * @wpa_s: wpa_supplicant structure for a network interface
  * @ssid: wpa_ssid structure for a configured network or %NULL
@@ -3788,8 +3901,8 @@ void wpa_supplicant_apply_vht_overrides(
        if (!vhtcaps || !vhtcaps_mask)
                return;
 
-       vhtcaps->vht_capabilities_info = ssid->vht_capa;
-       vhtcaps_mask->vht_capabilities_info = ssid->vht_capa_mask;
+       vhtcaps->vht_capabilities_info = host_to_le32(ssid->vht_capa);
+       vhtcaps_mask->vht_capabilities_info = host_to_le32(ssid->vht_capa_mask);
 
 #ifdef CONFIG_HT_OVERRIDES
        /* if max ampdu is <= 3, we have to make the HT cap the same */
@@ -3811,15 +3924,17 @@ void wpa_supplicant_apply_vht_overrides(
 #define OVERRIDE_MCS(i)                                                        \
        if (ssid->vht_tx_mcs_nss_ ##i >= 0) {                           \
                vhtcaps_mask->vht_supported_mcs_set.tx_map |=           \
-                       3 << 2 * (i - 1);                               \
+                       host_to_le16(3 << 2 * (i - 1));                 \
                vhtcaps->vht_supported_mcs_set.tx_map |=                \
-                       ssid->vht_tx_mcs_nss_ ##i << 2 * (i - 1);       \
+                       host_to_le16(ssid->vht_tx_mcs_nss_ ##i <<       \
+                                    2 * (i - 1));                      \
        }                                                               \
        if (ssid->vht_rx_mcs_nss_ ##i >= 0) {                           \
                vhtcaps_mask->vht_supported_mcs_set.rx_map |=           \
-                       3 << 2 * (i - 1);                               \
+                       host_to_le16(3 << 2 * (i - 1));                 \
                vhtcaps->vht_supported_mcs_set.rx_map |=                \
-                       ssid->vht_rx_mcs_nss_ ##i << 2 * (i - 1);       \
+                       host_to_le16(ssid->vht_rx_mcs_nss_ ##i <<       \
+                                    2 * (i - 1));                      \
        }
 
        OVERRIDE_MCS(1);
@@ -3991,8 +4106,9 @@ static void wpas_fst_update_mb_ie_cb(void *ctx, const u8 *addr,
 }
 
 
-const u8 * wpas_fst_get_peer_first(void *ctx, struct fst_get_peer_ctx **get_ctx,
-                                  Boolean mb_only)
+static const u8 * wpas_fst_get_peer_first(void *ctx,
+                                         struct fst_get_peer_ctx **get_ctx,
+                                         Boolean mb_only)
 {
        struct wpa_supplicant *wpa_s = ctx;
 
@@ -4004,8 +4120,9 @@ const u8 * wpas_fst_get_peer_first(void *ctx, struct fst_get_peer_ctx **get_ctx,
 }
 
 
-const u8 * wpas_fst_get_peer_next(void *ctx, struct fst_get_peer_ctx **get_ctx,
-                                 Boolean mb_only)
+static const u8 * wpas_fst_get_peer_next(void *ctx,
+                                        struct fst_get_peer_ctx **get_ctx,
+                                        Boolean mb_only)
 {
        return NULL;
 }
@@ -4846,6 +4963,8 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s,
        wpas_mbo_update_non_pref_chan(wpa_s, wpa_s->conf->non_pref_chan);
 #endif /* CONFIG_MBO */
 
+       wpa_supplicant_set_default_scan_ies(wpa_s);
+
        return 0;
 }
 
@@ -5822,6 +5941,19 @@ int wpas_get_ssid_pmf(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
                        return NO_MGMT_FRAME_PROTECTION;
                }
 
+               if (ssid &&
+                   (ssid->key_mgmt &
+                    ~(WPA_KEY_MGMT_NONE | WPA_KEY_MGMT_WPS |
+                      WPA_KEY_MGMT_IEEE8021X_NO_WPA)) == 0) {
+                       /*
+                        * Do not use the default PMF value for non-RSN networks
+                        * since PMF is available only with RSN and pmf=2
+                        * configuration would otherwise prevent connections to
+                        * all open networks.
+                        */
+                       return NO_MGMT_FRAME_PROTECTION;
+               }
+
                return wpa_s->conf->pmf;
        }
 
@@ -5980,6 +6112,27 @@ void wpas_request_connection(struct wpa_supplicant *wpa_s)
 }
 
 
+/**
+ * wpas_request_disconnection - Request disconnection
+ * @wpa_s: Pointer to the network interface
+ *
+ * This function is used to request disconnection from the currently connected
+ * network. This will stop any ongoing scans and initiate deauthentication.
+ */
+void wpas_request_disconnection(struct wpa_supplicant *wpa_s)
+{
+#ifdef CONFIG_SME
+       wpa_s->sme.prev_bssid_set = 0;
+#endif /* CONFIG_SME */
+       wpa_s->reassociate = 0;
+       wpa_s->disconnected = 1;
+       wpa_supplicant_cancel_sched_scan(wpa_s);
+       wpa_supplicant_cancel_scan(wpa_s);
+       wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+       eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL);
+}
+
+
 void dump_freq_data(struct wpa_supplicant *wpa_s, const char *title,
                    struct wpa_used_freq_data *freqs_data,
                    unsigned int len)
@@ -6340,6 +6493,147 @@ int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s,
 }
 
 
+static struct wpabuf * wpas_rrm_build_lci_report(struct wpa_supplicant *wpa_s,
+                                                const u8 *request, size_t len,
+                                                struct wpabuf *report)
+{
+       u8 token, type, subject;
+       u16 max_age = 0;
+       struct os_reltime t, diff;
+       unsigned long diff_l;
+       u8 *ptoken;
+       const u8 *subelem;
+
+       if (!wpa_s->lci || len < 3 + 4)
+               return report;
+
+       token = *request++;
+       /* Measurement request mode isn't used */
+       request++;
+       type = *request++;
+       subject = *request++;
+
+       wpa_printf(MSG_DEBUG,
+                  "Measurement request token %u type %u location subject %u",
+                  token, type, subject);
+
+       if (type != MEASURE_TYPE_LCI || subject != LOCATION_SUBJECT_REMOTE) {
+               wpa_printf(MSG_INFO,
+                          "Not building LCI report - bad type or location subject");
+               return report;
+       }
+
+       /* Subelements are formatted exactly like elements */
+       subelem = get_ie(request, len, LCI_REQ_SUBELEM_MAX_AGE);
+       if (subelem && subelem[1] == 2)
+               max_age = WPA_GET_LE16(subelem + 2);
+
+       if (os_get_reltime(&t))
+               return report;
+
+       os_reltime_sub(&t, &wpa_s->lci_time, &diff);
+       /* LCI age is calculated in 10th of a second units. */
+       diff_l = diff.sec * 10 + diff.usec / 100000;
+
+       if (max_age != 0xffff && max_age < diff_l)
+               return report;
+
+       if (wpabuf_resize(&report, 2 + wpabuf_len(wpa_s->lci)))
+               return report;
+
+       wpabuf_put_u8(report, WLAN_EID_MEASURE_REPORT);
+       wpabuf_put_u8(report, wpabuf_len(wpa_s->lci));
+       /* We'll override user's measurement token */
+       ptoken = wpabuf_put(report, 0);
+       wpabuf_put_buf(report, wpa_s->lci);
+       *ptoken = token;
+
+       return report;
+}
+
+
+void wpas_rrm_handle_radio_measurement_request(struct wpa_supplicant *wpa_s,
+                                              const u8 *src,
+                                              const u8 *frame, size_t len)
+{
+       struct wpabuf *buf, *report;
+       u8 token;
+       const u8 *ie, *end;
+
+       if (wpa_s->wpa_state != WPA_COMPLETED) {
+               wpa_printf(MSG_INFO,
+                          "RRM: Ignoring radio measurement request: Not associated");
+               return;
+       }
+
+       if (!wpa_s->rrm.rrm_used) {
+               wpa_printf(MSG_INFO,
+                          "RRM: Ignoring radio measurement request: Not RRM network");
+               return;
+       }
+
+       if (len < 3) {
+               wpa_printf(MSG_INFO,
+                          "RRM: Ignoring too short radio measurement request");
+               return;
+       }
+
+       end = frame + len;
+
+       token = *frame++;
+
+       /* Ignore number of repetitions because it's not used in LCI request */
+       frame += 2;
+
+       report = NULL;
+       while ((ie = get_ie(frame, end - frame, WLAN_EID_MEASURE_REQUEST)) &&
+              ie[1] >= 3) {
+               u8 msmt_type;
+
+               msmt_type = ie[4];
+               wpa_printf(MSG_DEBUG, "RRM request %d", msmt_type);
+
+               switch (msmt_type) {
+               case MEASURE_TYPE_LCI:
+                       report = wpas_rrm_build_lci_report(wpa_s, ie + 2, ie[1],
+                                                          report);
+                       break;
+               default:
+                       wpa_printf(MSG_INFO,
+                                  "RRM: Unsupported radio measurement request %d",
+                                  msmt_type);
+                       break;
+               }
+
+               frame = ie + ie[1] + 2;
+       }
+
+       if (!report)
+               return;
+
+       buf = wpabuf_alloc(3 + wpabuf_len(report));
+       if (!buf) {
+               wpabuf_free(report);
+               return;
+       }
+
+       wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
+       wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REPORT);
+       wpabuf_put_u8(buf, token);
+
+       wpabuf_put_buf(buf, report);
+       wpabuf_free(report);
+
+       if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, src,
+                               wpa_s->own_addr, wpa_s->bssid,
+                               wpabuf_head(buf), wpabuf_len(buf), 0)) {
+               wpa_printf(MSG_ERROR,
+                          "RRM: Radio measurement report failed: Sending Action frame failed");
+       }
+       wpabuf_free(buf);
+}
+
+
 void wpas_rrm_handle_link_measurement_request(struct wpa_supplicant *wpa_s,
                                              const u8 *src,
                                              const u8 *frame, size_t len,