/*
* WPA Supplicant
- * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2017, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
+#ifdef CONFIG_MATCH_IFACE
+#include <net/if.h>
+#include <fnmatch.h>
+#endif /* CONFIG_MATCH_IFACE */
#include "common.h"
#include "crypto/random.h"
#include "common/ieee802_11_defs.h"
#include "common/hw_features_common.h"
#include "p2p/p2p.h"
+#include "fst/fst.h"
#include "blacklist.h"
#include "wpas_glue.h"
#include "wps_supplicant.h"
const char *const wpa_supplicant_version =
"wpa_supplicant v" VERSION_STR "\n"
-"Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> and contributors";
+"Copyright (c) 2003-2017, Jouni Malinen <j@w1.fi> and contributors";
const char *const wpa_supplicant_license =
"This software may be distributed under the terms of the BSD license.\n"
{
struct wpa_supplicant *wpa_s = eloop_ctx;
const u8 *bssid = wpa_s->bssid;
- if (is_zero_ether_addr(bssid))
+ if (!is_zero_ether_addr(wpa_s->pending_bssid) &&
+ (wpa_s->wpa_state == WPA_AUTHENTICATING ||
+ wpa_s->wpa_state == WPA_ASSOCIATING))
bssid = wpa_s->pending_bssid;
wpa_msg(wpa_s, MSG_INFO, "Authentication with " MACSTR " timed out.",
MAC2STR(bssid));
eapol_sm_notify_config(wpa_s->eapol, &ssid->eap, &eapol_conf);
- ieee802_1x_alloc_kay_sm(wpa_s, ssid);
+#ifdef CONFIG_MACSEC
+ if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE && ssid->mka_psk_set)
+ ieee802_1x_create_preshared_mka(wpa_s, ssid);
+ else
+ ieee802_1x_alloc_kay_sm(wpa_s, ssid);
+#endif /* CONFIG_MACSEC */
#endif /* IEEE8021X_EAPOL */
}
}
+static void free_bss_tmp_disallowed(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_bss_tmp_disallowed *bss, *prev;
+
+ dl_list_for_each_safe(bss, prev, &wpa_s->bss_tmp_disallowed,
+ struct wpa_bss_tmp_disallowed, list) {
+ dl_list_del(&bss->list);
+ os_free(bss);
+ }
+}
+
+
+void wpas_flush_fils_hlp_req(struct wpa_supplicant *wpa_s)
+{
+ struct fils_hlp_req *req;
+
+ while ((req = dl_list_first(&wpa_s->fils_hlp_req, struct fils_hlp_req,
+ list)) != NULL) {
+ dl_list_del(&req->list);
+ wpabuf_free(req->pkt);
+ os_free(req);
+ }
+}
+
+
static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
{
int i;
#ifdef CONFIG_TESTING_OPTIONS
l2_packet_deinit(wpa_s->l2_test);
wpa_s->l2_test = NULL;
+ os_free(wpa_s->get_pref_freq_list_override);
+ wpa_s->get_pref_freq_list_override = NULL;
#endif /* CONFIG_TESTING_OPTIONS */
if (wpa_s->conf != NULL) {
wpas_mac_addr_rand_scan_clear(wpa_s, MAC_ADDR_RAND_ALL);
+ /*
+ * Need to remove any pending gas-query radio work before the
+ * gas_query_deinit() call because gas_query::work has not yet been set
+ * for works that have not been started. gas_query_free() will be unable
+ * to cancel such pending radio works and once the pending gas-query
+ * radio work eventually gets removed, the deinit notification call to
+ * gas_query_start_cb() would result in dereferencing freed memory.
+ */
+ if (wpa_s->radio)
+ radio_remove_works(wpa_s, "gas-query", 0);
gas_query_deinit(wpa_s->gas);
wpa_s->gas = NULL;
wpa_s->last_scan_res = NULL;
#ifdef CONFIG_HS20
+ if (wpa_s->drv_priv)
+ wpa_drv_configure_frame_filters(wpa_s, 0);
hs20_deinit(wpa_s);
#endif /* CONFIG_HS20 */
}
wmm_ac_notify_disassoc(wpa_s);
+
+ wpa_s->sched_scan_plans_num = 0;
+ os_free(wpa_s->sched_scan_plans);
+ wpa_s->sched_scan_plans = NULL;
+
+#ifdef CONFIG_MBO
+ wpa_s->non_pref_chan_num = 0;
+ os_free(wpa_s->non_pref_chan);
+ wpa_s->non_pref_chan = NULL;
+#endif /* CONFIG_MBO */
+
+ free_bss_tmp_disallowed(wpa_s);
+
+ wpabuf_free(wpa_s->lci);
+ wpa_s->lci = NULL;
+ wpas_clear_beacon_rep_data(wpa_s);
+
+#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
+#ifdef CONFIG_MESH
+ {
+ struct external_pmksa_cache *entry;
+
+ while ((entry = dl_list_last(&wpa_s->mesh_external_pmksa_cache,
+ struct external_pmksa_cache,
+ list)) != NULL) {
+ dl_list_del(&entry->list);
+ os_free(entry->pmksa_cache);
+ os_free(entry);
+ }
+ }
+#endif /* CONFIG_MESH */
+#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
+
+ wpas_flush_fils_hlp_req(wpa_s);
+
+ wpabuf_free(wpa_s->ric_ies);
+ wpa_s->ric_ies = NULL;
}
wpa_s->normal_scans = 0;
}
+#ifdef CONFIG_P2P
+ /*
+ * P2PS client has to reply to Probe Request frames received on the
+ * group operating channel. Enable Probe Request frame reporting for
+ * P2P connected client in case p2p_cli_probe configuration property is
+ * set to 1.
+ */
+ if (wpa_s->conf->p2p_cli_probe && wpa_s->current_ssid &&
+ wpa_s->current_ssid->mode == WPAS_MODE_INFRA &&
+ wpa_s->current_ssid->p2p_group) {
+ if (state == WPA_COMPLETED && !wpa_s->p2p_cli_probe) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "P2P: Enable CLI Probe Request RX reporting");
+ wpa_s->p2p_cli_probe =
+ wpa_drv_probe_req_report(wpa_s, 1) >= 0;
+ } else if (state != WPA_COMPLETED && wpa_s->p2p_cli_probe) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "P2P: Disable CLI Probe Request RX reporting");
+ wpa_s->p2p_cli_probe = 0;
+ wpa_drv_probe_req_report(wpa_s, 0);
+ }
+ }
+#endif /* CONFIG_P2P */
+
if (state != WPA_SCANNING)
wpa_supplicant_notify_scanning(wpa_s, 0);
eapol_sm_invalidate_cached_session(wpa_s->eapol);
if (wpa_s->current_ssid) {
- wpa_s->own_disconnect_req = 1;
+ if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
+ wpa_s->own_disconnect_req = 1;
wpa_supplicant_deauthenticate(wpa_s,
WLAN_REASON_DEAUTH_LEAVING);
}
wpa_supplicant_terminate_proc(global);
}
}
+
+ if (wpa_debug_reopen_file() < 0) {
+ /* Ignore errors since we cannot really do much to fix this */
+ wpa_printf(MSG_DEBUG, "Could not reopen debug log file");
+ }
}
return -1;
}
+#ifdef CONFIG_NO_WPA
+ wpa_s->group_cipher = WPA_CIPHER_NONE;
+ wpa_s->pairwise_cipher = WPA_CIPHER_NONE;
+#else /* CONFIG_NO_WPA */
sel = ie.group_cipher & ssid->group_cipher;
wpa_s->group_cipher = wpa_pick_group_cipher(sel);
if (wpa_s->group_cipher < 0) {
}
wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using PTK %s",
wpa_cipher_txt(wpa_s->pairwise_cipher));
+#endif /* CONFIG_NO_WPA */
sel = ie.key_mgmt & ssid->key_mgmt;
#ifdef CONFIG_SAE
wpa_dbg(wpa_s, MSG_DEBUG,
"WPA: using KEY_MGMT 802.1X with Suite B");
#endif /* CONFIG_SUITEB */
+#ifdef CONFIG_FILS
+#ifdef CONFIG_IEEE80211R
+ } else if (sel & WPA_KEY_MGMT_FT_FILS_SHA384) {
+ wpa_s->key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA384;
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT-FILS-SHA384");
+ } else if (sel & WPA_KEY_MGMT_FT_FILS_SHA256) {
+ wpa_s->key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA256;
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT-FILS-SHA256");
+#endif /* CONFIG_IEEE80211R */
+ } else if (sel & WPA_KEY_MGMT_FILS_SHA384) {
+ wpa_s->key_mgmt = WPA_KEY_MGMT_FILS_SHA384;
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FILS-SHA384");
+ } else if (sel & WPA_KEY_MGMT_FILS_SHA256) {
+ wpa_s->key_mgmt = WPA_KEY_MGMT_FILS_SHA256;
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FILS-SHA256");
+#endif /* CONFIG_FILS */
#ifdef CONFIG_IEEE80211R
} else if (sel & WPA_KEY_MGMT_FT_IEEE8021X) {
wpa_s->key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X;
int psk_set = 0;
if (ssid->psk_set) {
- wpa_sm_set_pmk(wpa_s->wpa, ssid->psk, PMK_LEN, NULL);
+ wpa_sm_set_pmk(wpa_s->wpa, ssid->psk, PMK_LEN, NULL,
+ NULL);
psk_set = 1;
}
#ifndef CONFIG_NO_PBKDF2
4096, psk, PMK_LEN);
wpa_hexdump_key(MSG_MSGDUMP, "PSK (from passphrase)",
psk, PMK_LEN);
- wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL);
+ wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL, NULL);
psk_set = 1;
os_memset(psk, 0, sizeof(psk));
}
wpa_hexdump_key(MSG_MSGDUMP, "PSK (from "
"external passphrase)",
psk, PMK_LEN);
- wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL);
+ wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL,
+ NULL);
psk_set = 1;
os_memset(psk, 0, sizeof(psk));
} else
ext_password_free(pw);
return -1;
}
- wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL);
+ wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL,
+ NULL);
psk_set = 1;
os_memset(psk, 0, sizeof(psk));
} else {
if (wpa_s->conf->hs20)
*pos |= 0x40; /* Bit 46 - WNM-Notification */
#endif /* CONFIG_HS20 */
+#ifdef CONFIG_MBO
+ *pos |= 0x40; /* Bit 46 - WNM-Notification */
+#endif /* CONFIG_MBO */
break;
case 6: /* Bits 48-55 */
break;
+ case 7: /* Bits 56-63 */
+ break;
+ case 8: /* Bits 64-71 */
+ if (wpa_s->conf->ftm_responder)
+ *pos |= 0x40; /* Bit 70 - FTM responder */
+ if (wpa_s->conf->ftm_initiator)
+ *pos |= 0x80; /* Bit 71 - FTM initiator */
+ break;
+ case 9: /* Bits 72-79 */
+#ifdef CONFIG_FILS
+ *pos |= 0x01;
+#endif /* CONFIG_FILS */
+ break;
}
}
int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf, size_t buflen)
{
u8 *pos = buf;
- u8 len = 6, i;
+ u8 len = 10, i;
if (len < wpa_s->extended_capa_len)
len = wpa_s->extended_capa_len;
struct wpa_connect_work *cwork;
int rand_style;
+ wpa_s->own_disconnect_req = 0;
+
+ /*
+ * If we are starting a new connection, any previously pending EAPOL
+ * RX cannot be valid anymore.
+ */
+ wpabuf_free(wpa_s->pending_eapol_rx);
+ wpa_s->pending_eapol_rx = NULL;
+
if (ssid->mac_addr == -1)
rand_style = wpa_s->conf->mac_addr;
else
wmm_ac_clear_saved_tspecs(wpa_s);
wpa_s->reassoc_same_bss = 0;
+ wpa_s->reassoc_same_ess = 0;
if (wpa_s->last_ssid == ssid) {
wpa_dbg(wpa_s, MSG_DEBUG, "Re-association to the same ESS");
+ wpa_s->reassoc_same_ess = 1;
if (wpa_s->current_bss && wpa_s->current_bss == bss) {
wmm_ac_save_tspecs(wpa_s);
wpa_s->reassoc_same_bss = 1;
}
- } else if (rand_style > 0) {
+ }
+
+ if (rand_style > 0 && !wpa_s->reassoc_same_ess) {
if (wpas_update_random_addr(wpa_s, rand_style) < 0)
return;
wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
- } else if (wpa_s->mac_addr_changed) {
+ } else if (rand_style == 0 && wpa_s->mac_addr_changed) {
if (wpa_drv_set_mac_addr(wpa_s, NULL) < 0) {
wpa_msg(wpa_s, MSG_INFO,
"Could not restore permanent MAC address");
#ifdef CONFIG_IBSS_RSN
ibss_rsn_deinit(wpa_s->ibss_rsn);
wpa_s->ibss_rsn = NULL;
+#else /* CONFIG_IBSS_RSN */
+ if (ssid->mode == WPAS_MODE_IBSS &&
+ !(ssid->key_mgmt & (WPA_KEY_MGMT_NONE | WPA_KEY_MGMT_WPA_NONE))) {
+ wpa_msg(wpa_s, MSG_INFO,
+ "IBSS RSN not supported in the build");
+ return;
+ }
#endif /* CONFIG_IBSS_RSN */
if (ssid->mode == WPAS_MODE_AP || ssid->mode == WPAS_MODE_P2P_GO ||
return;
}
wpa_s->current_bss = bss;
- wpa_msg_ctrl(wpa_s, MSG_INFO, MESH_GROUP_STARTED
- "ssid=\"%s\" id=%d",
- wpa_ssid_txt(ssid->ssid, ssid->ssid_len),
- ssid->id);
+ wpa_msg(wpa_s, MSG_INFO, MESH_GROUP_STARTED "ssid=\"%s\" id=%d",
+ wpa_ssid_txt(ssid->ssid, ssid->ssid_len),
+ ssid->id);
#else /* CONFIG_MESH */
wpa_msg(wpa_s, MSG_ERROR,
"mesh mode support not included in the build");
return;
}
+#ifdef CONFIG_SME
+ if (ssid->mode == WPAS_MODE_IBSS || ssid->mode == WPAS_MODE_MESH) {
+ /* Clear possibly set auth_alg, if any, from last attempt. */
+ wpa_s->sme.auth_alg = WPA_AUTH_ALG_OPEN;
+ }
+#endif /* CONFIG_SME */
+
+ wpas_abort_ongoing_scan(wpa_s);
+
cwork = os_zalloc(sizeof(*cwork));
if (cwork == NULL)
return;
}
+static int drv_supports_vht(struct wpa_supplicant *wpa_s,
+ const struct wpa_ssid *ssid)
+{
+ enum hostapd_hw_mode hw_mode;
+ struct hostapd_hw_modes *mode = NULL;
+ u8 channel;
+ int i;
+
+#ifdef CONFIG_HT_OVERRIDES
+ if (ssid->disable_ht)
+ return 0;
+#endif /* CONFIG_HT_OVERRIDES */
+
+ hw_mode = ieee80211_freq_to_chan(ssid->frequency, &channel);
+ if (hw_mode == NUM_HOSTAPD_MODES)
+ return 0;
+ for (i = 0; wpa_s->hw.modes && i < wpa_s->hw.num_modes; i++) {
+ if (wpa_s->hw.modes[i].mode == hw_mode) {
+ mode = &wpa_s->hw.modes[i];
+ break;
+ }
+ }
+
+ if (!mode)
+ return 0;
+
+ return mode->vht_capab != 0;
+}
+
+
void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s,
const struct wpa_ssid *ssid,
struct hostapd_freq_params *freq)
struct hostapd_channel_data *pri_chan = NULL, *sec_chan = NULL;
u8 channel;
int i, chan_idx, ht40 = -1, res, obss_scan = 1;
- unsigned int j;
+ unsigned int j, k;
struct hostapd_freq_params vht_freq;
+ int chwidth, seg0, seg1;
+ u32 vht_caps = 0;
freq->freq = ssid->frequency;
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;
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) {
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);
"IBSS/mesh: setup freq channel %d, sec_channel_offset %d",
freq->channel, freq->sec_channel_offset);
- /* Not sure if mesh is ready for VHT */
- if (ssid->mode != WPAS_MODE_IBSS)
+ if (!drv_supports_vht(wpa_s, ssid))
return;
/* For IBSS check VHT_IBSS flag */
- if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_VHT_IBSS))
+ if (ssid->mode == WPAS_MODE_IBSS &&
+ !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_VHT_IBSS))
return;
vht_freq = *freq;
+#ifdef CONFIG_VHT_OVERRIDES
+ if (ssid->disable_vht) {
+ freq->vht_enabled = 0;
+ return;
+ }
+#endif /* CONFIG_VHT_OVERRIDES */
+
vht_freq.vht_enabled = vht_supported(mode);
if (!vht_freq.vht_enabled)
return;
return;
}
+ chwidth = VHT_CHANWIDTH_80MHZ;
+ seg0 = vht80[j] + 6;
+ seg1 = 0;
+
+ if (ssid->max_oper_chwidth == VHT_CHANWIDTH_80P80MHZ) {
+ /* setup center_freq2, bandwidth */
+ for (k = 0; k < ARRAY_SIZE(vht80); k++) {
+ /* Only accept 80 MHz segments separated by a gap */
+ if (j == k || abs(vht80[j] - vht80[k]) == 16)
+ continue;
+ for (i = vht80[k]; i < vht80[k] + 16; i += 4) {
+ struct hostapd_channel_data *chan;
+
+ chan = hw_get_channel_chan(mode, i, NULL);
+ if (!chan)
+ continue;
+
+ if (chan->flag & (HOSTAPD_CHAN_DISABLED |
+ HOSTAPD_CHAN_NO_IR |
+ HOSTAPD_CHAN_RADAR))
+ continue;
+
+ /* Found a suitable second segment for 80+80 */
+ chwidth = VHT_CHANWIDTH_80P80MHZ;
+ vht_caps |=
+ VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
+ seg1 = vht80[k] + 6;
+ }
+
+ 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,
freq->channel, freq->ht_enabled,
vht_freq.vht_enabled,
freq->sec_channel_offset,
- VHT_CHANWIDTH_80MHZ,
- vht80[j] + 6, 0, 0) != 0)
+ chwidth, seg0, seg1, vht_caps) != 0)
return;
*freq = vht_freq;
int wep_keys_set = 0;
int assoc_failed = 0;
struct wpa_ssid *old_ssid;
+ u8 prev_bssid[ETH_ALEN];
#ifdef CONFIG_HT_OVERRIDES
struct ieee80211_ht_capabilities htcaps;
struct ieee80211_ht_capabilities htcaps_mask;
wpa_s->connect_work = work;
- if (cwork->bss_removed || !wpas_valid_bss_ssid(wpa_s, bss, ssid)) {
+ if (cwork->bss_removed || !wpas_valid_bss_ssid(wpa_s, bss, ssid) ||
+ wpas_network_disabled(wpa_s, ssid)) {
wpa_dbg(wpa_s, MSG_DEBUG, "BSS/SSID entry for association not valid anymore - drop connection attempt");
wpas_connect_work_done(wpa_s);
return;
}
+ os_memcpy(prev_bssid, wpa_s->bssid, ETH_ALEN);
os_memset(¶ms, 0, sizeof(params));
wpa_s->reassociate = 0;
wpa_s->eap_expected_failure = 0;
} else {
wpa_msg(wpa_s, MSG_INFO, "Trying to associate with SSID '%s'",
wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
- os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
+ if (bss)
+ os_memcpy(wpa_s->pending_bssid, bss->bssid, ETH_ALEN);
+ else
+ os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
}
- wpa_supplicant_cancel_sched_scan(wpa_s);
+ if (!wpa_s->pno)
+ wpa_supplicant_cancel_sched_scan(wpa_s);
+
wpa_supplicant_cancel_scan(wpa_s);
/* Starting new association, so clear the possibly used WPA IE from the
os_memset(wpa_s->p2p_ip_addr_info, 0, sizeof(wpa_s->p2p_ip_addr_info));
#endif /* CONFIG_P2P */
-#ifdef CONFIG_HS20
- if (is_hs20_network(wpa_s, ssid, bss)) {
- struct wpabuf *hs20;
- hs20 = wpabuf_alloc(20);
- if (hs20) {
- int pps_mo_id = hs20_get_pps_mo_id(wpa_s, ssid);
- size_t len;
-
- wpas_hs20_add_indication(hs20, pps_mo_id);
- len = sizeof(wpa_ie) - wpa_ie_len;
- if (wpabuf_len(hs20) <= len) {
- os_memcpy(wpa_ie + wpa_ie_len,
- wpabuf_head(hs20), wpabuf_len(hs20));
- wpa_ie_len += wpabuf_len(hs20);
- }
- wpabuf_free(hs20);
- }
+ if (bss) {
+ wpa_ie_len += wpas_supp_op_class_ie(wpa_s, bss->freq,
+ wpa_ie + wpa_ie_len,
+ sizeof(wpa_ie) -
+ wpa_ie_len);
}
-#endif /* CONFIG_HS20 */
/*
* Workaround: Add Extended Capabilities element only if the AP
* 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;
}
}
+#ifdef CONFIG_HS20
+ if (is_hs20_network(wpa_s, ssid, bss)) {
+ struct wpabuf *hs20;
+
+ hs20 = wpabuf_alloc(20);
+ if (hs20) {
+ int pps_mo_id = hs20_get_pps_mo_id(wpa_s, ssid);
+ size_t len;
+
+ wpas_hs20_add_indication(hs20, pps_mo_id);
+ len = sizeof(wpa_ie) - wpa_ie_len;
+ if (wpabuf_len(hs20) <= len) {
+ os_memcpy(wpa_ie + wpa_ie_len,
+ wpabuf_head(hs20), wpabuf_len(hs20));
+ wpa_ie_len += wpabuf_len(hs20);
+ }
+ wpabuf_free(hs20);
+
+ hs20_configure_frame_filters(wpa_s);
+ }
+ }
+#endif /* CONFIG_HS20 */
+
if (wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ]) {
struct wpabuf *buf = wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ];
size_t len;
}
}
+#ifdef CONFIG_FST
+ if (wpa_s->fst_ies) {
+ int fst_ies_len = wpabuf_len(wpa_s->fst_ies);
+
+ if (wpa_ie_len + fst_ies_len <= sizeof(wpa_ie)) {
+ os_memcpy(wpa_ie + wpa_ie_len,
+ wpabuf_head(wpa_s->fst_ies), fst_ies_len);
+ wpa_ie_len += fst_ies_len;
+ }
+ }
+#endif /* CONFIG_FST */
+
+#ifdef CONFIG_MBO
+ if (bss && wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE)) {
+ int len;
+
+ len = wpas_mbo_ie(wpa_s, wpa_ie + wpa_ie_len,
+ sizeof(wpa_ie) - wpa_ie_len);
+ if (len >= 0)
+ wpa_ie_len += len;
+ }
+#endif /* CONFIG_MBO */
+
wpa_clear_keys(wpa_s, bss ? bss->bssid : NULL);
use_crypt = 1;
cipher_pairwise = wpa_s->pairwise_cipher;
if (bss) {
params.ssid = bss->ssid;
params.ssid_len = bss->ssid_len;
- if (!wpas_driver_bss_selection(wpa_s) || ssid->bssid_set) {
+ if (!wpas_driver_bss_selection(wpa_s) || ssid->bssid_set ||
+ wpa_s->key_mgmt == WPA_KEY_MGMT_WPS) {
wpa_printf(MSG_DEBUG, "Limit connection to BSSID "
MACSTR " freq=%u MHz based on scan results "
- "(bssid_set=%d)",
+ "(bssid_set=%d wps=%d)",
MAC2STR(bss->bssid), bss->freq,
- ssid->bssid_set);
+ ssid->bssid_set,
+ wpa_s->key_mgmt == WPA_KEY_MGMT_WPS);
params.bssid = bss->bssid;
params.freq.freq = bss->freq;
}
params.bssid_hint = bss->bssid;
params.freq_hint = bss->freq;
+ params.pbss = bss_is_pbss(bss);
} else {
params.ssid = ssid->ssid;
params.ssid_len = ssid->ssid_len;
+ params.pbss = (ssid->pbss != 2) ? ssid->pbss : 0;
}
if (ssid->mode == WPAS_MODE_IBSS && ssid->bssid_set &&
params.p2p = ssid->p2p_group;
- if (wpa_s->parent->set_sta_uapsd)
- params.uapsd = wpa_s->parent->sta_uapsd;
+ if (wpa_s->p2pdev->set_sta_uapsd)
+ params.uapsd = wpa_s->p2pdev->sta_uapsd;
else
params.uapsd = -1;
}
#endif /* CONFIG_P2P */
+ if (wpa_s->reassoc_same_ess && !is_zero_ether_addr(prev_bssid) &&
+ wpa_s->current_ssid)
+ params.prev_bssid = prev_bssid;
+
ret = wpa_drv_associate(wpa_s, ¶ms);
if (ret < 0) {
wpa_msg(wpa_s, MSG_INFO, "Association request to the driver "
}
old_ssid = wpa_s->current_ssid;
wpa_s->current_ssid = ssid;
- wpa_s->current_bss = bss;
+
+ if (!wpas_driver_bss_selection(wpa_s) || ssid->bssid_set) {
+ wpa_s->current_bss = bss;
+#ifdef CONFIG_HS20
+ hs20_configure_frame_filters(wpa_s);
+#endif /* CONFIG_HS20 */
+ }
+
wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid);
wpa_supplicant_initiate_eapol(wpa_s);
if (old_ssid != wpa_s->current_ssid)
MAC2STR(wpa_s->bssid), MAC2STR(wpa_s->pending_bssid),
reason_code, wpa_supplicant_state_txt(wpa_s->wpa_state));
- if (!is_zero_ether_addr(wpa_s->bssid))
- addr = wpa_s->bssid;
- else if (!is_zero_ether_addr(wpa_s->pending_bssid) &&
- (wpa_s->wpa_state == WPA_AUTHENTICATING ||
- wpa_s->wpa_state == WPA_ASSOCIATING))
+ if (!is_zero_ether_addr(wpa_s->pending_bssid) &&
+ (wpa_s->wpa_state == WPA_AUTHENTICATING ||
+ wpa_s->wpa_state == WPA_ASSOCIATING))
addr = wpa_s->pending_bssid;
+ else if (!is_zero_ether_addr(wpa_s->bssid))
+ addr = wpa_s->bssid;
else if (wpa_s->wpa_state == WPA_ASSOCIATING) {
/*
* When using driver-based BSS selection, we may not know the
#ifdef CONFIG_MESH
if (wpa_s->ifmsh) {
- wpa_msg_ctrl(wpa_s, MSG_INFO, MESH_GROUP_REMOVED "%s",
- wpa_s->ifname);
+ wpa_msg(wpa_s, MSG_INFO, MESH_GROUP_REMOVED "%s",
+ wpa_s->ifname);
wpa_supplicant_leave_mesh(wpa_s);
}
#endif /* CONFIG_MESH */
}
+/**
+ * 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
} else
wpa_supplicant_enable_one_network(wpa_s, ssid);
- if (wpa_s->reassociate && !wpa_s->disconnected) {
+ if (wpa_s->reassociate && !wpa_s->disconnected &&
+ (!wpa_s->current_ssid ||
+ wpa_s->wpa_state == WPA_DISCONNECTED ||
+ wpa_s->wpa_state == WPA_SCANNING)) {
if (wpa_s->sched_scanning) {
wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan to add "
"new network to scan filters");
wpa_supplicant_cancel_sched_scan(wpa_s);
}
- if (wpa_supplicant_fast_associate(wpa_s) != 1)
+ if (wpa_supplicant_fast_associate(wpa_s) != 1) {
+ wpa_s->scan_req = NORMAL_SCAN_REQ;
wpa_supplicant_req_scan(wpa_s, 0, 0);
+ }
}
}
int disconnected = 0;
if (ssid && ssid != wpa_s->current_ssid && wpa_s->current_ssid) {
- wpa_s->own_disconnect_req = 1;
+ if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
+ wpa_s->own_disconnect_req = 1;
wpa_supplicant_deauthenticate(
wpa_s, WLAN_REASON_DEAUTH_LEAVING);
disconnected = 1;
wpas_notify_network_enabled_changed(wpa_s, other_ssid);
}
- if (ssid && ssid == wpa_s->current_ssid && wpa_s->current_ssid) {
+ if (ssid && ssid == wpa_s->current_ssid && wpa_s->current_ssid &&
+ wpa_s->wpa_state >= WPA_AUTHENTICATING) {
/* We are already associated with the selected network */
wpa_printf(MSG_DEBUG, "Already associated with the "
"selected network - do nothing");
wpa_s->reassociate = 1;
if (wpa_s->connect_without_scan ||
- wpa_supplicant_fast_associate(wpa_s) != 1)
+ wpa_supplicant_fast_associate(wpa_s) != 1) {
+ wpa_s->scan_req = NORMAL_SCAN_REQ;
+ wpas_scan_reset_sched_scan(wpa_s);
wpa_supplicant_req_scan(wpa_s, 0, disconnected ? 100000 : 0);
+ }
if (ssid)
wpas_notify_network_selected(wpa_s, ssid);
if (ap_scan < 0 || ap_scan > 2)
return -1;
+ if (ap_scan == 2 && os_strcmp(wpa_s->driver->name, "nl80211") == 0) {
+ wpa_printf(MSG_INFO,
+ "Note: nl80211 driver interface is not designed to be used with ap_scan=2; this can result in connection failures");
+ }
+
#ifdef ANDROID
if (ap_scan == 2 && ap_scan != wpa_s->conf->ap_scan &&
wpa_s->wpa_state >= WPA_ASSOCIATING &&
struct wpa_global *global = wpa_s->global;
if (wpa_drivers[i]->global_init && global->drv_priv[i] == NULL) {
- global->drv_priv[i] = wpa_drivers[i]->global_init();
+ global->drv_priv[i] = wpa_drivers[i]->global_init(global);
if (global->drv_priv[i] == NULL) {
wpa_printf(MSG_ERROR, "Failed to initialize driver "
"'%s'", wpa_drivers[i]->name);
wpa_dbg(wpa_s, MSG_DEBUG, "RX EAPOL from " MACSTR, MAC2STR(src_addr));
wpa_hexdump(MSG_MSGDUMP, "RX EAPOL", buf, len);
+#ifdef CONFIG_TESTING_OPTIONS
+ if (wpa_s->ignore_auth_resp) {
+ wpa_printf(MSG_INFO, "RX EAPOL - ignore_auth_resp active!");
+ return;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
#ifdef CONFIG_PEERKEY
if (wpa_s->wpa_state > WPA_ASSOCIATED && wpa_s->current_ssid &&
wpa_s->current_ssid->peerkey &&
timeout = 70;
}
+#ifdef CONFIG_WPS
if (wpa_s->current_ssid && wpa_s->current_bss &&
(wpa_s->current_ssid->key_mgmt & WPA_KEY_MGMT_WPS) &&
eap_is_wps_pin_enrollee(&wpa_s->current_ssid->eap)) {
wps_ie = wpa_bss_get_vendor_ie_multi(
wpa_s->current_bss, WPS_IE_VENDOR_TYPE);
- if (wps_ie && !wps_is_selected_pbc_registrar(wps_ie))
+ if (wps_ie &&
+ !wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 1))
timeout = 10;
wpabuf_free(wps_ie);
}
+#endif /* CONFIG_WPS */
wpa_supplicant_req_auth_timeout(wpa_s, timeout, 0);
}
}
}
+ if (wpa_s->conf->ap_scan == 2 &&
+ os_strcmp(wpa_s->driver->name, "nl80211") == 0) {
+ wpa_printf(MSG_INFO,
+ "Note: nl80211 driver interface is not designed to be used with ap_scan=2; this can result in connection failures");
+ }
+
wpa_clear_keys(wpa_s, NULL);
/* Make sure that TKIP countermeasures are not left enabled (could
wpa_s->scan_interval = 5;
wpa_s->new_connection = 1;
wpa_s->parent = parent ? parent : wpa_s;
+ wpa_s->p2pdev = wpa_s->parent;
wpa_s->sched_scanning = 0;
+ dl_list_init(&wpa_s->bss_tmp_disallowed);
+ dl_list_init(&wpa_s->fils_hlp_req);
+
return wpa_s;
}
wpa_msg(wpa_s, MSG_DEBUG, "set_htcap, ht_mcs -:%s:-", ht_mcs);
for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) {
+ long v;
+
errno = 0;
- long v = strtol(tmp, &end, 16);
+ v = strtol(tmp, &end, 16);
+
if (errno == 0) {
wpa_msg(wpa_s, MSG_DEBUG,
"htcap value[%i]: %ld end: %p tmp: %p",
struct ieee80211_ht_capabilities *htcaps_mask,
int disabled)
{
- /* Masking these out disables HT40 */
- le16 msk = host_to_le16(HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET |
- HT_CAP_INFO_SHORT_GI40MHZ);
-
wpa_msg(wpa_s, MSG_DEBUG, "set_disable_ht40: %d", disabled);
- if (disabled)
- htcaps->ht_capabilities_info &= ~msk;
- else
- htcaps->ht_capabilities_info |= msk;
-
- htcaps_mask->ht_capabilities_info |= msk;
+ set_disable_ht40(htcaps, disabled);
+ set_disable_ht40(htcaps_mask, 0);
return 0;
}
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 */
#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);
}
-static int wpas_set_wowlan_triggers(struct wpa_supplicant *wpa_s,
- const struct wpa_driver_capa *capa)
+#ifdef CONFIG_FST
+
+static const u8 * wpas_fst_get_bssid_cb(void *ctx)
{
- struct wowlan_triggers *triggers;
- int ret = 0;
+ struct wpa_supplicant *wpa_s = ctx;
+
+ return (is_zero_ether_addr(wpa_s->bssid) ||
+ wpa_s->wpa_state != WPA_COMPLETED) ? NULL : wpa_s->bssid;
+}
+
+
+static void wpas_fst_get_channel_info_cb(void *ctx,
+ enum hostapd_hw_mode *hw_mode,
+ u8 *channel)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ if (wpa_s->current_bss) {
+ *hw_mode = ieee80211_freq_to_chan(wpa_s->current_bss->freq,
+ channel);
+ } else if (wpa_s->hw.num_modes) {
+ *hw_mode = wpa_s->hw.modes[0].mode;
+ } else {
+ WPA_ASSERT(0);
+ *hw_mode = 0;
+ }
+}
+
+
+static int wpas_fst_get_hw_modes(void *ctx, struct hostapd_hw_modes **modes)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ *modes = wpa_s->hw.modes;
+ return wpa_s->hw.num_modes;
+}
+
+
+static void wpas_fst_set_ies_cb(void *ctx, const struct wpabuf *fst_ies)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ wpa_hexdump_buf(MSG_DEBUG, "FST: Set IEs", fst_ies);
+ wpa_s->fst_ies = fst_ies;
+}
+
+
+static int wpas_fst_send_action_cb(void *ctx, const u8 *da, struct wpabuf *data)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ if (os_memcmp(wpa_s->bssid, da, ETH_ALEN) != 0) {
+ wpa_printf(MSG_INFO, "FST:%s:bssid=" MACSTR " != da=" MACSTR,
+ __func__, MAC2STR(wpa_s->bssid), MAC2STR(da));
+ return -1;
+ }
+ return wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+ wpa_s->own_addr, wpa_s->bssid,
+ wpabuf_head(data), wpabuf_len(data),
+ 0);
+}
+
+
+static const struct wpabuf * wpas_fst_get_mb_ie_cb(void *ctx, const u8 *addr)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ WPA_ASSERT(os_memcmp(wpa_s->bssid, addr, ETH_ALEN) == 0);
+ return wpa_s->received_mb_ies;
+}
+
+
+static void wpas_fst_update_mb_ie_cb(void *ctx, const u8 *addr,
+ const u8 *buf, size_t size)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ struct mb_ies_info info;
+
+ WPA_ASSERT(os_memcmp(wpa_s->bssid, addr, ETH_ALEN) == 0);
+
+ if (!mb_ies_info_by_ies(&info, buf, size)) {
+ wpabuf_free(wpa_s->received_mb_ies);
+ wpa_s->received_mb_ies = mb_ies_by_info(&info);
+ }
+}
+
+
+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;
+
+ *get_ctx = NULL;
+ if (!is_zero_ether_addr(wpa_s->bssid))
+ return (wpa_s->received_mb_ies || !mb_only) ?
+ wpa_s->bssid : NULL;
+ return NULL;
+}
+
+
+static const u8 * wpas_fst_get_peer_next(void *ctx,
+ struct fst_get_peer_ctx **get_ctx,
+ Boolean mb_only)
+{
+ return NULL;
+}
+
+void fst_wpa_supplicant_fill_iface_obj(struct wpa_supplicant *wpa_s,
+ struct fst_wpa_obj *iface_obj)
+{
+ iface_obj->ctx = wpa_s;
+ iface_obj->get_bssid = wpas_fst_get_bssid_cb;
+ iface_obj->get_channel_info = wpas_fst_get_channel_info_cb;
+ iface_obj->get_hw_modes = wpas_fst_get_hw_modes;
+ iface_obj->set_ies = wpas_fst_set_ies_cb;
+ iface_obj->send_action = wpas_fst_send_action_cb;
+ iface_obj->get_mb_ie = wpas_fst_get_mb_ie_cb;
+ iface_obj->update_mb_ie = wpas_fst_update_mb_ie_cb;
+ iface_obj->get_peer_first = wpas_fst_get_peer_first;
+ iface_obj->get_peer_next = wpas_fst_get_peer_next;
+}
+#endif /* CONFIG_FST */
+
+static int wpas_set_wowlan_triggers(struct wpa_supplicant *wpa_s,
+ const struct wpa_driver_capa *capa)
+{
+ struct wowlan_triggers *triggers;
+ int ret = 0;
if (!wpa_s->conf->wowlan_triggers)
return 0;
}
+enum wpa_radio_work_band wpas_freq_to_band(int freq)
+{
+ if (freq < 3000)
+ return BAND_2_4_GHZ;
+ if (freq > 50000)
+ return BAND_60_GHZ;
+ return BAND_5_GHZ;
+}
+
+
+unsigned int wpas_get_bands(struct wpa_supplicant *wpa_s, const int *freqs)
+{
+ int i;
+ unsigned int band = 0;
+
+ if (freqs) {
+ /* freqs are specified for the radio work */
+ for (i = 0; freqs[i]; i++)
+ band |= wpas_freq_to_band(freqs[i]);
+ } else {
+ /*
+ * freqs are not specified, implies all
+ * the supported freqs by HW
+ */
+ for (i = 0; i < wpa_s->hw.num_modes; i++) {
+ if (wpa_s->hw.modes[i].num_channels != 0) {
+ if (wpa_s->hw.modes[i].mode ==
+ HOSTAPD_MODE_IEEE80211B ||
+ wpa_s->hw.modes[i].mode ==
+ HOSTAPD_MODE_IEEE80211G)
+ band |= BAND_2_4_GHZ;
+ else if (wpa_s->hw.modes[i].mode ==
+ HOSTAPD_MODE_IEEE80211A)
+ band |= BAND_5_GHZ;
+ else if (wpa_s->hw.modes[i].mode ==
+ HOSTAPD_MODE_IEEE80211AD)
+ band |= BAND_60_GHZ;
+ else if (wpa_s->hw.modes[i].mode ==
+ HOSTAPD_MODE_IEEE80211ANY)
+ band = BAND_2_4_GHZ | BAND_5_GHZ |
+ BAND_60_GHZ;
+ }
+ }
+ }
+
+ return band;
+}
+
+
static struct wpa_radio * radio_add_interface(struct wpa_supplicant *wpa_s,
const char *rn)
{
}
#endif /* CONFIG_P2P */
+ if (work->started) {
+ work->wpa_s->radio->num_active_works--;
+ wpa_dbg(work->wpa_s, MSG_DEBUG,
+ "radio_work_free('%s'@%p: num_active_works --> %u",
+ work->type, work,
+ work->wpa_s->radio->num_active_works);
+ }
+
dl_list_del(&work->list);
os_free(work);
}
+static int radio_work_is_connect(struct wpa_radio_work *work)
+{
+ return os_strcmp(work->type, "sme-connect") == 0 ||
+ os_strcmp(work->type, "connect") == 0;
+}
+
+
+static int radio_work_is_scan(struct wpa_radio_work *work)
+{
+ return os_strcmp(work->type, "scan") == 0 ||
+ os_strcmp(work->type, "p2p-scan") == 0;
+}
+
+
+static struct wpa_radio_work * radio_work_get_next_work(struct wpa_radio *radio)
+{
+ struct wpa_radio_work *active_work = NULL;
+ struct wpa_radio_work *tmp;
+
+ /* Get the active work to know the type and band. */
+ dl_list_for_each(tmp, &radio->work, struct wpa_radio_work, list) {
+ if (tmp->started) {
+ active_work = tmp;
+ break;
+ }
+ }
+
+ if (!active_work) {
+ /* No active work, start one */
+ radio->num_active_works = 0;
+ dl_list_for_each(tmp, &radio->work, struct wpa_radio_work,
+ list) {
+ if (os_strcmp(tmp->type, "scan") == 0 &&
+ radio->external_scan_running &&
+ (((struct wpa_driver_scan_params *)
+ tmp->ctx)->only_new_results ||
+ tmp->wpa_s->clear_driver_scan_cache))
+ continue;
+ return tmp;
+ }
+ return NULL;
+ }
+
+ if (radio_work_is_connect(active_work)) {
+ /*
+ * If the active work is either connect or sme-connect,
+ * do not parallelize them with other radio works.
+ */
+ wpa_dbg(active_work->wpa_s, MSG_DEBUG,
+ "Do not parallelize radio work with %s",
+ active_work->type);
+ return NULL;
+ }
+
+ dl_list_for_each(tmp, &radio->work, struct wpa_radio_work, list) {
+ if (tmp->started)
+ continue;
+
+ /*
+ * If connect or sme-connect are enqueued, parallelize only
+ * those operations ahead of them in the queue.
+ */
+ if (radio_work_is_connect(tmp))
+ break;
+
+ /* Serialize parallel scan and p2p_scan operations on the same
+ * interface since the driver_nl80211 mechanism for tracking
+ * scan cookies does not yet have support for this. */
+ if (active_work->wpa_s == tmp->wpa_s &&
+ radio_work_is_scan(active_work) &&
+ radio_work_is_scan(tmp)) {
+ wpa_dbg(active_work->wpa_s, MSG_DEBUG,
+ "Do not start work '%s' when another work '%s' is already scheduled",
+ tmp->type, active_work->type);
+ continue;
+ }
+ /*
+ * Check that the radio works are distinct and
+ * on different bands.
+ */
+ if (os_strcmp(active_work->type, tmp->type) != 0 &&
+ (active_work->bands != tmp->bands)) {
+ /*
+ * If a scan has to be scheduled through nl80211 scan
+ * interface and if an external scan is already running,
+ * do not schedule the scan since it is likely to get
+ * rejected by kernel.
+ */
+ if (os_strcmp(tmp->type, "scan") == 0 &&
+ radio->external_scan_running &&
+ (((struct wpa_driver_scan_params *)
+ tmp->ctx)->only_new_results ||
+ tmp->wpa_s->clear_driver_scan_cache))
+ continue;
+
+ wpa_dbg(active_work->wpa_s, MSG_DEBUG,
+ "active_work:%s new_work:%s",
+ active_work->type, tmp->type);
+ return tmp;
+ }
+ }
+
+ /* Did not find a radio work to schedule in parallel. */
+ return NULL;
+}
+
+
static void radio_start_next_work(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_radio *radio = eloop_ctx;
struct wpa_supplicant *wpa_s;
work = dl_list_first(&radio->work, struct wpa_radio_work, list);
- if (work == NULL)
+ if (work == NULL) {
+ radio->num_active_works = 0;
return;
-
- if (work->started)
- return; /* already started and still in progress */
+ }
wpa_s = dl_list_first(&radio->ifaces, struct wpa_supplicant,
radio_list);
- if (wpa_s && wpa_s->radio->external_scan_running) {
- wpa_printf(MSG_DEBUG, "Delay radio work start until externally triggered scan completes");
- return;
+
+ if (!(wpa_s &&
+ wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS)) {
+ if (work->started)
+ return; /* already started and still in progress */
+
+ if (wpa_s && wpa_s->radio->external_scan_running) {
+ wpa_printf(MSG_DEBUG, "Delay radio work start until externally triggered scan completes");
+ return;
+ }
+ } else {
+ work = NULL;
+ if (radio->num_active_works < MAX_ACTIVE_WORKS) {
+ /* get the work to schedule next */
+ work = radio_work_get_next_work(radio);
+ }
+ if (!work)
+ return;
}
+ wpa_s = work->wpa_s;
os_get_reltime(&now);
os_reltime_sub(&now, &work->time, &diff);
- wpa_dbg(work->wpa_s, MSG_DEBUG, "Starting radio work '%s'@%p after %ld.%06ld second wait",
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Starting radio work '%s'@%p after %ld.%06ld second wait",
work->type, work, diff.sec, diff.usec);
work->started = 1;
work->time = now;
+ radio->num_active_works++;
+
work->cb(work, 0);
+
+ if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS) &&
+ radio->num_active_works < MAX_ACTIVE_WORKS)
+ radio_work_check_next(wpa_s);
}
void (*cb)(struct wpa_radio_work *work, int deinit),
void *ctx)
{
+ struct wpa_radio *radio = wpa_s->radio;
struct wpa_radio_work *work;
int was_empty;
work->cb = cb;
work->ctx = ctx;
+ if (freq)
+ work->bands = wpas_freq_to_band(freq);
+ else if (os_strcmp(type, "scan") == 0 ||
+ os_strcmp(type, "p2p-scan") == 0)
+ work->bands = wpas_get_bands(wpa_s,
+ ((struct wpa_driver_scan_params *)
+ ctx)->freqs);
+ else
+ work->bands = wpas_get_bands(wpa_s, NULL);
+
was_empty = dl_list_empty(&wpa_s->radio->work);
if (next)
dl_list_add(&wpa_s->radio->work, &work->list);
if (was_empty) {
wpa_dbg(wpa_s, MSG_DEBUG, "First radio work item in the queue - schedule start immediately");
radio_work_check_next(wpa_s);
+ } else if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS)
+ && radio->num_active_works < MAX_ACTIVE_WORKS) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Try to schedule a radio work (num_active_works=%u)",
+ radio->num_active_works);
+ radio_work_check_next(wpa_s);
}
return 0;
wpa_s->probe_resp_offloads = capa.probe_resp_offloads;
wpa_s->max_scan_ssids = capa.max_scan_ssids;
wpa_s->max_sched_scan_ssids = capa.max_sched_scan_ssids;
+ wpa_s->max_sched_scan_plans = capa.max_sched_scan_plans;
+ wpa_s->max_sched_scan_plan_interval =
+ capa.max_sched_scan_plan_interval;
+ wpa_s->max_sched_scan_plan_iterations =
+ capa.max_sched_scan_plan_iterations;
wpa_s->sched_scan_supported = capa.sched_scan_supported;
wpa_s->max_match_sets = capa.max_match_sets;
wpa_s->max_remain_on_chan = capa.max_remain_on_chan;
return -1;
}
+#ifdef CONFIG_FST
+ if (wpa_s->conf->fst_group_id) {
+ struct fst_iface_cfg cfg;
+ struct fst_wpa_obj iface_obj;
+
+ fst_wpa_supplicant_fill_iface_obj(wpa_s, &iface_obj);
+ os_strlcpy(cfg.group_id, wpa_s->conf->fst_group_id,
+ sizeof(cfg.group_id));
+ cfg.priority = wpa_s->conf->fst_priority;
+ cfg.llt = wpa_s->conf->fst_llt;
+
+ wpa_s->fst = fst_attach(wpa_s->ifname, wpa_s->own_addr,
+ &iface_obj, &cfg);
+ if (!wpa_s->fst) {
+ wpa_msg(wpa_s, MSG_ERROR,
+ "FST: Cannot attach iface %s to group %s",
+ wpa_s->ifname, cfg.group_id);
+ return -1;
+ }
+ }
+#endif /* CONFIG_FST */
+
if (wpas_wps_init(wpa_s))
return -1;
if (wpa_bss_init(wpa_s) < 0)
return -1;
+#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
+#ifdef CONFIG_MESH
+ dl_list_init(&wpa_s->mesh_external_pmksa_cache);
+#endif /* CONFIG_MESH */
+#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
+
/*
* Set Wake-on-WLAN triggers, if configured.
* Note: We don't restore/remove the triggers on shutdown (it doesn't
wpas_rrm_reset(wpa_s);
+ wpas_sched_scan_plans_set(wpa_s, wpa_s->conf->sched_scan_plans);
+
+#ifdef CONFIG_HS20
+ hs20_init(wpa_s);
+#endif /* CONFIG_HS20 */
+#ifdef CONFIG_MBO
+ 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;
}
iface = global->ifaces;
while (iface) {
+ if (iface->p2pdev == wpa_s)
+ iface->p2pdev = iface->parent;
if (iface == wpa_s || iface->parent != wpa_s) {
iface = iface->next;
continue;
wpas_ctrl_radio_work_flush(wpa_s);
radio_remove_interface(wpa_s);
+#ifdef CONFIG_FST
+ if (wpa_s->fst) {
+ fst_detach(wpa_s->fst);
+ wpa_s->fst = NULL;
+ }
+ if (wpa_s->received_mb_ies) {
+ wpabuf_free(wpa_s->received_mb_ies);
+ wpa_s->received_mb_ies = NULL;
+ }
+#endif /* CONFIG_FST */
+
if (wpa_s->drv_priv)
wpa_drv_deinit(wpa_s);
wpa_s->conf = NULL;
}
+ os_free(wpa_s->ssids_from_scan_req);
+
os_free(wpa_s);
}
+#ifdef CONFIG_MATCH_IFACE
+
+/**
+ * wpa_supplicant_match_iface - Match an interface description to a name
+ * @global: Pointer to global data from wpa_supplicant_init()
+ * @ifname: Name of the interface to match
+ * Returns: Pointer to the created interface description or %NULL on failure
+ */
+struct wpa_interface * wpa_supplicant_match_iface(struct wpa_global *global,
+ const char *ifname)
+{
+ int i;
+ struct wpa_interface *iface, *miface;
+
+ for (i = 0; i < global->params.match_iface_count; i++) {
+ miface = &global->params.match_ifaces[i];
+ if (!miface->ifname ||
+ fnmatch(miface->ifname, ifname, 0) == 0) {
+ iface = os_zalloc(sizeof(*iface));
+ if (!iface)
+ return NULL;
+ *iface = *miface;
+ iface->ifname = ifname;
+ return iface;
+ }
+ }
+
+ return NULL;
+}
+
+
+/**
+ * wpa_supplicant_match_existing - Match existing interfaces
+ * @global: Pointer to global data from wpa_supplicant_init()
+ * Returns: 0 on success, -1 on failure
+ */
+static int wpa_supplicant_match_existing(struct wpa_global *global)
+{
+ struct if_nameindex *ifi, *ifp;
+ struct wpa_supplicant *wpa_s;
+ struct wpa_interface *iface;
+
+ ifp = if_nameindex();
+ if (!ifp) {
+ wpa_printf(MSG_ERROR, "if_nameindex: %s", strerror(errno));
+ return -1;
+ }
+
+ for (ifi = ifp; ifi->if_name; ifi++) {
+ wpa_s = wpa_supplicant_get_iface(global, ifi->if_name);
+ if (wpa_s)
+ continue;
+ iface = wpa_supplicant_match_iface(global, ifi->if_name);
+ if (iface) {
+ wpa_s = wpa_supplicant_add_iface(global, iface, NULL);
+ os_free(iface);
+ if (wpa_s)
+ wpa_s->matched = 1;
+ }
+ }
+
+ if_freenameindex(ifp);
+ return 0;
+}
+
+#endif /* CONFIG_MATCH_IFACE */
+
+
/**
* wpa_supplicant_add_iface - Add a new network interface
* @global: Pointer to global data from wpa_supplicant_init()
if (wpa_s->global->p2p == NULL &&
!wpa_s->global->p2p_disabled && !wpa_s->conf->p2p_disabled &&
(wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) &&
- wpas_p2p_add_p2pdev_interface(wpa_s, iface->conf_p2p_dev) < 0) {
+ wpas_p2p_add_p2pdev_interface(
+ wpa_s, wpa_s->global->params.conf_p2p_dev) < 0) {
wpa_printf(MSG_INFO,
"P2P: Failed to enable P2P Device interface");
/* Try to continue without. P2P will be disabled. */
#ifdef CONFIG_MESH
unsigned int mesh_if_created = wpa_s->mesh_if_created;
char *ifname = NULL;
+ struct wpa_supplicant *parent = wpa_s->parent;
#endif /* CONFIG_MESH */
/* Remove interface from the global list of interfaces */
#ifdef CONFIG_MESH
if (mesh_if_created) {
- wpa_drv_if_remove(global->ifaces, WPA_IF_MESH, ifname);
+ wpa_drv_if_remove(parent, WPA_IF_MESH, ifname);
os_free(ifname);
}
#endif /* CONFIG_MESH */
#endif /* CONFIG_NO_WPA_MSG */
+#ifndef WPA_SUPPLICANT_CLEANUP_INTERVAL
+#define WPA_SUPPLICANT_CLEANUP_INTERVAL 10
+#endif /* WPA_SUPPLICANT_CLEANUP_INTERVAL */
+
+/* Periodic cleanup tasks */
+static void wpas_periodic(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_global *global = eloop_ctx;
+ struct wpa_supplicant *wpa_s;
+
+ eloop_register_timeout(WPA_SUPPLICANT_CLEANUP_INTERVAL, 0,
+ wpas_periodic, global, NULL);
+
+#ifdef CONFIG_P2P
+ if (global->p2p)
+ p2p_expire_peers(global->p2p);
+#endif /* CONFIG_P2P */
+
+ for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+ wpa_bss_flush_by_age(wpa_s, wpa_s->conf->bss_expiration_age);
+#ifdef CONFIG_AP
+ ap_periodic(wpa_s);
+#endif /* CONFIG_AP */
+ }
+}
+
+
/**
* wpa_supplicant_init - Initialize %wpa_supplicant
* @params: Parameters for %wpa_supplicant
if (params->override_ctrl_interface)
global->params.override_ctrl_interface =
os_strdup(params->override_ctrl_interface);
+#ifdef CONFIG_MATCH_IFACE
+ global->params.match_iface_count = params->match_iface_count;
+ if (params->match_iface_count) {
+ global->params.match_ifaces =
+ os_calloc(params->match_iface_count,
+ sizeof(struct wpa_interface));
+ os_memcpy(global->params.match_ifaces,
+ params->match_ifaces,
+ params->match_iface_count *
+ sizeof(struct wpa_interface));
+ }
+#endif /* CONFIG_MATCH_IFACE */
+#ifdef CONFIG_P2P
+ if (params->conf_p2p_dev)
+ global->params.conf_p2p_dev =
+ os_strdup(params->conf_p2p_dev);
+#endif /* CONFIG_P2P */
wpa_debug_level = global->params.wpa_debug_level =
params->wpa_debug_level;
wpa_debug_show_keys = global->params.wpa_debug_show_keys =
}
#endif /* CONFIG_WIFI_DISPLAY */
+ eloop_register_timeout(WPA_SUPPLICANT_CLEANUP_INTERVAL, 0,
+ wpas_periodic, global, NULL);
+
return global;
}
struct wpa_supplicant *wpa_s;
if (global->params.daemonize &&
- wpa_supplicant_daemon(global->params.pid_file))
+ (wpa_supplicant_daemon(global->params.pid_file) ||
+ eloop_sock_requeue()))
return -1;
+#ifdef CONFIG_MATCH_IFACE
+ if (wpa_supplicant_match_existing(global))
+ return -1;
+#endif
+
if (global->params.wait_for_monitor) {
for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next)
- if (wpa_s->ctrl_iface)
+ if (wpa_s->ctrl_iface && !wpa_s->p2p_mgmt)
wpa_supplicant_ctrl_iface_wait(
wpa_s->ctrl_iface);
}
if (global == NULL)
return;
+ eloop_cancel_timeout(wpas_periodic, global, NULL);
+
#ifdef CONFIG_WIFI_DISPLAY
wifi_display_deinit(global);
#endif /* CONFIG_WIFI_DISPLAY */
os_free(global->params.ctrl_interface_group);
os_free(global->params.override_driver);
os_free(global->params.override_ctrl_interface);
+#ifdef CONFIG_MATCH_IFACE
+ os_free(global->params.match_ifaces);
+#endif /* CONFIG_MATCH_IFACE */
+#ifdef CONFIG_P2P
+ os_free(global->params.conf_p2p_dev);
+#endif /* CONFIG_P2P */
os_free(global->p2p_disallow_freq.range);
os_free(global->p2p_go_avoid_freq.range);
if (wpa_s->conf->changed_parameters & CFG_CHANGED_EXT_PW_BACKEND)
wpas_init_ext_pw(wpa_s);
+ if (wpa_s->conf->changed_parameters & CFG_CHANGED_SCHED_SCAN_PLANS)
+ wpas_sched_scan_plans_set(wpa_s, wpa_s->conf->sched_scan_plans);
+
#ifdef CONFIG_WPS
wpas_wps_update_config(wpa_s);
#endif /* CONFIG_WPS */
case WPA_CTRL_REQ_SIM:
str_clear_free(eap->external_sim_resp);
eap->external_sim_resp = os_strdup(value);
+ eap->pending_req_sim = 0;
break;
case WPA_CTRL_REQ_PSK_PASSPHRASE:
if (wpa_config_set(ssid, "psk", value, 0) < 0)
if (wpa_s->wpa_state == WPA_SCANNING && !wpa_s->scanning)
wpa_supplicant_req_scan(wpa_s, 0, 0);
break;
+ case WPA_CTRL_REQ_EXT_CERT_CHECK:
+ if (eap->pending_ext_cert_check != PENDING_CHECK)
+ return -1;
+ if (os_strcmp(value, "good") == 0)
+ eap->pending_ext_cert_check = EXT_CERT_CHECK_GOOD;
+ else if (os_strcmp(value, "bad") == 0)
+ eap->pending_ext_cert_check = EXT_CERT_CHECK_BAD;
+ else
+ return -1;
+ break;
default:
wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown field '%s'", field);
return -1;
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;
}
}
+/**
+ * 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)
continue;
if (ifs->current_ssid->mode == WPAS_MODE_AP ||
- ifs->current_ssid->mode == WPAS_MODE_P2P_GO)
+ ifs->current_ssid->mode == WPAS_MODE_P2P_GO ||
+ ifs->current_ssid->mode == WPAS_MODE_MESH)
freq = ifs->current_ssid->frequency;
else if (wpa_drv_get_bssid(ifs, bssid) == 0)
freq = ifs->assoc_freq;
freqs_data[idx++].freq = freq;
if (ifs->current_ssid->mode == WPAS_MODE_INFRA) {
- freqs_data[i].flags = ifs->current_ssid->p2p_group ?
+ freqs_data[i].flags |= ifs->current_ssid->p2p_group ?
WPA_FREQ_USED_BY_P2P_CLIENT :
WPA_FREQ_USED_BY_INFRA_STATION;
}
}
-static void wpas_rrm_neighbor_rep_timeout_handler(void *data, void *user_ctx)
+struct wpa_supplicant *
+wpas_vendor_elem(struct wpa_supplicant *wpa_s, enum wpa_vendor_elem_frame frame)
{
- struct rrm_data *rrm = data;
-
- if (!rrm->notify_neighbor_rep) {
- wpa_printf(MSG_ERROR,
- "RRM: Unexpected neighbor report timeout");
- return;
+ switch (frame) {
+#ifdef CONFIG_P2P
+ case VENDOR_ELEM_PROBE_REQ_P2P:
+ case VENDOR_ELEM_PROBE_RESP_P2P:
+ case VENDOR_ELEM_PROBE_RESP_P2P_GO:
+ case VENDOR_ELEM_BEACON_P2P_GO:
+ case VENDOR_ELEM_P2P_PD_REQ:
+ case VENDOR_ELEM_P2P_PD_RESP:
+ case VENDOR_ELEM_P2P_GO_NEG_REQ:
+ case VENDOR_ELEM_P2P_GO_NEG_RESP:
+ case VENDOR_ELEM_P2P_GO_NEG_CONF:
+ case VENDOR_ELEM_P2P_INV_REQ:
+ case VENDOR_ELEM_P2P_INV_RESP:
+ case VENDOR_ELEM_P2P_ASSOC_REQ:
+ case VENDOR_ELEM_P2P_ASSOC_RESP:
+ return wpa_s->p2pdev;
+#endif /* CONFIG_P2P */
+ default:
+ return wpa_s;
}
-
- wpa_printf(MSG_DEBUG, "RRM: Notifying neighbor report - NONE");
- rrm->notify_neighbor_rep(rrm->neighbor_rep_cb_ctx, NULL);
-
- rrm->notify_neighbor_rep = NULL;
- rrm->neighbor_rep_cb_ctx = NULL;
}
-/*
- * wpas_rrm_reset - Clear and reset all RRM data in wpa_supplicant
- * @wpa_s: Pointer to wpa_supplicant
- */
-void wpas_rrm_reset(struct wpa_supplicant *wpa_s)
-{
- wpa_s->rrm.rrm_used = 0;
-
- eloop_cancel_timeout(wpas_rrm_neighbor_rep_timeout_handler, &wpa_s->rrm,
- NULL);
- if (wpa_s->rrm.notify_neighbor_rep)
- wpas_rrm_neighbor_rep_timeout_handler(&wpa_s->rrm, NULL);
- wpa_s->rrm.next_neighbor_rep_token = 1;
-}
-
-
-/*
- * wpas_rrm_process_neighbor_rep - Handle incoming neighbor report
- * @wpa_s: Pointer to wpa_supplicant
- * @report: Neighbor report buffer, prefixed by a 1-byte dialog token
- * @report_len: Length of neighbor report buffer
- */
-void wpas_rrm_process_neighbor_rep(struct wpa_supplicant *wpa_s,
- const u8 *report, size_t report_len)
+void wpas_vendor_elem_update(struct wpa_supplicant *wpa_s)
{
- struct wpabuf *neighbor_rep;
-
- wpa_hexdump(MSG_DEBUG, "RRM: New Neighbor Report", report, report_len);
- if (report_len < 1)
- return;
+ unsigned int i;
+ char buf[30];
- if (report[0] != wpa_s->rrm.next_neighbor_rep_token - 1) {
- wpa_printf(MSG_DEBUG,
- "RRM: Discarding neighbor report with token %d (expected %d)",
- report[0], wpa_s->rrm.next_neighbor_rep_token - 1);
- return;
- }
+ wpa_printf(MSG_DEBUG, "Update vendor elements");
- eloop_cancel_timeout(wpas_rrm_neighbor_rep_timeout_handler, &wpa_s->rrm,
- NULL);
+ for (i = 0; i < NUM_VENDOR_ELEM_FRAMES; i++) {
+ if (wpa_s->vendor_elem[i]) {
+ int res;
- if (!wpa_s->rrm.notify_neighbor_rep) {
- wpa_printf(MSG_ERROR, "RRM: Unexpected neighbor report");
- return;
+ res = os_snprintf(buf, sizeof(buf), "frame[%u]", i);
+ if (!os_snprintf_error(sizeof(buf), res)) {
+ wpa_hexdump_buf(MSG_DEBUG, buf,
+ wpa_s->vendor_elem[i]);
+ }
+ }
}
- /* skipping the first byte, which is only an id (dialog token) */
- neighbor_rep = wpabuf_alloc(report_len - 1);
- if (neighbor_rep == NULL)
- return;
- wpabuf_put_data(neighbor_rep, report + 1, report_len - 1);
- wpa_printf(MSG_DEBUG, "RRM: Notifying neighbor report (token = %d)",
- report[0]);
- wpa_s->rrm.notify_neighbor_rep(wpa_s->rrm.neighbor_rep_cb_ctx,
- neighbor_rep);
- wpa_s->rrm.notify_neighbor_rep = NULL;
- wpa_s->rrm.neighbor_rep_cb_ctx = NULL;
+#ifdef CONFIG_P2P
+ if (wpa_s->parent == wpa_s &&
+ wpa_s->global->p2p &&
+ !wpa_s->global->p2p_disabled)
+ p2p_set_vendor_elems(wpa_s->global->p2p, wpa_s->vendor_elem);
+#endif /* CONFIG_P2P */
}
-#if defined(__CYGWIN__) || defined(CONFIG_NATIVE_WINDOWS)
-/* Workaround different, undefined for Windows, error codes used here */
-#define ENOTCONN -1
-#define EOPNOTSUPP -1
-#define ECANCELED -1
-#endif
-
-/**
- * wpas_rrm_send_neighbor_rep_request - Request a neighbor report from our AP
- * @wpa_s: Pointer to wpa_supplicant
- * @ssid: if not null, this is sent in the request. Otherwise, no SSID IE
- * is sent in the request.
- * @cb: Callback function to be called once the requested report arrives, or
- * timed out after RRM_NEIGHBOR_REPORT_TIMEOUT seconds.
- * In the former case, 'neighbor_rep' is a newly allocated wpabuf, and it's
- * the requester's responsibility to free it.
- * In the latter case NULL will be sent in 'neighbor_rep'.
- * @cb_ctx: Context value to send the callback function
- * Returns: 0 in case of success, negative error code otherwise
- *
- * In case there is a previous request which has not been answered yet, the
- * new request fails. The caller may retry after RRM_NEIGHBOR_REPORT_TIMEOUT.
- * Request must contain a callback function.
- */
-int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s,
- const struct wpa_ssid *ssid,
- void (*cb)(void *ctx,
- struct wpabuf *neighbor_rep),
- void *cb_ctx)
+int wpas_vendor_elem_remove(struct wpa_supplicant *wpa_s, int frame,
+ const u8 *elem, size_t len)
{
- struct wpabuf *buf;
- const u8 *rrm_ie;
+ u8 *ie, *end;
- if (wpa_s->wpa_state != WPA_COMPLETED || wpa_s->current_ssid == NULL) {
- wpa_printf(MSG_DEBUG, "RRM: No connection, no RRM.");
- return -ENOTCONN;
- }
+ ie = wpabuf_mhead_u8(wpa_s->vendor_elem[frame]);
+ end = ie + wpabuf_len(wpa_s->vendor_elem[frame]);
- if (!wpa_s->rrm.rrm_used) {
- wpa_printf(MSG_DEBUG, "RRM: No RRM in current connection.");
- return -EOPNOTSUPP;
- }
+ for (; ie + 1 < end; ie += 2 + ie[1]) {
+ if (ie + len > end)
+ break;
+ if (os_memcmp(ie, elem, len) != 0)
+ continue;
- rrm_ie = wpa_bss_get_ie(wpa_s->current_bss,
- WLAN_EID_RRM_ENABLED_CAPABILITIES);
- if (!rrm_ie || !(wpa_s->current_bss->caps & IEEE80211_CAP_RRM) ||
- !(rrm_ie[2] & WLAN_RRM_CAPS_NEIGHBOR_REPORT)) {
- wpa_printf(MSG_DEBUG,
- "RRM: No network support for Neighbor Report.");
- return -EOPNOTSUPP;
+ if (wpabuf_len(wpa_s->vendor_elem[frame]) == len) {
+ wpabuf_free(wpa_s->vendor_elem[frame]);
+ wpa_s->vendor_elem[frame] = NULL;
+ } else {
+ os_memmove(ie, ie + len, end - (ie + len));
+ wpa_s->vendor_elem[frame]->used -= len;
+ }
+ wpas_vendor_elem_update(wpa_s);
+ return 0;
}
- if (!cb) {
- wpa_printf(MSG_DEBUG,
- "RRM: Neighbor Report request must provide a callback.");
- return -EINVAL;
- }
+ return -1;
+}
- /* Refuse if there's a live request */
- if (wpa_s->rrm.notify_neighbor_rep) {
- wpa_printf(MSG_DEBUG,
- "RRM: Currently handling previous Neighbor Report.");
- return -EBUSY;
- }
- /* 3 = action category + action code + dialog token */
- buf = wpabuf_alloc(3 + (ssid ? 2 + ssid->ssid_len : 0));
- if (buf == NULL) {
- wpa_printf(MSG_DEBUG,
- "RRM: Failed to allocate Neighbor Report Request");
- return -ENOMEM;
+struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes,
+ u16 num_modes, enum hostapd_hw_mode mode)
+{
+ u16 i;
+
+ for (i = 0; i < num_modes; i++) {
+ if (modes[i].mode == mode)
+ return &modes[i];
}
- wpa_printf(MSG_DEBUG, "RRM: Neighbor report request (for %s), token=%d",
- (ssid ? wpa_ssid_txt(ssid->ssid, ssid->ssid_len) : ""),
- wpa_s->rrm.next_neighbor_rep_token);
+ return NULL;
+}
- wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
- wpabuf_put_u8(buf, WLAN_RRM_NEIGHBOR_REPORT_REQUEST);
- wpabuf_put_u8(buf, wpa_s->rrm.next_neighbor_rep_token);
- if (ssid) {
- wpabuf_put_u8(buf, WLAN_EID_SSID);
- wpabuf_put_u8(buf, ssid->ssid_len);
- wpabuf_put_data(buf, ssid->ssid, ssid->ssid_len);
- }
- wpa_s->rrm.next_neighbor_rep_token++;
+static struct
+wpa_bss_tmp_disallowed * wpas_get_disallowed_bss(struct wpa_supplicant *wpa_s,
+ const u8 *bssid)
+{
+ struct wpa_bss_tmp_disallowed *bss;
- if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
- wpa_s->own_addr, wpa_s->bssid,
- wpabuf_head(buf), wpabuf_len(buf), 0) < 0) {
- wpa_printf(MSG_DEBUG,
- "RRM: Failed to send Neighbor Report Request");
- wpabuf_free(buf);
- return -ECANCELED;
+ dl_list_for_each(bss, &wpa_s->bss_tmp_disallowed,
+ struct wpa_bss_tmp_disallowed, list) {
+ if (os_memcmp(bssid, bss->bssid, ETH_ALEN) == 0)
+ return bss;
}
- wpa_s->rrm.neighbor_rep_cb_ctx = cb_ctx;
- wpa_s->rrm.notify_neighbor_rep = cb;
- eloop_register_timeout(RRM_NEIGHBOR_REPORT_TIMEOUT, 0,
- wpas_rrm_neighbor_rep_timeout_handler,
- &wpa_s->rrm, NULL);
-
- wpabuf_free(buf);
- return 0;
+ return NULL;
}
-void wpas_rrm_handle_link_measurement_request(struct wpa_supplicant *wpa_s,
- const u8 *src,
- const u8 *frame, size_t len,
- int rssi)
+void wpa_bss_tmp_disallow(struct wpa_supplicant *wpa_s, const u8 *bssid,
+ unsigned int sec)
{
- struct wpabuf *buf;
- const struct rrm_link_measurement_request *req;
- struct rrm_link_measurement_report report;
-
- if (wpa_s->wpa_state != WPA_COMPLETED) {
- wpa_printf(MSG_INFO,
- "RRM: Ignoring link measurement request. Not associated");
- return;
- }
+ struct wpa_bss_tmp_disallowed *bss;
+ struct os_reltime until;
- if (!wpa_s->rrm.rrm_used) {
- wpa_printf(MSG_INFO,
- "RRM: Ignoring link measurement request. Not RRM network");
- return;
- }
+ os_get_reltime(&until);
+ until.sec += sec;
- if (!(wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_TX_POWER_INSERTION)) {
- wpa_printf(MSG_INFO,
- "RRM: Measurement report failed. TX power insertion not supported");
+ bss = wpas_get_disallowed_bss(wpa_s, bssid);
+ if (bss) {
+ bss->disallowed_until = until;
return;
}
- req = (const struct rrm_link_measurement_request *) frame;
- if (len < sizeof(*req)) {
- wpa_printf(MSG_INFO,
- "RRM: Link measurement report failed. Request too short");
+ bss = os_malloc(sizeof(*bss));
+ if (!bss) {
+ wpa_printf(MSG_DEBUG,
+ "Failed to allocate memory for temp disallow BSS");
return;
}
- os_memset(&report, 0, sizeof(report));
- report.tpc.eid = WLAN_EID_TPC_REPORT;
- report.tpc.len = 2;
- report.rsni = 255; /* 255 indicates that RSNI is not available */
- report.dialog_token = req->dialog_token;
+ bss->disallowed_until = until;
+ os_memcpy(bss->bssid, bssid, ETH_ALEN);
+ dl_list_add(&wpa_s->bss_tmp_disallowed, &bss->list);
+}
- /*
- * It's possible to estimate RCPI based on RSSI in dBm. This
- * calculation will not reflect the correct value for high rates,
- * but it's good enough for Action frames which are transmitted
- * with up to 24 Mbps rates.
- */
- if (!rssi)
- report.rcpi = 255; /* not available */
- else if (rssi < -110)
- report.rcpi = 0;
- else if (rssi > 0)
- report.rcpi = 220;
- else
- report.rcpi = (rssi + 110) * 2;
- /* action_category + action_code */
- buf = wpabuf_alloc(2 + sizeof(report));
- if (buf == NULL) {
- wpa_printf(MSG_ERROR,
- "RRM: Link measurement report failed. Buffer allocation failed");
- return;
- }
+int wpa_is_bss_tmp_disallowed(struct wpa_supplicant *wpa_s, const u8 *bssid)
+{
+ struct wpa_bss_tmp_disallowed *bss = NULL, *tmp, *prev;
+ struct os_reltime now, age;
- wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
- wpabuf_put_u8(buf, WLAN_RRM_LINK_MEASUREMENT_REPORT);
- wpabuf_put_data(buf, &report, sizeof(report));
- wpa_hexdump(MSG_DEBUG, "RRM: Link measurement report:",
- wpabuf_head(buf), wpabuf_len(buf));
+ os_get_reltime(&now);
- 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: Link measurement report failed. Send action failed");
+ dl_list_for_each_safe(tmp, prev, &wpa_s->bss_tmp_disallowed,
+ struct wpa_bss_tmp_disallowed, list) {
+ if (!os_reltime_before(&now, &tmp->disallowed_until)) {
+ /* This BSS is not disallowed anymore */
+ dl_list_del(&tmp->list);
+ os_free(tmp);
+ continue;
+ }
+ if (os_memcmp(bssid, tmp->bssid, ETH_ALEN) == 0) {
+ bss = tmp;
+ break;
+ }
}
- wpabuf_free(buf);
+ if (!bss)
+ return 0;
+
+ os_reltime_sub(&bss->disallowed_until, &now, &age);
+ wpa_printf(MSG_DEBUG,
+ "BSS " MACSTR " disabled for %ld.%0ld seconds",
+ MAC2STR(bss->bssid), age.sec, age.usec);
+ return 1;
}