]> git.ipfire.org Git - thirdparty/hostap.git/blobdiff - wpa_supplicant/wpa_supplicant.c
Use radio work for P2P scan requests
[thirdparty/hostap.git] / wpa_supplicant / wpa_supplicant.c
index 21bd73a73c727ba82d3b38763d1669dc2860d6e6..138e975a8ddcd9aafec583f1ace64dd52a920377 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * 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.
@@ -17,6 +17,7 @@
 #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"
@@ -102,11 +104,6 @@ const char *wpa_supplicant_full_license5 =
 "\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)
 {
@@ -126,8 +123,8 @@ 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;
@@ -198,17 +195,6 @@ static void wpa_supplicant_timeout(void *eloop_ctx, void *timeout_ctx)
         * So, wait a second until scanning again.
         */
        wpa_supplicant_req_scan(wpa_s, 1, 0);
-
-#ifdef CONFIG_P2P
-       if (wpa_s->global->p2p_cb_on_scan_complete && !wpa_s->global->p2p_disabled &&
-           wpa_s->global->p2p != NULL) {
-               wpa_s->global->p2p_cb_on_scan_complete = 0;
-               if (p2p_other_scan_completed(wpa_s->global->p2p) == 1) {
-                       wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Pending P2P operation "
-                               "continued after timed out authentication");
-               }
-       }
-#endif /* CONFIG_P2P */
 }
 
 
@@ -224,7 +210,7 @@ static void wpa_supplicant_timeout(void *eloop_ctx, void *timeout_ctx)
 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;
 
@@ -300,16 +286,16 @@ void wpa_supplicant_initiate_eapol(struct wpa_supplicant *wpa_s)
                                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 */
 }
@@ -408,6 +394,9 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
        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;
@@ -425,6 +414,7 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
 
        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);
@@ -462,6 +452,9 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
        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;
 
@@ -476,6 +469,9 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
        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;
@@ -497,29 +493,23 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
  */
 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) */
@@ -528,7 +518,7 @@ void wpa_clear_keys(struct wpa_supplicant *wpa_s, const u8 *addr)
                        MLME_SETPROTECTION_PROTECT_TYPE_NONE,
                        MLME_SETPROTECTION_KEY_TYPE_PAIRWISE);
        }
-       wpa_s->keys_cleared = 1;
+       wpa_s->keys_cleared = (u32) -1;
 }
 
 
@@ -570,14 +560,22 @@ const char * wpa_supplicant_state_txt(enum wpa_states state)
 
 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");
                        /*
@@ -655,8 +653,8 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s,
                wpa_supplicant_notify_scanning(wpa_s, 0);
 
        if (state == WPA_COMPLETED && wpa_s->new_connection) {
-#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
                struct wpa_ssid *ssid = wpa_s->current_ssid;
+#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
                wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_CONNECTED "- Connection to "
                        MACSTR " completed [id=%d id_str=%s]",
                        MAC2STR(wpa_s->bssid),
@@ -671,6 +669,7 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s,
                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 */
@@ -690,7 +689,7 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s,
 #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 */
 
@@ -716,9 +715,15 @@ void wpa_supplicant_terminate_proc(struct wpa_global *global)
 #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)
@@ -769,12 +774,14 @@ int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s)
 
        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
@@ -845,34 +852,6 @@ static void wpa_supplicant_reconfig(int sig, void *signal_ctx)
 }
 
 
-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)
@@ -1187,30 +1166,71 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
 }
 
 
-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)
+                       *pos |= 0x80; /* Bit 31 - Interworking */
+#endif /* CONFIG_INTERWORKING */
+               break;
+       case 4: /* Bits 32-39 */
 #ifdef CONFIG_INTERWORKING
-       if (wpa_s->conf->interworking)
-               ext_capab |= BIT(31); /* 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);
+
+               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 pos - buf;
+       return 2 + len;
 }
 
 
@@ -1229,14 +1249,11 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
        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;
@@ -1257,6 +1274,8 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
                }
                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;
@@ -1352,8 +1371,7 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
                                     ssid->proactive_key_caching) &&
                        (ssid->proto & WPA_PROTO_RSN);
                if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid,
-                                           wpa_s->current_ssid,
-                                           try_opportunistic) == 0)
+                                           ssid, try_opportunistic) == 0)
                        eapol_sm_notify_pmkid_attempt(wpa_s->eapol, 1);
                wpa_ie_len = sizeof(wpa_ie);
                if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
@@ -1434,7 +1452,7 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
 #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) {
@@ -1447,21 +1465,33 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
        }
 #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)
@@ -1485,7 +1515,7 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
                        /* 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 */
