diff options
Diffstat (limited to 'contrib/wpa/wpa_supplicant/wpa_supplicant.c')
-rw-r--r-- | contrib/wpa/wpa_supplicant/wpa_supplicant.c | 1644 |
1 files changed, 1380 insertions, 264 deletions
diff --git a/contrib/wpa/wpa_supplicant/wpa_supplicant.c b/contrib/wpa/wpa_supplicant/wpa_supplicant.c index 31b1bc8e5c80..cf990f5a8f85 100644 --- a/contrib/wpa/wpa_supplicant/wpa_supplicant.c +++ b/contrib/wpa/wpa_supplicant/wpa_supplicant.c @@ -1,6 +1,6 @@ /* * WPA Supplicant - * Copyright (c) 2003-2019, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2022, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -41,9 +41,10 @@ #include "common/hw_features_common.h" #include "common/gas_server.h" #include "common/dpp.h" +#include "common/ptksa_cache.h" #include "p2p/p2p.h" #include "fst/fst.h" -#include "blacklist.h" +#include "bssid_ignore.h" #include "wpas_glue.h" #include "wps_supplicant.h" #include "ibss_rsn.h" @@ -70,7 +71,7 @@ const char *const wpa_supplicant_version = "wpa_supplicant v" VERSION_STR "\n" -"Copyright (c) 2003-2019, Jouni Malinen <j@w1.fi> and contributors"; +"Copyright (c) 2003-2022, 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" @@ -125,8 +126,12 @@ static void wpa_bss_tmp_disallow_timeout(void *eloop_ctx, void *timeout_ctx); #if defined(CONFIG_FILS) && defined(IEEE8021X_EAPOL) static void wpas_update_fils_connect_params(struct wpa_supplicant *wpa_s); #endif /* CONFIG_FILS && IEEE8021X_EAPOL */ +#ifdef CONFIG_OWE +static void wpas_update_owe_connect_params(struct wpa_supplicant *wpa_s); +#endif /* CONFIG_OWE */ +#ifdef CONFIG_WEP /* Configure default/group WEP keys for static WEP */ int wpa_set_wep_keys(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { @@ -139,11 +144,15 @@ int wpa_set_wep_keys(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) set = 1; wpa_drv_set_key(wpa_s, WPA_ALG_WEP, NULL, i, i == ssid->wep_tx_keyidx, NULL, 0, - ssid->wep_key[i], ssid->wep_key_len[i]); + ssid->wep_key[i], ssid->wep_key_len[i], + i == ssid->wep_tx_keyidx ? + KEY_FLAG_GROUP_RX_TX_DEFAULT : + KEY_FLAG_GROUP_RX_TX); } return set; } +#endif /* CONFIG_WEP */ int wpa_supplicant_set_wpa_none_key(struct wpa_supplicant *wpa_s, @@ -197,7 +206,8 @@ int wpa_supplicant_set_wpa_none_key(struct wpa_supplicant *wpa_s, /* TODO: should actually remember the previously used seq#, both for TX * and RX from each STA.. */ - ret = wpa_drv_set_key(wpa_s, alg, NULL, 0, 1, seq, 6, key, keylen); + ret = wpa_drv_set_key(wpa_s, alg, NULL, 0, 1, seq, 6, key, keylen, + KEY_FLAG_GROUP_RX_TX_DEFAULT); os_memset(key, 0, sizeof(key)); return ret; } @@ -213,7 +223,7 @@ static void wpa_supplicant_timeout(void *eloop_ctx, void *timeout_ctx) bssid = wpa_s->pending_bssid; wpa_msg(wpa_s, MSG_INFO, "Authentication with " MACSTR " timed out.", MAC2STR(bssid)); - wpa_blacklist_add(wpa_s, bssid); + wpa_bssid_ignore_add(wpa_s, bssid); wpa_sm_notify_disassoc(wpa_s->wpa); wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); wpa_s->reassociate = 1; @@ -281,7 +291,7 @@ void wpa_supplicant_cancel_auth_timeout(struct wpa_supplicant *wpa_s) { wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling authentication timeout"); eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL); - wpa_blacklist_del(wpa_s, wpa_s->bssid); + wpa_bssid_ignore_del(wpa_s, wpa_s->bssid); os_free(wpa_s->last_con_fail_realm); wpa_s->last_con_fail_realm = NULL; wpa_s->last_con_fail_realm_len = 0; @@ -310,14 +320,14 @@ void wpa_supplicant_initiate_eapol(struct wpa_supplicant *wpa_s) * per-BSSID EAPOL authentication. */ eapol_sm_notify_portControl(wpa_s->eapol, ForceAuthorized); - eapol_sm_notify_eap_success(wpa_s->eapol, TRUE); - eapol_sm_notify_eap_fail(wpa_s->eapol, FALSE); + eapol_sm_notify_eap_success(wpa_s->eapol, true); + eapol_sm_notify_eap_fail(wpa_s->eapol, false); return; } #endif /* CONFIG_IBSS_RSN */ - eapol_sm_notify_eap_success(wpa_s->eapol, FALSE); - eapol_sm_notify_eap_fail(wpa_s->eapol, FALSE); + eapol_sm_notify_eap_success(wpa_s->eapol, false); + eapol_sm_notify_eap_fail(wpa_s->eapol, false); if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE || wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) @@ -389,7 +399,9 @@ void wpa_supplicant_initiate_eapol(struct wpa_supplicant *wpa_s) void wpa_supplicant_set_non_wpa_policy(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { +#ifdef CONFIG_WEP int i; +#endif /* CONFIG_WEP */ if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) wpa_s->key_mgmt = WPA_KEY_MGMT_WPS; @@ -399,11 +411,15 @@ void wpa_supplicant_set_non_wpa_policy(struct wpa_supplicant *wpa_s, wpa_s->key_mgmt = WPA_KEY_MGMT_NONE; wpa_sm_set_ap_wpa_ie(wpa_s->wpa, NULL, 0); wpa_sm_set_ap_rsn_ie(wpa_s->wpa, NULL, 0); + wpa_sm_set_ap_rsnxe(wpa_s->wpa, NULL, 0); wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0); + wpa_sm_set_assoc_rsnxe(wpa_s->wpa, NULL, 0); + wpa_s->rsnxe_len = 0; wpa_s->pairwise_cipher = WPA_CIPHER_NONE; wpa_s->group_cipher = WPA_CIPHER_NONE; wpa_s->mgmt_group_cipher = 0; +#ifdef CONFIG_WEP for (i = 0; i < NUM_WEP_KEYS; i++) { if (ssid->wep_key_len[i] > 5) { wpa_s->pairwise_cipher = WPA_CIPHER_WEP104; @@ -415,16 +431,15 @@ void wpa_supplicant_set_non_wpa_policy(struct wpa_supplicant *wpa_s, break; } } +#endif /* CONFIG_WEP */ wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_RSN_ENABLED, 0); wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_KEY_MGMT, wpa_s->key_mgmt); wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_PAIRWISE, wpa_s->pairwise_cipher); wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_GROUP, wpa_s->group_cipher); -#ifdef CONFIG_IEEE80211W wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MGMT_GROUP, wpa_s->mgmt_group_cipher); -#endif /* CONFIG_IEEE80211W */ pmksa_cache_clear_current(wpa_s->wpa); } @@ -446,16 +461,22 @@ void free_hw_features(struct wpa_supplicant *wpa_s) } +static void remove_bss_tmp_disallowed_entry(struct wpa_supplicant *wpa_s, + struct wpa_bss_tmp_disallowed *bss) +{ + eloop_cancel_timeout(wpa_bss_tmp_disallow_timeout, wpa_s, bss); + dl_list_del(&bss->list); + os_free(bss); +} + + 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) { - eloop_cancel_timeout(wpa_bss_tmp_disallow_timeout, wpa_s, bss); - dl_list_del(&bss->list); - os_free(bss); - } + struct wpa_bss_tmp_disallowed, list) + remove_bss_tmp_disallowed_entry(wpa_s, bss); } @@ -472,6 +493,31 @@ void wpas_flush_fils_hlp_req(struct wpa_supplicant *wpa_s) } +void wpas_clear_disabled_interface(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + + if (wpa_s->wpa_state != WPA_INTERFACE_DISABLED) + return; + wpa_dbg(wpa_s, MSG_DEBUG, "Clear cached state on disabled interface"); + wpa_bss_flush(wpa_s); +} + + +#ifdef CONFIG_TESTING_OPTIONS +void wpas_clear_driver_signal_override(struct wpa_supplicant *wpa_s) +{ + struct driver_signal_override *dso; + + while ((dso = dl_list_first(&wpa_s->drv_signal_override, + struct driver_signal_override, list))) { + dl_list_del(&dso->list); + os_free(dso); + } +} +#endif /* CONFIG_TESTING_OPTIONS */ + + static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) { int i; @@ -495,6 +541,15 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) wpa_s->get_pref_freq_list_override = NULL; wpabuf_free(wpa_s->last_assoc_req_wpa_ie); wpa_s->last_assoc_req_wpa_ie = NULL; + os_free(wpa_s->extra_sae_rejected_groups); + wpa_s->extra_sae_rejected_groups = NULL; + wpabuf_free(wpa_s->rsne_override_eapol); + wpa_s->rsne_override_eapol = NULL; + wpabuf_free(wpa_s->rsnxe_override_assoc); + wpa_s->rsnxe_override_assoc = NULL; + wpabuf_free(wpa_s->rsnxe_override_eapol); + wpa_s->rsnxe_override_eapol = NULL; + wpas_clear_driver_signal_override(wpa_s); #endif /* CONFIG_TESTING_OPTIONS */ if (wpa_s->conf != NULL) { @@ -525,9 +580,15 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) wmm_ac_clear_saved_tspecs(wpa_s); pmksa_candidate_free(wpa_s->wpa); + ptksa_cache_deinit(wpa_s->ptksa); + wpa_s->ptksa = NULL; wpa_sm_deinit(wpa_s->wpa); wpa_s->wpa = NULL; - wpa_blacklist_clear(wpa_s); + wpa_bssid_ignore_clear(wpa_s); + +#ifdef CONFIG_PASN + wpas_pasn_auth_stop(wpa_s); +#endif /* CONFIG_PASN */ wpa_bss_deinit(wpa_s); @@ -541,6 +602,7 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) #endif /* CONFIG_DELAYED_MIC_ERROR_REPORT */ eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL); + eloop_cancel_timeout(wpas_clear_disabled_interface, wpa_s, NULL); wpas_wps_deinit(wpa_s); @@ -677,6 +739,12 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) dpp_global_deinit(wpa_s->dpp); wpa_s->dpp = NULL; #endif /* CONFIG_DPP */ + +#ifdef CONFIG_PASN + wpas_pasn_auth_stop(wpa_s); +#endif /* CONFIG_PASN */ + wpas_scs_deinit(wpa_s); + wpas_dscp_deinit(wpa_s); } @@ -690,25 +758,24 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) */ void wpa_clear_keys(struct wpa_supplicant *wpa_s, const u8 *addr) { - int i, max; - -#ifdef CONFIG_IEEE80211W - max = 6; -#else /* CONFIG_IEEE80211W */ - max = 4; -#endif /* CONFIG_IEEE80211W */ + int i, max = 6; /* MLME-DELETEKEYS.request */ for (i = 0; i < max; i++) { if (wpa_s->keys_cleared & BIT(i)) continue; wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, i, 0, NULL, 0, - NULL, 0); + NULL, 0, KEY_FLAG_GROUP); } - if (!(wpa_s->keys_cleared & BIT(0)) && addr && + /* Pairwise Key ID 1 for Extended Key ID is tracked in bit 15 */ + if (~wpa_s->keys_cleared & (BIT(0) | BIT(15)) && addr && !is_zero_ether_addr(addr)) { - wpa_drv_set_key(wpa_s, WPA_ALG_NONE, addr, 0, 0, NULL, 0, NULL, - 0); + if (!(wpa_s->keys_cleared & BIT(0))) + wpa_drv_set_key(wpa_s, WPA_ALG_NONE, addr, 0, 0, NULL, + 0, NULL, 0, KEY_FLAG_PAIRWISE); + if (!(wpa_s->keys_cleared & BIT(15))) + wpa_drv_set_key(wpa_s, WPA_ALG_NONE, addr, 1, 0, NULL, + 0, NULL, 0, KEY_FLAG_PAIRWISE); /* MLME-SETPROTECTION.request(None) */ wpa_drv_mlme_setprotection( wpa_s, addr, @@ -755,7 +822,22 @@ const char * wpa_supplicant_state_txt(enum wpa_states state) #ifdef CONFIG_BGSCAN -static void wpa_supplicant_start_bgscan(struct wpa_supplicant *wpa_s) +static void wpa_supplicant_stop_bgscan(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->bgscan_ssid) { + bgscan_deinit(wpa_s); + wpa_s->bgscan_ssid = NULL; + } +} + + +/** + * wpa_supplicant_reset_bgscan - Reset the bgscan for the current SSID. + * @wpa_s: Pointer to the wpa_supplicant data + * + * Stop, start, or reconfigure the scan parameters depending on the method. + */ +void wpa_supplicant_reset_bgscan(struct wpa_supplicant *wpa_s) { const char *name; @@ -763,12 +845,12 @@ static void wpa_supplicant_start_bgscan(struct wpa_supplicant *wpa_s) name = wpa_s->current_ssid->bgscan; else name = wpa_s->conf->bgscan; - if (name == NULL || name[0] == '\0') + if (!name || name[0] == '\0') { + wpa_supplicant_stop_bgscan(wpa_s); return; + } if (wpas_driver_bss_selection(wpa_s)) return; - if (wpa_s->current_ssid == wpa_s->bgscan_ssid) - return; #ifdef CONFIG_P2P if (wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) return; @@ -798,15 +880,6 @@ static void wpa_supplicant_start_bgscan(struct wpa_supplicant *wpa_s) wpa_s->bgscan_ssid = NULL; } - -static void wpa_supplicant_stop_bgscan(struct wpa_supplicant *wpa_s) -{ - if (wpa_s->bgscan_ssid != NULL) { - bgscan_deinit(wpa_s); - wpa_s->bgscan_ssid = NULL; - } -} - #endif /* CONFIG_BGSCAN */ @@ -845,6 +918,9 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s, enum wpa_states state) { enum wpa_states old_state = wpa_s->wpa_state; +#if defined(CONFIG_FILS) && defined(IEEE8021X_EAPOL) + bool update_fils_connect_params = false; +#endif /* CONFIG_FILS && IEEE8021X_EAPOL */ wpa_dbg(wpa_s, MSG_DEBUG, "State: %s -> %s", wpa_supplicant_state_txt(wpa_s->wpa_state), @@ -927,8 +1003,7 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s, fils_hlp_sent ? " FILS_HLP_SENT" : ""); #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ wpas_clear_temp_disabled(wpa_s, ssid, 1); - wpa_blacklist_clear(wpa_s); - wpa_s->extra_blacklist_count = 0; + wpa_s->consecutive_conn_failures = 0; wpa_s->new_connection = 0; wpa_drv_set_operstate(wpa_s, 1); #ifndef IEEE8021X_EAPOL @@ -942,8 +1017,12 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s, #if defined(CONFIG_FILS) && defined(IEEE8021X_EAPOL) if (!fils_hlp_sent && ssid && ssid->eap.erp) - wpas_update_fils_connect_params(wpa_s); + update_fils_connect_params = true; #endif /* CONFIG_FILS && IEEE8021X_EAPOL */ +#ifdef CONFIG_OWE + if (ssid && (ssid->key_mgmt & WPA_KEY_MGMT_OWE)) + wpas_update_owe_connect_params(wpa_s); +#endif /* CONFIG_OWE */ } else if (state == WPA_DISCONNECTED || state == WPA_ASSOCIATING || state == WPA_ASSOCIATED) { wpa_s->new_connection = 1; @@ -956,8 +1035,8 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s, wpa_s->wpa_state = state; #ifdef CONFIG_BGSCAN - if (state == WPA_COMPLETED) - wpa_supplicant_start_bgscan(wpa_s); + if (state == WPA_COMPLETED && wpa_s->current_ssid != wpa_s->bgscan_ssid) + wpa_supplicant_reset_bgscan(wpa_s); else if (state < WPA_ASSOCIATED) wpa_supplicant_stop_bgscan(wpa_s); #endif /* CONFIG_BGSCAN */ @@ -983,7 +1062,15 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s, if (wpa_s->wpa_state == WPA_COMPLETED || old_state == WPA_COMPLETED) wpas_notify_auth_changed(wpa_s); +#ifdef CONFIG_DPP2 + if (wpa_s->wpa_state == WPA_COMPLETED) + wpas_dpp_connected(wpa_s); +#endif /* CONFIG_DPP2 */ } +#if defined(CONFIG_FILS) && defined(IEEE8021X_EAPOL) + if (update_fils_connect_params) + wpas_update_fils_connect_params(wpa_s); +#endif /* CONFIG_FILS && IEEE8021X_EAPOL */ } @@ -1020,13 +1107,19 @@ static void wpa_supplicant_terminate(int sig, void *signal_ctx) void wpa_supplicant_clear_status(struct wpa_supplicant *wpa_s) { enum wpa_states old_state = wpa_s->wpa_state; + enum wpa_states new_state; + + if (old_state == WPA_SCANNING) + new_state = WPA_SCANNING; + else + new_state = WPA_DISCONNECTED; wpa_s->pairwise_cipher = 0; wpa_s->group_cipher = 0; wpa_s->mgmt_group_cipher = 0; wpa_s->key_mgmt = 0; if (wpa_s->wpa_state != WPA_INTERFACE_DISABLED) - wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); + wpa_supplicant_set_state(wpa_s, new_state); if (wpa_s->wpa_state != old_state) wpas_notify_state_changed(wpa_s, wpa_s->wpa_state, old_state); @@ -1073,8 +1166,8 @@ int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s) os_strcmp(conf->ctrl_interface, wpa_s->conf->ctrl_interface) != 0); - if (reconf_ctrl && wpa_s->ctrl_iface) { - wpa_supplicant_ctrl_iface_deinit(wpa_s->ctrl_iface); + if (reconf_ctrl) { + wpa_supplicant_ctrl_iface_deinit(wpa_s, wpa_s->ctrl_iface); wpa_s->ctrl_iface = NULL; } @@ -1097,7 +1190,7 @@ int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s) * Clear forced success to clear EAP state for next * authentication. */ - eapol_sm_notify_eap_success(wpa_s->eapol, FALSE); + eapol_sm_notify_eap_success(wpa_s->eapol, false); } eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); wpa_sm_set_config(wpa_s->wpa, NULL); @@ -1121,6 +1214,7 @@ int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s) wpa_s->reassociate = 1; wpa_supplicant_req_scan(wpa_s, 0, 0); } + wpa_bssid_ignore_clear(wpa_s); wpa_dbg(wpa_s, MSG_DEBUG, "Reconfiguration completed"); return 0; } @@ -1179,7 +1273,6 @@ static int wpa_supplicant_suites_from_ai(struct wpa_supplicant *wpa_s, return -1; } -#ifdef CONFIG_IEEE80211W if (!(ie->capabilities & WPA_CAPABILITY_MFPC) && wpas_get_ssid_pmf(wpa_s, ssid) == MGMT_FRAME_PROTECTION_REQUIRED) { wpa_msg(wpa_s, MSG_INFO, "WPA: Driver associated with an AP " @@ -1187,7 +1280,6 @@ static int wpa_supplicant_suites_from_ai(struct wpa_supplicant *wpa_s, "reject"); return -1; } -#endif /* CONFIG_IEEE80211W */ return 0; } @@ -1205,6 +1297,47 @@ static int matching_ciphers(struct wpa_ssid *ssid, struct wpa_ie_data *ie, } +void wpas_set_mgmt_group_cipher(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, struct wpa_ie_data *ie) +{ + int sel; + + sel = ie->mgmt_group_cipher; + if (ssid->group_mgmt_cipher) + sel &= ssid->group_mgmt_cipher; + if (wpas_get_ssid_pmf(wpa_s, ssid) == NO_MGMT_FRAME_PROTECTION || + !(ie->capabilities & WPA_CAPABILITY_MFPC)) + sel = 0; + wpa_dbg(wpa_s, MSG_DEBUG, + "WPA: AP mgmt_group_cipher 0x%x network profile mgmt_group_cipher 0x%x; available mgmt_group_cipher 0x%x", + ie->mgmt_group_cipher, ssid->group_mgmt_cipher, sel); + if (sel & WPA_CIPHER_AES_128_CMAC) { + wpa_s->mgmt_group_cipher = WPA_CIPHER_AES_128_CMAC; + wpa_dbg(wpa_s, MSG_DEBUG, + "WPA: using MGMT group cipher AES-128-CMAC"); + } else if (sel & WPA_CIPHER_BIP_GMAC_128) { + wpa_s->mgmt_group_cipher = WPA_CIPHER_BIP_GMAC_128; + wpa_dbg(wpa_s, MSG_DEBUG, + "WPA: using MGMT group cipher BIP-GMAC-128"); + } else if (sel & WPA_CIPHER_BIP_GMAC_256) { + wpa_s->mgmt_group_cipher = WPA_CIPHER_BIP_GMAC_256; + wpa_dbg(wpa_s, MSG_DEBUG, + "WPA: using MGMT group cipher BIP-GMAC-256"); + } else if (sel & WPA_CIPHER_BIP_CMAC_256) { + wpa_s->mgmt_group_cipher = WPA_CIPHER_BIP_CMAC_256; + wpa_dbg(wpa_s, MSG_DEBUG, + "WPA: using MGMT group cipher BIP-CMAC-256"); + } else { + wpa_s->mgmt_group_cipher = 0; + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: not using MGMT group cipher"); + } + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MGMT_GROUP, + wpa_s->mgmt_group_cipher); + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MFP, + wpas_get_ssid_pmf(wpa_s, ssid)); +} + + /** * wpa_supplicant_set_suites - Set authentication and encryption parameters * @wpa_s: Pointer to wpa_supplicant data @@ -1224,15 +1357,17 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, u8 *wpa_ie, size_t *wpa_ie_len) { struct wpa_ie_data ie; - int sel, proto; - const u8 *bss_wpa, *bss_rsn, *bss_osen; + int sel, proto, sae_pwe; + const u8 *bss_wpa, *bss_rsn, *bss_rsnx, *bss_osen; if (bss) { bss_wpa = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); bss_rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN); + bss_rsnx = wpa_bss_get_ie(bss, WLAN_EID_RSNX); bss_osen = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE); - } else - bss_wpa = bss_rsn = bss_osen = NULL; + } else { + bss_wpa = bss_rsn = bss_rsnx = bss_osen = NULL; + } if (bss_rsn && (ssid->proto & WPA_PROTO_RSN) && wpa_parse_wpa_ie(bss_rsn, 2 + bss_rsn[1], &ie) == 0 && @@ -1312,7 +1447,6 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, ie.group_cipher = ssid->group_cipher; ie.pairwise_cipher = ssid->pairwise_cipher; ie.key_mgmt = ssid->key_mgmt; -#ifdef CONFIG_IEEE80211W ie.mgmt_group_cipher = 0; if (ssid->ieee80211w != NO_MGMT_FRAME_PROTECTION) { if (ssid->group_mgmt_cipher & @@ -1331,7 +1465,6 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, ie.mgmt_group_cipher = WPA_CIPHER_AES_128_CMAC; } -#endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_OWE if ((ssid->key_mgmt & WPA_KEY_MGMT_OWE) && !ssid->owe_only && @@ -1351,12 +1484,10 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Selected cipher suites: group %d " "pairwise %d key_mgmt %d proto %d", ie.group_cipher, ie.pairwise_cipher, ie.key_mgmt, proto); -#ifdef CONFIG_IEEE80211W if (ssid->ieee80211w) { wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Selected mgmt group cipher %d", ie.mgmt_group_cipher); } -#endif /* CONFIG_IEEE80211W */ wpa_s->wpa_proto = proto; wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_PROTO, proto); @@ -1367,7 +1498,9 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, if (wpa_sm_set_ap_wpa_ie(wpa_s->wpa, bss_wpa, bss_wpa ? 2 + bss_wpa[1] : 0) || wpa_sm_set_ap_rsn_ie(wpa_s->wpa, bss_rsn, - bss_rsn ? 2 + bss_rsn[1] : 0)) + bss_rsn ? 2 + bss_rsn[1] : 0) || + wpa_sm_set_ap_rsnxe(wpa_s->wpa, bss_rsnx, + bss_rsnx ? 2 + bss_rsnx[1] : 0)) return -1; } @@ -1403,17 +1536,23 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, #endif /* CONFIG_NO_WPA */ sel = ie.key_mgmt & ssid->key_mgmt; - wpa_dbg(wpa_s, MSG_DEBUG, - "WPA: AP key_mgmt 0x%x network profile key_mgmt 0x%x; available key_mgmt 0x%x", - ie.key_mgmt, ssid->key_mgmt, sel); #ifdef CONFIG_SAE if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE)) sel &= ~(WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_FT_SAE); #endif /* CONFIG_SAE */ +#ifdef CONFIG_IEEE80211R + if (!(wpa_s->drv_flags & (WPA_DRIVER_FLAGS_SME | + WPA_DRIVER_FLAGS_UPDATE_FT_IES))) + sel &= ~WPA_KEY_MGMT_FT; +#endif /* CONFIG_IEEE80211R */ + wpa_dbg(wpa_s, MSG_DEBUG, + "WPA: AP key_mgmt 0x%x network profile key_mgmt 0x%x; available key_mgmt 0x%x", + ie.key_mgmt, ssid->key_mgmt, sel); if (0) { #ifdef CONFIG_IEEE80211R #ifdef CONFIG_SHA384 - } else if (sel & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) { + } else if ((sel & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) && + os_strcmp(wpa_supplicant_get_eap_mode(wpa_s), "LEAP") != 0) { wpa_s->key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X_SHA384; wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT/802.1X-SHA384"); @@ -1456,7 +1595,8 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, 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) { + } else if ((sel & WPA_KEY_MGMT_FT_IEEE8021X) && + os_strcmp(wpa_supplicant_get_eap_mode(wpa_s), "LEAP") != 0) { wpa_s->key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X; wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT/802.1X"); if (!ssid->ft_eap_pmksa_caching && @@ -1486,7 +1626,6 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, wpa_s->key_mgmt = WPA_KEY_MGMT_FT_PSK; wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT/PSK"); #endif /* CONFIG_IEEE80211R */ -#ifdef CONFIG_IEEE80211W } else if (sel & WPA_KEY_MGMT_IEEE8021X_SHA256) { wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_SHA256; wpa_dbg(wpa_s, MSG_DEBUG, @@ -1495,7 +1634,6 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, wpa_s->key_mgmt = WPA_KEY_MGMT_PSK_SHA256; wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT PSK with SHA256"); -#endif /* CONFIG_IEEE80211W */ } else if (sel & WPA_KEY_MGMT_IEEE8021X) { wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X; wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT 802.1X"); @@ -1526,55 +1664,89 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, wpa_s->pairwise_cipher); wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_GROUP, wpa_s->group_cipher); -#ifdef CONFIG_IEEE80211W - sel = ie.mgmt_group_cipher; - if (ssid->group_mgmt_cipher) - sel &= ssid->group_mgmt_cipher; - if (wpas_get_ssid_pmf(wpa_s, ssid) == NO_MGMT_FRAME_PROTECTION || - !(ie.capabilities & WPA_CAPABILITY_MFPC)) - sel = 0; - wpa_dbg(wpa_s, MSG_DEBUG, - "WPA: AP mgmt_group_cipher 0x%x network profile mgmt_group_cipher 0x%x; available mgmt_group_cipher 0x%x", - ie.mgmt_group_cipher, ssid->group_mgmt_cipher, sel); - if (sel & WPA_CIPHER_AES_128_CMAC) { - wpa_s->mgmt_group_cipher = WPA_CIPHER_AES_128_CMAC; - wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher " - "AES-128-CMAC"); - } else if (sel & WPA_CIPHER_BIP_GMAC_128) { - wpa_s->mgmt_group_cipher = WPA_CIPHER_BIP_GMAC_128; - wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher " - "BIP-GMAC-128"); - } else if (sel & WPA_CIPHER_BIP_GMAC_256) { - wpa_s->mgmt_group_cipher = WPA_CIPHER_BIP_GMAC_256; - wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher " - "BIP-GMAC-256"); - } else if (sel & WPA_CIPHER_BIP_CMAC_256) { - wpa_s->mgmt_group_cipher = WPA_CIPHER_BIP_CMAC_256; - wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher " - "BIP-CMAC-256"); - } else { - wpa_s->mgmt_group_cipher = 0; - wpa_dbg(wpa_s, MSG_DEBUG, "WPA: not using MGMT group cipher"); + if (!(ie.capabilities & WPA_CAPABILITY_MFPC) && + wpas_get_ssid_pmf(wpa_s, ssid) == MGMT_FRAME_PROTECTION_REQUIRED) { + wpa_msg(wpa_s, MSG_INFO, + "RSN: Management frame protection required but the selected AP does not enable it"); + return -1; } - wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MGMT_GROUP, - wpa_s->mgmt_group_cipher); - wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MFP, - wpas_get_ssid_pmf(wpa_s, ssid)); -#endif /* CONFIG_IEEE80211W */ + + wpas_set_mgmt_group_cipher(wpa_s, ssid, &ie); #ifdef CONFIG_OCV - wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_OCV, ssid->ocv); + if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) || + (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_OCV)) + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_OCV, ssid->ocv); #endif /* CONFIG_OCV */ + sae_pwe = wpa_s->conf->sae_pwe; + if (ssid->sae_password_id && sae_pwe != 3) + sae_pwe = 1; + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_SAE_PWE, sae_pwe); +#ifdef CONFIG_SAE_PK + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_SAE_PK, + wpa_key_mgmt_sae(ssid->key_mgmt) && + ssid->sae_pk != SAE_PK_MODE_DISABLED && + ((ssid->sae_password && + sae_pk_valid_password(ssid->sae_password)) || + (!ssid->sae_password && ssid->passphrase && + sae_pk_valid_password(ssid->passphrase)))); +#endif /* CONFIG_SAE_PK */ +#ifdef CONFIG_TESTING_OPTIONS + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_FT_RSNXE_USED, + wpa_s->ft_rsnxe_used); + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_OCI_FREQ_EAPOL, + wpa_s->oci_freq_override_eapol); + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_OCI_FREQ_EAPOL_G2, + wpa_s->oci_freq_override_eapol_g2); + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_OCI_FREQ_FT_ASSOC, + wpa_s->oci_freq_override_ft_assoc); + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_OCI_FREQ_FILS_ASSOC, + wpa_s->oci_freq_override_fils_assoc); +#endif /* CONFIG_TESTING_OPTIONS */ + + /* Extended Key ID is only supported in infrastructure BSS so far */ + if (ssid->mode == WPAS_MODE_INFRA && wpa_s->conf->extended_key_id && + (ssid->proto & WPA_PROTO_RSN) && + ssid->pairwise_cipher & (WPA_CIPHER_CCMP | WPA_CIPHER_CCMP_256 | + WPA_CIPHER_GCMP | WPA_CIPHER_GCMP_256) && + (wpa_s->drv_flags & WPA_DRIVER_FLAGS_EXTENDED_KEY_ID)) { + int use_ext_key_id = 0; + + wpa_msg(wpa_s, MSG_DEBUG, + "WPA: Enable Extended Key ID support"); + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_EXT_KEY_ID, + wpa_s->conf->extended_key_id); + if (bss_rsn && + wpa_s->conf->extended_key_id && + wpa_s->pairwise_cipher != WPA_CIPHER_TKIP && + (ie.capabilities & WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST)) + use_ext_key_id = 1; + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_USE_EXT_KEY_ID, + use_ext_key_id); + } else { + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_EXT_KEY_ID, 0); + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_USE_EXT_KEY_ID, 0); + } if (wpa_sm_set_assoc_wpa_ie_default(wpa_s->wpa, wpa_ie, wpa_ie_len)) { wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to generate WPA IE"); return -1; } + wpa_s->rsnxe_len = sizeof(wpa_s->rsnxe); + if (wpa_sm_set_assoc_rsnxe_default(wpa_s->wpa, wpa_s->rsnxe, + &wpa_s->rsnxe_len)) { + wpa_msg(wpa_s, MSG_WARNING, "RSN: Failed to generate RSNXE"); + return -1; + } + if (0) { #ifdef CONFIG_DPP } else if (wpa_s->key_mgmt == WPA_KEY_MGMT_DPP) { /* Use PMK from DPP network introduction (PMKSA entry) */ wpa_sm_set_pmk_from_pmksa(wpa_s->wpa); +#ifdef CONFIG_DPP2 + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_DPP_PFS, ssid->dpp_pfs); +#endif /* CONFIG_DPP2 */ #endif /* CONFIG_DPP */ } else if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt)) { int psk_set = 0; @@ -1691,12 +1863,28 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, } else wpa_sm_set_pmk_from_pmksa(wpa_s->wpa); + if (ssid->mode != WPAS_MODE_IBSS && + !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED) && + (ssid->wpa_deny_ptk0_rekey == PTK0_REKEY_ALLOW_NEVER || + (ssid->wpa_deny_ptk0_rekey == PTK0_REKEY_ALLOW_LOCAL_OK && + !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAFE_PTK0_REKEYS)))) { + wpa_msg(wpa_s, MSG_INFO, + "Disable PTK0 rekey support - replaced with reconnect"); + wpa_s->deny_ptk0_rekey = 1; + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_DENY_PTK0_REKEY, 1); + } else { + wpa_s->deny_ptk0_rekey = 0; + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_DENY_PTK0_REKEY, 0); + } + return 0; } static void wpas_ext_capab_byte(struct wpa_supplicant *wpa_s, u8 *pos, int idx) { + bool scs = true, mscs = true; + *pos = 0x00; switch (idx) { @@ -1711,7 +1899,7 @@ static void wpas_ext_capab_byte(struct wpa_supplicant *wpa_s, u8 *pos, int idx) case 2: /* Bits 16-23 */ #ifdef CONFIG_WNM *pos |= 0x02; /* Bit 17 - WNM-Sleep Mode */ - if (!wpa_s->conf->disable_btm) + if (!wpa_s->disable_mbo_oce && !wpa_s->conf->disable_btm) *pos |= 0x08; /* Bit 19 - BSS Transition */ #endif /* CONFIG_WNM */ break; @@ -1740,6 +1928,12 @@ static void wpas_ext_capab_byte(struct wpa_supplicant *wpa_s, u8 *pos, int idx) #endif /* CONFIG_MBO */ break; case 6: /* Bits 48-55 */ +#ifdef CONFIG_TESTING_OPTIONS + if (wpa_s->disable_scs_support) + scs = false; +#endif /* CONFIG_TESTING_OPTIONS */ + if (scs) + *pos |= 0x40; /* Bit 54 - SCS */ break; case 7: /* Bits 56-63 */ break; @@ -1755,6 +1949,14 @@ static void wpas_ext_capab_byte(struct wpa_supplicant *wpa_s, u8 *pos, int idx) *pos |= 0x01; #endif /* CONFIG_FILS */ break; + case 10: /* Bits 80-87 */ +#ifdef CONFIG_TESTING_OPTIONS + if (wpa_s->disable_mscs_support) + mscs = false; +#endif /* CONFIG_TESTING_OPTIONS */ + if (mscs) + *pos |= 0x20; /* Bit 85 - Mirrored SCS */ + break; } } @@ -1762,7 +1964,7 @@ static void wpas_ext_capab_byte(struct wpa_supplicant *wpa_s, u8 *pos, int idx) int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf, size_t buflen) { u8 *pos = buf; - u8 len = 10, i; + u8 len = 11, i; if (len < wpa_s->extended_capa_len) len = wpa_s->extended_capa_len; @@ -1918,6 +2120,79 @@ int wpas_update_random_addr_disassoc(struct wpa_supplicant *wpa_s) } +static void wpa_s_setup_sae_pt(struct wpa_config *conf, struct wpa_ssid *ssid) +{ +#ifdef CONFIG_SAE + int *groups = conf->sae_groups; + int default_groups[] = { 19, 20, 21, 0 }; + const char *password; + + if (!groups || groups[0] <= 0) + groups = default_groups; + + password = ssid->sae_password; + if (!password) + password = ssid->passphrase; + + if (!password || + (conf->sae_pwe == 0 && !ssid->sae_password_id && + !sae_pk_valid_password(password)) || + conf->sae_pwe == 3) { + /* PT derivation not needed */ + sae_deinit_pt(ssid->pt); + ssid->pt = NULL; + return; + } + + if (ssid->pt) + return; /* PT already derived */ + ssid->pt = sae_derive_pt(groups, ssid->ssid, ssid->ssid_len, + (const u8 *) password, os_strlen(password), + ssid->sae_password_id); +#endif /* CONFIG_SAE */ +} + + +static void wpa_s_clear_sae_rejected(struct wpa_supplicant *wpa_s) +{ +#if defined(CONFIG_SAE) && defined(CONFIG_SME) + os_free(wpa_s->sme.sae_rejected_groups); + wpa_s->sme.sae_rejected_groups = NULL; +#ifdef CONFIG_TESTING_OPTIONS + if (wpa_s->extra_sae_rejected_groups) { + int i, *groups = wpa_s->extra_sae_rejected_groups; + + for (i = 0; groups[i]; i++) { + wpa_printf(MSG_DEBUG, + "TESTING: Indicate rejection of an extra SAE group %d", + groups[i]); + int_array_add_unique(&wpa_s->sme.sae_rejected_groups, + groups[i]); + } + } +#endif /* CONFIG_TESTING_OPTIONS */ +#endif /* CONFIG_SAE && CONFIG_SME */ +} + + +int wpas_restore_permanent_mac_addr(struct wpa_supplicant *wpa_s) +{ + if (wpa_drv_set_mac_addr(wpa_s, NULL) < 0) { + wpa_msg(wpa_s, MSG_INFO, + "Could not restore permanent MAC address"); + return -1; + } + wpa_s->mac_addr_changed = 0; + if (wpa_supplicant_update_mac_addr(wpa_s) < 0) { + wpa_msg(wpa_s, MSG_INFO, + "Could not update MAC address information"); + return -1; + } + wpa_msg(wpa_s, MSG_DEBUG, "Using permanent MAC address"); + return 0; +} + + static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit); /** @@ -1935,6 +2210,7 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, int rand_style; wpa_s->own_disconnect_req = 0; + wpa_s->own_reconnect_req = 0; /* * If we are starting a new connection, any previously pending EAPOL @@ -1948,6 +2224,7 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, else rand_style = ssid->mac_addr; + wpa_s->multi_ap_ie = 0; wmm_ac_clear_saved_tspecs(wpa_s); wpa_s->reassoc_same_bss = 0; wpa_s->reassoc_same_ess = 0; @@ -1964,25 +2241,22 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, } else if (wpa_s->current_bss && wpa_s->current_bss != bss) { os_get_reltime(&wpa_s->roam_start); } + } else { +#ifdef CONFIG_SAE + wpa_s_clear_sae_rejected(wpa_s); +#endif /* CONFIG_SAE */ } +#ifdef CONFIG_SAE + wpa_s_setup_sae_pt(wpa_s->conf, ssid); +#endif /* CONFIG_SAE */ 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 (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"); + if (wpas_restore_permanent_mac_addr(wpa_s) < 0) return; - } - wpa_s->mac_addr_changed = 0; - if (wpa_supplicant_update_mac_addr(wpa_s) < 0) { - wpa_msg(wpa_s, MSG_INFO, - "Could not update MAC address information"); - return; - } - wpa_msg(wpa_s, MSG_DEBUG, "Using permanent MAC address"); } wpa_s->last_ssid = ssid; @@ -2034,10 +2308,6 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, return; } wpa_s->current_bss = bss; - wpa_msg(wpa_s, MSG_INFO, MESH_GROUP_STARTED "ssid=\"%s\" id=%d", - wpa_ssid_txt(ssid->ssid, ssid->ssid_len), - ssid->id); - wpas_notify_mesh_group_started(wpa_s, ssid); #else /* CONFIG_MESH */ wpa_msg(wpa_s, MSG_ERROR, "mesh mode support not included in the build"); @@ -2061,10 +2331,13 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, #ifdef CONFIG_TDLS if (bss) - wpa_tdls_ap_ies(wpa_s->wpa, (const u8 *) (bss + 1), - bss->ie_len); + wpa_tdls_ap_ies(wpa_s->wpa, wpa_bss_ie_ptr(bss), bss->ie_len); #endif /* CONFIG_TDLS */ +#ifdef CONFIG_MBO + wpas_mbo_check_pmf(wpa_s, bss, ssid); +#endif /* CONFIG_MBO */ + if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) && ssid->mode == WPAS_MODE_INFRA) { sme_authenticate(wpa_s, bss, ssid); @@ -2136,6 +2409,23 @@ static int drv_supports_vht(struct wpa_supplicant *wpa_s, } +static bool ibss_mesh_is_80mhz_avail(int channel, struct hostapd_hw_modes *mode) +{ + int i; + + for (i = channel; i < channel + 16; i += 4) { + struct hostapd_channel_data *chan; + + chan = hw_get_channel_chan(mode, i, NULL); + if (!chan || + chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR)) + return false; + } + + return true; +} + + void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s, const struct wpa_ssid *ssid, struct hostapd_freq_params *freq) @@ -2145,7 +2435,10 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s, struct hostapd_hw_modes *mode = NULL; int ht40plus[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157, 184, 192 }; - int vht80[] = { 36, 52, 100, 116, 132, 149 }; + int bw80[] = { 5180, 5260, 5500, 5580, 5660, 5745, 5955, + 6035, 6115, 6195, 6275, 6355, 6435, 6515, + 6595, 6675, 6755, 6835, 6915, 6995 }; + int bw160[] = { 5955, 6115, 6275, 6435, 6595, 6755, 6915 }; struct hostapd_channel_data *pri_chan = NULL, *sec_chan = NULL; u8 channel; int i, chan_idx, ht40 = -1, res, obss_scan = 1; @@ -2153,6 +2446,7 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s, struct hostapd_freq_params vht_freq; int chwidth, seg0, seg1; u32 vht_caps = 0; + bool is_24ghz, is_6ghz; freq->freq = ssid->frequency; @@ -2204,8 +2498,17 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s, if (!mode) return; - /* HE can work without HT + VHT */ - freq->he_enabled = mode->he_capab[ieee80211_mode].he_supported; + freq->channel = channel; + + is_24ghz = hw_mode == HOSTAPD_MODE_IEEE80211G || + hw_mode == HOSTAPD_MODE_IEEE80211B; + + /* HT/VHT and corresponding overrides are not applicable to 6 GHz. + * However, HE is mandatory for 6 GHz. + */ + is_6ghz = is_6ghz_freq(freq->freq); + if (is_6ghz) + goto skip_to_6ghz; #ifdef CONFIG_HT_OVERRIDES if (ssid->disable_ht) { @@ -2218,6 +2521,14 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s, if (!freq->ht_enabled) return; + /* Allow HE on 2.4 GHz without VHT: see nl80211_put_freq_params() */ + if (is_24ghz) + freq->he_enabled = mode->he_capab[ieee80211_mode].he_supported; +#ifdef CONFIG_HE_OVERRIDES + if (is_24ghz && ssid->disable_he) + freq->he_enabled = 0; +#endif /* CONFIG_HE_OVERRIDES */ + /* Setup higher BW only for 5 GHz */ if (mode->mode != HOSTAPD_MODE_IEEE80211A) return; @@ -2239,8 +2550,10 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s, #ifdef CONFIG_HT_OVERRIDES if (ssid->disable_ht40) { +#ifdef CONFIG_VHT_OVERRIDES if (ssid->disable_vht) return; +#endif /* CONFIG_VHT_OVERRIDES */ goto skip_ht40; } #endif /* CONFIG_HT_OVERRIDES */ @@ -2286,8 +2599,7 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s, return; } - res = check_40mhz_5g(mode, scan_res, pri_chan->chan, - sec_chan->chan); + res = check_40mhz_5g(scan_res, pri_chan, sec_chan); switch (res) { case 0: /* Back to HT20 */ @@ -2325,8 +2637,6 @@ skip_ht40: !(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; @@ -2334,43 +2644,67 @@ skip_ht40: } #endif /* CONFIG_VHT_OVERRIDES */ +skip_to_6ghz: + vht_freq = *freq; + + /* 6 GHz does not have VHT enabled, so allow that exception here. */ vht_freq.vht_enabled = vht_supported(mode); - if (!vht_freq.vht_enabled) + if (!vht_freq.vht_enabled && !is_6ghz) return; + /* Enable HE with VHT for 5 GHz */ + freq->he_enabled = mode->he_capab[ieee80211_mode].he_supported; + /* setup center_freq1, bandwidth */ - for (j = 0; j < ARRAY_SIZE(vht80); j++) { - if (freq->channel >= vht80[j] && - freq->channel < vht80[j] + 16) + for (j = 0; j < ARRAY_SIZE(bw80); j++) { + if (freq->freq >= bw80[j] && + freq->freq < bw80[j] + 80) break; } - if (j == ARRAY_SIZE(vht80)) + if (j == ARRAY_SIZE(bw80) || + ieee80211_freq_to_chan(bw80[j], &channel) == NUM_HOSTAPD_MODES) return; - for (i = vht80[j]; i < vht80[j] + 16; i += 4) { - struct hostapd_channel_data *chan; + /* Back to HT configuration if channel not usable */ + if (!ibss_mesh_is_80mhz_avail(channel, mode)) + return; - chan = hw_get_channel_chan(mode, i, NULL); - if (!chan) - return; + chwidth = CHANWIDTH_80MHZ; + seg0 = channel + 6; + seg1 = 0; - /* Back to HT configuration if channel not usable */ - if (chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR)) + if ((mode->he_capab[ieee80211_mode].phy_cap[ + HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] & + HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G) && is_6ghz) { + /* In 160 MHz, the initial four 20 MHz channels were validated + * above; check the remaining four 20 MHz channels for the total + * of 160 MHz bandwidth. + */ + if (!ibss_mesh_is_80mhz_avail(channel + 16, mode)) return; - } - chwidth = CHANWIDTH_80MHZ; - seg0 = vht80[j] + 6; - seg1 = 0; + for (j = 0; j < ARRAY_SIZE(bw160); j++) { + if (freq->freq == bw160[j]) { + chwidth = CHANWIDTH_160MHZ; + seg0 = channel + 14; + break; + } + } + } if (ssid->max_oper_chwidth == CHANWIDTH_80P80MHZ) { /* setup center_freq2, bandwidth */ - for (k = 0; k < ARRAY_SIZE(vht80); k++) { + for (k = 0; k < ARRAY_SIZE(bw80); k++) { /* Only accept 80 MHz segments separated by a gap */ - if (j == k || abs(vht80[j] - vht80[k]) == 16) + if (j == k || abs(bw80[j] - bw80[k]) == 80) continue; - for (i = vht80[k]; i < vht80[k] + 16; i += 4) { + + if (ieee80211_freq_to_chan(bw80[k], &channel) == + NUM_HOSTAPD_MODES) + return; + + for (i = channel; i < channel + 16; i += 4) { struct hostapd_channel_data *chan; chan = hw_get_channel_chan(mode, i, NULL); @@ -2384,9 +2718,10 @@ skip_ht40: /* Found a suitable second segment for 80+80 */ chwidth = CHANWIDTH_80P80MHZ; - vht_caps |= - VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ; - seg1 = vht80[k] + 6; + if (!is_6ghz) + vht_caps |= + VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ; + seg1 = channel + 6; } if (chwidth == CHANWIDTH_80P80MHZ) @@ -2404,15 +2739,22 @@ skip_ht40: } } else if (ssid->max_oper_chwidth == CHANWIDTH_USE_HT) { chwidth = CHANWIDTH_USE_HT; - seg0 = vht80[j] + 2; + seg0 = channel + 2; #ifdef CONFIG_HT_OVERRIDES if (ssid->disable_ht40) seg0 = 0; #endif /* CONFIG_HT_OVERRIDES */ } +#ifdef CONFIG_HE_OVERRIDES + if (ssid->disable_he) { + vht_freq.he_enabled = 0; + freq->he_enabled = 0; + } +#endif /* CONFIG_HE_OVERRIDES */ if (hostapd_set_freq_params(&vht_freq, mode->mode, freq->freq, - freq->channel, freq->ht_enabled, + freq->channel, ssid->enable_edmg, + ssid->edmg_channel, freq->ht_enabled, vht_freq.vht_enabled, freq->he_enabled, freq->sec_channel_offset, chwidth, seg0, seg1, vht_caps, @@ -2517,6 +2859,54 @@ int wpa_is_fils_sk_pfs_supported(struct wpa_supplicant *wpa_s) #endif /* CONFIG_FILS */ +static int wpas_populate_wfa_capa(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss, + u8 *wpa_ie, size_t wpa_ie_len, + size_t max_wpa_ie_len) +{ + struct wpabuf *wfa_ie = NULL; + u8 wfa_capa[1]; + size_t wfa_ie_len, buf_len; + + os_memset(wfa_capa, 0, sizeof(wfa_capa)); + if (wpa_s->enable_dscp_policy_capa) + wfa_capa[0] |= WFA_CAPA_QM_DSCP_POLICY; + + if (!wfa_capa[0]) + return wpa_ie_len; + + /* Wi-Fi Alliance element */ + buf_len = 1 + /* Element ID */ + 1 + /* Length */ + 3 + /* OUI */ + 1 + /* OUI Type */ + 1 + /* Capabilities Length */ + sizeof(wfa_capa); /* Capabilities */ + wfa_ie = wpabuf_alloc(buf_len); + if (!wfa_ie) + return wpa_ie_len; + + wpabuf_put_u8(wfa_ie, WLAN_EID_VENDOR_SPECIFIC); + wpabuf_put_u8(wfa_ie, buf_len - 2); + wpabuf_put_be24(wfa_ie, OUI_WFA); + wpabuf_put_u8(wfa_ie, WFA_CAPA_OUI_TYPE); + wpabuf_put_u8(wfa_ie, sizeof(wfa_capa)); + wpabuf_put_data(wfa_ie, wfa_capa, sizeof(wfa_capa)); + + wfa_ie_len = wpabuf_len(wfa_ie); + if (wpa_ie_len + wfa_ie_len <= max_wpa_ie_len) { + wpa_hexdump_buf(MSG_MSGDUMP, "WFA Capabilities element", + wfa_ie); + os_memcpy(wpa_ie + wpa_ie_len, wpabuf_head(wfa_ie), + wfa_ie_len); + wpa_ie_len += wfa_ie_len; + } + + wpabuf_free(wfa_ie); + return wpa_ie_len; +} + + static u8 * wpas_populate_assoc_ies( struct wpa_supplicant *wpa_s, struct wpa_bss *bss, struct wpa_ssid *ssid, @@ -2530,9 +2920,9 @@ static u8 * wpas_populate_assoc_ies( #ifdef CONFIG_MBO const u8 *mbo_ie; #endif -#ifdef CONFIG_SAE - int sae_pmksa_cached = 0; -#endif /* CONFIG_SAE */ +#if defined(CONFIG_SAE) || defined(CONFIG_FILS) + int pmksa_cached = 0; +#endif /* CONFIG_SAE || CONFIG_FILS */ #ifdef CONFIG_FILS const u8 *realm, *username, *rrk; size_t realm_len, username_len, rrk_len; @@ -2572,9 +2962,9 @@ static u8 * wpas_populate_assoc_ies( ssid, try_opportunistic, cache_id, 0) == 0) { eapol_sm_notify_pmkid_attempt(wpa_s->eapol); -#ifdef CONFIG_SAE - sae_pmksa_cached = 1; -#endif /* CONFIG_SAE */ +#if defined(CONFIG_SAE) || defined(CONFIG_FILS) + pmksa_cached = 1; +#endif /* CONFIG_SAE || CONFIG_FILS */ } wpa_ie_len = max_wpa_ie_len; if (wpa_supplicant_set_suites(wpa_s, bss, ssid, @@ -2673,6 +3063,10 @@ static u8 * wpas_populate_assoc_ies( if (mask) *mask |= WPA_DRV_UPDATE_FILS_ERP_INFO; + } else if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD) && + ssid->eap.erp && wpa_key_mgmt_fils(wpa_s->key_mgmt) && + pmksa_cached) { + algs = WPA_AUTH_ALG_FILS; } #endif /* CONFIG_FILS */ #endif /* IEEE8021X_EAPOL */ @@ -2689,7 +3083,7 @@ static u8 * wpas_populate_assoc_ies( } #ifdef CONFIG_SAE - if (sae_pmksa_cached && algs == WPA_AUTH_ALG_SAE) { + if (pmksa_cached && algs == WPA_AUTH_ALG_SAE) { wpa_dbg(wpa_s, MSG_DEBUG, "SAE: Use WPA_AUTH_ALG_OPEN for PMKSA caching attempt"); algs = WPA_AUTH_ALG_OPEN; @@ -2728,7 +3122,7 @@ static u8 * wpas_populate_assoc_ies( #endif /* CONFIG_P2P */ if (bss) { - wpa_ie_len += wpas_supp_op_class_ie(wpa_s, ssid, bss->freq, + wpa_ie_len += wpas_supp_op_class_ie(wpa_s, ssid, bss, wpa_ie + wpa_ie_len, max_wpa_ie_len - wpa_ie_len); @@ -2815,7 +3209,7 @@ static u8 * wpas_populate_assoc_ies( #ifdef CONFIG_MBO mbo_ie = bss ? wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE) : NULL; - if (mbo_ie) { + if (!wpa_s->disable_mbo_oce && mbo_ie) { int len; len = wpas_mbo_ie(wpa_s, wpa_ie + wpa_ie_len, @@ -2876,8 +3270,16 @@ static u8 * wpas_populate_assoc_ies( #endif /* CONFIG_OWE */ #ifdef CONFIG_DPP2 - if (wpa_sm_get_key_mgmt(wpa_s->wpa) == WPA_KEY_MGMT_DPP && - ssid->dpp_netaccesskey) { + if (DPP_VERSION > 1 && + wpa_sm_get_key_mgmt(wpa_s->wpa) == WPA_KEY_MGMT_DPP && + ssid->dpp_netaccesskey && + ssid->dpp_pfs != 2 && !ssid->dpp_pfs_fallback) { + struct rsn_pmksa_cache_entry *pmksa; + + pmksa = pmksa_cache_get_current(wpa_s->wpa); + if (!pmksa || !pmksa->dpp_pfs) + goto pfs_fail; + dpp_pfs_free(wpa_s->dpp_pfs); wpa_s->dpp_pfs = dpp_pfs_init(ssid->dpp_netaccesskey, ssid->dpp_netaccesskey_len); @@ -2930,6 +3332,61 @@ pfs_fail: } #endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_TESTING_OPTIONS + if (wpa_s->rsnxe_override_assoc && + wpabuf_len(wpa_s->rsnxe_override_assoc) <= + max_wpa_ie_len - wpa_ie_len) { + wpa_printf(MSG_DEBUG, "TESTING: RSNXE AssocReq override"); + os_memcpy(wpa_ie + wpa_ie_len, + wpabuf_head(wpa_s->rsnxe_override_assoc), + wpabuf_len(wpa_s->rsnxe_override_assoc)); + wpa_ie_len += wpabuf_len(wpa_s->rsnxe_override_assoc); + } else +#endif /* CONFIG_TESTING_OPTIONS */ + if (wpa_s->rsnxe_len > 0 && + wpa_s->rsnxe_len <= max_wpa_ie_len - wpa_ie_len) { + os_memcpy(wpa_ie + wpa_ie_len, wpa_s->rsnxe, wpa_s->rsnxe_len); + wpa_ie_len += wpa_s->rsnxe_len; + } + +#ifdef CONFIG_TESTING_OPTIONS + if (wpa_s->disable_mscs_support) + goto mscs_end; +#endif /* CONFIG_TESTING_OPTIONS */ + if (wpa_bss_ext_capab(bss, WLAN_EXT_CAPAB_MSCS) && + wpa_s->robust_av.valid_config) { + struct wpabuf *mscs_ie; + size_t mscs_ie_len, buf_len; + + buf_len = 3 + /* MSCS descriptor IE header */ + 1 + /* Request type */ + 2 + /* User priority control */ + 4 + /* Stream timeout */ + 3 + /* TCLAS Mask IE header */ + wpa_s->robust_av.frame_classifier_len; + mscs_ie = wpabuf_alloc(buf_len); + if (!mscs_ie) { + wpa_printf(MSG_INFO, + "MSCS: Failed to allocate MSCS IE"); + goto mscs_end; + } + + wpas_populate_mscs_descriptor_ie(&wpa_s->robust_av, mscs_ie); + if ((wpa_ie_len + wpabuf_len(mscs_ie)) <= max_wpa_ie_len) { + wpa_hexdump_buf(MSG_MSGDUMP, "MSCS IE", mscs_ie); + mscs_ie_len = wpabuf_len(mscs_ie); + os_memcpy(wpa_ie + wpa_ie_len, wpabuf_head(mscs_ie), + mscs_ie_len); + wpa_ie_len += mscs_ie_len; + } + + wpabuf_free(mscs_ie); + } +mscs_end: + + wpa_ie_len = wpas_populate_wfa_capa(wpa_s, bss, wpa_ie, wpa_ie_len, + max_wpa_ie_len); + if (ssid->multi_ap_backhaul_sta) { size_t multi_ap_ie_len; @@ -2955,6 +3412,24 @@ pfs_fail: } +#ifdef CONFIG_OWE +static void wpas_update_owe_connect_params(struct wpa_supplicant *wpa_s) +{ + struct wpa_driver_associate_params params; + u8 *wpa_ie; + + os_memset(¶ms, 0, sizeof(params)); + wpa_ie = wpas_populate_assoc_ies(wpa_s, wpa_s->current_bss, + wpa_s->current_ssid, ¶ms, NULL); + if (!wpa_ie) + return; + + wpa_drv_update_connect_params(wpa_s, ¶ms, WPA_DRV_UPDATE_ASSOC_IES); + os_free(wpa_ie); +} +#endif /* CONFIG_OWE */ + + #if defined(CONFIG_FILS) && defined(IEEE8021X_EAPOL) static void wpas_update_fils_connect_params(struct wpa_supplicant *wpa_s) { @@ -2971,18 +3446,127 @@ static void wpas_update_fils_connect_params(struct wpa_supplicant *wpa_s) if (!wpa_ie) return; - if (params.auth_alg != WPA_AUTH_ALG_FILS) { - os_free(wpa_ie); - return; + if (params.auth_alg == WPA_AUTH_ALG_FILS) { + wpa_s->auth_alg = params.auth_alg; + wpa_drv_update_connect_params(wpa_s, ¶ms, mask); } - wpa_s->auth_alg = params.auth_alg; - wpa_drv_update_connect_params(wpa_s, ¶ms, mask); os_free(wpa_ie); } #endif /* CONFIG_FILS && IEEE8021X_EAPOL */ +static u8 wpa_ie_get_edmg_oper_chans(const u8 *edmg_ie) +{ + if (!edmg_ie || edmg_ie[1] < 6) + return 0; + return edmg_ie[EDMG_BSS_OPERATING_CHANNELS_OFFSET]; +} + + +static u8 wpa_ie_get_edmg_oper_chan_width(const u8 *edmg_ie) +{ + if (!edmg_ie || edmg_ie[1] < 6) + return 0; + return edmg_ie[EDMG_OPERATING_CHANNEL_WIDTH_OFFSET]; +} + + +/* Returns the intersection of two EDMG configurations. + * Note: The current implementation is limited to CB2 only (CB1 included), + * i.e., the implementation supports up to 2 contiguous channels. + * For supporting non-contiguous (aggregated) channels and for supporting + * CB3 and above, this function will need to be extended. + */ +static struct ieee80211_edmg_config +get_edmg_intersection(struct ieee80211_edmg_config a, + struct ieee80211_edmg_config b, + u8 primary_channel) +{ + struct ieee80211_edmg_config result; + int i, contiguous = 0; + int max_contiguous = 0; + + result.channels = b.channels & a.channels; + if (!result.channels) { + wpa_printf(MSG_DEBUG, + "EDMG not possible: cannot intersect channels 0x%x and 0x%x", + a.channels, b.channels); + goto fail; + } + + if (!(result.channels & BIT(primary_channel - 1))) { + wpa_printf(MSG_DEBUG, + "EDMG not possible: the primary channel %d is not one of the intersected channels 0x%x", + primary_channel, result.channels); + goto fail; + } + + /* Find max contiguous channels */ + for (i = 0; i < 6; i++) { + if (result.channels & BIT(i)) + contiguous++; + else + contiguous = 0; + + if (contiguous > max_contiguous) + max_contiguous = contiguous; + } + + /* Assuming AP and STA supports ONLY contiguous channels, + * bw configuration can have value between 4-7. + */ + if ((b.bw_config < a.bw_config)) + result.bw_config = b.bw_config; + else + result.bw_config = a.bw_config; + + if ((max_contiguous >= 2 && result.bw_config < EDMG_BW_CONFIG_5) || + (max_contiguous >= 1 && result.bw_config < EDMG_BW_CONFIG_4)) { + wpa_printf(MSG_DEBUG, + "EDMG not possible: not enough contiguous channels %d for supporting CB1 or CB2", + max_contiguous); + goto fail; + } + + return result; + +fail: + result.channels = 0; + result.bw_config = 0; + return result; +} + + +static struct ieee80211_edmg_config +get_supported_edmg(struct wpa_supplicant *wpa_s, + struct hostapd_freq_params *freq, + struct ieee80211_edmg_config request_edmg) +{ + enum hostapd_hw_mode hw_mode; + struct hostapd_hw_modes *mode = NULL; + u8 primary_channel; + + if (!wpa_s->hw.modes) + goto fail; + + hw_mode = ieee80211_freq_to_chan(freq->freq, &primary_channel); + if (hw_mode == NUM_HOSTAPD_MODES) + goto fail; + + mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, hw_mode, false); + if (!mode) + goto fail; + + return get_edmg_intersection(mode->edmg, request_edmg, primary_channel); + +fail: + request_edmg.channels = 0; + request_edmg.bw_config = 0; + return request_edmg; +} + + #ifdef CONFIG_MBO void wpas_update_mbo_connect_params(struct wpa_supplicant *wpa_s) { @@ -3018,10 +3602,13 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) struct wpa_ssid *ssid = cwork->ssid; struct wpa_supplicant *wpa_s = work->wpa_s; u8 *wpa_ie; - int use_crypt, ret, i, bssid_changed; + const u8 *edmg_ie_oper; + int use_crypt, ret, bssid_changed; unsigned int cipher_pairwise, cipher_group, cipher_group_mgmt; struct wpa_driver_associate_params params; +#if defined(CONFIG_WEP) || defined(IEEE8021X_EAPOL) int wep_keys_set = 0; +#endif /* CONFIG_WEP || IEEE8021X_EAPOL */ int assoc_failed = 0; struct wpa_ssid *old_ssid; u8 prev_bssid[ETH_ALEN]; @@ -3034,6 +3621,11 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) struct ieee80211_vht_capabilities vhtcaps_mask; #endif /* CONFIG_VHT_OVERRIDES */ + wpa_s->roam_in_progress = false; +#ifdef CONFIG_WNM + wpa_s->bss_trans_mgmt_in_progress = false; +#endif /* CONFIG_WNM */ + if (deinit) { if (work->started) { wpa_s->connect_work = NULL; @@ -3059,6 +3651,20 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) os_memset(¶ms, 0, sizeof(params)); wpa_s->reassociate = 0; wpa_s->eap_expected_failure = 0; + + /* Starting new association, so clear the possibly used WPA IE from the + * previous association. */ + wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0); + wpa_sm_set_assoc_rsnxe(wpa_s->wpa, NULL, 0); + wpa_s->rsnxe_len = 0; + wpa_s->mscs_setup_done = false; + + wpa_ie = wpas_populate_assoc_ies(wpa_s, bss, ssid, ¶ms, NULL); + if (!wpa_ie) { + wpas_connect_work_done(wpa_s); + return; + } + if (bss && (!wpas_driver_bss_selection(wpa_s) || wpas_wps_searching(wpa_s))) { #ifdef CONFIG_IEEE80211R @@ -3092,6 +3698,7 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) wpa_s->scan_req = MANUAL_SCAN_REQ; wpa_s->reassociate = 1; wpa_supplicant_req_scan(wpa_s, 0, 0); + os_free(wpa_ie); return; #endif /* CONFIG_WPS */ } else { @@ -3107,16 +3714,6 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) wpa_supplicant_cancel_scan(wpa_s); - /* Starting new association, so clear the possibly used WPA IE from the - * previous association. */ - wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0); - - wpa_ie = wpas_populate_assoc_ies(wpa_s, bss, ssid, ¶ms, NULL); - if (!wpa_ie) { - wpas_connect_work_done(wpa_s); - return; - } - wpa_clear_keys(wpa_s, bss ? bss->bssid : NULL); use_crypt = 1; cipher_pairwise = wpa_s->pairwise_cipher; @@ -3126,10 +3723,12 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) { if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE) use_crypt = 0; +#ifdef CONFIG_WEP if (wpa_set_wep_keys(wpa_s, ssid)) { use_crypt = 1; wep_keys_set = 1; } +#endif /* CONFIG_WEP */ } if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS) use_crypt = 0; @@ -3201,6 +3800,71 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) params.beacon_int = wpa_s->conf->beacon_int; } + if (bss && ssid->enable_edmg) + edmg_ie_oper = wpa_bss_get_ie_ext(bss, + WLAN_EID_EXT_EDMG_OPERATION); + else + edmg_ie_oper = NULL; + + if (edmg_ie_oper) { + params.freq.edmg.channels = + wpa_ie_get_edmg_oper_chans(edmg_ie_oper); + params.freq.edmg.bw_config = + wpa_ie_get_edmg_oper_chan_width(edmg_ie_oper); + wpa_printf(MSG_DEBUG, + "AP supports EDMG channels 0x%x, bw_config %d", + params.freq.edmg.channels, + params.freq.edmg.bw_config); + + /* User may ask for specific EDMG channel for EDMG connection + * (must be supported by AP) + */ + if (ssid->edmg_channel) { + struct ieee80211_edmg_config configured_edmg; + enum hostapd_hw_mode hw_mode; + u8 primary_channel; + + hw_mode = ieee80211_freq_to_chan(bss->freq, + &primary_channel); + if (hw_mode == NUM_HOSTAPD_MODES) + goto edmg_fail; + + hostapd_encode_edmg_chan(ssid->enable_edmg, + ssid->edmg_channel, + primary_channel, + &configured_edmg); + + if (ieee802_edmg_is_allowed(params.freq.edmg, + configured_edmg)) { + params.freq.edmg = configured_edmg; + wpa_printf(MSG_DEBUG, + "Use EDMG channel %d for connection", + ssid->edmg_channel); + } else { + edmg_fail: + params.freq.edmg.channels = 0; + params.freq.edmg.bw_config = 0; + wpa_printf(MSG_WARNING, + "EDMG channel %d not supported by AP, fallback to DMG", + ssid->edmg_channel); + } + } + + if (params.freq.edmg.channels) { + wpa_printf(MSG_DEBUG, + "EDMG before: channels 0x%x, bw_config %d", + params.freq.edmg.channels, + params.freq.edmg.bw_config); + params.freq.edmg = get_supported_edmg(wpa_s, + ¶ms.freq, + params.freq.edmg); + wpa_printf(MSG_DEBUG, + "EDMG after: channels 0x%x, bw_config %d", + params.freq.edmg.channels, + params.freq.edmg.bw_config); + } + } + params.pairwise_suite = cipher_pairwise; params.group_suite = cipher_group; params.mgmt_group_suite = cipher_group_mgmt; @@ -3209,12 +3873,18 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) wpa_s->auth_alg = params.auth_alg; params.mode = ssid->mode; params.bg_scan_period = ssid->bg_scan_period; - for (i = 0; i < NUM_WEP_KEYS; i++) { - if (ssid->wep_key_len[i]) - params.wep_key[i] = ssid->wep_key[i]; - params.wep_key_len[i] = ssid->wep_key_len[i]; +#ifdef CONFIG_WEP + { + int i; + + for (i = 0; i < NUM_WEP_KEYS; i++) { + if (ssid->wep_key_len[i]) + params.wep_key[i] = ssid->wep_key[i]; + params.wep_key_len[i] = ssid->wep_key_len[i]; + } + params.wep_tx_keyidx = ssid->wep_tx_keyidx; } - params.wep_tx_keyidx = ssid->wep_tx_keyidx; +#endif /* CONFIG_WEP */ if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK) && (params.key_mgmt_suite == WPA_KEY_MGMT_PSK || @@ -3251,7 +3921,6 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) params.drop_unencrypted = use_crypt; -#ifdef CONFIG_IEEE80211W params.mgmt_frame_protection = wpas_get_ssid_pmf(wpa_s, ssid); if (params.mgmt_frame_protection != NO_MGMT_FRAME_PROTECTION && bss) { const u8 *rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN); @@ -3270,7 +3939,6 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) #endif /* CONFIG_OWE */ } } -#endif /* CONFIG_IEEE80211W */ params.p2p = ssid->p2p_group; @@ -3293,6 +3961,9 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) params.vhtcaps_mask = &vhtcaps_mask; wpa_supplicant_apply_vht_overrides(wpa_s, ssid, ¶ms); #endif /* CONFIG_VHT_OVERRIDES */ +#ifdef CONFIG_HE_OVERRIDES + wpa_supplicant_apply_he_overrides(wpa_s, ssid, ¶ms); +#endif /* CONFIG_HE_OVERRIDES */ #ifdef CONFIG_P2P /* @@ -3321,12 +3992,16 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) wpa_s->current_ssid) params.prev_bssid = prev_bssid; +#ifdef CONFIG_SAE + params.sae_pwe = wpa_s->conf->sae_pwe; +#endif /* CONFIG_SAE */ + ret = wpa_drv_associate(wpa_s, ¶ms); os_free(wpa_ie); if (ret < 0) { wpa_msg(wpa_s, MSG_INFO, "Association request to the driver " "failed"); - if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SANE_ERROR_CODES) { + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_VALID_ERROR_CODES) { /* * The driver is known to mean what is saying, so we * can stop right here; the association will not @@ -3374,11 +4049,13 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) wpa_supplicant_req_auth_timeout(wpa_s, timeout, 0); } +#ifdef CONFIG_WEP if (wep_keys_set && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC)) { /* Set static WEP keys again */ wpa_set_wep_keys(wpa_s, ssid); } +#endif /* CONFIG_WEP */ if (wpa_s->current_ssid && wpa_s->current_ssid != ssid) { /* @@ -3417,6 +4094,9 @@ static void wpa_supplicant_clear_connection(struct wpa_supplicant *wpa_s, eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); if (old_ssid != wpa_s->current_ssid) wpas_notify_network_changed(wpa_s); + + wpas_scs_deinit(wpa_s); + wpas_dscp_deinit(wpa_s); eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL); } @@ -3475,7 +4155,7 @@ void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s, wpa_s->ifname); wpas_notify_mesh_group_removed(wpa_s, mconf->meshid, mconf->meshid_len, reason_code); - wpa_supplicant_leave_mesh(wpa_s); + wpa_supplicant_leave_mesh(wpa_s, true); } #endif /* CONFIG_MESH */ @@ -3492,6 +4172,15 @@ void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s, wpa_supplicant_clear_connection(wpa_s, addr); } + +void wpa_supplicant_reconnect(struct wpa_supplicant *wpa_s) +{ + wpa_s->own_reconnect_req = 1; + wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_UNSPECIFIED); + +} + + static void wpa_supplicant_enable_one_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { @@ -3602,6 +4291,52 @@ int wpa_supplicant_remove_network(struct wpa_supplicant *wpa_s, int id) /** + * wpa_supplicant_remove_all_networks - Remove all configured networks + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: 0 on success (errors are currently ignored) + * + * This function performs the following operations: + * 1. Remove all networks. + * 2. Send network removal notifications. + * 3. Update internal state machines. + * 4. Stop any running sched scans. + */ +int wpa_supplicant_remove_all_networks(struct wpa_supplicant *wpa_s) +{ + struct wpa_ssid *ssid; + + if (wpa_s->sched_scanning) + wpa_supplicant_cancel_sched_scan(wpa_s); + + eapol_sm_invalidate_cached_session(wpa_s->eapol); + if (wpa_s->current_ssid) { +#ifdef CONFIG_SME + wpa_s->sme.prev_bssid_set = 0; +#endif /* CONFIG_SME */ + 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); + } + ssid = wpa_s->conf->ssid; + while (ssid) { + struct wpa_ssid *remove_ssid = ssid; + int id; + + id = ssid->id; + ssid = ssid->next; + if (wpa_s->last_ssid == remove_ssid) + wpa_s->last_ssid = NULL; + wpas_notify_network_removed(wpa_s, remove_ssid); + wpa_config_remove_network(wpa_s->conf, id); + } + 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 @@ -3763,9 +4498,12 @@ void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s, wpa_s->disconnected = 0; wpa_s->reassociate = 1; + wpa_s_clear_sae_rejected(wpa_s); wpa_s->last_owe_group = 0; - if (ssid) + if (ssid) { ssid->owe_transition_bss_select_count = 0; + wpa_s_setup_sae_pt(wpa_s->conf, ssid); + } if (wpa_s->connect_without_scan || wpa_supplicant_fast_associate(wpa_s) != 1) { @@ -3780,6 +4518,82 @@ void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s, /** + * wpas_remove_cred - Remove the specified credential and all the network + * entries created based on the removed credential + * @wpa_s: wpa_supplicant structure for a network interface + * @cred: The credential to remove + * Returns: 0 on success, -1 on failure + */ +int wpas_remove_cred(struct wpa_supplicant *wpa_s, struct wpa_cred *cred) +{ + struct wpa_ssid *ssid, *next; + int id; + + if (!cred) { + wpa_printf(MSG_DEBUG, "Could not find cred"); + return -1; + } + + id = cred->id; + if (wpa_config_remove_cred(wpa_s->conf, id) < 0) { + wpa_printf(MSG_DEBUG, "Could not find cred %d", id); + return -1; + } + + wpa_msg(wpa_s, MSG_INFO, CRED_REMOVED "%d", id); + + /* Remove any network entry created based on the removed credential */ + ssid = wpa_s->conf->ssid; + while (ssid) { + next = ssid->next; + + if (ssid->parent_cred == cred) { + wpa_printf(MSG_DEBUG, + "Remove network id %d since it used the removed credential", + ssid->id); + if (wpa_supplicant_remove_network(wpa_s, ssid->id) == + -1) { + wpa_printf(MSG_DEBUG, + "Could not find network id=%d", + ssid->id); + } + } + + ssid = next; + } + + return 0; +} + + +/** + * wpas_remove_cred - Remove all the Interworking credentials + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: 0 on success, -1 on failure + */ +int wpas_remove_all_creds(struct wpa_supplicant *wpa_s) +{ + int res, ret = 0; + struct wpa_cred *cred, *prev; + + cred = wpa_s->conf->cred; + while (cred) { + prev = cred; + cred = cred->next; + res = wpas_remove_cred(wpa_s, prev); + if (res < 0) { + wpa_printf(MSG_DEBUG, + "Removal of all credentials failed - failed to remove credential id=%d", + prev->id); + ret = -1; + } + } + + return ret; +} + + +/** * wpas_set_pkcs11_engine_and_module_path - Set PKCS #11 engine and module path * @wpa_s: wpa_supplicant structure for a network interface * @pkcs11_engine_path: PKCS #11 engine path or NULL @@ -4126,8 +4940,13 @@ static int wpa_supplicant_set_driver(struct wpa_supplicant *wpa_s, } if (name == NULL) { - /* default to first driver in the list */ - return select_driver(wpa_s, 0); + /* Default to first successful driver in the list */ + for (i = 0; wpa_drivers[i]; i++) { + if (select_driver(wpa_s, i) == 0) + return 0; + } + /* Drivers have each reported failure, so no wpa_msg() here. */ + return -1; } do { @@ -4177,7 +4996,15 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr, wpa_dbg(wpa_s, MSG_DEBUG, "RX EAPOL from " MACSTR, MAC2STR(src_addr)); wpa_hexdump(MSG_MSGDUMP, "RX EAPOL", buf, len); + if (wpa_s->own_disconnect_req) { + wpa_printf(MSG_DEBUG, + "Drop received EAPOL frame as we are disconnecting"); + return; + } + #ifdef CONFIG_TESTING_OPTIONS + wpa_msg_ctrl(wpa_s, MSG_INFO, "EAPOL-RX " MACSTR " %zu", + MAC2STR(src_addr), len); if (wpa_s->ignore_auth_resp) { wpa_printf(MSG_INFO, "RX EAPOL - ignore_auth_resp active!"); return; @@ -4303,16 +5130,23 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr, wpa_sm_rx_eapol(wpa_s->wpa, src_addr, buf, len); else if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) { /* - * Set portValid = TRUE here since we are going to skip 4-way + * Set portValid = true here since we are going to skip 4-way * handshake processing which would normally set portValid. We * need this to allow the EAPOL state machines to be completed * without going through EAPOL-Key handshake. */ - eapol_sm_notify_portValid(wpa_s->eapol, TRUE); + eapol_sm_notify_portValid(wpa_s->eapol, true); } } +static int wpas_eapol_needs_l2_packet(struct wpa_supplicant *wpa_s) +{ + return !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_CONTROL_PORT) || + !(wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_CONTROL_PORT_RX); +} + + int wpa_supplicant_update_mac_addr(struct wpa_supplicant *wpa_s) { if ((!wpa_s->p2p_mgmt || @@ -4322,7 +5156,9 @@ int wpa_supplicant_update_mac_addr(struct wpa_supplicant *wpa_s) wpa_s->l2 = l2_packet_init(wpa_s->ifname, wpa_drv_get_mac_addr(wpa_s), ETH_P_EAPOL, - wpa_supplicant_rx_eapol, wpa_s, 0); + wpas_eapol_needs_l2_packet(wpa_s) ? + wpa_supplicant_rx_eapol : NULL, + wpa_s, 0); if (wpa_s->l2 == NULL) return -1; @@ -4330,18 +5166,25 @@ int wpa_supplicant_update_mac_addr(struct wpa_supplicant *wpa_s) L2_PACKET_FILTER_PKTTYPE)) wpa_dbg(wpa_s, MSG_DEBUG, "Failed to attach pkt_type filter"); + + if (l2_packet_get_own_addr(wpa_s->l2, wpa_s->own_addr)) { + wpa_msg(wpa_s, MSG_ERROR, + "Failed to get own L2 address"); + return -1; + } } else { const u8 *addr = wpa_drv_get_mac_addr(wpa_s); if (addr) os_memcpy(wpa_s->own_addr, addr, ETH_ALEN); } - if (wpa_s->l2 && l2_packet_get_own_addr(wpa_s->l2, wpa_s->own_addr)) { - wpa_msg(wpa_s, MSG_ERROR, "Failed to get own L2 address"); - return -1; - } - wpa_sm_set_own_addr(wpa_s->wpa, wpa_s->own_addr); + wpas_wps_update_mac_addr(wpa_s); + +#ifdef CONFIG_FST + if (wpa_s->fst) + fst_update_mac_addr(wpa_s->fst, wpa_s->own_addr); +#endif /* CONFIG_FST */ return 0; } @@ -4372,6 +5215,65 @@ static void wpa_supplicant_rx_eapol_bridge(void *ctx, const u8 *src_addr, } +int wpa_supplicant_update_bridge_ifname(struct wpa_supplicant *wpa_s, + const char *bridge_ifname) +{ + if (wpa_s->wpa_state > WPA_SCANNING) + return -EBUSY; + + if (bridge_ifname && + os_strlen(bridge_ifname) >= sizeof(wpa_s->bridge_ifname)) + return -EINVAL; + + if (!bridge_ifname) + bridge_ifname = ""; + + if (os_strcmp(wpa_s->bridge_ifname, bridge_ifname) == 0) + return 0; + + if (wpa_s->l2_br) { + l2_packet_deinit(wpa_s->l2_br); + wpa_s->l2_br = NULL; + } + + os_strlcpy(wpa_s->bridge_ifname, bridge_ifname, + sizeof(wpa_s->bridge_ifname)); + + if (wpa_s->bridge_ifname[0]) { + wpa_dbg(wpa_s, MSG_DEBUG, + "Receiving packets from bridge interface '%s'", + wpa_s->bridge_ifname); + wpa_s->l2_br = l2_packet_init_bridge( + wpa_s->bridge_ifname, wpa_s->ifname, wpa_s->own_addr, + ETH_P_EAPOL, wpa_supplicant_rx_eapol_bridge, wpa_s, 1); + if (!wpa_s->l2_br) { + wpa_msg(wpa_s, MSG_ERROR, + "Failed to open l2_packet connection for the bridge interface '%s'", + wpa_s->bridge_ifname); + goto fail; + } + } + +#ifdef CONFIG_TDLS + if (!wpa_s->p2p_mgmt && wpa_tdls_init(wpa_s->wpa)) + goto fail; +#endif /* CONFIG_TDLS */ + + return 0; +fail: + wpa_s->bridge_ifname[0] = 0; + if (wpa_s->l2_br) { + l2_packet_deinit(wpa_s->l2_br); + wpa_s->l2_br = NULL; + } +#ifdef CONFIG_TDLS + if (!wpa_s->p2p_mgmt) + wpa_tdls_init(wpa_s->wpa); +#endif /* CONFIG_TDLS */ + return -EIO; +} + + /** * wpa_supplicant_driver_init - Initialize driver interface parameters * @wpa_s: Pointer to wpa_supplicant data @@ -4393,7 +5295,7 @@ int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s) os_memcpy(wpa_s->perm_addr, wpa_s->own_addr, ETH_ALEN); wpa_sm_set_own_addr(wpa_s->wpa, wpa_s->own_addr); - if (wpa_s->bridge_ifname[0]) { + if (wpa_s->bridge_ifname[0] && wpas_eapol_needs_l2_packet(wpa_s)) { wpa_dbg(wpa_s, MSG_DEBUG, "Receiving packets from bridge " "interface '%s'", wpa_s->bridge_ifname); wpa_s->l2_br = l2_packet_init_bridge( @@ -4467,9 +5369,14 @@ wpa_supplicant_alloc(struct wpa_supplicant *parent) wpa_s->parent = parent ? parent : wpa_s; wpa_s->p2pdev = wpa_s->parent; wpa_s->sched_scanning = 0; + wpa_s->setband_mask = WPA_SETBAND_AUTO; dl_list_init(&wpa_s->bss_tmp_disallowed); dl_list_init(&wpa_s->fils_hlp_req); +#ifdef CONFIG_TESTING_OPTIONS + dl_list_init(&wpa_s->drv_signal_override); +#endif /* CONFIG_TESTING_OPTIONS */ + dl_list_init(&wpa_s->active_scs_ids); return wpa_s; } @@ -4832,6 +5739,19 @@ void wpa_supplicant_apply_vht_overrides( #endif /* CONFIG_VHT_OVERRIDES */ +#ifdef CONFIG_HE_OVERRIDES +void wpa_supplicant_apply_he_overrides( + struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, + struct wpa_driver_associate_params *params) +{ + if (!ssid) + return; + + params->disable_he = ssid->disable_he; +} +#endif /* CONFIG_HE_OVERRIDES */ + + static int pcsc_reader_init(struct wpa_supplicant *wpa_s) { #ifdef PCSC_FUNCS @@ -4995,7 +5915,7 @@ static void wpas_fst_update_mb_ie_cb(void *ctx, const u8 *addr, static const u8 * wpas_fst_get_peer_first(void *ctx, struct fst_get_peer_ctx **get_ctx, - Boolean mb_only) + bool mb_only) { struct wpa_supplicant *wpa_s = ctx; @@ -5009,7 +5929,7 @@ static const u8 * wpas_fst_get_peer_first(void *ctx, static const u8 * wpas_fst_get_peer_next(void *ctx, struct fst_get_peer_ctx **get_ctx, - Boolean mb_only) + bool mb_only) { return NULL; } @@ -5195,7 +6115,7 @@ static struct wpa_radio_work * radio_work_get_next_work(struct wpa_radio *radio) dl_list_for_each(tmp, &radio->work, struct wpa_radio_work, list) { if (os_strcmp(tmp->type, "scan") == 0 && - radio->external_scan_running && + external_scan_running(radio) && (((struct wpa_driver_scan_params *) tmp->ctx)->only_new_results || tmp->wpa_s->clear_driver_scan_cache)) @@ -5251,7 +6171,7 @@ static struct wpa_radio_work * radio_work_get_next_work(struct wpa_radio *radio) * rejected by kernel. */ if (os_strcmp(tmp->type, "scan") == 0 && - radio->external_scan_running && + external_scan_running(radio) && (((struct wpa_driver_scan_params *) tmp->ctx)->only_new_results || tmp->wpa_s->clear_driver_scan_cache)) @@ -5290,7 +6210,7 @@ static void radio_start_next_work(void *eloop_ctx, void *timeout_ctx) if (work->started) return; /* already started and still in progress */ - if (wpa_s && wpa_s->radio->external_scan_running) { + if (wpa_s && external_scan_running(wpa_s->radio)) { wpa_printf(MSG_DEBUG, "Delay radio work start until externally triggered scan completes"); return; } @@ -5386,6 +6306,10 @@ static void radio_remove_interface(struct wpa_supplicant *wpa_s) wpa_s->ifname, radio->name); dl_list_del(&wpa_s->radio_list); radio_remove_works(wpa_s, NULL, 0); + /* If the interface that triggered the external scan was removed, the + * external scan is no longer running. */ + if (wpa_s == radio->external_scan_req_interface) + radio->external_scan_req_interface = NULL; wpa_s->radio = NULL; if (!dl_list_empty(&radio->ifaces)) return; /* Interfaces remain for this radio */ @@ -5539,6 +6463,8 @@ next_driver: wpa_s->drv_priv = wpa_drv_init(wpa_s, wpa_s->ifname); if (wpa_s->drv_priv == NULL) { const char *pos; + int level = MSG_ERROR; + pos = driver ? os_strchr(driver, ',') : NULL; if (pos) { wpa_dbg(wpa_s, MSG_DEBUG, "Failed to initialize " @@ -5546,8 +6472,12 @@ next_driver: driver = pos + 1; goto next_driver; } - wpa_msg(wpa_s, MSG_ERROR, "Failed to initialize driver " - "interface"); + +#ifdef CONFIG_MATCH_IFACE + if (wpa_s->matched == WPA_IFACE_MATCHED_NULL) + level = MSG_DEBUG; +#endif /* CONFIG_MATCH_IFACE */ + wpa_msg(wpa_s, level, "Failed to initialize driver interface"); return -1; } if (wpa_drv_set_param(wpa_s, wpa_s->conf->driver_param) < 0) { @@ -5692,6 +6622,9 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, return -1; } os_strlcpy(wpa_s->ifname, iface->ifname, sizeof(wpa_s->ifname)); +#ifdef CONFIG_MATCH_IFACE + wpa_s->matched = iface->matched; +#endif /* CONFIG_MATCH_IFACE */ if (iface->bridge_ifname) { if (os_strlen(iface->bridge_ifname) >= @@ -5705,8 +6638,8 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, } /* RSNA Supplicant Key Management - INITIALIZE */ - eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE); - eapol_sm_notify_portValid(wpa_s->eapol, FALSE); + eapol_sm_notify_portEnabled(wpa_s->eapol, false); + eapol_sm_notify_portValid(wpa_s->eapol, false); /* Initialize driver interface and register driver event handler before * L2 receive handler so that association events are processed before @@ -5773,8 +6706,8 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, if (capa_res == 0) { wpa_s->drv_capa_known = 1; wpa_s->drv_flags = capa.flags; + wpa_s->drv_flags2 = capa.flags2; wpa_s->drv_enc = capa.enc; - wpa_s->drv_smps_modes = capa.smps_modes; wpa_s->drv_rrm_flags = capa.rrm_flags; wpa_s->probe_resp_offloads = capa.probe_resp_offloads; wpa_s->max_scan_ssids = capa.max_scan_ssids; @@ -5951,7 +6884,7 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, hs20_init(wpa_s); #endif /* CONFIG_HS20 */ #ifdef CONFIG_MBO - if (wpa_s->conf->oce) { + if (!wpa_s->disable_mbo_oce && wpa_s->conf->oce) { if ((wpa_s->conf->oce & OCE_STA) && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OCE_STA)) wpa_s->enable_oce = OCE_STA; @@ -5998,11 +6931,21 @@ static void wpa_supplicant_deinit_iface(struct wpa_supplicant *wpa_s, wpa_s->disconnected = 1; if (wpa_s->drv_priv) { - wpa_supplicant_deauthenticate(wpa_s, - WLAN_REASON_DEAUTH_LEAVING); + /* + * Don't deauthenticate if WoWLAN is enable and not explicitly + * been configured to disconnect. + */ + if (!wpa_drv_get_wowlan(wpa_s) || + wpa_s->conf->wowlan_disconnect_on_deinit) { + wpa_supplicant_deauthenticate( + wpa_s, WLAN_REASON_DEAUTH_LEAVING); - wpa_drv_set_countermeasures(wpa_s, 0); - wpa_clear_keys(wpa_s, NULL); + wpa_drv_set_countermeasures(wpa_s, 0); + wpa_clear_keys(wpa_s, NULL); + } else { + wpa_msg(wpa_s, MSG_INFO, + "Do not deauthenticate as part of interface deinit since WoWLAN is enabled"); + } } wpa_supplicant_cleanup(wpa_s); @@ -6031,14 +6974,12 @@ static void wpa_supplicant_deinit_iface(struct wpa_supplicant *wpa_s, if (terminate) wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_TERMINATING); - if (wpa_s->ctrl_iface) { - wpa_supplicant_ctrl_iface_deinit(wpa_s->ctrl_iface); - wpa_s->ctrl_iface = NULL; - } + wpa_supplicant_ctrl_iface_deinit(wpa_s, wpa_s->ctrl_iface); + wpa_s->ctrl_iface = NULL; #ifdef CONFIG_MESH if (wpa_s->ifmsh) { - wpa_supplicant_mesh_iface_deinit(wpa_s, wpa_s->ifmsh); + wpa_supplicant_mesh_iface_deinit(wpa_s, wpa_s->ifmsh, true); wpa_s->ifmsh = NULL; } #endif /* CONFIG_MESH */ @@ -6049,6 +6990,7 @@ static void wpa_supplicant_deinit_iface(struct wpa_supplicant *wpa_s, } os_free(wpa_s->ssids_from_scan_req); + os_free(wpa_s->last_scan_freqs); os_free(wpa_s); } @@ -6076,6 +7018,10 @@ struct wpa_interface * wpa_supplicant_match_iface(struct wpa_global *global, if (!iface) return NULL; *iface = *miface; + if (!miface->ifname) + iface->matched = WPA_IFACE_MATCHED_NULL; + else + iface->matched = WPA_IFACE_MATCHED; iface->ifname = ifname; return iface; } @@ -6108,10 +7054,8 @@ static int wpa_supplicant_match_existing(struct wpa_global *global) continue; iface = wpa_supplicant_match_iface(global, ifi->if_name); if (iface) { - wpa_s = wpa_supplicant_add_iface(global, iface, NULL); + wpa_supplicant_add_iface(global, iface, NULL); os_free(iface); - if (wpa_s) - wpa_s->matched = 1; } } @@ -6373,7 +7317,7 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params) if (params->wpa_debug_file_path) wpa_debug_open_file(params->wpa_debug_file_path); - else + if (!params->wpa_debug_file_path && !params->wpa_debug_syslog) wpa_debug_setup_stdout(); if (params->wpa_debug_syslog) wpa_debug_open_syslog(); @@ -6441,7 +7385,7 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params) wpa_debug_timestamp = global->params.wpa_debug_timestamp = params->wpa_debug_timestamp; - wpa_printf(MSG_DEBUG, "wpa_supplicant v" VERSION_STR); + wpa_printf(MSG_DEBUG, "wpa_supplicant v%s", VERSION_STR); if (eloop_init()) { wpa_printf(MSG_ERROR, "Failed to initialize event loop"); @@ -6632,6 +7576,18 @@ void wpa_supplicant_update_config(struct wpa_supplicant *wpa_s) if (wpa_s->conf->changed_parameters & CFG_CHANGED_DISABLE_BTM) wpa_supplicant_set_default_scan_ies(wpa_s); +#ifdef CONFIG_BGSCAN + /* + * We default to global bgscan parameters only when per-network bgscan + * parameters aren't set. Only bother resetting bgscan parameters if + * this is the case. + */ + if ((wpa_s->conf->changed_parameters & CFG_CHANGED_BGSCAN) && + wpa_s->current_ssid && !wpa_s->current_ssid->bgscan && + wpa_s->wpa_state == WPA_COMPLETED) + wpa_supplicant_reset_bgscan(wpa_s); +#endif /* CONFIG_BGSCAN */ + #ifdef CONFIG_WPS wpas_wps_update_config(wpa_s); #endif /* CONFIG_WPS */ @@ -6672,7 +7628,7 @@ static int * get_bss_freqs_in_ess(struct wpa_supplicant *wpa_s) continue; if (bss->ssid_len == cbss->ssid_len && os_memcmp(bss->ssid, cbss->ssid, bss->ssid_len) == 0 && - wpa_blacklist_get(wpa_s, bss->bssid) == NULL) { + !wpa_bssid_ignore_is_listed(wpa_s, bss->bssid)) { add_freq(freqs, &num_freqs, bss->freq); if (num_freqs == max_freqs) break; @@ -6702,10 +7658,10 @@ void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid) eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL); /* - * There is no point in blacklisting the AP if this event is + * There is no point in ignoring the AP temporarily if this event is * generated based on local request to disconnect. */ - if (wpa_s->own_disconnect_req) { + if (wpa_s->own_disconnect_req || wpa_s->own_reconnect_req) { wpa_s->own_disconnect_req = 0; wpa_dbg(wpa_s, MSG_DEBUG, "Ignore connection failure due to local request to disconnect"); @@ -6719,17 +7675,13 @@ void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid) } /* - * Add the failed BSSID into the blacklist and speed up next scan + * Add the failed BSSID into the ignore list and speed up next scan * attempt if there could be other APs that could accept association. - * The current blacklist count indicates how many times we have tried - * connecting to this AP and multiple attempts mean that other APs are - * either not available or has already been tried, so that we can start - * increasing the delay here to avoid constant scanning. */ - count = wpa_blacklist_add(wpa_s, bssid); + count = wpa_bssid_ignore_add(wpa_s, bssid); if (count == 1 && wpa_s->current_bss) { /* - * This BSS was not in the blacklist before. If there is + * This BSS was not in the ignore list before. If there is * another BSS available for the same ESS, we should try that * next. Otherwise, we may as well try this one once more * before allowing other, likely worse, ESSes to be considered. @@ -6738,7 +7690,7 @@ void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid) if (freqs) { wpa_dbg(wpa_s, MSG_DEBUG, "Another BSS in this ESS " "has been seen; try it next"); - wpa_blacklist_add(wpa_s, bssid); + wpa_bssid_ignore_add(wpa_s, bssid); /* * On the next scan, go through only the known channels * used in this ESS based on previous scans to speed up @@ -6749,19 +7701,19 @@ void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid) } } - /* - * Add previous failure count in case the temporary blacklist was - * cleared due to no other BSSes being available. - */ - count += wpa_s->extra_blacklist_count; + wpa_s->consecutive_conn_failures++; - if (count > 3 && wpa_s->current_ssid) { + if (wpa_s->consecutive_conn_failures > 3 && wpa_s->current_ssid) { wpa_printf(MSG_DEBUG, "Continuous association failures - " "consider temporary network disabling"); wpas_auth_failed(wpa_s, "CONN_FAILED"); } - - switch (count) { + /* + * Multiple consecutive connection failures mean that other APs are + * either not available or have already been tried, so we can start + * increasing the delay here to avoid constant scanning. + */ + switch (wpa_s->consecutive_conn_failures) { case 1: timeout = 100; break; @@ -6779,8 +7731,9 @@ void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid) break; } - wpa_dbg(wpa_s, MSG_DEBUG, "Blacklist count %d --> request scan in %d " - "ms", count, timeout); + wpa_dbg(wpa_s, MSG_DEBUG, + "Consecutive connection failures: %d --> request scan in %d ms", + wpa_s->consecutive_conn_failures, timeout); /* * TODO: if more than one possible AP is available in scan results, @@ -6795,6 +7748,46 @@ void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid) #ifdef CONFIG_FILS + +void fils_pmksa_cache_flush(struct wpa_supplicant *wpa_s) +{ + struct wpa_ssid *ssid = wpa_s->current_ssid; + const u8 *realm, *username, *rrk; + size_t realm_len, username_len, rrk_len; + u16 next_seq_num; + + /* Clear the PMKSA cache entry if FILS authentication was rejected. + * Check for ERP keys existing to limit when this can be done since + * the rejection response is not protected and such triggers should + * really not allow internal state to be modified unless required to + * avoid significant issues in functionality. In addition, drop + * externally configure PMKSA entries even without ERP keys since it + * is possible for an external component to add PMKSA entries for FILS + * authentication without restoring previously generated ERP keys. + * + * In this case, this is needed to allow recovery from cases where the + * AP or authentication server has dropped PMKSAs and ERP keys. */ + if (!ssid || !ssid->eap.erp || !wpa_key_mgmt_fils(ssid->key_mgmt)) + return; + + if (eapol_sm_get_erp_info(wpa_s->eapol, &ssid->eap, + &username, &username_len, + &realm, &realm_len, &next_seq_num, + &rrk, &rrk_len) != 0 || + !realm) { + wpa_dbg(wpa_s, MSG_DEBUG, + "FILS: Drop external PMKSA cache entry"); + wpa_sm_aborted_external_cached(wpa_s->wpa); + wpa_sm_external_pmksa_cache_flush(wpa_s->wpa, ssid); + return; + } + + wpa_dbg(wpa_s, MSG_DEBUG, "FILS: Drop PMKSA cache entry"); + wpa_sm_aborted_cached(wpa_s->wpa); + wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid); +} + + void fils_connection_failure(struct wpa_supplicant *wpa_s) { struct wpa_ssid *ssid = wpa_s->current_ssid; @@ -6869,8 +7862,8 @@ int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s, wpa_s->reassociate = 1; break; case WPA_CTRL_REQ_EAP_PIN: - str_clear_free(eap->pin); - eap->pin = os_strdup(value); + str_clear_free(eap->cert.pin); + eap->cert.pin = os_strdup(value); eap->pending_req_pin = 0; if (ssid == wpa_s->current_ssid) wpa_s->reassociate = 1; @@ -6884,8 +7877,8 @@ int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s, eap->pending_req_otp_len = 0; break; case WPA_CTRL_REQ_EAP_PASSPHRASE: - str_clear_free(eap->private_key_passwd); - eap->private_key_passwd = os_strdup(value); + str_clear_free(eap->cert.private_key_passwd); + eap->cert.private_key_passwd = os_strdup(value); eap->pending_req_passphrase = 0; if (ssid == wpa_s->current_ssid) wpa_s->reassociate = 1; @@ -6930,8 +7923,10 @@ int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s, int wpas_network_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { +#ifdef CONFIG_WEP int i; unsigned int drv_enc; +#endif /* CONFIG_WEP */ if (wpa_s->p2p_mgmt) return 1; /* no normal network profiles on p2p_mgmt interface */ @@ -6942,6 +7937,7 @@ int wpas_network_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) if (ssid->disabled) return 1; +#ifdef CONFIG_WEP if (wpa_s->drv_capa_known) drv_enc = wpa_s->drv_enc; else @@ -6959,6 +7955,7 @@ int wpas_network_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) continue; return 1; /* invalid WEP key */ } +#endif /* CONFIG_WEP */ if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt) && !ssid->psk_set && (!ssid->passphrase || ssid->ssid_len != 0) && !ssid->ext_psk && @@ -6972,7 +7969,6 @@ int wpas_network_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) int wpas_get_ssid_pmf(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { -#ifdef CONFIG_IEEE80211W if (ssid == NULL || ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT) { if (wpa_s->conf->pmf == MGMT_FRAME_PROTECTION_OPTIONAL && !(wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_BIP)) { @@ -7001,9 +7997,16 @@ int wpas_get_ssid_pmf(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) } return ssid->ieee80211w; -#else /* CONFIG_IEEE80211W */ - return NO_MGMT_FRAME_PROTECTION; -#endif /* CONFIG_IEEE80211W */ +} + + +int pmf_in_use(struct wpa_supplicant *wpa_s, const u8 *addr) +{ + if (wpa_s->current_ssid == NULL || + wpa_s->wpa_state < WPA_4WAY_HANDSHAKE || + os_memcmp(addr, wpa_s->bssid, ETH_ALEN) != 0) + return 0; + return wpa_sm_pmf_enabled(wpa_s->wpa); } @@ -7144,7 +8147,6 @@ void wpas_request_connection(struct wpa_supplicant *wpa_s) wpa_s->normal_scans = 0; wpa_s->scan_req = NORMAL_SCAN_REQ; wpa_supplicant_reinit_autoscan(wpa_s); - wpa_s->extra_blacklist_count = 0; wpa_s->disconnected = 0; wpa_s->reassociate = 1; wpa_s->last_owe_group = 0; @@ -7176,6 +8178,10 @@ void wpas_request_disconnection(struct wpa_supplicant *wpa_s) eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL); radio_remove_works(wpa_s, "connect", 0); radio_remove_works(wpa_s, "sme-connect", 0); + wpa_s->roam_in_progress = false; +#ifdef CONFIG_WNM + wpa_s->bss_trans_mgmt_in_progress = false; +#endif /* CONFIG_WNM */ } @@ -7360,12 +8366,20 @@ int wpas_vendor_elem_remove(struct wpa_supplicant *wpa_s, int frame, struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes, - u16 num_modes, enum hostapd_hw_mode mode) + u16 num_modes, enum hostapd_hw_mode mode, + bool is_6ghz) { u16 i; + if (!modes) + return NULL; + for (i = 0; i < num_modes; i++) { - if (modes[i].mode == mode) + if (modes[i].mode != mode || + !modes[i].num_channels || !modes[i].channels) + continue; + if ((!is_6ghz && !is_6ghz_freq(modes[i].channels[0].freq)) || + (is_6ghz && is_6ghz_freq(modes[i].channels[0].freq))) return &modes[i]; } @@ -7373,6 +8387,22 @@ struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes, } +struct hostapd_hw_modes * get_mode_with_freq(struct hostapd_hw_modes *modes, + u16 num_modes, int freq) +{ + int i, j; + + for (i = 0; i < num_modes; i++) { + for (j = 0; j < modes[i].num_channels; j++) { + if (freq == modes[i].channels[j].freq) + return &modes[i]; + } + } + + return NULL; +} + + static struct wpa_bss_tmp_disallowed * wpas_get_disallowed_bss(struct wpa_supplicant *wpa_s, const u8 *bssid) @@ -7405,7 +8435,7 @@ static int wpa_set_driver_tmp_disallow_list(struct wpa_supplicant *wpa_s) ETH_ALEN); num_bssid++; } - ret = wpa_drv_set_bssid_blacklist(wpa_s, num_bssid, bssids); + ret = wpa_drv_set_bssid_tmp_disallow(wpa_s, num_bssid, bssids); os_free(bssids); return ret; } @@ -7420,8 +8450,7 @@ static void wpa_bss_tmp_disallow_timeout(void *eloop_ctx, void *timeout_ctx) dl_list_for_each(tmp, &wpa_s->bss_tmp_disallowed, struct wpa_bss_tmp_disallowed, list) { if (bss == tmp) { - dl_list_del(&tmp->list); - os_free(tmp); + remove_bss_tmp_disallowed_entry(wpa_s, tmp); wpa_set_driver_tmp_disallow_list(wpa_s); break; } @@ -7474,8 +8503,11 @@ int wpa_is_bss_tmp_disallowed(struct wpa_supplicant *wpa_s, return 0; if (disallowed->rssi_threshold != 0 && - bss->level > disallowed->rssi_threshold) + bss->level > disallowed->rssi_threshold) { + remove_bss_tmp_disallowed_entry(wpa_s, disallowed); + wpa_set_driver_tmp_disallow_list(wpa_s); return 0; + } return 1; } @@ -7542,3 +8574,87 @@ int wpas_disable_mac_addr_randomization(struct wpa_supplicant *wpa_s, return 0; } + + +int wpa_drv_signal_poll(struct wpa_supplicant *wpa_s, + struct wpa_signal_info *si) +{ + int res; + + if (!wpa_s->driver->signal_poll) + return -1; + + res = wpa_s->driver->signal_poll(wpa_s->drv_priv, si); + +#ifdef CONFIG_TESTING_OPTIONS + if (res == 0) { + struct driver_signal_override *dso; + + dl_list_for_each(dso, &wpa_s->drv_signal_override, + struct driver_signal_override, list) { + if (os_memcmp(wpa_s->bssid, dso->bssid, + ETH_ALEN) != 0) + continue; + wpa_printf(MSG_DEBUG, + "Override driver signal_poll information: current_signal: %d->%d avg_signal: %d->%d avg_beacon_signal: %d->%d current_noise: %d->%d", + si->current_signal, + dso->si_current_signal, + si->avg_signal, + dso->si_avg_signal, + si->avg_beacon_signal, + dso->si_avg_beacon_signal, + si->current_noise, + dso->si_current_noise); + si->current_signal = dso->si_current_signal; + si->avg_signal = dso->si_avg_signal; + si->avg_beacon_signal = dso->si_avg_beacon_signal; + si->current_noise = dso->si_current_noise; + break; + } + } +#endif /* CONFIG_TESTING_OPTIONS */ + + return res; +} + + +struct wpa_scan_results * +wpa_drv_get_scan_results2(struct wpa_supplicant *wpa_s) +{ + struct wpa_scan_results *scan_res; +#ifdef CONFIG_TESTING_OPTIONS + size_t idx; +#endif /* CONFIG_TESTING_OPTIONS */ + + if (!wpa_s->driver->get_scan_results2) + return NULL; + + scan_res = wpa_s->driver->get_scan_results2(wpa_s->drv_priv); + +#ifdef CONFIG_TESTING_OPTIONS + for (idx = 0; scan_res && idx < scan_res->num; idx++) { + struct driver_signal_override *dso; + struct wpa_scan_res *res = scan_res->res[idx]; + + dl_list_for_each(dso, &wpa_s->drv_signal_override, + struct driver_signal_override, list) { + if (os_memcmp(res->bssid, dso->bssid, ETH_ALEN) != 0) + continue; + wpa_printf(MSG_DEBUG, + "Override driver scan signal level %d->%d for " + MACSTR, + res->level, dso->scan_level, + MAC2STR(res->bssid)); + res->flags |= WPA_SCAN_QUAL_INVALID; + if (dso->scan_level < 0) + res->flags |= WPA_SCAN_LEVEL_DBM; + else + res->flags &= ~WPA_SCAN_LEVEL_DBM; + res->level = dso->scan_level; + break; + } + } +#endif /* CONFIG_TESTING_OPTIONS */ + + return scan_res; +} |