/*
* WPA Supplicant
- * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
#include "crypto/sha1.h"
#include "eapol_supp/eapol_supp_sm.h"
#include "eap_peer/eap.h"
+#include "eap_peer/eap_proxy.h"
#include "eap_server/eap_methods.h"
#include "rsn_supp/wpa.h"
#include "eloop.h"
#include "scan.h"
#include "offchannel.h"
#include "hs20_supplicant.h"
+#include "wnm_sta.h"
const char *wpa_supplicant_version =
"wpa_supplicant v" VERSION_STR "\n"
-"Copyright (c) 2003-2013, Jouni Malinen <j@w1.fi> and contributors";
+"Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi> and contributors";
const char *wpa_supplicant_license =
"This software may be distributed under the terms of the BSD license.\n"
"\n";
#endif /* CONFIG_NO_STDOUT_DEBUG */
-extern int wpa_debug_level;
-extern int wpa_debug_show_keys;
-extern int wpa_debug_timestamp;
-extern struct wpa_driver_ops *wpa_drivers[];
-
/* Configure default/group WEP keys for static WEP */
int wpa_set_wep_keys(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
{
}
-static int wpa_supplicant_set_wpa_none_key(struct wpa_supplicant *wpa_s,
- struct wpa_ssid *ssid)
+int wpa_supplicant_set_wpa_none_key(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
{
u8 key[32];
size_t keylen;
* So, wait a second until scanning again.
*/
wpa_supplicant_req_scan(wpa_s, 1, 0);
-
- wpas_p2p_continue_after_scan(wpa_s);
}
void wpa_supplicant_req_auth_timeout(struct wpa_supplicant *wpa_s,
int sec, int usec)
{
- if (wpa_s->conf && wpa_s->conf->ap_scan == 0 &&
+ if (wpa_s->conf->ap_scan == 0 &&
(wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED))
return;
EAPOL_REQUIRE_KEY_BROADCAST;
}
- if (wpa_s->conf && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED))
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED)
eapol_conf.required_keys = 0;
}
- if (wpa_s->conf)
- eapol_conf.fast_reauth = wpa_s->conf->fast_reauth;
+ eapol_conf.fast_reauth = wpa_s->conf->fast_reauth;
eapol_conf.workaround = ssid->eap_workaround;
eapol_conf.eap_disabled =
!wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) &&
wpa_s->key_mgmt != WPA_KEY_MGMT_IEEE8021X_NO_WPA &&
wpa_s->key_mgmt != WPA_KEY_MGMT_WPS;
+ eapol_conf.external_sim = wpa_s->conf->external_sim;
eapol_sm_notify_config(wpa_s->eapol, &ssid->eap, &eapol_conf);
#endif /* IEEE8021X_EAPOL */
}
os_free(wpa_s->confname);
wpa_s->confname = NULL;
+ os_free(wpa_s->confanother);
+ wpa_s->confanother = NULL;
+
wpa_sm_set_eapol(wpa_s->wpa, NULL);
eapol_sm_deinit(wpa_s->eapol);
wpa_s->eapol = NULL;
wpa_bss_deinit(wpa_s);
+ wpa_supplicant_cancel_delayed_sched_scan(wpa_s);
wpa_supplicant_cancel_scan(wpa_s);
wpa_supplicant_cancel_auth_timeout(wpa_s);
eloop_cancel_timeout(wpa_supplicant_stop_countermeasures, wpa_s, NULL);
os_free(wpa_s->next_scan_freqs);
wpa_s->next_scan_freqs = NULL;
+ os_free(wpa_s->manual_scan_freqs);
+ wpa_s->manual_scan_freqs = NULL;
+
gas_query_deinit(wpa_s->gas);
wpa_s->gas = NULL;
wpa_s->disallow_aps_ssid = NULL;
wnm_bss_keep_alive_deinit(wpa_s);
+#ifdef CONFIG_WNM
+ wnm_deallocate_memory(wpa_s);
+#endif /* CONFIG_WNM */
ext_password_deinit(wpa_s->ext_pw);
wpa_s->ext_pw = NULL;
*/
void wpa_clear_keys(struct wpa_supplicant *wpa_s, const u8 *addr)
{
- if (wpa_s->keys_cleared) {
- /* Some drivers (e.g., ndiswrapper & NDIS drivers) seem to have
- * timing issues with keys being cleared just before new keys
- * are set or just after association or something similar. This
- * shows up in group key handshake failing often because of the
- * client not receiving the first encrypted packets correctly.
- * Skipping some of the extra key clearing steps seems to help
- * in completing group key handshake more reliably. */
- wpa_dbg(wpa_s, MSG_DEBUG, "No keys have been configured - "
- "skip key clearing");
- return;
- }
+ int i, max;
- /* MLME-DELETEKEYS.request */
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 0, 0, NULL, 0, NULL, 0);
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 1, 0, NULL, 0, NULL, 0);
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 2, 0, NULL, 0, NULL, 0);
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 3, 0, NULL, 0, NULL, 0);
#ifdef CONFIG_IEEE80211W
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 4, 0, NULL, 0, NULL, 0);
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 5, 0, NULL, 0, NULL, 0);
+ max = 6;
+#else /* CONFIG_IEEE80211W */
+ max = 4;
#endif /* CONFIG_IEEE80211W */
- if (addr) {
+
+ /* 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);
+ }
+ if (!(wpa_s->keys_cleared & BIT(0)) && addr &&
+ !is_zero_ether_addr(addr)) {
wpa_drv_set_key(wpa_s, WPA_ALG_NONE, addr, 0, 0, NULL, 0, NULL,
0);
/* MLME-SETPROTECTION.request(None) */
MLME_SETPROTECTION_PROTECT_TYPE_NONE,
MLME_SETPROTECTION_KEY_TYPE_PAIRWISE);
}
- wpa_s->keys_cleared = 1;
+ wpa_s->keys_cleared = (u32) -1;
}
static void wpa_supplicant_start_bgscan(struct wpa_supplicant *wpa_s)
{
+ const char *name;
+
+ if (wpa_s->current_ssid && wpa_s->current_ssid->bgscan)
+ name = wpa_s->current_ssid->bgscan;
+ else
+ name = wpa_s->conf->bgscan;
+ if (name == NULL)
+ return;
if (wpas_driver_bss_selection(wpa_s))
return;
if (wpa_s->current_ssid == wpa_s->bgscan_ssid)
return;
bgscan_deinit(wpa_s);
- if (wpa_s->current_ssid && wpa_s->current_ssid->bgscan) {
- if (bgscan_init(wpa_s, wpa_s->current_ssid)) {
+ if (wpa_s->current_ssid) {
+ if (bgscan_init(wpa_s, wpa_s->current_ssid, name)) {
wpa_dbg(wpa_s, MSG_DEBUG, "Failed to initialize "
"bgscan");
/*
wpa_drv_set_supp_port(wpa_s, 1);
#endif /* IEEE8021X_EAPOL */
wpa_s->after_wps = 0;
+ wpa_s->known_wps_freq = 0;
#ifdef CONFIG_P2P
wpas_p2p_completed(wpa_s);
#endif /* CONFIG_P2P */
#ifdef CONFIG_BGSCAN
if (state == WPA_COMPLETED)
wpa_supplicant_start_bgscan(wpa_s);
- else
+ else if (state < WPA_ASSOCIATED)
wpa_supplicant_stop_bgscan(wpa_s);
#endif /* CONFIG_BGSCAN */
#ifdef CONFIG_WPS
struct wpa_supplicant *wpa_s = global->ifaces;
while (wpa_s) {
+ struct wpa_supplicant *next = wpa_s->next;
+#ifdef CONFIG_P2P
+ if (wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE ||
+ (wpa_s->current_ssid && wpa_s->current_ssid->p2p_group))
+ wpas_p2p_disconnect(wpa_s);
+#endif /* CONFIG_P2P */
if (wpas_wps_terminate_pending(wpa_s) == 1)
pending = 1;
- wpa_s = wpa_s->next;
+ wpa_s = next;
}
#endif /* CONFIG_WPS */
if (pending)
if (wpa_s->confname == NULL)
return -1;
- conf = wpa_config_read(wpa_s->confname);
+ conf = wpa_config_read(wpa_s->confname, NULL);
if (conf == NULL) {
wpa_msg(wpa_s, MSG_ERROR, "Failed to parse the configuration "
"file '%s' - exiting", wpa_s->confname);
return -1;
}
+ wpa_config_read(wpa_s->confanother, conf);
+
conf->changed_parameters = (unsigned int) -1;
reconf_ctrl = !!conf->ctrl_interface != !!wpa_s->conf->ctrl_interface
}
-enum wpa_key_mgmt key_mgmt2driver(int key_mgmt)
-{
- switch (key_mgmt) {
- case WPA_KEY_MGMT_NONE:
- return KEY_MGMT_NONE;
- case WPA_KEY_MGMT_IEEE8021X_NO_WPA:
- return KEY_MGMT_802_1X_NO_WPA;
- case WPA_KEY_MGMT_IEEE8021X:
- return KEY_MGMT_802_1X;
- case WPA_KEY_MGMT_WPA_NONE:
- return KEY_MGMT_WPA_NONE;
- case WPA_KEY_MGMT_FT_IEEE8021X:
- return KEY_MGMT_FT_802_1X;
- case WPA_KEY_MGMT_FT_PSK:
- return KEY_MGMT_FT_PSK;
- case WPA_KEY_MGMT_IEEE8021X_SHA256:
- return KEY_MGMT_802_1X_SHA256;
- case WPA_KEY_MGMT_PSK_SHA256:
- return KEY_MGMT_PSK_SHA256;
- case WPA_KEY_MGMT_WPS:
- return KEY_MGMT_WPS;
- case WPA_KEY_MGMT_PSK:
- default:
- return KEY_MGMT_PSK;
- }
-}
-
-
static int wpa_supplicant_suites_from_ai(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid,
struct wpa_ie_data *ie)
}
-int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf)
+static void wpas_ext_capab_byte(struct wpa_supplicant *wpa_s, u8 *pos, int idx)
{
- u32 ext_capab = 0;
- u8 *pos = buf;
+ *pos = 0x00;
+ switch (idx) {
+ case 0: /* Bits 0-7 */
+ break;
+ case 1: /* Bits 8-15 */
+ break;
+ case 2: /* Bits 16-23 */
+#ifdef CONFIG_WNM
+ *pos |= 0x02; /* Bit 17 - WNM-Sleep Mode */
+ *pos |= 0x08; /* Bit 19 - BSS Transition */
+#endif /* CONFIG_WNM */
+ break;
+ case 3: /* Bits 24-31 */
+#ifdef CONFIG_WNM
+ *pos |= 0x02; /* Bit 25 - SSID List */
+#endif /* CONFIG_WNM */
#ifdef CONFIG_INTERWORKING
- if (wpa_s->conf->interworking)
- ext_capab |= BIT(31); /* Interworking */
+ if (wpa_s->conf->interworking)
+ *pos |= 0x80; /* Bit 31 - Interworking */
#endif /* CONFIG_INTERWORKING */
+ break;
+ case 4: /* Bits 32-39 */
+#ifdef CONFIG_INTERWORKING
+ if (wpa_s->drv_flags / WPA_DRIVER_FLAGS_QOS_MAPPING)
+ *pos |= 0x01; /* Bit 32 - QoS Map */
+#endif /* CONFIG_INTERWORKING */
+ break;
+ case 5: /* Bits 40-47 */
+ break;
+ case 6: /* Bits 48-55 */
+ break;
+ }
+}
-#ifdef CONFIG_WNM
- ext_capab |= BIT(17); /* WNM-Sleep Mode */
- ext_capab |= BIT(19); /* BSS Transition */
-#endif /* CONFIG_WNM */
- if (!ext_capab)
- return 0;
+int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf)
+{
+ u8 *pos = buf;
+ u8 len = 4, i;
+
+ if (len < wpa_s->extended_capa_len)
+ len = wpa_s->extended_capa_len;
*pos++ = WLAN_EID_EXT_CAPAB;
- *pos++ = 4;
- WPA_PUT_LE32(pos, ext_capab);
- pos += 4;
+ *pos++ = len;
+ for (i = 0; i < len; i++, pos++) {
+ wpas_ext_capab_byte(wpa_s, pos, i);
- return pos - buf;
+ if (i < wpa_s->extended_capa_len) {
+ *pos &= ~wpa_s->extended_capa_mask[i];
+ *pos |= wpa_s->extended_capa[i];
+ }
+ }
+
+ while (len > 0 && buf[1 + len] == 0) {
+ len--;
+ buf[1] = len;
+ }
+ if (len == 0)
+ return 0;
+
+ return 2 + len;
}
size_t wpa_ie_len;
int use_crypt, ret, i, bssid_changed;
int algs = WPA_AUTH_ALG_OPEN;
- enum wpa_cipher cipher_pairwise, cipher_group;
+ unsigned int cipher_pairwise, cipher_group;
struct wpa_driver_associate_params params;
int wep_keys_set = 0;
- struct wpa_driver_capa capa;
int assoc_failed = 0;
struct wpa_ssid *old_ssid;
- u8 ext_capab[10];
- int ext_capab_len;
#ifdef CONFIG_HT_OVERRIDES
struct ieee80211_ht_capabilities htcaps;
struct ieee80211_ht_capabilities htcaps_mask;
}
if (wpa_supplicant_create_ap(wpa_s, ssid) < 0) {
wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
+ if (ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION)
+ wpas_p2p_ap_setup_failed(wpa_s);
return;
}
wpa_s->current_bss = bss;
#endif /* CONFIG_P2P */
#ifdef CONFIG_HS20
- if (wpa_s->conf->hs20) {
+ if (is_hs20_network(wpa_s, ssid, bss)) {
struct wpabuf *hs20;
hs20 = wpabuf_alloc(20);
if (hs20) {
}
#endif /* CONFIG_HS20 */
- ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab);
- if (ext_capab_len > 0) {
- u8 *pos = wpa_ie;
- if (wpa_ie_len > 0 && pos[0] == WLAN_EID_RSN)
- pos += 2 + pos[1];
- os_memmove(pos + ext_capab_len, pos,
- wpa_ie_len - (pos - wpa_ie));
- wpa_ie_len += ext_capab_len;
- os_memcpy(pos, ext_capab, ext_capab_len);
+ /*
+ * Workaround: Add Extended Capabilities element only if the AP
+ * included this element in Beacon/Probe Response frames. Some older
+ * APs seem to have interoperability issues if this element is
+ * included, so while the standard may require us to include the
+ * element in all cases, it is justifiable to skip it to avoid
+ * interoperability issues.
+ */
+ if (!bss || wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB)) {
+ u8 ext_capab[10];
+ int ext_capab_len;
+ ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab);
+ if (ext_capab_len > 0) {
+ u8 *pos = wpa_ie;
+ if (wpa_ie_len > 0 && pos[0] == WLAN_EID_RSN)
+ pos += 2 + pos[1];
+ os_memmove(pos + ext_capab_len, pos,
+ wpa_ie_len - (pos - wpa_ie));
+ wpa_ie_len += ext_capab_len;
+ os_memcpy(pos, ext_capab, ext_capab_len);
+ }
}
wpa_clear_keys(wpa_s, bss ? bss->bssid : NULL);
use_crypt = 1;
- cipher_pairwise = wpa_cipher_to_suite_driver(wpa_s->pairwise_cipher);
- cipher_group = wpa_cipher_to_suite_driver(wpa_s->group_cipher);
+ cipher_pairwise = wpa_s->pairwise_cipher;
+ cipher_group = wpa_s->group_cipher;
if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE ||
wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE)
/* Assume that dynamic WEP-104 keys will be used and
* set cipher suites in order for drivers to expect
* encryption. */
- cipher_pairwise = cipher_group = CIPHER_WEP104;
+ cipher_pairwise = cipher_group = WPA_CIPHER_WEP104;
}
}
#endif /* IEEE8021X_EAPOL */
params.wpa_ie_len = wpa_ie_len;
params.pairwise_suite = cipher_pairwise;
params.group_suite = cipher_group;
- params.key_mgmt_suite = key_mgmt2driver(wpa_s->key_mgmt);
+ params.key_mgmt_suite = wpa_s->key_mgmt;
params.wpa_proto = wpa_s->wpa_proto;
params.auth_alg = algs;
params.mode = ssid->mode;
params.wep_tx_keyidx = ssid->wep_tx_keyidx;
if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE) &&
- (params.key_mgmt_suite == KEY_MGMT_PSK ||
- params.key_mgmt_suite == KEY_MGMT_FT_PSK)) {
+ (params.key_mgmt_suite == WPA_KEY_MGMT_PSK ||
+ params.key_mgmt_suite == WPA_KEY_MGMT_FT_PSK)) {
params.passphrase = ssid->passphrase;
if (ssid->psk_set)
params.psk = ssid->psk;
wpa_supplicant_apply_ht_overrides(wpa_s, ssid, ¶ms);
#endif /* CONFIG_HT_OVERRIDES */
+#ifdef CONFIG_P2P
+ /*
+ * If multi-channel concurrency is not supported, check for any
+ * frequency conflict. In case of any frequency conflict, remove the
+ * least prioritized connection.
+ */
+ if (wpa_s->num_multichan_concurrent < 2) {
+ int freq = wpa_drv_shared_freq(wpa_s);
+ if (freq > 0 && freq != params.freq) {
+ wpa_printf(MSG_DEBUG, "Shared interface with conflicting frequency found (%d != %d)",
+ freq, params.freq);
+ if (wpas_p2p_handle_frequency_conflicts(wpa_s,
+ params.freq,
+ ssid) < 0)
+ return;
+ }
+ }
+#endif /* CONFIG_P2P */
+
ret = wpa_drv_associate(wpa_s, ¶ms);
if (ret < 0) {
wpa_msg(wpa_s, MSG_INFO, "Association request to the driver "
wpa_supplicant_req_auth_timeout(wpa_s, timeout, 0);
}
- if (wep_keys_set && wpa_drv_get_capa(wpa_s, &capa) == 0 &&
- capa.flags & WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC) {
+ 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);
}
* Try to reassociate since there is no current configuration and a new
* network was made available.
*/
- if (!wpa_s->current_ssid)
+ if (!wpa_s->current_ssid && !wpa_s->disconnected)
wpa_s->reassociate = 1;
}
} else
wpa_supplicant_enable_one_network(wpa_s, ssid);
- if (wpa_s->reassociate) {
+ if (wpa_s->reassociate && !wpa_s->disconnected) {
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);
}
- wpa_supplicant_req_scan(wpa_s, 0, 0);
+ if (wpa_supplicant_fast_associate(wpa_s) != 1)
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
}
}
return;
}
- if (ssid)
+ if (ssid) {
wpa_s->current_ssid = ssid;
+ eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
+ }
wpa_s->connect_without_scan = NULL;
wpa_s->disconnected = 0;
wpa_s->reassociate = 1;
}
+/**
+ * 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
+ * @pkcs11_module_path: PKCS #11 module path or NULL
+ * Returns: 0 on success; -1 on failure
+ *
+ * Sets the PKCS #11 engine and module path. Both have to be NULL or a valid
+ * path. If resetting the EAPOL state machine with the new PKCS #11 engine and
+ * module path fails the paths will be reset to the default value (NULL).
+ */
+int wpas_set_pkcs11_engine_and_module_path(struct wpa_supplicant *wpa_s,
+ const char *pkcs11_engine_path,
+ const char *pkcs11_module_path)
+{
+ char *pkcs11_engine_path_copy = NULL;
+ char *pkcs11_module_path_copy = NULL;
+
+ if (pkcs11_engine_path != NULL) {
+ pkcs11_engine_path_copy = os_strdup(pkcs11_engine_path);
+ if (pkcs11_engine_path_copy == NULL)
+ return -1;
+ }
+ if (pkcs11_module_path != NULL) {
+ pkcs11_module_path_copy = os_strdup(pkcs11_module_path);
+ if (pkcs11_engine_path_copy == NULL) {
+ os_free(pkcs11_engine_path_copy);
+ return -1;
+ }
+ }
+
+ os_free(wpa_s->conf->pkcs11_engine_path);
+ os_free(wpa_s->conf->pkcs11_module_path);
+ wpa_s->conf->pkcs11_engine_path = pkcs11_engine_path_copy;
+ wpa_s->conf->pkcs11_module_path = pkcs11_module_path_copy;
+
+ wpa_sm_set_eapol(wpa_s->wpa, NULL);
+ eapol_sm_deinit(wpa_s->eapol);
+ wpa_s->eapol = NULL;
+ if (wpa_supplicant_init_eapol(wpa_s)) {
+ /* Error -> Reset paths to the default value (NULL) once. */
+ if (pkcs11_engine_path != NULL && pkcs11_module_path != NULL)
+ wpas_set_pkcs11_engine_and_module_path(wpa_s, NULL,
+ NULL);
+
+ return -1;
+ }
+ wpa_sm_set_eapol(wpa_s->wpa, wpa_s->eapol);
+
+ return 0;
+}
+
+
/**
* wpa_supplicant_set_ap_scan - Set AP scan mode for interface
* @wpa_s: wpa_supplicant structure for a network interface
wpa_dbg(wpa_s, MSG_DEBUG, "RX EAPOL from " MACSTR, MAC2STR(src_addr));
wpa_hexdump(MSG_MSGDUMP, "RX EAPOL", buf, len);
+#ifdef CONFIG_PEERKEY
+ if (wpa_s->wpa_state > WPA_ASSOCIATED && wpa_s->current_ssid &&
+ wpa_s->current_ssid->peerkey &&
+ !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE) &&
+ wpa_sm_rx_eapol_peerkey(wpa_s->wpa, src_addr, buf, len) == 1) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "RSN: Processed PeerKey EAPOL-Key");
+ return;
+ }
+#endif /* CONFIG_PEERKEY */
+
if (wpa_s->wpa_state < WPA_ASSOCIATED ||
(wpa_s->last_eapol_matches_bssid &&
#ifdef CONFIG_AP
wpabuf_free(wpa_s->pending_eapol_rx);
wpa_s->pending_eapol_rx = wpabuf_alloc_copy(buf, len);
if (wpa_s->pending_eapol_rx) {
- os_get_time(&wpa_s->pending_eapol_rx_time);
+ os_get_reltime(&wpa_s->pending_eapol_rx_time);
os_memcpy(wpa_s->pending_eapol_rx_src, src_addr,
ETH_ALEN);
}
const u8 *addr = wpa_drv_get_mac_addr(wpa_s);
if (addr)
os_memcpy(wpa_s->own_addr, addr, ETH_ALEN);
- } else if (!(wpa_s->drv_flags &
+ } else if ((!wpa_s->p2p_mgmt ||
+ !(wpa_s->drv_flags &
+ WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)) &&
+ !(wpa_s->drv_flags &
WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE)) {
l2_packet_deinit(wpa_s->l2);
wpa_s->l2 = l2_packet_init(wpa_s->ifname,
return -1;
}
- wpa_dbg(wpa_s, MSG_DEBUG, "Own MAC address: " MACSTR,
- MAC2STR(wpa_s->own_addr));
- wpa_sm_set_own_addr(wpa_s->wpa, wpa_s->own_addr);
-
return 0;
}
if (wpa_supplicant_update_mac_addr(wpa_s) < 0)
return -1;
+ wpa_dbg(wpa_s, MSG_DEBUG, "Own MAC address: " MACSTR,
+ MAC2STR(wpa_s->own_addr));
+ wpa_sm_set_own_addr(wpa_s->wpa, wpa_s->own_addr);
+
if (wpa_s->bridge_ifname[0]) {
wpa_dbg(wpa_s, MSG_DEBUG, "Receiving packets from bridge "
"interface '%s'", wpa_s->bridge_ifname);
if (!wpa_s->conf->pcsc_reader)
return 0;
- wpa_s->scard = scard_init(SCARD_TRY_BOTH, wpa_s->conf->pcsc_reader);
+ wpa_s->scard = scard_init(wpa_s->conf->pcsc_reader);
if (!wpa_s->scard)
return 1;
}
+static struct wpa_radio * radio_add_interface(struct wpa_supplicant *wpa_s,
+ const char *rn)
+{
+ struct wpa_supplicant *iface = wpa_s->global->ifaces;
+ struct wpa_radio *radio;
+
+ while (rn && iface) {
+ radio = iface->radio;
+ if (radio && os_strcmp(rn, radio->name) == 0) {
+ wpa_printf(MSG_DEBUG, "Add interface %s to existing radio %s",
+ wpa_s->ifname, rn);
+ dl_list_add(&radio->ifaces, &wpa_s->radio_list);
+ return radio;
+ }
+
+ iface = iface->next;
+ }
+
+ wpa_printf(MSG_DEBUG, "Add interface %s to a new radio %s",
+ wpa_s->ifname, rn ? rn : "N/A");
+ radio = os_zalloc(sizeof(*radio));
+ if (radio == NULL)
+ return NULL;
+
+ if (rn)
+ os_strlcpy(radio->name, rn, sizeof(radio->name));
+ dl_list_init(&radio->ifaces);
+ dl_list_init(&radio->work);
+ dl_list_add(&radio->ifaces, &wpa_s->radio_list);
+
+ return radio;
+}
+
+
+static void radio_work_free(struct wpa_radio_work *work)
+{
+ if (work->wpa_s->scan_work == work) {
+ /* This should not really happen. */
+ wpa_dbg(work->wpa_s, MSG_INFO, "Freeing radio work '%s'@%p (started=%d) that is marked as scan_work",
+ work->type, work, work->started);
+ work->wpa_s->scan_work = NULL;
+ }
+
+#ifdef CONFIG_P2P
+ if (work->wpa_s->p2p_scan_work == work) {
+ /* This should not really happen. */
+ wpa_dbg(work->wpa_s, MSG_INFO, "Freeing radio work '%s'@%p (started=%d) that is marked as p2p_scan_work",
+ work->type, work, work->started);
+ work->wpa_s->p2p_scan_work = NULL;
+ }
+#endif /* CONFIG_P2P */
+
+ dl_list_del(&work->list);
+ os_free(work);
+}
+
+
+static void radio_start_next_work(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_radio *radio = eloop_ctx;
+ struct wpa_radio_work *work;
+ struct os_reltime now, diff;
+
+ work = dl_list_first(&radio->work, struct wpa_radio_work, list);
+ if (work == NULL)
+ return;
+
+ if (work->started)
+ return; /* already started and still in progress */
+
+ 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",
+ work->type, work, diff.sec, diff.usec);
+ work->started = 1;
+ work->time = now;
+ work->cb(work, 0);
+}
+
+
+void radio_remove_unstarted_work(struct wpa_supplicant *wpa_s, const char *type)
+{
+ struct wpa_radio_work *work, *tmp;
+ struct wpa_radio *radio = wpa_s->radio;
+
+ dl_list_for_each_safe(work, tmp, &radio->work, struct wpa_radio_work,
+ list) {
+ if (type && (work->started || os_strcmp(type, work->type) != 0))
+ continue;
+ if (work->started) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Leaving started radio work '%s'@%p in the list",
+ work->type, work);
+ continue;
+ }
+ wpa_dbg(wpa_s, MSG_DEBUG, "Remove unstarted radio work '%s'@%p",
+ work->type, work);
+ work->cb(work, 1);
+ radio_work_free(work);
+ }
+}
+
+
+static void radio_remove_interface(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_radio *radio = wpa_s->radio;
+
+ if (!radio)
+ return;
+
+ wpa_printf(MSG_DEBUG, "Remove interface %s from radio %s",
+ wpa_s->ifname, radio->name);
+ dl_list_del(&wpa_s->radio_list);
+ if (!dl_list_empty(&radio->ifaces)) {
+ wpa_s->radio = NULL;
+ return; /* Interfaces remain for this radio */
+ }
+
+ wpa_printf(MSG_DEBUG, "Remove radio %s", radio->name);
+ radio_remove_unstarted_work(wpa_s, NULL);
+ eloop_cancel_timeout(radio_start_next_work, radio, NULL);
+ wpa_s->radio = NULL;
+ os_free(radio);
+}
+
+
+static void radio_work_check_next(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_radio *radio = wpa_s->radio;
+
+ if (dl_list_empty(&radio->work))
+ return;
+ eloop_cancel_timeout(radio_start_next_work, radio, NULL);
+ eloop_register_timeout(0, 0, radio_start_next_work, radio, NULL);
+}
+
+
+/**
+ * radio_add_work - Add a radio work item
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @freq: Frequency of the offchannel operation in MHz or 0
+ * @type: Unique identifier for each type of work
+ * @next: Force as the next work to be executed
+ * @cb: Callback function for indicating when radio is available
+ * @ctx: Context pointer for the work (work->ctx in cb())
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to request time for an operation that requires
+ * exclusive radio control. Once the radio is available, the registered callback
+ * function will be called. radio_work_done() must be called once the exclusive
+ * radio operation has been completed, so that the radio is freed for other
+ * operations. The special case of deinit=1 is used to free the context data
+ * during interface removal. That does not allow the callback function to start
+ * the radio operation, i.e., it must free any resources allocated for the radio
+ * work and return.
+ *
+ * The @freq parameter can be used to indicate a single channel on which the
+ * offchannel operation will occur. This may allow multiple radio work
+ * operations to be performed in parallel if they apply for the same channel.
+ * Setting this to 0 indicates that the work item may use multiple channels or
+ * requires exclusive control of the radio.
+ */
+int radio_add_work(struct wpa_supplicant *wpa_s, unsigned int freq,
+ const char *type, int next,
+ void (*cb)(struct wpa_radio_work *work, int deinit),
+ void *ctx)
+{
+ struct wpa_radio_work *work;
+ int was_empty;
+
+ work = os_zalloc(sizeof(*work));
+ if (work == NULL)
+ return -1;
+ wpa_dbg(wpa_s, MSG_DEBUG, "Add radio work '%s'@%p", type, work);
+ os_get_reltime(&work->time);
+ work->freq = freq;
+ work->type = type;
+ work->wpa_s = wpa_s;
+ work->cb = cb;
+ work->ctx = ctx;
+
+ was_empty = dl_list_empty(&wpa_s->radio->work);
+ if (next)
+ dl_list_add(&wpa_s->radio->work, &work->list);
+ else
+ dl_list_add_tail(&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);
+ }
+
+ return 0;
+}
+
+
+/**
+ * radio_work_done - Indicate that a radio work item has been completed
+ * @work: Completed work
+ *
+ * This function is called once the callback function registered with
+ * radio_add_work() has completed its work.
+ */
+void radio_work_done(struct wpa_radio_work *work)
+{
+ struct wpa_supplicant *wpa_s = work->wpa_s;
+ struct os_reltime now, diff;
+
+ os_get_reltime(&now);
+ os_reltime_sub(&now, &work->time, &diff);
+ wpa_dbg(wpa_s, MSG_DEBUG, "Radio work '%s'@%p done in %ld.%06ld seconds",
+ work->type, work, diff.sec, diff.usec);
+ radio_work_free(work);
+ radio_work_check_next(wpa_s);
+}
+
+
+static int wpas_init_driver(struct wpa_supplicant *wpa_s,
+ struct wpa_interface *iface)
+{
+ const char *ifname, *driver, *rn;
+
+ driver = iface->driver;
+next_driver:
+ if (wpa_supplicant_set_driver(wpa_s, driver) < 0)
+ return -1;
+
+ wpa_s->drv_priv = wpa_drv_init(wpa_s, wpa_s->ifname);
+ if (wpa_s->drv_priv == NULL) {
+ const char *pos;
+ pos = driver ? os_strchr(driver, ',') : NULL;
+ if (pos) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Failed to initialize "
+ "driver interface - try next driver wrapper");
+ driver = pos + 1;
+ goto next_driver;
+ }
+ wpa_msg(wpa_s, MSG_ERROR, "Failed to initialize driver "
+ "interface");
+ return -1;
+ }
+ if (wpa_drv_set_param(wpa_s, wpa_s->conf->driver_param) < 0) {
+ wpa_msg(wpa_s, MSG_ERROR, "Driver interface rejected "
+ "driver_param '%s'", wpa_s->conf->driver_param);
+ return -1;
+ }
+
+ ifname = wpa_drv_get_ifname(wpa_s);
+ if (ifname && os_strcmp(ifname, wpa_s->ifname) != 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Driver interface replaced "
+ "interface name with '%s'", ifname);
+ os_strlcpy(wpa_s->ifname, ifname, sizeof(wpa_s->ifname));
+ }
+
+ if (wpa_s->driver->get_radio_name)
+ rn = wpa_s->driver->get_radio_name(wpa_s->drv_priv);
+ else
+ rn = NULL;
+ if (rn && rn[0] == '\0')
+ rn = NULL;
+
+ wpa_s->radio = radio_add_interface(wpa_s, rn);
+ if (wpa_s->radio == NULL)
+ return -1;
+
+ return 0;
+}
+
+
static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s,
struct wpa_interface *iface)
{
- const char *ifname, *driver;
struct wpa_driver_capa capa;
wpa_printf(MSG_DEBUG, "Initializing interface '%s' conf '%s' driver "
#else /* CONFIG_BACKEND_FILE */
wpa_s->confname = os_strdup(iface->confname);
#endif /* CONFIG_BACKEND_FILE */
- wpa_s->conf = wpa_config_read(wpa_s->confname);
+ wpa_s->conf = wpa_config_read(wpa_s->confname, NULL);
if (wpa_s->conf == NULL) {
wpa_printf(MSG_ERROR, "Failed to read or parse "
"configuration '%s'.", wpa_s->confname);
return -1;
}
+ wpa_s->confanother = os_rel2abs_path(iface->confanother);
+ wpa_config_read(wpa_s->confanother, wpa_s->conf);
/*
* Override ctrl_interface and driver_param if set on command
wpa_s->conf->driver_param =
os_strdup(iface->driver_param);
}
+
+ if (iface->p2p_mgmt && !iface->ctrl_interface) {
+ os_free(wpa_s->conf->ctrl_interface);
+ wpa_s->conf->ctrl_interface = NULL;
+ }
} else
wpa_s->conf = wpa_config_alloc_empty(iface->ctrl_interface,
iface->driver_param);
* L2 receive handler so that association events are processed before
* EAPOL-Key packets if both become available for the same select()
* call. */
- driver = iface->driver;
-next_driver:
- if (wpa_supplicant_set_driver(wpa_s, driver) < 0)
+ if (wpas_init_driver(wpa_s, iface) < 0)
return -1;
- wpa_s->drv_priv = wpa_drv_init(wpa_s, wpa_s->ifname);
- if (wpa_s->drv_priv == NULL) {
- const char *pos;
- pos = driver ? os_strchr(driver, ',') : NULL;
- if (pos) {
- wpa_dbg(wpa_s, MSG_DEBUG, "Failed to initialize "
- "driver interface - try next driver wrapper");
- driver = pos + 1;
- goto next_driver;
- }
- wpa_msg(wpa_s, MSG_ERROR, "Failed to initialize driver "
- "interface");
- return -1;
- }
- if (wpa_drv_set_param(wpa_s, wpa_s->conf->driver_param) < 0) {
- wpa_msg(wpa_s, MSG_ERROR, "Driver interface rejected "
- "driver_param '%s'", wpa_s->conf->driver_param);
- return -1;
- }
-
- ifname = wpa_drv_get_ifname(wpa_s);
- if (ifname && os_strcmp(ifname, wpa_s->ifname) != 0) {
- wpa_dbg(wpa_s, MSG_DEBUG, "Driver interface replaced "
- "interface name with '%s'", ifname);
- os_strlcpy(wpa_s->ifname, ifname, sizeof(wpa_s->ifname));
- }
-
if (wpa_supplicant_init_wpa(wpa_s) < 0)
return -1;
wpa_s->max_match_sets = capa.max_match_sets;
wpa_s->max_remain_on_chan = capa.max_remain_on_chan;
wpa_s->max_stations = capa.max_stations;
+ wpa_s->extended_capa = capa.extended_capa;
+ wpa_s->extended_capa_mask = capa.extended_capa_mask;
+ wpa_s->extended_capa_len = capa.extended_capa_len;
+ wpa_s->num_multichan_concurrent =
+ capa.num_multichan_concurrent;
}
if (wpa_s->max_remain_on_chan == 0)
wpa_s->max_remain_on_chan = 1000;
+ /*
+ * Only take p2p_mgmt parameters when P2P Device is supported.
+ * Doing it here as it determines whether l2_packet_init() will be done
+ * during wpa_supplicant_driver_init().
+ */
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)
+ wpa_s->p2p_mgmt = iface->p2p_mgmt;
+ else
+ iface->p2p_mgmt = 1;
+
+ if (wpa_s->num_multichan_concurrent == 0)
+ wpa_s->num_multichan_concurrent = 1;
+
if (wpa_supplicant_driver_init(wpa_s) < 0)
return -1;
#ifdef CONFIG_TDLS
- if (wpa_tdls_init(wpa_s->wpa))
+ if ((!iface->p2p_mgmt ||
+ !(wpa_s->drv_flags &
+ WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)) &&
+ wpa_tdls_init(wpa_s->wpa))
return -1;
#endif /* CONFIG_TDLS */
}
#ifdef CONFIG_P2P
- if (wpas_p2p_init(wpa_s->global, wpa_s) < 0) {
+ if (iface->p2p_mgmt && wpas_p2p_init(wpa_s->global, wpa_s) < 0) {
wpa_msg(wpa_s, MSG_ERROR, "Failed to init P2P");
return -1;
}
if (wpa_bss_init(wpa_s) < 0)
return -1;
+#ifdef CONFIG_EAP_PROXY
+{
+ size_t len;
+ wpa_s->mnc_len = eapol_sm_get_eap_proxy_imsi(wpa_s->eapol, wpa_s->imsi,
+ &len);
+ if (wpa_s->mnc_len > 0) {
+ wpa_s->imsi[len] = '\0';
+ wpa_printf(MSG_DEBUG, "eap_proxy: IMSI %s (MNC length %d)",
+ wpa_s->imsi, wpa_s->mnc_len);
+ } else {
+ wpa_printf(MSG_DEBUG, "eap_proxy: IMSI not available");
+ }
+}
+#endif /* CONFIG_EAP_PROXY */
+
if (pcsc_reader_init(wpa_s) < 0)
return -1;
static void wpa_supplicant_deinit_iface(struct wpa_supplicant *wpa_s,
int notify, int terminate)
{
+ wpa_s->disconnected = 1;
if (wpa_s->drv_priv) {
wpa_supplicant_deauthenticate(wpa_s,
WLAN_REASON_DEAUTH_LEAVING);
}
#endif /* CONFIG_P2P */
+ radio_remove_interface(wpa_s);
+
if (wpa_s->drv_priv)
wpa_drv_deinit(wpa_s);
wpa_config_free(wpa_s->conf);
wpa_s->conf = NULL;
}
+
+ os_free(wpa_s);
}
wpa_printf(MSG_DEBUG, "Failed to add interface %s",
iface->ifname);
wpa_supplicant_deinit_iface(wpa_s, 0, 0);
- os_free(wpa_s);
return NULL;
}
/* Notify the control interfaces about new iface */
if (wpas_notify_iface_added(wpa_s)) {
wpa_supplicant_deinit_iface(wpa_s, 1, 0);
- os_free(wpa_s);
return NULL;
}
if (global->p2p_invite_group == wpa_s)
global->p2p_invite_group = NULL;
wpa_supplicant_deinit_iface(wpa_s, 1, terminate);
- os_free(wpa_s);
return 0;
}
if (params->ctrl_interface)
global->params.ctrl_interface =
os_strdup(params->ctrl_interface);
+ if (params->ctrl_interface_group)
+ global->params.ctrl_interface_group =
+ os_strdup(params->ctrl_interface_group);
if (params->override_driver)
global->params.override_driver =
os_strdup(params->override_driver);
#ifdef CONFIG_WIFI_DISPLAY
wifi_display_deinit(global);
#endif /* CONFIG_WIFI_DISPLAY */
-#ifdef CONFIG_P2P
- wpas_p2p_deinit_global(global);
-#endif /* CONFIG_P2P */
while (global->ifaces)
wpa_supplicant_remove_iface(global, global->ifaces, 1);
os_free(global->params.pid_file);
}
os_free(global->params.ctrl_interface);
+ os_free(global->params.ctrl_interface_group);
os_free(global->params.override_driver);
os_free(global->params.override_ctrl_interface);
- os_free(global->p2p_disallow_freq);
+ os_free(global->p2p_disallow_freq.range);
+ os_free(global->add_psk);
os_free(global);
wpa_debug_close_syslog();
*/
eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL);
+ if (wpa_s->disconnected) {
+ /*
+ * There is no point in blacklisting the AP if this event is
+ * generated based on local request to disconnect.
+ */
+ wpa_dbg(wpa_s, MSG_DEBUG, "Ignore connection failure "
+ "indication since interface has been put into "
+ "disconnected state");
+ return;
+ }
+
/*
* Add the failed BSSID into the blacklist and speed up next scan
* attempt if there could be other APs that could accept association.
*/
count += wpa_s->extra_blacklist_count;
+ if (count > 3 && wpa_s->current_ssid) {
+ wpa_printf(MSG_DEBUG, "Continuous association failures - "
+ "consider temporary network disabling");
+ wpas_auth_failed(wpa_s);
+ }
+
switch (count) {
case 1:
timeout = 100;
*/
wpa_supplicant_req_scan(wpa_s, timeout / 1000,
1000 * (timeout % 1000));
-
- wpas_p2p_continue_after_scan(wpa_s);
}
if (ssid == wpa_s->current_ssid)
wpa_s->reassociate = 1;
break;
+ case WPA_CTRL_REQ_SIM:
+ os_free(eap->external_sim_resp);
+ eap->external_sim_resp = os_strdup(value);
+ break;
default:
wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown field '%s'", field);
return -1;
{
struct wpa_ssid *ssid = wpa_s->current_ssid;
int dur;
- struct os_time now;
+ struct os_reltime now;
if (ssid == NULL) {
wpa_printf(MSG_DEBUG, "Authentication failure but no known "
return;
ssid->auth_failures++;
+
+#ifdef CONFIG_P2P
+ if (ssid->p2p_group &&
+ (wpa_s->p2p_in_provisioning || wpa_s->show_group_started)) {
+ /*
+ * Skip the wait time since there is a short timeout on the
+ * connection to a P2P group.
+ */
+ return;
+ }
+#endif /* CONFIG_P2P */
+
if (ssid->auth_failures > 50)
dur = 300;
else if (ssid->auth_failures > 20)
else
dur = 10;
- os_get_time(&now);
+ os_get_reltime(&now);
if (now.sec + dur <= ssid->disabled_until.sec)
return;
if (wpa_supplicant_fast_associate(wpa_s) != 1)
wpa_supplicant_req_scan(wpa_s, 0, 0);
}
+
+
+static int wpas_conn_in_progress(struct wpa_supplicant *wpa_s)
+{
+ return wpa_s->wpa_state >= WPA_AUTHENTICATING &&
+ wpa_s->wpa_state != WPA_COMPLETED;
+}
+
+
+/**
+ * wpas_wpa_is_in_progress - Check whether a connection is in progress
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @include_current: Whether to consider specified interface
+ *
+ * This function is to check if the wpa state is in beginning of the connection
+ * during 4-way handshake or group key handshake with WPA on any shared
+ * interface.
+ */
+int wpas_wpa_is_in_progress(struct wpa_supplicant *wpa_s, int include_current)
+{
+ struct wpa_supplicant *ifs;
+
+ dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant,
+ radio_list) {
+ if (!include_current && ifs == wpa_s)
+ continue;
+
+ if (wpas_conn_in_progress(ifs)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Connection is in progress "
+ "on interface %s - defer", ifs->ifname);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+
+void dump_freq_array(struct wpa_supplicant *wpa_s, const char *title,
+ int *freq_array, unsigned int len)
+{
+ unsigned int i;
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "Shared frequencies (len=%u): %s",
+ len, title);
+ for (i = 0; i < len; i++)
+ wpa_dbg(wpa_s, MSG_DEBUG, "freq[%u]: %d", i, freq_array[i]);
+}
+
+
+/*
+ * Find the operating frequencies of any of the virtual interfaces that
+ * are using the same radio as the current interface.
+ */
+int get_shared_radio_freqs(struct wpa_supplicant *wpa_s,
+ int *freq_array, unsigned int len)
+{
+ struct wpa_supplicant *ifs;
+ u8 bssid[ETH_ALEN];
+ int freq;
+ unsigned int idx = 0, i;
+
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Determining shared radio frequencies (max len %u)", len);
+ os_memset(freq_array, 0, sizeof(int) * len);
+
+ /* First add the frequency of the local interface */
+ if (wpa_s->current_ssid != NULL && wpa_s->assoc_freq != 0) {
+ if (wpa_s->current_ssid->mode == WPAS_MODE_AP ||
+ wpa_s->current_ssid->mode == WPAS_MODE_P2P_GO)
+ freq_array[idx++] = wpa_s->current_ssid->frequency;
+ else if (wpa_drv_get_bssid(wpa_s, bssid) == 0)
+ freq_array[idx++] = wpa_s->assoc_freq;
+ }
+
+ /* If get_radio_name is not supported, use only the local freq */
+ if (!wpa_s->driver->get_radio_name) {
+ freq = wpa_drv_shared_freq(wpa_s);
+ if (freq > 0 && idx < len &&
+ (idx == 0 || freq_array[0] != freq))
+ freq_array[idx++] = freq;
+ dump_freq_array(wpa_s, "No get_radio_name", freq_array, idx);
+ return idx;
+ }
+
+ dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant,
+ radio_list) {
+ if (wpa_s == ifs)
+ continue;
+
+ if (ifs->current_ssid == NULL || ifs->assoc_freq == 0)
+ continue;
+
+ if (ifs->current_ssid->mode == WPAS_MODE_AP ||
+ ifs->current_ssid->mode == WPAS_MODE_P2P_GO)
+ freq = ifs->current_ssid->frequency;
+ else if (wpa_drv_get_bssid(ifs, bssid) == 0)
+ freq = ifs->assoc_freq;
+ else
+ continue;
+
+ /* Hold only distinct freqs */
+ for (i = 0; i < idx; i++)
+ if (freq_array[i] == freq)
+ break;
+
+ if (i == idx)
+ freq_array[idx++] = freq;
+ }
+
+ dump_freq_array(wpa_s, "completed iteration", freq_array, idx);
+ return idx;
+}