@@ -1526,7 +1556,7 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
        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;
@@ -1539,8 +1569,8 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
        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;
@@ -1581,6 +1611,25 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
        wpa_supplicant_apply_ht_overrides(wpa_s, ssid, &params);
 #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, &params);
        if (ret < 0) {
                wpa_msg(wpa_s, MSG_INFO, "Association request to the driver "
@@ -1633,8 +1682,8 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
                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);
        }
@@ -1709,6 +1758,10 @@ void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s,
                zero_addr = 1;
        }
 
+#ifdef CONFIG_TDLS
+       wpa_tdls_teardown_peers(wpa_s->wpa);
+#endif /* CONFIG_TDLS */
+
        if (addr) {
                wpa_drv_deauthenticate(wpa_s, addr, reason_code);
                os_memset(&event, 0, sizeof(event));
@@ -1722,6 +1775,24 @@ void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s,
        wpa_supplicant_clear_connection(wpa_s, addr);
 }
 
+static void wpa_supplicant_enable_one_network(struct wpa_supplicant *wpa_s,
+                                             struct wpa_ssid *ssid)
+{
+       if (!ssid || !ssid->disabled || ssid->disabled == 2)
+               return;
+
+       ssid->disabled = 0;
+       wpas_clear_temp_disabled(wpa_s, ssid, 1);
+       wpas_notify_network_enabled_changed(wpa_s, ssid);
+
+       /*
+        * Try to reassociate since there is no current configuration and a new
+        * network was made available.
+        */
+       if (!wpa_s->current_ssid && !wpa_s->disconnected)
+               wpa_s->reassociate = 1;
+}
+
 
 /**
  * wpa_supplicant_enable_network - Mark a configured network as enabled
@@ -1733,48 +1804,21 @@ void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s,
 void wpa_supplicant_enable_network(struct wpa_supplicant *wpa_s,
                                   struct wpa_ssid *ssid)
 {
-       struct wpa_ssid *other_ssid;
-       int was_disabled;
-
        if (ssid == NULL) {
-               for (other_ssid = wpa_s->conf->ssid; other_ssid;
-                    other_ssid = other_ssid->next) {
-                       if (other_ssid->disabled == 2)
-                               continue; /* do not change persistent P2P group
-                                          * data */
-                       if (other_ssid == wpa_s->current_ssid &&
-                           other_ssid->disabled)
-                               wpa_s->reassociate = 1;
-
-                       was_disabled = other_ssid->disabled;
-
-                       other_ssid->disabled = 0;
-                       if (was_disabled)
-                               wpas_clear_temp_disabled(wpa_s, other_ssid, 0);
+               for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next)
+                       wpa_supplicant_enable_one_network(wpa_s, ssid);
+       } else
+               wpa_supplicant_enable_one_network(wpa_s, ssid);
 
-                       if (was_disabled != other_ssid->disabled)
-                               wpas_notify_network_enabled_changed(
-                                       wpa_s, other_ssid);
-               }
-               if (wpa_s->reassociate)
-                       wpa_supplicant_req_scan(wpa_s, 0, 0);
-       } else if (ssid->disabled && ssid->disabled != 2) {
-               if (wpa_s->current_ssid == NULL) {
-                       /*
-                        * Try to reassociate since there is no current
-                        * configuration and a new network was made available.
-                        */
-                       wpa_s->reassociate = 1;
-                       wpa_supplicant_req_scan(wpa_s, 0, 0);
+       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);
                }
 
-               was_disabled = ssid->disabled;
-
-               ssid->disabled = 0;
-               wpas_clear_temp_disabled(wpa_s, ssid, 1);
-
-               if (was_disabled != ssid->disabled)
-                       wpas_notify_network_enabled_changed(wpa_s, ssid);
+               if (wpa_supplicant_fast_associate(wpa_s) != 1)
+                       wpa_supplicant_req_scan(wpa_s, 0, 0);
        }
 }
 
@@ -1793,6 +1837,9 @@ void wpa_supplicant_disable_network(struct wpa_supplicant *wpa_s,
        int was_disabled;
 
        if (ssid == NULL) {
+               if (wpa_s->sched_scanning)
+                       wpa_supplicant_cancel_sched_scan(wpa_s);
+
                for (other_ssid = wpa_s->conf->ssid; other_ssid;
                     other_ssid = other_ssid->next) {
                        was_disabled = other_ssid->disabled;
@@ -1818,8 +1865,15 @@ void wpa_supplicant_disable_network(struct wpa_supplicant *wpa_s,
 
                ssid->disabled = 1;
 
-               if (was_disabled != ssid->disabled)
+               if (was_disabled != ssid->disabled) {
                        wpas_notify_network_enabled_changed(wpa_s, ssid);
+                       if (wpa_s->sched_scanning) {
+                               wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan "
+                                          "to remove network from filters");
+                               wpa_supplicant_cancel_sched_scan(wpa_s);
+                               wpa_supplicant_req_scan(wpa_s, 0, 0);
+                       }
+               }
        }
 }
 
@@ -1870,18 +1924,75 @@ void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s,
                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;
-       wpa_supplicant_req_scan(wpa_s, 0, disconnected ? 100000 : 0);
+
+       if (wpa_supplicant_fast_associate(wpa_s) != 1)
+               wpa_supplicant_req_scan(wpa_s, 0, disconnected ? 100000 : 0);
 
        if (ssid)
                wpas_notify_network_selected(wpa_s, ssid);
 }
 
 
+/**
+ * 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
@@ -2176,6 +2287,16 @@ 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);
 
+#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
@@ -2201,7 +2322,7 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr,
                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);
                }
@@ -2285,7 +2406,10 @@ int wpa_supplicant_update_mac_addr(struct wpa_supplicant *wpa_s)
                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,
@@ -2305,10 +2429,6 @@ int wpa_supplicant_update_mac_addr(struct wpa_supplicant *wpa_s)
                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;
 }
 
@@ -2354,6 +2474,10 @@ int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s)
        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);
@@ -2619,6 +2743,54 @@ void wpa_supplicant_apply_ht_overrides(
 #endif /* CONFIG_HT_OVERRIDES */
 
 
+#ifdef CONFIG_VHT_OVERRIDES
+void wpa_supplicant_apply_vht_overrides(
+       struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+       struct wpa_driver_associate_params *params)
+{
+       struct ieee80211_vht_capabilities *vhtcaps;
+       struct ieee80211_vht_capabilities *vhtcaps_mask;
+
+       if (!ssid)
+               return;
+
+       params->disable_vht = ssid->disable_vht;
+
+       vhtcaps = (void *) params->vhtcaps;
+       vhtcaps_mask = (void *) params->vhtcaps_mask;
+
+       if (!vhtcaps || !vhtcaps_mask)
+               return;
+
+       vhtcaps->vht_capabilities_info = ssid->vht_capa;
+       vhtcaps_mask->vht_capabilities_info = ssid->vht_capa_mask;
+
+#define OVERRIDE_MCS(i)                                                        \
+       if (ssid->vht_tx_mcs_nss_ ##i >= 0) {                           \
+               vhtcaps_mask->vht_supported_mcs_set.tx_map |=           \
+                       3 << 2 * (i - 1);                               \
+               vhtcaps->vht_supported_mcs_set.tx_map |=                \
+                       ssid->vht_tx_mcs_nss_ ##i << 2 * (i - 1);       \
+       }                                                               \
+       if (ssid->vht_rx_mcs_nss_ ##i >= 0) {                           \
+               vhtcaps_mask->vht_supported_mcs_set.rx_map |=           \
+                       3 << 2 * (i - 1);                               \
+               vhtcaps->vht_supported_mcs_set.rx_map |=                \
+                       ssid->vht_rx_mcs_nss_ ##i << 2 * (i - 1);       \
+       }
+
+       OVERRIDE_MCS(1);
+       OVERRIDE_MCS(2);
+       OVERRIDE_MCS(3);
+       OVERRIDE_MCS(4);
+       OVERRIDE_MCS(5);
+       OVERRIDE_MCS(6);
+       OVERRIDE_MCS(7);
+       OVERRIDE_MCS(8);
+}
+#endif /* CONFIG_VHT_OVERRIDES */
+
+
 static int pcsc_reader_init(struct wpa_supplicant *wpa_s)
 {
 #ifdef PCSC_FUNCS
@@ -2627,7 +2799,7 @@ static int pcsc_reader_init(struct wpa_supplicant *wpa_s)
        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;
 
@@ -2693,10 +2865,276 @@ int wpas_init_ext_pw(struct wpa_supplicant *wpa_s)
 }
 
 
+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 "
@@ -2720,12 +3158,14 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s,
 #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
@@ -2742,6 +3182,11 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s,
                        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);
@@ -2781,37 +3226,8 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s,
         * 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)
-               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");
+       if (wpas_init_driver(wpa_s, iface) < 0)
                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;
@@ -2860,15 +3276,36 @@ next_driver:
                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 */
 
@@ -2906,7 +3343,7 @@ next_driver:
        }
 
 #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;
        }
@@ -2915,6 +3352,21 @@ next_driver:
        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;
 
@@ -2928,6 +3380,7 @@ next_driver:
 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);
@@ -2946,6 +3399,8 @@ static void wpa_supplicant_deinit_iface(struct wpa_supplicant *wpa_s,
        }
 #endif /* CONFIG_P2P */
 
+       radio_remove_interface(wpa_s);
+
        if (wpa_s->drv_priv)
                wpa_drv_deinit(wpa_s);
 
@@ -2964,6 +3419,8 @@ static void wpa_supplicant_deinit_iface(struct wpa_supplicant *wpa_s,
                wpa_config_free(wpa_s->conf);
                wpa_s->conf = NULL;
        }
+
+       os_free(wpa_s);
 }
 
 
@@ -3014,14 +3471,12 @@ struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global,
                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;
        }
 
@@ -3071,8 +3526,9 @@ int wpa_supplicant_remove_iface(struct wpa_global *global,
 
        if (global->p2p_group_formation == wpa_s)
                global->p2p_group_formation = 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;
 }
@@ -3192,6 +3648,9 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params)
        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);
@@ -3301,9 +3760,6 @@ void wpa_supplicant_deinit(struct wpa_global *global)
 #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);
@@ -3334,10 +3790,12 @@ void wpa_supplicant_deinit(struct wpa_global *global)
                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();
@@ -3434,6 +3892,17 @@ void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid)
         */
        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.
@@ -3471,6 +3940,12 @@ void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid)
         */
        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;
@@ -3498,17 +3973,6 @@ void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid)
         */
        wpa_supplicant_req_scan(wpa_s, timeout / 1000,
                                1000 * (timeout % 1000));
-
-#ifdef CONFIG_P2P
-       if (wpa_s->global->p2p_cb_on_scan_complete && !wpa_s->global->p2p_disabled &&
-           wpa_s->global->p2p != NULL) {
-               wpa_s->global->p2p_cb_on_scan_complete = 0;
-               if (p2p_other_scan_completed(wpa_s->global->p2p) == 1) {
-                       wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Pending P2P operation "
-                               "continued after failed association");
-               }
-       }
-#endif /* CONFIG_P2P */
 }
 
 
@@ -3579,6 +4043,10 @@ int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *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;
@@ -3644,7 +4112,7 @@ void wpas_auth_failed(struct wpa_supplicant *wpa_s)
 {
        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 "
@@ -3656,6 +4124,18 @@ void wpas_auth_failed(struct wpa_supplicant *wpa_s)
                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)
@@ -3669,7 +4149,7 @@ void wpas_auth_failed(struct wpa_supplicant *wpa_s)
        else
                dur = 10;
 
-       os_get_time(&now);
+       os_get_reltime(&now);
        if (now.sec + dur <= ssid->disabled_until.sec)
                return;
 
@@ -3751,5 +4231,120 @@ void wpas_request_connection(struct wpa_supplicant *wpa_s)
        wpa_s->extra_blacklist_count = 0;
        wpa_s->disconnected = 0;
        wpa_s->reassociate = 1;
-       wpa_supplicant_req_scan(wpa_s, 0, 0);
+
+       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;
 }